diff --git a/public/assets/route.svg b/public/assets/route.svg new file mode 100644 index 0000000..7afae49 --- /dev/null +++ b/public/assets/route.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/components/map/Map.css b/src/components/map/Map.css index 64851bf..08a77bf 100644 --- a/src/components/map/Map.css +++ b/src/components/map/Map.css @@ -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; } \ No newline at end of file diff --git a/src/components/map/ObjectsRenderQueue.js b/src/components/map/ObjectsRenderQueue.js index 7307b8f..e0a2b5d 100644 --- a/src/components/map/ObjectsRenderQueue.js +++ b/src/components/map/ObjectsRenderQueue.js @@ -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'] } ] diff --git a/src/components/map/SceneryRoot.js b/src/components/map/SceneryRoot.js index 37fb4b7..f7753e2 100644 --- a/src/components/map/SceneryRoot.js +++ b/src/components/map/SceneryRoot.js @@ -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 ( + + {Object.values(objects).map((object) => { + if((type !== undefined && type !== object.type) || (types !== undefined && !types.includes(object.type))) return null; - return ; - }); - }).flat(); + return ; + })} + + ); + }); }, [scenery, settings]); return ( diff --git a/src/components/map/object-renderers/RouteRenderer.js b/src/components/map/object-renderers/RouteRenderer.js new file mode 100644 index 0000000..846f9b7 --- /dev/null +++ b/src/components/map/object-renderers/RouteRenderer.js @@ -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 ( + + + { + svg.setAttribute('width', '20mm'); + svg.setAttribute('height', '20mm'); + }} + /> + + + + {object.route_name} + + + + ); +} diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 4bad6dc..363a2eb 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -28,6 +28,11 @@ const Constants = { id: 'signals', name: 'Signals', default: true, + }, + { + id: 'routes', + name: 'Routes', + default: true, } ] }; diff --git a/src/model/route.js b/src/model/route.js new file mode 100644 index 0000000..73d2abe --- /dev/null +++ b/src/model/route.js @@ -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; + } + +} \ No newline at end of file diff --git a/src/model/scenery.js b/src/model/scenery.js index a1b93cf..4cbd714 100644 --- a/src/model/scenery.js +++ b/src/model/scenery.js @@ -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 {