diff --git a/src/components/App.js b/src/components/App.js index 09a5c77..5c3c130 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -8,20 +8,22 @@ import SettingsManager from './SettingsManager'; import LocList from './loclist/LocList'; import DialogManager from './dialog/DialogManager'; import DistanceMeterContext from '../contexts/DistanceMeterContext'; +import {GradientsManager} from "./GradientsManager"; function App() { const [isLoading, setIsLoading] = useState(false); const [scenery, setScenery] = useState(null); const [sideMenuOpen, setSideMenuOpen] = useState(false); const [distancePoints, setDistancePoints] = useState(null); - + return ( + -
@@ -34,9 +36,9 @@ function App() {
+
); } - + export default App; - \ No newline at end of file diff --git a/src/components/GradientsManager.js b/src/components/GradientsManager.js new file mode 100644 index 0000000..066dd71 --- /dev/null +++ b/src/components/GradientsManager.js @@ -0,0 +1,68 @@ +import Constants from "../helpers/constants"; +import {useMemo} from "react"; +import GradientsContext from "../contexts/GradientsContext"; + +function getMinMax(trackColorMode, scenery) { + if (trackColorMode === 'elevation') { + if (!scenery) return null; + if (scenery.trackElevationBounds.minY > scenery.trackElevationBounds.maxY) return null; + + const min = scenery.trackElevationBounds.minY; + // Make sure diff > 0 even on flat sceneries + let diff = Math.max(scenery.trackElevationBounds.maxY - scenery.trackElevationBounds.minY, 1); + // Ensure `diff` is even so that the min, max and middle legend values are integers + diff = Math.ceil(diff / 2)*2; + const max = min + diff; + + return [min, max]; + } + return null; +} + +function createGradientSettings(trackColorMode, scenery, def) { + if ('gradient' in def) return def.gradient; + if ('dynamicGradient' in def) { + const gradient = def.dynamicGradient; + const [minValue, maxValue] = getMinMax(trackColorMode, scenery) ?? [gradient.defaultMin, gradient.defaultMax]; + + const diff = gradient.min.map((min, i) => { + const max = gradient.max[i]; + return (max - min) / (maxValue - minValue); + }); + const base = gradient.min.map((min, i) => { + return min - diff[i] * minValue; + }); + + return { + base, + diff, + legendMin: minValue, + legendMax: maxValue, + unit: gradient.unit, + startLegendAt0: gradient.startLegendAt0 ?? false, + }; + } + return null; +} + +function createGradientDefs(scenery) { + return Object.fromEntries( + Object.entries(Constants.trackColorModes).map( + ([mode, def]) => [mode, createGradientSettings(mode, scenery, def)], + ), + ); +} + +export function GradientsManager(props) { + const { children, scenery } = props; + + const gradientDefs = useMemo(() => createGradientDefs(scenery), [scenery]); + + return ( + + { children } + + ); +} diff --git a/src/components/map/Map.css b/src/components/map/Map.css index 3e72537..daf38cc 100644 --- a/src/components/map/Map.css +++ b/src/components/map/Map.css @@ -128,3 +128,7 @@ .map svg g.spawn-point path.background { fill: #111; } + +.map svg g.platform rect { + fill: #444; +} diff --git a/src/components/map/ObjectsRenderQueue.js b/src/components/map/ObjectsRenderQueue.js index 738951c..2c43b76 100644 --- a/src/components/map/ObjectsRenderQueue.js +++ b/src/components/map/ObjectsRenderQueue.js @@ -11,8 +11,17 @@ import DerailerRenderer from './object-renderers/DerailerRenderer'; import ElectrificationStatusPopup from './additional-layer-components/ElectrificationStatusPopup'; import SpawnPointRenderer from './object-renderers/SpawnPointRenderer'; import IsolationEndRenderer from './object-renderers/IsolationEndRenderer'; +import PlatformRenderer from './object-renderers/PlatformRenderer'; +import Constants from '../../helpers/constants'; const ObjectRendererQueue = [ + { + 'name': 'platforms', + 'category': 'misc', + 'type': 'Platform', + 'renderer': PlatformRenderer, + 'cond': (layers) => layers['platforms'] && !Constants.parser.skipPlatforms + }, { 'name': 'tracks', 'category': 'tracks', diff --git a/src/components/map/object-renderers/PlatformRenderer.js b/src/components/map/object-renderers/PlatformRenderer.js new file mode 100644 index 0000000..eb3946a --- /dev/null +++ b/src/components/map/object-renderers/PlatformRenderer.js @@ -0,0 +1,23 @@ +import Constants from "../../../helpers/constants"; + +export default function PlatformRenderer(props) { + const { object } = props; + const [x, y] = object.pos.toSVGCoords(); + const tiltNotOk = object.yawData.tilt > Constants.map.platformMaxTilt; + if(!object.def || tiltNotOk) return null; + + const [centerX, centerY] = object.def.center.toSVGCoords(); + const sizeX = object.def.size.x; + const sizeY = object.def.size.z; + + return ( + + + + ); +} diff --git a/src/components/map/object-renderers/SignalBoxRenderer.js b/src/components/map/object-renderers/SignalBoxRenderer.js index 71368b5..32a92d1 100644 --- a/src/components/map/object-renderers/SignalBoxRenderer.js +++ b/src/components/map/object-renderers/SignalBoxRenderer.js @@ -6,8 +6,9 @@ export default function SignalBoxRenderer(props) { const [x, y] = object.pos.toSVGCoords(); const text = object.getPrintableSignalBoxName(); - const rotDiff = object.def.undef ? Math.round(object.rot.y / 90) * (-90) : object.def.rot * 90; - const rot = object.rot.y + rotDiff; + const yaw = object.yawData?.yaw || object.rot.y; + const rotDiff = object.def.undef ? Math.round(yaw / 90) * (-90) : object.def.rot * 90; + const rot = yaw + rotDiff; return ( diff --git a/src/components/map/object-renderers/TrackRenderer.js b/src/components/map/object-renderers/TrackRenderer.js index 6f9c9c8..c5b99a7 100644 --- a/src/components/map/object-renderers/TrackRenderer.js +++ b/src/components/map/object-renderers/TrackRenderer.js @@ -2,31 +2,35 @@ import React, { useContext } from "react"; import SettingsContext from "../../../contexts/SettingsContext"; import Constants from "../../../helpers/constants"; import { ElectrificationStatus } from "../../../model/electrification-status"; -import MiscHelper from "../../../helpers/miscHelper"; import { setHoveredTrack, unsetHoveredTrack } from "../../../services/trackHoverInfoService"; import ZoomPanContext from "../../../contexts/ZoomPanContext"; +import GradientsContext from "../../../contexts/GradientsContext"; +import MiscHelper from "../../../helpers/miscHelper"; export default function TrackRenderer(props) { const { object } = props; const { trackColorMode } = useContext(SettingsContext); - const {alignView} = useContext(ZoomPanContext); + const { gradientDefs } = useContext(GradientsContext); + const {alignView} = useContext(ZoomPanContext); - const onAlign = (event) => { - alignView(object.rot.y, event); - }; + const onAlign = (event) => { + alignView(object.rot.y, event); + }; return ( + gradientDef={gradientDefs[trackColorMode] ?? null} + onAlign={onAlign} + /> ); } const MemoizedTrackRenderer = React.memo(StatelessTrackRenderer); function StatelessTrackRenderer(props) { - const { object, trackColorMode , onAlign} = props; + const { object, trackColorMode, gradientDef, onAlign } = props; if (object.points.start.distanceSq(object.points.end) < 0.001) { return null; @@ -41,16 +45,16 @@ function StatelessTrackRenderer(props) { }; const onClick = (event) => { - if (object.type !== 'StandardTrack' || object.r !== 0) return; - if (event.detail === 2) { - event.preventDefault(); - onAlign(event); - } - }; + if (object.type !== 'StandardTrack' || object.r !== 0) return; + if (event.detail === 2) { + event.preventDefault(); + onAlign(event); + } + }; - const path = getTrackPath(object); - const color = getTrackColor(object, trackColorMode); - const defs = getTrackDefs(object, trackColorMode); + const path = getTrackPath(object); + const color = getTrackColor(object, trackColorMode, gradientDef); + const defs = getTrackDefs(object, trackColorMode, gradientDef); return ( @@ -97,7 +101,18 @@ function getTrackPath(object) { } } -function getTrackColor(object, trackColorMode) { +function getGradientValues(object, trackColorMode) { + switch (trackColorMode) { + case 'slope': + return [Math.abs(object.start_slope), Math.abs(object.end_slope)]; + case 'elevation': + return [object.points.start.y, object.points.end.y]; + default: + return null; + } +} + +function getTrackColor(object, trackColorMode, gradientDef) { const modeDef = Constants.trackColorModes[trackColorMode]; switch (trackColorMode) { @@ -135,46 +150,50 @@ function getTrackColor(object, trackColorMode) { default: return modeDef.options[modeDef.optionDefault][0]; } - case "slope": - // do not use track gradient unless the slope is different on both ends - if (object.start_slope === object.end_slope) { - return MiscHelper.getTrackGradient(modeDef.gradient, Math.abs(object.start_slope)); - } - - return `url(#track-slope-${object.id})`; case "max-speed": if (!object.maxspeed) { if (object.type === 'RouteTrack') return modeDef.options['unknown'][0]; return modeDef.options['derail'][0]; } - return MiscHelper.getTrackGradient(modeDef.gradient, object.maxspeed); + return MiscHelper.getTrackGradientColor(gradientDef, object.maxspeed); + + case "elevation": + case "slope": + const [startValue, endValue] = getGradientValues(object, trackColorMode); + if (startValue === endValue) return MiscHelper.getTrackGradientColor(gradientDef, startValue); + return `url(#track-${trackColorMode}-${object.id})`; } } -function getTrackDefs(object, trackColorMode) { - if (trackColorMode !== "slope") return null; - if (object.start_slope === object.end_slope) return null; - +function getGradientDefs(object, trackColorMode, gradientDef, startValue, endValue) { + if (startValue === endValue) return null; const [x1, y1] = object.points.start.toSVGCoords(); const [x2, y2] = object.points.end.toSVGCoords(); - const gradId = `track-slope-${object.id}`; - 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 gradId = `track-${trackColorMode}-${object.id}`; + const startColor = MiscHelper.getTrackGradientColor(gradientDef, startValue); + const endColor = MiscHelper.getTrackGradientColor(gradientDef, endValue); return ( - - - - + + + + ); } + +function getTrackDefs(object, trackColorMode, gradientDef) { + const gradientVals = getGradientValues(object, trackColorMode); + if (gradientVals === null) return null; + const [startValue, endValue] = gradientVals; + return getGradientDefs(object, trackColorMode, gradientDef, startValue, endValue); +} diff --git a/src/components/sidemenu/LayersMenu.js b/src/components/sidemenu/LayersMenu.js index aa59caf..5f2528d 100644 --- a/src/components/sidemenu/LayersMenu.js +++ b/src/components/sidemenu/LayersMenu.js @@ -36,6 +36,8 @@ export default function LayersMenu(props) { function LayersMenuItem(props) { const { layer, isChecked, onToggle } = props; + if(layer.cond && !layer.cond()) return null; + return (