@expofp/wayfinding 3.33.0 → 3.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/createWayfinding.d.ts +4 -3
- package/dist/createWayfinding.js +2 -3
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4 -1
- package/dist/renderer/createWayfindingRenderer.d.ts +2 -1
- package/dist/renderer/createWayfindingRenderer.js +7 -4
- package/dist/renderer/iconManager.d.ts +10 -3
- package/dist/renderer/iconManager.js +51 -15
- package/dist/renderer/index.d.ts +2 -0
- package/dist/renderer/index.js +1 -0
- package/dist/renderer/layerManager.d.ts +11 -13
- package/dist/renderer/layerManager.js +7 -23
- package/dist/renderer/layers.d.ts +39 -0
- package/dist/renderer/layers.js +62 -0
- package/dist/renderer/routeLineManager.d.ts +5 -5
- package/dist/renderer/routeLineManager.js +49 -33
- package/dist/renderer/trailManager.d.ts +5 -6
- package/dist/renderer/trailManager.js +26 -44
- package/dist/renderer/types.d.ts +25 -40
- package/dist/runtime/createWayfindingRuntime.d.ts +1 -1
- package/dist/runtime/createWayfindingRuntime.js +4 -15
- package/dist/runtime/endpointView.d.ts +1 -2
- package/dist/runtime/endpointView.js +1 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/positionTrailView.d.ts +1 -2
- package/dist/runtime/positionTrailView.js +2 -2
- package/dist/runtime/positionView.d.ts +1 -2
- package/dist/runtime/positionView.js +3 -2
- package/dist/runtime/routeLinesView.d.ts +1 -3
- package/dist/runtime/routeLinesView.js +3 -11
- package/dist/runtime/trailView.d.ts +1 -2
- package/dist/runtime/trailView.js +2 -5
- package/dist/runtime/transitionView.d.ts +1 -2
- package/dist/runtime/transitionView.js +1 -2
- package/dist/runtime/types.d.ts +0 -8
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@ Framework-neutral wayfinding for the ExpoFP SDK: a routing/snapping engine, an
|
|
|
4
4
|
imperative runtime orchestrator, and a scene renderer — with **no** coupling to
|
|
5
5
|
efp stores or MobX. `core` and `runtime` are environment-agnostic; the scene
|
|
6
6
|
renderer assumes a browser-like environment (`requestAnimationFrame`, `document`),
|
|
7
|
-
so the package is not suitable for Node/SSR as-is. All three layers sit behind
|
|
8
|
-
|
|
7
|
+
so the package is not suitable for Node/SSR as-is. All three layers sit behind two
|
|
8
|
+
entry points: `initWayfindingLayers`, which builds the layer block for the host
|
|
9
|
+
to mount, and `createWayfinding`, which finds it in the scene and fills it.
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
@@ -26,7 +27,9 @@ reference it, so its types must resolve for `@expofp/wayfinding`'s `.d.ts` to co
|
|
|
26
27
|
| `renderer` | Scene mutator (icons, trails, route lines) against an injected `RendererPort`. |
|
|
27
28
|
|
|
28
29
|
These layers are internal. The package exports only the `createWayfinding`
|
|
29
|
-
facade, the
|
|
30
|
+
facade, `initWayfindingLayers` (the layer block it draws into, for the host to
|
|
31
|
+
mount) with `resolveWayfindingLayers` (which finds that block in a scene again),
|
|
32
|
+
the port interfaces the host implements, the boundary data types, the
|
|
30
33
|
`CURRENT_POSITION_POINT_ID` protocol sentinel, and the `optimizeWaypointOrder`
|
|
31
34
|
helper.
|
|
32
35
|
|
|
@@ -36,7 +39,7 @@ The package knows nothing concrete about the host renderer, graph data, or app
|
|
|
36
39
|
state. The host supplies four ports via `WayfindingConfig`:
|
|
37
40
|
|
|
38
41
|
- **`dataSource`** (`GraphDataSource`) — graph lines + line ends for pathfinding.
|
|
39
|
-
- **`renderer`** (`RendererPort`) —
|
|
42
|
+
- **`renderer`** (`RendererPort`) — the host's cardinal-snap rule and commit.
|
|
40
43
|
- **`iconProvider`** (`IconProvider`) — canvases for icon names.
|
|
41
44
|
- **`floorContext`** (`FloorContext`) — active floor / visibility predicates.
|
|
42
45
|
|
|
@@ -54,7 +57,7 @@ const wayfinding = createWayfinding({
|
|
|
54
57
|
renderer, // RendererPort
|
|
55
58
|
iconProvider, // IconProvider
|
|
56
59
|
floorContext, // FloorContext
|
|
57
|
-
layers, //
|
|
60
|
+
layers, // WayfindingLayers, from resolveWayfindingLayers(scene.rootLayer)
|
|
58
61
|
gpsConfig, // optional GPS calibration + snap/reroute thresholds
|
|
59
62
|
onTransitionClick: (point) => {
|
|
60
63
|
/* switch active floor */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Box } from '@expofp/geometry';
|
|
2
2
|
import type { RenderableDef } from '@expofp/renderer';
|
|
3
3
|
import { type GpsProjectionConfig, type GraphDataSource, type Route, type RouteEndpoint, type RouteLine, type RoutePoint, type TransitionPointDef } from './core/index.js';
|
|
4
|
-
import { type IconConfig, type RendererPort } from './renderer/index.js';
|
|
5
|
-
import { type FloorContext, type IconProvider, type
|
|
4
|
+
import { type IconConfig, type RendererPort, type WayfindingLayers } from './renderer/index.js';
|
|
5
|
+
import { type FloorContext, type IconProvider, type PositionInput, type SetRouteInput } from './runtime/index.js';
|
|
6
6
|
/** Plan GPS calibration plus optional snap / reroute distance overrides (meters). */
|
|
7
7
|
export type GpsConfig = GpsProjectionConfig & {
|
|
8
8
|
snapThreshold?: number;
|
|
@@ -18,7 +18,8 @@ export interface WayfindingConfig {
|
|
|
18
18
|
readonly renderer: RendererPort;
|
|
19
19
|
readonly iconProvider: IconProvider;
|
|
20
20
|
readonly floorContext: FloorContext;
|
|
21
|
-
|
|
21
|
+
/** The layers from `initWayfindingLayers()` — the block the host mounted, which this fills. */
|
|
22
|
+
readonly layers: WayfindingLayers;
|
|
22
23
|
/** Plan GPS calibration; the snap and reroute thresholds are derived from it. */
|
|
23
24
|
readonly gpsConfig?: GpsConfig;
|
|
24
25
|
readonly onTransitionClick?: (point: TransitionPointDef) => void;
|
package/dist/createWayfinding.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createWayfindingEngine, getThresholdFromMetersToSvg, getThresholdOrDefault, resolveRerouteThresholdSvg, SNAP_THRESHOLD_METERS, } from './core/index.js';
|
|
2
|
-
import { createWayfindingRenderer } from './renderer/index.js';
|
|
2
|
+
import { createWayfindingRenderer, } from './renderer/index.js';
|
|
3
3
|
import { createWayfindingRuntime, } from './runtime/index.js';
|
|
4
4
|
export function createWayfinding(config) {
|
|
5
5
|
const { dataSource, renderer: rendererPort, iconProvider, floorContext, layers, gpsConfig, } = config;
|
|
6
6
|
const engine = createWayfindingEngine(dataSource);
|
|
7
|
-
const renderer = createWayfindingRenderer(rendererPort);
|
|
7
|
+
const renderer = createWayfindingRenderer(rendererPort, layers);
|
|
8
8
|
const snapThresholdMeters = getThresholdOrDefault(gpsConfig?.snapThreshold, SNAP_THRESHOLD_METERS);
|
|
9
9
|
const snapThreshold = getThresholdFromMetersToSvg({
|
|
10
10
|
thresholdInMeters: snapThresholdMeters,
|
|
@@ -15,7 +15,6 @@ export function createWayfinding(config) {
|
|
|
15
15
|
renderer,
|
|
16
16
|
iconProvider,
|
|
17
17
|
floorContext,
|
|
18
|
-
layers,
|
|
19
18
|
snapThreshold,
|
|
20
19
|
rerouteThreshold: resolveRerouteThresholdSvg(gpsConfig),
|
|
21
20
|
onTransitionClick: (point) => config.onTransitionClick?.(point),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@expofp/wayfinding` — framework-neutral wayfinding: routing/snapping engine,
|
|
3
|
-
* an imperative runtime, and a scene renderer behind
|
|
3
|
+
* an imperative runtime, and a scene renderer behind two entry points:
|
|
4
|
+
* `initWayfindingLayers` builds the layers for the host to mount,
|
|
5
|
+
* `createWayfinding` fills them.
|
|
4
6
|
*/
|
|
5
7
|
export type { GpsConfig, Wayfinding, WayfindingConfig } from './createWayfinding.js';
|
|
6
8
|
export { createWayfinding } from './createWayfinding.js';
|
|
9
|
+
export type { WayfindingLayers } from './renderer/index.js';
|
|
10
|
+
export { initWayfindingLayers, resolveWayfindingLayers } from './renderer/index.js';
|
|
7
11
|
export type { GraphDataSource } from './core/index.js';
|
|
8
12
|
export type { IconConfig, RendererPort } from './renderer/index.js';
|
|
9
|
-
export type { FloorContext, IconAsset, IconName, IconProvider
|
|
13
|
+
export type { FloorContext, IconAsset, IconName, IconProvider } from './runtime/index.js';
|
|
10
14
|
export type { GraphLine, Route, RouteEndpoint, RouteLine, RoutePoint, TransitionPointDef, } from './core/index.js';
|
|
11
15
|
export type { PositionInput, SetRouteInput } from './runtime/index.js';
|
|
12
16
|
export { CURRENT_POSITION_POINT_ID } from './runtime/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@expofp/wayfinding` — framework-neutral wayfinding: routing/snapping engine,
|
|
3
|
-
* an imperative runtime, and a scene renderer behind
|
|
3
|
+
* an imperative runtime, and a scene renderer behind two entry points:
|
|
4
|
+
* `initWayfindingLayers` builds the layers for the host to mount,
|
|
5
|
+
* `createWayfinding` fills them.
|
|
4
6
|
*/
|
|
5
7
|
export { createWayfinding } from './createWayfinding.js';
|
|
8
|
+
export { initWayfindingLayers, resolveWayfindingLayers } from './renderer/index.js';
|
|
6
9
|
// Protocol sentinel: the host must stamp it as the `id` of a "current position"
|
|
7
10
|
// route endpoint so the runtime renders that point as the live position.
|
|
8
11
|
export { CURRENT_POSITION_POINT_ID } from './runtime/index.js';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { WayfindingLayers } from './layers.js';
|
|
1
2
|
import type { RendererPort, WayfindingRenderer, WayfindingRendererConfig } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Create a {@link WayfindingRenderer} — the composition root over {@link LayerManager},
|
|
4
5
|
* {@link IconManager}, {@link TrailManager} and {@link RouteLineManager}.
|
|
5
6
|
*/
|
|
6
|
-
export declare function createWayfindingRenderer(port: RendererPort, config?: WayfindingRendererConfig): WayfindingRenderer;
|
|
7
|
+
export declare function createWayfindingRenderer(port: RendererPort, layers: WayfindingLayers, config?: WayfindingRendererConfig): WayfindingRenderer;
|
|
7
8
|
//# sourceMappingURL=createWayfindingRenderer.d.ts.map
|
|
@@ -11,12 +11,12 @@ const DEFAULT_COLORS = {
|
|
|
11
11
|
* Create a {@link WayfindingRenderer} — the composition root over {@link LayerManager},
|
|
12
12
|
* {@link IconManager}, {@link TrailManager} and {@link RouteLineManager}.
|
|
13
13
|
*/
|
|
14
|
-
export function createWayfindingRenderer(port, config = {}) {
|
|
14
|
+
export function createWayfindingRenderer(port, layers, config = {}) {
|
|
15
15
|
const colors = { ...DEFAULT_COLORS, ...config.colors };
|
|
16
16
|
const layerManager = createLayerManager(port);
|
|
17
|
-
const iconManager = createIconManager(layerManager, port);
|
|
18
|
-
const trailManager = createTrailManager(layerManager,
|
|
19
|
-
const routeLineManager = createRouteLineManager(layerManager, colors, port);
|
|
17
|
+
const iconManager = createIconManager(layerManager, port, layers);
|
|
18
|
+
const trailManager = createTrailManager(layerManager, layers.trail);
|
|
19
|
+
const routeLineManager = createRouteLineManager(layerManager, colors, port, layers);
|
|
20
20
|
const wfRenderer = {
|
|
21
21
|
setIcon: (...args) => iconManager.setIcon(...args),
|
|
22
22
|
clearIcons: (...args) => iconManager.clearIcons(...args),
|
|
@@ -35,6 +35,9 @@ export function createWayfindingRenderer(port, config = {}) {
|
|
|
35
35
|
iconManager.destroy();
|
|
36
36
|
trailManager.destroy();
|
|
37
37
|
routeLineManager.destroy();
|
|
38
|
+
// The layers belong to the host and outlive this instance, so emptying them has to reach the
|
|
39
|
+
// renderer before the dirty sets go — nothing is left to flush this afterwards.
|
|
40
|
+
layerManager.flush();
|
|
38
41
|
layerManager.destroy();
|
|
39
42
|
},
|
|
40
43
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ImageDef, RenderableDef } from '@expofp/renderer';
|
|
2
2
|
import type { LayerManager } from './layerManager.js';
|
|
3
|
+
import type { WayfindingLayers } from './layers.js';
|
|
3
4
|
import type { IconConfig, RendererPort } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* Lays one wayfinding icon out for a new pixel size: an icon holds its on-screen size, so its
|
|
6
|
-
* bounds are the source's pixel dimensions times the pixel size, about its own
|
|
7
|
+
* bounds are the source's pixel dimensions times the pixel size, about its own center.
|
|
7
8
|
*
|
|
8
9
|
* The **only** writer of an icon's bounds size, which is what lets everything else here write
|
|
9
10
|
* logical fields alone and never track a scale. Returns nothing when the size was already right, so
|
|
@@ -30,6 +31,12 @@ export interface IconManager {
|
|
|
30
31
|
/** Whether a def is a clickable icon. */
|
|
31
32
|
handleHover(def: RenderableDef): boolean;
|
|
32
33
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Create an {@link IconManager} that places icons into the two icon layers of the block.
|
|
36
|
+
* @param layerManager - Tracks what changed.
|
|
37
|
+
* @param port - The host's roll rule and commit.
|
|
38
|
+
* @param layers - The icon layers to place into.
|
|
39
|
+
* @returns The manager.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createIconManager(layerManager: LayerManager, port: RendererPort, layers: Pick<WayfindingLayers, 'icons' | 'currentPosition'>): IconManager;
|
|
35
42
|
//# sourceMappingURL=iconManager.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Rect } from '@expofp/geometry';
|
|
2
2
|
/**
|
|
3
3
|
* Lays one wayfinding icon out for a new pixel size: an icon holds its on-screen size, so its
|
|
4
|
-
* bounds are the source's pixel dimensions times the pixel size, about its own
|
|
4
|
+
* bounds are the source's pixel dimensions times the pixel size, about its own center.
|
|
5
5
|
*
|
|
6
6
|
* The **only** writer of an icon's bounds size, which is what lets everything else here write
|
|
7
7
|
* logical fields alone and never track a scale. Returns nothing when the size was already right, so
|
|
@@ -17,13 +17,28 @@ export function rescaleIcon(def, pixelSize) {
|
|
|
17
17
|
bounds.set(bounds.center.x, bounds.center.y, width, height, bounds.rotation, bounds.elevation);
|
|
18
18
|
return [def];
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Create an {@link IconManager} that places icons into the two icon layers of the block.
|
|
22
|
+
* @param layerManager - Tracks what changed.
|
|
23
|
+
* @param port - The host's roll rule and commit.
|
|
24
|
+
* @param layers - The icon layers to place into.
|
|
25
|
+
* @returns The manager.
|
|
26
|
+
*/
|
|
27
|
+
export function createIconManager(layerManager, port, layers) {
|
|
22
28
|
const icons = new Map();
|
|
23
29
|
const iconKeysByName = new Map();
|
|
24
30
|
const keyToName = new Map();
|
|
25
31
|
/** The reverse of {@link icons}: a pointer hit names a def, so it is a lookup, not a scan. */
|
|
26
32
|
const iconsByDef = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* Which of the two icon layers an icon belongs in — the only structural choice left to a caller,
|
|
35
|
+
* and not a layer name: `onTop` says the icon must not be covered by the others.
|
|
36
|
+
* @param cfg - The icon's config.
|
|
37
|
+
* @returns The layer to place it in.
|
|
38
|
+
*/
|
|
39
|
+
function layerFor(cfg) {
|
|
40
|
+
return cfg.onTop ? layers.currentPosition : layers.icons;
|
|
41
|
+
}
|
|
27
42
|
function composeKey(name, key) {
|
|
28
43
|
return key === undefined ? name : `${name}#${key}`;
|
|
29
44
|
}
|
|
@@ -45,22 +60,26 @@ export function createIconManager(layerManager, port) {
|
|
|
45
60
|
nameKeys.delete(iconKey);
|
|
46
61
|
keyToName.delete(iconKey);
|
|
47
62
|
}
|
|
48
|
-
function createIcon(iconKey, name, cfg) {
|
|
49
|
-
const layer =
|
|
63
|
+
function createIcon(iconKey, name, cfg, rotation = cfg.rotation) {
|
|
64
|
+
const layer = layerFor(cfg);
|
|
65
|
+
layerManager.touchLayer(layer);
|
|
50
66
|
// Size zero on purpose: an icon's size is only expressible in terms of the current pixel size, and
|
|
51
67
|
// `rescaleIcon` is its one writer. The renderer lays a def out as it registers, hidden or not, so
|
|
52
68
|
// it is never drawn at this placeholder.
|
|
53
|
-
const def =
|
|
69
|
+
const def = {
|
|
70
|
+
source: cfg.canvas,
|
|
71
|
+
bounds: new Rect({ x: cfg.x, y: cfg.y }, ZERO, rotation),
|
|
54
72
|
hidden: cfg.hidden ?? false,
|
|
55
73
|
dim: cfg.dimmed ? 1 : 0,
|
|
56
74
|
origin: cfg.origin,
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
};
|
|
76
|
+
if (cfg.cardinalSnap)
|
|
77
|
+
port.attachCardinalSnap(def);
|
|
59
78
|
def.onScale = ({ pixelSize }) => rescaleIcon(def, pixelSize);
|
|
60
79
|
layer.children.push(def);
|
|
61
80
|
const record = {
|
|
62
81
|
imageDef: def,
|
|
63
|
-
|
|
82
|
+
layer,
|
|
64
83
|
callback: cfg.onClick ?? null,
|
|
65
84
|
};
|
|
66
85
|
icons.set(iconKey, record);
|
|
@@ -69,20 +88,28 @@ export function createIconManager(layerManager, port) {
|
|
|
69
88
|
return record;
|
|
70
89
|
}
|
|
71
90
|
function recreateIcon(iconKey, name, record, cfg) {
|
|
72
|
-
const
|
|
73
|
-
const oldChildren = oldLayer.children;
|
|
91
|
+
const oldChildren = record.layer.children;
|
|
74
92
|
const idx = oldChildren.indexOf(record.imageDef);
|
|
93
|
+
// The heading the icon holds, which the replacement inherits when the caller names none: a
|
|
94
|
+
// cardinal-snap icon carries whole quarters the camera gave it, and a new canvas is no reason
|
|
95
|
+
// to lose them.
|
|
96
|
+
const rotation = cfg.rotation ?? record.imageDef.bounds.rotation;
|
|
97
|
+
// Hidden as well as removed. The renderer commits a def write synchronously and defers the
|
|
98
|
+
// membership sweep, so a def only spliced out stays drawn — the old canvas, at the old place —
|
|
99
|
+
// until that sweep lands.
|
|
100
|
+
record.imageDef.hidden = true;
|
|
101
|
+
layerManager.touchDef(record.imageDef);
|
|
75
102
|
if (idx >= 0)
|
|
76
103
|
oldChildren.splice(idx, 1);
|
|
77
|
-
layerManager.
|
|
104
|
+
layerManager.touchLayer(record.layer);
|
|
78
105
|
icons.delete(iconKey);
|
|
79
106
|
iconsByDef.delete(record.imageDef);
|
|
80
107
|
untrackKey(iconKey);
|
|
81
|
-
createIcon(iconKey, name, cfg);
|
|
108
|
+
createIcon(iconKey, name, cfg, rotation);
|
|
82
109
|
}
|
|
83
110
|
function updateIcon(iconKey, name, record, cfg) {
|
|
84
|
-
if (record.
|
|
85
|
-
throw new Error(`renderer: icon "${iconKey}" cannot move between layers (was
|
|
111
|
+
if (record.layer !== layerFor(cfg)) {
|
|
112
|
+
throw new Error(`renderer: icon "${iconKey}" cannot move between layers (was onTop=${!!(record.layer === layers.currentPosition)}, got onTop=${!!cfg.onTop})`);
|
|
86
113
|
}
|
|
87
114
|
if (record.imageDef.source !== cfg.canvas) {
|
|
88
115
|
recreateIcon(iconKey, name, record, cfg);
|
|
@@ -140,6 +167,15 @@ export function createIconManager(layerManager, port) {
|
|
|
140
167
|
hideIcon(record);
|
|
141
168
|
},
|
|
142
169
|
destroy() {
|
|
170
|
+
// The layers belong to the host and outlive this manager, so what was put in them comes back
|
|
171
|
+
// out: a handle that keeps a torn-down instance's icons is not reusable.
|
|
172
|
+
for (const record of icons.values()) {
|
|
173
|
+
const children = record.layer.children;
|
|
174
|
+
const idx = children.indexOf(record.imageDef);
|
|
175
|
+
if (idx >= 0)
|
|
176
|
+
children.splice(idx, 1);
|
|
177
|
+
layerManager.touchLayer(record.layer);
|
|
178
|
+
}
|
|
143
179
|
icons.clear();
|
|
144
180
|
iconsByDef.clear();
|
|
145
181
|
iconKeysByName.clear();
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { createWayfindingRenderer } from './createWayfindingRenderer.js';
|
|
2
|
+
export type { WayfindingLayers } from './layers.js';
|
|
3
|
+
export { initWayfindingLayers, resolveWayfindingLayers } from './layers.js';
|
|
2
4
|
export type { IconConfig, RendererPort, WayfindingRenderer } from './types.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/renderer/index.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { LayerDef, RenderableDef } from '@expofp/renderer';
|
|
2
2
|
import type { RendererPort } from './types.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Tracks what changed in the wayfinding layers and commits it.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* through the injected `update` in a single call.
|
|
6
|
+
* `touchLayer` marks a layer's **membership** as dirty and `touchDef` marks one def's **fields**;
|
|
7
|
+
* `flush` commits both through the injected `update` in a single call.
|
|
9
8
|
*
|
|
10
9
|
* The distinction is not cosmetic. A renderer reconciles a layer's children but writes a def's slot,
|
|
11
10
|
* so a def whose `hidden` or `bounds` moved is only drawn correctly if the def itself is committed —
|
|
@@ -13,12 +12,11 @@ import type { RendererPort } from './types.js';
|
|
|
13
12
|
* instance not at all.
|
|
14
13
|
*/
|
|
15
14
|
export interface LayerManager {
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
touchLayer(name: string): LayerDef;
|
|
15
|
+
/**
|
|
16
|
+
* Mark a layer's membership as dirty for the next {@link flush}.
|
|
17
|
+
* @param layer - The layer whose children changed.
|
|
18
|
+
*/
|
|
19
|
+
touchLayer(layer: LayerDef): void;
|
|
22
20
|
/**
|
|
23
21
|
* Mark one def's fields as dirty for the next {@link flush}.
|
|
24
22
|
* @param def - The def whose fields were mutated.
|
|
@@ -26,12 +24,12 @@ export interface LayerManager {
|
|
|
26
24
|
touchDef(def: RenderableDef): void;
|
|
27
25
|
/** Commit everything dirty and clear the dirty sets. */
|
|
28
26
|
flush(): void;
|
|
29
|
-
/** Clear
|
|
27
|
+
/** Clear the dirty sets. */
|
|
30
28
|
destroy(): void;
|
|
31
29
|
}
|
|
32
30
|
/**
|
|
33
|
-
* Create a {@link LayerManager}
|
|
34
|
-
* @param port - The
|
|
31
|
+
* Create a {@link LayerManager}.
|
|
32
|
+
* @param port - The commit function; see {@link RendererPort}.
|
|
35
33
|
* @returns The manager.
|
|
36
34
|
*/
|
|
37
35
|
export declare function createLayerManager(port: RendererPort): LayerManager;
|
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Create a {@link LayerManager}
|
|
3
|
-
* @param port - The
|
|
2
|
+
* Create a {@link LayerManager}.
|
|
3
|
+
* @param port - The commit function; see {@link RendererPort}.
|
|
4
4
|
* @returns The manager.
|
|
5
5
|
*/
|
|
6
6
|
export function createLayerManager(port) {
|
|
7
|
-
const
|
|
8
|
-
const layerCache = new Map();
|
|
9
|
-
const touchedLayers = new Map();
|
|
7
|
+
const touchedLayers = new Set();
|
|
10
8
|
const touchedDefs = new Set();
|
|
11
9
|
return {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const cached = layerCache.get(name);
|
|
15
|
-
if (cached)
|
|
16
|
-
return cached;
|
|
17
|
-
const layer = allLayers.find((candidate) => candidate.name === name);
|
|
18
|
-
if (!layer)
|
|
19
|
-
throw new Error(`renderer: layer "${name}" not found in scene`);
|
|
20
|
-
layerCache.set(name, layer);
|
|
21
|
-
return layer;
|
|
22
|
-
},
|
|
23
|
-
touchLayer(name) {
|
|
24
|
-
const layer = this.resolveLayer(name);
|
|
25
|
-
touchedLayers.set(name, layer);
|
|
26
|
-
return layer;
|
|
10
|
+
touchLayer(layer) {
|
|
11
|
+
touchedLayers.add(layer);
|
|
27
12
|
},
|
|
28
13
|
touchDef(def) {
|
|
29
14
|
touchedDefs.add(def);
|
|
@@ -31,17 +16,16 @@ export function createLayerManager(port) {
|
|
|
31
16
|
flush() {
|
|
32
17
|
if (touchedLayers.size === 0 && touchedDefs.size === 0)
|
|
33
18
|
return;
|
|
34
|
-
for (const layer of touchedLayers
|
|
19
|
+
for (const layer of touchedLayers) {
|
|
35
20
|
layer.children = [...layer.children];
|
|
36
21
|
}
|
|
37
|
-
port.update(...touchedLayers
|
|
22
|
+
port.update(...touchedLayers, ...touchedDefs);
|
|
38
23
|
touchedLayers.clear();
|
|
39
24
|
touchedDefs.clear();
|
|
40
25
|
},
|
|
41
26
|
destroy() {
|
|
42
27
|
touchedLayers.clear();
|
|
43
28
|
touchedDefs.clear();
|
|
44
|
-
layerCache.clear();
|
|
45
29
|
},
|
|
46
30
|
};
|
|
47
31
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ImageDef, LayerDef, LineDef } from '@expofp/renderer';
|
|
2
|
+
/**
|
|
3
|
+
* The layers wayfinding fills, resolved out of the block the host mounted.
|
|
4
|
+
*
|
|
5
|
+
* One layer per def kind is not a choice: `RenderableDefCollection` admits no mixed children, so
|
|
6
|
+
* images and lines cannot share one. Which layers exist and how they stack among themselves is this
|
|
7
|
+
* package's business; where the block sits in a scene is the host's.
|
|
8
|
+
*/
|
|
9
|
+
export interface WayfindingLayers {
|
|
10
|
+
/** Dot trail between an off-graph anchor and the route. */
|
|
11
|
+
readonly trail: LayerDef<ImageDef>;
|
|
12
|
+
/** Static route lines — passed and remaining. */
|
|
13
|
+
readonly lines: LayerDef<LineDef>;
|
|
14
|
+
/** The chase animation over the remaining route. */
|
|
15
|
+
readonly linesAnimated: LayerDef<LineDef>;
|
|
16
|
+
/** Endpoint, transition and host icons. The one layer worth making pickable. */
|
|
17
|
+
readonly icons: LayerDef<ImageDef>;
|
|
18
|
+
/** The live position, above the icons so an endpoint pin never covers it. */
|
|
19
|
+
readonly currentPosition: LayerDef<ImageDef>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates the wayfinding block, empty.
|
|
23
|
+
*
|
|
24
|
+
* Mount it wherever wayfinding belongs in the scene — the route has to draw over the pathway layers
|
|
25
|
+
* (efp #1206). `createWayfinding` finds it there and fills it.
|
|
26
|
+
* @returns The block to mount.
|
|
27
|
+
*/
|
|
28
|
+
export declare function initWayfindingLayers(): LayerDef<LayerDef>;
|
|
29
|
+
/**
|
|
30
|
+
* Finds the mounted block in a scene and hands back the layers inside it, so a host that mounted
|
|
31
|
+
* the block needs to carry nothing but its scene.
|
|
32
|
+
*
|
|
33
|
+
* The block sits directly under the scene's root layer, which is where the host mounted it.
|
|
34
|
+
* @param sceneRoot - The scene's root layer.
|
|
35
|
+
* @returns The layers to fill.
|
|
36
|
+
* @throws If the scene holds no wayfinding block — it was never mounted, or this is another scene.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveWayfindingLayers(sceneRoot: LayerDef): WayfindingLayers;
|
|
39
|
+
//# sourceMappingURL=layers.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The block's own layer names. Private: after this module nothing addresses a wayfinding layer by
|
|
2
|
+
// name — the host mounts the block, and the renderer holds the references.
|
|
3
|
+
const BLOCK_LAYER_NAME = 'wayfinding';
|
|
4
|
+
const TRAIL_LAYER_NAME = 'wf-trail-points';
|
|
5
|
+
const LINES_LAYER_NAME = 'wf-lines';
|
|
6
|
+
const LINES_ANIMATED_LAYER_NAME = 'wf-lines-animated';
|
|
7
|
+
const ICONS_LAYER_NAME = 'wf-points';
|
|
8
|
+
const CURRENT_POSITION_LAYER_NAME = 'wf-current-position';
|
|
9
|
+
/**
|
|
10
|
+
* Creates the wayfinding block, empty.
|
|
11
|
+
*
|
|
12
|
+
* Mount it wherever wayfinding belongs in the scene — the route has to draw over the pathway layers
|
|
13
|
+
* (efp #1206). `createWayfinding` finds it there and fills it.
|
|
14
|
+
* @returns The block to mount.
|
|
15
|
+
*/
|
|
16
|
+
export function initWayfindingLayers() {
|
|
17
|
+
const trail = { name: TRAIL_LAYER_NAME, children: [] };
|
|
18
|
+
const lines = { name: LINES_LAYER_NAME, children: [] };
|
|
19
|
+
const linesAnimated = { name: LINES_ANIMATED_LAYER_NAME, children: [] };
|
|
20
|
+
const icons = { name: ICONS_LAYER_NAME, children: [] };
|
|
21
|
+
const currentPosition = {
|
|
22
|
+
name: CURRENT_POSITION_LAYER_NAME,
|
|
23
|
+
children: [],
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
name: BLOCK_LAYER_NAME,
|
|
27
|
+
// Bottom → top, and the only place the block's order is stated.
|
|
28
|
+
children: [trail, lines, linesAnimated, icons, currentPosition],
|
|
29
|
+
// Once for the block, which the children inherit: a search dims the venue behind the route,
|
|
30
|
+
// never the route.
|
|
31
|
+
dim: 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Finds the mounted block in a scene and hands back the layers inside it, so a host that mounted
|
|
36
|
+
* the block needs to carry nothing but its scene.
|
|
37
|
+
*
|
|
38
|
+
* The block sits directly under the scene's root layer, which is where the host mounted it.
|
|
39
|
+
* @param sceneRoot - The scene's root layer.
|
|
40
|
+
* @returns The layers to fill.
|
|
41
|
+
* @throws If the scene holds no wayfinding block — it was never mounted, or this is another scene.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveWayfindingLayers(sceneRoot) {
|
|
44
|
+
const children = sceneRoot.children;
|
|
45
|
+
const root = children.find((child) => 'name' in child && child.name === BLOCK_LAYER_NAME && 'children' in child);
|
|
46
|
+
if (!root)
|
|
47
|
+
throw new Error('The scene holds no wayfinding block — mount `root` first.');
|
|
48
|
+
const byName = new Map(root.children.map((layer) => [layer.name, layer]));
|
|
49
|
+
function layerOf(name) {
|
|
50
|
+
const layer = byName.get(name);
|
|
51
|
+
if (!layer)
|
|
52
|
+
throw new Error(`The wayfinding block is missing its \`${name}\` layer.`);
|
|
53
|
+
return layer;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
trail: layerOf(TRAIL_LAYER_NAME),
|
|
57
|
+
lines: layerOf(LINES_LAYER_NAME),
|
|
58
|
+
linesAnimated: layerOf(LINES_ANIMATED_LAYER_NAME),
|
|
59
|
+
icons: layerOf(ICONS_LAYER_NAME),
|
|
60
|
+
currentPosition: layerOf(CURRENT_POSITION_LAYER_NAME),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { LayerManager } from './layerManager.js';
|
|
2
|
+
import type { WayfindingLayers } from './layers.js';
|
|
2
3
|
import type { RendererPort, RouteColors } from './types.js';
|
|
3
4
|
/**
|
|
4
5
|
* Manages static route lines (passed + remaining) and an animated overlay
|
|
@@ -23,9 +24,7 @@ export interface RouteLineManager {
|
|
|
23
24
|
x: number;
|
|
24
25
|
y: number;
|
|
25
26
|
};
|
|
26
|
-
}[], options
|
|
27
|
-
readonly linesLayer: string;
|
|
28
|
-
readonly animatedLinesLayer: string;
|
|
27
|
+
}[], options?: {
|
|
29
28
|
readonly resetAnimation?: boolean;
|
|
30
29
|
}): void;
|
|
31
30
|
/** Stop animation, wipe both lines layers. */
|
|
@@ -36,10 +35,11 @@ export interface RouteLineManager {
|
|
|
36
35
|
/**
|
|
37
36
|
* Create a {@link RouteLineManager} that renders `LineDef` segments
|
|
38
37
|
* into layers managed by the given {@link LayerManager}.
|
|
39
|
-
* @param layerManager -
|
|
38
|
+
* @param layerManager - Tracks what changed.
|
|
40
39
|
* @param colors - The route palette.
|
|
41
40
|
* @param port - The commit function the animation ticks through.
|
|
41
|
+
* @param layers - The two line layers to draw into.
|
|
42
42
|
* @returns The manager.
|
|
43
43
|
*/
|
|
44
|
-
export declare function createRouteLineManager(layerManager: LayerManager, colors: RouteColors, port: RendererPort): RouteLineManager;
|
|
44
|
+
export declare function createRouteLineManager(layerManager: LayerManager, colors: RouteColors, port: RendererPort, layers: Pick<WayfindingLayers, 'lines' | 'linesAnimated'>): RouteLineManager;
|
|
45
45
|
//# sourceMappingURL=routeLineManager.d.ts.map
|