Create IsolationId class, split IsolationEndRenderer and IsolationIdRenderer
This commit is contained in:
parent
5546601b67
commit
3ae1827d10
@ -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']
|
||||
},
|
||||
|
||||
40
src/components/map/object-renderers/IsolationEndRenderer.js
Normal file
40
src/components/map/object-renderers/IsolationEndRenderer.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { useMemo } from "react";
|
||||
import { TrackConnectionEnd } from "../../../model/track-connection";
|
||||
|
||||
export default function IsolationEndRenderer(props) {
|
||||
const { object } = props;
|
||||
return (<>
|
||||
<IsolationEndMarker object={object} end={TrackConnectionEnd.START} />
|
||||
<IsolationEndMarker object={object} end={TrackConnectionEnd.END} />
|
||||
</>);
|
||||
}
|
||||
|
||||
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 <path
|
||||
className="isolation-end-marker"
|
||||
d={`M ${x - 1.5 * cos} ${y - 1.5 * sin} L ${x + 1.5 * cos} ${y + 1.5 * sin}`}
|
||||
/>
|
||||
}
|
||||
@ -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 && <SimpleLabelText
|
||||
text={text}
|
||||
x={x}
|
||||
y={y}
|
||||
textProps={{
|
||||
className: "isolation-id"
|
||||
}}
|
||||
/>}
|
||||
<IsolationEndMarker object={object} end={TrackConnectionEnd.START} />
|
||||
<IsolationEndMarker object={object} end={TrackConnectionEnd.END} />
|
||||
</>);
|
||||
}
|
||||
|
||||
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 <path
|
||||
className="isolation-end-marker"
|
||||
d={`M ${x - 1.5 * cos} ${y - 1.5 * sin} L ${x + 1.5 * cos} ${y + 1.5 * sin}`}
|
||||
/>
|
||||
return (<SimpleLabelText
|
||||
text={text}
|
||||
x={x}
|
||||
y={y}
|
||||
textProps={{
|
||||
className: "isolation-id"
|
||||
}}
|
||||
/>);
|
||||
}
|
||||
|
||||
15
src/model/isolation-id.js
Normal file
15
src/model/isolation-id.js
Normal file
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user