Add Track.getPointAtDist method and refactor StandardTrack._calcPoints()

This commit is contained in:
dominik-korsa 2025-07-19 16:19:46 +02:00
parent ff3baa3c26
commit 94bfa7c060
No known key found for this signature in database
GPG Key ID: 5A24D76EE0A30974
4 changed files with 40 additions and 39 deletions

View File

@ -40,7 +40,7 @@ function InfoPopupSwitchItems(props) {
function InfoPopupShapeItems(props) {
const { track } = props;
if (track.type !== 'StandardTrack' || track.r === 0) return null;
if (track.r === 0) return null;
return <InfoPopupItem value={Math.abs(track.r)} label="Radius" options={{suffix: ' m', precision: 0}} />
}

View File

@ -13,8 +13,15 @@ export default class BezierTrack extends Track
middle: Vector3.zero(),
};
constructor(id, start, control1, end, control2, rot, len, r, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
super(id, start, rot, len, r, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed, TrackSource.BEZIER);
constructor(id, start, control1, end, control2, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
const length = start.distance(end); // TODO: calculate more precise length
super(
id, start,
Vector3.zero(), // rotation
length,
0, // radius
connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed, TrackSource.BEZIER,
);
Object.assign(this.points, {
start,
@ -22,11 +29,10 @@ export default class BezierTrack extends Track
end,
control2: end.add(control2),
});
// This is not the the actual middle point, but it's close enough
this.points.middle = this._pointAtT(0.5);
this.points.middle = this.getPointAtDist(length / 2);
}
_pointAtT(t) {
_getPointAtT(t) {
const a1 = this.points.start.lerp(this.points.control1, t);
const a2 = this.points.control1.lerp(this.points.control2, t);
const a3 = this.points.control2.lerp(this.points.end, t);
@ -37,6 +43,12 @@ export default class BezierTrack extends Track
return b1.lerp(b2, t);
}
getPointAtDist(dist, fromEnd = TrackConnectionEnd.START) {
if (fromEnd === TrackConnectionEnd.END) return this.getPointAtDist(this.len - dist, TrackConnectionEnd.START);
// TODO: This is just an approximation for a Bezier curve
return this._getPointAtT(dist / this.len);
}
getStartAngleXZ() {
return this.points.start.atanY(this.points.control1);
}
@ -58,10 +70,6 @@ export default class BezierTrack extends Track
Vector3.fromValuesArray(values, 6), // control1
Vector3.fromValuesArray(values, 9), // end
Vector3.fromValuesArray(values, 12), // control2
// TODO: values below
Vector3.zero(),
0, // len ??
0, // r ??
connections,
values[17], // id_station
...Track.slopesFromText(values[18]), // start_slope, end_slope

View File

@ -3,13 +3,13 @@ import Track, {TrackSource} from "./track";
import Vector3 from "../vector3";
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
import {ElectrificationStatus} from "../electrification-status";
import CurveHelper from "../../helpers/curveHelper";
export default class StandardTrack extends Track {
type = "StandardTrack";
points = {
start: Vector3.zero(),
end: Vector3.zero(),
circleCenter: Vector3.zero(),
middle: Vector3.zero(),
};
@ -18,6 +18,19 @@ export default class StandardTrack extends Track {
this._calcPoints();
}
_transformPoint(point) {
const rotRad = AngleHelper.rotationDegToRad(this.rot);
return point.rotate(rotRad).add(this.points.start);
}
getPointAtDist(dist, fromEnd = TrackConnectionEnd.START) {
if (fromEnd === TrackConnectionEnd.END) return this.getPointAtDist(this.len - dist, TrackConnectionEnd.START)
const curveEnd = CurveHelper.calculateCurveEndStandard(this.r, dist).endPos;
// TODO: Verify slope logic
curveEnd.y += (this.start_slope + this.end_slope) / 2000 * dist;
return this._transformPoint(curveEnd);
}
getStartAngleXZ() {
return AngleHelper.degToRad(this.rot.y);
}
@ -25,10 +38,9 @@ export default class StandardTrack extends Track {
getEndAngleXZ() {
const startAngle = AngleHelper.degToRad(this.rot.y);
if(this.r === 0) {
return startAngle
}
if (this.r === 0) return startAngle
// This is only correct for a flat arc on the XZ plane
return startAngle - this.len / this.r;
}
@ -59,32 +71,9 @@ export default class StandardTrack extends Track {
}
_calcPoints() {
const rotRad = AngleHelper.degToRad(this.rot.y);
this.points.start = this.pos.clone();
if (this.r === 0) {
this.points.end = this.pos.add(Vector3.fromAngleY(rotRad, this.len));
this.points.middle = this.points.start.lerp(this.points.end, 0.5);
this.points.circleCenter = this.pos.clone(); // default circle center
} else {
const centerAngle = rotRad + Math.PI / 2;
this.points.circleCenter = this.pos.sub(Vector3.fromAngleY(centerAngle, this.r));
const middleAngle = centerAngle - (this.len / this.r) / 2;
this.points.middle = this.points.circleCenter.add(Vector3.fromAngleY(middleAngle, this.r));
const endAngle = centerAngle - this.len / this.r;
this.points.end = this.points.circleCenter.add(Vector3.fromAngleY(endAngle, this.r));
}
// adjust end point by the slope
if (this.end_slope !== 0) {
const startHeightDiff = this.start_slope * this.len / 1000;
const endHeightDiff = this.end_slope * this.len / 1000;
this.points.end.y += startHeightDiff + (endHeightDiff - startHeightDiff) / 2;
}
this.points.middle = this.getPointAtDist(this.len / 2, TrackConnectionEnd.START);
this.points.end = this.getPointAtDist(this.len, TrackConnectionEnd.START);
}
applyObject(scenery) {

View File

@ -55,6 +55,10 @@ export default class Track extends SceneryObject {
else return this.getEndAngleXZ();
}
getPointAtDist(_dist, _fromEnd) {
throw new Error("getPointAtDist() must be implemented in subclass");
}
static slopesFromText(text) {
return text.split(",", 2).map((slope) => parseFloat(slope));
}