trainsim/train.ts
2025-02-02 23:29:40 -07:00

146 lines
3.7 KiB
TypeScript

import { drawLine } from "./drawing/line.ts";
import { ComplexPath, PathSegment } from "./math/path.ts";
import { Vector } from "doodler";
import { Follower } from "./physics/follower.ts";
import { Mover } from "./physics/mover.ts";
import { Spline, Track } from "./track.ts";
export class Train {
nodes: Vector[] = [];
cars: TrainCar[] = [];
path: Spline<Track>;
t: number;
engineLength = 40;
spacing = 30;
speed = 0;
constructor(track: Spline<Track>, cars: TrainCar[] = []) {
this.path = track;
this.t = 0;
this.nodes.push(this.path.followEvenPoints(this.t));
this.nodes.push(this.path.followEvenPoints(this.t - this.real2Track(40)));
const engineSprites = document.getElementById(
"engine-sprites",
)! as HTMLImageElement;
this.cars.push(
new TrainCar(
55,
engineSprites,
80,
20,
{ at: new Vector(0, 60), width: 80, height: 20 },
),
new TrainCar(
25,
engineSprites,
40,
20,
{ at: new Vector(80, 0), width: 40, height: 20 },
),
);
this.cars[0].points = this.nodes.map((n) => n) as [Vector, Vector];
this.cars[1].points = this.nodes.map((n) => n) as [Vector, Vector];
let currentOffset = 40;
for (const car of cars) {
currentOffset += this.spacing;
const a = this.path.followEvenPoints(this.t - currentOffset);
currentOffset += car.length;
const b = this.path.followEvenPoints(this.t - currentOffset);
car.points = [a, b];
this.cars.push(car);
}
}
move(dTime: number) {
this.t = (this.t + this.speed * dTime * 10) % this.path.evenPoints.length;
// console.log(this.t);
let currentOffset = 0;
for (const car of this.cars) {
if (!car.points) return;
const [a, b] = car.points;
a.set(this.path.followEvenPoints(this.t - currentOffset));
currentOffset += car.length;
b.set(this.path.followEvenPoints(this.t - currentOffset));
currentOffset += this.spacing;
car.draw();
}
// this.draw();
}
// draw() {
// for (const [i, node] of this.nodes.entries()) {
// doodler.drawCircle(node.point, 10, { color: 'purple', weight: 3 })
// // const next = this.nodes[i + 1];
// // if (next) {
// // const to = Vector.sub(node.point, next.point);
// // to.setMag(40);
// // doodler.line(next.point, Vector.add(to, next.point))
// // }
// }
// }
real2Track(length: number) {
return length / this.path.pointSpacing;
}
}
export class TrainCar {
img: HTMLImageElement;
imgWidth: number;
imgHeight: number;
sprite?: ISprite;
points?: [Vector, Vector];
length: number;
constructor(
length: number,
img: HTMLImageElement,
w: number,
h: number,
sprite?: ISprite,
) {
this.img = img;
this.sprite = sprite;
this.imgWidth = w;
this.imgHeight = h;
this.length = length;
}
draw() {
if (!this.points) return;
const [a, b] = this.points;
const origin = Vector.add(Vector.sub(a, b).div(2), b);
const angle = Vector.sub(b, a).heading();
doodler.drawCircle(origin, 4, { color: "blue" });
doodler.drawRotated(origin, angle, () => {
this.sprite
? doodler.drawSprite(
this.img,
this.sprite.at,
this.sprite.width,
this.sprite.height,
origin.copy().sub(this.imgWidth / 2, this.imgHeight / 2),
this.imgWidth,
this.imgHeight,
)
: doodler.drawImage(
this.img,
origin.copy().sub(this.imgWidth / 2, this.imgHeight / 2),
);
});
}
}
interface ISprite {
at: Vector;
width: number;
height: number;
}