Merge pull request #3 from dominik-korsa/switch-tracks
This commit is contained in:
commit
a0b792fb6f
5
.gitignore
vendored
5
.gitignore
vendored
@ -22,4 +22,7 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
public/sceneries/*
|
public/sceneries/*
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.idea
|
||||||
|
|||||||
@ -63,11 +63,11 @@
|
|||||||
fill: #111;
|
fill: #111;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map svg g.route g {
|
.map svg g.route-arrow {
|
||||||
fill: #eee;
|
fill: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.map svg g.route text {
|
.map svg g.route-name text {
|
||||||
fill: #eee;
|
fill: #eee;
|
||||||
font-size: 72mm;
|
font-size: 72mm;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,15 @@ 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': ['tracks', 'switches'],
|
||||||
|
'types': ['StandardTrack', 'BezierTrack', 'Switch'],
|
||||||
'renderer': IsolationIdRenderer,
|
'renderer': IsolationIdRenderer,
|
||||||
'cond': (layers) => layers['isolations-ids']
|
'cond': (layers) => layers['isolations-ids']
|
||||||
},
|
},
|
||||||
@ -92,4 +100,4 @@ const ObjectRendererQueue = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export default ObjectRendererQueue;
|
export default ObjectRendererQueue;
|
||||||
|
|||||||
@ -8,19 +8,26 @@ export default function SceneryLayer(props) {
|
|||||||
const { name, category, type, types, cond, renderer: Renderer, additionalComponents: AdditionalComponents } = queueItem;
|
const { name, category, type, types, cond, renderer: Renderer, additionalComponents: AdditionalComponents } = queueItem;
|
||||||
const { scenery } = useContext(MainContext);
|
const { scenery } = useContext(MainContext);
|
||||||
const { layers } = useContext(SettingsContext);
|
const { layers } = useContext(SettingsContext);
|
||||||
|
|
||||||
const isVisible = (cond ? cond(layers) : true);
|
const isVisible = (cond ? cond(layers) : true);
|
||||||
|
|
||||||
const objects = useMemo(() => {
|
const objects = useMemo(() => {
|
||||||
if (!scenery || !isVisible) return [];
|
if (!scenery || !isVisible) return [];
|
||||||
const categoryObjs = scenery.objects[category];
|
|
||||||
return categoryObjs ? Object.values(categoryObjs) : [];
|
const getCategoryObjects = (category) => {
|
||||||
|
const categoryObjs = scenery.objects[category];
|
||||||
|
if (!categoryObjs) return [];
|
||||||
|
return Object.values(categoryObjs);
|
||||||
|
}
|
||||||
|
// flatMap is not used in the case of a single category to avoid an unnecessary array copy
|
||||||
|
if (typeof category === 'string') return getCategoryObjects(category);
|
||||||
|
return category.flatMap(getCategoryObjects);
|
||||||
}, [scenery, category, isVisible]);
|
}, [scenery, category, isVisible]);
|
||||||
|
|
||||||
if (!scenery || !isVisible) return null;
|
if (!scenery || !isVisible) return null;
|
||||||
|
|
||||||
const pointerEvents = Constants.map.forcePointerEvents || queueItem.pointerEvents || false;
|
const pointerEvents = Constants.map.forcePointerEvents || queueItem.pointerEvents || false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MemoizedSceneryLayer
|
<MemoizedSceneryLayer
|
||||||
name={name}
|
name={name}
|
||||||
@ -33,13 +40,13 @@ export default function SceneryLayer(props) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemoizedSceneryLayer = memo(StatelessSceneryLayer);
|
const MemoizedSceneryLayer = memo(StatelessSceneryLayer);
|
||||||
|
|
||||||
|
|
||||||
function StatelessSceneryLayer({ name, Renderer, objects, type, types, pointerEvents = false, AdditionalComponents = [] }) {
|
function StatelessSceneryLayer({ name, Renderer, objects, type, types, pointerEvents = false, AdditionalComponents = [] }) {
|
||||||
return (
|
return (
|
||||||
<g
|
<g
|
||||||
className={`scenery-layer-${name}`}
|
className={`scenery-layer-${name}`}
|
||||||
pointerEvents={pointerEvents ? "all" : "none"}
|
pointerEvents={pointerEvents ? "all" : "none"}
|
||||||
>
|
>
|
||||||
@ -53,4 +60,4 @@ function StatelessSceneryLayer({ name, Renderer, objects, type, types, pointerEv
|
|||||||
))}
|
))}
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
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,50 +1,20 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import SimpleLabelText from "../text/SimpleLabelText";
|
import SimpleLabelText from "../text/SimpleLabelText";
|
||||||
import { TrackConnectionType } 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] = pos.toSVGCoords();
|
|
||||||
const text = object.id_isolation ?? '';
|
const text = object.id_isolation ?? '';
|
||||||
|
if (!text) return null;
|
||||||
|
|
||||||
const showText = (!object.hide_isolation && !!text);
|
let pos = (object.type === 'Switch' ? object.isolation_id_pos : object.points.middle) ?? object.pos;
|
||||||
|
|
||||||
return (<>
|
|
||||||
{showText && <SimpleLabelText
|
|
||||||
text={text}
|
|
||||||
x={x}
|
|
||||||
y={y}
|
|
||||||
textProps={{
|
|
||||||
className: "isolation-id"
|
|
||||||
}}
|
|
||||||
/>}
|
|
||||||
<IsolationEndMarker object={object} isStart={true} />
|
|
||||||
<IsolationEndMarker object={object} />
|
|
||||||
</>);
|
|
||||||
}
|
|
||||||
|
|
||||||
function IsolationEndMarker(props) {
|
|
||||||
const { object, isStart = false } = props;
|
|
||||||
|
|
||||||
const isShown = useMemo(() => {
|
|
||||||
return object.connections.some(conn =>
|
|
||||||
(isStart ? conn.type === TrackConnectionType.START : conn.type === TrackConnectionType.END) &&
|
|
||||||
conn.otherTrack.id_isolation !== object.id_isolation
|
|
||||||
);
|
|
||||||
}, [object, isStart]);
|
|
||||||
|
|
||||||
if (!isShown) return null;
|
|
||||||
|
|
||||||
const pos = isStart ? object.points.start : object.points.end;
|
|
||||||
const [x, y] = pos.toSVGCoords();
|
const [x, y] = pos.toSVGCoords();
|
||||||
const angle = isStart ? object.getStartAngleXZ() : object.getEndAngleXZ();
|
|
||||||
|
|
||||||
const cos = Math.cos(angle);
|
return (<SimpleLabelText
|
||||||
const sin = Math.sin(angle);
|
text={text}
|
||||||
|
x={x}
|
||||||
return <path
|
y={y}
|
||||||
className="isolation-end-marker"
|
textProps={{
|
||||||
d={`M ${x - 1.5 * cos} ${y - 1.5 * sin} L ${x + 1.5 * cos} ${y + 1.5 * sin}`}
|
className: "isolation-id"
|
||||||
/>
|
}}
|
||||||
|
/>);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,33 @@
|
|||||||
import { ReactSVG } from "react-svg";
|
import { ReactSVG } from "react-svg";
|
||||||
import AlwaysUpText from "../text/AlwaysUpText";
|
import AlwaysUpText from "../text/AlwaysUpText";
|
||||||
|
import AngleHelper from "../../../helpers/angleHelper";
|
||||||
|
|
||||||
export default function RouteRenderer(props) {
|
export default function RouteRenderer(props) {
|
||||||
const { object } = props;
|
const { object } = props;
|
||||||
const [x, y] = object.pos.toSVGCoords();
|
|
||||||
|
const [startX, startY] = object.pos.toSVGCoords();
|
||||||
|
const startAngle = object.rot.y;
|
||||||
|
|
||||||
|
const [endX, endY] = object.end_center.toSVGCoords();
|
||||||
|
const endAngle = AngleHelper.radToDeg(object.end_angle_rad);
|
||||||
|
|
||||||
const offY = object.track_count === 2 ? -22 : -20;
|
const offY = object.track_count === 2 ? -22 : -20;
|
||||||
const arrowOffset = - object.track_offset - 37.8;
|
const arrowOffset = - object.track_offset - 37.8;
|
||||||
|
|
||||||
return (
|
return (<>
|
||||||
<g className="route" transform={`translate(${x}, ${y}) rotate(${object.rot.y})`}>
|
<g className="route-arrow" transform={`translate(${endX}, ${endY}) rotate(${endAngle + 90}) translate(-80, ${arrowOffset})`}>
|
||||||
<g transform={`rotate(90) translate(-80, ${arrowOffset})`}>
|
<ReactSVG
|
||||||
<ReactSVG
|
src={`${process.env.PUBLIC_URL}/assets/route.svg`}
|
||||||
src={`${process.env.PUBLIC_URL}/assets/route.svg`}
|
wrapper='svg'
|
||||||
wrapper='svg'
|
beforeInjection={(svg) => {
|
||||||
beforeInjection={(svg) => {
|
svg.setAttribute('width', '20mm');
|
||||||
svg.setAttribute('width', '20mm');
|
svg.setAttribute('height', '20mm');
|
||||||
svg.setAttribute('height', '20mm');
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</g>
|
||||||
</g>
|
<g className="route-name" transform={`translate(${startX}, ${startY}) rotate(${startAngle})`}>
|
||||||
<AlwaysUpText
|
<AlwaysUpText
|
||||||
baseRot={object.rot.y}
|
baseRot={startAngle}
|
||||||
additionalRot={-90}
|
additionalRot={-90}
|
||||||
offsetX={object.track_offset}
|
offsetX={object.track_offset}
|
||||||
reverseAnchor={true}
|
reverseAnchor={true}
|
||||||
@ -32,5 +38,5 @@ export default function RouteRenderer(props) {
|
|||||||
text={object.route_name}
|
text={object.route_name}
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
</>);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,10 @@ const MemoizedTrackRenderer = React.memo(StatelessTrackRenderer);
|
|||||||
function StatelessTrackRenderer(props) {
|
function StatelessTrackRenderer(props) {
|
||||||
const { object, trackColorMode } = props;
|
const { object, trackColorMode } = props;
|
||||||
|
|
||||||
|
if (object.points.start.distanceSq(object.points.end) < 0.001) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const onMouseEnter = () => {
|
const onMouseEnter = () => {
|
||||||
setHoveredTrack(object);
|
setHoveredTrack(object);
|
||||||
};
|
};
|
||||||
@ -56,7 +60,8 @@ function StatelessTrackRenderer(props) {
|
|||||||
function getTrackPath(object) {
|
function getTrackPath(object) {
|
||||||
switch (object.type) {
|
switch (object.type) {
|
||||||
case "StandardTrack":
|
case "StandardTrack":
|
||||||
case "PointTrack":
|
case "PointTrack":
|
||||||
|
case "RouteTrack":
|
||||||
const [x1, y1] = object.points.start.toSVGCoords();
|
const [x1, y1] = object.points.start.toSVGCoords();
|
||||||
const [x2, y2] = object.points.end.toSVGCoords();
|
const [x2, y2] = object.points.end.toSVGCoords();
|
||||||
const ra = Math.abs(object.r);
|
const ra = Math.abs(object.r);
|
||||||
@ -84,7 +89,7 @@ function getTrackColor(object, trackColorMode) {
|
|||||||
switch (trackColorMode) {
|
switch (trackColorMode) {
|
||||||
case "standard":
|
case "standard":
|
||||||
default:
|
default:
|
||||||
if(object.prefab_name.includes('trans-mat'))
|
if(object.prefab_name && object.prefab_name.includes('trans-mat'))
|
||||||
return modeDef.options['invisible'][0];
|
return modeDef.options['invisible'][0];
|
||||||
|
|
||||||
return modeDef.options[modeDef.optionDefault][0];
|
return modeDef.options[modeDef.optionDefault][0];
|
||||||
@ -102,13 +107,15 @@ function getTrackColor(object, trackColorMode) {
|
|||||||
default:
|
default:
|
||||||
return modeDef.options[modeDef.optionDefault][0];
|
return modeDef.options[modeDef.optionDefault][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
case "type":
|
case "type":
|
||||||
switch (object.type) {
|
switch (object.type) {
|
||||||
case "StandardTrack":
|
case "StandardTrack":
|
||||||
return modeDef.options['standard-track'][0];
|
return modeDef.options['standard-track'][0];
|
||||||
case "PointTrack":
|
case "PointTrack":
|
||||||
return modeDef.options['point-track'][0];
|
return modeDef.options['point-track'][0];
|
||||||
|
case "RouteTrack":
|
||||||
|
return modeDef.options['route-track'][0];
|
||||||
case "BezierTrack":
|
case "BezierTrack":
|
||||||
return modeDef.options['bezier-track'][0];
|
return modeDef.options['bezier-track'][0];
|
||||||
default:
|
default:
|
||||||
@ -123,7 +130,8 @@ function getTrackColor(object, trackColorMode) {
|
|||||||
return `url(#track-slope-${object.id})`;
|
return `url(#track-slope-${object.id})`;
|
||||||
|
|
||||||
case "max-speed":
|
case "max-speed":
|
||||||
if(!object.maxspeed) {
|
if (!object.maxspeed) {
|
||||||
|
if (object.type === 'RouteTrack') return modeDef.options['unknown'][0];
|
||||||
return modeDef.options['derail'][0];
|
return modeDef.options['derail'][0];
|
||||||
}
|
}
|
||||||
return MiscHelper.getTrackGradient(modeDef.gradient, object.maxspeed);
|
return MiscHelper.getTrackGradient(modeDef.gradient, object.maxspeed);
|
||||||
@ -133,13 +141,13 @@ function getTrackColor(object, trackColorMode) {
|
|||||||
function getTrackDefs(object, trackColorMode) {
|
function getTrackDefs(object, trackColorMode) {
|
||||||
if (trackColorMode !== "slope") return null;
|
if (trackColorMode !== "slope") return null;
|
||||||
if (object.start_slope === object.end_slope) return null;
|
if (object.start_slope === object.end_slope) return null;
|
||||||
|
|
||||||
const [x1, y1] = object.points.start.toSVGCoords();
|
const [x1, y1] = object.points.start.toSVGCoords();
|
||||||
const [x2, y2] = object.points.end.toSVGCoords();
|
const [x2, y2] = object.points.end.toSVGCoords();
|
||||||
const gradId = `track-slope-${object.id}`;
|
const gradId = `track-slope-${object.id}`;
|
||||||
const startColor = MiscHelper.getTrackGradient(Constants.trackColorModes['slope'].gradient, Math.abs(object.start_slope));
|
const startColor = MiscHelper.getTrackGradient(Constants.trackColorModes['slope'].gradient, Math.abs(object.start_slope));
|
||||||
const endColor = MiscHelper.getTrackGradient(Constants.trackColorModes['slope'].gradient, Math.abs(object.end_slope));
|
const endColor = MiscHelper.getTrackGradient(Constants.trackColorModes['slope'].gradient, Math.abs(object.end_slope));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
|
|||||||
@ -11,6 +11,7 @@ const TrackHoverInfoConsts = {
|
|||||||
'StandardTrack': 'Standard track',
|
'StandardTrack': 'Standard track',
|
||||||
'PointTrack': 'Switch track',
|
'PointTrack': 'Switch track',
|
||||||
'BezierTrack': 'Bezier track',
|
'BezierTrack': 'Bezier track',
|
||||||
|
'RouteTrack': 'Route track',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -8,22 +8,18 @@ const Constants = {
|
|||||||
forcePointerEvents: false
|
forcePointerEvents: false
|
||||||
},
|
},
|
||||||
parser: {
|
parser: {
|
||||||
forceAutoSwitches: false,
|
|
||||||
logNewAutoSwitches: true,
|
|
||||||
logSceneryAfterFinished: true,
|
logSceneryAfterFinished: true,
|
||||||
sceneryInfoVersion: 29,
|
sceneryInfoVersion: 29,
|
||||||
alwaysShowLogDialog: false,
|
alwaysShowLogDialog: false,
|
||||||
runTracksConnectionTest: false,
|
runTracksConnectionTest: false,
|
||||||
resolveElectrification: true,
|
resolveElectrification: true,
|
||||||
skipElectrificationErrorsPropagation: false,
|
skipElectrificationErrorsPropagation: false,
|
||||||
maxRouteConnectionDistance: 0.2,
|
|
||||||
attachSigns: true,
|
attachSigns: true,
|
||||||
attachSignsNeedsSameTrack: false,
|
attachSignsNeedsSameTrack: false,
|
||||||
attachSignsMaxDistanceZ: 1.0,
|
attachSignsMaxDistanceZ: 1.0,
|
||||||
attachSignsMaxDistanceX: 0.2,
|
attachSignsMaxDistanceX: 0.2,
|
||||||
attachSignsGridSize: 10,
|
attachSignsGridSize: 10,
|
||||||
logAttachedSigns: false,
|
logAttachedSigns: false,
|
||||||
connectTracks: true,
|
|
||||||
},
|
},
|
||||||
warnings: {
|
warnings: {
|
||||||
all: false, // enable all warnings
|
all: false, // enable all warnings
|
||||||
@ -32,19 +28,17 @@ const Constants = {
|
|||||||
unknownObjectType: true,
|
unknownObjectType: true,
|
||||||
unknownTrackType: true,
|
unknownTrackType: true,
|
||||||
spawnWithoutSpawnInfo: true,
|
spawnWithoutSpawnInfo: true,
|
||||||
trackAliasAlreadyExists: true,
|
|
||||||
trackAliasNoTrack: true,
|
|
||||||
switchUndefinedModel: true,
|
switchUndefinedModel: true,
|
||||||
switchAutoDefFailed: true,
|
|
||||||
switchInvalidDataFormat: true,
|
switchInvalidDataFormat: true,
|
||||||
switchNoModel: true,
|
switchNoModel: true,
|
||||||
|
switchMissingTrackId: true,
|
||||||
|
switchInvalidInternalConnection: true,
|
||||||
signalBoxUndefinedPrefabName: true,
|
signalBoxUndefinedPrefabName: true,
|
||||||
invalidSceneryInfoVersion: true,
|
invalidSceneryInfoVersion: true,
|
||||||
signUndefinedPrefabName: true,
|
signUndefinedPrefabName: true,
|
||||||
tracksConnectionTest: true,
|
tracksConnectionTest: true,
|
||||||
electrificationNevpNotApplied: false,
|
electrificationNevpNotApplied: false,
|
||||||
electrificationConflict: false,
|
electrificationConflict: false,
|
||||||
electrificationMissingRouteTracks: true,
|
|
||||||
electrificationResolverError: true,
|
electrificationResolverError: true,
|
||||||
electrificationResolverWarnings: true,
|
electrificationResolverWarnings: true,
|
||||||
signalElemsUnknownPrefab: true,
|
signalElemsUnknownPrefab: true,
|
||||||
@ -53,6 +47,7 @@ const Constants = {
|
|||||||
signalElemsUnknownSign: true,
|
signalElemsUnknownSign: true,
|
||||||
signalElemsUnknownSignText: true,
|
signalElemsUnknownSignText: true,
|
||||||
connectTracksFailed: true,
|
connectTracksFailed: true,
|
||||||
|
routeInvalidSegment: true,
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
invalidSceneryInfo: true
|
invalidSceneryInfo: true
|
||||||
@ -146,6 +141,7 @@ const Constants = {
|
|||||||
'standard-track': ['#00a', 'Standard track'],
|
'standard-track': ['#00a', 'Standard track'],
|
||||||
'point-track': ['#0a0', 'Switch track'],
|
'point-track': ['#0a0', 'Switch track'],
|
||||||
'bezier-track': ['#aa8', 'Bezier track'],
|
'bezier-track': ['#aa8', 'Bezier track'],
|
||||||
|
'route-track': ['#a6d', 'Route track'],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'slope': {
|
'slope': {
|
||||||
@ -170,9 +166,10 @@ const Constants = {
|
|||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
'derail': ['#f22', 'Derail track'],
|
'derail': ['#f22', 'Derail track'],
|
||||||
|
'unknown': ['#aaa', 'Unknown speed'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Constants;
|
export default Constants;
|
||||||
|
|||||||
@ -1,70 +1,83 @@
|
|||||||
|
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
|
||||||
import SceneryParserLog from "./scenery-parser-log";
|
import SceneryParserLog from "./scenery-parser-log";
|
||||||
import TrackConnection, { TrackConnectionType } from "./track-connection";
|
import Constants from "../helpers/constants";
|
||||||
|
|
||||||
|
const connectionThresholdDistanceSq = 0.05;
|
||||||
|
|
||||||
export function connectTracks(scenery) {
|
export function connectTracks(scenery) {
|
||||||
const tracks = scenery.objects['tracks'] || {};
|
_connectRoutes(scenery);
|
||||||
|
_connectRemainingTracks(scenery);
|
||||||
Object.values(tracks).forEach(track => {
|
}
|
||||||
connectTrack(scenery, track, false);
|
|
||||||
connectTrack(scenery, track, true);
|
function _connectRoutes(scenery) {
|
||||||
|
const routeTracks = new Map();
|
||||||
|
|
||||||
|
Object.values(scenery.objects['routes'] || {}).forEach((route) => {
|
||||||
|
if (!route.segments[0]) return;
|
||||||
|
route.segments[0].tracks.forEach((track) => {
|
||||||
|
routeTracks.set(track.id, track);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.values(scenery.objects['tracks'] || {}).forEach((track) => {
|
||||||
|
track.connections.forEach((connection) => {
|
||||||
|
const routeTrack = routeTracks.get(connection.otherTrackId);
|
||||||
|
if (!routeTrack) return;
|
||||||
|
const alreadyConnected = routeTrack.connections.some(
|
||||||
|
(reverseConnection) => reverseConnection.otherTrackId === track.id,
|
||||||
|
);
|
||||||
|
if (alreadyConnected) return;
|
||||||
|
routeTrack.connections.push(new TrackConnection(track, TrackConnectionEnd.START));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectTrack(scenery, track, isPrev) {
|
function _connectRemainingTracks(scenery) {
|
||||||
const vNext = isPrev ? track.previd : track.nextid;
|
Object.values(scenery.objects['tracks'] || {}).forEach((track) => {
|
||||||
if (!vNext) return;
|
track.connections.forEach((connection) => {
|
||||||
|
_checkConnection(scenery, track, connection);
|
||||||
const vNextTrackId = scenery.getTrackIdByAlias(vNext);
|
});
|
||||||
const vNextTrack = scenery.getObject('tracks', vNextTrackId);
|
track.connections = track.connections.filter((connection) => connection.otherTrack !== null);
|
||||||
|
});
|
||||||
if (!vNextTrack) {
|
}
|
||||||
SceneryParserLog.warn('connectTracks', `Track ${track.id} has non-existing ${isPrev ? 'previous' : 'next'} track: ${isPrev ? track.previd : track.nextid} (${vNextTrackId})`);
|
|
||||||
|
function _checkConnection(scenery, track, connection) {
|
||||||
|
if (connection.otherTrack === null) {
|
||||||
|
const otherTrack = scenery.getObject('tracks', connection.otherTrackId);
|
||||||
|
if (!otherTrack) {
|
||||||
|
SceneryParserLog.warn('connectTracksFailed', `Track ${track.id} has a non-existing ${connection.end === TrackConnectionEnd.START ? 'previous' : 'next'} track: ${connection.otherTrackId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connection.otherTrack = otherTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Constants.parser.runTracksConnectionTest) return;
|
||||||
|
|
||||||
|
const reverseConnections = connection.otherTrack.connections.filter(
|
||||||
|
(otherConnection) => otherConnection.otherTrackId === track.id,
|
||||||
|
);
|
||||||
|
if (reverseConnections.length > 1) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'tracksConnectionTest',
|
||||||
|
`Track ${connection.otherTrack.id} has multiple connections to ${track.id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (reverseConnections.length === 0) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'tracksConnectionTest',
|
||||||
|
`Track ${track.id} is connected to ${connection.otherTrackId} but there is no reverse connection`,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const reverseConnection = reverseConnections[0];
|
||||||
const vNextTrackNextId = scenery.getTrackIdByAlias(vNextTrack.nextid);
|
|
||||||
const vNextTrackPrevId = scenery.getTrackIdByAlias(vNextTrack.previd);
|
const ownPosition = track.getEndPos(connection.end);
|
||||||
const isConnectedAsNext = vNextTrackPrevId === track.id || track.aliases.includes(vNextTrackPrevId);
|
const otherPosition = connection.otherTrack.getEndPos(reverseConnection.end);
|
||||||
const isConnectedAsPrev = vNextTrackNextId === track.id || track.aliases.includes(vNextTrackNextId);
|
const distSq = ownPosition.distanceSq(otherPosition);
|
||||||
|
if (distSq > connectionThresholdDistanceSq) {
|
||||||
let otherConnectionType = isConnectedAsNext ? TrackConnectionType.START : TrackConnectionType.END;
|
SceneryParserLog.warn(
|
||||||
let addReverseConnection = false;
|
'tracksConnectionTest',
|
||||||
|
`Track ${track.id} with ${connection.end === TrackConnectionEnd.START ? 'start' : 'end'} position ${ownPosition.toString()} is too far from the track ${connection.otherTrackId} with ${connection.end === TrackConnectionEnd.START ? 'start' : 'end'} position ${otherPosition.toString()}. Distance: ${Math.sqrt(distSq).toFixed(3)}`,
|
||||||
if (!isConnectedAsNext && !isConnectedAsPrev) {
|
);
|
||||||
// switch connection
|
|
||||||
if(!track.switch) {
|
|
||||||
SceneryParserLog.warn('connectTracksFailed', `${isPrev ? 'Previous' : 'Next'} track ${vNextTrackId} is not connected back to track ${track.id} (${track.previd} / ${track.nextid}). Cannot connect tracks`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const switchId = track.switch.id;
|
|
||||||
const isConnectedAsSwitchNext = vNextTrackNextId.slice(0, -1) === switchId;
|
|
||||||
const isConnectedAsSwitchPrev = vNextTrackPrevId.slice(0, -1) === switchId;
|
|
||||||
|
|
||||||
if(!isConnectedAsSwitchNext && !isConnectedAsSwitchPrev) {
|
|
||||||
SceneryParserLog.warn('connectTracksFailed', `${isPrev ? 'Previous' : 'Next'} track ${vNextTrackId} is not connected back to switch track ${track.id} (${track.previd} / ${track.nextid}). Cannot connect tracks`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
otherConnectionType = isConnectedAsSwitchNext ? TrackConnectionType.START : TrackConnectionType.END;
|
|
||||||
addReverseConnection = true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const mainConnectionType = isPrev ? TrackConnectionType.START : TrackConnectionType.END;
|
|
||||||
|
|
||||||
track.connections.push(new TrackConnection(
|
|
||||||
track,
|
|
||||||
vNextTrack,
|
|
||||||
mainConnectionType,
|
|
||||||
otherConnectionType
|
|
||||||
));
|
|
||||||
|
|
||||||
if(!addReverseConnection) return;
|
|
||||||
|
|
||||||
vNextTrack.connections.push(new TrackConnection(
|
|
||||||
vNextTrack,
|
|
||||||
track,
|
|
||||||
otherConnectionType,
|
|
||||||
mainConnectionType
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,573 +1,96 @@
|
|||||||
import Vector3 from "../vector3";
|
import SwitchPrefab from "../switch-descriptions/switch-prefab";
|
||||||
|
|
||||||
const DefinedSwitches = {
|
const DefinedSwitches = {
|
||||||
"Crossing": [
|
"Crossing": SwitchPrefab.crossing(33.2294, 9.0),
|
||||||
0,
|
"Crossing4.444": SwitchPrefab.crossing(20.0, 4.444),
|
||||||
0,
|
|
||||||
[0,1],
|
|
||||||
new Vector3(-0.919, 0, -16.59),
|
|
||||||
new Vector3(0.919, 0, -16.59),
|
|
||||||
new Vector3(0.919, 0, 16.59),
|
|
||||||
new Vector3(-0.919, 0, 16.59)
|
|
||||||
],
|
|
||||||
"Crossing4.444": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[0,1],
|
|
||||||
new Vector3(-1.104, 0, -9.939),
|
|
||||||
new Vector3(1.104, 0, -9.939),
|
|
||||||
new Vector3(1.104, 0, 9.939),
|
|
||||||
new Vector3(-1.104, 0, 9.939)
|
|
||||||
],
|
|
||||||
"Rz 60E1-300-1_9 R": [
|
|
||||||
0,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4, 2, 1],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 33.231),
|
|
||||||
new Vector3(1.835, 0, 33.129)
|
|
||||||
],
|
|
||||||
"Rz 60E1-300-1_9 L": [
|
|
||||||
0,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4, 2, 1],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 33.231),
|
|
||||||
new Vector3(-1.835, 0, 33.129)
|
|
||||||
],
|
|
||||||
"Rz 60E1-190-1_9 R": [
|
|
||||||
0,
|
|
||||||
-190,
|
|
||||||
[0, 0, 5, 6, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 27.139),
|
|
||||||
new Vector3(1.835, 0, 27.037)
|
|
||||||
],
|
|
||||||
"Rz 60E1-190-1_9 L": [
|
|
||||||
0,
|
|
||||||
190,
|
|
||||||
[0, 0, 5, 6, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 27.139),
|
|
||||||
new Vector3(-1.835, 0, 27.037)
|
|
||||||
],
|
|
||||||
"Rz 60E1-500-1_12 R": [
|
|
||||||
0,
|
|
||||||
-500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 41.595),
|
|
||||||
new Vector3(1.727, 0, 41.523)
|
|
||||||
],
|
|
||||||
"Rz 60E1-500-1_12 L": [
|
|
||||||
0,
|
|
||||||
500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 41.595),
|
|
||||||
new Vector3(-1.727, 0, 41.523)
|
|
||||||
],
|
|
||||||
"Rkpd 60E1-190-1_9": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[0, 1, 2, 3],
|
|
||||||
new Vector3(-0.918, 0, -16.557),
|
|
||||||
new Vector3(0.917, 0, -16.557),
|
|
||||||
new Vector3(0.917, 0, 16.557),
|
|
||||||
new Vector3(-0.917, 0, 16.557)
|
|
||||||
],
|
|
||||||
"Rkp 60E1-190-1_9 ab": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[0, 1, 2, 3],
|
|
||||||
new Vector3(-0.918, 0, -16.557),
|
|
||||||
new Vector3(0.917, 0, -16.556),
|
|
||||||
new Vector3(0.917, 0, 16.558),
|
|
||||||
new Vector3(-0.921, 0, 16.558)
|
|
||||||
],
|
|
||||||
"Rkp 60E1-190-1_9 ba": [
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
[0, 1, 2, 3],
|
|
||||||
new Vector3(-0.918, 0, -16.557),
|
|
||||||
new Vector3(0.917, 0, -16.556),
|
|
||||||
new Vector3(0.917, 0, 16.558),
|
|
||||||
new Vector3(-0.921, 0, 16.558)
|
|
||||||
],
|
|
||||||
"Rz 60E1-265-1_10 R": [
|
|
||||||
0,
|
|
||||||
-265,
|
|
||||||
[0, 0, 5, 6],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 31.181),
|
|
||||||
new Vector3(1.789, 0, 31.094)
|
|
||||||
],
|
|
||||||
"Rz 60E1-265-1_10 L": [
|
|
||||||
0,
|
|
||||||
265,
|
|
||||||
[0, 0, 5, 6],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 31.181),
|
|
||||||
new Vector3(-1.789, 0, 31.094)
|
|
||||||
],
|
|
||||||
"Rz 60E1-760-1_14 R": [
|
|
||||||
0,
|
|
||||||
-760,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 54.217),
|
|
||||||
new Vector3(1.931, 0, 54.148)
|
|
||||||
],
|
|
||||||
"Rz 60E1-760-1_14 L": [
|
|
||||||
0,
|
|
||||||
760,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 54.217),
|
|
||||||
new Vector3(-1.931, 0, 54.148)
|
|
||||||
],
|
|
||||||
"Rz 60E1-1200-1_18.5 R": [
|
|
||||||
0,
|
|
||||||
-1200,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 64.817),
|
|
||||||
new Vector3(1.749, 0, 64.77)
|
|
||||||
],
|
|
||||||
"Rz 60E1-1200-1_18.5 L": [
|
|
||||||
0,
|
|
||||||
1200,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 64.817),
|
|
||||||
new Vector3(-1.75, 0, 64.77)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_250-1_8.5 R": [
|
|
||||||
2500,
|
|
||||||
-250,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.172, 0, 29.31),
|
|
||||||
new Vector3(-1.716, 0, 29.244)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_250-1_8.5 L": [
|
|
||||||
-2500,
|
|
||||||
250,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.172, 0, 29.31),
|
|
||||||
new Vector3(1.716, 0, 29.244)
|
|
||||||
],
|
|
||||||
"Rld 60E1-700_300-1_10 R": [
|
|
||||||
700,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.616, 0, 29.377),
|
|
||||||
new Vector3(1.438, 0, 29.339)
|
|
||||||
],
|
|
||||||
"Rld 60E1-700_300-1_10 L": [
|
|
||||||
-700,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.616, 0, 29.377),
|
|
||||||
new Vector3(-1.438, 0, 29.339)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1800_300-1_9 R": [
|
|
||||||
1800,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.307, 0, 33.228),
|
|
||||||
new Vector3(1.838, 0, 33.163)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1800_300-1_9 L": [
|
|
||||||
-1800,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.307, 0, 33.228),
|
|
||||||
new Vector3(-1.838, 0, 33.163)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_1200-1_22 R": [
|
|
||||||
2500,
|
|
||||||
-1200,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.586, 0, 54.51),
|
|
||||||
new Vector3(1.238, 0, 54.499)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_1200-1_22 L": [
|
|
||||||
-2500,
|
|
||||||
1200,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.586, 0, 54.51),
|
|
||||||
new Vector3(-1.238, 0, 54.499)
|
|
||||||
],
|
|
||||||
"Rld 60E1-650_450-1_15 R": [
|
|
||||||
650,
|
|
||||||
-450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.691, 0, 29.956),
|
|
||||||
new Vector3(0.997, 0, 29.945)
|
|
||||||
],
|
|
||||||
"Rld 60E1-650_450-1_15 L": [
|
|
||||||
-650,
|
|
||||||
450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.691, 0, 29.956),
|
|
||||||
new Vector3(-0.997, 0, 29.945)
|
|
||||||
],
|
|
||||||
"Rld 60E1-600_300-1_9 R": [
|
|
||||||
600,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.749, 0, 29.918),
|
|
||||||
new Vector3(1.49, 0, 29.881)
|
|
||||||
],
|
|
||||||
"Rld 60E1-600_300-1_9 L": [
|
|
||||||
-600,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.749, 0, 29.918),
|
|
||||||
new Vector3(-1.49, 0, 29.881)
|
|
||||||
],
|
|
||||||
"Rz 60E1-205-1_9 R": [
|
|
||||||
0,
|
|
||||||
-205,
|
|
||||||
[0, 0, 5, 6],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 28.127),
|
|
||||||
new Vector3(1.853, 0, 28.025)
|
|
||||||
],
|
|
||||||
"Rz 60E1-205-1_9 L": [
|
|
||||||
0,
|
|
||||||
205,
|
|
||||||
[0, 0, 5, 6],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 28.127),
|
|
||||||
new Vector3(-1.853, 0, 28.025),
|
|
||||||
],
|
|
||||||
"Rz 60E1-190-1_7.5 R": [
|
|
||||||
0,
|
|
||||||
-190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 25.221),
|
|
||||||
new Vector3(1.667, 0, 25.111)
|
|
||||||
],
|
|
||||||
"Rz 60E1-190-1_7.5 L": [
|
|
||||||
0,
|
|
||||||
190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 25.221),
|
|
||||||
new Vector3(-1.667, 0, 25.111)
|
|
||||||
],
|
|
||||||
"Rz 60E1-2500-1_26.5 R": [
|
|
||||||
0,
|
|
||||||
-2500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 94.306),
|
|
||||||
new Vector3(1.778, 0, 94.273)
|
|
||||||
],
|
|
||||||
"Rz 60E1-2500-1_26.5 L": [
|
|
||||||
0,
|
|
||||||
2500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 94.306),
|
|
||||||
new Vector3(-1.778, 0, 94.273)
|
|
||||||
],
|
|
||||||
"Rlds 60E1-600-600-1_18.5": [
|
|
||||||
-600,
|
|
||||||
600,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.92, 0, 33.206),
|
|
||||||
new Vector3(-0.92, 0, 33.206)
|
|
||||||
],
|
|
||||||
"Rlds 60E1-190-190-1_9": [
|
|
||||||
-190,
|
|
||||||
190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(1.184, 0, 21.186),
|
|
||||||
new Vector3(-1.185, 0, 21.186)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-650_190-1_20 R": [
|
|
||||||
-650,
|
|
||||||
-190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.811, 0, 32.466),
|
|
||||||
new Vector3(2.77, 0, 32.332)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-650_190-1_20 L": [
|
|
||||||
650,
|
|
||||||
190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.811, 0, 32.466),
|
|
||||||
new Vector3(-2.77, 0, 32.332)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1200_300-1_7 R": [
|
|
||||||
-1200,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.758, 0, 42.632),
|
|
||||||
new Vector3(3.025, 0, 42.498)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1200_300-1_7 L": [
|
|
||||||
1200,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.758, 0, 42.632),
|
|
||||||
new Vector3(-3.025, 0, 42.498)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-750_190-1_6 R": [
|
|
||||||
-750,
|
|
||||||
-190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.66, 0, 31.44),
|
|
||||||
new Vector3(2.597, 0, 31.306)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-750_190-1_6 L": [
|
|
||||||
750,
|
|
||||||
190,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.66, 0, 31.44),
|
|
||||||
new Vector3(-2.597, 0, 31.306)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1200_600-1_15 R": [
|
|
||||||
1200,
|
|
||||||
-600,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.665, 0, 39.948),
|
|
||||||
new Vector3(1.33, 0, 39.926)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1200_600-1_15 L": [
|
|
||||||
-1200,
|
|
||||||
600,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.665, 0, 39.948),
|
|
||||||
new Vector3(-1.33, 0, 39.926)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1200_900-1_18.5 R": [
|
|
||||||
1200,
|
|
||||||
-900,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.984, 0, 48.6),
|
|
||||||
new Vector3(1.313, 0, 48.589)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1200_900-1_18.5 L": [
|
|
||||||
-1200,
|
|
||||||
900,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.984, 0, 48.6),
|
|
||||||
new Vector3(-1.313, 0, 48.589)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-900_300-1_7.5 R": [
|
|
||||||
-900,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.881, 0, 39.81),
|
|
||||||
new Vector3(2.639, 0, 39.706)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-900_300-1_7.5 L": [
|
|
||||||
900,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.881, 0, 39.81),
|
|
||||||
new Vector3(-2.639, 0, 39.706)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-600_300-1_6 R": [
|
|
||||||
-600,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(1.919, 0, 47.949),
|
|
||||||
new Vector3(3.832, 0, 47.795)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-600_300-1_6 L": [
|
|
||||||
600,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-1.919, 0, 47.949),
|
|
||||||
new Vector3(-3.832, 0, 47.795)
|
|
||||||
],
|
|
||||||
"Rld 60E1-900_450-1_12 R": [
|
|
||||||
900,
|
|
||||||
-450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.778, 0, 37.425),
|
|
||||||
new Vector3(1.556, 0, 37.392)
|
|
||||||
],
|
|
||||||
"Rld 60E1-900_450-1_12 L": [
|
|
||||||
-900,
|
|
||||||
450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.778, 0, 37.425),
|
|
||||||
new Vector3(-1.556, 0, 37.392)
|
|
||||||
],
|
|
||||||
"Rld 60E1-700_500-1_14 R": [
|
|
||||||
700,
|
|
||||||
-500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.909, 0, 35.653),
|
|
||||||
new Vector3(1.272, 0, 35.639)
|
|
||||||
],
|
|
||||||
"Rld 60E1-700_500-1_14 L": [
|
|
||||||
-700,
|
|
||||||
500,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.909, 0, 35.653),
|
|
||||||
new Vector3(-1.272, 0, 35.639)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_400-1_10.5 R": [
|
|
||||||
2500,
|
|
||||||
-400,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.289, 0, 38.008),
|
|
||||||
new Vector3(1.805, 0, 37.952)
|
|
||||||
],
|
|
||||||
"Rld 60E1-2500_400-1_10.5 L": [
|
|
||||||
-2500,
|
|
||||||
400,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.289, 0, 38.008),
|
|
||||||
new Vector3(-1.805, 0, 37.952)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1800_300-1_7.5 R": [
|
|
||||||
-1800,
|
|
||||||
-300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.423, 0, 38.998),
|
|
||||||
new Vector3(2.531, 0, 38.89)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1800_300-1_7.5 L": [
|
|
||||||
1800,
|
|
||||||
300,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.423, 0, 38.998),
|
|
||||||
new Vector3(-2.531, 0, 38.89)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1800_600-1_14 R": [
|
|
||||||
1800,
|
|
||||||
-600,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.509, 0, 42.798),
|
|
||||||
new Vector3(1.526, 0, 42.767)
|
|
||||||
],
|
|
||||||
"Rld 60E1-1800_600-1_14 L": [
|
|
||||||
-1800,
|
|
||||||
600,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.509, 0, 42.798),
|
|
||||||
new Vector3(-1.526, 0, 42.767)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1800_450-1_9 R": [
|
|
||||||
-1800,
|
|
||||||
-450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0.69, 0, 49.84),
|
|
||||||
new Vector3(2.758, 0, 49.744)
|
|
||||||
],
|
|
||||||
"Rlj 60E1-1800_450-1_9 L": [
|
|
||||||
1800,
|
|
||||||
450,
|
|
||||||
[0, 0, 3, 4],
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(0, 0, 0),
|
|
||||||
new Vector3(-0.69, 0, 49.84),
|
|
||||||
new Vector3(-2.758, 0, 49.745)
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DefinedSwitches;
|
"Rkp 60E1-190-1_9 ab": SwitchPrefab.slip(33.165, 6.06, 7.461676, 190.0, 9.0, false, true),
|
||||||
|
"Rkp 60E1-190-1_9 ba": SwitchPrefab.slip(33.165, 6.06, 7.461676, 190.0, 9.0, false, true),
|
||||||
|
"Rkpd 60E1-190-1_9": SwitchPrefab.slip(33.165, 6.06, 7.461676, 190.0, 9.0, true, true),
|
||||||
|
|
||||||
|
"Rld 60E1-1200_600-1_15 L": SwitchPrefab.fork(600.0, -1200.0, 39.95566),
|
||||||
|
"Rld 60E1-1200_600-1_15 R": SwitchPrefab.fork(-600.0, 1200.0, 39.95566),
|
||||||
|
"Rld 60E1-1200_900-1_18.5 L": SwitchPrefab.fork(900.0, -1200.0, 48.61317),
|
||||||
|
"Rld 60E1-1200_900-1_18.5 R": SwitchPrefab.fork(-900.0, 1200.0, 48.61317),
|
||||||
|
"Rld 60E1-1800_300-1_9 L": SwitchPrefab.fork(300.0, -1800.0, 33.23108),
|
||||||
|
"Rld 60E1-1800_300-1_9 R": SwitchPrefab.fork(-300.0, 1800.0, 33.23108),
|
||||||
|
"Rld 60E1-1800_600-1_14 L": SwitchPrefab.fork(600.0, -1800.0, 42.80262),
|
||||||
|
"Rld 60E1-1800_600-1_14 R": SwitchPrefab.fork(-600.0, 1800.0, 42.80262),
|
||||||
|
"Rld 60E1-1800_900-1_18.5 L": SwitchPrefab.fork(900.0, -1800.0, 48.61317),
|
||||||
|
"Rld 60E1-1800_900-1_18.5 R": SwitchPrefab.fork(-900.0, 1800.0, 48.61317),
|
||||||
|
"Rld 60E1-2500_1200-1_22 L": SwitchPrefab.fork(1200.0, -2500.0, 54.51731),
|
||||||
|
"Rld 60E1-2500_1200-1_22 R": SwitchPrefab.fork(-1200.0, 2500.0, 54.51731),
|
||||||
|
"Rld 60E1-2500_250-1_8.5 L": SwitchPrefab.fork(250.0, -2500.0, 29.31069),
|
||||||
|
"Rld 60E1-2500_250-1_8.5 R": SwitchPrefab.fork(-250.0, 2500.0, 29.31069),
|
||||||
|
"Rld 60E1-2500_400-1_10.5 L": SwitchPrefab.fork(400.0, -2500.0, 38.00925),
|
||||||
|
"Rld 60E1-2500_400-1_10.5 R": SwitchPrefab.fork(-400.0, 2500.0, 38.00925),
|
||||||
|
"Rld 60E1-2500_600-1_14 L": SwitchPrefab.fork(600.0, -2500.0, 42.80262),
|
||||||
|
"Rld 60E1-2500_600-1_14 R": SwitchPrefab.fork(-600.0, 2500.0, 42.80262),
|
||||||
|
"Rld 60E1-2500_900-1_17 L": SwitchPrefab.fork(900.0, -2500.0, 52.14928),
|
||||||
|
"Rld 60E1-2500_900-1_17 R": SwitchPrefab.fork(-900.0, 2500.0, 52.14928),
|
||||||
|
"Rld 60E1-600_300-1_9 L": SwitchPrefab.fork(300.0, -600.0, 29.92537),
|
||||||
|
"Rld 60E1-600_300-1_9 R": SwitchPrefab.fork(-300.0, 600.0, 29.92537),
|
||||||
|
"Rld 60E1-650_450-1_15 L": SwitchPrefab.fork(450.0, -650.0, 29.96674),
|
||||||
|
"Rld 60E1-650_450-1_15 R": SwitchPrefab.fork(-450.0, 650.0, 29.96674),
|
||||||
|
"Rld 60E1-700_190-1_7.5 L": SwitchPrefab.fork(190.0, -700.0, 25.22173),
|
||||||
|
"Rld 60E1-700_190-1_7.5 R": SwitchPrefab.fork(-190.0, 700.0, 25.22173),
|
||||||
|
"Rld 60E1-700_300-1_10 L": SwitchPrefab.fork(300.0, -700.0, 29.38637),
|
||||||
|
"Rld 60E1-700_300-1_10 R": SwitchPrefab.fork(-300.0, 700.0, 29.38637),
|
||||||
|
"Rld 60E1-700_500-1_14 L": SwitchPrefab.fork(500.0, -700.0, 35.66885),
|
||||||
|
"Rld 60E1-700_500-1_14 R": SwitchPrefab.fork(-500.0, 700.0, 35.66885),
|
||||||
|
"Rld 60E1-900_300-1_9 L": SwitchPrefab.fork(300.0, -900.0, 33.23108),
|
||||||
|
"Rld 60E1-900_300-1_9 R": SwitchPrefab.fork(-300.0, 900.0, 33.23108),
|
||||||
|
"Rld 60E1-900_450-1_12 L": SwitchPrefab.fork(450.0, -900.0, 37.43512),
|
||||||
|
"Rld 60E1-900_450-1_12 R": SwitchPrefab.fork(-450.0, 900.0, 37.43512),
|
||||||
|
"Rld 60E1-900_600-1_15 L": SwitchPrefab.fork(600.0, -900.0, 39.95566),
|
||||||
|
"Rld 60E1-900_600-1_15 R": SwitchPrefab.fork(-600.0, 900.0, 39.95566),
|
||||||
|
"Rlds 60E1-1000-1000-1_23": SwitchPrefab.fork(1000.0, -1000.0, 43.45773),
|
||||||
|
"Rlds 60E1-190-190-1_9": SwitchPrefab.fork(190.0, -190.0, 21.23),
|
||||||
|
"Rlds 60E1-600-600-1_18.5": SwitchPrefab.fork(599.9205, -600.0, 33.22262),
|
||||||
|
"Rlj 60E1-1200_300-1_7 L": SwitchPrefab.fork(300.0, 1200.0, 42.64069),
|
||||||
|
"Rlj 60E1-1200_300-1_7 R": SwitchPrefab.fork(-300.0, -1200.0, 42.64069),
|
||||||
|
"Rlj 60E1-1200_600-1_9 L": SwitchPrefab.fork(600.0, 1200.0, 68.0),
|
||||||
|
"Rlj 60E1-1200_600-1_9 R": SwitchPrefab.fork(-600.0, -1200.0, 68.0),
|
||||||
|
"Rlj 60E1-1500_450-1_9 L": SwitchPrefab.fork(450.0, 1500.0, 49.84663),
|
||||||
|
"Rlj 60E1-1500_450-1_9 R": SwitchPrefab.fork(-450.0, -1500.0, 49.84663),
|
||||||
|
"Rlj 60E1-1800_300-1_7.5 L": SwitchPrefab.fork(300.0, 1800.0, 39.0),
|
||||||
|
"Rlj 60E1-1800_300-1_7.5 R": SwitchPrefab.fork(-300.0, -1800.0, 39.0),
|
||||||
|
"Rlj 60E1-1800_450-1_9 L": SwitchPrefab.fork(450.0, 1800.0, 49.84663),
|
||||||
|
"Rlj 60E1-1800_450-1_9 R": SwitchPrefab.fork(-450.0, -1800.0, 49.84663),
|
||||||
|
"Rlj 60E1-1800_600-1_10 L": SwitchPrefab.fork(599.48096, 1800.0, 59.85075),
|
||||||
|
"Rlj 60E1-1800_600-1_10 R": SwitchPrefab.fork(-599.48096, -1800.0, 59.85075),
|
||||||
|
"Rlj 60E1-600_300-1_6 L": SwitchPrefab.fork(300.0, 600.0, 48.0),
|
||||||
|
"Rlj 60E1-600_300-1_6 R": SwitchPrefab.fork(-300.0, -600.0, 48.0),
|
||||||
|
"Rlj 60E1-650_190-1_20 L": SwitchPrefab.fork(190.0, 650.0, 32.47971),
|
||||||
|
"Rlj 60E1-650_190-1_20 R": SwitchPrefab.fork(-190.0, -650.0, 32.47971),
|
||||||
|
"Rlj 60E1-750_190-1_6 L": SwitchPrefab.fork(190.0, 750.0, 31.44976),
|
||||||
|
"Rlj 60E1-750_190-1_6 R": SwitchPrefab.fork(-190.0, -750.0, 31.44976),
|
||||||
|
"Rlj 60E1-800_250-1_6.5 L": SwitchPrefab.fork(250.0, 800.0, 38.23661),
|
||||||
|
"Rlj 60E1-800_250-1_6.5 R": SwitchPrefab.fork(-250.0, -800.0, 38.23661),
|
||||||
|
"Rlj 60E1-900_300-1_7.5 L": SwitchPrefab.fork(300.0, 900.0, 39.82378),
|
||||||
|
"Rlj 60E1-900_300-1_7.5 R": SwitchPrefab.fork(-300.0, -900.0, 39.82378),
|
||||||
|
"Rlj 60E1-900_300-1_9 L": SwitchPrefab.fork(300.0, 900.0, 49.84663),
|
||||||
|
"Rlj 60E1-900_300-1_9 R": SwitchPrefab.fork(-300.0, -900.0, 49.84663),
|
||||||
|
"Rlj 60E1-900_450-1_7.5 L": SwitchPrefab.fork(450.0, 900.0, 59.0),
|
||||||
|
"Rlj 60E1-900_450-1_7.5 R": SwitchPrefab.fork(-450.0, -900.0, 59.0),
|
||||||
|
"Rz 60E1-1200-1_18.5 L": SwitchPrefab.fork(1200.0, 0.0, 64.81756),
|
||||||
|
"Rz 60E1-1200-1_18.5 R": SwitchPrefab.fork(-1200.0, 0.0, 64.81756),
|
||||||
|
"Rz 60E1-190-1_7.5 L": SwitchPrefab.fork(190.0, 0.0, 25.221731),
|
||||||
|
"Rz 60E1-190-1_7.5 R": SwitchPrefab.fork(-190.0, 0.0, 25.221731),
|
||||||
|
"Rz 60E1-190-1_9 L": SwitchPrefab.fork(0.0, 190.0, 21.046352, 6.0923653),
|
||||||
|
"Rz 60E1-190-1_9 R": SwitchPrefab.fork(0.0, -190.0, 21.046352, 6.0923653),
|
||||||
|
"Rz 60E1-205-1_9 L": SwitchPrefab.fork(205.0, 0.0, 22.707907, 5.42 /* added manually */),
|
||||||
|
"Rz 60E1-205-1_9 R": SwitchPrefab.fork(-205.0, 0.0, 22.707907, 5.42 /* added manually */),
|
||||||
|
"Rz 60E1-2500-1_26.5 L": SwitchPrefab.fork(2500.0, 0.0, 94.30607),
|
||||||
|
"Rz 60E1-2500-1_26.5 R": SwitchPrefab.fork(-2500.0, 0.0, 94.30607),
|
||||||
|
"Rz 60E1-265-1_10 L": SwitchPrefab.fork(0.0, 265.0, 26.434078, 4.75 /* added manually */),
|
||||||
|
"Rz 60E1-265-1_10 R": SwitchPrefab.fork(0.0, -265.0, 26.434078, 4.75 /* added manually */),
|
||||||
|
"Rz 60E1-300-1_9 L": SwitchPrefab.fork(300.0, 0.0, 33.231083),
|
||||||
|
"Rz 60E1-300-1_9 R": SwitchPrefab.fork(-300.0, 0.0, 33.231083),
|
||||||
|
"Rz 60E1-500-1_12 L": SwitchPrefab.fork(500.0, 0.0, 41.59458),
|
||||||
|
"Rz 60E1-500-1_12 R": SwitchPrefab.fork(-500.0, 0.0, 41.59458),
|
||||||
|
"Rz 60E1-760-1_14 L": SwitchPrefab.fork(760.0, 0.0, 54.21665),
|
||||||
|
"Rz 60E1-760-1_14 R": SwitchPrefab.fork(-760.0, 0.0, 54.21665),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DefinedSwitches;
|
||||||
|
|||||||
@ -2,16 +2,6 @@ import Constants from "../helpers/constants";
|
|||||||
import SceneryParserLog from "./scenery-parser-log";
|
import SceneryParserLog from "./scenery-parser-log";
|
||||||
import {ElectrificationStatus, ElectrificationResolutionStatus} from "./electrification-status";
|
import {ElectrificationStatus, ElectrificationResolutionStatus} from "./electrification-status";
|
||||||
|
|
||||||
class RouteTrack {
|
|
||||||
route;
|
|
||||||
track;
|
|
||||||
electrified;
|
|
||||||
|
|
||||||
constructor(route, track, electrified) {
|
|
||||||
Object.assign(this, { route, track, electrified });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ElectrificationResolver {
|
export default class ElectrificationResolver {
|
||||||
static hasWarnings = false;
|
static hasWarnings = false;
|
||||||
static propagationQueue = [];
|
static propagationQueue = [];
|
||||||
@ -21,21 +11,22 @@ export default class ElectrificationResolver {
|
|||||||
ElectrificationResolver.propagationQueue = [];
|
ElectrificationResolver.propagationQueue = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const routeTracks = this._findRouteTracks(scenery);
|
const routeTracks = this._findTracksAdjacentToRoutes(scenery);
|
||||||
this._markNEVPTracks(scenery);
|
this._markNEVPTracks(scenery);
|
||||||
|
|
||||||
routeTracks.forEach(routeTrack => {
|
routeTracks.forEach(({ track, routeTrack }) => {
|
||||||
this._propagate(
|
this._propagate(
|
||||||
routeTrack.track,
|
track,
|
||||||
[],
|
routeTrack.id,
|
||||||
routeTrack.electrified ? ElectrificationStatus.ELECTRIFIED : ElectrificationStatus.NON_ELECTRIFIED
|
routeTrack.electrificationStatus,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._runResolution();
|
this._runResolution();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
scenery.electrificationResolved = ElectrificationResolutionStatus.ERROR;
|
scenery.electrificationResolved = ElectrificationResolutionStatus.RESOLVING_ERROR;
|
||||||
SceneryParserLog.warn('electrificationResolverError', `Error resolving electrification: ${error.message}`);
|
SceneryParserLog.warn('electrificationResolverError', `Error resolving electrification: ${error.message}`);
|
||||||
|
console.warn(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,48 +44,18 @@ export default class ElectrificationResolver {
|
|||||||
SceneryParserLog.warn(type, message);
|
SceneryParserLog.warn(type, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _checkTrackConnectedToRoute(track, routePoint) {
|
static _findTracksAdjacentToRoutes(scenery) {
|
||||||
const mStartDist = track.points.start.mannhattanDistance(routePoint.point);
|
return Object.values(scenery.objects.routes || {}).flatMap(route => {
|
||||||
if(mStartDist <= Constants.parser.maxRouteConnectionDistance) {
|
if (route.segments.length === 0) return;
|
||||||
const startDistSq = track.points.start.distanceSq(routePoint.point);
|
return route.segments[0].tracks.flatMap((routeTrack) => {
|
||||||
if(startDistSq <= Constants.parser.maxRouteConnectionDistance ** 2) {
|
return routeTrack.connections
|
||||||
return true;
|
.map((connection) => {
|
||||||
}
|
if (connection.otherTrack.type === 'RouteTrack') return null;
|
||||||
}
|
return { track: connection.otherTrack, routeTrack };
|
||||||
|
})
|
||||||
const mEndDist = track.points.end.mannhattanDistance(routePoint.point);
|
.filter((adjacent) => adjacent !== null);
|
||||||
if(mEndDist <= Constants.parser.maxRouteConnectionDistance) {
|
|
||||||
const endDistSq = track.points.end.distanceSq(routePoint.point);
|
|
||||||
if(endDistSq <= Constants.parser.maxRouteConnectionDistance ** 2) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static _findRouteTracks(scenery) {
|
|
||||||
const routePoints = Object.values(scenery.objects.routes || {})
|
|
||||||
.map(route => route.points.map(point => ({ route, point }))).flat();
|
|
||||||
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
Object.values(scenery.objects.tracks || {}).forEach(track => {
|
|
||||||
routePoints.forEach(routePoint => {
|
|
||||||
if (this._checkTrackConnectedToRoute(track, routePoint)) {
|
|
||||||
results.push(new RouteTrack(routePoint.route, track, routePoint.route.electrified));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if(results.length < routePoints.length) {
|
|
||||||
ElectrificationResolver._passWarn(
|
|
||||||
'electrificationMissingRouteTracks',
|
|
||||||
`While resolving electrification, found ${results.length} tracks connected to routes, but expected ${routePoints.length}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static _markNEVPTracks(scenery) {
|
static _markNEVPTracks(scenery) {
|
||||||
@ -103,7 +64,7 @@ export default class ElectrificationResolver {
|
|||||||
|
|
||||||
if(!trackObject.track) {
|
if(!trackObject.track) {
|
||||||
ElectrificationResolver._passWarn(
|
ElectrificationResolver._passWarn(
|
||||||
'electrificationNevpNotApplied',
|
'electrificationNevpNotApplied',
|
||||||
`NEVP object ${trackObject.name} (${trackObject.id}) at track ${trackObject.track_id} has not been applied. Electrification resolution may fail`
|
`NEVP object ${trackObject.name} (${trackObject.id}) at track ${trackObject.track_id} has not been applied. Electrification resolution may fail`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -115,16 +76,16 @@ export default class ElectrificationResolver {
|
|||||||
|
|
||||||
static _runResolution() {
|
static _runResolution() {
|
||||||
while(ElectrificationResolver.propagationQueue.length > 0) {
|
while(ElectrificationResolver.propagationQueue.length > 0) {
|
||||||
const { track, skipTrackIds, status } = ElectrificationResolver.propagationQueue.shift();
|
const { track, skipTrackId, status } = ElectrificationResolver.propagationQueue.pop();
|
||||||
ElectrificationResolver._resolveTrack(track, skipTrackIds, status);
|
ElectrificationResolver._resolveTrack(track, skipTrackId, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _propagate(track, skipTrackIds, status) {
|
static _propagate(track, skipTrackId, status) {
|
||||||
ElectrificationResolver.propagationQueue.push({ track, skipTrackIds, status });
|
ElectrificationResolver.propagationQueue.push({ track, skipTrackId, status });
|
||||||
}
|
}
|
||||||
|
|
||||||
static _resolveTrack(track, skipTrackIds, status) {
|
static _resolveTrack(track, skipTrackId, status) {
|
||||||
if (track.electrificationStatus === status) return; // already set
|
if (track.electrificationStatus === status) return; // already set
|
||||||
if (Constants.parser.skipElectrificationErrorsPropagation && status === ElectrificationStatus.CONFLICT) {
|
if (Constants.parser.skipElectrificationErrorsPropagation && status === ElectrificationStatus.CONFLICT) {
|
||||||
return;
|
return;
|
||||||
@ -137,38 +98,24 @@ export default class ElectrificationResolver {
|
|||||||
const fsne = status === ElectrificationStatus.NON_ELECTRIFIED;
|
const fsne = status === ElectrificationStatus.NON_ELECTRIFIED;
|
||||||
const tse = track.electrificationStatus === ElectrificationStatus.ELECTRIFIED;
|
const tse = track.electrificationStatus === ElectrificationStatus.ELECTRIFIED;
|
||||||
const tsne = track.electrificationStatus === ElectrificationStatus.NON_ELECTRIFIED;
|
const tsne = track.electrificationStatus === ElectrificationStatus.NON_ELECTRIFIED;
|
||||||
|
|
||||||
track.electrificationStatus = status;
|
track.electrificationStatus = status;
|
||||||
|
|
||||||
if (track.hasNEVP) {
|
if (track.hasNEVP) {
|
||||||
track.electrificationStatus = ElectrificationStatus.NON_ELECTRIFIED;
|
track.electrificationStatus = ElectrificationStatus.NON_ELECTRIFIED;
|
||||||
if(!fse) return;
|
if(!fse) return;
|
||||||
} else if ((tse && fsne) || (tsne && fse)) {
|
} else if ((tse && fsne) || (tsne && fse)) {
|
||||||
track.electrificationStatus = ElectrificationStatus.CONFLICT;
|
track.electrificationStatus = ElectrificationStatus.CONFLICT;
|
||||||
ElectrificationResolver._passWarn(
|
ElectrificationResolver._passWarn(
|
||||||
'electrificationConflict',
|
'electrificationConflict',
|
||||||
`Track ${track.id} has conflicting electrification status (=>${status})`
|
`Track ${track.id} has conflicting electrification status (=>${status})`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const propagation = track.connections.map(conn => conn.otherTrack);
|
track.connections.forEach(connection => {
|
||||||
const nextSkipIds = [track.id];
|
if (skipTrackId === connection.otherTrackId) return;
|
||||||
|
this._propagate(connection.otherTrack, track.id, track.electrificationStatus);
|
||||||
if (track.switch) {
|
|
||||||
nextSkipIds.push(track.switch.trackA.id, track.switch.trackB.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
propagation.forEach(nextTrack => {
|
|
||||||
if (skipTrackIds.includes(nextTrack.id)) return;
|
|
||||||
|
|
||||||
this._propagate(nextTrack, nextSkipIds, track.electrificationStatus);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Additional propagation for double switches
|
|
||||||
if (!track.switch || track.switch.def?.[2]?.length <= 2 || track.hasNEVP) return;
|
|
||||||
|
|
||||||
const otherSwitchTrack = track.switch.trackA === track ? track.switch.trackB : track.switch.trackA;
|
|
||||||
this._propagate(otherSwitchTrack, [track.id, ...propagation.map(track => track.id)], track.electrificationStatus);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import SceneryObject from "./scenery-object";
|
import SceneryObject from "./scenery-object";
|
||||||
import Vector3 from "./vector3";
|
import Vector3 from "./vector3";
|
||||||
|
import SceneryParserLog from "./scenery-parser-log";
|
||||||
|
import RouteTrack from "./tracks/route-track";
|
||||||
|
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
|
||||||
|
import AngleHelper from "../helpers/angleHelper";
|
||||||
|
|
||||||
export default class Route extends SceneryObject {
|
export default class Route extends SceneryObject {
|
||||||
track_count;
|
track_count;
|
||||||
@ -8,7 +12,12 @@ export default class Route extends SceneryObject {
|
|||||||
track_offset;
|
track_offset;
|
||||||
category = "routes";
|
category = "routes";
|
||||||
type = "Route";
|
type = "Route";
|
||||||
points = [];
|
points;
|
||||||
|
offsets;
|
||||||
|
segments = [];
|
||||||
|
end_center;
|
||||||
|
end_points;
|
||||||
|
end_angle_rad;
|
||||||
|
|
||||||
constructor(prefab_name, pos, rot, track_count, route_name, track_offset, electrified) {
|
constructor(prefab_name, pos, rot, track_count, route_name, track_offset, electrified) {
|
||||||
super(route_name, pos, rot);
|
super(route_name, pos, rot);
|
||||||
@ -21,10 +30,14 @@ export default class Route extends SceneryObject {
|
|||||||
electrified,
|
electrified,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.points = this._getConnectionPoints();
|
this.offsets = this._getOffsets();
|
||||||
|
this.end_angle_rad = this.rot.y * Math.PI / 180;
|
||||||
|
this.end_center = this.pos.clone();
|
||||||
|
this.end_points = this._getConnectionPoints();
|
||||||
|
this.points = [...this.end_points];
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromText(text) {
|
static fromText(text) {
|
||||||
const values = text.split(";");
|
const values = text.split(";");
|
||||||
const route = new Route(
|
const route = new Route(
|
||||||
values[2], // prefab_name
|
values[2], // prefab_name
|
||||||
@ -32,25 +45,104 @@ export default class Route extends SceneryObject {
|
|||||||
Vector3.fromValuesArray(values, 6), // rot
|
Vector3.fromValuesArray(values, 6), // rot
|
||||||
parseInt(values[9]), // track_count
|
parseInt(values[9]), // track_count
|
||||||
values[10], // route_name
|
values[10], // route_name
|
||||||
parseFloat(values[11]) || 0, // track_offset
|
parseFloat(values[11]) || 0, // center_offset
|
||||||
values[13] === "True" || values[12] === "1" // electrified
|
values[13] === "True" || values[12] === "1" // electrified
|
||||||
);
|
);
|
||||||
|
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getConnectionPoints() {
|
addSegment(length, radius, trackIds) {
|
||||||
if (this.track_count === 1) {
|
if (trackIds.length !== this.track_count) {
|
||||||
return [this.pos.clone()];
|
SceneryParserLog.warn('routeInvalidSegment', `Route segment has invalid track count: expected ${this.track_count}, got ${trackIds.length}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const startAngleRad = this.end_angle_rad;
|
||||||
|
const startRot = new Vector3(0, AngleHelper.radToDeg(startAngleRad), 0);
|
||||||
|
|
||||||
|
const startPoints = this.end_points;
|
||||||
|
this.end_points = [];
|
||||||
|
|
||||||
|
const radii = [];
|
||||||
|
const lengths = [];
|
||||||
|
|
||||||
|
if (radius === 0) {
|
||||||
|
this.end_center = this.end_center.add(Vector3.fromAngleY(startAngleRad, length));
|
||||||
|
this.offsets.forEach(offset => {
|
||||||
|
radii.push(0);
|
||||||
|
lengths.push(length);
|
||||||
|
const end_point = this.end_center
|
||||||
|
.add(Vector3.fromAngleY(this.end_angle_rad + Math.PI / 2, offset));
|
||||||
|
this.end_points.push(end_point);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.end_angle_rad -= length / radius;
|
||||||
|
const startToCenter = Vector3.fromAngleY(startAngleRad + Math.sign(radius) * Math.PI / 2, Math.abs(radius));
|
||||||
|
const circleCenter = this.end_center.add(startToCenter.negate());
|
||||||
|
const centerToEndUnit = Vector3.fromAngleY(this.end_angle_rad + Math.sign(radius) * Math.PI / 2, 1).multiply(Math.sign(radius));
|
||||||
|
this.end_center = circleCenter.add(centerToEndUnit.multiply(radius));
|
||||||
|
|
||||||
|
this.offsets.forEach(offset => {
|
||||||
|
const trackRadius = radius + offset;
|
||||||
|
const trackLength = length * trackRadius / radius;
|
||||||
|
radii.push(trackRadius);
|
||||||
|
lengths.push(trackLength);
|
||||||
|
const end_point = circleCenter.add(centerToEndUnit.multiply(trackRadius));
|
||||||
|
this.end_points.push(end_point);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const angle = (this.rot.y + 90) * Math.PI / 180;
|
this.points.push(...this.end_points);
|
||||||
const rightTrackOffset = Vector3.fromAngleY(angle, this.track_offset - 2);
|
|
||||||
const leftTrackOffset = Vector3.fromAngleY(angle, this.track_offset + 2);
|
|
||||||
|
|
||||||
return [
|
const tracks = trackIds.map((trackId, index) => {
|
||||||
this.pos.add(rightTrackOffset),
|
return new RouteTrack(
|
||||||
this.pos.add(leftTrackOffset)
|
trackId,
|
||||||
];
|
startPoints[index],
|
||||||
|
this.end_points[index],
|
||||||
|
startRot,
|
||||||
|
lengths[index],
|
||||||
|
radii[index],
|
||||||
|
[], // connections
|
||||||
|
this.electrified,
|
||||||
|
this, // route
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.segments.length >= 2) {
|
||||||
|
const prevSegment = this.segments[this.segments.length - 2];
|
||||||
|
const newSegment = this.segments[this.segments.length - 1];
|
||||||
|
newSegment.tracks.forEach((track, index) => {
|
||||||
|
const prevTrack = prevSegment.tracks[index];
|
||||||
|
track.connections.push(new TrackConnection(prevTrack, TrackConnectionEnd.START));
|
||||||
|
prevTrack.connections.push(new TrackConnection(track, TrackConnectionEnd.END));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.segments.push({ length, radius, trackIds, tracks });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// This method is future-proofed for more than 2 tracks
|
||||||
|
_getOffsets() {
|
||||||
|
const leftmost_offset = (this.track_count - 1) * 2 + this.track_offset;
|
||||||
|
const result = [];
|
||||||
|
for (let i = 0; i < this.track_count; i++) {
|
||||||
|
result.push(leftmost_offset - i * 4);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getConnectionPoints() {
|
||||||
|
return this.offsets.map((offset) => {
|
||||||
|
const offsetVec = Vector3.fromAngleY(this.end_angle_rad + Math.PI / 2, offset);
|
||||||
|
return this.pos.add(offsetVec);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
applyObject(scenery) {
|
||||||
|
this.segments.forEach((segment) => {
|
||||||
|
segment.tracks.forEach((track) => {
|
||||||
|
scenery.addObject(track);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import Scenery from './scenery';
|
import Scenery from './scenery';
|
||||||
import { tracksConnectionTest } from './tracks-connection-test';
|
|
||||||
import Switch from './switch';
|
import Switch from './switch';
|
||||||
import StandardTrack from './tracks/standard-track';
|
import StandardTrack from './tracks/standard-track';
|
||||||
import BezierTrack from './tracks/bezier-track';
|
import BezierTrack from './tracks/bezier-track';
|
||||||
@ -19,7 +18,7 @@ import NEVP from './track-objects/nevp';
|
|||||||
import Derailer from './track-objects/derailer';
|
import Derailer from './track-objects/derailer';
|
||||||
import SpawnPoint from './track-objects/spawn-point';
|
import SpawnPoint from './track-objects/spawn-point';
|
||||||
import { attachSigns } from './attach-signs';
|
import { attachSigns } from './attach-signs';
|
||||||
import { connectTracks } from './connect-tracks';
|
import {connectTracks} from "./connect-tracks";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: add support for WorldRotation and WorldTranslation
|
TODO: add support for WorldRotation and WorldTranslation
|
||||||
@ -30,23 +29,36 @@ export default class SceneryParser {
|
|||||||
const lines = scText.split("\n").map(line => line.trim());
|
const lines = scText.split("\n").map(line => line.trim());
|
||||||
const scenery = new Scenery();
|
const scenery = new Scenery();
|
||||||
|
|
||||||
|
let currentRoute = null;
|
||||||
|
|
||||||
lines.forEach((line, index) => {
|
lines.forEach((line, index) => {
|
||||||
if(index === 0) {
|
if(index === 0) {
|
||||||
const sceneryInfo = SceneryParser._parseSceneryInfo(line);
|
const sceneryInfo = SceneryParser._parseSceneryInfo(line);
|
||||||
if(sceneryInfo) scenery.addObject(sceneryInfo);
|
if(sceneryInfo) scenery.addObject(sceneryInfo);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!line?.length) return;
|
if(!line?.length) return;
|
||||||
|
|
||||||
const object = SceneryParser._parseObject(line);
|
if (currentRoute !== null) {
|
||||||
if(!object) return; // skip null objects
|
const foundRouteEnd = SceneryParser._parseRouteLine(line, currentRoute);
|
||||||
scenery.addObject(object);
|
if (foundRouteEnd) {
|
||||||
|
scenery.addObject(currentRoute);
|
||||||
|
currentRoute = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const object = SceneryParser._parseObject(line);
|
||||||
|
if(!object) return; // skip null objects
|
||||||
|
if (object.type === 'Route') {
|
||||||
|
currentRoute = object;
|
||||||
|
} else {
|
||||||
|
scenery.addObject(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
scenery.applyObjects();
|
scenery.applyObjects();
|
||||||
|
connectTracks(scenery);
|
||||||
|
|
||||||
if(Constants.parser.runTracksConnectionTest) tracksConnectionTest(scenery);
|
|
||||||
if(Constants.parser.connectTracks) connectTracks(scenery);
|
|
||||||
if(Constants.parser.resolveElectrification) ElectrificationResolver.resolveScenery(scenery);
|
if(Constants.parser.resolveElectrification) ElectrificationResolver.resolveScenery(scenery);
|
||||||
if(Constants.parser.attachSigns) attachSigns(scenery);
|
if(Constants.parser.attachSigns) attachSigns(scenery);
|
||||||
if(Constants.parser.logSceneryAfterFinished) console.log(scenery);
|
if(Constants.parser.logSceneryAfterFinished) console.log(scenery);
|
||||||
@ -66,12 +78,13 @@ export default class SceneryParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return SceneryInfo.fromText(text);
|
return SceneryInfo.fromText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _parseObject(text) {
|
static _parseObject(text) {
|
||||||
const type = text.split(";", 2)[0];
|
const type = text.split(";", 2)[0];
|
||||||
|
|
||||||
if(type.indexOf("Forest") !== -1 || type.indexOf("Empty") !== -1) {
|
if (type === 'EndRoute' || type.indexOf("Forest") !== -1 || type.indexOf("Empty") !== -1) {
|
||||||
|
SceneryParserLog.warn('unknownObjectType', `Unexpected entry of type ${type} without a preceding Route object`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +103,6 @@ export default class SceneryParser {
|
|||||||
return CameraHome.fromText(text);
|
return CameraHome.fromText(text);
|
||||||
case 'MainCamera':
|
case 'MainCamera':
|
||||||
return MainCamera.fromText(text);
|
return MainCamera.fromText(text);
|
||||||
case 'EndRoute':
|
|
||||||
case 'MiscGroup':
|
case 'MiscGroup':
|
||||||
case 'EndMiscGroup':
|
case 'EndMiscGroup':
|
||||||
case 'Wires':
|
case 'Wires':
|
||||||
@ -108,7 +120,7 @@ export default class SceneryParser {
|
|||||||
default:
|
default:
|
||||||
SceneryParserLog.warn('unknownObjectType', `Unknown object type: ${type}`);
|
SceneryParserLog.warn('unknownObjectType', `Unknown object type: ${type}`);
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _parseTrack(text) {
|
static _parseTrack(text) {
|
||||||
@ -149,4 +161,31 @@ export default class SceneryParser {
|
|||||||
return Misc.fromText(Scenery.nextMiscId++, text);
|
return Misc.fromText(Scenery.nextMiscId++, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Returns true if EndRoute was found, false otherwise
|
||||||
|
static _parseRouteLine(text, route) {
|
||||||
|
if (text === 'EndRoute') return true;
|
||||||
|
const fields = text.split(';');
|
||||||
|
if (fields.length < 4) {
|
||||||
|
SceneryParserLog.warn('routeInvalidSegment', `Invalid route segment: "${text}", not enough fields`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const trackIds = fields[3]
|
||||||
|
.split(',')
|
||||||
|
.map(segment => {
|
||||||
|
const id = segment.split(':')[0].trim();
|
||||||
|
if (id === '') return null;
|
||||||
|
return id;
|
||||||
|
});
|
||||||
|
if (trackIds.some(id => id === null || isNaN(id))) {
|
||||||
|
SceneryParserLog.warn('routeInvalidSegment', `Invalid route segment track IDs: ${fields[3]}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
route.addSegment(
|
||||||
|
parseInt(fields[1]), // length
|
||||||
|
parseInt(fields[2]), // radius
|
||||||
|
trackIds,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { ElectrificationResolutionStatus } from './electrification-status.js';
|
import { ElectrificationResolutionStatus } from './electrification-status.js';
|
||||||
import SceneryParserLog from './scenery-parser-log.js';
|
|
||||||
|
|
||||||
export default class Scenery
|
export default class Scenery
|
||||||
{
|
{
|
||||||
@ -7,7 +6,6 @@ export default class Scenery
|
|||||||
signalBoxes = [];
|
signalBoxes = [];
|
||||||
spawnPoints = [];
|
spawnPoints = [];
|
||||||
bounds = { minX: Infinity, minZ: Infinity, maxX: -Infinity, maxZ: -Infinity };
|
bounds = { minX: Infinity, minZ: Infinity, maxX: -Infinity, maxZ: -Infinity };
|
||||||
trackAliases = {};
|
|
||||||
electrificationResolved = ElectrificationResolutionStatus.NOT_RESOLVED;
|
electrificationResolved = ElectrificationResolutionStatus.NOT_RESOLVED;
|
||||||
static nextMiscId = 1;
|
static nextMiscId = 1;
|
||||||
|
|
||||||
@ -36,14 +34,30 @@ export default class Scenery
|
|||||||
this._updateBounds(object.getRenderBounds());
|
this._updateBounds(object.getRenderBounds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applyObjects() {
|
applyObjects() {
|
||||||
|
const categories_order = [
|
||||||
|
'switches', 'routes', // switches and routes create tracks
|
||||||
|
'tracks',
|
||||||
|
'track-objects', // track objects might refer to tracks
|
||||||
|
];
|
||||||
|
|
||||||
|
categories_order.forEach((category) => this._applyCategory(category));
|
||||||
|
|
||||||
|
// Remaining categories
|
||||||
Object.keys(this.objects).forEach(category => {
|
Object.keys(this.objects).forEach(category => {
|
||||||
Object.values(this.objects[category]).forEach(object => {
|
if (categories_order.includes(category)) return;
|
||||||
if(object.applyObject) {
|
this._applyCategory(category);
|
||||||
object.applyObject(this);
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
_applyCategory(category) {
|
||||||
|
if(!this.objects[category]) return;
|
||||||
|
|
||||||
|
Object.values(this.objects[category]).forEach(object => {
|
||||||
|
if (object.applyObject) {
|
||||||
|
object.applyObject(this);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,26 +66,6 @@ export default class Scenery
|
|||||||
return this.objects[category][id] ?? null;
|
return this.objects[category][id] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTrackAlias(id, alias) {
|
|
||||||
if(this.trackAliases[alias]) {
|
|
||||||
SceneryParserLog.warn('trackAliasAlreadyExists', `Track alias "${alias}" already exists for track "${this.trackAliases[alias]}". Cannot add alias for "${id}"`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const track = this.getObject('tracks', id);
|
|
||||||
if(!track) {
|
|
||||||
SceneryParserLog.warn('trackAliasNoTrack', `Cannot add alias "${alias}" for non-existing track "${id}"`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
track.aliases.push(alias);
|
|
||||||
this.trackAliases[alias] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
getTrackIdByAlias(idOrAlias) {
|
|
||||||
return this.trackAliases[idOrAlias] || idOrAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateBounds(objBounds) {
|
_updateBounds(objBounds) {
|
||||||
if(!objBounds || !Array.isArray(objBounds)) return;
|
if(!objBounds || !Array.isArray(objBounds)) return;
|
||||||
|
|
||||||
@ -80,7 +74,6 @@ export default class Scenery
|
|||||||
if(point.z < this.bounds.minZ) this.bounds.minZ = point.z;
|
if(point.z < this.bounds.minZ) this.bounds.minZ = point.z;
|
||||||
if(point.x > this.bounds.maxX) this.bounds.maxX = point.x;
|
if(point.x > this.bounds.maxX) this.bounds.maxX = point.x;
|
||||||
if(point.z > this.bounds.maxZ) this.bounds.maxZ = point.z;
|
if(point.z > this.bounds.maxZ) this.bounds.maxZ = point.z;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/model/switch-descriptions/switch-prefab-track.js
Normal file
32
src/model/switch-descriptions/switch-prefab-track.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
export const SwitchTrackConnectionType = {
|
||||||
|
INTERNAL: Symbol('SwitchTrackConnectionType.INTERNAL'),
|
||||||
|
EXTERNAL: Symbol('SwitchTrackConnectionType.EXTERNAL'),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class SwitchPrefabTrack {
|
||||||
|
startPos;
|
||||||
|
endPos;
|
||||||
|
radius;
|
||||||
|
dataIndex;
|
||||||
|
connections;
|
||||||
|
|
||||||
|
constructor(startPos, endPos, radius, dataIndex, connections = []) {
|
||||||
|
Object.assign(this, {
|
||||||
|
startPos,
|
||||||
|
endPos,
|
||||||
|
radius,
|
||||||
|
dataIndex,
|
||||||
|
connections,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static point(pos, dataIndex, connections = []) {
|
||||||
|
return new SwitchPrefabTrack(
|
||||||
|
pos,
|
||||||
|
pos,
|
||||||
|
0,
|
||||||
|
dataIndex,
|
||||||
|
connections,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
467
src/model/switch-descriptions/switch-prefab.js
Normal file
467
src/model/switch-descriptions/switch-prefab.js
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
import Vector3 from "../vector3";
|
||||||
|
import SwitchPrefabTrack, {SwitchTrackConnectionType} from "./switch-prefab-track";
|
||||||
|
import SceneryParserLog from "../scenery-parser-log";
|
||||||
|
import {TrackConnectionEnd} from "../track-connection";
|
||||||
|
|
||||||
|
export default class SwitchPrefab {
|
||||||
|
tracks = {};
|
||||||
|
isolation_id_offset;
|
||||||
|
|
||||||
|
constructor(tracks, isolation_id_offset) {
|
||||||
|
Object.assign(this, {
|
||||||
|
tracks,
|
||||||
|
isolation_id_offset,
|
||||||
|
});
|
||||||
|
this._verifyConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
_verifyConnections() {
|
||||||
|
// TODO: Should SceneryParserLog be used here?
|
||||||
|
Object.entries(this.tracks).forEach(([internalId, track]) => {
|
||||||
|
track.connections.forEach((connection) => {
|
||||||
|
if (connection.type === SwitchTrackConnectionType.INTERNAL) {
|
||||||
|
const otherTrack = this.tracks[connection.internalId];
|
||||||
|
if (!otherTrack) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchInvalidInternalConnection',
|
||||||
|
`In a switch prefab the referenced internal track ${connection.internalId} in a connection of track ${internalId} not found`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reverseConnection = otherTrack.connections.find(
|
||||||
|
(otherConnection) => otherConnection.type === SwitchTrackConnectionType.INTERNAL && otherConnection.internalId === internalId,
|
||||||
|
);
|
||||||
|
if (!reverseConnection) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchInvalidInternalConnection',
|
||||||
|
`In a switch prefab the internal track ${internalId} is connected to ${connection.internalId} but there is no reverse connection`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reverseConnection.end !== connection.otherEnd) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchInvalidInternalConnection',
|
||||||
|
`In a switch prefab the connection between tracks internal tracks ${internalId} and ${connection.internalId} has mismatched ends`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static fork(radiusA, radiusB, curveLength, addedLength = 0.0) {
|
||||||
|
const tracks = {
|
||||||
|
start: new SwitchPrefabTrack(
|
||||||
|
Vector3.zero(),
|
||||||
|
Vector3.zero(),
|
||||||
|
radiusA,
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: 'curve_a',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: 'curve_b',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
...SwitchPrefab._createForkSwitchTracks(
|
||||||
|
radiusA,
|
||||||
|
curveLength,
|
||||||
|
addedLength,
|
||||||
|
'a',
|
||||||
|
),
|
||||||
|
...SwitchPrefab._createForkSwitchTracks(
|
||||||
|
radiusB,
|
||||||
|
curveLength,
|
||||||
|
addedLength,
|
||||||
|
'b',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (radius === 0) {
|
||||||
|
return { endPos: Vector3.fromAngleY(startAngle, curveLength), endAngle: startAngle };
|
||||||
|
}
|
||||||
|
const centerToStart = Vector3.fromAngleY(startAngle + Math.sign(radius) * Math.PI / 2, Math.abs(radius));
|
||||||
|
const circleCenter = startPos.add(centerToStart.negate());
|
||||||
|
const endAngle = startAngle -curveLength / radius;
|
||||||
|
const centerToEnd = Vector3.fromAngleY(endAngle + Math.sign(radius) * Math.PI / 2, Math.abs(radius));
|
||||||
|
const endPos = circleCenter.add(centerToEnd);
|
||||||
|
return { endPos, endAngle };
|
||||||
|
}
|
||||||
|
|
||||||
|
static _createForkSwitchTracks(radius, curveLength, addedLength, side) {
|
||||||
|
const { endPos: curveEnd, endAngle } = SwitchPrefab._calculateCurveEnd(Vector3.zero(), 0, radius, curveLength);
|
||||||
|
|
||||||
|
const tracks = {
|
||||||
|
[`curve_${side}`]: new SwitchPrefabTrack(
|
||||||
|
Vector3.zero(),
|
||||||
|
curveEnd,
|
||||||
|
radius,
|
||||||
|
side === 'a' ? 1 : 2,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: 'start',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: addedLength > 0 ? `added_${side}` : `end_${side}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let end = curveEnd;
|
||||||
|
if (addedLength > 0) {
|
||||||
|
end = end.add(Vector3.fromAngleY(endAngle, addedLength));
|
||||||
|
tracks[`added_${side}`] = new SwitchPrefabTrack(
|
||||||
|
curveEnd,
|
||||||
|
end,
|
||||||
|
0,
|
||||||
|
side === 'a' ? 3 : 4,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `curve_${side}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: `end_${side}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracks[`end_${side}`] = SwitchPrefabTrack.point(
|
||||||
|
end,
|
||||||
|
addedLength > 0 ?
|
||||||
|
(side === 'a' ? 5 : 6) : // A fork switch with added length has the track A before B
|
||||||
|
(side === 'a' ? 4 : 3), // and without any added length, track B before A,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: addedLength > 0 ? `added_${side}` : `curve_${side}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static crossing(length, tangentInv) {
|
||||||
|
const vectorA = Vector3.fromAngleY(Math.atan(1 / tangentInv / 2), length / 2);
|
||||||
|
const vectorB = Vector3.fromAngleY(-Math.atan(1 / tangentInv / 2), length / 2);
|
||||||
|
const tracks = {
|
||||||
|
a: new SwitchPrefabTrack(
|
||||||
|
vectorA.negate(),
|
||||||
|
vectorA,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
b: new SwitchPrefabTrack(
|
||||||
|
vectorB.negate(),
|
||||||
|
vectorB,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new SwitchPrefab(tracks, Vector3.zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
static slip(totalLength, outerLength, transitionLength, radius, tangentInv, leftSlipEnabled, rightSlipEnabled) {
|
||||||
|
const aAngle = Math.atan(1 / tangentInv / 2);
|
||||||
|
const bAngle = -aAngle;
|
||||||
|
const unitVectorA = Vector3.fromAngleY(aAngle);
|
||||||
|
const unitVectorB = Vector3.fromAngleY(bAngle);
|
||||||
|
|
||||||
|
const outerStart = totalLength / 2;
|
||||||
|
const transitionStart = outerStart - outerLength;
|
||||||
|
const innerStart = transitionStart - transitionLength;
|
||||||
|
|
||||||
|
const makeSlipConfig = (radiusSign, unitVector, startAngle) => {
|
||||||
|
const startPos = unitVector.multiply(transitionStart);
|
||||||
|
const slipRadius = radiusSign * radius;
|
||||||
|
const { endPos } = SwitchPrefab._calculateCurveEnd(startPos, startAngle, slipRadius, transitionLength);
|
||||||
|
return {
|
||||||
|
startPos,
|
||||||
|
slipRadius,
|
||||||
|
endPos,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const slipPoints = {
|
||||||
|
a_enter: makeSlipConfig(1, unitVectorA.negate(), aAngle),
|
||||||
|
a_exit: makeSlipConfig(1, unitVectorA, aAngle + Math.PI),
|
||||||
|
b_enter: makeSlipConfig(-1, unitVectorB.negate(), bAngle),
|
||||||
|
b_exit: makeSlipConfig(-1, unitVectorB, bAngle + Math.PI),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dataIndex = 0;
|
||||||
|
const outerTrack = (unitVector, name) => [
|
||||||
|
`outer_${name}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
unitVector.multiply(outerStart),
|
||||||
|
unitVector.multiply(transitionStart),
|
||||||
|
0,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.EXTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: `transition_${name}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const transitionTrack = (unitVector, path, end) => [
|
||||||
|
`transition_${path}_${end}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
unitVector.multiply(transitionStart),
|
||||||
|
unitVector.multiply(innerStart),
|
||||||
|
0,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `outer_${path}_${end}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: end === 'enter' ? TrackConnectionEnd.START : TrackConnectionEnd.END,
|
||||||
|
internalId: `crossing_${path}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const slipTransitionTrack = (path, side, end) => {
|
||||||
|
const { startPos, slipRadius, endPos } = slipPoints[`${path}_${end}`];
|
||||||
|
return [
|
||||||
|
`slip_transition_${path}_${end}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
startPos,
|
||||||
|
endPos,
|
||||||
|
slipRadius,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `outer_${path}_${end}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: end === 'enter' ? TrackConnectionEnd.START : TrackConnectionEnd.END,
|
||||||
|
internalId: `side_${side}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
const crossingTrack = (unitVector, path) => [
|
||||||
|
`crossing_${path}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
unitVector.multiply(-innerStart),
|
||||||
|
unitVector.multiply(innerStart),
|
||||||
|
0,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `transition_${path}_enter`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `transition_${path}_exit`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
const sideTrack = (unitVectorEnter, unitVectorExit, enterPath, exitPath, side, slipEnabled) => {
|
||||||
|
if (slipEnabled) {
|
||||||
|
const enterConfig = slipPoints[`${enterPath}_enter`];
|
||||||
|
const exitConfig = slipPoints[`${exitPath}_exit`];
|
||||||
|
return [
|
||||||
|
`side_${side}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
enterConfig.endPos,
|
||||||
|
exitConfig.endPos,
|
||||||
|
enterConfig.slipRadius,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `slip_transition_${enterPath}_enter`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `slip_transition_${exitPath}_exit`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
`side_${side}`,
|
||||||
|
new SwitchPrefabTrack(
|
||||||
|
unitVectorEnter.multiply(innerStart),
|
||||||
|
unitVectorExit.multiply(innerStart),
|
||||||
|
0,
|
||||||
|
dataIndex++,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.START,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `transition_${enterPath}_enter`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `transition_${exitPath}_exit`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// the order is important, each function call increments dataIndex
|
||||||
|
const trackList = [
|
||||||
|
outerTrack(unitVectorA.negate(), 'a_enter'),
|
||||||
|
outerTrack(unitVectorB.negate(), 'b_enter'),
|
||||||
|
outerTrack(unitVectorA, 'a_exit'),
|
||||||
|
outerTrack(unitVectorB, 'b_exit'),
|
||||||
|
transitionTrack(unitVectorA.negate(), 'a', 'enter'),
|
||||||
|
transitionTrack(unitVectorB.negate(), 'b', 'enter'),
|
||||||
|
];
|
||||||
|
if (leftSlipEnabled) {
|
||||||
|
trackList.push(slipTransitionTrack('a', 'left', 'enter'));
|
||||||
|
}
|
||||||
|
if (rightSlipEnabled) {
|
||||||
|
trackList.push(slipTransitionTrack('b', 'right', 'enter'));
|
||||||
|
}
|
||||||
|
trackList.push(
|
||||||
|
crossingTrack(unitVectorA, 'a'),
|
||||||
|
crossingTrack(unitVectorB, 'b'),
|
||||||
|
sideTrack(unitVectorA.negate(), unitVectorB, 'a', 'b', 'left', leftSlipEnabled),
|
||||||
|
sideTrack(unitVectorB.negate(), unitVectorA, 'b', 'a', 'right', rightSlipEnabled),
|
||||||
|
transitionTrack(unitVectorA, 'a', 'exit'),
|
||||||
|
transitionTrack(unitVectorB, 'b', 'exit'),
|
||||||
|
);
|
||||||
|
if (rightSlipEnabled) {
|
||||||
|
trackList.push(slipTransitionTrack('a', 'right', 'exit'));
|
||||||
|
}
|
||||||
|
if (leftSlipEnabled) {
|
||||||
|
trackList.push(slipTransitionTrack('b', 'left', 'exit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const tracks = Object.fromEntries(trackList);
|
||||||
|
|
||||||
|
const addSideConnection = (side, enter_path, exit_path, slipEnabled) => {
|
||||||
|
if (slipEnabled) {
|
||||||
|
tracks[`outer_${enter_path}_enter`].connections.push({
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: `slip_transition_${enter_path}_enter`,
|
||||||
|
});
|
||||||
|
tracks[`outer_${exit_path}_exit`].connections.push({
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: `slip_transition_${exit_path}_exit`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tracks[`transition_${enter_path}_enter`].connections.push({
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.START,
|
||||||
|
internalId: `side_${side}`,
|
||||||
|
});
|
||||||
|
tracks[`transition_${exit_path}_exit`].connections.push({
|
||||||
|
type: SwitchTrackConnectionType.INTERNAL,
|
||||||
|
end: TrackConnectionEnd.END,
|
||||||
|
otherEnd: TrackConnectionEnd.END,
|
||||||
|
internalId: `side_${side}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
addSideConnection('left', 'a', 'b', leftSlipEnabled);
|
||||||
|
addSideConnection('right', 'b', 'a', rightSlipEnabled);
|
||||||
|
|
||||||
|
return new SwitchPrefab(tracks, Vector3.zero());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,7 +3,8 @@ import SceneryObject from "./scenery-object.js";
|
|||||||
import SceneryParserLog from "./scenery-parser-log.js";
|
import SceneryParserLog from "./scenery-parser-log.js";
|
||||||
import Vector3 from "./vector3.js";
|
import Vector3 from "./vector3.js";
|
||||||
import DefinedSwitches from "./defs/defined-switches.js";
|
import DefinedSwitches from "./defs/defined-switches.js";
|
||||||
import Constants from "../helpers/constants.js";
|
import {SwitchTrackConnectionType} from "./switch-descriptions/switch-prefab-track";
|
||||||
|
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
|
||||||
|
|
||||||
export default class Switch extends SceneryObject {
|
export default class Switch extends SceneryObject {
|
||||||
model;
|
model;
|
||||||
@ -17,9 +18,9 @@ export default class Switch extends SceneryObject {
|
|||||||
type = "Switch";
|
type = "Switch";
|
||||||
applied = false;
|
applied = false;
|
||||||
track_prefab_name;
|
track_prefab_name;
|
||||||
trackA;
|
tracks = [];
|
||||||
trackB;
|
|
||||||
def = null;
|
def = null;
|
||||||
|
isolation_id_pos = null;
|
||||||
|
|
||||||
constructor(id, model, pos, rot, data, id_isolation, id_switch, maxspeed, derailspeed, track_prefab_name) {
|
constructor(id, model, pos, rot, data, id_isolation, id_switch, maxspeed, derailspeed, track_prefab_name) {
|
||||||
super(id, pos, rot);
|
super(id, pos, rot);
|
||||||
@ -64,188 +65,104 @@ export default class Switch extends SceneryObject {
|
|||||||
|
|
||||||
let def = DefinedSwitches[this.bare_model] || null;
|
let def = DefinedSwitches[this.bare_model] || null;
|
||||||
|
|
||||||
if(!def && !Constants.parser.forceAutoSwitches) {
|
if (!def) {
|
||||||
SceneryParserLog.warn('switchUndefinedModel', `Switch ${this.id} has an undefined model "${this.bare_model}", trying to generate auto model definition`);
|
SceneryParserLog.warn('switchUndefinedModel', `Switch ${this.id} has an undefined model "${this.bare_model}"`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!def || Constants.parser.forceAutoSwitches) {
|
this._applySwitchFromDef(scenery, def);
|
||||||
def = this._tryGetAutoDef(scenery);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(def) {
|
|
||||||
this._applySwitchFromDef(scenery, def);
|
|
||||||
} else {
|
|
||||||
SceneryParserLog.warn('switchAutoDefFailed', `Could not generate auto definition for switch ${this.id} with model "${this.bare_model}"`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_applySwitchFromDef(scenery, def) {
|
_applySwitchFromDef(scenery, def) {
|
||||||
const ids = this._getTrackIds(def[2]);
|
const trackIds = this._getTrackIds();
|
||||||
if(!ids) return;
|
if (!trackIds) return;
|
||||||
|
|
||||||
|
const tracks = Object.entries(def.tracks).map(([key, track]) => {
|
||||||
|
return this._createSwitchTrackFromDef(scenery, def, track, trackIds);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tracks.some(track => !track)) return;
|
||||||
|
|
||||||
|
this.tracks = tracks;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
|
|
||||||
const trackAAliases = this._getTrackAliases(ids);
|
|
||||||
this.trackA = this._createSwitchTrackFromDef(scenery, this.id+"A", def[0], ids[0][1], ids[2][1], def[3], def[5], trackAAliases);
|
|
||||||
|
|
||||||
const trackBAliases = this._getTrackAliases(ids, 1, trackAAliases);
|
|
||||||
this.trackB = this._createSwitchTrackFromDef(scenery, this.id+"B", def[1], ids[1][1], ids[3][1], def[4], def[6], trackBAliases);
|
|
||||||
this.trackB.hide_isolation = true; // hide isolation for the second track
|
|
||||||
}
|
|
||||||
|
|
||||||
_getTrackAliases(ids, startingId = 0, exclude = []) {
|
|
||||||
const aliases = new Set();
|
|
||||||
for(let i = startingId; i < ids.length; i+=2) {
|
|
||||||
aliases.add(ids[i][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
exclude.forEach(alias => {
|
|
||||||
aliases.delete(alias);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(aliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getTrackIds(outs) {
|
|
||||||
const dataValues = this.data.split(",");
|
|
||||||
const isCrossing = outs.length === 2;
|
|
||||||
if(isCrossing) outs = [outs[0], outs[1], outs[0], outs[1]];
|
|
||||||
|
|
||||||
return outs.map((out, index) => {
|
|
||||||
const parts = dataValues[out]?.split(":");
|
|
||||||
if(parts.length !== 3) {
|
|
||||||
SceneryParserLog.warn('switchInvalidDataFormat', `Switch ${this.id} has invalid data: ${this.data}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const trackId = parts[0].trim();
|
|
||||||
let connectedId = parts[1].trim() || parts[2].trim() || null;
|
|
||||||
if(isCrossing) connectedId = parts[~~(index / 2) + 1].trim();
|
|
||||||
|
|
||||||
return [trackId, connectedId];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_createSwitchTrackFromDef(scenery, id, r, previd, nextid, startPos, endPos, aliases = []) {
|
|
||||||
const rotRad = this.rot.multiply(Math.PI / 180);
|
const rotRad = this.rot.multiply(Math.PI / 180);
|
||||||
|
this.isolation_id_pos = this.pos.add(def.isolation_id_offset.rotate(rotRad));
|
||||||
|
super.applyObject(scenery);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTrackIds() {
|
||||||
|
const ids = this.data
|
||||||
|
.split(",")
|
||||||
|
.filter(part => part.trim() !== '')
|
||||||
|
.map(
|
||||||
|
(value) => value.split(":").map((part) => part.trim()),
|
||||||
|
);
|
||||||
|
if (ids.some((part) => part.length !== 3)) {
|
||||||
|
SceneryParserLog.warn('switchInvalidDataFormat', `Switch ${this.id} has invalid data: ${this.data}`);
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createSwitchTrackFromDef(scenery, switchDef, trackDef, ids) {
|
||||||
|
const rotRad = this.rot.multiply(Math.PI / 180);
|
||||||
|
if (trackDef.dataIndex >= ids.length) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchMissingTrackId',
|
||||||
|
`Switch ${this.id} with model ${this.bare_model} is missing an id for the track at index ${trackDef.dataIndex}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const [trackId, prevId, nextId] = ids[trackDef.dataIndex];
|
||||||
|
|
||||||
|
const connections = [];
|
||||||
|
trackDef.connections.forEach((connection) => {
|
||||||
|
if (connection.type === SwitchTrackConnectionType.INTERNAL) {
|
||||||
|
const otherTrackDef = switchDef.tracks[connection.internalId];
|
||||||
|
if (!otherTrackDef) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchMissingTrackId',
|
||||||
|
`Switch prefab for model with model ${this.bare_model} of track ${this.id} does not have the internal track ${connection.internalId}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (otherTrackDef.dataIndex >= ids.length) {
|
||||||
|
SceneryParserLog.warn(
|
||||||
|
'switchMissingTrackId',
|
||||||
|
`Switch ${this.id} with model ${this.bare_model} is missing an id for the track at index ${otherTrackDef.dataIndex}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const otherTrackId = ids[otherTrackDef.dataIndex][0];
|
||||||
|
if (otherTrackId) {
|
||||||
|
connections.push(new TrackConnection(otherTrackId, connection.end));
|
||||||
|
}
|
||||||
|
} else if (connection.type === SwitchTrackConnectionType.EXTERNAL) {
|
||||||
|
const otherTrackId = connection.end === TrackConnectionEnd.START ? prevId : nextId;
|
||||||
|
if (otherTrackId) {
|
||||||
|
connections.push(new TrackConnection(otherTrackId, connection.end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const trackObj = new PointTrack(
|
const trackObj = new PointTrack(
|
||||||
id,
|
trackId,
|
||||||
this.pos.add(startPos.rotate(rotRad)),
|
this.pos.add(trackDef.startPos.rotate(rotRad)),
|
||||||
this.pos.add(endPos.rotate(rotRad)),
|
this.pos.add(trackDef.endPos.rotate(rotRad)),
|
||||||
r,
|
trackDef.radius,
|
||||||
nextid,
|
connections,
|
||||||
previd,
|
this.id_switch,
|
||||||
this.id_switch,
|
|
||||||
0, // start_slope
|
0, // start_slope
|
||||||
0, // end_slope
|
0, // end_slope
|
||||||
this.id_isolation,
|
this.id_isolation,
|
||||||
this.track_prefab_name,
|
this.track_prefab_name,
|
||||||
this.maxspeed,
|
this.maxspeed,
|
||||||
this.derailspeed
|
this.derailspeed
|
||||||
);
|
);
|
||||||
|
|
||||||
trackObj.switch = this;
|
trackObj.switch = this;
|
||||||
scenery.addObject(trackObj);
|
scenery.addObject(trackObj);
|
||||||
aliases.forEach(alias => {
|
|
||||||
scenery.addTrackAlias(id, alias);
|
|
||||||
});
|
|
||||||
|
|
||||||
return trackObj;
|
return trackObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
_tryGetAutoDef(scenery) {
|
|
||||||
const outs = Switch.autoGetOutsFromData(this.data);
|
|
||||||
if(!outs) return null;
|
|
||||||
|
|
||||||
const radiuses = Switch.autoGetRadiusesFromModel(this.bare_model);
|
|
||||||
if(!radiuses) return null;
|
|
||||||
const [ra, rb] = radiuses;
|
|
||||||
|
|
||||||
const ids = this._getTrackIds(outs);
|
|
||||||
if(!ids) return null;
|
|
||||||
|
|
||||||
const trackPoints = ids.map(idsVal =>
|
|
||||||
this._autoFindClosestTrackEnd(scenery, idsVal[1])
|
|
||||||
);
|
|
||||||
|
|
||||||
if(trackPoints.length !== 4 || trackPoints.some(point => !point)) return null;
|
|
||||||
|
|
||||||
const rotRadNeg = this.rot.multiply(-Math.PI / 180);
|
|
||||||
const transformedPoints = trackPoints.map(point => point.sub(this.pos).rotate(rotRadNeg).toPrecision(3));
|
|
||||||
|
|
||||||
const newDef = [
|
|
||||||
ra,
|
|
||||||
rb,
|
|
||||||
outs,
|
|
||||||
...transformedPoints
|
|
||||||
];
|
|
||||||
|
|
||||||
if(!DefinedSwitches[this.bare_model]) DefinedSwitches[this.bare_model] = newDef;
|
|
||||||
if(Constants.parser.logNewAutoSwitches) console.log(this.bare_model, newDef);
|
|
||||||
return newDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
static autoGetOutsFromData(data) {
|
|
||||||
const tracks = data.split(",");
|
|
||||||
|
|
||||||
if(tracks.length === 3) return [0, 1]; // Crossing
|
|
||||||
|
|
||||||
if(tracks.length < 4) {
|
|
||||||
SceneryParserLog.warn('switchInvalidDataFormat', `Switch data "${data}" is invalid, expected at least 4 tracks`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const outs = [];
|
|
||||||
tracks.forEach((ct, i) => {
|
|
||||||
const ctv = ct.split(":");
|
|
||||||
outs.push(ctv[1] || ctv[2]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
0,
|
|
||||||
outs[1] ? 1 : 0, // if no second track, use first track
|
|
||||||
outs[2] ? 2 : (outs[3] ? 3 : 5), // if no third track, use fourth or sixth track
|
|
||||||
outs[4] ? 4 : (outs[6] ? 6 : 3) // if no fourth track, use sixth or third track
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static autoGetRadiusesFromModel(model) {
|
|
||||||
const bareModel = (model.split(',')[0] || model).trim();
|
|
||||||
if(/Rkp|Crossing/.test(bareModel)) return [0, 0];
|
|
||||||
const right = bareModel.at(-1) === "R";
|
|
||||||
const [prefix, rText, rldsR2Text] = bareModel.split("-");
|
|
||||||
const r = parseFloat(rText) || 0;
|
|
||||||
const [r1, r2] = rText.split('_').map(value => parseFloat(value) || 0);
|
|
||||||
|
|
||||||
switch(prefix) {
|
|
||||||
case 'Rz 60E1':
|
|
||||||
return [0, right ? -r : r];
|
|
||||||
case 'Rld 60E1':
|
|
||||||
return right ? [r1, -r2] : [-r1, r2];
|
|
||||||
case 'Rlds 60E1':
|
|
||||||
const rldsR2 = parseFloat(rldsR2Text) || 0;
|
|
||||||
return [-r1, rldsR2];
|
|
||||||
case 'Rlj 60E1':
|
|
||||||
return right ? [-r1, -r2] : [r1, r2];
|
|
||||||
default:
|
|
||||||
SceneryParserLog.warn('switchCannotResolveModel', `Switch model ${model} cannot be auto-resolved to radiuses`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_autoFindClosestTrackEnd(scenery, trackId) {
|
|
||||||
if(!trackId) {
|
|
||||||
SceneryParserLog.warn('switchInvalidTrackConnection', `Switch ${this.id}, trackId of one of the required connections is empty`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const track = scenery.getObject("tracks", trackId);
|
|
||||||
|
|
||||||
if(!track) {
|
|
||||||
SceneryParserLog.warn('switchInvalidTrackConnection', `Switch ${this.id}, track ${trackId} that the switch connects to not found`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return track.getCloserEndPos(this.pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,25 @@
|
|||||||
export const TrackConnectionType = {
|
export const TrackConnectionEnd = {
|
||||||
END: 0,
|
START: Symbol('TrackConnectionEnd.START'),
|
||||||
START: 1,
|
END: Symbol('TrackConnectionEnd.END'),
|
||||||
}
|
};
|
||||||
|
|
||||||
export default class TrackConnection {
|
export default class TrackConnection {
|
||||||
tracks;
|
otherTrackId;
|
||||||
otherTrack;
|
otherTrack = null;
|
||||||
type;
|
end;
|
||||||
otherType;
|
|
||||||
|
|
||||||
constructor(track, otherTrack, type, otherType) {
|
/**
|
||||||
Object.assign(this, {
|
* If only an ID is passed as `otherTrack`,
|
||||||
track,
|
* it needs to be resolved later in the `connectTracks` function.
|
||||||
otherTrack,
|
*/
|
||||||
type,
|
constructor(otherTrack, end) {
|
||||||
otherType
|
this.end = end;
|
||||||
});
|
if (typeof otherTrack === 'string') {
|
||||||
|
this.otherTrackId = otherTrack;
|
||||||
|
this.otherTrack = null;
|
||||||
|
} else {
|
||||||
|
this.otherTrackId = otherTrack.id;
|
||||||
|
this.otherTrack = otherTrack;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export default class TrackObject extends SceneryObject {
|
|||||||
super(id, pos, rot);
|
super(id, pos, rot);
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
prefab_name,
|
prefab_name,
|
||||||
track_id,
|
track_id,
|
||||||
name
|
name
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -38,13 +38,11 @@ export default class TrackObject extends SceneryObject {
|
|||||||
if(this.applied) return; // already applied
|
if(this.applied) return; // already applied
|
||||||
this.applied = true;
|
this.applied = true;
|
||||||
|
|
||||||
const trackId = scenery.getTrackIdByAlias(this.track_id);
|
const track = scenery.getObject('tracks', this.track_id);
|
||||||
const track = scenery.getObject('tracks', trackId);
|
|
||||||
if (!track) {
|
if (!track) {
|
||||||
SceneryParserLog.warn('trackObjectCannotBeApplied', `TrackObject ${this.id} cannot be applied: track ${trackId} not found`)
|
SceneryParserLog.warn('trackObjectCannotBeApplied', `TrackObject ${this.id} cannot be applied: track ${this.track_id} not found`)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.track = track;
|
this.track = track;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
import SceneryParserLog from './scenery-parser-log';
|
|
||||||
|
|
||||||
const connectionThresholdDistanceSq = 0.05;
|
|
||||||
|
|
||||||
function trackEndConnectionTest(scenery, track, isPrev) {
|
|
||||||
const vNext = isPrev ? track.previd : track.nextid;
|
|
||||||
if (!vNext) return;
|
|
||||||
|
|
||||||
const vEndPos = isPrev ? track.points.start : track.points.end;
|
|
||||||
const vNextTrackId = scenery.getTrackIdByAlias(vNext);
|
|
||||||
const vNextTrack = scenery.getObject('tracks', vNextTrackId);
|
|
||||||
|
|
||||||
if (!vNextTrack) {
|
|
||||||
SceneryParserLog.warn('tracksConnectionTest', `Track ${track.id} has non-existing ${isPrev ? 'previous' : 'next'} track: ${isPrev ? track.previd : track.nextid} (${vNextTrackId})`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [vNextTrackNext, vNextTrackPrev] = isPrev ? [vNextTrack.previd, vNextTrack.nextid] : [vNextTrack.nextid, vNextTrack.previd];
|
|
||||||
const vNextTrackNextId = scenery.getTrackIdByAlias(vNextTrackNext);
|
|
||||||
const vNextTrackPrevId = scenery.getTrackIdByAlias(vNextTrackPrev);
|
|
||||||
const directlyConnected = track.aliases.includes(vNextTrackPrevId) || vNextTrackPrevId === track.id;
|
|
||||||
const reverseConnected = track.aliases.includes(vNextTrackNextId) || vNextTrackNextId === track.id;
|
|
||||||
const isSwitchBTrackPrev = isPrev && track.id.at(-1) === 'B';
|
|
||||||
|
|
||||||
if (directlyConnected) {
|
|
||||||
const vNextTrackStartPos = isPrev ? vNextTrack.points.end : vNextTrack.points.start;
|
|
||||||
const distSq = vEndPos.distanceSq(vNextTrackStartPos);
|
|
||||||
if (distSq > connectionThresholdDistanceSq) {
|
|
||||||
SceneryParserLog.warn('tracksConnectionTest', `Track ${track.id} with ${isPrev ? 'start' : 'end'} position ${vEndPos.toString()} is too far from the ${isPrev ? 'previous' : 'next'} track ${vNextTrackId} with ${isPrev ? 'end' : 'start'} position ${vNextTrackStartPos.toString()}. Distance: ${Math.sqrt(distSq).toFixed(3)}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (reverseConnected) {
|
|
||||||
const vNextTrackEndPos = isPrev ? vNextTrack.points.start : vNextTrack.points.end;
|
|
||||||
const distSq = vEndPos.distanceSq(vNextTrackEndPos);
|
|
||||||
if (distSq > connectionThresholdDistanceSq) {
|
|
||||||
SceneryParserLog.warn('tracksConnectionTest', `Track ${track.id} with ${isPrev ? 'start' : 'end'} position ${vEndPos.toString()} is too far from the ${isPrev ? 'previous' : 'next'} track ${vNextTrackId} with ${isPrev ? 'start' : 'end'} position ${vNextTrackEndPos.toString()}. Distance: ${Math.sqrt(distSq).toFixed(3)}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if(!isSwitchBTrackPrev) {
|
|
||||||
SceneryParserLog.warn('tracksConnectionTest', `Track ${track.id} ${isPrev ? 'previous' : 'next'} track ${vNextTrackId} does not match its ${isPrev ? 'next' : 'previous'} track id: ${vNextTrackNext} (${vNextTrackNextId}), ${vNextTrackPrev} (${vNextTrackPrevId})`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tracksConnectionTest(scenery) {
|
|
||||||
const tracks = scenery.objects['tracks'] || [];
|
|
||||||
|
|
||||||
Object.values(tracks).forEach(track => {
|
|
||||||
trackEndConnectionTest(scenery, track, false);
|
|
||||||
trackEndConnectionTest(scenery, track, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import Track from "./track";
|
import Track from "./track";
|
||||||
import Vector3 from "../vector3";
|
import Vector3 from "../vector3";
|
||||||
|
import TrackConnection, {TrackConnectionEnd} from "../track-connection";
|
||||||
|
|
||||||
export default class BezierTrack extends Track
|
export default class BezierTrack extends Track
|
||||||
{
|
{
|
||||||
@ -8,18 +9,32 @@ 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, nextid, previd, 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) {
|
||||||
super(id, start, rot, len, r, nextid, previd, 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);
|
||||||
|
|
||||||
Object.assign(this.points, {
|
Object.assign(this.points, {
|
||||||
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() {
|
||||||
@ -32,6 +47,11 @@ export default class BezierTrack extends Track
|
|||||||
|
|
||||||
static fromText(text) {
|
static fromText(text) {
|
||||||
const values = text.split(";");
|
const values = text.split(";");
|
||||||
|
|
||||||
|
const connections = [];
|
||||||
|
if (values[15]) connections.push(new TrackConnection(values[15], TrackConnectionEnd.END));
|
||||||
|
if (values[16]) connections.push(new TrackConnection(values[16], TrackConnectionEnd.START));
|
||||||
|
|
||||||
const track = new BezierTrack(
|
const track = new BezierTrack(
|
||||||
values[1], // id
|
values[1], // id
|
||||||
Vector3.fromValuesArray(values, 3), // start
|
Vector3.fromValuesArray(values, 3), // start
|
||||||
@ -42,8 +62,7 @@ export default class BezierTrack extends Track
|
|||||||
Vector3.zero(),
|
Vector3.zero(),
|
||||||
0, // len ??
|
0, // len ??
|
||||||
0, // r ??
|
0, // r ??
|
||||||
values[15], // nextid
|
connections,
|
||||||
values[16], // previd
|
|
||||||
values[17], // id_station
|
values[17], // id_station
|
||||||
...Track.slopesFromText(values[18]), // start_slope, end_slope
|
...Track.slopesFromText(values[18]), // start_slope, end_slope
|
||||||
values[21], // id_isolation
|
values[21], // id_isolation
|
||||||
@ -54,4 +73,8 @@ export default class BezierTrack extends Track
|
|||||||
|
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyObject(scenery) {
|
||||||
|
super.applyObject(scenery);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,9 +10,9 @@ export default class PointTrack extends Track
|
|||||||
end: Vector3.zero()
|
end: Vector3.zero()
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(id, start, end, r, nextid, previd, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
|
constructor(id, start, end, r, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
|
||||||
const [rot, len] = PointTrack._getRotLen(start, end, r);
|
const [rot, len] = PointTrack._getRotLen(start, end, r);
|
||||||
super(id, start, rot, len, r, nextid, previd, 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);
|
||||||
|
|
||||||
Object.assign(this.points, {
|
Object.assign(this.points, {
|
||||||
start, end
|
start, end
|
||||||
|
|||||||
42
src/model/tracks/route-track.js
Normal file
42
src/model/tracks/route-track.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import AngleHelper from "../../helpers/angleHelper";
|
||||||
|
import Track from "./track";
|
||||||
|
import {ElectrificationStatus} from "../electrification-status";
|
||||||
|
|
||||||
|
export default class RouteTrack extends Track {
|
||||||
|
type = "RouteTrack";
|
||||||
|
points;
|
||||||
|
route;
|
||||||
|
|
||||||
|
constructor(id, start, end, rot, len, r, connections, electrified, route) {
|
||||||
|
super(
|
||||||
|
id, start, rot, len, r, connections,
|
||||||
|
null, // id_station,
|
||||||
|
0, // start_slope,
|
||||||
|
0, // end_slope,
|
||||||
|
null, // id_isolation
|
||||||
|
null, // prefab_name
|
||||||
|
null, // maxspeed,
|
||||||
|
null, // derailspeed,
|
||||||
|
);
|
||||||
|
this.electrificationStatus = electrified ? ElectrificationStatus.ELECTRIFIED : ElectrificationStatus.NON_ELECTRIFIED;
|
||||||
|
this.points = {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
};
|
||||||
|
this.route = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStartAngleXZ() {
|
||||||
|
return AngleHelper.degToRad(this.rot.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEndAngleXZ() {
|
||||||
|
const startAngle = AngleHelper.degToRad(this.rot.y);
|
||||||
|
|
||||||
|
if(this.r === 0) {
|
||||||
|
return startAngle
|
||||||
|
}
|
||||||
|
|
||||||
|
return startAngle - this.len / this.r;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import AngleHelper from "../../helpers/angleHelper";
|
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";
|
||||||
|
|
||||||
export default class StandardTrack extends Track
|
export default class StandardTrack extends Track
|
||||||
{
|
{
|
||||||
@ -8,11 +9,12 @@ 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, nextid, previd, 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) {
|
||||||
super(id, start, rot, len, r, nextid, previd, 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);
|
||||||
this._calcPoints();
|
this._calcPoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,14 +34,18 @@ export default class StandardTrack extends Track
|
|||||||
|
|
||||||
static fromText(text) {
|
static fromText(text) {
|
||||||
const values = text.split(";");
|
const values = text.split(";");
|
||||||
|
|
||||||
|
const connections = [];
|
||||||
|
if (values[11]) connections.push(new TrackConnection(values[11], TrackConnectionEnd.END));
|
||||||
|
if (values[12]) connections.push(new TrackConnection(values[12], TrackConnectionEnd.START));
|
||||||
|
|
||||||
const track = new StandardTrack(
|
const track = new StandardTrack(
|
||||||
values[1], // id
|
values[1], // id
|
||||||
Vector3.fromValuesArray(values, 3), // start
|
Vector3.fromValuesArray(values, 3), // start
|
||||||
Vector3.fromValuesArray(values, 6), // rot
|
Vector3.fromValuesArray(values, 6), // rot
|
||||||
parseFloat(values[9]), // len
|
parseFloat(values[9]), // len
|
||||||
parseFloat(values[10]), // r
|
parseFloat(values[10]), // r
|
||||||
values[11], // nextid
|
connections,
|
||||||
values[12], // previd
|
|
||||||
values[13], // id_station
|
values[13], // id_station
|
||||||
...Track.slopesFromText(values[14]), // start_slope, end_slope
|
...Track.slopesFromText(values[14]), // start_slope, end_slope
|
||||||
values[17], // id_isolation
|
values[17], // id_isolation
|
||||||
@ -55,16 +61,22 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust end point by the slope
|
// adjust end point by the slope
|
||||||
if (this.end_slope !== 0) {
|
if (this.end_slope !== 0) {
|
||||||
const startHeightDiff = this.start_slope * this.len / 1000;
|
const startHeightDiff = this.start_slope * this.len / 1000;
|
||||||
@ -73,4 +85,8 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import SceneryObject from "../scenery-object";
|
import SceneryObject from "../scenery-object";
|
||||||
import { ElectrificationStatus } from "../electrification-status";
|
import { ElectrificationStatus } from "../electrification-status";
|
||||||
|
import {TrackConnectionEnd} from "../track-connection";
|
||||||
|
|
||||||
export default class Track extends SceneryObject {
|
export default class Track extends SceneryObject {
|
||||||
len;
|
len;
|
||||||
@ -10,28 +11,24 @@ export default class Track extends SceneryObject {
|
|||||||
derailspeed;
|
derailspeed;
|
||||||
category = "tracks";
|
category = "tracks";
|
||||||
type = "Track";
|
type = "Track";
|
||||||
nextid;
|
connections = [];
|
||||||
previd;
|
|
||||||
start_slope;
|
start_slope;
|
||||||
end_slope;
|
end_slope;
|
||||||
prefab_name;
|
prefab_name;
|
||||||
hide_isolation = false;
|
|
||||||
aliases = [];
|
|
||||||
switch = null;
|
switch = null;
|
||||||
electrificationStatus = ElectrificationStatus.NOT_CHECKED;
|
electrificationStatus = ElectrificationStatus.NOT_CHECKED;
|
||||||
hasNEVP = false;
|
hasNEVP = false;
|
||||||
connections = [];
|
|
||||||
|
|
||||||
constructor(id, pos, rot, len, r, nextid, previd, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
|
constructor(id, pos, rot, len, r, connections, id_station, start_slope, end_slope, id_isolation, prefab_name, maxspeed, derailspeed) {
|
||||||
super(id, pos, rot);
|
super(id, pos, rot);
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
len, r,
|
len, r,
|
||||||
nextid, previd,
|
connections,
|
||||||
id_station,
|
id_station,
|
||||||
start_slope, end_slope,
|
start_slope, end_slope,
|
||||||
id_isolation,
|
id_isolation,
|
||||||
prefab_name,
|
prefab_name,
|
||||||
maxspeed, derailspeed
|
maxspeed, derailspeed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,10 +40,19 @@ export default class Track extends SceneryObject {
|
|||||||
throw new Error("getEndAngleXZ() must be implemented in subclass");
|
throw new Error("getEndAngleXZ() must be implemented in subclass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAngleXZForEnd(end) {
|
||||||
|
if (end === TrackConnectionEnd.START) return this.getStartAngleXZ();
|
||||||
|
else return this.getEndAngleXZ();
|
||||||
|
}
|
||||||
|
|
||||||
static slopesFromText(text) {
|
static slopesFromText(text) {
|
||||||
return text.split(",", 2);
|
return text.split(",", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEndPos(end) {
|
||||||
|
return end === TrackConnectionEnd.START ? this.points.start : this.points.end;
|
||||||
|
}
|
||||||
|
|
||||||
getCloserEndPos(pos) {
|
getCloserEndPos(pos) {
|
||||||
const distStartSq = pos.distanceSq(this.points.start);
|
const distStartSq = pos.distanceSq(this.points.start);
|
||||||
const distEndSq = pos.distanceSq(this.points.end);
|
const distEndSq = pos.distanceSq(this.points.end);
|
||||||
@ -56,7 +62,7 @@ export default class Track extends SceneryObject {
|
|||||||
return this.points.end;
|
return this.points.end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRenderBounds() {
|
getRenderBounds() {
|
||||||
return Object.values(this.points);
|
return Object.values(this.points);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,22 +7,24 @@ import sys
|
|||||||
# v1.4
|
# v1.4
|
||||||
#
|
#
|
||||||
|
|
||||||
BAD_WORDS = ['Empty,Empty', 'Forest Start,Forest Start', 'EndMiscGroup', 'Fence', 'TerrainPoint', 'Wires']
|
BAD_WORDS = ['EndMiscGroup', 'Fence', 'TerrainPoint', 'Wires']
|
||||||
|
|
||||||
def should_exclude(line: str) -> bool:
|
def should_exclude(line: str) -> bool:
|
||||||
for word in BAD_WORDS:
|
for word in BAD_WORDS:
|
||||||
if line.startswith(word):
|
if line.startswith(word):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if line.startswith("Misc"):
|
if line.startswith("Misc"):
|
||||||
values = line.split(';')
|
values = line.split(';')
|
||||||
return not values[2].startswith("SignalBox")
|
return not values[2].startswith("SignalBox")
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def process_file(file_path: str):
|
def process_file(file_path: str):
|
||||||
if not file_path.endswith(".sc"):
|
if not file_path.endswith(".sc"):
|
||||||
print(f"Skipping non-.sc file: {file_path}")
|
print(f"Skipping non-.sc file: {file_path}")
|
||||||
return
|
return
|
||||||
|
|
||||||
if file_path.endswith(".lite.sc"):
|
if file_path.endswith(".lite.sc"):
|
||||||
print(f"Skipping already processed file: {file_path}")
|
print(f"Skipping already processed file: {file_path}")
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user