diff --git a/src/components/map/ZoomPanWrapper.js b/src/components/map/ZoomPanWrapper.js index b29ece7..e4866d2 100644 --- a/src/components/map/ZoomPanWrapper.js +++ b/src/components/map/ZoomPanWrapper.js @@ -2,6 +2,7 @@ import React, { useRef, useEffect, useCallback } from 'react'; import './ZoomPanWrapper.css'; import Constants from '../../helpers/constants'; import { useZoomPanSubscriber, viewBox$, clientRect$, camera$ } from '../../hooks/useZoomPubSub'; +import { mapRotation$ } from '../../services/mapRotationService'; export default function ZoomPanWrapper({children}) { const wrapperRef = useRef(null); @@ -38,6 +39,7 @@ export default function ZoomPanWrapper({children}) { rafScheduledRef.current = false; cameraWrapperRef.current.setAttribute('transform', getCameraTransformString()); camera$.next(cameraRef.current); + mapRotation$.next(cameraRef.current.rotation); }); }, []); diff --git a/src/components/map/distance-meter/DistanceMeterText.js b/src/components/map/distance-meter/DistanceMeterText.js index bdac799..597c102 100644 --- a/src/components/map/distance-meter/DistanceMeterText.js +++ b/src/components/map/distance-meter/DistanceMeterText.js @@ -1,4 +1,9 @@ +import { useEffect, useState } from "react"; +import { mapRotation$ } from "../../../services/mapRotationService"; +import AngleHelper from "../../../helpers/angleHelper"; + export default function DistanceMeterText(props) { + const [mapRotation, setMapRotation] = useState(0); const { start, end, totalDistance } = props; const cx = (start[0] + end[0]) / 2; @@ -7,10 +12,19 @@ export default function DistanceMeterText(props) { const dy = end[1] - start[1]; const len = Math.sqrt(dx * dx + dy * dy); const angle = Math.atan2(dy, dx) * (180 / Math.PI); - const upsideDown = angle >= 90 || angle <= -90; + const rotation = AngleHelper.normalizeDegAngle(mapRotation + angle); + const upsideDown = rotation >= 90 && rotation <= 270; const x = cx + len / 2; const y = cy + (upsideDown ? 2 : -2); + useEffect(() => { + const subscription = mapRotation$.subscribe(date => { + setMapRotation(date); + }); + + return () => subscription.unsubscribe(); + }, []); + return ( = 180; - const upsideDownRot = upsideDown ? 90 : -90; - const anchor = upsideDown ? "end" : "start"; - return ( @@ -23,11 +18,12 @@ export default function DerailerRenderer(props) { }} /> - - - {object.getPrintableDerailerName()} - - + ); } diff --git a/src/components/map/object-renderers/IsolationIdRenderer.js b/src/components/map/object-renderers/IsolationIdRenderer.js index 49cd061..785b312 100644 --- a/src/components/map/object-renderers/IsolationIdRenderer.js +++ b/src/components/map/object-renderers/IsolationIdRenderer.js @@ -1,3 +1,5 @@ +import SimpleLabelText from "../text/SimpleLabelText"; + export default function IsolationIdRenderer(props) { const { object } = props; const pos = object.points.start.lerp(object.points.end, 0.5); @@ -8,16 +10,10 @@ export default function IsolationIdRenderer(props) { return null; } - return ( - - {text} - - ); + return } diff --git a/src/components/map/object-renderers/RouteRenderer.js b/src/components/map/object-renderers/RouteRenderer.js index 5217bd7..7a3c390 100644 --- a/src/components/map/object-renderers/RouteRenderer.js +++ b/src/components/map/object-renderers/RouteRenderer.js @@ -1,15 +1,11 @@ import { ReactSVG } from "react-svg"; -import AngleHelper from "../../../helpers/angleHelper"; +import AlwaysUpText from "../text/AlwaysUpText"; export default function RouteRenderer(props) { const { object } = props; const [x, y] = object.pos.toSVGCoords(); - const rot = AngleHelper.normalizeDegAngle(object.rot.y); - const upsideDown = rot >= 180; - const upsideDownRot = upsideDown ? 90 : -90; const offY = object.track_count === 2 ? -22 : -20; - const anchor = upsideDown ? "start" : "end"; const arrowOffset = - object.track_offset - 37.8; return ( @@ -24,11 +20,15 @@ export default function RouteRenderer(props) { }} /> - - - {object.route_name} - - + ); } diff --git a/src/components/map/object-renderers/SignalBoxRenderer.js b/src/components/map/object-renderers/SignalBoxRenderer.js index c4d4c39..b7b495d 100644 --- a/src/components/map/object-renderers/SignalBoxRenderer.js +++ b/src/components/map/object-renderers/SignalBoxRenderer.js @@ -1,5 +1,5 @@ import { ReactSVG } from 'react-svg'; -import AngleHelper from '../../../helpers/angleHelper'; +import AlwaysUpText from '../text/AlwaysUpText'; export default function SignalBoxRenderer(props) { const { object } = props; @@ -7,9 +7,7 @@ export default function SignalBoxRenderer(props) { const text = object.getPrintableSignalBoxName(); const rotDiff = object.def.undef ? Math.round(object.rot.y / 90) * (-90) : object.def.rot * 90; - const rot = AngleHelper.normalizeDegAngle(object.rot.y + rotDiff); - const upsideDown = rot >= 90 && rot < 270; - const upsideDownRot = upsideDown ? 180 : 0; + const rot = object.rot.y + rotDiff; return ( @@ -24,11 +22,13 @@ export default function SignalBoxRenderer(props) { }} /> - - - {text} - - + ); } diff --git a/src/components/map/object-renderers/SignalExtendedRenderer.js b/src/components/map/object-renderers/SignalExtendedRenderer.js index 277989d..59372ca 100644 --- a/src/components/map/object-renderers/SignalExtendedRenderer.js +++ b/src/components/map/object-renderers/SignalExtendedRenderer.js @@ -3,6 +3,7 @@ import AngleHelper from '../../../helpers/angleHelper'; import DefinedSignalSigns from '../../../model/defs/defined-signal-signs'; import SignalElementsEnums from '../../../model/signal-elements/enums'; import ExtendedSignalsHelper from '../../../helpers/extendedSignalsHelper'; +import AlwaysUpText from '../text/AlwaysUpText'; import C from '../../../helpers/extendedSignalsConstants'; export default function SignalExtendedRenderer(props) { @@ -24,20 +25,18 @@ export default function SignalExtendedRenderer(props) { function SignalNameText(props) { const {object, rot, pointsData} = props; - - const upsideDown = rot >= 180; - const upsideDownRot = upsideDown ? 90 : -90; - const anchor = upsideDown ? "start" : "end"; const isOverhead = object.signal_elements.isOverhead(); const baseY = isOverhead ? pointsData.headOffsetY + pointsData.polePoints.end : 0; return ( - - - {object.getPrintableSignalName()} - - + ); } diff --git a/src/components/map/object-renderers/SignalStandardRenderer.js b/src/components/map/object-renderers/SignalStandardRenderer.js index 87964f6..4535e57 100644 --- a/src/components/map/object-renderers/SignalStandardRenderer.js +++ b/src/components/map/object-renderers/SignalStandardRenderer.js @@ -1,15 +1,10 @@ import { ReactSVG } from "react-svg"; -import AngleHelper from "../../../helpers/angleHelper"; +import AlwaysUpText from "../text/AlwaysUpText"; export default function SignalStandardRenderer(props) { const { object } = props; const [x, y] = object.pos.toSVGCoords(); - const rot = AngleHelper.normalizeDegAngle(object.rot.y); - const upsideDown = rot >= 180; - const upsideDownRot = upsideDown ? 90 : -90; - const anchor = upsideDown ? "end" : "start"; - return ( @@ -23,11 +18,12 @@ export default function SignalStandardRenderer(props) { }} /> - - - {object.getPrintableSignalName()} - - + ); } diff --git a/src/components/map/object-renderers/SpawnPointRenderer.js b/src/components/map/object-renderers/SpawnPointRenderer.js index 430e882..b7b4740 100644 --- a/src/components/map/object-renderers/SpawnPointRenderer.js +++ b/src/components/map/object-renderers/SpawnPointRenderer.js @@ -1,15 +1,10 @@ import { ReactSVG } from 'react-svg' -import AngleHelper from '../../../helpers/angleHelper'; +import AlwaysUpText from '../text/AlwaysUpText'; export default function SpawnPointRenderer(props) { const { object } = props; const [x, y] = object.pos.toSVGCoords(); - const rot = AngleHelper.normalizeDegAngle(object.rot.y); - const upsideDown = rot >= 180; - const upsideDownRot = upsideDown ? 90 : -90; - const anchor = upsideDown ? "start" : "end"; - return ( @@ -23,11 +18,13 @@ export default function SpawnPointRenderer(props) { }} /> - - - {object.getPrintableSpawnPointName()} - - + ); } diff --git a/src/components/map/object-renderers/SwitchNameRenderer.js b/src/components/map/object-renderers/SwitchNameRenderer.js index ea7782e..b7d9a02 100644 --- a/src/components/map/object-renderers/SwitchNameRenderer.js +++ b/src/components/map/object-renderers/SwitchNameRenderer.js @@ -1,18 +1,14 @@ +import SimpleLabelText from "../text/SimpleLabelText"; + export default function SwitchNameRenderer(props) { const { object } = props; const [x, y] = object.pos.toSVGCoords(); const text = object.id_switch; - return ( - - {text} - - ); + return ; } diff --git a/src/components/map/object-renderers/TrackObjectRenderer.js b/src/components/map/object-renderers/TrackObjectRenderer.js index db5b8b2..b1b7340 100644 --- a/src/components/map/object-renderers/TrackObjectRenderer.js +++ b/src/components/map/object-renderers/TrackObjectRenderer.js @@ -1,3 +1,5 @@ +import SimpleLabelText from "../text/SimpleLabelText"; + export default function TrackObjectRenderer(props) { const { object } = props; const [x, y] = object.pos.toSVGCoords(); @@ -6,29 +8,26 @@ export default function TrackObjectRenderer(props) { return ( - - {text} - + wrapperStyle={{pointerEvents: 'none'}} + /> ); } diff --git a/src/components/map/text/AlwaysUpText.js b/src/components/map/text/AlwaysUpText.js new file mode 100644 index 0000000..6e9a330 --- /dev/null +++ b/src/components/map/text/AlwaysUpText.js @@ -0,0 +1,29 @@ +import { useEffect, useState } from "react"; +import AngleHelper from "../../../helpers/angleHelper"; +import { mapRotation$ } from "../../../services/mapRotationService"; + +export default function AlwaysUpText(props) { + const [mapRotation, setMapRotation] = useState(0); + const { baseRot, additionalRot, text, reverseAnchor, offsetX, offsetY } = props; + + const adjustedRot = AngleHelper.normalizeDegAngle(baseRot + mapRotation); + const upsideDown = adjustedRot >= 180; + const upsideDownRot = upsideDown ? 180 : 0; + const anchor = (upsideDown !== !!reverseAnchor) ? "end" : "start"; + + useEffect(() => { + const subscription = mapRotation$.subscribe(rotation => { + setMapRotation(rotation); + }); + + return () => subscription.unsubscribe(); + }, []); + + return ( + + + {text} + + + ); +} \ No newline at end of file diff --git a/src/components/map/text/SimpleLabelText.js b/src/components/map/text/SimpleLabelText.js new file mode 100644 index 0000000..79e650d --- /dev/null +++ b/src/components/map/text/SimpleLabelText.js @@ -0,0 +1,29 @@ +import { useCallback, useEffect, useRef } from "react"; +import { mapRotation$ } from "../../../services/mapRotationService"; + +export default function SimpleLabelText(props) { + const { text, x, y, wrapperStyle } = props; + const gRef = useRef(null); + + const getTransformString = useCallback((rot) => ( + `translate(${x || 0}, ${y || 0}) rotate(${rot})` + ), [x, y]); + + useEffect(() => { + const subscription = mapRotation$.subscribe(rotation => { + if(!gRef.current) return; + + gRef.current.setAttribute("transform", getTransformString(-rotation)); + }); + + return () => subscription.unsubscribe(); + }, [getTransformString]); + + return ( + + + {text} + + + ); +} \ No newline at end of file diff --git a/src/services/mapRotationService.js b/src/services/mapRotationService.js new file mode 100644 index 0000000..4cd7c5c --- /dev/null +++ b/src/services/mapRotationService.js @@ -0,0 +1,7 @@ +import { BehaviorSubject } from 'rxjs'; + +export const mapRotation$ = new BehaviorSubject(null); + +export function setMapRotation(rotation) { + mapRotation$.next(rotation); +}