import { Doodler, Vector } from "@bearmetal/doodler"; import { getContextItem, setContextItem } from "../../lib/context.ts"; import { InputManager } from "../../lib/input.ts"; import { TrackSystem } from "../../track/system.ts"; import { State, StateMachine } from "../machine.ts"; import { States } from "./index.ts"; import { StraightTrack } from "../../track/shapes.ts"; import { TrackSegment } from "../../track/system.ts"; export class EditTrackState extends State { override name: States = States.EDIT_TRACK; override validTransitions: Set = new Set([ States.RUNNING, States.PAUSED, ]); private heldEvents: Map void) | undefined> = new Map(); private currentSegment?: TrackSegment; override update(dt: number): void { const inputManager = getContextItem("inputManager"); const track = getContextItem("track"); // For moving a segment, i.e. the currently active one // const segment = track.lastSegment; // if (segment) { // const firstPoint = segment.points[0].copy(); // const { x, y } = inputManager.getMouseLocation(); // segment.points.forEach((p, i) => { // const relativePoint = Vector.sub(p, firstPoint); // p.set(x, y); // p.add(relativePoint); // }); // } // manipulate only end of segment while maintaining length // const segment = track.lastSegment; // if (segment) { // const p3 = segment.points[2]; // const p4 = segment.points[3]; // let curveLength = Math.round(segment.calculateApproxLength()); // this.startingLength = this.startingLength ?? curveLength; // curveLength = this.startingLength; // const { x, y } = inputManager.getMouseLocation(); // p4.set(x, y); // const points = segment.calculateEvenlySpacedPoints(1); // if (points.length > curveLength) p4.set(points[curveLength - 1]); // // doodler.fillText(curveLength.toFixed(2), p3.copy().add(10, 0), 100); // } const doodler = getContextItem("doodler"); // Adjust angles until tangent points to mouse const segment = this.currentSegment; if (segment) { segment.propagate(); const mousePos = inputManager.getMouseLocationV(); const p1 = segment.points[0]; const p2 = segment.points[1]; const p3 = segment.points[2]; const p4 = segment.points[3]; const prevp3 = p3.copy(); const dirToMouse = Vector.sub(mousePos, p2).normalize(); const angleToMouse = dirToMouse.heading(); const angleToP1 = Vector.sub(p2, p1).heading(); const p2DistToMouse = Vector.dist(p2, mousePos); const p3DistToMouse = Vector.dist(p3, mousePos); const distToP3 = Vector.dist(p2, p3); const distToP4 = Vector.dist(prevp3, p4); if ( Math.abs(angleToMouse - angleToP1) < .6 && p2DistToMouse > distToP3 && p3DistToMouse > distToP4 ) { { const dirToNewP3 = dirToMouse.copy().rotate( -(angleToMouse - angleToP1) / 2, ); dirToNewP3.setMag(distToP3); p3.set(Vector.add(p2, dirToNewP3)); doodler.line(p2, Vector.add(p2, dirToNewP3), { color: "blue" }); doodler.line(p2, Vector.add(p2, dirToMouse.mult(100)), { color: "red", }); } { const dirToMouse = Vector.sub(mousePos, p3).normalize(); dirToMouse.setMag(distToP4); p4.set(Vector.add(p3, dirToMouse)); doodler.line(p3, Vector.add(p3, dirToMouse), { color: "green" }); } segment.clampLength(); } doodler.fillText( segment.calculateApproxLength().toFixed(2), p2.copy().add(10, 0), 100, ); } track.draw(true); // TODO // Draw ui // Draw track points // Draw track tangents } override start(): void { const inputManager = getContextItem("inputManager"); this.heldEvents.set("e", inputManager.offKey("e")); this.heldEvents.set("Escape", inputManager.offKey("Escape")); inputManager.onKey("e", () => { const state = getContextItem>("state"); state.transitionTo(States.RUNNING); }); const track = getContextItem("track"); setContextItem("trackCopy", track.copy()); inputManager.onKey("Escape", () => { const trackCopy = getContextItem("trackCopy"); setContextItem("track", trackCopy); setContextItem("trackCopy", undefined); const state = getContextItem>("state"); state.transitionTo(States.RUNNING); }); inputManager.onKey("w", () => { const track = getContextItem("track"); const segment = track.lastSegment; if (!segment) return; const n = new StraightTrack(segment.points[3]); const t = segment.tangent(1).heading(); n.rotate(t); segment.frontNeighbours.push(n); track.registerSegment(n); this.currentSegment = n; }); inputManager.onKey("1", () => { this.currentSegment = track.firstSegment; }); this.currentSegment = track.lastSegment; // TODO // Cache trains and save } override stop(): void { const inputManager = getContextItem("inputManager"); inputManager.offKey("e"); inputManager.offKey("Escape"); if (this.heldEvents.size > 0) { for (const [key, cb] of this.heldEvents) { if (cb) { getContextItem("inputManager").onKey(key, cb); } this.heldEvents.delete(key); } } setContextItem("trackCopy", undefined); } }