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
This commit is contained in:
izawartka 2025-05-30 18:55:36 +02:00
parent f8725c0371
commit 06dd81eec6
10 changed files with 191 additions and 3 deletions

36
package-lock.json generated
View File

@ -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",

View File

@ -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",

11
public/assets/signal.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100mm" height="100mm" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="#000" stroke-width="8">
<circle cx="50" cy="50" r="20"/>
<g stroke-linecap="round">
<path d="m50 82.5v11.894"/>
<path d="m63 81.5 2 12.894"/>
<path d="m37 81.5-2 12.894"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@ -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;
}

View File

@ -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']
}
]

View File

@ -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 (
<g className="signal" transform={`translate(${x}, ${z})`}>
<g transform={`rotate(${object.rot}) translate(-2, -2)`}>
<ReactSVG
src="/assets/signal.svg"
wrapper='svg'
beforeInjection={(svg) => {
svg.setAttribute('width', '1mm');
svg.setAttribute('height', '1mm');
}}
/>
</g>
<text x="2.5" y="0" textAnchor="start" dominantBaseline="middle">
{object.getPrintableSignalName()}
</text>
</g>
);
}

View File

@ -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,
}
]
};

View File

@ -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);
}
}

64
src/model/signal.js Normal file
View File

@ -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;
}
}

28
src/model/spawn-info.js Normal file
View File

@ -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
);
}
}