pick and place editing working, saving and loading working

This commit is contained in:
2025-02-09 02:54:17 -07:00
parent 8dc0af650f
commit 3d4596f8fb
13 changed files with 879 additions and 309 deletions

View File

@@ -12,3 +12,37 @@ export class StraightTrack extends TrackSegment {
]);
}
}
export class SBendLeft extends StraightTrack {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, -25);
this.points[3].add(0, -25);
}
}
export class SBendRight extends StraightTrack {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, 25);
this.points[3].add(0, 25);
}
}
export class BankLeft extends StraightTrack {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, -25);
this.points[3].add(0, 25);
}
}
export class BankRight extends StraightTrack {
constructor(start?: Vector) {
start = start || new Vector(100, 100);
super(start);
this.points[2].add(0, 25);
this.points[3].add(0, -25);
}
}

View File

@@ -1,4 +1,4 @@
import { Doodler, Vector } from "@bearmetal/doodler";
import { Doodler, Point, Vector } from "@bearmetal/doodler";
import { PathSegment } from "../math/path.ts";
import { getContextItem, setDefaultContext } from "../lib/context.ts";
@@ -25,57 +25,74 @@ export class TrackSystem {
segment.setTrack(this);
this.segments.set(segment.id, segment);
}
unregisterSegment(segment: TrackSegment) {
this.segments.delete(segment.id);
for (const s of this.segments.values()) {
s.backNeighbours = s.backNeighbours.filter((n) => n !== segment);
s.frontNeighbours = s.frontNeighbours.filter((n) => n !== segment);
}
}
draw(showControls = false) {
for (const segment of this.segments.values()) {
segment.draw(showControls);
}
try {
if (getContextItem<boolean>("showEnds")) {
const ends = this.findEnds();
for (const end of ends) {
this.doodler.fillCircle(end.pos, 2, {
color: "red",
// weight: 3,
});
if (getContextItem<boolean>("debug")) {
this.doodler.line(
end.pos,
end.pos.copy().add(end.tangent.copy().mult(20)),
{
color: "blue",
// weight: 3,
},
);
}
}
}
} catch {
setDefaultContext({ showEnds: false });
}
// try {
// if (getContextItem<boolean>("showEnds")) {
// const ends = this.findEnds();
// for (const end of ends) {
// this.doodler.fillCircle(end.pos, 2, {
// color: "red",
// // weight: 3,
// });
// if (getContextItem<boolean>("debug")) {
// this.doodler.line(
// end.pos,
// end.pos.copy().add(end.tangent.copy().mult(20)),
// {
// color: "blue",
// // weight: 3,
// },
// );
// }
// }
// }
// } catch {
// setDefaultContext({ showEnds: false });
// }
}
ends: Map<TrackSegment, [End, End]> = new Map();
endArray: End[] = [];
findEnds() {
const ends: { pos: Vector; segment: TrackSegment; tangent: Vector }[] = [];
for (const segment of this.segments.values()) {
const [a, b, c, d] = segment.points;
{
const tangent = Vector.sub(a, b).normalize();
const pos = a.copy();
ends.push({ pos, segment, tangent });
}
{
const tangent = Vector.sub(d, c).normalize();
const pos = d.copy();
ends.push({ pos, segment, tangent });
}
if (this.ends.has(segment)) continue;
const ends: [End, End] = [
{
pos: segment.points[0],
segment,
tangent: Vector.sub(segment.points[1], segment.points[0]).normalize(),
frontOrBack: "back",
},
{
pos: segment.points[3],
segment,
tangent: Vector.sub(segment.points[3], segment.points[2]).normalize(),
frontOrBack: "front",
},
];
this.ends.set(segment, ends);
this.endArray.push(...ends);
}
return ends;
return this.endArray;
}
serialize() {
return this.segments.values().map((s) => s.serialize()).toArray();
return JSON.stringify(
this.segments.values().map((s) => s.serialize()).toArray(),
);
}
copy() {
@@ -107,6 +124,12 @@ export class TrackSystem {
}
return track;
}
translate(v: Vector) {
for (const segment of this.segments.values()) {
segment.translate(v);
}
}
}
type VectorSet = [Vector, Vector, Vector, Vector];
@@ -142,22 +165,18 @@ export class TrackSegment extends PathSegment {
},
);
if (showControls) {
// this.doodler.drawCircle(this.points[0], 4, {
// color: "red",
// weight: 3,
// });
this.doodler.drawCircle(this.points[1], 4, {
this.doodler.fillCircle(this.points[0], 1, {
color: "red",
weight: 3,
});
this.doodler.drawCircle(this.points[2], 4, {
this.doodler.fillCircle(this.points[1], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[2], 1, {
color: "red",
});
this.doodler.fillCircle(this.points[3], 1, {
color: "red",
weight: 3,
});
// this.doodler.drawCircle(this.points[3], 4, {
// color: "red",
// weight: 3,
// });
}
}
@@ -177,30 +196,26 @@ export class TrackSegment extends PathSegment {
);
}
propagate() {
const [_, __, p3, p4] = this.points;
const tangent = Vector.sub(p4, p3);
cleanCopy() {
return new TrackSegment(
this.points.map((p) => p.copy()) as VectorSet,
);
}
propagateTranslation(v: Vector) {
for (const fNeighbour of this.frontNeighbours) {
fNeighbour.receivePropagation(tangent);
fNeighbour.receivePropagation(v);
}
for (const bNeighbour of this.backNeighbours) {
bNeighbour.receivePropagation(v);
}
}
lastHeading?: number;
receivePropagation(tangent: Vector) {
const [p1, p2, p3, p4] = this.points;
// const angle = tangent.heading() - (this.lastHeading ?? 0);
// this.lastHeading = tangent.heading();
// const newP2 = Vector.add(p1, tangent);
// const p2ToP3 = Vector.sub(p3, p2);
// p2ToP3.rotate(angle);
// p3.set(Vector.add(newP2, p2ToP3));
// const p2Top4 = Vector.sub(p4, p2);
// p2Top4.rotate(angle);
// p4.set(Vector.add(newP2, p2Top4));
// p2.set(newP2);
this.rotate(tangent);
this.propagate();
receivePropagation(v: Vector) {
this.translate(v);
this.propagateTranslation(v);
}
// TODO: this duplicates receivePropagation, but for some reason it doesn't work when called from receivePropagation
@@ -231,4 +246,35 @@ export class TrackSegment extends PathSegment {
data.id,
);
}
setPositionByPoint(pos: Vector, point: Vector) {
if (!this.points.includes(point)) return;
point = point.copy();
this.points.forEach((p, i) => {
const relativePoint = Vector.sub(p, point);
p.set(pos);
p.add(relativePoint);
});
}
rotateAboutPoint(angle: number, point: Vector) {
if (!this.points.includes(point)) return;
point = point.copy();
this.points.forEach((p, i) => {
const relativePoint = Vector.sub(p, point);
relativePoint.rotate(angle);
p.set(Vector.add(point, relativePoint));
});
}
// resetRotation() {
// const angle = this.tangent(0).heading();
// this.rotateAboutPoint(-angle, this.points[0]);
// }
translate(v: Point) {
this.points.forEach((p) => {
p.add(v.x, v.y);
});
}
}