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 DerailerRenderer from './object-renderers/DerailerRenderer';
|
||||||
import ElectrificationStatusPopup from './additional-layer-components/ElectrificationStatusPopup';
|
import ElectrificationStatusPopup from './additional-layer-components/ElectrificationStatusPopup';
|
||||||
import SpawnPointRenderer from './object-renderers/SpawnPointRenderer';
|
import SpawnPointRenderer from './object-renderers/SpawnPointRenderer';
|
||||||
|
import IsolationEndRenderer from "./object-renderers/IsolationEndRenderer";
|
||||||
|
|
||||||
const ObjectRendererQueue = [
|
const ObjectRendererQueue = [
|
||||||
{
|
{
|
||||||
@ -23,8 +24,14 @@ const ObjectRendererQueue = [
|
|||||||
'pointerEvents': true
|
'pointerEvents': true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'isolations-ids',
|
'name': 'isolations-ends',
|
||||||
'category': 'tracks',
|
'category': 'tracks',
|
||||||
|
'renderer': IsolationEndRenderer,
|
||||||
|
'cond': (layers) => layers['isolations-ids']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'isolations-ids',
|
||||||
|
'category': 'isolation-ids',
|
||||||
'renderer': IsolationIdRenderer,
|
'renderer': IsolationIdRenderer,
|
||||||
'cond': (layers) => layers['isolations-ids']
|
'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 SimpleLabelText from "../text/SimpleLabelText";
|
||||||
import { TrackConnectionEnd } from "../../../model/track-connection";
|
|
||||||
|
|
||||||
export default function IsolationIdRenderer(props) {
|
export default function IsolationIdRenderer(props) {
|
||||||
const { object } = props;
|
const { object } = props;
|
||||||
const pos = object.points.start.lerp(object.points.end, 0.5);
|
const [x, y] = object.pos.toSVGCoords();
|
||||||
const [x, y] = pos.toSVGCoords();
|
|
||||||
const text = object.id_isolation ?? '';
|
const text = object.id_isolation ?? '';
|
||||||
|
if (!text) return null;
|
||||||
|
|
||||||
const showText = (!object.hide_isolation && !!text);
|
return (<SimpleLabelText
|
||||||
|
text={text}
|
||||||
return (<>
|
x={x}
|
||||||
{showText && <SimpleLabelText
|
y={y}
|
||||||
text={text}
|
textProps={{
|
||||||
x={x}
|
className: "isolation-id"
|
||||||
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}`}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
|
|||||||
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 {
|
export default class SwitchPrefab {
|
||||||
tracks = {};
|
tracks = {};
|
||||||
|
isolation_id_offset;
|
||||||
|
|
||||||
constructor(tracks) {
|
constructor(tracks, isolation_id_offset) {
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
tracks,
|
tracks,
|
||||||
|
isolation_id_offset,
|
||||||
});
|
});
|
||||||
this._verifyConnections();
|
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) {
|
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) {
|
static slip(totalLength, outerLength, transitionLength, radius, tangentInv, leftSlipEnabled, rightSlipEnabled) {
|
||||||
@ -457,6 +462,6 @@ export default class SwitchPrefab {
|
|||||||
addSideConnection('left', 'a', 'b', leftSlipEnabled);
|
addSideConnection('left', 'a', 'b', leftSlipEnabled);
|
||||||
addSideConnection('right', 'b', 'a', rightSlipEnabled);
|
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 DefinedSwitches from "./defs/defined-switches.js";
|
||||||
import {SwitchTrackConnectionType} from "./switch-descriptions/switch-prefab-track";
|
import {SwitchTrackConnectionType} from "./switch-descriptions/switch-prefab-track";
|
||||||
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
|
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
|
||||||
|
import IsolationId from "./isolation-id";
|
||||||
|
|
||||||
export default class Switch extends SceneryObject {
|
export default class Switch extends SceneryObject {
|
||||||
model;
|
model;
|
||||||
@ -84,6 +85,11 @@ export default class Switch extends SceneryObject {
|
|||||||
|
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
this.def = def;
|
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() {
|
_getTrackIds() {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import Track from "./track";
|
import Track from "./track";
|
||||||
import Vector3 from "../vector3";
|
import Vector3 from "../vector3";
|
||||||
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
|
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
|
||||||
|
import IsolationId from "../isolation-id";
|
||||||
|
|
||||||
export default class BezierTrack extends Track
|
export default class BezierTrack extends Track
|
||||||
{
|
{
|
||||||
@ -9,7 +10,8 @@ export default class BezierTrack extends Track
|
|||||||
start: Vector3.zero(),
|
start: Vector3.zero(),
|
||||||
control1: Vector3.zero(),
|
control1: Vector3.zero(),
|
||||||
end: 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) {
|
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,
|
start,
|
||||||
control1: start.add(control1),
|
control1: start.add(control1),
|
||||||
end,
|
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() {
|
getStartAngleXZ() {
|
||||||
@ -59,4 +74,9 @@ export default class BezierTrack extends Track
|
|||||||
|
|
||||||
return 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 Track from "./track";
|
||||||
import Vector3 from "../vector3";
|
import Vector3 from "../vector3";
|
||||||
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
|
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
|
||||||
|
import IsolationId from "../isolation-id";
|
||||||
|
|
||||||
export default class StandardTrack extends Track
|
export default class StandardTrack extends Track
|
||||||
{
|
{
|
||||||
@ -9,7 +10,8 @@ export default class StandardTrack extends Track
|
|||||||
points = {
|
points = {
|
||||||
start: Vector3.zero(),
|
start: Vector3.zero(),
|
||||||
end: 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) {
|
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);
|
const rotRad = AngleHelper.degToRad(this.rot.y);
|
||||||
|
|
||||||
this.points.start = this.pos.clone();
|
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;
|
const centerAngle = rotRad + Math.PI / 2;
|
||||||
this.points.circleCenter = this.pos.sub(Vector3.fromAngleY(centerAngle, this.r));
|
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;
|
const endAngle = centerAngle - this.len / this.r;
|
||||||
this.points.end = this.points.circleCenter.add(Vector3.fromAngleY(endAngle, 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;
|
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;
|
start_slope;
|
||||||
end_slope;
|
end_slope;
|
||||||
prefab_name;
|
prefab_name;
|
||||||
hide_isolation = false;
|
|
||||||
switch = null;
|
switch = null;
|
||||||
electrificationStatus = ElectrificationStatus.NOT_CHECKED;
|
electrificationStatus = ElectrificationStatus.NOT_CHECKED;
|
||||||
hasNEVP = false;
|
hasNEVP = false;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user