mudlet-map-renderer 2.5.1 → 2.6.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 +68 -0
- package/dist/Area-MLM4Xe0E.js +157 -0
- package/dist/Area-MLM4Xe0E.js.map +1 -0
- package/dist/MapReader-BeVNpm6y.js +77 -0
- package/dist/MapReader-BeVNpm6y.js.map +1 -0
- package/dist/SkeletonMapReader-ZPwsqemZ.js +351 -0
- package/dist/SkeletonMapReader-ZPwsqemZ.js.map +1 -0
- package/dist/bigmap/PlaneIndex.d.ts +42 -0
- package/dist/bigmap/Skeleton.d.ts +60 -0
- package/dist/bigmap/SkeletonMapReader.d.ts +65 -0
- package/dist/bigmap/buildSkeleton.d.ts +23 -0
- package/dist/bigmap/index.d.ts +24 -0
- package/dist/bigmap.mjs +61 -0
- package/dist/bigmap.mjs.map +1 -0
- package/dist/binary/convert.d.ts +19 -0
- package/dist/binary/index.d.ts +2 -0
- package/dist/binary/loadMudletMap.d.ts +62 -0
- package/dist/binary.mjs +195 -8
- package/dist/binary.mjs.map +1 -1
- package/dist/export/exportViewport.d.ts +25 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.mjs +978 -601
- package/dist/index.mjs.map +1 -1
- package/dist/reader/Area.d.ts +8 -2
- package/dist/reader/HashLookup.d.ts +17 -0
- package/dist/reader/ViewportDataSource.d.ts +33 -0
- package/dist/render/CullIndex.d.ts +54 -0
- package/dist/rendering/KonvaRenderBackend.d.ts +68 -0
- package/dist/rendering/SceneManager.d.ts +28 -5
- package/dist/rendering/lod/LodController.d.ts +45 -0
- package/dist/rendering/lod/RasterOverview.d.ts +46 -0
- package/dist/rendering/lod/lodDecision.d.ts +45 -0
- package/dist/rendering/lod/roomsOnlyArea.d.ts +16 -0
- package/dist/types/Settings.d.ts +63 -0
- package/package.json +9 -4
- package/dist/MapReader-Brzg-X7T.js +0 -224
- package/dist/MapReader-Brzg-X7T.js.map +0 -1
package/dist/reader/Area.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { default as Plane, IPlane } from './Plane';
|
|
2
2
|
import { default as IExit } from './Exit';
|
|
3
|
+
/**
|
|
4
|
+
* Pair each room's half-exits into bidirectional {@link IExit}s (or one-way
|
|
5
|
+
* fallbacks when a room's neighbour doesn't exit back). Exposed standalone so
|
|
6
|
+
* callers with an already-filtered room list (e.g. a viewport-narrowed set)
|
|
7
|
+
* can pair exits directly, without constructing a full {@link Area}.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pairLinkExits(rooms: MapData.Room[]): Map<string, IExit>;
|
|
3
10
|
/**
|
|
4
11
|
* Public, renderer-facing surface of an area. The renderer never inspects
|
|
5
12
|
* private state of {@link Area}; it talks to this interface only. Custom data
|
|
@@ -39,7 +46,7 @@ export interface IArea {
|
|
|
39
46
|
export default class Area implements IArea {
|
|
40
47
|
private readonly planes;
|
|
41
48
|
private readonly area;
|
|
42
|
-
private
|
|
49
|
+
private exits;
|
|
43
50
|
private version;
|
|
44
51
|
constructor(area: MapData.Area);
|
|
45
52
|
getAreaName(): string;
|
|
@@ -58,6 +65,5 @@ export default class Area implements IArea {
|
|
|
58
65
|
};
|
|
59
66
|
getLinkExits(zIndex: number): IExit[];
|
|
60
67
|
private createPlanes;
|
|
61
|
-
private static readonly oppositeDir;
|
|
62
68
|
private createExits;
|
|
63
69
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional capability interface for readers that can resolve a Mudlet room
|
|
3
|
+
* content hash (`MapData.Room.hash`) directly to a room id.
|
|
4
|
+
*
|
|
5
|
+
* `SkeletonMapReader.getRooms()` deliberately always returns `[]` (see
|
|
6
|
+
* {@link ViewportDataSource} — never materialise the full room list), so a
|
|
7
|
+
* linear `getRooms().find(r => r.hash === hash)` scan — the only option
|
|
8
|
+
* `IMapReader` supports on its own — can never find anything on a big/
|
|
9
|
+
* streamed map. This interface gives such readers a way to opt into an O(1)
|
|
10
|
+
* (or otherwise scan-free) hash lookup instead.
|
|
11
|
+
*/
|
|
12
|
+
export interface HashLookupCapable {
|
|
13
|
+
readonly hashLookupCapable: true;
|
|
14
|
+
/** Resolve a room's content hash to its id, or `undefined` if unknown. */
|
|
15
|
+
getRoomIdByHash(hash: string): number | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function isHashLookupCapable(reader: unknown): reader is HashLookupCapable;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ViewportBounds } from '../types/Settings';
|
|
2
|
+
/**
|
|
3
|
+
* Optional capability interface for readers that materialise rooms
|
|
4
|
+
* per-viewport instead of holding a full object graph (see
|
|
5
|
+
* `SkeletonMapReader` in `src/bigmap/`).
|
|
6
|
+
*
|
|
7
|
+
* The interactive backend detects this capability (via
|
|
8
|
+
* {@link isViewportDataSource}) and, when present:
|
|
9
|
+
* - pushes padded viewport bounds into the reader before every scene build,
|
|
10
|
+
* rebuilding the scene when the camera leaves the previously applied
|
|
11
|
+
* window (rebuild-on-pan), and
|
|
12
|
+
* - drives the raster LOD overview through {@link forEachInBounds}, which
|
|
13
|
+
* visits raw room positions without materialising `MapData.Room` objects.
|
|
14
|
+
*
|
|
15
|
+
* All bounds are renderer space (the space `getViewportBounds()` reports).
|
|
16
|
+
*/
|
|
17
|
+
export interface ViewportDataSource {
|
|
18
|
+
readonly viewportAware: true;
|
|
19
|
+
/**
|
|
20
|
+
* Set the window that subsequent `getPlane().getRooms()` /
|
|
21
|
+
* `getLinkExits()` calls materialise. Implementations must bump their
|
|
22
|
+
* areas' `getVersion()` when the bounds actually change.
|
|
23
|
+
*/
|
|
24
|
+
setViewport(bounds: ViewportBounds): void;
|
|
25
|
+
getViewport(): ViewportBounds;
|
|
26
|
+
/** Total rooms on the (area, z) plane — input to the LOD mode decision. */
|
|
27
|
+
getPlaneRoomCount(areaId: number, z: number): number;
|
|
28
|
+
/** Cheap upper bound of the rooms inside `bounds` on that plane. */
|
|
29
|
+
estimateVisibleCount(areaId: number, z: number, bounds: ViewportBounds): number;
|
|
30
|
+
/** Visit every room inside `bounds` (renderer-space x/y + env id). */
|
|
31
|
+
forEachInBounds(areaId: number, z: number, bounds: ViewportBounds, fn: (x: number, y: number, envId: number) => void): void;
|
|
32
|
+
}
|
|
33
|
+
export declare function isViewportDataSource(reader: unknown): reader is ViewportDataSource;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Shape } from '../scene/Shape';
|
|
2
|
+
import { ViewportBounds } from '../types/Settings';
|
|
3
|
+
import { CoordFn } from '../coord/CoordFn';
|
|
4
|
+
/** One indexed shape with its scene-space axis-aligned bounding box. */
|
|
5
|
+
export interface CullIndexEntry {
|
|
6
|
+
shape: Shape;
|
|
7
|
+
minX: number;
|
|
8
|
+
minY: number;
|
|
9
|
+
maxX: number;
|
|
10
|
+
maxY: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class CullIndex {
|
|
13
|
+
private entries;
|
|
14
|
+
private allShapes;
|
|
15
|
+
private linear;
|
|
16
|
+
private originX;
|
|
17
|
+
private originY;
|
|
18
|
+
private cellSize;
|
|
19
|
+
private cols;
|
|
20
|
+
private rows;
|
|
21
|
+
/** Per-cell lists of entry indices (row-major, length cols*rows). */
|
|
22
|
+
private cells;
|
|
23
|
+
/** Indices of entries too large to grid — always tested. */
|
|
24
|
+
private oversized;
|
|
25
|
+
/** Per-query dedup stamps so a multi-cell entry is tested at most once. */
|
|
26
|
+
private stamp;
|
|
27
|
+
private queryGen;
|
|
28
|
+
/**
|
|
29
|
+
* (Re)build the index from scene-space entries. `entries` is retained by
|
|
30
|
+
* reference; callers should not mutate it afterwards.
|
|
31
|
+
*/
|
|
32
|
+
build(entries: CullIndexEntry[]): void;
|
|
33
|
+
/** Shapes known to this index (i.e. cullable). Order is build order. */
|
|
34
|
+
getAllShapes(): ReadonlySet<Shape>;
|
|
35
|
+
/**
|
|
36
|
+
* Return the set of shapes whose AABB intersects `viewport`. Inclusive on
|
|
37
|
+
* every edge, matching `buildCullingVisibilityMap`.
|
|
38
|
+
*/
|
|
39
|
+
queryVisible(viewport: ViewportBounds): Set<Shape>;
|
|
40
|
+
private clampCol;
|
|
41
|
+
private clampRow;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Project a world-space AABB through `transform` into a scene-space AABB by
|
|
45
|
+
* transforming its four corners and taking the min/max. For the identity
|
|
46
|
+
* transform the box is returned unchanged. Mirrors the `transformedBbox`
|
|
47
|
+
* helper in `clipSceneToViewport` so index bounds match the cull predicate.
|
|
48
|
+
*/
|
|
49
|
+
export declare function projectBounds(minX: number, minY: number, maxX: number, maxY: number, transform: CoordFn): {
|
|
50
|
+
minX: number;
|
|
51
|
+
minY: number;
|
|
52
|
+
maxX: number;
|
|
53
|
+
maxY: number;
|
|
54
|
+
};
|
|
@@ -25,6 +25,8 @@ import { Style } from '../style/Style';
|
|
|
25
25
|
*/
|
|
26
26
|
export declare class KonvaRenderBackend implements InteractiveBackend {
|
|
27
27
|
readonly stage: Konva.Stage;
|
|
28
|
+
/** Bottom-most layer: raster LOD overview underlay (see Settings.lodEnabled). */
|
|
29
|
+
readonly lodLayer: Konva.Layer;
|
|
28
30
|
readonly gridLayer: Konva.Layer;
|
|
29
31
|
readonly linkLayer: Konva.Layer;
|
|
30
32
|
readonly roomLayer: Konva.Layer;
|
|
@@ -47,6 +49,14 @@ export declare class KonvaRenderBackend implements InteractiveBackend {
|
|
|
47
49
|
readonly hitTester: HitTester;
|
|
48
50
|
private lastHitShapes;
|
|
49
51
|
private shapeToDrawEntry;
|
|
52
|
+
/**
|
|
53
|
+
* Managed shapes visible after the last cull pass. applyClipping diffs the
|
|
54
|
+
* fresh visible set against this and flips only the entries that changed,
|
|
55
|
+
* so a pan touches O(shapes entering/leaving the viewport), not O(all).
|
|
56
|
+
* Seeded with every managed shape on rebuild (all entries start visible) so
|
|
57
|
+
* the first pass hides the off-screen ones.
|
|
58
|
+
*/
|
|
59
|
+
private lastVisibleShapes;
|
|
50
60
|
private sceneNode;
|
|
51
61
|
/** Lookup from a pipeline-emitted shape to its recording node; rebuilt per buildScene. */
|
|
52
62
|
private shapeToGroup;
|
|
@@ -73,6 +83,14 @@ export declare class KonvaRenderBackend implements InteractiveBackend {
|
|
|
73
83
|
private sceneOverlays;
|
|
74
84
|
private sceneOverlayNodes;
|
|
75
85
|
private viewportSubscribers;
|
|
86
|
+
private readonly lodController;
|
|
87
|
+
/** Current LOD tier for the displayed plane — see {@link computeLodMode}. */
|
|
88
|
+
private lodMode;
|
|
89
|
+
/** Padded bounds last pushed into a ViewportDataSource reader (rebuild-on-pan hysteresis). */
|
|
90
|
+
private lastAppliedViewport;
|
|
91
|
+
/** Camera scale at the last applied viewport — see {@link onCameraViewportChanged}. */
|
|
92
|
+
private lastAppliedScale;
|
|
93
|
+
private refreshScheduled;
|
|
76
94
|
constructor(state: MapState, container?: HTMLDivElement);
|
|
77
95
|
setStyle(style: Style): void;
|
|
78
96
|
/** Pull forward/inverse coord transforms from the active shape Style. */
|
|
@@ -89,7 +107,57 @@ export declare class KonvaRenderBackend implements InteractiveBackend {
|
|
|
89
107
|
pixelRatio?: number;
|
|
90
108
|
}): ExportCanvas | undefined;
|
|
91
109
|
private applyViewportToStage;
|
|
110
|
+
/**
|
|
111
|
+
* Camera-move follow-up for LOD and viewport-aware readers. A scene
|
|
112
|
+
* rebuild (RAF-coalesced) is scheduled when:
|
|
113
|
+
* - the LOD mode decision flipped (zoom crossed the raster threshold), or
|
|
114
|
+
* - a {@link ViewportDataSource} reader's camera left the padded viewport
|
|
115
|
+
* applied on the last build (rebuild-on-pan with hysteresis — panning
|
|
116
|
+
* inside the padding is covered by the existing culling), or
|
|
117
|
+
* - the zoom has drifted materially (±20%) from the scale the padded
|
|
118
|
+
* viewport was computed at. The generous (50%-per-side) padding means a
|
|
119
|
+
* pure zoom-IN can leave the camera viewport spatially inside the old
|
|
120
|
+
* padded region indefinitely — without this check the reader would
|
|
121
|
+
* keep reporting the stale, much-too-wide room count (visibleEstimate,
|
|
122
|
+
* the vector/hit-test budget decisions) forever while zooming in,
|
|
123
|
+
* instead of narrowing to reflect what's actually worth materialising
|
|
124
|
+
* at the new zoom.
|
|
125
|
+
* In raster mode the underlay repaints itself independently (also
|
|
126
|
+
* coalesced, and only when the painted region no longer covers the view).
|
|
127
|
+
*/
|
|
128
|
+
private onCameraViewportChanged;
|
|
129
|
+
private scheduleRefresh;
|
|
92
130
|
refresh(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Viewport clamping and LOD only engage once the camera has a real size.
|
|
133
|
+
* Container-backed stages always do; a headless backend starts at 1×1
|
|
134
|
+
* (a meaningless viewport that must not clamp exports) until the caller
|
|
135
|
+
* opts in via `camera.setSize(w, h)`.
|
|
136
|
+
*/
|
|
137
|
+
private hasRealViewport;
|
|
138
|
+
/**
|
|
139
|
+
* Pad viewport bounds by 6% + 1 map unit per side: rooms are selected by
|
|
140
|
+
* centre, so a room just off-screen can still have half its body in view —
|
|
141
|
+
* the margin keeps edge rooms from popping and lets the regular culling
|
|
142
|
+
* reveal margin rooms between rebuilds (rebuild-on-pan hysteresis).
|
|
143
|
+
*/
|
|
144
|
+
private padViewport;
|
|
145
|
+
private planeRoomCount;
|
|
146
|
+
private computeLodMode;
|
|
147
|
+
/** Room source for the raster painter (never materialises rooms for viewport-aware readers). */
|
|
148
|
+
private rasterVisit;
|
|
149
|
+
/** Tear down the vector scene (raster mode replaces it entirely). */
|
|
150
|
+
private clearVectorScene;
|
|
151
|
+
/**
|
|
152
|
+
* Above `settings.lodHitTestBudget` rooms in the current plane build, skip
|
|
153
|
+
* rebuilding the hit index instead of paying for it on every rebuild — the
|
|
154
|
+
* vector scene still renders at full detail, but clicks/hover stop
|
|
155
|
+
* resolving to a room until the density drops back under the budget.
|
|
156
|
+
* Counted in rooms (same unit as `lodRoomBudget`), not raw hittable-shape
|
|
157
|
+
* count, so the two settings are directly comparable.
|
|
158
|
+
*/
|
|
159
|
+
private hitTestBudgetExceeded;
|
|
160
|
+
private emitLodEvent;
|
|
93
161
|
addLiveEffect(id: string, effect: LiveEffect): void;
|
|
94
162
|
removeLiveEffect(id: string): void;
|
|
95
163
|
addSceneOverlay(id: string, overlay: SceneOverlay): void;
|
|
@@ -32,6 +32,9 @@ export declare class SceneManager {
|
|
|
32
32
|
private pipeline;
|
|
33
33
|
private lastBuildResult?;
|
|
34
34
|
private standaloneExitShapeSet;
|
|
35
|
+
/** Spatial index over the current scene, built lazily and memoised per transform. */
|
|
36
|
+
private cullIndex?;
|
|
37
|
+
private cullIndexTransform?;
|
|
35
38
|
constructor(camera: Camera, settings: Settings, mapReader: IMapReader);
|
|
36
39
|
get exitRenderer(): import('../ExitRenderer').default;
|
|
37
40
|
get lastResult(): SceneBuildResult | undefined;
|
|
@@ -45,11 +48,31 @@ export declare class SceneManager {
|
|
|
45
48
|
reset(): void;
|
|
46
49
|
resetPipeline(mapReader: IMapReader): void;
|
|
47
50
|
/**
|
|
48
|
-
* Lightweight cull for the interactive render path.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* the
|
|
51
|
+
* Lightweight cull for the interactive render path. Returns the set of
|
|
52
|
+
* currently-visible managed shapes (rooms, exits, labels, stubs…) by
|
|
53
|
+
* querying the spatial {@link CullIndex} — O(visible cells), not O(all
|
|
54
|
+
* shapes). Shapes outside this set that are *known to the index* should be
|
|
55
|
+
* hidden; shapes the index has never heard of are unmanaged pass-throughs
|
|
56
|
+
* (always visible) and the caller must leave them alone.
|
|
57
|
+
*
|
|
58
|
+
* When culling is disabled the full managed-shape set is returned, so the
|
|
59
|
+
* caller's delta logic naturally reveals everything.
|
|
52
60
|
*/
|
|
53
|
-
cullInteractive(coordinateTransform?: CoordFn):
|
|
61
|
+
cullInteractive(coordinateTransform?: CoordFn): Set<Shape>;
|
|
62
|
+
/**
|
|
63
|
+
* Every shape the cull index can hide. Callers seed their "previously
|
|
64
|
+
* visible" set with this after a rebuild (all entries start visible) so the
|
|
65
|
+
* first cull pass hides the off-screen ones.
|
|
66
|
+
*/
|
|
67
|
+
managedShapes(coordinateTransform?: CoordFn): Set<Shape>;
|
|
68
|
+
/** Build (or reuse) the spatial index for the current scene + transform. */
|
|
69
|
+
private ensureCullIndex;
|
|
70
|
+
/**
|
|
71
|
+
* Project every cullable shape's world-space bounds into scene space and
|
|
72
|
+
* collect them as index entries. Mirrors the bounds used by
|
|
73
|
+
* `buildCullingVisibilityMap` exactly so the index and the export-path
|
|
74
|
+
* predicate agree on which shapes a viewport contains.
|
|
75
|
+
*/
|
|
76
|
+
private buildCullEntries;
|
|
54
77
|
cull(coordinateTransform?: CoordFn): CullOutput;
|
|
55
78
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { default as Konva } from 'konva';
|
|
2
|
+
import { Camera } from '../../camera/Camera';
|
|
3
|
+
import { ViewportBounds } from '../../types/Settings';
|
|
4
|
+
/** Visit every room (renderer-space x/y + env id) inside `bounds`. */
|
|
5
|
+
export type RasterVisit = (bounds: ViewportBounds, fn: (x: number, y: number, envId: number) => void) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Owns the raster LOD underlay: an offscreen canvas painted with the room
|
|
8
|
+
* overview, shown through a single map-space `Konva.Image` on the (bottom)
|
|
9
|
+
* LOD layer. Because the image is positioned in map coordinates, the stage
|
|
10
|
+
* transform pans and zooms it like any other node — panning inside the
|
|
11
|
+
* painted region costs nothing, and a repaint is only needed when the camera
|
|
12
|
+
* escapes the region or the zoom changes (RAF-coalesced; the stretched image
|
|
13
|
+
* covers the frames in between).
|
|
14
|
+
*
|
|
15
|
+
* Note: the raster paints raw renderer-space coordinates — decorator styles
|
|
16
|
+
* with a `worldToScene` transform (e.g. isometric) are not applied to it.
|
|
17
|
+
*/
|
|
18
|
+
export declare class LodController {
|
|
19
|
+
private readonly layer;
|
|
20
|
+
private readonly camera;
|
|
21
|
+
private readonly colorCss;
|
|
22
|
+
private readonly roomSizeOf;
|
|
23
|
+
private readonly canvas;
|
|
24
|
+
private image?;
|
|
25
|
+
private visit;
|
|
26
|
+
private paintedRegion;
|
|
27
|
+
private paintedScale;
|
|
28
|
+
private repaintScheduled;
|
|
29
|
+
private destroyed;
|
|
30
|
+
private readonly packedCache;
|
|
31
|
+
constructor(layer: Konva.Layer, camera: Camera, colorCss: (envId: number) => string, roomSizeOf: () => number);
|
|
32
|
+
/** Activate the raster overview over `visit` and paint immediately. */
|
|
33
|
+
setSource(visit: RasterVisit): void;
|
|
34
|
+
/** Deactivate and hide the overview (vector mode). */
|
|
35
|
+
clear(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Camera moved: repaint (coalesced) if the viewport left the painted
|
|
38
|
+
* region or the zoom changed; otherwise the stage transform already
|
|
39
|
+
* moved the image correctly and there is nothing to do.
|
|
40
|
+
*/
|
|
41
|
+
onViewportChange(): void;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
private colorOf;
|
|
44
|
+
private paint;
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure pixel painter for the raster LOD overview: each room becomes a small
|
|
3
|
+
* filled box in an `ImageData`, written directly as packed 32-bit pixels.
|
|
4
|
+
* Fast enough to repaint a million-room viewport in a few milliseconds —
|
|
5
|
+
* which is exactly the regime where Konva nodes are not an option.
|
|
6
|
+
*/
|
|
7
|
+
export interface RasterPaintParams {
|
|
8
|
+
/** Camera scale — screen = map * scale + offset. */
|
|
9
|
+
scale: number;
|
|
10
|
+
offsetX: number;
|
|
11
|
+
offsetY: number;
|
|
12
|
+
/** Packed little-endian RGBA (see {@link cssToPackedRGBA}) for an env id. */
|
|
13
|
+
colorOf: (envId: number) => number;
|
|
14
|
+
/**
|
|
15
|
+
* `settings.roomSize` at the time of painting — how large a room's own
|
|
16
|
+
* footprint is relative to the ~1-map-unit spacing between room centres.
|
|
17
|
+
* Only ever GROWS the box beyond the gap-free minimum (when > 1, rooms
|
|
18
|
+
* overlap their neighbours in vector mode too); values ≤ 1 leave the box
|
|
19
|
+
* at the gap-free minimum, since shrinking to match a smaller footprint
|
|
20
|
+
* would reintroduce visible gaps/erosion in the overview raster is meant
|
|
21
|
+
* to avoid. Omit (or 1) for the previous gap-free-only behaviour.
|
|
22
|
+
*/
|
|
23
|
+
roomSize?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Room box side in pixels for a given camera scale. Must always cover the
|
|
27
|
+
* inter-room spacing (= `scale` px, fractional): `round(scale)` under-covers
|
|
28
|
+
* at some zooms and the sub-pixel gaps read as a flickering grid, so
|
|
29
|
+
* `ceil(scale)+1` guarantees overlap → consistently solid regions. This is
|
|
30
|
+
* the FLOOR — `roomSize` above 1 (rooms configured larger than the spacing
|
|
31
|
+
* between them) grows the box further so the raster overview's density
|
|
32
|
+
* roughly matches how much vector mode's rooms overlap their neighbours;
|
|
33
|
+
* `roomSize` at or below 1 never shrinks the box below that floor.
|
|
34
|
+
*/
|
|
35
|
+
export declare function rasterBoxSize(scale: number, roomSize?: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* Paint every room `visit` yields into `img`. Pixels not covered by a room
|
|
38
|
+
* are left untouched (alpha 0 on a fresh ImageData), so the layer composites
|
|
39
|
+
* over the stage background — no background fill here.
|
|
40
|
+
*
|
|
41
|
+
* `visit` is typically `ViewportDataSource.forEachInBounds` curried with the
|
|
42
|
+
* painted region's map-space bounds.
|
|
43
|
+
*/
|
|
44
|
+
export declare function paintRasterOverview(img: ImageData, visit: (fn: (x: number, y: number, envId: number) => void) => void, p: RasterPaintParams): void;
|
|
45
|
+
/** `#rgb` / `#rrggbb` / `rgb(a)(r,g,b[,a])` → packed little-endian RGBA uint32 (alpha 255). */
|
|
46
|
+
export declare function cssToPackedRGBA(css: string): number;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface LodDecisionInput {
|
|
2
|
+
/** Total rooms on the whole (area, z) plane — NOT the viewport count. */
|
|
3
|
+
planeRoomCount: number;
|
|
4
|
+
/** Camera scale (screen pixels per map unit), `camera.getScale()`. */
|
|
5
|
+
scale: number;
|
|
6
|
+
stageWidth: number;
|
|
7
|
+
stageHeight: number;
|
|
8
|
+
/** Max rooms the vector (Konva) scene may hold, `settings.lodRoomBudget`. */
|
|
9
|
+
roomBudget: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Decide raster overview vs full vector detail.
|
|
13
|
+
*
|
|
14
|
+
* If the WHOLE plane fits the budget, always draw vectors — a small area
|
|
15
|
+
* never needs the raster overview. Genuinely large planes switch by ZOOM,
|
|
16
|
+
* not by in-view room count: count varies with local density and would flip
|
|
17
|
+
* modes mid-pan, while scale is constant during a pan. Bound: a fully-packed
|
|
18
|
+
* viewport holds ~stageW*stageH/scale² rooms (one per map unit), so vectors
|
|
19
|
+
* are safe once that worst case is within budget.
|
|
20
|
+
*/
|
|
21
|
+
export declare function shouldUseRaster(i: LodDecisionInput): boolean;
|
|
22
|
+
export type LodMode = "vector" | "roomsOnly" | "raster";
|
|
23
|
+
export interface LodModeInput extends Omit<LodDecisionInput, "roomBudget"> {
|
|
24
|
+
/** Max rooms the vector scene may hold before switching to raster. */
|
|
25
|
+
roomBudget: number;
|
|
26
|
+
/**
|
|
27
|
+
* Above this many rooms (and below `roomBudget`), exit lines are dropped
|
|
28
|
+
* but rooms still render as full vector shapes — a bridge tier between
|
|
29
|
+
* full vector and raster. Exit pairing + exit-shape building is typically
|
|
30
|
+
* the single largest share of a dense rebuild's cost (exit count often
|
|
31
|
+
* runs ~2x room count), so this tier buys back most of that cost while
|
|
32
|
+
* keeping real vector rooms, not raster pixels. Must be ≤ `roomBudget`;
|
|
33
|
+
* treated as unlimited (never hides exits) if omitted.
|
|
34
|
+
*/
|
|
35
|
+
exitBudget?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Decide the LOD tier: full vector (rooms + exits + hit-test-eligible),
|
|
39
|
+
* rooms-only vector (exits dropped, rooms still real vector shapes), or
|
|
40
|
+
* raster overview. Same pan-invariant zoom-based math as {@link shouldUseRaster}
|
|
41
|
+
* applied at two budgets — `exitBudget` always yields "roomsOnly" no later
|
|
42
|
+
* (i.e. at an equal or lower zoom-out) than `roomBudget` yields "raster",
|
|
43
|
+
* since exitBudget ≤ roomBudget.
|
|
44
|
+
*/
|
|
45
|
+
export declare function computeLodMode(i: LodModeInput): LodMode;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IArea } from '../../reader/Area';
|
|
2
|
+
/**
|
|
3
|
+
* Wrap an {@link IArea} so `getLinkExits` reports none, while every other
|
|
4
|
+
* method delegates unchanged. Used for the "rooms only" LOD tier: a bridge
|
|
5
|
+
* between full vector (rooms + exit lines) and the raster overview — rooms
|
|
6
|
+
* still render as real vector shapes (env colour, border, shape), but exit
|
|
7
|
+
* pairing and exit-line shape building are skipped entirely, which is a large
|
|
8
|
+
* share of a dense rebuild's cost (pairing scales with exit count, typically
|
|
9
|
+
* ~2x room count in a well-connected area).
|
|
10
|
+
*
|
|
11
|
+
* A plain object literal rather than a class: `IArea` implementations are
|
|
12
|
+
* often plain objects or class instances whose methods live on the
|
|
13
|
+
* prototype, so `{...area}` would silently drop them — explicit delegation
|
|
14
|
+
* avoids that trap.
|
|
15
|
+
*/
|
|
16
|
+
export declare function withoutLinkExits(area: IArea): IArea;
|
package/dist/types/Settings.d.ts
CHANGED
|
@@ -41,6 +41,25 @@ export type ViewportBounds = {
|
|
|
41
41
|
maxY: number;
|
|
42
42
|
};
|
|
43
43
|
export type PanEventDetail = ViewportBounds;
|
|
44
|
+
/** Emitted after every LOD mode decision (see {@link Settings.lodEnabled}). */
|
|
45
|
+
export type LodEventDetail = {
|
|
46
|
+
/**
|
|
47
|
+
* "vector": rooms + exit lines, full detail. "roomsOnly": exit lines
|
|
48
|
+
* dropped, rooms still real vector shapes (bridge tier — see
|
|
49
|
+
* {@link Settings.lodExitBudget}). "raster": pixel overview.
|
|
50
|
+
*/
|
|
51
|
+
mode: "vector" | "roomsOnly" | "raster";
|
|
52
|
+
/** Total rooms on the displayed (area, z) plane. */
|
|
53
|
+
planeRoomCount: number;
|
|
54
|
+
/** Cheap upper bound of the rooms inside the applied viewport. */
|
|
55
|
+
visibleEstimate: number;
|
|
56
|
+
/**
|
|
57
|
+
* Whether clicks/hover currently resolve to a room. Always false in
|
|
58
|
+
* raster mode; false in vector/roomsOnly mode once the room count exceeds
|
|
59
|
+
* {@link Settings.lodHitTestBudget}.
|
|
60
|
+
*/
|
|
61
|
+
hitTestActive: boolean;
|
|
62
|
+
};
|
|
44
63
|
export type RendererEventMap = {
|
|
45
64
|
roomclick: RoomClickEventDetail;
|
|
46
65
|
roomcontextmenu: RoomContextMenuEventDetail;
|
|
@@ -48,6 +67,7 @@ export type RendererEventMap = {
|
|
|
48
67
|
mapclick: undefined;
|
|
49
68
|
pan: PanEventDetail;
|
|
50
69
|
zoom: ZoomChangeEventDetail;
|
|
70
|
+
lod: LodEventDetail;
|
|
51
71
|
};
|
|
52
72
|
/**
|
|
53
73
|
* Style configuration for the player position marker.
|
|
@@ -238,6 +258,49 @@ export type Settings = {
|
|
|
238
258
|
/** Max number of steps from the player's room to spill neighbouring-area rooms across a
|
|
239
259
|
* boundary (BFS depth over planar exits). Default: 20 */
|
|
240
260
|
neighborSpillDistance: number;
|
|
261
|
+
/**
|
|
262
|
+
* Level-of-detail for very dense planes: when the current plane holds more
|
|
263
|
+
* rooms than {@link lodRoomBudget} and the zoom is far enough out that a
|
|
264
|
+
* viewport could exceed that budget, the vector scene is replaced by a
|
|
265
|
+
* raster pixel overview (one filled box per room) painted on an underlay.
|
|
266
|
+
* Zooming in switches back to full vector detail. The mode is reported via
|
|
267
|
+
* the renderer's `lod` event. Interactive Konva backend only. Default: false
|
|
268
|
+
*/
|
|
269
|
+
lodEnabled: boolean;
|
|
270
|
+
/**
|
|
271
|
+
* Max rooms the vector scene may hold before zoomed-out views switch to the
|
|
272
|
+
* raster overview; also the per-viewport budget the zoom threshold is
|
|
273
|
+
* derived from. Each rebuild at this density touches shape building, hit
|
|
274
|
+
* testing, and draw for the whole visible set, so lower this if rebuilds
|
|
275
|
+
* near the flip feel slow. Default: 16000
|
|
276
|
+
*/
|
|
277
|
+
lodRoomBudget: number;
|
|
278
|
+
/**
|
|
279
|
+
* Above this many rooms in the current plane build (same unit as
|
|
280
|
+
* {@link lodRoomBudget}), the hit-test index is skipped instead of
|
|
281
|
+
* rebuilt — the scene still renders at full vector detail, but
|
|
282
|
+
* clicks/hover won't resolve to a room until you zoom in below this
|
|
283
|
+
* density. Rebuilding the hit index is a meaningful fraction of a large
|
|
284
|
+
* rebuild, and precise pointer interaction is rarely needed at very high
|
|
285
|
+
* densities anyway. Only takes effect while `lodEnabled` is true, and only
|
|
286
|
+
* below `lodRoomBudget` (above it the plane is raster and has no hit
|
|
287
|
+
* shapes at all). Default: 10000
|
|
288
|
+
*/
|
|
289
|
+
lodHitTestBudget: number;
|
|
290
|
+
/**
|
|
291
|
+
* Above this many rooms in the current plane build (same unit as
|
|
292
|
+
* {@link lodRoomBudget}) and below `lodRoomBudget`, exit lines are
|
|
293
|
+
* dropped but rooms still render as full vector shapes — a bridge tier
|
|
294
|
+
* between full vector detail and the raster overview. Exit pairing and
|
|
295
|
+
* exit-line shape building are typically the single largest share of a
|
|
296
|
+
* dense rebuild's cost (exit count often runs ~2x room count in a
|
|
297
|
+
* well-connected area), so this buys back most of that cost while still
|
|
298
|
+
* showing real room shapes instead of raster pixels. Only takes effect
|
|
299
|
+
* while `lodEnabled` is true. Set above `lodRoomBudget` (or to `Infinity`)
|
|
300
|
+
* to disable this tier and jump straight from full vector to raster.
|
|
301
|
+
* Default: 12000
|
|
302
|
+
*/
|
|
303
|
+
lodExitBudget: number;
|
|
241
304
|
};
|
|
242
305
|
/** Creates a new Settings object with default values. */
|
|
243
306
|
export declare function createSettings(): Settings;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mudlet-map-renderer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/Delwing/mudlet-map-renderer"
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"types": "./dist/binary/index.d.ts",
|
|
19
19
|
"import": "./dist/binary.mjs"
|
|
20
20
|
},
|
|
21
|
+
"./bigmap": {
|
|
22
|
+
"types": "./dist/bigmap/index.d.ts",
|
|
23
|
+
"import": "./dist/bigmap.mjs"
|
|
24
|
+
},
|
|
21
25
|
"./offscreen": {
|
|
22
26
|
"types": "./dist/rendering/offscreen/index.d.ts",
|
|
23
27
|
"import": "./dist/offscreen.mjs"
|
|
@@ -39,14 +43,15 @@
|
|
|
39
43
|
"test": "vitest run",
|
|
40
44
|
"test:watch": "vitest",
|
|
41
45
|
"test:visual": "playwright test",
|
|
42
|
-
"test:visual:update": "playwright test --update-snapshots"
|
|
46
|
+
"test:visual:update": "playwright test --update-snapshots",
|
|
47
|
+
"demo:streaming": "vite --config vite.streaming.config.ts"
|
|
43
48
|
},
|
|
44
49
|
"dependencies": {
|
|
45
50
|
"konva": "^10.2.4",
|
|
46
51
|
"node-dijkstra": "^2.5.1"
|
|
47
52
|
},
|
|
48
53
|
"peerDependencies": {
|
|
49
|
-
"mudlet-map-binary-reader": ">=1.
|
|
54
|
+
"mudlet-map-binary-reader": ">=1.2.0"
|
|
50
55
|
},
|
|
51
56
|
"peerDependenciesMeta": {
|
|
52
57
|
"mudlet-map-binary-reader": {
|
|
@@ -59,7 +64,7 @@
|
|
|
59
64
|
"@types/node-dijkstra": "^2.5.6",
|
|
60
65
|
"@types/three": "^0.184.1",
|
|
61
66
|
"canvas": "^3.2.3",
|
|
62
|
-
"mudlet-map-binary-reader": "^1.0
|
|
67
|
+
"mudlet-map-binary-reader": "^1.2.0",
|
|
63
68
|
"three": "^0.184.0",
|
|
64
69
|
"tslib": "^2.8.1",
|
|
65
70
|
"tsx": "^4.21.0",
|