loading saved track layouts
This commit is contained in:
parent
9b03e6c2cb
commit
657f228d47
95
bundle.js
95
bundle.js
@ -414,16 +414,25 @@ class Doodler {
|
|||||||
}
|
}
|
||||||
this.draggables = this.draggables.filter((d)=>d.point !== point);
|
this.draggables = this.draggables.filter((d)=>d.point !== point);
|
||||||
}
|
}
|
||||||
|
addDragEvents({ onDragEnd , onDragStart , point }) {
|
||||||
|
const d = this.draggables.find((d)=>d.point === point);
|
||||||
|
if (d) {
|
||||||
|
d.onDragEnd = onDragEnd;
|
||||||
|
d.onDragStart = onDragStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
for (const d of this.draggables){
|
for (const d of this.draggables){
|
||||||
if (d.point.dist(new Vector(this.mouseX, this.mouseY)) <= d.radius) {
|
if (d.point.dist(new Vector(this.mouseX, this.mouseY)) <= d.radius) {
|
||||||
d.beingDragged = true;
|
d.beingDragged = true;
|
||||||
|
d.onDragStart?.call(null);
|
||||||
} else d.beingDragged = false;
|
} else d.beingDragged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offClick(e) {
|
offClick(e) {
|
||||||
for (const d of this.draggables){
|
for (const d of this.draggables){
|
||||||
d.beingDragged = false;
|
d.beingDragged = false;
|
||||||
|
d.onDragEnd?.call(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uiElements = new Map();
|
uiElements = new Map();
|
||||||
@ -948,6 +957,14 @@ class Track extends PathSegment {
|
|||||||
e.drawDot();
|
e.drawDot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setNext(t) {
|
||||||
|
this.next = t;
|
||||||
|
this.next.points[0] = this.points[3];
|
||||||
|
}
|
||||||
|
setPrev(t) {
|
||||||
|
this.prev = t;
|
||||||
|
this.prev.points[3] = this.points[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class Spline {
|
class Spline {
|
||||||
segments = [];
|
segments = [];
|
||||||
@ -955,7 +972,7 @@ class Spline {
|
|||||||
evenPoints;
|
evenPoints;
|
||||||
constructor(segs){
|
constructor(segs){
|
||||||
this.segments = segs;
|
this.segments = segs;
|
||||||
this.evenPoints = this.calculateEvenlySpacedPoints(3);
|
this.evenPoints = this.calculateEvenlySpacedPoints(1);
|
||||||
}
|
}
|
||||||
setContext(ctx) {
|
setContext(ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
@ -969,9 +986,31 @@ class Spline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
calculateEvenlySpacedPoints(spacing, resolution = 1) {
|
calculateEvenlySpacedPoints(spacing, resolution = 1) {
|
||||||
return this.segments.flatMap((s)=>s.calculateEvenlySpacedPoints(spacing, resolution));
|
const points = [];
|
||||||
|
points.push(this.segments[0].points[0]);
|
||||||
|
let prev = points[0];
|
||||||
|
let distSinceLastEvenPoint = 0;
|
||||||
|
for (const seg of this.segments){
|
||||||
|
let t = 0;
|
||||||
|
const div = Math.ceil(seg.length * resolution * 10);
|
||||||
|
while(t < 1){
|
||||||
|
t += 1 / div;
|
||||||
|
const point = seg.getPointAtT(t);
|
||||||
|
distSinceLastEvenPoint += prev.dist(point);
|
||||||
|
if (distSinceLastEvenPoint >= spacing) {
|
||||||
|
const overshoot = distSinceLastEvenPoint - spacing;
|
||||||
|
const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot));
|
||||||
|
distSinceLastEvenPoint = overshoot;
|
||||||
|
points.push(evenPoint);
|
||||||
|
prev = evenPoint;
|
||||||
|
}
|
||||||
|
prev = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return points;
|
||||||
}
|
}
|
||||||
followEvenPoints(t) {
|
followEvenPoints(t) {
|
||||||
|
if (t < 0) t += this.evenPoints.length;
|
||||||
const i = Math.floor(t);
|
const i = Math.floor(t);
|
||||||
const a = this.evenPoints[i];
|
const a = this.evenPoints[i];
|
||||||
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
|
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
|
||||||
@ -1056,39 +1095,61 @@ const generateSquareTrack = ()=>{
|
|||||||
eighth
|
eighth
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
const loadFromJson = ()=>{
|
||||||
|
const json = JSON.parse(localStorage.getItem('railPath') || '');
|
||||||
|
if (!json) return generateSquareTrack();
|
||||||
|
const segments = [];
|
||||||
|
for (const { points } of json.segments){
|
||||||
|
segments.push(new Track(points.map((p)=>new Vector(p.x, p.y))));
|
||||||
|
}
|
||||||
|
for (const [i, s] of segments.entries()){
|
||||||
|
s.setNext(segments[(i + 1) % segments.length]);
|
||||||
|
s.setPrev(segments.at(i - 1));
|
||||||
|
}
|
||||||
|
return new Spline(segments);
|
||||||
|
};
|
||||||
init({
|
init({
|
||||||
width: 400,
|
width: 400,
|
||||||
height: 400,
|
height: 400,
|
||||||
bg: '#333'
|
bg: '#333'
|
||||||
});
|
});
|
||||||
const path = generateSquareTrack();
|
const path = loadFromJson();
|
||||||
let t = 0;
|
let t = 0;
|
||||||
const trains = Array(1).fill(null).map((_, i)=>new Train(path.segments[i % path.segments.length], 5));
|
let speed = 1;
|
||||||
|
Array(1).fill(null).map((_, i)=>new Train(path.segments[i % path.segments.length], 5));
|
||||||
doodler.createLayer(()=>{
|
doodler.createLayer(()=>{
|
||||||
path.draw();
|
path.draw();
|
||||||
for (const p of path.evenPoints){
|
const points = Array(5).fill(null).map((_, i)=>path.followEvenPoints(t - i * 15));
|
||||||
p.drawDot();
|
for (const point of points){
|
||||||
|
point && doodler.drawCircle(point, 5, {
|
||||||
|
strokeColor: 'green'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const point = path.followEvenPoints(t);
|
t = (t + speed / 2) % path.evenPoints.length;
|
||||||
point && doodler.drawCircle(point, 5, {
|
|
||||||
strokeColor: 'green'
|
|
||||||
});
|
|
||||||
t = (t + 1 / 3) % path.evenPoints.length;
|
|
||||||
});
|
});
|
||||||
document.addEventListener('keyup', (e)=>{
|
document.addEventListener('keyup', (e)=>{
|
||||||
if (e.key === 'd') {}
|
if (e.key === 'd') {}
|
||||||
if (e.key === 'ArrowUp') {}
|
if (e.key === 'ArrowUp') {
|
||||||
|
speed += .1;
|
||||||
|
}
|
||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
for (const train of trains){
|
speed -= .1;
|
||||||
train.speed -= .1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (e.key === 'e') {
|
if (e.key === 'e') {
|
||||||
for (const t of path.segments){
|
for (const t of path.segments){
|
||||||
t.editable = !t.editable;
|
t.editable = !t.editable;
|
||||||
for (const p of t.points){
|
for (const p of t.points){
|
||||||
if (t.editable) doodler.registerDraggable(p, 10);
|
if (t.editable) {
|
||||||
else doodler.unregisterDraggable(p);
|
doodler.registerDraggable(p, 10);
|
||||||
|
doodler.addDragEvents({
|
||||||
|
point: p,
|
||||||
|
onDragEnd: ()=>{
|
||||||
|
console.log('dragend');
|
||||||
|
t.length = t.calculateApproxLength(100);
|
||||||
|
path.evenPoints = path.calculateEvenlySpacedPoints(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else doodler.unregisterDraggable(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@
|
|||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"drawing": "./drawing/index.ts",
|
"drawing": "./drawing/index.ts",
|
||||||
"doodler": "https://git.cyborggrizzly.com/emma/doodler/raw/tag/0.0.3a/mod.ts"
|
"doodler": "https://git.cyborggrizzly.com/emma/doodler/raw/tag/0.0.4a/mod.ts"
|
||||||
}
|
}
|
||||||
}
|
}
|
44
main.ts
44
main.ts
@ -3,7 +3,7 @@ import { ComplexPath, PathSegment } from "./math/path.ts";
|
|||||||
import { Mover } from "./physics/mover.ts";
|
import { Mover } from "./physics/mover.ts";
|
||||||
import { Train } from "./train.ts";
|
import { Train } from "./train.ts";
|
||||||
import { fillCircle, drawCircle } from 'drawing';
|
import { fillCircle, drawCircle } from 'drawing';
|
||||||
import { generateSquareTrack } from "./track.ts";
|
import { generateSquareTrack, loadFromJson } from "./track.ts";
|
||||||
import { drawLine } from "./drawing/line.ts";
|
import { drawLine } from "./drawing/line.ts";
|
||||||
import { initializeDoodler, Vector } from 'doodler';
|
import { initializeDoodler, Vector } from 'doodler';
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ initializeDoodler({
|
|||||||
bg: '#333'
|
bg: '#333'
|
||||||
});
|
});
|
||||||
|
|
||||||
const path = generateSquareTrack();
|
const path = loadFromJson();
|
||||||
|
|
||||||
const controls = {
|
const controls = {
|
||||||
ArrowUp: false,
|
ArrowUp: false,
|
||||||
@ -31,7 +31,7 @@ const controls = {
|
|||||||
|
|
||||||
let t = 0;
|
let t = 0;
|
||||||
let currentSeg = 0;
|
let currentSeg = 0;
|
||||||
const speed = 1;
|
let speed = 1;
|
||||||
|
|
||||||
const trainCount = 1;
|
const trainCount = 1;
|
||||||
const trains = Array(trainCount).fill(null).map((_, i) => new Train(path.segments[i % path.segments.length], 5));
|
const trains = Array(trainCount).fill(null).map((_, i) => new Train(path.segments[i % path.segments.length], 5));
|
||||||
@ -50,17 +50,22 @@ doodler.createLayer(() => {
|
|||||||
// const tan = seg.tangent(t).normalize().mult(25);
|
// const tan = seg.tangent(t).normalize().mult(25);
|
||||||
// const tan = seg.tangent(t);
|
// const tan = seg.tangent(t);
|
||||||
|
|
||||||
for (const p of path.evenPoints) {
|
// for (const p of path.evenPoints) {
|
||||||
p.drawDot();
|
// p.drawDot();
|
||||||
}
|
// }
|
||||||
// doodler.line(start, new Vector(start.x + tan.x, start.y + tan.y), {color: 'blue'});
|
// doodler.line(start, new Vector(start.x + tan.x, start.y + tan.y), {color: 'blue'});
|
||||||
// doodler.fillCircle(start, 5, {fillColor: 'blue'})
|
// doodler.fillCircle(start, 5, {fillColor: 'blue'})
|
||||||
|
|
||||||
const point = path.followEvenPoints(t);
|
const points = Array(5).fill(null).map((_,i) => path.followEvenPoints(t - (i * 15)))
|
||||||
point &&
|
for (const point of points) {
|
||||||
doodler.drawCircle(point, 5, {strokeColor: 'green'})
|
point &&
|
||||||
|
doodler.drawCircle(point, 5, { strokeColor: 'green' })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// const point = path.followEvenPoints(t);
|
||||||
|
|
||||||
t = (t + (1 / 3)) % path.evenPoints.length;
|
t = (t + (speed / 2)) % path.evenPoints.length;
|
||||||
|
|
||||||
// path.segments.forEach(s => s.calculateApproxLength(10000))
|
// path.segments.forEach(s => s.calculateApproxLength(10000))
|
||||||
})
|
})
|
||||||
@ -76,19 +81,30 @@ document.addEventListener('keyup', e => {
|
|||||||
// for (const train of trains) {
|
// for (const train of trains) {
|
||||||
// train.speed += .1;
|
// train.speed += .1;
|
||||||
// }
|
// }
|
||||||
|
speed += .1
|
||||||
}
|
}
|
||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
for (const train of trains) {
|
// for (const train of trains) {
|
||||||
train.speed -= .1;
|
// train.speed -= .1;
|
||||||
}
|
// }
|
||||||
|
speed -= .1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === 'e') {
|
if (e.key === 'e') {
|
||||||
for (const t of path.segments) {
|
for (const t of path.segments) {
|
||||||
t.editable = !t.editable;
|
t.editable = !t.editable;
|
||||||
for (const p of t.points) {
|
for (const p of t.points) {
|
||||||
if (t.editable)
|
if (t.editable) {
|
||||||
doodler.registerDraggable(p, 10)
|
doodler.registerDraggable(p, 10)
|
||||||
|
doodler.addDragEvents({
|
||||||
|
point: p,
|
||||||
|
onDragEnd: () => {
|
||||||
|
console.log('dragend');
|
||||||
|
t.length = t.calculateApproxLength(100)
|
||||||
|
path.evenPoints = path.calculateEvenlySpacedPoints(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
else
|
else
|
||||||
doodler.unregisterDraggable(p)
|
doodler.unregisterDraggable(p)
|
||||||
}
|
}
|
||||||
|
76
track.ts
76
track.ts
@ -90,6 +90,16 @@ export class Track extends PathSegment {
|
|||||||
e.drawDot();
|
e.drawDot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setNext(t: Track) {
|
||||||
|
this.next = t;
|
||||||
|
this.next.points[0] = this.points[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrev(t: Track) {
|
||||||
|
this.prev = t;
|
||||||
|
this.prev.points[3] = this.points[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Spline<T extends PathSegment = PathSegment> {
|
export class Spline<T extends PathSegment = PathSegment> {
|
||||||
@ -99,7 +109,7 @@ export class Spline<T extends PathSegment = PathSegment> {
|
|||||||
evenPoints: Vector[];
|
evenPoints: Vector[];
|
||||||
constructor(segs: T[]) {
|
constructor(segs: T[]) {
|
||||||
this.segments = segs;
|
this.segments = segs;
|
||||||
this.evenPoints = this.calculateEvenlySpacedPoints(3);
|
this.evenPoints = this.calculateEvenlySpacedPoints(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext(ctx: CanvasRenderingContext2D) {
|
setContext(ctx: CanvasRenderingContext2D) {
|
||||||
@ -116,39 +126,40 @@ export class Spline<T extends PathSegment = PathSegment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculateEvenlySpacedPoints(spacing: number, resolution = 1) {
|
calculateEvenlySpacedPoints(spacing: number, resolution = 1) {
|
||||||
return this.segments.flatMap(s => s.calculateEvenlySpacedPoints(spacing, resolution));
|
// return this.segments.flatMap(s => s.calculateEvenlySpacedPoints(spacing, resolution));
|
||||||
// const points: Vector[] = []
|
const points: Vector[] = []
|
||||||
|
|
||||||
// points.push(this.segments[0].points[0]);
|
points.push(this.segments[0].points[0]);
|
||||||
// let prev = points[0];
|
let prev = points[0];
|
||||||
// let distSinceLastEvenPoint = 0
|
let distSinceLastEvenPoint = 0
|
||||||
// for (const seg of this.segments) {
|
for (const seg of this.segments) {
|
||||||
|
|
||||||
// let t = 0;
|
let t = 0;
|
||||||
|
|
||||||
// const div = Math.ceil(seg.length * resolution * 10);
|
const div = Math.ceil(seg.length * resolution * 10);
|
||||||
// while (t < 1) {
|
while (t < 1) {
|
||||||
// t += 1 / div;
|
t += 1 / div;
|
||||||
// const point = seg.getPointAtT(t);
|
const point = seg.getPointAtT(t);
|
||||||
// distSinceLastEvenPoint += prev.dist(point);
|
distSinceLastEvenPoint += prev.dist(point);
|
||||||
|
|
||||||
|
|
||||||
// if (distSinceLastEvenPoint >= spacing) {
|
if (distSinceLastEvenPoint >= spacing) {
|
||||||
// const overshoot = distSinceLastEvenPoint - spacing;
|
const overshoot = distSinceLastEvenPoint - spacing;
|
||||||
// const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot))
|
const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot))
|
||||||
// distSinceLastEvenPoint = overshoot;
|
distSinceLastEvenPoint = overshoot;
|
||||||
// points.push(evenPoint);
|
points.push(evenPoint);
|
||||||
// prev = evenPoint;
|
prev = evenPoint;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// prev = point
|
prev = point
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
followEvenPoints(t: number) {
|
followEvenPoints(t: number) {
|
||||||
|
if (t < 0) t+= this.evenPoints.length
|
||||||
const i = Math.floor(t);
|
const i = Math.floor(t);
|
||||||
const a = this.evenPoints[i]
|
const a = this.evenPoints[i]
|
||||||
const b = this.evenPoints[(i + 1) % this.evenPoints.length]
|
const b = this.evenPoints[(i + 1) % this.evenPoints.length]
|
||||||
@ -192,3 +203,20 @@ export const generateSquareTrack = () => {
|
|||||||
|
|
||||||
return new Spline<Track>([first, second, third, fourth, fifth, sixth, seventh, eighth]);
|
return new Spline<Track>([first, second, third, fourth, fifth, sixth, seventh, eighth]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const loadFromJson = () => {
|
||||||
|
const json = JSON.parse(localStorage.getItem('railPath') || '');
|
||||||
|
if (!json) return generateSquareTrack();
|
||||||
|
const segments: Track[] = [];
|
||||||
|
|
||||||
|
for (const {points} of json.segments) {
|
||||||
|
segments.push(new Track(points.map((p:{x:number,y:number}) => new Vector(p.x, p.y))));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [i,s] of segments.entries()) {
|
||||||
|
s.setNext(segments[(i+1)%segments.length])
|
||||||
|
s.setPrev(segments.at(i-1)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Spline<Track>(segments);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user