import { PathSegment } from "./math/path.ts"; import { Vector } from "doodler"; import { Train } from "./train.ts"; export class Track extends PathSegment { editable = false; next: Track; prev: Track; id: string; constructor(points: [Vector, Vector, Vector, Vector], next?: Track, prev?: Track) { super(points); this.id = crypto.randomUUID(); this.next = next || this; this.prev = prev || this; } followTrack(train: Train): [Vector, number] { const predict = train.velocity.copy(); predict.normalize(); predict.mult(1); const predictpos = Vector.add(train.position, predict) // const leading = train.leadingPoint; // let closest = this.points[0]; // let closestDistance = this.getClosestPoint(leading); let [closest, closestDistance, closestT] = this.getClosestPoint(predictpos); // deno-lint-ignore no-this-alias let mostValid: Track = this; if (this.next !== this) { const [point, distance, t] = this.next.getClosestPoint(predictpos); if (distance < closestDistance) { closest = point; closestDistance = distance; mostValid = this.next; closestT = t; } } if (this.prev !== this) { const [point, distance, t] = this.next.getClosestPoint(predictpos); if (distance < closestDistance) { closest = point; closestDistance = distance; mostValid = this.next; closestT = t; } } train.currentTrack = mostValid; train.arrive(closest); // if (predictpos.dist(closest) > 2) train.arrive(closest); return [closest, closestT]; } getNearestPoint(p: Vector) { let [closest, closestDistance] = this.getClosestPoint(p); if (this.next !== this) { const [point, distance, t] = this.next.getClosestPoint(p); if (distance < closestDistance) { closest = point; closestDistance = distance; } } if (this.prev !== this) { const [point, distance, t] = this.next.getClosestPoint(p); if (distance < closestDistance) { closest = point; closestDistance = distance; } } return closest; } getAllPointsInRange(v: Vector, r: number) { const points: [number, PathSegment][] = this.getPointsWithinRadius(v, r).concat(this.next.getPointsWithinRadius(v, r), this.prev.getPointsWithinRadius(v, r)) return points; } draw(): void { super.draw(); if (this.editable) for (const e of this.points) { e.drawDot(); } } } export class Spline { segments: T[] = []; ctx?: CanvasRenderingContext2D; evenPoints: Vector[]; constructor(segs: T[]) { this.segments = segs; this.evenPoints = this.calculateEvenlySpacedPoints(3); } setContext(ctx: CanvasRenderingContext2D) { this.ctx = ctx; for (const segment of this.segments) { segment.setContext(ctx); } } draw() { for (const segment of this.segments) { segment.draw(); } } calculateEvenlySpacedPoints(spacing: number, resolution = 1) { return this.segments.flatMap(s => s.calculateEvenlySpacedPoints(spacing, resolution)); // const points: Vector[] = [] // points.push(this.segments[0].points[0]); // let prev = points[0]; // let distSinceLastEvenPoint = 0 // for (const seg of this.segments) { // let t = 0; // const div = Math.ceil(seg.length * resolution * 10); // while (t < 1) { // t += 1 / div; // const point = seg.getPointAtT(t); // distSinceLastEvenPoint += prev.dist(point); // if (distSinceLastEvenPoint >= spacing) { // const overshoot = distSinceLastEvenPoint - spacing; // const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot)) // distSinceLastEvenPoint = overshoot; // points.push(evenPoint); // prev = evenPoint; // } // prev = point // } // } // return points; } followEvenPoints(t: number) { const i = Math.floor(t); const a = this.evenPoints[i] const b = this.evenPoints[(i + 1) % this.evenPoints.length] try { return Vector.lerp(a, b, t % 1); } catch { console.log(t, i, a, b); } } } export const generateSquareTrack = () => { const first = new Track([new Vector(20, 40), new Vector(20, 100), new Vector(20, 300), new Vector(20, 360)]); const second = new Track([first.points[3], new Vector(20, 370), new Vector(30, 380), new Vector(40, 380)]); const third = new Track([second.points[3], new Vector(100, 380), new Vector(300, 380), new Vector(360, 380)]); const fourth = new Track([third.points[3], new Vector(370, 380), new Vector(380, 370), new Vector(380, 360)]); const fifth = new Track([fourth.points[3], new Vector(380, 300), new Vector(380, 100), new Vector(380, 40)]); const sixth = new Track([fifth.points[3], new Vector(380, 30), new Vector(370, 20), new Vector(360, 20)]); const seventh = new Track([sixth.points[3], new Vector(300, 20), new Vector(100, 20), new Vector(40, 20)]); const eighth = new Track([seventh.points[3], new Vector(30, 20), new Vector(20, 30), first.points[0]]); const tracks = [first, second, third, fourth, fifth, sixth, seventh, eighth]; for (const [i, track] of tracks.entries()) { track.next = tracks[(i + 1) % tracks.length]; track.prev = tracks.at(i - 1)!; } // first.next = second; // first.prev = eighth; // second.next = third; // second.prev = first; // third. return new Spline([first, second, third, fourth, fifth, sixth, seventh, eighth]); }