Optimized objects rendering

*Optimized objects on the map to render only above the specified zoom threshold
This commit is contained in:
izawartka 2025-08-13 14:04:42 +02:00
parent 0ca6f6b9e0
commit f5bc06ad8b
4 changed files with 64 additions and 12 deletions

View File

@ -37,49 +37,56 @@ const ObjectRendererQueue = [
'name': 'isolations-ends', 'name': 'isolations-ends',
'category': 'tracks', 'category': 'tracks',
'renderer': IsolationEndRenderer, 'renderer': IsolationEndRenderer,
'cond': (layers) => layers['isolations-ids'] 'cond': (layers) => layers['isolations-ids'],
'usesObjectDetails': true
}, },
{ {
'name': 'isolations-ids', 'name': 'isolations-ids',
'category': ['tracks', 'switches'], 'category': ['tracks', 'switches'],
'trackSources': [TrackSource.STANDARD], 'trackSources': [TrackSource.STANDARD],
'renderer': IsolationIdRenderer, 'renderer': IsolationIdRenderer,
'cond': (layers) => layers['isolations-ids'] 'cond': (layers) => layers['isolations-ids'],
'usesObjectDetails': true
}, },
{ {
'name': 'switches-names', 'name': 'switches-names',
'category': 'switches', 'category': 'switches',
'type': 'Switch', 'type': 'Switch',
'renderer': SwitchNameRenderer, 'renderer': SwitchNameRenderer,
'cond': (layers) => layers['switches-names'] 'cond': (layers) => layers['switches-names'],
'usesObjectDetails': true
}, },
{ {
'name': 'derailers', 'name': 'derailers',
'category': 'track-objects', 'category': 'track-objects',
'type': 'Derailer', 'type': 'Derailer',
'renderer': DerailerRenderer, 'renderer': DerailerRenderer,
'cond': (layers) => layers['derailers'] 'cond': (layers) => layers['derailers'],
'usesObjectDetails': true
}, },
{ {
'name': 'signs', 'name': 'signs',
'category': 'track-objects', 'category': 'track-objects',
'type': 'Sign', 'type': 'Sign',
'renderer': SignRenderer, 'renderer': SignRenderer,
'cond': (layers) => layers['signs'] 'cond': (layers) => layers['signs'],
'usesObjectDetails': true
}, },
{ {
'name': 'nevps', 'name': 'nevps',
'category': 'track-objects', 'category': 'track-objects',
'type': 'NEVP', 'type': 'NEVP',
'renderer': NEVPRenderer, 'renderer': NEVPRenderer,
'cond': (layers) => layers['nevps'] 'cond': (layers) => layers['nevps'],
'usesObjectDetails': true
}, },
{ {
'name': 'signals', 'name': 'signals',
'category': 'track-objects', 'category': 'track-objects',
'type': 'Signal', 'type': 'Signal',
'renderer': SignalRenderer, 'renderer': SignalRenderer,
'cond': (layers) => layers['signals'] 'cond': (layers) => layers['signals'],
'usesObjectDetails': true
}, },
{ {
'name': 'spawn-points', 'name': 'spawn-points',
@ -87,6 +94,7 @@ const ObjectRendererQueue = [
'type': 'SpawnPoint', 'type': 'SpawnPoint',
'renderer': SpawnPointRenderer, 'renderer': SpawnPointRenderer,
'cond': (layers) => layers['spawn-points'], 'cond': (layers) => layers['spawn-points'],
'usesObjectDetails': true
}, },
{ {
'name': 'signalboxes', 'name': 'signalboxes',
@ -106,7 +114,8 @@ const ObjectRendererQueue = [
'category': 'track-objects', 'category': 'track-objects',
'renderer': TrackObjectRenderer, 'renderer': TrackObjectRenderer,
'cond': (layers) => layers['track-objects'], 'cond': (layers) => layers['track-objects'],
'pointerEvents': true 'pointerEvents': true,
'usesObjectDetails': true
} }
] ]

View File

