From 06dd81eec6fcba8a0d0f485aa899ea7c0d7fe64f Mon Sep 17 00:00:00 2001 From: izawartka <59137928+izawartka@users.noreply.github.com> Date: Fri, 30 May 2025 18:55:36 +0200 Subject: [PATCH] SignalRenderer, minor tweaks +Added Signal class +Added SignalRenderer *Isolations IDs are no longer shown by default *Switches names are now light blue instead of white --- package-lock.json | 36 ++++++++++- package.json | 3 +- public/assets/signal.svg | 11 ++++ src/components/map/Map.css | 5 ++ src/components/map/ObjectsRenderQueue.js | 8 +++ .../map/object-renderers/SignalRenderer.js | 27 ++++++++ src/helpers/constants.js | 7 +- src/model/scenery.js | 5 ++ src/model/signal.js | 64 +++++++++++++++++++ src/model/spawn-info.js | 28 ++++++++ 10 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 public/assets/signal.svg create mode 100644 src/components/map/object-renderers/SignalRenderer.js create mode 100644 src/model/signal.js create mode 100644 src/model/spawn-info.js diff --git a/package-lock.json b/package-lock.json index 11cdc21..ccb235f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "@testing-library/user-event": "^13.5.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-scripts": "^5.0.1" + "react-scripts": "^5.0.1", + "react-svg": "^16.3.0" } }, "node_modules/-": { @@ -3441,6 +3442,17 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@tanem/svg-injector": { + "version": "10.1.68", + "resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-10.1.68.tgz", + "integrity": "sha512-UkJajeR44u73ujtr5GVSbIlELDWD/mzjqWe54YMK61ljKxFcJoPd9RBSaO7xj02ISCWUqJW99GjrS+sVF0UnrA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "content-type": "^1.0.5", + "tslib": "^2.6.2" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -3809,6 +3821,12 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, "node_modules/@types/q": { "version": "1.5.8", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", @@ -13996,6 +14014,22 @@ } } }, + "node_modules/react-svg": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/react-svg/-/react-svg-16.3.0.tgz", + "integrity": "sha512-MvoQbITgkmpPJYwDTNdiUyoncJFfoa0D86WzoZuMQ9c/ORJURPR6rPMnXDsLOWDCAyXuV9nKZhQhGyP0HZ0MVQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@tanem/svg-injector": "^10.1.68", + "@types/prop-types": "^15.7.14", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 57d923d..6e18768 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "@testing-library/user-event": "^13.5.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "react-scripts": "^5.0.1" + "react-scripts": "^5.0.1", + "react-svg": "^16.3.0" }, "scripts": { "start": "react-scripts start", diff --git a/public/assets/signal.svg b/public/assets/signal.svg new file mode 100644 index 0000000..8d3392a --- /dev/null +++ b/public/assets/signal.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/map/Map.css b/src/components/map/Map.css index 5d48734..64851bf 100644 --- a/src/components/map/Map.css +++ b/src/components/map/Map.css @@ -16,6 +16,7 @@ .map svg text.switch-name { fill: #eee; + fill: #bbf; } .map svg text.isolation-id { @@ -33,4 +34,8 @@ .map svg g.track-object:hover text { display: block; +} + +.map svg g.signal g { + stroke: #eee; } \ No newline at end of file diff --git a/src/components/map/ObjectsRenderQueue.js b/src/components/map/ObjectsRenderQueue.js index b4282b0..7307b8f 100644 --- a/src/components/map/ObjectsRenderQueue.js +++ b/src/components/map/ObjectsRenderQueue.js @@ -2,6 +2,7 @@ import TrackRenderer from './object-renderers/TrackRenderer'; import SwitchNameRenderer from './object-renderers/SwitchNameRenderer'; import IsolationIdRenderer from './object-renderers/IsolationIdRenderer'; import TrackObjectRenderer from './object-renderers/TrackObjectRenderer'; +import SignalRenderer from './object-renderers/SignalRenderer'; const ObjectRendererQueue = [ { @@ -28,6 +29,13 @@ const ObjectRendererQueue = [ 'category': 'track-objects', 'renderer': TrackObjectRenderer, 'cond': (settings) => settings.layers['track-objects'] + }, + { + 'name': 'signals', + 'category': 'track-objects', + 'type': 'Signal', + 'renderer': SignalRenderer, + 'cond': (settings) => settings.layers['signals'] } ] diff --git a/src/components/map/object-renderers/SignalRenderer.js b/src/components/map/object-renderers/SignalRenderer.js new file mode 100644 index 0000000..50738d2 --- /dev/null +++ b/src/components/map/object-renderers/SignalRenderer.js @@ -0,0 +1,27 @@ +import { ReactSVG } from 'react-svg' + +export default function SignalRenderer(props) { + const { object } = props; + + const x = object.x; + const z = -object.z; + + return ( + + + { + svg.setAttribute('width', '1mm'); + svg.setAttribute('height', '1mm'); + }} + /> + + + {object.getPrintableSignalName()} + + + ); +} diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 2e56cda..4bad6dc 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -17,12 +17,17 @@ const Constants = { { id: 'isolations-ids', name: 'Isolations IDs', - default: true, + default: false, }, { id: 'track-objects', name: 'Track objects', default: false, + }, + { + id: 'signals', + name: 'Signals', + default: true, } ] }; diff --git a/src/model/scenery.js b/src/model/scenery.js index 793a4df..a1b93cf 100644 --- a/src/model/scenery.js +++ b/src/model/scenery.js @@ -2,6 +2,7 @@ import Switch from './switch'; import StandardTrack from './standard-track'; import BezierTrack from './bezier-track'; import TrackObject from './track-object'; +import Signal from './signal'; export default class Scenery { @@ -94,6 +95,10 @@ export default class Scenery switch(prefabName) { default: return TrackObject.fromText(text); + if(Signal.isSignal(text)) { + return Signal.fromText(text); + } else { + return TrackObject.fromText(text); } } diff --git a/src/model/signal.js b/src/model/signal.js new file mode 100644 index 0000000..3bbdd6b --- /dev/null +++ b/src/model/signal.js @@ -0,0 +1,64 @@ +import SpawnInfo from "./spawn-info"; +import TrackObject from "./track-object"; + +export default class Signal extends TrackObject { + is_spawn; + spawn_info; + signal_name; + type = "Signal"; + + constructor(id, prefab_name, x, y, z, rot, track_id, name, is_spawn, spawn_info, signal_name) { + super(id, prefab_name, x, y, z, rot, track_id, name); + + Object.assign(this, { + is_spawn, + spawn_info: is_spawn ? spawn_info : null, + signal_name + }); + } + + getPrintableSignalName() { + // remove everything after the first '[' or '<' character (inclusive) + const match = this.signal_name.match(/^[^[<]*/); + if (match) { + return match[0].trim(); + } + } + + /* + Check if a TrackObject text is a Signal. + @param {string} text - The text representation of the TrackObject. + @returns {boolean} - Returns true if the text represents a Signal, false otherwise. + @static + @memberof Signal + */ + static isSignal(text) { + const values = text.split(";"); + if(values.length < 32) return false; + + const prefabInfo = values[2].split(","); + if(prefabInfo.length < 2) return false; + + return true; + } + + static fromText(text) { + const values = text.split(";"); + const signal = new Signal( + values[1], // id + values[2], // prefab_name + parseFloat(values[3]), // x + parseFloat(values[4]), // y + parseFloat(values[5]), // z + parseFloat(values[7]), // rot + values[9], // track_id + values[11], // name, + values[12] === "Spawn Signal", // is_spawn + SpawnInfo.fromText(values[15]), // spawn_info + values[29] + ); + + return signal; + } + +} \ No newline at end of file diff --git a/src/model/spawn-info.js b/src/model/spawn-info.js new file mode 100644 index 0000000..0ca2696 --- /dev/null +++ b/src/model/spawn-info.js @@ -0,0 +1,28 @@ +export default class SpawnInfo { + max_length; + allow_electric; + multiple_units_only; + light_only; + name; + + constructor(max_length, allow_electric, multiple_units_only, light_only, name) { + Object.assign(this, { + max_length, + allow_electric, + multiple_units_only, + light_only, + name + }); + } + + static fromText(text) { + const values = text.split(","); + return new SpawnInfo( + parseFloat(values[1]), // max_length + values[2] === "1", // allow_electric + values[3] === "1", // multiple_units_only + values[4] === "1", // light_only + values[5] // name + ); + } +} \ No newline at end of file