CARS and also better node editing

This commit is contained in:
Emma
2023-02-13 16:38:58 -07:00
parent 657f228d47
commit 08d63395e3
13 changed files with 832 additions and 710 deletions

155
track.ts
View File

@@ -18,43 +18,43 @@ export class Track extends PathSegment {
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)
// 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;
// // 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;
}
}
// 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];
}
// 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);
@@ -85,10 +85,11 @@ export class Track extends PathSegment {
draw(): void {
super.draw();
if (this.editable)
for (const e of this.points) {
e.drawDot();
}
if (this.editable) {
const [a, b, c, d] = this.points;
doodler.line(a, b);
doodler.line(c, d);
}
}
setNext(t: Track) {
@@ -107,9 +108,28 @@ export class Spline<T extends PathSegment = PathSegment> {
ctx?: CanvasRenderingContext2D;
evenPoints: Vector[];
pointSpacing: number;
get points() {
return Array.from(new Set(this.segments.flatMap(s => s.points)));
}
nodes: IControlNode[];
constructor(segs: T[]) {
this.segments = segs;
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);
}
}
setContext(ctx: CanvasRenderingContext2D) {
@@ -126,6 +146,7 @@ export class Spline<T extends PathSegment = PathSegment> {
}
calculateEvenlySpacedPoints(spacing: number, resolution = 1) {
this.pointSpacing = 1;
// return this.segments.flatMap(s => s.calculateEvenlySpacedPoints(spacing, resolution));
const points: Vector[] = []
@@ -155,20 +176,51 @@ export class Spline<T extends PathSegment = PathSegment> {
}
}
this.evenPoints = points;
return points;
}
followEvenPoints(t: number) {
if (t < 0) t+= this.evenPoints.length
if (t < 0) t += this.evenPoints.length
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);
return Vector.lerp(a, b, t % 1);
}
} catch {
console.log(t, i, a, b);
calculateApproxLength() {
for (const s of this.segments) {
s.calculateApproxLength();
}
}
toggleNodeTangent(p: Vector) {
const node = this.nodes.find(n => n.anchor === p);
node && (node.tangent = !node.tangent);
}
toggleNodeMirrored(p: Vector) {
const node = this.nodes.find(n => n.anchor === p);
node && (node.mirrored = !node.mirrored);
}
handleNodeEdit(p: Vector, movement: { x: number, y: number }) {
const node = this.nodes.find(n => n.anchor === p || n.controls.includes(p));
if (!node || !(node.mirrored || node.tangent)) return;
if (node.anchor !== p) {
if (node.mirrored || node.tangent) {
const mover = node.controls.find(e => e !== p)!;
const v = Vector.sub(node.anchor, p);
if (!node.mirrored) v.setMag(Vector.sub(node.anchor, mover).mag());
mover.set(Vector.add(v, node.anchor));
}
} else {
for (const control of node.controls) {
control.add(movement.x, movement.y)
}
}
}
}
@@ -209,14 +261,21 @@ export const loadFromJson = () => {
if (!json) return generateSquareTrack();
const segments: Track[] = [];
for (const {points} of json.segments) {
segments.push(new Track(points.map((p:{x:number,y:number}) => new Vector(p.x, p.y))));
for (const { points } of json.segments) {
segments.push(new Track(points.map((p: { x: number, y: number }) => new Vector(p.x, p.y))));
}
for (const [i,s] of segments.entries()) {
s.setNext(segments[(i+1)%segments.length])
s.setPrev(segments.at(i-1)!)
for (const [i, s] of segments.entries()) {
s.setNext(segments[(i + 1) % segments.length])
s.setPrev(segments.at(i - 1)!)
}
return new Spline<Track>(segments);
}
export interface IControlNode {
anchor: Vector;
controls: [Vector, Vector];
tangent: boolean;
mirrored: boolean;
}