traces options
This commit is contained in:
parent
a290c1bd4c
commit
59bf820081
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*.sw[po]
|
||||
node_modules/
|
||||
*.diff
|
||||
|
||||
@ -10,20 +10,23 @@ export const DISPLAY_PATH_TRACES = false;
|
||||
|
||||
// VELOCITY
|
||||
export const VELOCITY_VECTOR_SCALE = 5E0;
|
||||
export const VELOCITY_VECTOR_COLOR = 'rgb(150, 150, 150)'; // optionally set to 'object color'
|
||||
export const VELOCITY_VECTOR_COLOR = 'rgba(150, 150, 150, 0.8)'; // optionally set to 'object color'
|
||||
export const VELOCITY_VECTOR_WIDTH = 1.5;
|
||||
export const VELOCITY_VECTOR_ARROWHEAD = true;
|
||||
|
||||
// ACCELERATION
|
||||
export const ACCELERATION_VECTOR_SCALE = 5E0;
|
||||
export const ACCELERATION_VECTOR_COLOR = 'rgb(0, 128, 0)'; // optionally set to 'object color'
|
||||
export const ACCELERATION_VECTOR_COLOR = 'rgba(0, 128, 0, 0.8)'; // optionally set to 'object color'
|
||||
export const ACCELERATION_VECTOR_WIDTH = 1.5;
|
||||
export const ACCELERATION_VECTOR_ARROWHEAD = true;
|
||||
|
||||
// PATH TRACES
|
||||
// export const PATH_TRACES_COLOR = 'rgb(128, 128, 0)'; // optionally set to 'object color'
|
||||
export const PATH_TRACES_COLOR = 'object color';
|
||||
export const PATH_TRACES_OPACITY = 0.8;
|
||||
export const PATH_TRACES_WIDTH = 1.5;
|
||||
export const PATH_TRACES_DASHED = true;
|
||||
export const PATH_TRACES_DASHED_OPACITY = 1.0;
|
||||
|
||||
// SCALING FACTORS
|
||||
export const MASS_CREATION_RATE = 1E1;
|
||||
|
||||
73
display.js
73
display.js
@ -1,20 +1,22 @@
|
||||
import {
|
||||
VELOCITY_VECTOR_SCALE,
|
||||
VELOCITY_VECTOR_COLOR,
|
||||
VELOCITY_VECTOR_WIDTH,
|
||||
VELOCITY_VECTOR_ARROWHEAD,
|
||||
ACCELERATION_VECTOR_SCALE,
|
||||
ACCELERATION_VECTOR_COLOR,
|
||||
ACCELERATION_VECTOR_WIDTH,
|
||||
import {
|
||||
ACCELERATION_VECTOR_ARROWHEAD,
|
||||
ACCELERATION_VECTOR_COLOR,
|
||||
ACCELERATION_VECTOR_SCALE,
|
||||
ACCELERATION_VECTOR_WIDTH,
|
||||
ARROWHEAD_LENGTH,
|
||||
ARROWHEAD_WIDTH,
|
||||
DISPLAY_CANVAS_SIZE,
|
||||
OFFSCREEN_OBJECT_ARROWHEAD_LENGTH,
|
||||
OFFSCREEN_OBJECT_LINE_SCALE,
|
||||
OFFSCREEN_OBJECT_LINE_WIDTH,
|
||||
OFFSCREEN_OBJECT_ARROWHEAD_LENGTH,
|
||||
DISPLAY_CANVAS_SIZE,
|
||||
PATH_TRACES_COLOR,
|
||||
PATH_TRACES_OPACITY,
|
||||
PATH_TRACES_DASHED_OPACITY,
|
||||
PATH_TRACES_WIDTH,
|
||||
VELOCITY_VECTOR_ARROWHEAD,
|
||||
VELOCITY_VECTOR_COLOR,
|
||||
VELOCITY_VECTOR_SCALE,
|
||||
VELOCITY_VECTOR_WIDTH,
|
||||
} from './config.js';
|
||||
|
||||
export class Display {
|
||||
@ -99,24 +101,25 @@ export class Display {
|
||||
|
||||
// Draw path traces
|
||||
if (this.sim.getOption('display.traces') && obj.history?.length) {
|
||||
ctx.strokeStyle = PATH_TRACES_COLOR === 'object color' ?
|
||||
`rgb(${r}, ${g}, ${b})` : PATH_TRACES_COLOR;
|
||||
const dashedTraces = this.sim.getOption('display.dashedTraces');
|
||||
const opacity = dashedTraces ? PATH_TRACES_DASHED_OPACITY : PATH_TRACES_OPACITY;
|
||||
ctx.strokeStyle = PATH_TRACES_COLOR === 'object color' ?
|
||||
`rgba(${r}, ${g}, ${b}, ${opacity})` : PATH_TRACES_COLOR;
|
||||
ctx.lineWidth = PATH_TRACES_WIDTH / this.scale;
|
||||
ctx.beginPath();
|
||||
let dash = false;
|
||||
for (let i = 0; i < obj.history.length; i++) {
|
||||
// for (let i = obj.history.length - 1; i >= 0; i--) {
|
||||
if (i % 2) continue;
|
||||
const {position} = obj.history[i];
|
||||
const x = position.x;
|
||||
const y = position.y;
|
||||
if (dash) {
|
||||
for (let i = 0; i < obj.history.length ; i++) {
|
||||
// if (i % 2) continue;
|
||||
const {position: {x, y}} = obj.history[i];
|
||||
if (dashedTraces) {
|
||||
if (dash) {
|
||||
ctx.lineTo(x, y);
|
||||
} else {
|
||||
ctx.moveTo(x, y);
|
||||
}
|
||||
dash = !dash;
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
dash = false;
|
||||
} else if (Math.abs(x - cx) <= W / 2 &&
|
||||
Math.abs(y - cy) <= H / 2) {
|
||||
ctx.moveTo(x, y);
|
||||
dash = true;
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
@ -126,7 +129,7 @@ export class Display {
|
||||
|
||||
// If the object is outside the display area, draw an arrow at the edge of the display
|
||||
if (Math.abs(x - cx) - radius >= W / 2 ||
|
||||
Math.abs(y - cy) - radius >= H / 2) {
|
||||
Math.abs(y - cy) - radius >= H / 2) {
|
||||
// Find where a line from center of display to object intersects display edge
|
||||
let px, py;
|
||||
if (y <= cy) {
|
||||
@ -158,7 +161,7 @@ export class Display {
|
||||
|
||||
const arrowDirection = Math.atan2(py - cy, px - cx);
|
||||
// Length of arrow based on distance (logarithmic scale)
|
||||
const distance = Math.sqrt((x - px)**2, (y - py)**2) * this.scale;
|
||||
const distance = Math.sqrt((x - px) ** 2, (y - py) ** 2) * this.scale;
|
||||
const arrowLength = Math.log(distance) * OFFSCREEN_OBJECT_LINE_SCALE / this.scale;
|
||||
const startAx = px - arrowLength * Math.cos(arrowDirection);
|
||||
const startAy = py - arrowLength * Math.sin(arrowDirection);
|
||||
@ -169,14 +172,14 @@ export class Display {
|
||||
fill: false,
|
||||
ifShort: 'head',
|
||||
});
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw filled circle for the object
|
||||
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, 2*Math.PI);
|
||||
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
// Draw arrow for the velocity
|
||||
@ -184,10 +187,10 @@ export class Display {
|
||||
const speed = Math.sqrt(vx ** 2 + vy ** 2);
|
||||
const endVx = x + VELOCITY_VECTOR_SCALE * vx / speed * Math.log(speed);
|
||||
const endVy = y + VELOCITY_VECTOR_SCALE * vy / speed * Math.log(speed);
|
||||
const style = VELOCITY_VECTOR_COLOR === 'object color' ?
|
||||
`rgb(${r}, ${g}, ${b})` : VELOCITY_VECTOR_COLOR;
|
||||
const style = VELOCITY_VECTOR_COLOR === 'object color' ?
|
||||
`rgb(${r}, ${g}, ${b})` : VELOCITY_VECTOR_COLOR;
|
||||
this.drawArrow(x, y, endVx, endVy, {
|
||||
style,
|
||||
style,
|
||||
width: VELOCITY_VECTOR_WIDTH,
|
||||
arrowhead: VELOCITY_VECTOR_ARROWHEAD,
|
||||
fill: false,
|
||||
@ -202,10 +205,10 @@ export class Display {
|
||||
accelerationMagnitude * Math.log(accelerationMagnitude);
|
||||
const endAy = y + ACCELERATION_VECTOR_SCALE * acceleration.y /
|
||||
accelerationMagnitude * Math.log(accelerationMagnitude);
|
||||
const style = ACCELERATION_VECTOR_COLOR === 'object color' ?
|
||||
const style = ACCELERATION_VECTOR_COLOR === 'object color' ?
|
||||
`rgb(${r}, ${g}, ${b})` : ACCELERATION_VECTOR_COLOR;
|
||||
this.drawArrow(x, y, endAx, endAy, {
|
||||
style,
|
||||
style,
|
||||
width: ACCELERATION_VECTOR_WIDTH,
|
||||
arrowhead: ACCELERATION_VECTOR_ARROWHEAD,
|
||||
fill: false,
|
||||
@ -228,14 +231,14 @@ export class Display {
|
||||
const scaledArrowheadLength = arrowheadLength / this.scale;
|
||||
ifShort = ifShort ?? 'tail';
|
||||
const arrowDirection = Math.atan2(endY - startY, endX - startX);
|
||||
const length = Math.sqrt((endX - startX)**2 + (endY - startY)**2);
|
||||
const length = Math.sqrt((endX - startX) ** 2 + (endY - startY) ** 2);
|
||||
let tail = true;
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
if (length <= scaledArrowheadLength) {
|
||||
switch (ifShort) {
|
||||
case 'head': {
|
||||
case 'head': {
|
||||
arrowhead = true;
|
||||
tail = false;
|
||||
break;
|
||||
|
||||
69
pointer.js
69
pointer.js
@ -2,6 +2,7 @@ import {
|
||||
DISPLAY_CURSOR_INFO,
|
||||
DRAGGABLE_ELEMENT_CLASSNAME,
|
||||
MODE_MASS_GENERATION,
|
||||
MODE_OBJECT_SELECT,
|
||||
MODE_PAN_VIEW,
|
||||
POINTER_HISTORY_SIZE,
|
||||
ZOOM_IN_FACTOR,
|
||||
@ -14,6 +15,8 @@ export class Pointer {
|
||||
pointerHistory = [];
|
||||
draggingElement = undefined;
|
||||
panning = undefined;
|
||||
panTouchStart = undefined; // {x: undefined, y: undefined, t: undefined};
|
||||
panTouchLatest = undefined; // {x: undefined, y: undefined, t: undefined};
|
||||
suppressClick = false;
|
||||
|
||||
constructor(sim) {
|
||||
@ -130,14 +133,22 @@ export class Pointer {
|
||||
handlePointerDown({x: clientX, y: clientY}) {
|
||||
this.clearPointerHistory(5);
|
||||
this.updatePointer({x: clientX, y: clientY});
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY)
|
||||
|
||||
if (this.sim.isCurrentMode(MODE_MASS_GENERATION)) {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY)
|
||||
this.sim.objects.handlePointerDown({x, y});
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
this.panning = this.panning || {};
|
||||
this.panning.gathering = true;
|
||||
this.panning.velocity = this.panning.velocity || {x: 0, y: 0};
|
||||
this.panTouchStart = {
|
||||
x,
|
||||
y,
|
||||
t: document.timeline.currentTime,
|
||||
viewOrigin: {...this.sim.display.viewOrigin},
|
||||
};
|
||||
this.panTouchLatest = {...this.panTouchStart};
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_OBJECT_SELECT)) {
|
||||
// TODO: Start a selection box
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,8 +158,17 @@ export class Pointer {
|
||||
this.sim.objects.handlePointerUp({x, y});
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
if (this.panning?.gathering) {
|
||||
this.panning.gathering = false;
|
||||
// Set panning velocity
|
||||
if (this.panTouchStart && this.panTouchLatest) {
|
||||
const dt = this.panTouchLatest.t - this.panTouchStart.t;
|
||||
const current = this.panning?.velocity;
|
||||
this.panning = {
|
||||
velocity: {
|
||||
x: (current?.x ?? 0) + (this.panTouchLatest.x - this.panTouchStart.x) / dt,
|
||||
y: (current?.y ?? 0) + (this.panTouchLatest.y - this.panTouchStart.y) / dt,
|
||||
}
|
||||
};
|
||||
this.panTouchStart = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -158,20 +178,27 @@ export class Pointer {
|
||||
handlePointerMove({x: clientX, y: clientY}) {
|
||||
this.updatePointer({x: clientX, y: clientY});
|
||||
const {v} = this.pointerHistory[this.pointerHistory.length - 1];
|
||||
// const a = this.getPointerAcceleration();
|
||||
// Convert pointer velocity to simulation scale
|
||||
v.x /= this.sim.display.scale;
|
||||
v.y /= this.sim.display.scale;
|
||||
// v.x = (v.x + a.x * v.dt / this.sim.display.scale) / 2 ;
|
||||
// v.y = (v.y + a.y * v.dt / this.sim.display.scale) / 2;
|
||||
|
||||
// const a = this.getPointerAcceleration();
|
||||
// v.x = v.x + a.x * v.dt / this.sim.display.scale / 2;
|
||||
// v.y = v.y + a.y * v.dt / this.sim.display.scale / 2;
|
||||
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
|
||||
if (this.sim.isCurrentMode(MODE_MASS_GENERATION)) {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
// Convert pointer velocity to sim internal scale
|
||||
this.sim.objects.handlePointerMove({x, y, vx: v.x, vy: v.y});
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
if (this.panning?.gathering) {
|
||||
this.panning.velocity = v;
|
||||
}
|
||||
// Event loop should be able to read
|
||||
this.panTouchLatest = {
|
||||
x,
|
||||
y,
|
||||
t: document.timeline.currentTime,
|
||||
viewOrigin: {...this.sim.display.viewOrigin},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,11 +208,15 @@ export class Pointer {
|
||||
const currentPointer = this.pointerHistory[this.pointerHistory.length - 1];
|
||||
this.updatePointer(currentPointer);
|
||||
}
|
||||
|
||||
// Apply update to viewOrigin based on panning
|
||||
if (this.panning && !this.panning.paused) {
|
||||
const {velocity} = this.panning;
|
||||
// Convert pointer velocity to sim internal scale
|
||||
if (this.panTouchStart && this.panTouchLatest) {
|
||||
// Direct translate
|
||||
const start = this.panTouchStart;
|
||||
const latest = this.panTouchLatest;
|
||||
this.sim.display.viewOrigin.x -= latest.x - start.x;
|
||||
this.sim.display.viewOrigin.y -= latest.y - start.y;
|
||||
} else if (this.panning && !this.panning.paused) {
|
||||
// Apply update to viewOrigin based on panning
|
||||
const { velocity } = this.panning;
|
||||
this.sim.display.viewOrigin.x -= velocity.x * elapsedTime;
|
||||
this.sim.display.viewOrigin.y -= velocity.y * elapsedTime;
|
||||
}
|
||||
|
||||
@ -1,30 +1,35 @@
|
||||
// Options picker
|
||||
import { Tool } from '../tool.js';
|
||||
import {
|
||||
DISPLAY_ACCELERATION_VECTORS,
|
||||
DISPLAY_VELOCITY_VECTORS,
|
||||
MERGE_ON_COLLIDE,
|
||||
PATH_TRACES_DASHED,
|
||||
PAUSE_DURING_CREATION,
|
||||
PAUSE_DURING_SELECTION,
|
||||
MERGE_ON_COLLIDE,
|
||||
} from '../config.js';
|
||||
import {Tool} from '../tool.js';
|
||||
|
||||
export class Options extends Tool {
|
||||
options = [{
|
||||
type: 'group', name: 'pauseDuring', title: 'Pause During Mass',
|
||||
items: [
|
||||
{ type: 'boolean', name: 'creation', title: 'Creation', default: PAUSE_DURING_CREATION },
|
||||
{ type: 'boolean', name: 'selection', title: 'Selection', default: PAUSE_DURING_SELECTION },
|
||||
]}, {
|
||||
{type: 'boolean', name: 'creation', title: 'Creation', default: PAUSE_DURING_CREATION},
|
||||
{type: 'boolean', name: 'selection', title: 'Selection', default: PAUSE_DURING_SELECTION},
|
||||
]
|
||||
}, {
|
||||
type: 'group', name: 'display', title: 'Display',
|
||||
items: [
|
||||
{ type: 'boolean', name: 'velocity', title: 'Velocity', default: DISPLAY_VELOCITY_VECTORS },
|
||||
{ type: 'boolean', name: 'acceleration', title: 'Acceleration', default: DISPLAY_ACCELERATION_VECTORS },
|
||||
{ type: 'boolean', name: 'traces', title: 'Path Traces', default: DISPLAY_ACCELERATION_VECTORS , wide: true},
|
||||
]}, {
|
||||
{type: 'boolean', name: 'velocity', title: 'Velocity', default: DISPLAY_VELOCITY_VECTORS},
|
||||
{type: 'boolean', name: 'acceleration', title: 'Acceleration', default: DISPLAY_ACCELERATION_VECTORS},
|
||||
{type: 'boolean', name: 'traces', title: 'Traces', default: DISPLAY_ACCELERATION_VECTORS},
|
||||
{type: 'boolean', name: 'dashedTraces', title: 'Dashed', default: PATH_TRACES_DASHED},
|
||||
]
|
||||
}, {
|
||||
type: 'group', name: 'collision', title: 'Collision',
|
||||
items: [
|
||||
{ type: 'boolean', name: 'merge', title: 'Merge Masses', default: MERGE_ON_COLLIDE, wide: true},
|
||||
]},
|
||||
{type: 'boolean', name: 'merge', title: 'Merge Masses', default: MERGE_ON_COLLIDE, wide: true},
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
values = {};
|
||||
@ -52,8 +57,8 @@ export class Options extends Tool {
|
||||
this.setOption(path, item.default);
|
||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
||||
button.addEventListener('click', () => {
|
||||
this.setOption(path, !this.getOption(path));
|
||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
||||
this.setOption(path, !this.getOption(path));
|
||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
||||
});
|
||||
return button;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user