166 lines
5.6 KiB
TypeScript
166 lines
5.6 KiB
TypeScript
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<States> {
|
|
override name: States = States.EDIT_TRACK;
|
|
override validTransitions: Set<States> = new Set([
|
|
States.RUNNING,
|
|
States.PAUSED,
|
|
]);
|
|
|
|
private heldEvents: Map<string | number, (() => void) | undefined> =
|
|
new Map();
|
|
|
|
private currentSegment?: TrackSegment;
|
|
|
|
override update(dt: number): void {
|
|
const inputManager = getContextItem<InputManager>("inputManager");
|
|
const track = getContextItem<TrackSystem>("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>("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>("inputManager");
|
|
this.heldEvents.set("e", inputManager.offKey("e"));
|
|
this.heldEvents.set("Escape", inputManager.offKey("Escape"));
|
|
inputManager.onKey("e", () => {
|
|
const state = getContextItem<StateMachine<States>>("state");
|
|
state.transitionTo(States.RUNNING);
|
|
});
|
|
|
|
const track = getContextItem<TrackSystem>("track");
|
|
setContextItem("trackCopy", track.copy());
|
|
inputManager.onKey("Escape", () => {
|
|
const trackCopy = getContextItem<TrackSystem>("trackCopy");
|
|
setContextItem("track", trackCopy);
|
|
setContextItem("trackCopy", undefined);
|
|
const state = getContextItem<StateMachine<States>>("state");
|
|
state.transitionTo(States.RUNNING);
|
|
});
|
|
|
|
inputManager.onKey("w", () => {
|
|
const track = getContextItem<TrackSystem>("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");
|
|
inputManager.offKey("e");
|
|
inputManager.offKey("Escape");
|
|
if (this.heldEvents.size > 0) {
|
|
for (const [key, cb] of this.heldEvents) {
|
|
if (cb) {
|
|
getContextItem<InputManager>("inputManager").onKey(key, cb);
|
|
}
|
|
this.heldEvents.delete(key);
|
|
}
|
|
}
|
|
setContextItem("trackCopy", undefined);
|
|
}
|
|
}
|