Fixed ghost track rotation on rear ends

Recalculation on track edit end
Changes rendering of ties to be evenly spaced
Fixes ghost and held track rendering
This commit is contained in:
Emmaline Autumn 2025-02-15 06:40:39 -07:00
parent 3befb69f51
commit 968867c5d9
6 changed files with 73 additions and 41 deletions

View File

@ -1,7 +1,7 @@
(() => {
// lib/context.ts
var contextStack = [];
var defaultContext = {};
var contextStack = [defaultContext];
var debug = JSON.parse(localStorage.getItem("debug") || "false");
function setDefaultContext(context) {
Object.assign(defaultContext, context);
@ -1350,8 +1350,8 @@
}
calculateEvenlySpacedPoints(spacing, resolution = 1, targetLength) {
const points = [];
points.push(this.points[0]);
let prev = points[0];
points.push([this.points[0], this.tangent(0).heading()]);
let [prev] = points[0];
let distSinceLastEvenPoint = 0;
let t = 0;
const div = Math.ceil(this.length * resolution * 10);
@ -1366,7 +1366,7 @@
Vector.sub(point, prev).normalize().mult(overshoot)
);
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
points.push([evenPoint, this.tangent(t).heading()]);
prev = evenPoint;
}
prev = point;
@ -1383,7 +1383,7 @@
Vector.sub(point, prev).normalize().mult(overshoot)
);
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
points.push([evenPoint, this.tangent(t).heading()]);
prev = evenPoint;
}
prev = point;
@ -1403,7 +1403,7 @@
const curveLength = this.startingLength;
const points = this.calculateEvenlySpacedPoints(1, 1, curveLength + 1);
if (points.length >= curveLength) {
this.points[3].set(points[curveLength]);
this.points[3].set(points[curveLength][0]);
}
}
draw() {
@ -1437,6 +1437,12 @@
segment.recalculateRailPoints(Math.round(percent * 100 / 4));
}
}
recalculateAll() {
for (const segment of this.segments.values()) {
segment.recalculateRailPoints();
segment.length = segment.calculateApproxLength();
}
}
registerSegment(segment) {
segment.setTrack(this);
this.segments.set(segment.id, segment);
@ -1447,6 +1453,9 @@
s.backNeighbours = s.backNeighbours.filter((n) => n !== segment);
s.frontNeighbours = s.frontNeighbours.filter((n) => n !== segment);
}
const ends = this.ends.get(segment);
this.ends.delete(segment);
this.endArray = this.endArray.filter((e) => !ends?.includes(e));
}
draw(showControls = false) {
for (const [i, segment] of this.segments.entries()) {
@ -1642,7 +1651,7 @@
setTrack(t) {
this.track = t;
}
draw(showControls = false) {
draw(showControls = false, recalculateRailPoints = false) {
if (showControls) {
this.doodler.deferDrawing(() => {
this.doodler.fillCircle(this.points[0], 1, {
@ -1659,11 +1668,11 @@
});
});
}
const ties = Math.ceil(this.length / 10);
for (let i = 0; i < ties; i++) {
const t = i / ties;
const p = this.getPointAtT(t);
this.doodler.drawRotated(p, this.tangent(t).heading(), () => {
const spacing = Math.ceil(this.length / 10);
const points = this.calculateEvenlySpacedPoints(this.length / spacing);
for (let i = 0; i < points.length - 1; i++) {
const [p, t] = points[i];
this.doodler.drawRotated(p, t, () => {
this.doodler.line(p, p.copy().add(0, 10), {
color: "#291b17",
weight: 4
@ -1674,6 +1683,9 @@
});
});
}
if (recalculateRailPoints) {
this.recalculateRailPoints();
}
this.doodler.deferDrawing(
() => {
this.doodler.drawLine(this.normalPoints, {
@ -2044,18 +2056,20 @@
);
this.ghostRotated = true;
break;
case "back":
case "back": {
this.ghostSegment.setPositionByPoint(
this.closestEnd.pos,
this.ghostSegment.points[3]
);
const ghostEndTangent = this.ghostSegment.tangent(1);
!this.ghostRotated && this.ghostSegment.rotateAboutPoint(
this.closestEnd.tangent.heading(),
this.closestEnd.tangent.heading() - ghostEndTangent.heading(),
this.ghostSegment.points[3]
);
this.ghostRotated = true;
break;
}
}
} else if (!this.closestEnd || !closestEnd) {
this.ghostSegment = void 0;
this.ghostRotated = false;
@ -2082,11 +2096,11 @@
const doodler2 = getContextItem("doodler");
this.layers.push(
doodler2.createLayer(() => {
this.selectedSegment?.draw();
this.selectedSegment?.draw(false, true);
if (this.ghostSegment) {
doodler2.drawWithAlpha(0.5, () => {
if (!this.ghostSegment) return;
this.ghostSegment.draw();
this.ghostSegment.draw(false, true);
if (getContextItemOrDefault("debug", false)) {
const colors2 = getContextItem("colors");
for (const [i, point] of this.ghostSegment.points.entries() ?? []) {
@ -2182,6 +2196,8 @@
for (const layer of this.layers) {
getContextItem("doodler").deleteLayer(layer);
}
const track = getContextItem("track");
track.recalculateAll();
const inputManager2 = getContextItem("inputManager");
inputManager2.offKey("e");
inputManager2.offKey("w");
@ -2830,10 +2846,6 @@
fpsEl.id = "fps";
document.body.appendChild(fpsEl);
}
const fPerc = frameRate / 60;
if (fPerc < 0.6) {
state.optimizePerformance(fPerc);
}
fpsEl.textContent = frameRate.toFixed(1) + " fps";
}, 1e3);
var gameLoop = new GameLoop();

View File

@ -1,7 +1,7 @@
type ContextStore = Record<string, any>;
const contextStack: ContextStore[] = [];
const defaultContext: ContextStore = {};
const contextStack: ContextStore[] = [defaultContext];
const debug = JSON.parse(localStorage.getItem("debug") || "false");

View File

@ -74,10 +74,10 @@ setInterval(() => {
fpsEl.id = "fps";
document.body.appendChild(fpsEl);
}
const fPerc = frameRate / 60;
if (fPerc < 0.6) {
state.optimizePerformance(fPerc);
}
// const fPerc = frameRate / 60;
// if (fPerc < 0.6) {
// state.optimizePerformance(fPerc);
// }
fpsEl.textContent = frameRate.toFixed(1) + " fps";
}, 1000);

View File

@ -237,10 +237,10 @@ export class PathSegment {
resolution = 1,
targetLength?: number,
) {
const points: Vector[] = [];
const points: [Vector, number][] = [];
points.push(this.points[0]);
let prev = points[0];
points.push([this.points[0], this.tangent(0).heading()]);
let [prev] = points[0];
let distSinceLastEvenPoint = 0;
let t = 0;
@ -258,7 +258,7 @@ export class PathSegment {
Vector.sub(point, prev).normalize().mult(overshoot),
);
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
points.push([evenPoint, this.tangent(t).heading()]);
prev = evenPoint;
}
@ -278,7 +278,7 @@ export class PathSegment {
Vector.sub(point, prev).normalize().mult(overshoot),
);
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
points.push([evenPoint, this.tangent(t).heading()]);
prev = evenPoint;
}
@ -304,7 +304,7 @@ export class PathSegment {
const curveLength = this.startingLength;
const points = this.calculateEvenlySpacedPoints(1, 1, curveLength + 1);
if (points.length >= curveLength) {
this.points[3].set(points[curveLength]);
this.points[3].set(points[curveLength][0]);
}
}

View File

@ -121,19 +121,21 @@ export class EditTrackState extends State<States> {
);
this.ghostRotated = true;
break;
case "back":
case "back": {
this.ghostSegment.setPositionByPoint(
this.closestEnd.pos,
this.ghostSegment.points[3],
);
const ghostEndTangent = this.ghostSegment.tangent(1);
// this.ghostSegment.points[3] = this.closestEnd.pos;
!this.ghostRotated && this.ghostSegment.rotateAboutPoint(
this.closestEnd.tangent.heading(),
this.closestEnd.tangent.heading() - ghostEndTangent.heading(),
this.ghostSegment.points[3],
);
this.ghostRotated = true;
break;
}
}
// } else if (closestEnd) {
// this.closestEnd = closestEnd;
} else if (!this.closestEnd || !closestEnd) {
@ -258,11 +260,11 @@ export class EditTrackState extends State<States> {
const doodler = getContextItem<Doodler>("doodler");
this.layers.push(
doodler.createLayer(() => {
this.selectedSegment?.draw();
this.selectedSegment?.draw(false, true);
if (this.ghostSegment) {
doodler.drawWithAlpha(0.5, () => {
if (!this.ghostSegment) return;
this.ghostSegment.draw();
this.ghostSegment.draw(false, true);
if (getContextItemOrDefault("debug", false)) {
const colors = getContextItem<string[]>("colors");
for (
@ -396,6 +398,9 @@ export class EditTrackState extends State<States> {
getContextItem<Doodler>("doodler").deleteLayer(layer);
}
const track = getContextItem<TrackSystem>("track");
track.recalculateAll();
const inputManager = getContextItem<InputManager>("inputManager");
inputManager.offKey("e");
inputManager.offKey("w");

View File

@ -29,6 +29,13 @@ export class TrackSystem {
}
}
recalculateAll() {
for (const segment of this.segments.values()) {
segment.recalculateRailPoints();
segment.length = segment.calculateApproxLength();
}
}
registerSegment(segment: TrackSegment) {
segment.setTrack(this);
this.segments.set(segment.id, segment);
@ -39,6 +46,9 @@ export class TrackSystem {
s.backNeighbours = s.backNeighbours.filter((n) => n !== segment);
s.frontNeighbours = s.frontNeighbours.filter((n) => n !== segment);
}
const ends = this.ends.get(segment);
this.ends.delete(segment);
this.endArray = this.endArray.filter((e) => !ends?.includes(e));
}
draw(showControls = false) {
@ -291,7 +301,7 @@ export class TrackSegment extends PathSegment {
this.track = t;
}
override draw(showControls = false) {
override draw(showControls = false, recalculateRailPoints = false) {
// if (showControls) {
// this.doodler.drawBezier(
// this.points[0],
@ -320,15 +330,17 @@ export class TrackSegment extends PathSegment {
});
}
const ties = Math.ceil(this.length / 10);
for (let i = 0; i < ties; i++) {
const t = i / ties;
const p = this.getPointAtT(t);
const spacing = Math.ceil(this.length / 10);
const points = this.calculateEvenlySpacedPoints(this.length / spacing);
for (let i = 0; i < points.length - 1; i++) {
// const t = i / ties;
// const p = this.getPointAtT(t);
const [p, t] = points[i];
// this.doodler.drawCircle(p, 2, {
// color: "red",
// weight: 3,
// });
this.doodler.drawRotated(p, this.tangent(t).heading(), () => {
this.doodler.drawRotated(p, t, () => {
this.doodler.line(p, p.copy().add(0, 10), {
color: "#291b17",
weight: 4,
@ -348,6 +360,9 @@ export class TrackSegment extends PathSegment {
});
}
if (recalculateRailPoints) {
this.recalculateRailPoints();
}
this.doodler.deferDrawing(
() => {
this.doodler.drawLine(this.normalPoints, {