mudlet-map-renderer 2.5.0 → 2.6.0

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.
@@ -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 readonly exits;
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,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. Returns a
49
- * `Map<Shape, boolean>` where absent shapes are unmanaged pass-throughs
50
- * (always visible). Avoids the 12 Sets + 2 filtered arrays produced by
51
- * the full {@link cull} path.
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): Map<Shape, boolean>;
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;
@@ -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.5.0",
3
+ "version": "2.6.0",
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.0.0"
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.1",
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",