offscreen object arrows
This commit is contained in:
parent
757e85bbcf
commit
2024c55888
@ -3,3 +3,10 @@ export const DISPLAY_OBJECTS_INFO = false;
|
|||||||
export const DISPLAY_CURSOR_INFO = false;
|
export const DISPLAY_CURSOR_INFO = false;
|
||||||
export const DISPLAY_VELOCITY_VECTORS = true;
|
export const DISPLAY_VELOCITY_VECTORS = true;
|
||||||
export const POINTER_HISTORY_SIZE = 20;
|
export const POINTER_HISTORY_SIZE = 20;
|
||||||
|
export const VELOCITY_VECTOR_SCALE = 0.2;
|
||||||
|
export const VELOCITY_VECTOR_COLOR = 'rgb(150, 150, 150)'; // optionally set to 'object color'
|
||||||
|
export const VELOCITY_VECTOR_WIDTH = 1.5;
|
||||||
|
export const ARROWHEAD_LENGTH = 10;
|
||||||
|
export const ARROWHEAD_WIDTH = 7;
|
||||||
|
export const MOTION_TIME_SCALE = 0.001;
|
||||||
|
export const OFFSCREEN_OBJECT_ARROW_SCALE = 5;
|
||||||
|
|||||||
89
display.js
89
display.js
@ -1,8 +1,15 @@
|
|||||||
|
import {
|
||||||
|
VELOCITY_VECTOR_SCALE,
|
||||||
|
VELOCITY_VECTOR_COLOR,
|
||||||
|
VELOCITY_VECTOR_WIDTH,
|
||||||
|
ARROWHEAD_LENGTH,
|
||||||
|
ARROWHEAD_WIDTH,
|
||||||
|
OFFSCREEN_OBJECT_ARROW_SCALE,
|
||||||
|
} from './config.js';
|
||||||
|
|
||||||
export class Display {
|
export class Display {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
|
|
||||||
VELOCITY_VECTOR_SCALE = 0.2;
|
|
||||||
|
|
||||||
constructor(sim) {
|
constructor(sim) {
|
||||||
this.sim = sim;
|
this.sim = sim;
|
||||||
// Create canvas that fills the window
|
// Create canvas that fills the window
|
||||||
@ -37,6 +44,57 @@ export class Display {
|
|||||||
const {x, y} = obj.position;
|
const {x, y} = obj.position;
|
||||||
const {x: vx, y: vy} = obj.velocity;
|
const {x: vx, y: vy} = obj.velocity;
|
||||||
const radius = obj.radius;
|
const radius = obj.radius;
|
||||||
|
const {height: H, width: W} = this.canvas;
|
||||||
|
|
||||||
|
// If the object is outside the display area, draw an arrow at the edge of the display
|
||||||
|
if (x - radius >= W || x + radius <= 0 ||
|
||||||
|
y - radius >= H || y + radius <= 0) {
|
||||||
|
// Find where a line from center of display to object intersects display edge
|
||||||
|
const cx = W / 2;
|
||||||
|
const cy = H / 2;
|
||||||
|
let px, py;
|
||||||
|
if (y <= cy) {
|
||||||
|
// Line intersects y = 0:
|
||||||
|
const y0px = cx + cy / (cy - y) * (x - cx);
|
||||||
|
if (y0px >= 0 && y0px <= W) {
|
||||||
|
px = y0px;
|
||||||
|
py = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Line intersects y = H
|
||||||
|
const yHpx = cx + cy / (y - cy) * (x - cx);
|
||||||
|
if (yHpx >= 0 && yHpx <= W) {
|
||||||
|
px = yHpx;
|
||||||
|
py = H;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (px === undefined) {
|
||||||
|
if (x <= 0) {
|
||||||
|
// Line intersects x = 0:
|
||||||
|
px = 0;
|
||||||
|
py = cy + cx / (cx - x) * (y - cy);
|
||||||
|
} else {
|
||||||
|
// Line intersects x = W:
|
||||||
|
px = W;
|
||||||
|
py = cy + cx / (x - cx) * (y - cy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrowDirection = Math.atan2(py - cy, px - cx);
|
||||||
|
this.drawArrowHead(px, py, arrowDirection, `rgb(${r}, ${g}, ${b})`);
|
||||||
|
// Length of arrow based on distance (logarithmic scale)
|
||||||
|
const distance = Math.sqrt((x - px)**2, (y - py)**2);
|
||||||
|
const arrowLength = Math.log(distance) * OFFSCREEN_OBJECT_ARROW_SCALE;
|
||||||
|
const ax = px - arrowLength * Math.cos(arrowDirection);
|
||||||
|
const ay = py - arrowLength * Math.sin(arrowDirection);
|
||||||
|
ctx.strokeStyle = `rgb(${r}, ${g}, ${b})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(ax, ay);
|
||||||
|
ctx.lineTo(px, py);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Draw filled circle for the object
|
// Draw filled circle for the object
|
||||||
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
|
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
|
||||||
@ -45,13 +103,15 @@ export class Display {
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// Draw line for the velocity
|
// Draw line for the velocity
|
||||||
// TODO: Arrow
|
const endVx = x + VELOCITY_VECTOR_SCALE * vx;
|
||||||
ctx.strokeStyle = ctx.fillStyle;
|
const endVy = y + VELOCITY_VECTOR_SCALE * vy;
|
||||||
ctx.lineWidth = 2.0;
|
ctx.strokeStyle = VELOCITY_VECTOR_COLOR === 'object color' ? ctx.fillStyle : VELOCITY_VECTOR_COLOR;
|
||||||
|
ctx.lineWidth = VELOCITY_VECTOR_WIDTH;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x, y);
|
ctx.moveTo(x, y);
|
||||||
ctx.lineTo(x + this.VELOCITY_VECTOR_SCALE * vx, y + this.VELOCITY_VECTOR_SCALE * vy);
|
ctx.lineTo(endVx, endVy);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
this.drawArrowHead(endVx, endVy, Math.atan2(vy, vx), ctx.strokeStyle);
|
||||||
|
|
||||||
// TODO: Draw line for acceleration
|
// TODO: Draw line for acceleration
|
||||||
}
|
}
|
||||||
@ -61,4 +121,21 @@ export class Display {
|
|||||||
this.drawObject(i);
|
this.drawObject(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawArrowHead(x, y, direction, fillStyle) {
|
||||||
|
const ctx = this.ctx;
|
||||||
|
if (fillStyle) {
|
||||||
|
ctx.fillStyle = fillStyle;
|
||||||
|
}
|
||||||
|
// To make this simple, draw the arrowhead and then rotate and translate it as needed.
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
ctx.translate(x, y);
|
||||||
|
ctx.rotate(direction);
|
||||||
|
ctx.lineTo(-ARROWHEAD_LENGTH, -ARROWHEAD_WIDTH / 2);
|
||||||
|
ctx.lineTo(-ARROWHEAD_LENGTH, ARROWHEAD_WIDTH / 2);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.resetTransform();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
objects.js
13
objects.js
@ -1,5 +1,9 @@
|
|||||||
import { MassObject } from './object.js';
|
import { MassObject } from './object.js';
|
||||||
import { MASS_CREATION_RATE, DISPLAY_OBJECTS_INFO } from './config.js';
|
import {
|
||||||
|
MASS_CREATION_RATE,
|
||||||
|
DISPLAY_OBJECTS_INFO,
|
||||||
|
MOTION_TIME_SCALE,
|
||||||
|
} from './config.js';
|
||||||
|
|
||||||
export class Objects {
|
export class Objects {
|
||||||
objects = [];
|
objects = [];
|
||||||
@ -65,6 +69,13 @@ export class Objects {
|
|||||||
obj.mass += rate * elapsedTime;
|
obj.mass += rate * elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update positions. Simple Euler method for now.
|
||||||
|
for (let i = 0; i < this.objects.length; i++) {
|
||||||
|
const obj = this.objects[i];
|
||||||
|
obj.position.x += obj.velocity.x * MOTION_TIME_SCALE;
|
||||||
|
obj.position.y += obj.velocity.y * MOTION_TIME_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
// Display objects info
|
// Display objects info
|
||||||
if (DISPLAY_OBJECTS_INFO) {
|
if (DISPLAY_OBJECTS_INFO) {
|
||||||
for (let i = 0; i < this.objects.length; i++) {
|
for (let i = 0; i < this.objects.length; i++) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user