trainsim/track/system.ts
2025-02-05 04:00:40 -07:00

125 lines
2.9 KiB
TypeScript

import { Doodler, Vector } from "@bearmetal/doodler";
import { PathSegment } from "../math/path.ts";
import { getContextItem, setDefaultContext } from "../lib/context.ts";
export class TrackSystem {
private segments: Map<string, TrackSegment> = new Map();
private doodler: Doodler;
constructor(segments: TrackSegment[]) {
this.doodler = getContextItem<Doodler>("doodler");
for (const segment of segments) {
this.segments.set(segment.id, segment);
}
}
draw() {
for (const segment of this.segments.values()) {
segment.draw();
}
try {
if (getContextItem<boolean>("showEnds")) {
const ends = this.findEnds();
for (const end of ends) {
this.doodler.fillCircle(end.pos, 2, {
color: "red",
// weight: 3,
});
if (getContextItem<boolean>("debug")) {
this.doodler.line(
end.pos,
end.pos.copy().add(end.tangent.copy().mult(20)),
{
color: "blue",
// weight: 3,
},
);
}
}
}
} catch {
setDefaultContext({ showEnds: false });
}
}
findEnds() {
const ends: { pos: Vector; segment: TrackSegment; tangent: Vector }[] = [];
for (const segment of this.segments.values()) {
const [a, b, c, d] = segment.points;
{
const tangent = Vector.sub(a, b).normalize();
const pos = a.copy();
ends.push({ pos, segment, tangent });
}
{
const tangent = Vector.sub(d, c).normalize();
const pos = d.copy();
ends.push({ pos, segment, tangent });
}
}
return ends;
}
serialize() {
return this.segments.values().map((s) => s.serialize()).toArray();
}
static deserialize(data: any) {
const track = new TrackSystem([]);
for (const segment of data) {
track.segments.set(segment.id, TrackSegment.deserialize(segment));
}
return track;
}
}
export class TrackSegment extends PathSegment {
frontNeighbours: TrackSegment[] = [];
backNeighbours: TrackSegment[] = [];
track?: TrackSystem;
doodler: Doodler;
id: string;
constructor(p: [Vector, Vector, Vector, Vector], id?: string) {
super(p);
this.doodler = getContextItem<Doodler>("doodler");
this.id = id ?? crypto.randomUUID();
}
setTrack(t: TrackSystem) {
this.track = t;
}
draw() {
this.doodler.drawBezier(
this.points[0],
this.points[1],
this.points[2],
this.points[3],
{
strokeColor: "#ffffff50",
},
);
}
serialize() {
return {
p: this.points.map((p) => p.array()),
id: this.id,
bNeighbors: this.backNeighbours.map((n) => n.id),
fNeighbors: this.frontNeighbours.map((n) => n.id),
};
}
static deserialize(data: any) {
return new TrackSegment(
data.p.map((p: [number, number, number]) => new Vector(p[0], p[1], p[2])),
data.id,
);
}
}