diff --git a/src/components/map/track-hover-info/TrackHoverInfoPopup.js b/src/components/map/track-hover-info/TrackHoverInfoPopup.js index 270814e..027e073 100644 --- a/src/components/map/track-hover-info/TrackHoverInfoPopup.js +++ b/src/components/map/track-hover-info/TrackHoverInfoPopup.js @@ -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 } diff --git a/src/model/tracks/bezier-track.js b/src/model/tracks/bezier-track.js index 79ddeda..f34740f 100644 --- a/src/model/tracks/bezier-track.js +++ b/src/model/tracks/bezier-track.js @@ -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 diff --git a/src/model/tracks/standard-track.js b/src/model/tracks/standard-track.js index 5ef3ce2..8b00ccb 100644 --- a/src/model/tracks/standard-track.js +++ b/src/model/tracks/standard-track.js @@ -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) { diff --git a/src/model/tracks/track.js b/src/model/tracks/track.js index 72ec8cf..276e38e 100644 --- a/src/model/tracks/track.js +++ b/src/model/tracks/track.js @@ -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)); }