@ -1,11 +1,22 @@
import React, { useContext, useMemo, memo } from "react"; import { useContext, useMemo, memo, useCallback, useRef, useEffect } from "react";
import SceneryContext from "../../contexts/SceneryContext"; import SceneryContext from "../../contexts/SceneryContext";
import SettingsContext from "../../contexts/SettingsContext"; import SettingsContext from "../../contexts/SettingsContext";
import Constants from "../../helpers/constants"; import Constants from "../../helpers/constants";
import { mapZoomObjectDetails$ } from "../../services/mapZoomService";
export default function SceneryLayer(props) { export default function SceneryLayer(props) {
const { queueItem } = props; const { queueItem } = props;
const { name, category, type, types, trackSources, cond, renderer: Renderer, additionalComponents: AdditionalComponents } = queueItem; const {
name,
category,
type,
types,
trackSources,
cond,
renderer: Renderer,
additionalComponents: AdditionalComponents,
usesObjectDetails
} = queueItem;
const { scenery } = useContext(SceneryContext); const { scenery } = useContext(SceneryContext);
const { layers } = useContext(SettingsContext); const { layers } = useContext(SettingsContext);
@ -38,6 +49,7 @@ export default function SceneryLayer(props) {
trackSources={trackSources} trackSources={trackSources}
pointerEvents={pointerEvents} pointerEvents={pointerEvents}
AdditionalComponents={AdditionalComponents} AdditionalComponents={AdditionalComponents}
usesObjectDetails={usesObjectDetails || false}
/> />
); );
} }
@ -45,9 +57,32 @@ export default function SceneryLayer(props) {
const MemoizedSceneryLayer = memo(StatelessSceneryLayer); const MemoizedSceneryLayer = memo(StatelessSceneryLayer);
function StatelessSceneryLayer({ name, Renderer, objects, type, types, trackSources, pointerEvents = false, AdditionalComponents = [] }) { function StatelessSceneryLayer({ name, Renderer, objects, type, types, trackSources, pointerEvents = false, AdditionalComponents = [], usesObjectDetails }) {
const layerRef = useRef(null);
const handleDetailsUpdate = useCallback((details) => {
if (!layerRef.current) return;
if (details) {
layerRef.current.style.display = '';
}
else {
layerRef.current.style.display = 'none';
}
}, []);
useEffect(() => {
if (!usesObjectDetails) return;
const subscription = mapZoomObjectDetails$.subscribe(details => {
handleDetailsUpdate(details);
});
return () => subscription.unsubscribe();
}, [handleDetailsUpdate, usesObjectDetails]);
return ( return (
<g <g
ref={layerRef}
className={`scenery-layer-${name}`} className={`scenery-layer-${name}`}
pointerEvents={pointerEvents ? "all" : "none"} pointerEvents={pointerEvents ? "all" : "none"}
> >

View File

@ -9,7 +9,8 @@ const Constants = {
forcePointerEvents: false, forcePointerEvents: false,
platformMaxTilt: 0.5, platformMaxTilt: 0.5,
showDebugTrackIds: false, showDebugTrackIds: false,
mapZoomTrackDetailsThreshold: 1 / 1.44 mapZoomTrackDetailsThreshold: 1 / 1.44,
mapZoomObjectDetailsThreshold: 0.5
}, },
parser: { parser: {
logSceneryAfterFinished: true, logSceneryAfterFinished: true,

View File

@ -3,6 +3,8 @@ import Constants from '../helpers/constants';
export const mapZoom$ = new BehaviorSubject(null); export const mapZoom$ = new BehaviorSubject(null);
export const mapZoomTrackDetails$ = new BehaviorSubject(true); export const mapZoomTrackDetails$ = new BehaviorSubject(true);
export const mapZoomObjectDetails$ = new BehaviorSubject(true);
export function setMapZoom(zoom) { export function setMapZoom(zoom) {
mapZoom$.next(zoom); mapZoom$.next(zoom);
@ -10,4 +12,9 @@ export function setMapZoom(zoom) {
if (mapZoomTrackDetails$.getValue() !== newZoomTrackDetails) { if (mapZoomTrackDetails$.getValue() !== newZoomTrackDetails) {
mapZoomTrackDetails$.next(newZoomTrackDetails); mapZoomTrackDetails$.next(newZoomTrackDetails);
} }
const newZoomObjectDetails = zoom > Constants.map.mapZoomObjectDetailsThreshold;
if (mapZoomObjectDetails$.getValue() !== newZoomObjectDetails) {
mapZoomObjectDetails$.next(newZoomObjectDetails);
}
} }