track drawing and shape tweaks, train controls, fps counter, non-looping

This commit is contained in:
2025-02-13 03:23:37 -07:00
parent e3194e45ff
commit 43a5268ed5
12 changed files with 631 additions and 128 deletions

View File

@@ -13,20 +13,26 @@ export class StraightTrack extends TrackSegment {
}
}
export class SBendLeft extends StraightTrack {
export class SBendLeft extends TrackSegment {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, -25);
this.points[3].add(0, -25);
super([
start,
start.copy().add(60, 0),
start.copy().add(90, -25),
start.copy().add(150, -25),
]);
}
}
export class SBendRight extends StraightTrack {
export class SBendRight extends TrackSegment {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, 25);
this.points[3].add(0, 25);
super([
start,
start.copy().add(60, 0),
start.copy().add(90, 25),
start.copy().add(150, 25),
]);
}
}

View File

@@ -1,6 +1,7 @@
import { Doodler, Point, Vector } from "@bearmetal/doodler";
import { ComplexPath, PathSegment } from "../math/path.ts";
import { getContextItem, setDefaultContext } from "../lib/context.ts";
import { clamp } from "../math/clamp.ts";
export class TrackSystem {
private segments: Map<string, TrackSegment> = new Map();
@@ -34,7 +35,7 @@ export class TrackSystem {
}
draw(showControls = false) {
for (const segment of this.segments.values()) {
for (const [i, segment] of this.segments.entries()) {
segment.draw(showControls);
}
@@ -232,8 +233,6 @@ export class TrackSegment extends PathSegment {
doodler: Doodler;
id: string;
constructor(p: VectorSet, id?: string) {
super(p);
this.doodler = getContextItem<Doodler>("doodler");
@@ -245,29 +244,82 @@ export class TrackSegment extends PathSegment {
}
override draw(showControls = false) {
this.doodler.drawBezier(
this.points[0],
this.points[1],
this.points[2],
this.points[3],
{
strokeColor: "#ffffff50",
},
);
// if (showControls) {
// this.doodler.drawBezier(
// this.points[0],
// this.points[1],
// this.points[2],
// this.points[3],
// {
// strokeColor: "#ffffff50",
// },
// );
// }
if (showControls) {
this.doodler.fillCircle(this.points[0], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[1], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[2], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[3], 1, {
color: "red",
this.doodler.deferDrawing(() => {
this.doodler.fillCircle(this.points[0], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[1], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[2], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[3], 1, {
color: "red",
});
});
}
const ties = Math.ceil(this.length / 10);
for (let i = 0; i < ties; i++) {
const t = i / ties;
const p = this.getPointAtT(t);
// this.doodler.drawCircle(p, 2, {
// color: "red",
// weight: 3,
// });
this.doodler.drawRotated(p, this.tangent(t).heading(), () => {
this.doodler.line(p, p.copy().add(0, 10), {
color: "#291b17",
weight: 4,
});
this.doodler.line(p, p.copy().add(0, -10), {
color: "#291b17",
weight: 4,
});
// this.doodler.line(p.copy().add(-6, 5), p.copy().add(6, 5), {
// color: "grey",
// weight: 1,
// });
// this.doodler.line(p.copy().add(-6, -5), p.copy().add(6, -5), {
// color: "grey",
// weight: 1,
// });
});
}
const lineResolution = 100;
const normalPoints: Vector[] = [];
const antiNormalPoints: Vector[] = [];
for (let i = 0; i <= lineResolution; i++) {
const t = i / lineResolution;
const normal = this.tangent(t).rotate(Math.PI / 2);
normal.setMag(6);
const p = this.getPointAtT(t);
normalPoints.push(p.copy().add(normal));
antiNormalPoints.push(p.copy().add(normal.rotate(Math.PI)));
}
this.doodler.deferDrawing(
() => {
this.doodler.drawLine(normalPoints, { color: "grey" });
this.doodler.drawLine(antiNormalPoints, { color: "grey" });
},
);
// this.doodler.drawCircle(p, 2, {
// color: "red",
// weight: 3,
// });
}
serialize(): SerializedTrackSegment {
@@ -382,6 +434,8 @@ export class Spline<T extends PathSegment = PathSegment> {
nodes: IControlNode[];
looped = false;
constructor(segs: T[]) {
this.segments = segs;
this.pointSpacing = 1;
@@ -452,10 +506,20 @@ export class Spline<T extends PathSegment = PathSegment> {
}
followEvenPoints(t: number) {
if (t < 0) t += this.evenPoints.length;
const i = Math.floor(t) % this.evenPoints.length;
const a = this.evenPoints[i];
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
if (this.looped) {
if (t < 0) t += this.evenPoints.length;
const i = Math.floor(t) % this.evenPoints.length;
const a = this.evenPoints[i];
const b = this.evenPoints[(i + 1) % this.evenPoints.length];
return Vector.lerp(a, b, t % 1);
}
t = clamp(t, 0, this.evenPoints.length - 1);
const i = clamp(Math.floor(t), 0, this.evenPoints.length - 1);
const a = this.evenPoints[clamp(i, 0, this.evenPoints.length - 1)];
const b = this
.evenPoints[
clamp((i + 1) % this.evenPoints.length, 0, this.evenPoints.length - 1)
];
return Vector.lerp(a, b, t % 1);
}