diff --git a/src/components/map/ObjectsRenderQueue.js b/src/components/map/ObjectsRenderQueue.js
index a4f84bf..8f98b70 100644
--- a/src/components/map/ObjectsRenderQueue.js
+++ b/src/components/map/ObjectsRenderQueue.js
@@ -10,6 +10,7 @@ import NEVPRenderer from './object-renderers/NEVPRenderer';
import DerailerRenderer from './object-renderers/DerailerRenderer';
import ElectrificationStatusPopup from './additional-layer-components/ElectrificationStatusPopup';
import SpawnPointRenderer from './object-renderers/SpawnPointRenderer';
+import IsolationEndRenderer from "./object-renderers/IsolationEndRenderer";
const ObjectRendererQueue = [
{
@@ -23,8 +24,14 @@ const ObjectRendererQueue = [
'pointerEvents': true
},
{
- 'name': 'isolations-ids',
+ 'name': 'isolations-ends',
'category': 'tracks',
+ 'renderer': IsolationEndRenderer,
+ 'cond': (layers) => layers['isolations-ids']
+ },
+ {
+ 'name': 'isolations-ids',
+ 'category': 'isolation-ids',
'renderer': IsolationIdRenderer,
'cond': (layers) => layers['isolations-ids']
},
@@ -92,4 +99,4 @@ const ObjectRendererQueue = [
}
]
-export default ObjectRendererQueue;
\ No newline at end of file
+export default ObjectRendererQueue;
diff --git a/src/components/map/object-renderers/IsolationEndRenderer.js b/src/components/map/object-renderers/IsolationEndRenderer.js
new file mode 100644
index 0000000..12e3e5e
--- /dev/null
+++ b/src/components/map/object-renderers/IsolationEndRenderer.js
@@ -0,0 +1,40 @@
+import { useMemo } from "react";
+import { TrackConnectionEnd } from "../../../model/track-connection";
+
+export default function IsolationEndRenderer(props) {
+ const { object } = props;
+ return (<>
+
+
+ >);
+}
+
+function IsolationEndMarker(props) {
+ const { object, end = false } = props;
+
+ const isShown = useMemo(() => {
+ return object.connections.some((conn) => {
+ const isCorrectEnd = conn.end === end;
+ const isolationIdChanged = conn.otherTrack.id_isolation !== object.id_isolation;
+
+ // Prevent the marker from showing twice for both tracks
+ const isPreferred = object.id < conn.otherTrackId;
+
+ return isCorrectEnd && isolationIdChanged && isPreferred;
+ });
+ }, [object, end]);
+
+ if (!isShown) return null;
+
+ const pos = object.getEndPos(end);
+ const [x, y] = pos.toSVGCoords();
+ const angle = object.getAngleXZForEnd(end);
+
+ const cos = Math.cos(angle);
+ const sin = Math.sin(angle);
+
+ return
+}
diff --git a/src/components/map/object-renderers/IsolationIdRenderer.js b/src/components/map/object-renderers/IsolationIdRenderer.js
index d802f65..881585b 100644
--- a/src/components/map/object-renderers/IsolationIdRenderer.js
+++ b/src/components/map/object-renderers/IsolationIdRenderer.js
@@ -1,55 +1,17 @@
-import { useMemo } from "react";
import SimpleLabelText from "../text/SimpleLabelText";
-import { TrackConnectionEnd } from "../../../model/track-connection";
export default function IsolationIdRenderer(props) {
const { object } = props;
- const pos = object.points.start.lerp(object.points.end, 0.5);
- const [x, y] = pos.toSVGCoords();
+ const [x, y] = object.pos.toSVGCoords();
const text = object.id_isolation ?? '';
+ if (!text) return null;
- const showText = (!object.hide_isolation && !!text);
-
- return (<>
- {showText && }
-
-
- >);
-}
-
-function IsolationEndMarker(props) {
- const { object, end = false } = props;
-
- const isShown = useMemo(() => {
- return object.connections.some((conn) => {
- const isCorrectEnd = conn.end === end;
- const isolationIdChanged = conn.otherTrack.id_isolation !== object.id_isolation;
-
- // Prevent the marker from showing twice for both tracks
- const isPreferred = object.id < conn.otherTrackId;
-
- return isCorrectEnd && isolationIdChanged && isPreferred;
- });
- }, [object, end]);
-
- if (!isShown) return null;
-
- const pos = object.getEndPos(end);
- const [x, y] = pos.toSVGCoords();
- const angle = object.getAngleXZForEnd(end);
-
- const cos = Math.cos(angle);
- const sin = Math.sin(angle);
-
- return
+ return ();
}
diff --git a/src/model/isolation-id.js b/src/model/isolation-id.js
new file mode 100644
index 0000000..feefa5d
--- /dev/null
+++ b/src/model/isolation-id.js
@@ -0,0 +1,15 @@
+import SceneryObject from "./scenery-object";
+import Vector3 from "./vector3";
+
+export default class IsolationId extends SceneryObject {
+ category = "isolation-ids";
+ type = "IsolationId";
+ id_isolation;
+
+ constructor(object_type, object_id, pos, id_isolation) {
+ super(`${object_type}:${object_id}`, pos, Vector3.zero());
+ Object.assign(this, {
+ id_isolation,
+ });
+ }
+}
diff --git a/src/model/switch-descriptions/switch-prefab.js b/src/model/switch-descriptions/switch-prefab.js
index 6d2f623..3bfa580 100644
--- a/src/model/switch-descriptions/switch-prefab.js
+++ b/src/model/switch-descriptions/switch-prefab.js
@@ -5,10 +5,12 @@ import {TrackConnectionEnd} from "../track-connection";
export default class SwitchPrefab {
tracks = {};
+ isolation_id_offset;
- constructor(tracks) {
+ constructor(tracks, isolation_id_offset) {
Object.assign(this, {
tracks,
+ isolation_id_offset,
});
this._verifyConnections();
}
@@ -89,7 +91,10 @@ export default class SwitchPrefab {
),
};
- return new SwitchPrefab(tracks);
+ const midpointA = this._calculateCurveEnd(Vector3.zero(), 0, radiusA, curveLength / 2).endPos;
+ const midpointB = this._calculateCurveEnd(Vector3.zero(), 0, radiusB, curveLength / 2).endPos;
+ const isolationIdOffset = midpointA.lerp(midpointB, 0.5);
+ return new SwitchPrefab(tracks, isolationIdOffset);
}
static _calculateCurveEnd(startPos, startAngle, radius, curveLength) {
@@ -215,7 +220,7 @@ export default class SwitchPrefab {
),
};
- return new SwitchPrefab(tracks);
+ return new SwitchPrefab(tracks, Vector3.zero());
}
static slip(totalLength, outerLength, transitionLength, radius, tangentInv, leftSlipEnabled, rightSlipEnabled) {
@@ -457,6 +462,6 @@ export default class SwitchPrefab {
addSideConnection('left', 'a', 'b', leftSlipEnabled);
addSideConnection('right', 'b', 'a', rightSlipEnabled);
- return new SwitchPrefab(tracks);
+ return new SwitchPrefab(tracks, Vector3.zero());
}
}
diff --git a/src/model/switch.js b/src/model/switch.js
index 0950a52..27412e6 100644
--- a/src/model/switch.js
+++ b/src/model/switch.js
@@ -5,6 +5,7 @@ import Vector3 from "./vector3.js";
import DefinedSwitches from "./defs/defined-switches.js";
import {SwitchTrackConnectionType} from "./switch-descriptions/switch-prefab-track";
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
+import IsolationId from "./isolation-id";
export default class Switch extends SceneryObject {
model;
@@ -84,6 +85,11 @@ export default class Switch extends SceneryObject {
this.tracks = tracks;
this.def = def;
+
+ const rotRad = this.rot.multiply(Math.PI / 180);
+ const isolationIdPos = this.pos.add(def.isolation_id_offset.rotate(rotRad));
+ super.applyObject(scenery);
+ scenery.addObject(new IsolationId(this.category, this.id, isolationIdPos, this.id_isolation));
}
_getTrackIds() {
diff --git a/src/model/tracks/bezier-track.js b/src/model/tracks/bezier-track.js
index ff79db7..96b9cc2 100644
--- a/src/model/tracks/bezier-track.js
+++ b/src/model/tracks/bezier-track.js
@@ -1,6 +1,7 @@
import Track from "./track";
import Vector3 from "../vector3";
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
+import IsolationId from "../isolation-id";
export default class BezierTrack extends Track
{
@@ -9,7 +10,8 @@ export default class BezierTrack extends Track
start: Vector3.zero(),
control1: Vector3.zero(),
end: Vector3.zero(),
- control2: Vector3.zero()
+ control2: Vector3.zero(),
+ 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) {
@@ -19,8 +21,21 @@ export default class BezierTrack extends Track
start,
control1: start.add(control1),
end,
- control2: end.add(control2)
+ control2: end.add(control2),
});
+ // This is not the the actual middle point, but it's close enough
+ this.points.middle = this._pointAtT(0.5);
+ }
+
+ _pointAtT(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);
+
+ const b1 = a1.lerp(a2, t);
+ const b2 = a2.lerp(a3, t);
+
+ return b1.lerp(b2, t);
}
getStartAngleXZ() {
@@ -59,4 +74,9 @@ export default class BezierTrack extends Track
return track;
}
+
+ applyObject(scenery) {
+ super.applyObject(scenery);
+ scenery.addObject(new IsolationId(this.category, this.id, this.points.middle, this.id_isolation));
+ }
}
diff --git a/src/model/tracks/standard-track.js b/src/model/tracks/standard-track.js
index 81cc799..2d0d263 100644
--- a/src/model/tracks/standard-track.js
+++ b/src/model/tracks/standard-track.js
@@ -2,6 +2,7 @@ import AngleHelper from "../../helpers/angleHelper";
import Track from "./track";
import Vector3 from "../vector3";
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
+import IsolationId from "../isolation-id";
export default class StandardTrack extends Track
{
@@ -9,7 +10,8 @@ export default class StandardTrack extends Track
points = {
start: Vector3.zero(),
end: Vector3.zero(),
- circleCenter: Vector3.zero()
+ circleCenter: Vector3.zero(),
+ middle: Vector3.zero(),
};
constructor(id, start, rot, len, r, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
@@ -60,12 +62,18 @@ export default class StandardTrack extends Track
const rotRad = AngleHelper.degToRad(this.rot.y);
this.points.start = this.pos.clone();
- this.points.end = this.pos.add(Vector3.fromAngleY(rotRad, this.len));
- this.points.circleCenter = this.pos.clone(); // default circle center
- if (this.r !== 0) {
+ 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));
}
@@ -78,4 +86,9 @@ export default class StandardTrack extends Track
this.points.end.y += startHeightDiff + (endHeightDiff - startHeightDiff) / 2;
}
}
+
+ applyObject(scenery) {
+ super.applyObject(scenery);
+ scenery.addObject(new IsolationId(this.category, this.id, this.points.middle, this.id_isolation));
+ }
}
diff --git a/src/model/tracks/track.js b/src/model/tracks/track.js
index 711517e..926425b 100644
--- a/src/model/tracks/track.js
+++ b/src/model/tracks/track.js
@@ -15,7 +15,6 @@ export default class Track extends SceneryObject {
start_slope;
end_slope;
prefab_name;
- hide_isolation = false;
switch = null;
electrificationStatus = ElectrificationStatus.NOT_CHECKED;
hasNEVP = false;