Separates game loop from doodler draw loop. Each state is in charge of registering and unregistering layers

This commit is contained in:
2025-02-13 06:07:42 -07:00
parent 43a5268ed5
commit 3befb69f51
10 changed files with 421 additions and 130 deletions

View File

@@ -22,6 +22,13 @@ export class TrackSystem {
return this.segments.values().toArray().pop();
}
optimize(percent: number) {
console.log("Optimizing track", percent * 100 / 4);
for (const segment of this.segments.values()) {
segment.recalculateRailPoints(Math.round(percent * 100 / 4));
}
}
registerSegment(segment: TrackSegment) {
segment.setTrack(this);
this.segments.set(segment.id, segment);
@@ -147,9 +154,14 @@ export class TrackSystem {
generatePath() {
if (!this.firstSegment) throw new Error("No first segment");
const flags = { looping: true };
const rightOnlyPath = [
this.firstSegment.copy(),
...this.findRightPath(this.firstSegment, new Set([this.firstSegment.id])),
...this.findRightPath(
this.firstSegment,
new Set([this.firstSegment.id]),
flags,
),
];
rightOnlyPath.forEach((s, i, arr) => {
@@ -159,6 +171,14 @@ export class TrackSystem {
s.prev = prev;
prev.next = s;
});
if (flags.looping) {
const first = rightOnlyPath[0];
const last = rightOnlyPath[rightOnlyPath.length - 1];
first.points[0] = last.points[3];
last.points[3] = first.points[0];
first.prev = last;
last.next = first;
}
return new Spline<TrackSegment>(rightOnlyPath);
}
@@ -166,6 +186,7 @@ export class TrackSystem {
*findRightPath(
start: TrackSegment,
seen: Set<string>,
flags: { looping: boolean },
): Generator<TrackSegment> {
if (start.frontNeighbours.length === 0) {
return;
@@ -187,14 +208,20 @@ export class TrackSystem {
rightMost = segment;
}
}
if (seen.has(rightMost.id)) return;
if (seen.has(rightMost.id)) {
if (seen.values().next().value === rightMost.id) {
flags.looping = true;
}
return;
}
seen.add(rightMost.id);
yield rightMost.copy();
yield* this.findRightPath(rightMost, seen);
yield* this.findRightPath(rightMost, seen, flags);
}
*findLeftPath(
start: TrackSegment,
seen: Set<string>,
flags: { looping: boolean },
): Generator<TrackSegment> {
if (start.frontNeighbours.length === 0) {
return;
@@ -216,10 +243,15 @@ export class TrackSystem {
leftMost = segment;
}
}
if (seen.has(leftMost.id)) return;
if (seen.has(leftMost.id)) {
if (seen.values().next().value === leftMost.id) {
flags.looping = true;
}
return;
}
seen.add(leftMost.id);
yield leftMost.copy();
yield* this.findLeftPath(leftMost, seen);
yield* this.findLeftPath(leftMost, seen, flags);
}
}
@@ -232,11 +264,27 @@ export class TrackSegment extends PathSegment {
track?: TrackSystem;
doodler: Doodler;
normalPoints: Vector[] = [];
antiNormalPoints: Vector[] = [];
constructor(p: VectorSet, id?: string) {
super(p);
this.doodler = getContextItem<Doodler>("doodler");
this.id = id ?? crypto.randomUUID();
this.recalculateRailPoints();
}
recalculateRailPoints(resolution = 100) {
this.normalPoints = [];
this.antiNormalPoints = [];
for (let i = 0; i <= resolution; i++) {
const t = i / resolution;
const normal = this.tangent(t).rotate(Math.PI / 2);
normal.setMag(6);
const p = this.getPointAtT(t);
this.normalPoints.push(p.copy().add(normal));
this.antiNormalPoints.push(p.copy().add(normal.rotate(Math.PI)));
}
}
setTrack(t: TrackSystem) {
@@ -299,21 +347,17 @@ export class TrackSegment extends PathSegment {
// });
});
}
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.drawLine(this.normalPoints, {
color: "grey",
weight: 1.5,
});
this.doodler.drawLine(this.antiNormalPoints, {
color: "grey",
weight: 1.5,
});
},
);
// this.doodler.drawCircle(p, 2, {
@@ -438,21 +482,24 @@ export class Spline<T extends PathSegment = PathSegment> {
constructor(segs: T[]) {
this.segments = segs;
if (this.segments.at(-1)?.next === this.segments[0]) {
this.looped = true;
}
this.pointSpacing = 1;
this.evenPoints = this.calculateEvenlySpacedPoints(1);
this.nodes = [];
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],
],
mirrored: false,
tangent: true,
};
this.nodes.push(node);
}
// 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],
// ],
// mirrored: false,
// tangent: true,
// };
// this.nodes.push(node);
// }
}
// setContext(ctx: CanvasRenderingContext2D) {
@@ -464,7 +511,11 @@ export class Spline<T extends PathSegment = PathSegment> {
draw() {
for (const segment of this.segments) {
segment.draw();
// segment.draw();
const doodler = getContextItem<Doodler>("doodler");
doodler.drawWithAlpha(0.5, () => {
doodler.drawBezier(...segment.points, { color: "red" });
});
}
}