diff --git a/bundle.js b/bundle.js index 3fc58ae..a7afec6 100644 --- a/bundle.js +++ b/bundle.js @@ -1376,7 +1376,6 @@ fillScreen: true, bg: "#333" }, true); - var doodler2 = window.doodler; var path; try { path = loadFromJson(); @@ -1396,7 +1395,7 @@ var train = new Train(path, cars); var dragEndCounter = 0; var selectedNode; - doodler2.createLayer((_1, _2, _3) => { + doodler.createLayer((_1, _2, _3) => { _1.imageSmoothingEnabled = false; const dTime = (_3 < 0 ? 1 : _3) / 1e3; for (let i = 0; i < path.evenPoints.length; i += 10) { @@ -1405,14 +1404,14 @@ const last = path.evenPoints.at(i - 1); if (!last) break; const tan = Vector.sub(last, next); - doodler2.drawRotated(p, tan.heading(), () => { - doodler2.line(p, p.copy().add(0, 10), { color: "#291b17", weight: 4 }); - doodler2.line(p, p.copy().add(0, -10), { color: "#291b17", weight: 4 }); - doodler2.line(p.copy().add(-6, 5), p.copy().add(6, 5), { + doodler.drawRotated(p, tan.heading(), () => { + doodler.line(p, p.copy().add(0, 10), { color: "#291b17", weight: 4 }); + doodler.line(p, p.copy().add(0, -10), { color: "#291b17", weight: 4 }); + doodler.line(p.copy().add(-6, 5), p.copy().add(6, 5), { color: "grey", weight: 2 }); - doodler2.line(p.copy().add(-6, -5), p.copy().add(6, -5), { + doodler.line(p.copy().add(-6, -5), p.copy().add(6, -5), { color: "grey", weight: 2 }); @@ -1459,8 +1458,8 @@ t.editable = !t.editable; for (const p of t.points) { if (t.editable) { - doodler2.registerDraggable(p, 10); - doodler2.addDragEvents({ + doodler.registerDraggable(p, 10); + doodler.addDragEvents({ point: p, onDragEnd: () => { dragEndCounter++; @@ -1472,7 +1471,7 @@ } }); } else { - doodler2.unregisterDraggable(p); + doodler.unregisterDraggable(p); } } } @@ -1485,14 +1484,14 @@ ); }; clickables.set(p, onClick); - doodler2.registerClickable( + doodler.registerClickable( p.copy().sub(10, 10), p.copy().add(10, 10), onClick ); } else { const the = clickables.get(p); - doodler2.unregisterClickable(the); + doodler.unregisterClickable(the); } } } diff --git a/lib/context.ts b/lib/context.ts new file mode 100644 index 0000000..115d374 --- /dev/null +++ b/lib/context.ts @@ -0,0 +1,34 @@ +type ContextStore = Record; + +const contextStack: ContextStore[] = []; +const defaultContext: ContextStore = {}; + +export function setDefaultContext(context: ContextStore) { + Object.assign(defaultContext, context); +} + +export function withContext(context: ContextStore, fn: () => T): T { + contextStack.push(context); + try { + return fn(); + } finally { + contextStack.pop(); + } +} + +export const ctx = new Proxy( + {}, + { + get(_, prop: string) { + for (let i = contextStack.length - 1; i >= 0; i--) { + if (prop in contextStack[i]) return contextStack[i][prop]; + } + if (prop in defaultContext) return defaultContext[prop]; // ✅ Fallback to default + throw new Error(`Context variable '${prop}' is not defined.`); + }, + }, +) as Record; + +export function getContext() { + return ctx; +} diff --git a/lib/input.ts b/lib/input.ts new file mode 100644 index 0000000..f4b4a92 --- /dev/null +++ b/lib/input.ts @@ -0,0 +1,41 @@ +export class InputManager { + private keyStates: Map = new Map(); + private mouseStates: Map = new Map(); + private mouseLocation: { x: number; y: number } = { x: 0, y: 0 }; + private mouseDelta: { x: number; y: number } = { x: 0, y: 0 }; + constructor() { + document.addEventListener("keydown", (e) => { + this.keyStates.set(e.key, true); + }); + document.addEventListener("keyup", (e) => { + this.keyStates.set(e.key, false); + }); + document.addEventListener("mousedown", (e) => { + this.mouseStates.set(e.button, true); + }); + document.addEventListener("mouseup", (e) => { + this.mouseStates.set(e.button, false); + }); + + self.addEventListener("mousemove", (e) => { + this.mouseLocation = { x: e.clientX, y: e.clientY }; + this.mouseDelta = { + x: e.movementX, + y: e.movementY, + }; + }); + } + + getKeyState(key: string | number) { + return this.keyStates.get(key); + } + getMouseState(key: string | number) { + return this.mouseStates.get(key); + } + getMouseLocation() { + return this.mouseLocation; + } + getMouseDelta() { + return this.mouseDelta; + } +} diff --git a/main.ts b/main.ts index 1c00e42..e69de29 100644 --- a/main.ts +++ b/main.ts @@ -1,284 +0,0 @@ -import { lerp } from "./math/lerp.ts"; -import { ComplexPath, PathSegment } from "./math/path.ts"; -import { Mover } from "./physics/mover.ts"; -import { Train, TrainCar } from "./train.ts"; -import { drawCircle, fillCircle } from "drawing"; -import { generateSquareTrack, IControlNode, loadFromJson } from "./track.ts"; -import { drawLine } from "./drawing/line.ts"; -import { Doodler, initializeDoodler, Vector } from "doodler"; - -const engineSprites = document.createElement("img"); -engineSprites.src = "./sprites/EngineSprites.png"; -engineSprites.style.display = "none"; -engineSprites.id = "engine-sprites"; -document.body.append(engineSprites); - -initializeDoodler({ - fillScreen: true, - bg: "#333", -}, true); -const doodler = window.doodler as Doodler; - -let path; - -try { - path = loadFromJson(); -} catch { - path = generateSquareTrack(); -} - -const controls = { - ArrowUp: false, - ArrowRight: false, - ArrowDown: false, - ArrowLeft: false, -}; - -let t = 0; -let currentSeg = 0; -let speed = 1; - -// const trainCount = 1; -// const trains = Array(trainCount).fill(null).map((_, i) => new Train(path.segments[i % path.segments.length], 5)); -// const car = new TrainCar(55, engineSprites, 80, 20, { -// at: new Vector(0, 80), -// height: 20, -// width: 80, -// }); -const length = Math.floor(Math.random() * 7); -const cars = Array.from( - { length }, - () => - new TrainCar(40, engineSprites, 61, 20, { - at: new Vector(80, 20 * Math.ceil(Math.random() * 3)), - width: 61, - height: 20, - }), -); -const train = new Train(path, cars); - -let dragEndCounter = 0; -let selectedNode: IControlNode | undefined; - -doodler.createLayer((_1, _2, _3) => { - // console.log(_1, _2, _3); - _1.imageSmoothingEnabled = false; - const dTime = (_3 < 0 ? 1 : _3) / 1000; - // console.log(dTime); - for (let i = 0; i < path.evenPoints.length; i += 10) { - const p = path.evenPoints[i]; - const next = path.evenPoints[(i + 1) % path.evenPoints.length]; - const last = path.evenPoints.at(i - 1); - if (!last) break; - const tan = Vector.sub(last, next); - - doodler.drawRotated(p, tan.heading(), () => { - doodler.line(p, p.copy().add(0, 10), { color: "#291b17", weight: 4 }); - doodler.line(p, p.copy().add(0, -10), { color: "#291b17", weight: 4 }); - doodler.line(p.copy().add(-6, 5), p.copy().add(6, 5), { - color: "grey", - weight: 2, - }); - doodler.line(p.copy().add(-6, -5), p.copy().add(6, -5), { - color: "grey", - weight: 2, - }); - }); - } - path.draw(); - train.move(dTime); - - selectedNode?.anchor.drawDot(); - selectedNode?.controls.forEach((e) => e.drawDot()); -}); - -let editable = false; - -const clickables = new Map(); - -let selectedPoint: Vector; - -document.addEventListener("keyup", (e) => { - if (e.key === "d") { - // console.log(trains) - // console.log(path.segments.reduce((a,b) => a + b.calculateApproxLength(1000), 0)) - // console.log(path.evenPoints); - } - - if (e.key === "ArrowUp") { - // for (const train of trains) { - // train.speed += .1; - // } - speed += .1; - train.speed += 1; - } - if (e.key === "ArrowDown") { - // for (const train of trains) { - // train.speed -= .1; - // } - speed -= .1; - train.speed -= 1; - } - - if (e.key === "m" && selectedPoint) { - const points = path.points; - const index = points.findIndex((p) => p === selectedPoint); - if (index > -1) { - const prev = points.at(index - 1)!; - const next = points[(index + 1) % points.length]; - - const toPrev = Vector.sub(prev, selectedPoint); - toPrev.setMag(next.dist(selectedPoint)); - toPrev.rotate(Math.PI); - const toNext = Vector.add(toPrev, selectedPoint); - next.set(toNext); - - path.calculateApproxLength(); - path.calculateEvenlySpacedPoints(1); - } - } - let translate: boolean = false; - - if (e.key === "e" && !translate) { - editable = !editable; - for (const t of path.segments) { - t.editable = !t.editable; - for (const p of t.points) { - if (t.editable) { - doodler.registerDraggable(p, 10); - doodler.addDragEvents({ - point: p, - onDragEnd: () => { - dragEndCounter++; - t.length = t.calculateApproxLength(100); - path.evenPoints = path.calculateEvenlySpacedPoints(1); - }, - onDrag: (movement) => { - // todo - remove ! after updating doodler - path.handleNodeEdit(p, movement!); - }, - }); - } else { - doodler.unregisterDraggable(p); - } - } - } - for (const p of path.points) { - if (editable) { - const onClick = () => { - selectedPoint = p; - selectedNode = path.nodes.find((e) => - e.anchor === p || e.controls.includes(p) - ); - }; - - clickables.set(p, onClick); - doodler.registerClickable( - p.copy().sub(10, 10), - p.copy().add(10, 10), - onClick, - ); - } else { - const the = clickables.get(p); - doodler.unregisterClickable(the); - } - } - } - - let x = 0; - let y = 0; - const onDrag = (e: MouseEvent) => { - x += e.movementX; - y += e.movementY; - console.log("draggin"); - }; - const dragEnd = () => { - x = 0; - y = 0; - for (const t of path.points) { - t.add(x, y); - } - }; - if (e.key === "t" && editable) { - // translate = !translate; - - // console.log(translate); - - for (const t of path.points) { - t.add(100, 100); - } - path.calculateEvenlySpacedPoints(1); - - // switch (translate) { - // case true: - // console.log("adding"); - // ((doodler as any)._canvas as HTMLCanvasElement).addEventListener( - // "drag", - // onDrag, - // ); - // ((doodler as any)._canvas as HTMLCanvasElement).addEventListener( - // "dragend", - // dragEnd, - // ); - // break; - // case false: - // ((doodler as any)._canvas as HTMLCanvasElement).removeEventListener( - // "drag", - // onDrag, - // ); - // ((doodler as any)._canvas as HTMLCanvasElement).removeEventListener( - // "dragend", - // dragEnd, - // ); - // break; - // } - } -}); - -// document.addEventListener('keydown', e => { -// const valid = ["ArrowUp", -// "ArrowRight", -// "ArrowDown", -// "ArrowLeft",] -// if (valid.includes(e.key)) -// controls[e.key as keyof typeof controls] = true; -// }) -// document.addEventListener('keyup', e => { -// const valid = ["ArrowUp", -// "ArrowRight", -// "ArrowDown", -// "ArrowLeft",] -// if (valid.includes(e.key)) -// controls[e.key as keyof typeof controls] = false; -// }) - -// function getSteeringForce(mover: Mover, dir: string) { -// const dirs = { -// ArrowUp: 0, -// ArrowRight: .1 * Math.PI, -// ArrowDown: Math.PI, -// ArrowLeft: -.1 * Math.PI, -// } - -// const target = mover.velocity.copy(); -// target.normalize(); -// target.mult(10); -// target.rotate(dirs[dir as keyof typeof dirs]); -// const force = Vector.sub(target, mover.velocity); -// force.limit(.1) -// return force; -// } - -document.addEventListener("keydown", (e) => { - if (e.key === "s") { - e.preventDefault(); - path.segments.forEach((s: any) => { - s.next = s.next.id; - s.prev = s.prev.id; - delete s.ctx; - }); - delete path.ctx; - const json = JSON.stringify(path); - localStorage.setItem("railPath", json); - } -}); diff --git a/prototype.ts b/prototype.ts new file mode 100644 index 0000000..81603f8 --- /dev/null +++ b/prototype.ts @@ -0,0 +1,282 @@ +import { lerp } from "./math/lerp.ts"; +import { ComplexPath, PathSegment } from "./math/path.ts"; +import { Mover } from "./physics/mover.ts"; +import { Train, TrainCar } from "./train/train.ts"; +import { generateSquareTrack, IControlNode, loadFromJson } from "./track.ts"; +import { drawLine } from "./drawing/line.ts"; +import { initializeDoodler, Vector } from "doodler"; + +const engineSprites = document.createElement("img"); +engineSprites.src = "./sprites/EngineSprites.png"; +engineSprites.style.display = "none"; +engineSprites.id = "engine-sprites"; +document.body.append(engineSprites); + +initializeDoodler({ + fillScreen: true, + bg: "#333", +}, true); + +let path; + +try { + path = loadFromJson(); +} catch { + path = generateSquareTrack(); +} + +const controls = { + ArrowUp: false, + ArrowRight: false, + ArrowDown: false, + ArrowLeft: false, +}; + +let t = 0; +let currentSeg = 0; +let speed = 1; + +// const trainCount = 1; +// const trains = Array(trainCount).fill(null).map((_, i) => new Train(path.segments[i % path.segments.length], 5)); +// const car = new TrainCar(55, engineSprites, 80, 20, { +// at: new Vector(0, 80), +// height: 20, +// width: 80, +// }); +const length = Math.floor(Math.random() * 7); +const cars = Array.from( + { length }, + () => + new TrainCar(40, engineSprites, 61, 20, { + at: new Vector(80, 20 * Math.ceil(Math.random() * 3)), + width: 61, + height: 20, + }), +); +const train = new Train(path, cars); + +let dragEndCounter = 0; +let selectedNode: IControlNode | undefined; + +doodler.createLayer((_1, _2, _3) => { + // console.log(_1, _2, _3); + _1.imageSmoothingEnabled = false; + const dTime = (_3 < 0 ? 1 : _3) / 1000; + // console.log(dTime); + for (let i = 0; i < path.evenPoints.length; i += 10) { + const p = path.evenPoints[i]; + const next = path.evenPoints[(i + 1) % path.evenPoints.length]; + const last = path.evenPoints.at(i - 1); + if (!last) break; + const tan = Vector.sub(last, next); + + doodler.drawRotated(p, tan.heading(), () => { + doodler.line(p, p.copy().add(0, 10), { color: "#291b17", weight: 4 }); + doodler.line(p, p.copy().add(0, -10), { color: "#291b17", weight: 4 }); + doodler.line(p.copy().add(-6, 5), p.copy().add(6, 5), { + color: "grey", + weight: 2, + }); + doodler.line(p.copy().add(-6, -5), p.copy().add(6, -5), { + color: "grey", + weight: 2, + }); + }); + } + path.draw(); + train.move(dTime); + + selectedNode?.anchor.drawDot(); + selectedNode?.controls.forEach((e) => e.drawDot()); +}); + +let editable = false; + +const clickables = new Map(); + +let selectedPoint: Vector; + +document.addEventListener("keyup", (e) => { + if (e.key === "d") { + // console.log(trains) + // console.log(path.segments.reduce((a,b) => a + b.calculateApproxLength(1000), 0)) + // console.log(path.evenPoints); + } + + if (e.key === "ArrowUp") { + // for (const train of trains) { + // train.speed += .1; + // } + speed += .1; + train.speed += 1; + } + if (e.key === "ArrowDown") { + // for (const train of trains) { + // train.speed -= .1; + // } + speed -= .1; + train.speed -= 1; + } + + if (e.key === "m" && selectedPoint) { + const points = path.points; + const index = points.findIndex((p) => p === selectedPoint); + if (index > -1) { + const prev = points.at(index - 1)!; + const next = points[(index + 1) % points.length]; + + const toPrev = Vector.sub(prev, selectedPoint); + toPrev.setMag(next.dist(selectedPoint)); + toPrev.rotate(Math.PI); + const toNext = Vector.add(toPrev, selectedPoint); + next.set(toNext); + + path.calculateApproxLength(); + path.calculateEvenlySpacedPoints(1); + } + } + let translate: boolean = false; + + if (e.key === "e" && !translate) { + editable = !editable; + for (const t of path.segments) { + t.editable = !t.editable; + for (const p of t.points) { + if (t.editable) { + doodler.registerDraggable(p, 10); + doodler.addDragEvents({ + point: p, + onDragEnd: () => { + dragEndCounter++; + t.length = t.calculateApproxLength(100); + path.evenPoints = path.calculateEvenlySpacedPoints(1); + }, + onDrag: (movement) => { + // todo - remove ! after updating doodler + path.handleNodeEdit(p, movement!); + }, + }); + } else { + doodler.unregisterDraggable(p); + } + } + } + for (const p of path.points) { + if (editable) { + const onClick = () => { + selectedPoint = p; + selectedNode = path.nodes.find((e) => + e.anchor === p || e.controls.includes(p) + ); + }; + + clickables.set(p, onClick); + doodler.registerClickable( + p.copy().sub(10, 10), + p.copy().add(10, 10), + onClick, + ); + } else { + const the = clickables.get(p); + doodler.unregisterClickable(the); + } + } + } + + let x = 0; + let y = 0; + const onDrag = (e: MouseEvent) => { + x += e.movementX; + y += e.movementY; + console.log("draggin"); + }; + const dragEnd = () => { + x = 0; + y = 0; + for (const t of path.points) { + t.add(x, y); + } + }; + if (e.key === "t" && editable) { + // translate = !translate; + + // console.log(translate); + + for (const t of path.points) { + t.add(100, 100); + } + path.calculateEvenlySpacedPoints(1); + + // switch (translate) { + // case true: + // console.log("adding"); + // ((doodler as any)._canvas as HTMLCanvasElement).addEventListener( + // "drag", + // onDrag, + // ); + // ((doodler as any)._canvas as HTMLCanvasElement).addEventListener( + // "dragend", + // dragEnd, + // ); + // break; + // case false: + // ((doodler as any)._canvas as HTMLCanvasElement).removeEventListener( + // "drag", + // onDrag, + // ); + // ((doodler as any)._canvas as HTMLCanvasElement).removeEventListener( + // "dragend", + // dragEnd, + // ); + // break; + // } + } +}); + +// document.addEventListener('keydown', e => { +// const valid = ["ArrowUp", +// "ArrowRight", +// "ArrowDown", +// "ArrowLeft",] +// if (valid.includes(e.key)) +// controls[e.key as keyof typeof controls] = true; +// }) +// document.addEventListener('keyup', e => { +// const valid = ["ArrowUp", +// "ArrowRight", +// "ArrowDown", +// "ArrowLeft",] +// if (valid.includes(e.key)) +// controls[e.key as keyof typeof controls] = false; +// }) + +// function getSteeringForce(mover: Mover, dir: string) { +// const dirs = { +// ArrowUp: 0, +// ArrowRight: .1 * Math.PI, +// ArrowDown: Math.PI, +// ArrowLeft: -.1 * Math.PI, +// } + +// const target = mover.velocity.copy(); +// target.normalize(); +// target.mult(10); +// target.rotate(dirs[dir as keyof typeof dirs]); +// const force = Vector.sub(target, mover.velocity); +// force.limit(.1) +// return force; +// } + +document.addEventListener("keydown", (e) => { + if (e.key === "s") { + e.preventDefault(); + path.segments.forEach((s: any) => { + s.next = s.next.id; + s.prev = s.prev.id; + delete s.ctx; + }); + delete path.ctx; + const json = JSON.stringify(path); + localStorage.setItem("railPath", json); + } +}); diff --git a/track.ts b/track.ts index 3370956..bbcb3e7 100644 --- a/track.ts +++ b/track.ts @@ -1,9 +1,8 @@ import { PathSegment } from "./math/path.ts"; import { Vector } from "doodler"; -import { Train } from "./train.ts"; +import { Train } from "./train/train.ts"; export class Track extends PathSegment { - editable = false; next: Track; @@ -11,7 +10,11 @@ export class Track extends PathSegment { id: string; - constructor(points: [Vector, Vector, Vector, Vector], next?: Track, prev?: Track) { + constructor( + points: [Vector, Vector, Vector, Vector], + next?: Track, + prev?: Track, + ) { super(points); this.id = crypto.randomUUID(); this.next = next || this; @@ -78,7 +81,11 @@ export class Track extends PathSegment { } getAllPointsInRange(v: Vector, r: number) { - const points: [number, PathSegment][] = this.getPointsWithinRadius(v, r).concat(this.next.getPointsWithinRadius(v, r), this.prev.getPointsWithinRadius(v, r)) + const points: [number, PathSegment][] = this.getPointsWithinRadius(v, r) + .concat( + this.next.getPointsWithinRadius(v, r), + this.prev.getPointsWithinRadius(v, r), + ); return points; } @@ -111,7 +118,7 @@ export class Spline { pointSpacing: number; get points() { - return Array.from(new Set(this.segments.flatMap(s => s.points))); + return Array.from(new Set(this.segments.flatMap((s) => s.points))); } nodes: IControlNode[]; @@ -124,10 +131,13 @@ export class Spline { for (let i = 0; i < this.points.length; i += 3) { const node: IControlNode = { anchor: this.points[i], - controls: [this.points.at(i - 1)!, this.points[(i + 1) % this.points.length]], + controls: [ + this.points.at(i - 1)!, + this.points[(i + 1) % this.points.length], + ], mirrored: false, - tangent: true - } + tangent: true, + }; this.nodes.push(node); } } @@ -148,13 +158,12 @@ export class Spline { calculateEvenlySpacedPoints(spacing: number, resolution = 1) { this.pointSpacing = 1; // return this.segments.flatMap(s => s.calculateEvenlySpacedPoints(spacing, resolution)); - const points: Vector[] = [] + const points: Vector[] = []; points.push(this.segments[0].points[0]); let prev = points[0]; - let distSinceLastEvenPoint = 0 + let distSinceLastEvenPoint = 0; for (const seg of this.segments) { - let t = 0; const div = Math.ceil(seg.length * resolution * 10); @@ -163,16 +172,18 @@ export class Spline { 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)) + const evenPoint = Vector.add( + point, + Vector.sub(point, prev).normalize().mult(overshoot), + ); distSinceLastEvenPoint = overshoot; points.push(evenPoint); prev = evenPoint; } - prev = point + prev = point; } } @@ -182,10 +193,10 @@ export class Spline { } followEvenPoints(t: number) { - if (t < 0) t += this.evenPoints.length + if (t < 0) t += this.evenPoints.length; const i = Math.floor(t); - const a = this.evenPoints[i] - const b = this.evenPoints[(i + 1) % this.evenPoints.length] + const a = this.evenPoints[i]; + const b = this.evenPoints[(i + 1) % this.evenPoints.length]; return Vector.lerp(a, b, t % 1); } @@ -197,50 +208,92 @@ export class Spline { } toggleNodeTangent(p: Vector) { - const node = this.nodes.find(n => n.anchor === p); + const node = this.nodes.find((n) => n.anchor === p); node && (node.tangent = !node.tangent); } toggleNodeMirrored(p: Vector) { - const node = this.nodes.find(n => n.anchor === p); + const node = this.nodes.find((n) => n.anchor === p); node && (node.mirrored = !node.mirrored); } - handleNodeEdit(p: Vector, movement: { x: number, y: number }) { - const node = this.nodes.find(n => n.anchor === p || n.controls.includes(p)); + handleNodeEdit(p: Vector, movement: { x: number; y: number }) { + const node = this.nodes.find((n) => + n.anchor === p || n.controls.includes(p) + ); if (!node || !(node.mirrored || node.tangent)) return; if (node.anchor !== p) { if (node.mirrored || node.tangent) { - const mover = node.controls.find(e => e !== p)!; + const mover = node.controls.find((e) => e !== p)!; const v = Vector.sub(node.anchor, p); if (!node.mirrored) v.setMag(Vector.sub(node.anchor, mover).mag()); mover.set(Vector.add(v, node.anchor)); } } else { for (const control of node.controls) { - control.add(movement.x, movement.y) + control.add(movement.x, movement.y); } } } } export const generateSquareTrack = () => { - const first = new Track([new Vector(20, 40), new Vector(20, 100), new Vector(20, 300), new Vector(20, 360)]); + const first = new Track([ + new Vector(20, 40), + new Vector(20, 100), + new Vector(20, 300), + new Vector(20, 360), + ]); - const second = new Track([first.points[3], new Vector(20, 370), new Vector(30, 380), new Vector(40, 380)]); + const second = new Track([ + first.points[3], + new Vector(20, 370), + new Vector(30, 380), + new Vector(40, 380), + ]); - const third = new Track([second.points[3], new Vector(100, 380), new Vector(300, 380), new Vector(360, 380)]); + const third = new Track([ + second.points[3], + new Vector(100, 380), + new Vector(300, 380), + new Vector(360, 380), + ]); - const fourth = new Track([third.points[3], new Vector(370, 380), new Vector(380, 370), new Vector(380, 360)]); + const fourth = new Track([ + third.points[3], + new Vector(370, 380), + new Vector(380, 370), + new Vector(380, 360), + ]); - const fifth = new Track([fourth.points[3], new Vector(380, 300), new Vector(380, 100), new Vector(380, 40)]); + const fifth = new Track([ + fourth.points[3], + new Vector(380, 300), + new Vector(380, 100), + new Vector(380, 40), + ]); - const sixth = new Track([fifth.points[3], new Vector(380, 30), new Vector(370, 20), new Vector(360, 20)]); + const sixth = new Track([ + fifth.points[3], + new Vector(380, 30), + new Vector(370, 20), + new Vector(360, 20), + ]); - const seventh = new Track([sixth.points[3], new Vector(300, 20), new Vector(100, 20), new Vector(40, 20)]); + const seventh = new Track([ + sixth.points[3], + new Vector(300, 20), + new Vector(100, 20), + new Vector(40, 20), + ]); - const eighth = new Track([seventh.points[3], new Vector(30, 20), new Vector(20, 30), first.points[0]]); + const eighth = new Track([ + seventh.points[3], + new Vector(30, 20), + new Vector(20, 30), + first.points[0], + ]); const tracks = [first, second, third, fourth, fifth, sixth, seventh, eighth]; for (const [i, track] of tracks.entries()) { @@ -253,25 +306,38 @@ export const generateSquareTrack = () => { // second.prev = first; // third. - return new Spline([first, second, third, fourth, fifth, sixth, seventh, eighth]); -} + return new Spline([ + first, + second, + third, + fourth, + fifth, + sixth, + seventh, + eighth, + ]); +}; export const loadFromJson = () => { - const json = JSON.parse(localStorage.getItem('railPath') || ''); + 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)))); + 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)!) + s.setNext(segments[(i + 1) % segments.length]); + s.setPrev(segments.at(i - 1)!); } return new Spline(segments); -} +}; export interface IControlNode { anchor: Vector; diff --git a/train.ts b/train/train.ts similarity index 93% rename from train.ts rename to train/train.ts index ed53e37..cd09e9e 100644 --- a/train.ts +++ b/train/train.ts @@ -1,9 +1,9 @@ -import { drawLine } from "./drawing/line.ts"; -import { ComplexPath, PathSegment } from "./math/path.ts"; +import { drawLine } from "../drawing/line.ts"; +import { ComplexPath, PathSegment } from "../math/path.ts"; import { Vector } from "doodler"; -import { Follower } from "./physics/follower.ts"; -import { Mover } from "./physics/mover.ts"; -import { Spline, Track } from "./track.ts"; +import { Follower } from "../physics/follower.ts"; +import { Mover } from "../physics/mover.ts"; +import { Spline, Track } from "../track.ts"; export class Train { nodes: Vector[] = [];