RouteRenderer, code tweaks

+Added Route class
+Added RouteRenderer
*Parts of the map rendered by different renderers are now separated into <g> groups
*Fixed signals not being parsed correctly
This commit is contained in:
izawartka 2025-05-30 18:58:20 +02:00
parent 06dd81eec6
commit b7f9d80045
8 changed files with 130 additions and 10 deletions

7
public/assets/route.svg Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="160mm" height="100mm" version="1.1" viewBox="0 0 160 100" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="m120 80s-28.351-30-112-30c83.62 0 112-30 112-30z"/>
<rect x="119" y="35" width="33" height="30"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@ -15,7 +15,6 @@
}
.map svg text.switch-name {
fill: #eee;
fill: #bbf;
}
@ -38,4 +37,13 @@
.map svg g.signal g {
stroke: #eee;
}
.map svg g.route g {
fill: #eee;
}
.map svg g.route text {
fill: #eee;
font-size: 72mm;
}

View File

@ -3,6 +3,7 @@ import SwitchNameRenderer from './object-renderers/SwitchNameRenderer';
import IsolationIdRenderer from './object-renderers/IsolationIdRenderer';
import TrackObjectRenderer from './object-renderers/TrackObjectRenderer';
import SignalRenderer from './object-renderers/SignalRenderer';
import RouteRenderer from './object-renderers/RouteRenderer';
const ObjectRendererQueue = [
{
@ -36,6 +37,12 @@ const ObjectRendererQueue = [
'type': 'Signal',
'renderer': SignalRenderer,
'cond': (settings) => settings.layers['signals']
},
{
'name': 'routes',
'category': 'routes',
'renderer': RouteRenderer,
'cond': (settings) => settings.layers['routes']
}
]

View File

@ -18,12 +18,16 @@ export default function SceneryRoot(props) {
const objects = scenery.objects[category] ?? null;
if(!objects) return [null];
return Object.values(objects).map((object) => {
if((type !== undefined && type !== object.type) || (types !== undefined && !types.includes(object.type))) return null;
return (
<g className={`scenery-layer-${name}`} key={name}>
{Object.values(objects).map((object) => {
if((type !== undefined && type !== object.type) || (types !== undefined && !types.includes(object.type))) return null;
return <RendererComponent key={`${name}-${object.id}`} object={object} />;
});
}).flat();
return <RendererComponent key={`${name}-${object.id}`} object={object} />;
})}
</g>
);
});
}, [scenery, settings]);
return (

View File

@ -0,0 +1,44 @@
import { useMemo } from "react";
import { ReactSVG } from "react-svg";
export default function RouteRenderer(props) {
const { object } = props;
const x = object.x;
const z = -object.z;
const upsideDown = useMemo(() => {
let rot = object.rot;
if (rot < 0) {
rot += 360 * Math.ceil(Math.abs(rot) / 360);
} else if (rot >= 360) {
rot -= 360 * Math.floor(rot / 360);
}
return rot > 180;
}, [object.rot]);
const upsideDownRot = upsideDown ? 90 : -90;
const offY = object.track_count === 2 ? -22 : 20;
const anchor = upsideDown ? "start" : "end";
return (
<g className="route" transform={`translate(${x}, ${z}) rotate(${object.rot})`}>
<g transform={`rotate(90) translate(-80, -40)`}>
<ReactSVG
src="/assets/route.svg"
wrapper='svg'
beforeInjection={(svg) => {
svg.setAttribute('width', '20mm');
svg.setAttribute('height', '20mm');
}}
/>
</g>
<g transform={`translate(${object.track_offset}, 0) rotate(${upsideDownRot})`}>
<text x='0' y={offY} textAnchor={anchor} dominantBaseline="bottom">
{object.route_name}
</text>
</g>
</g>
);
}

View File

@ -28,6 +28,11 @@ const Constants = {
id: 'signals',
name: 'Signals',
default: true,
},
{
id: 'routes',
name: 'Routes',
default: true,
}
]
};

43
src/model/route.js Normal file
View File

@ -0,0 +1,43 @@
import SceneryObject from "./scenery-object";
export default class Route extends SceneryObject {
track_count;
route_name;
electrified;
track_offset;
category = "routes";
type = "Route";
constructor(prefab_name, x, y, z, rot, track_count, route_name, track_offset, electrified) {
super(route_name, x, y, z);
Object.assign(this, {
prefab_name,
rot,
track_count,
route_name,
track_offset,
electrified,
});
}
// Route;;ConnectionForest;14418.61;-6.663832;8637.426;0;33.34996;0;2;Ol;2;;True
static fromText(text) {
const values = text.split(";");
const route = new Route(
values[2], // prefab_name
parseFloat(values[3]), // x
parseFloat(values[4]), // y
parseFloat(values[5]), // z
parseFloat(values[7]), // rot
parseInt(values[9]), // track_count
values[10], // route_name
parseFloat(values[11]), // track_offset
values[13] === "True" || values[12] === "1" // electrified
);
return route;
}
}

View File

@ -3,6 +3,7 @@ import StandardTrack from './standard-track';
import BezierTrack from './bezier-track';
import TrackObject from './track-object';
import Signal from './signal';
import Route from './route';
export default class Scenery
{
@ -67,6 +68,11 @@ export default class Scenery
return Switch.fromText(text);
case 'TrackObject':
return Scenery._parseTrackObject(text);
case 'Route':
return Route.fromText(text);
case 'Empty':
case 'EndRoute':
return null;
default:
console.warn(`Unknown object type: ${type}`);
return null;
@ -91,10 +97,6 @@ export default class Scenery
}
static _parseTrackObject(text) {
const prefabName = text.split(";", 4)[2];
switch(prefabName) {
default:
return TrackObject.fromText(text);
if(Signal.isSignal(text)) {
return Signal.fromText(text);
} else {