train following
This commit is contained in:
parent
20e6174658
commit
7b244526b9
@ -14,8 +14,9 @@
|
||||
]
|
||||
},
|
||||
"imports": {
|
||||
"@bearmetal/doodler": "jsr:@bearmetal/doodler@0.0.5-c",
|
||||
"@bearmetal/doodler": "jsr:@bearmetal/doodler@0.0.5-e",
|
||||
"@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.4",
|
||||
"vite": "npm:vite@^6.0.1"
|
||||
}
|
||||
},
|
||||
"nodeModulesDir": "auto"
|
||||
}
|
8
deno.lock
generated
8
deno.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "4",
|
||||
"specifiers": {
|
||||
"jsr:@bearmetal/doodler@0.0.5-c": "0.0.5-c",
|
||||
"jsr:@bearmetal/doodler@0.0.5-e": "0.0.5-e",
|
||||
"jsr:@std/assert@*": "1.0.10",
|
||||
"jsr:@std/assert@^1.0.10": "1.0.10",
|
||||
"jsr:@std/internal@^1.0.5": "1.0.5",
|
||||
@ -13,8 +13,8 @@
|
||||
"npm:web-ext@*": "8.4.0"
|
||||
},
|
||||
"jsr": {
|
||||
"@bearmetal/doodler@0.0.5-c": {
|
||||
"integrity": "34b0db85af1393b1b01622915963a8b33ee923c14b381afe9c771efd3d631cf1"
|
||||
"@bearmetal/doodler@0.0.5-e": {
|
||||
"integrity": "70bd19397deac3b8a2ff6641b5df99bd1881581258c1c9ef3dab1170cf348430"
|
||||
},
|
||||
"@std/assert@1.0.10": {
|
||||
"integrity": "59b5cbac5bd55459a19045d95cc7c2ff787b4f8527c0dd195078ff6f9481fbb3",
|
||||
@ -2103,7 +2103,7 @@
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"jsr:@bearmetal/doodler@0.0.5-c",
|
||||
"jsr:@bearmetal/doodler@0.0.5-e",
|
||||
"npm:@deno/vite-plugin@^1.0.4",
|
||||
"npm:vite@^6.0.1"
|
||||
]
|
||||
|
@ -47,6 +47,7 @@ const _fullDebug: Debug = {
|
||||
car: false,
|
||||
bogies: false,
|
||||
angles: false,
|
||||
aabb: false,
|
||||
};
|
||||
|
||||
const storedDebug = JSON.parse(localStorage.getItem("debug") || "0");
|
||||
|
@ -404,6 +404,7 @@ export class EditTrackState extends State<States> {
|
||||
const inputManager = getContextItem<InputManager>("inputManager");
|
||||
inputManager.offKey("e");
|
||||
inputManager.offKey("w");
|
||||
inputManager.offKey("z");
|
||||
inputManager.offKey("Escape");
|
||||
inputManager.offMouse("left");
|
||||
if (this.heldEvents.size > 0) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Doodler } from "@bearmetal/doodler";
|
||||
import { Doodler, Point, Vector, ZoomableDoodler } from "@bearmetal/doodler";
|
||||
import { getContext, getContextItem } from "../../lib/context.ts";
|
||||
import { InputManager } from "../../lib/input.ts";
|
||||
import { TrackSystem } from "../../track/system.ts";
|
||||
@ -19,8 +19,20 @@ export class RunningState extends State<States> {
|
||||
|
||||
layers: number[] = [];
|
||||
|
||||
activeTrain: Train | undefined;
|
||||
|
||||
override update(dt: number): void {
|
||||
const ctx = getContext() as { trains: Train[]; track: TrackSystem };
|
||||
const doodler = getContextItem<ZoomableDoodler>(
|
||||
"doodler",
|
||||
);
|
||||
if (this.activeTrain) {
|
||||
// (doodler as any).origin = doodler.worldToScreen(
|
||||
// doodler.width - this.activeTrain.aabb.center.x,
|
||||
// doodler.height - this.activeTrain.aabb.center.y,
|
||||
// );
|
||||
doodler.centerCameraOn(this.activeTrain.aabb.center);
|
||||
}
|
||||
// const ctx = getContext() as { trains: DotFollower[]; track: TrackSystem };
|
||||
// TODO
|
||||
// Update trains
|
||||
@ -67,6 +79,7 @@ export class RunningState extends State<States> {
|
||||
new LargeLadyTender(),
|
||||
]);
|
||||
ctx.trains.push(train);
|
||||
this.activeTrain = train;
|
||||
// const trainCount = 1000;
|
||||
// for (let i = 0; i < trainCount; i++) {
|
||||
// const train = new Train(track.path, [new LargeLady(), new Tender()]);
|
||||
|
@ -3,7 +3,7 @@ import { Train, TrainCar } from "./train.ts";
|
||||
import { getContextItem } from "../lib/context.ts";
|
||||
import { ResourceManager } from "../lib/resources.ts";
|
||||
import { debug } from "node:console";
|
||||
import { averageAngles } from "../math/lerp.ts";
|
||||
import { averageAngles, lerpAngle } from "../math/lerp.ts";
|
||||
|
||||
export class LargeLady extends TrainCar {
|
||||
scale = 1;
|
||||
@ -68,8 +68,12 @@ export class LargeLady extends TrainCar {
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
this.leading = 23;
|
||||
}
|
||||
|
||||
drawAngle?: number;
|
||||
|
||||
override draw(): void {
|
||||
const doodler = getContextItem<Doodler>("doodler");
|
||||
for (const b of this.bogies) {
|
||||
@ -95,11 +99,13 @@ export class LargeLady extends TrainCar {
|
||||
if (debug.bogies) return;
|
||||
|
||||
const difAngle = Vector.sub(a.pos, b.pos).heading();
|
||||
if (this.drawAngle == undefined) this.drawAngle = b.angle + Math.PI;
|
||||
const origin = b.pos.copy().add(new Vector(33, 0).rotate(difAngle));
|
||||
const angle = b.angle;
|
||||
const avgAngle = averageAngles(difAngle, angle) + Math.PI;
|
||||
this.drawAngle = lerpAngle(this.drawAngle, avgAngle, .1);
|
||||
|
||||
doodler.drawRotated(origin, avgAngle, () => {
|
||||
doodler.drawRotated(origin, this.drawAngle, () => {
|
||||
this.sprite
|
||||
? doodler.drawSprite(
|
||||
this.img,
|
||||
@ -121,7 +127,7 @@ export class LargeLady extends TrainCar {
|
||||
// doodler.drawCircle(origin, 4, { color: "blue" });
|
||||
|
||||
doodler.deferDrawing(() => {
|
||||
doodler.drawRotated(origin, avgAngle + Math.PI, () => {
|
||||
doodler.drawRotated(origin, this.drawAngle! + Math.PI, () => {
|
||||
doodler.drawSprite(
|
||||
this.img,
|
||||
new Vector(133, 0),
|
||||
@ -146,7 +152,7 @@ export class LargeLadyTender extends TrainCar {
|
||||
height: 23,
|
||||
});
|
||||
|
||||
this.leading = 10;
|
||||
this.leading = 19;
|
||||
}
|
||||
|
||||
override draw(): void {
|
||||
|
@ -4,6 +4,15 @@ import { Spline, TrackSegment, TrackSystem } from "../track/system.ts";
|
||||
import { Debuggable } from "../lib/debuggable.ts";
|
||||
import { lerp, lerpAngle, map } from "../math/lerp.ts";
|
||||
|
||||
type TrainAABB = {
|
||||
pos: Vector;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
center: Vector;
|
||||
};
|
||||
|
||||
export class Train extends Debuggable {
|
||||
nodes: Vector[] = [];
|
||||
|
||||
@ -12,10 +21,12 @@ export class Train extends Debuggable {
|
||||
path: Spline<TrackSegment>;
|
||||
t: number;
|
||||
|
||||
spacing = 5;
|
||||
spacing = 0;
|
||||
|
||||
speed = 0;
|
||||
|
||||
aabb!: TrainAABB;
|
||||
|
||||
get segments() {
|
||||
return Array.from(
|
||||
new Set(this.cars.flatMap((c) => c.segments.values().toArray())),
|
||||
@ -25,7 +36,7 @@ export class Train extends Debuggable {
|
||||
constructor(track: Spline<TrackSegment>, cars: TrainCar[], t = 0) {
|
||||
super("train", "path");
|
||||
this.path = track;
|
||||
this.path.pointSpacing = 5;
|
||||
this.path.pointSpacing = 4;
|
||||
this.cars = cars;
|
||||
this.t = this.cars.reduce((acc, c) => acc + c.length, 0) +
|
||||
(this.cars.length - 1) * this.spacing;
|
||||
@ -51,6 +62,8 @@ export class Train extends Debuggable {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
this.updateAABB();
|
||||
}
|
||||
|
||||
move(dTime: number) {
|
||||
@ -72,10 +85,27 @@ export class Train extends Debuggable {
|
||||
// car.draw();
|
||||
|
||||
currentOffset += car.moveAlongPath(this.t - currentOffset) +
|
||||
this.spacing / this.path.pointSpacing +
|
||||
car.leading / this.path.pointSpacing;
|
||||
(this.spacing / this.path.pointSpacing);
|
||||
}
|
||||
// this.draw();
|
||||
this.updateAABB();
|
||||
}
|
||||
|
||||
updateAABB() {
|
||||
const minX = Math.min(...this.cars.map((c) => c.aabb.x));
|
||||
const maxX = Math.max(...this.cars.map((c) => c.aabb.x + c.aabb.width));
|
||||
const minY = Math.min(...this.cars.map((c) => c.aabb.y));
|
||||
const maxY = Math.max(...this.cars.map((c) => c.aabb.y + c.aabb.height));
|
||||
this.aabb = {
|
||||
pos: new Vector(minX, minY),
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
center: new Vector(minX, minY).add(
|
||||
new Vector(maxX - minX, maxY - minY).div(2),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// draw() {
|
||||
@ -133,6 +163,16 @@ export class Train extends Debuggable {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (debug.aabb) {
|
||||
doodler.deferDrawing(() => {
|
||||
doodler.drawRect(this.aabb.pos, this.aabb.width, this.aabb.height, {
|
||||
color: "orange",
|
||||
});
|
||||
doodler.drawCircle(this.aabb.center, 2, {
|
||||
color: "lime",
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
real2Track(length: number) {
|
||||
@ -164,6 +204,8 @@ export class TrainCar extends Debuggable {
|
||||
|
||||
train?: Train;
|
||||
|
||||
aabb!: TrainAABB;
|
||||
|
||||
constructor(
|
||||
length: number,
|
||||
trailing: number,
|
||||
@ -191,6 +233,8 @@ export class TrainCar extends Debuggable {
|
||||
length: trailing,
|
||||
},
|
||||
];
|
||||
|
||||
this.updateAABB();
|
||||
}
|
||||
|
||||
get length() {
|
||||
@ -209,6 +253,59 @@ export class TrainCar extends Debuggable {
|
||||
}
|
||||
}
|
||||
|
||||
updateAABB() {
|
||||
let minX = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let minY = Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
this.bogies.forEach((bogie, index) => {
|
||||
// Unit vector in the direction the bogie is facing.
|
||||
const u = new Vector(Math.cos(bogie.angle), Math.sin(bogie.angle));
|
||||
// Perpendicular vector (to thicken the rectangle).
|
||||
const v = new Vector(-Math.sin(bogie.angle), Math.cos(bogie.angle));
|
||||
|
||||
// For the first bogie, extend in the opposite direction by this.leading.
|
||||
let front = bogie.pos.copy();
|
||||
if (index === 0) {
|
||||
front = front.sub(u.copy().rotate(Math.PI).mult(this.leading));
|
||||
}
|
||||
// Rear point is at bogie.pos plus the bogie length.
|
||||
const rear = bogie.pos.copy().add(
|
||||
u.copy().rotate(Math.PI).mult(bogie.length),
|
||||
);
|
||||
|
||||
// Calculate half the height to offset from the center line.
|
||||
const halfHeight = this.imgHeight / 2;
|
||||
|
||||
// Calculate the four corners of the rectangle.
|
||||
const corners = [
|
||||
front.copy().add(v.copy().mult(halfHeight)),
|
||||
front.copy().add(v.copy().mult(-halfHeight)),
|
||||
rear.copy().add(v.copy().mult(halfHeight)),
|
||||
rear.copy().add(v.copy().mult(-halfHeight)),
|
||||
];
|
||||
|
||||
// Update the overall AABB limits.
|
||||
corners.forEach((corner) => {
|
||||
minX = Math.min(minX, corner.x);
|
||||
minY = Math.min(minY, corner.y);
|
||||
maxX = Math.max(maxX, corner.x);
|
||||
maxY = Math.max(maxY, corner.y);
|
||||
});
|
||||
});
|
||||
this.aabb = {
|
||||
pos: new Vector(minX, minY),
|
||||
center: new Vector(minX, minY).add(
|
||||
new Vector(maxX - minX, maxY - minY).div(2),
|
||||
),
|
||||
x: minX,
|
||||
y: minY,
|
||||
width: maxX - minX,
|
||||
height: maxY - minY,
|
||||
};
|
||||
}
|
||||
|
||||
moveAlongPath(t: number, initial = false): number {
|
||||
if (!this.train) return 0;
|
||||
let offset = this.leading / this.train.path.pointSpacing;
|
||||
@ -224,6 +321,7 @@ export class TrainCar extends Debuggable {
|
||||
}
|
||||
this.segments.add(a.segmentId);
|
||||
}
|
||||
this.updateAABB();
|
||||
return offset;
|
||||
}
|
||||
|
||||
@ -294,6 +392,26 @@ export class TrainCar extends Debuggable {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (debug.aabb) {
|
||||
doodler.deferDrawing(() => {
|
||||
doodler.drawRect(this.aabb.pos, this.aabb.width, this.aabb.height, {
|
||||
color: "white",
|
||||
weight: .5,
|
||||
});
|
||||
doodler.drawCircle(this.aabb.center, 2, {
|
||||
color: "yellow",
|
||||
});
|
||||
doodler.fillText(
|
||||
this.aabb.width.toFixed(1).toString(),
|
||||
this.aabb.center.copy().add(10, 10),
|
||||
100,
|
||||
{
|
||||
color: "white",
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (debug.angles) {
|
||||
|
@ -23,5 +23,6 @@ declare global {
|
||||
path: boolean;
|
||||
bogies: boolean;
|
||||
angles: boolean;
|
||||
aabb: boolean;
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user