mudlet-map-renderer 0.27.0-konva → 0.28.0-konva
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 +392 -0
- package/dist/CullingManager.d.ts +68 -0
- package/dist/ExitRenderer.d.ts +4 -20
- package/dist/GridRenderer.d.ts +16 -0
- package/dist/HeadlessRenderer.d.ts +39 -38
- package/dist/InteractionHandler.d.ts +53 -0
- package/dist/MapGraph.d.ts +28 -0
- package/dist/MapRenderer.d.ts +4 -13
- package/dist/MapState.d.ts +104 -0
- package/dist/PathData.d.ts +1 -1
- package/dist/PathFinder.d.ts +2 -7
- package/dist/Renderer.d.ts +36 -316
- package/dist/RoomShapeRenderer.d.ts +16 -0
- package/dist/ScenePipeline.d.ts +75 -0
- package/dist/SvgTypes.d.ts +21 -0
- package/dist/TypedEventEmitter.d.ts +17 -0
- package/dist/Viewport.d.ts +68 -0
- package/dist/backend/DrawingBackend.d.ts +94 -0
- package/dist/backend/KonvaBackend.d.ts +37 -0
- package/dist/backend/SketchyBackend.d.ts +36 -0
- package/dist/backend/SvgBackend.d.ts +46 -0
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +22 -9
- package/dist/index.mjs +2803 -2189
- package/dist/index.mjs.map +1 -1
- package/dist/rendering/KonvaRenderBackend.d.ts +78 -0
- package/dist/rendering/MapRenderer.d.ts +96 -0
- package/dist/rendering/SvgRenderBackend.d.ts +21 -0
- package/dist/scene/AmbientLightStyle.d.ts +24 -0
- package/dist/scene/GridStyle.d.ts +21 -0
- package/dist/scene/InnerExitStyle.d.ts +24 -0
- package/dist/scene/OverlayRenderer.d.ts +35 -0
- package/dist/scene/OverlayStyle.d.ts +43 -0
- package/dist/scene/RoomStyle.d.ts +32 -0
- package/dist/scene/SpecialExitStyle.d.ts +35 -0
- package/dist/scene/StubStyle.d.ts +13 -0
- package/dist/types/Settings.d.ts +189 -0
- package/dist/utils/color.d.ts +4 -0
- package/dist/utils/textMeasure.d.ts +12 -1
- package/package.json +11 -3
- package/dist/CanvasExporter.d.ts +0 -76
- package/dist/PathRenderer.d.ts +0 -14
- package/dist/SvgExporter.d.ts +0 -51
package/README.md
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# mudlet-map-renderer
|
|
2
|
+
|
|
3
|
+
A rendering library for [Mudlet](https://www.mudlet.org/) map data. Takes Mudlet's JSON map format and renders interactive, zoomable maps using Konva, with SVG and PNG export support.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
yarn add mudlet-map-renderer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { MudletMapReader } from 'mudlet-map-binary-reader';
|
|
17
|
+
import { MapRenderer, MapReader, createSettings } from 'mudlet-map-renderer';
|
|
18
|
+
|
|
19
|
+
// Read a Mudlet binary map file and export renderer-compatible data
|
|
20
|
+
const map = MudletMapReader.read('map.dat');
|
|
21
|
+
const { mapData, colors } = MudletMapReader.export(map);
|
|
22
|
+
|
|
23
|
+
const mapReader = new MapReader(mapData, colors);
|
|
24
|
+
|
|
25
|
+
// Create an interactive renderer attached to a DOM element
|
|
26
|
+
const container = document.getElementById('map') as HTMLDivElement;
|
|
27
|
+
const renderer = new MapRenderer(mapReader, createSettings(), container);
|
|
28
|
+
|
|
29
|
+
// Display an area (area ID, z-level)
|
|
30
|
+
renderer.drawArea(42, 0);
|
|
31
|
+
|
|
32
|
+
// Show the player position (centers the viewport on the room)
|
|
33
|
+
renderer.setPosition(1234);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Settings
|
|
39
|
+
|
|
40
|
+
Create a settings object with `createSettings()` and customize before passing to the renderer:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createSettings } from 'mudlet-map-renderer';
|
|
44
|
+
|
|
45
|
+
const settings = createSettings();
|
|
46
|
+
settings.roomSize = 0.8;
|
|
47
|
+
settings.roomShape = 'circle'; // 'rectangle' | 'circle' | 'roundedRectangle'
|
|
48
|
+
settings.backgroundColor = '#1a1a2e';
|
|
49
|
+
settings.lineColor = 'rgb(200, 220, 255)';
|
|
50
|
+
settings.gridEnabled = true;
|
|
51
|
+
settings.emboss = true;
|
|
52
|
+
settings.areaName = true;
|
|
53
|
+
|
|
54
|
+
// Player position marker
|
|
55
|
+
settings.playerMarker.strokeColor = '#ff6600';
|
|
56
|
+
settings.playerMarker.fillColor = '#ff6600';
|
|
57
|
+
settings.playerMarker.fillAlpha = 0.3;
|
|
58
|
+
settings.playerMarker.matchRoomShape = true;
|
|
59
|
+
|
|
60
|
+
const renderer = new MapRenderer(mapReader, settings, container);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Settings are a shared mutable object. To change settings after the renderer is already running, modify the properties and call `refresh()`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// Change appearance at runtime
|
|
67
|
+
renderer.settings.roomShape = 'circle';
|
|
68
|
+
renderer.settings.emboss = true;
|
|
69
|
+
renderer.refresh();
|
|
70
|
+
|
|
71
|
+
// Background color has its own update method (CSS-only, no scene rebuild)
|
|
72
|
+
renderer.settings.backgroundColor = '#1a1a2e';
|
|
73
|
+
renderer.updateBackground();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Navigation
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// Switch to a different area and z-level
|
|
80
|
+
renderer.drawArea(areaId, zIndex);
|
|
81
|
+
|
|
82
|
+
// Move player position (auto-switches area if needed)
|
|
83
|
+
renderer.setPosition(roomId);
|
|
84
|
+
|
|
85
|
+
// Move player without centering the viewport
|
|
86
|
+
renderer.setPosition(roomId, false);
|
|
87
|
+
|
|
88
|
+
// Update the marker without triggering area switch logic
|
|
89
|
+
renderer.updatePositionMarker(roomId);
|
|
90
|
+
|
|
91
|
+
// Center viewport on a room (with animation)
|
|
92
|
+
renderer.centerOn(roomId);
|
|
93
|
+
|
|
94
|
+
// Center instantly (no animation)
|
|
95
|
+
renderer.centerOn(roomId, true);
|
|
96
|
+
|
|
97
|
+
// Remove the position marker
|
|
98
|
+
renderer.clearPosition();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Highlights and paths
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// Highlight rooms with a color
|
|
105
|
+
renderer.renderHighlight(roomId, '#ff0000');
|
|
106
|
+
renderer.renderHighlight(otherRoomId, 'rgba(0, 255, 0, 0.5)');
|
|
107
|
+
|
|
108
|
+
// Remove a specific highlight
|
|
109
|
+
renderer.removeHighlight(roomId);
|
|
110
|
+
|
|
111
|
+
// Check if a room is highlighted
|
|
112
|
+
if (renderer.hasHighlight(roomId)) { /* ... */ }
|
|
113
|
+
|
|
114
|
+
// Clear all highlights
|
|
115
|
+
renderer.clearHighlights();
|
|
116
|
+
|
|
117
|
+
// Render a path through a list of room IDs
|
|
118
|
+
renderer.renderPath([101, 102, 103, 104], '#66E64D');
|
|
119
|
+
|
|
120
|
+
// Clear all paths
|
|
121
|
+
renderer.clearPaths();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Viewport control
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// Zoom
|
|
128
|
+
renderer.setZoom(1.5);
|
|
129
|
+
renderer.zoomToCenter(2.0);
|
|
130
|
+
console.log(renderer.getZoom());
|
|
131
|
+
|
|
132
|
+
// Fit the entire area in view
|
|
133
|
+
renderer.fitArea();
|
|
134
|
+
|
|
135
|
+
// Get current viewport bounds (in map coordinates)
|
|
136
|
+
const bounds = renderer.getViewportBounds();
|
|
137
|
+
// { minX, maxX, minY, maxY }
|
|
138
|
+
|
|
139
|
+
// Control resize behavior
|
|
140
|
+
renderer.centerOnResize = true; // re-center on window resize
|
|
141
|
+
renderer.minZoom = 0.1;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Events
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
renderer.on('roomclick', ({ roomId, position }) => {
|
|
148
|
+
console.log(`Clicked room ${roomId} at (${position.x}, ${position.y})`);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
renderer.on('roomcontextmenu', ({ roomId, position }) => {
|
|
152
|
+
// Right-click or long-press on a room
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
renderer.on('areaexitclick', ({ targetRoomId, position }) => {
|
|
156
|
+
// Clicked an area exit — navigate to the target room's area
|
|
157
|
+
renderer.setPosition(targetRoomId);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
renderer.on('mapclick', () => {
|
|
161
|
+
// Clicked on empty space
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
renderer.on('zoom', ({ zoom }) => {
|
|
165
|
+
console.log(`Zoom level: ${zoom}`);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
renderer.on('pan', (bounds) => {
|
|
169
|
+
// Viewport moved
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Remove a listener
|
|
173
|
+
const handler = ({ roomId }) => { /* ... */ };
|
|
174
|
+
renderer.on('roomclick', handler);
|
|
175
|
+
renderer.off('roomclick', handler);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Culling
|
|
179
|
+
|
|
180
|
+
For large maps, spatial culling hides off-screen rooms for better performance:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
// "indexed" (default) — bucket-based spatial index, best for large maps
|
|
184
|
+
// "basic" — simple bounds check per room
|
|
185
|
+
// "none" — render everything (useful for debugging)
|
|
186
|
+
renderer.setCullingMode('indexed');
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Export
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
// SVG export
|
|
193
|
+
const svg = renderer.exportSvg();
|
|
194
|
+
const svgCentered = renderer.exportSvg({ roomId: 1234, padding: 5 });
|
|
195
|
+
|
|
196
|
+
// SVG with explicit overlays
|
|
197
|
+
const svgWithOverlays = renderer.exportSvg({
|
|
198
|
+
overlays: {
|
|
199
|
+
position: { roomId: 1234 },
|
|
200
|
+
highlights: [{ roomId: 100, color: '#ff0000' }],
|
|
201
|
+
paths: [{ locations: [101, 102, 103], color: '#00ff00' }],
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// PNG export (data URL)
|
|
206
|
+
const pngDataUrl = renderer.exportPng({ pixelRatio: 2 });
|
|
207
|
+
|
|
208
|
+
// PNG export (Blob)
|
|
209
|
+
const blob = await renderer.exportPngBlob({ pixelRatio: 2 });
|
|
210
|
+
|
|
211
|
+
// Canvas export (specific region)
|
|
212
|
+
const canvas = renderer.renderToCanvas({
|
|
213
|
+
width: 1920,
|
|
214
|
+
height: 1080,
|
|
215
|
+
roomId: 1234,
|
|
216
|
+
padding: 5,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Headless rendering (no DOM)
|
|
221
|
+
|
|
222
|
+
For server-side or offscreen rendering, omit the container argument:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
const renderer = new MapRenderer(mapReader, createSettings());
|
|
226
|
+
|
|
227
|
+
renderer.drawArea(42, 0);
|
|
228
|
+
renderer.setPosition(1234);
|
|
229
|
+
|
|
230
|
+
const svg = renderer.exportSvg({ padding: 5 });
|
|
231
|
+
const canvas = renderer.renderToCanvas({ width: 1920, height: 1080 });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Cleanup
|
|
235
|
+
|
|
236
|
+
Call `destroy()` when you're done with a renderer to release all resources — DOM event listeners, Konva stages, and internal subscriptions:
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const renderer = new MapRenderer(mapReader, settings, container);
|
|
240
|
+
|
|
241
|
+
// ... use the renderer ...
|
|
242
|
+
|
|
243
|
+
// Tear down completely
|
|
244
|
+
renderer.destroy();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
This is important in SPAs and frameworks like React where components mount and unmount. Without calling `destroy()`, event listeners on `window` and the container element will leak.
|
|
248
|
+
|
|
249
|
+
#### React example
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
import { useEffect, useRef } from 'react';
|
|
253
|
+
import { MapRenderer, MapReader, createSettings } from 'mudlet-map-renderer';
|
|
254
|
+
|
|
255
|
+
function MudletMap({ mapData, envData, areaId, roomId }) {
|
|
256
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
if (!containerRef.current) return;
|
|
260
|
+
|
|
261
|
+
const mapReader = new MapReader(mapData, envData);
|
|
262
|
+
const renderer = new MapRenderer(mapReader, createSettings(), containerRef.current);
|
|
263
|
+
|
|
264
|
+
renderer.drawArea(areaId, 0);
|
|
265
|
+
if (roomId) renderer.setPosition(roomId);
|
|
266
|
+
|
|
267
|
+
return () => {
|
|
268
|
+
renderer.destroy();
|
|
269
|
+
};
|
|
270
|
+
}, [mapData, envData, areaId, roomId]);
|
|
271
|
+
|
|
272
|
+
return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Pathfinding
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
import { MapReader, PathFinder, MapGraph, computePathData } from 'mudlet-map-renderer';
|
|
280
|
+
|
|
281
|
+
const mapReader = new MapReader(mapData, envData);
|
|
282
|
+
const pathFinder = new PathFinder(mapReader);
|
|
283
|
+
|
|
284
|
+
// Find shortest path between two rooms (Dijkstra or A*)
|
|
285
|
+
const roomIds = pathFinder.findPath(startRoomId, endRoomId, 'astar');
|
|
286
|
+
|
|
287
|
+
if (roomIds) {
|
|
288
|
+
// Render the path on the map
|
|
289
|
+
renderer.renderPath(roomIds, '#66E64D');
|
|
290
|
+
|
|
291
|
+
// Or compute detailed path geometry for custom rendering
|
|
292
|
+
const pathData = computePathData(mapReader, renderer.settings, roomIds, '#66E64D');
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Exploration mode
|
|
297
|
+
|
|
298
|
+
Show only rooms the player has visited (fog of war):
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
const mapReader = new MapReader(mapData, envData);
|
|
302
|
+
|
|
303
|
+
// Enable exploration with a set of visited room IDs
|
|
304
|
+
const visitedRooms = mapReader.decorateWithExploration(new Set([100, 101, 102, 200]));
|
|
305
|
+
|
|
306
|
+
// Add newly visited rooms as the player explores
|
|
307
|
+
visitedRooms.add(103);
|
|
308
|
+
|
|
309
|
+
// Areas automatically filter to only show visited rooms
|
|
310
|
+
const renderer = new MapRenderer(mapReader, createSettings(), container);
|
|
311
|
+
renderer.drawArea(areaId, 0);
|
|
312
|
+
|
|
313
|
+
// Disable exploration mode (show all rooms again)
|
|
314
|
+
mapReader.clearExplorationDecoration();
|
|
315
|
+
renderer.refresh();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Map data format
|
|
319
|
+
|
|
320
|
+
The renderer expects data produced by [mudlet-map-binary-reader](https://github.com/Delwing/node-mudlet-map-binary-reader), which reads Mudlet's binary `map.dat` format and exports renderer-compatible structures:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { MudletMapReader } from 'mudlet-map-binary-reader';
|
|
324
|
+
|
|
325
|
+
const map = MudletMapReader.read('map.dat');
|
|
326
|
+
const { mapData, colors } = MudletMapReader.export(map);
|
|
327
|
+
// mapData: MapData.Map (Area[])
|
|
328
|
+
// colors: MapData.Env[]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
`MapReader` accepts these two values directly:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
const mapReader = new MapReader(mapData, colors);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
The binary reader can also write exported data to files for static use:
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
MudletMapReader.export(map, 'output'); // writes .js files
|
|
341
|
+
MudletMapReader.exportJson(map, 'map.json');
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## API reference
|
|
345
|
+
|
|
346
|
+
### `MapRenderer`
|
|
347
|
+
|
|
348
|
+
| Method | Description |
|
|
349
|
+
|--------|-------------|
|
|
350
|
+
| `drawArea(id, zIndex)` | Display an area at a z-level |
|
|
351
|
+
| `setPosition(roomId, center?)` | Set player position (auto-switches area) |
|
|
352
|
+
| `updatePositionMarker(roomId)` | Update marker without area switch |
|
|
353
|
+
| `clearPosition()` | Remove position marker |
|
|
354
|
+
| `centerOn(roomId, instant?)` | Center viewport on a room |
|
|
355
|
+
| `renderHighlight(roomId, color)` | Highlight a room |
|
|
356
|
+
| `removeHighlight(roomId)` | Remove a highlight |
|
|
357
|
+
| `clearHighlights()` | Clear all highlights |
|
|
358
|
+
| `renderPath(roomIds, color?)` | Draw a path |
|
|
359
|
+
| `clearPaths()` | Clear all paths |
|
|
360
|
+
| `setZoom(zoom)` | Set zoom level |
|
|
361
|
+
| `zoomToCenter(zoom)` | Zoom keeping center fixed |
|
|
362
|
+
| `fitArea()` | Fit the full area in view |
|
|
363
|
+
| `exportSvg(options?)` | Export as SVG string |
|
|
364
|
+
| `exportPng(options?)` | Export as PNG data URL |
|
|
365
|
+
| `exportPngBlob(options?)` | Export as PNG Blob |
|
|
366
|
+
| `renderToCanvas(options)` | Export a region to canvas |
|
|
367
|
+
| `refresh()` | Force a full re-render |
|
|
368
|
+
| `on(event, handler)` | Subscribe to an event |
|
|
369
|
+
| `off(event, handler)` | Unsubscribe from an event |
|
|
370
|
+
| `setCullingMode(mode)` | Set culling strategy |
|
|
371
|
+
| `destroy()` | Release all resources |
|
|
372
|
+
|
|
373
|
+
### `PathFinder`
|
|
374
|
+
|
|
375
|
+
| Method | Description |
|
|
376
|
+
|--------|-------------|
|
|
377
|
+
| `findPath(from, to, algorithm?)` | Find shortest path (`'dijkstra'` or `'astar'`) |
|
|
378
|
+
|
|
379
|
+
### `MapReader`
|
|
380
|
+
|
|
381
|
+
| Method | Description |
|
|
382
|
+
|--------|-------------|
|
|
383
|
+
| `getArea(areaId)` | Get an area by ID |
|
|
384
|
+
| `getAreas()` | Get all areas |
|
|
385
|
+
| `getRoom(roomId)` | Get a room by ID |
|
|
386
|
+
| `getRooms()` | Get all rooms |
|
|
387
|
+
| `decorateWithExploration(visited?)` | Enable fog of war |
|
|
388
|
+
| `clearExplorationDecoration()` | Disable fog of war |
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Settings } from './types/Settings';
|
|
2
|
+
import { GroupNode, LayerNode } from './backend/DrawingBackend';
|
|
3
|
+
export type RoomNodeEntry = {
|
|
4
|
+
room: MapData.Room;
|
|
5
|
+
group: GroupNode;
|
|
6
|
+
};
|
|
7
|
+
type Bounds = {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
export type StandaloneExitEntry = {
|
|
14
|
+
group: GroupNode;
|
|
15
|
+
bounds: Bounds;
|
|
16
|
+
targetRoomId?: number;
|
|
17
|
+
};
|
|
18
|
+
export type StageInfo = {
|
|
19
|
+
scaleX(): number;
|
|
20
|
+
position(): {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
width(): number;
|
|
25
|
+
height(): number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Manages spatial indexing and viewport culling for rooms and exits.
|
|
29
|
+
* Toggles node visibility based on what's in the viewport.
|
|
30
|
+
* No direct Konva dependency.
|
|
31
|
+
*/
|
|
32
|
+
export declare class CullingManager {
|
|
33
|
+
private readonly stageInfo;
|
|
34
|
+
private readonly roomLayer;
|
|
35
|
+
private readonly linkLayer;
|
|
36
|
+
private readonly settings;
|
|
37
|
+
roomNodes: Map<number, RoomNodeEntry>;
|
|
38
|
+
standaloneExitNodes: StandaloneExitEntry[];
|
|
39
|
+
spatialBucketSize: number;
|
|
40
|
+
private roomSpatialIndex;
|
|
41
|
+
private exitSpatialIndex;
|
|
42
|
+
private visibleRooms;
|
|
43
|
+
private visibleStandaloneExitNodes;
|
|
44
|
+
private bufferRoomSet;
|
|
45
|
+
private bufferExitSet;
|
|
46
|
+
private bufferRoomCandidates;
|
|
47
|
+
private bufferExitCandidates;
|
|
48
|
+
private standaloneExitBoundsRoomSize?;
|
|
49
|
+
private cullingScheduled;
|
|
50
|
+
private perfMonitor;
|
|
51
|
+
constructor(stageInfo: StageInfo, roomLayer: LayerNode, linkLayer: LayerNode, settings: Settings);
|
|
52
|
+
computeBucketSize(): void;
|
|
53
|
+
clear(): void;
|
|
54
|
+
private getBucketKey;
|
|
55
|
+
private forEachBucket;
|
|
56
|
+
addRoomToSpatialIndex(entry: RoomNodeEntry): void;
|
|
57
|
+
addStandaloneExitToSpatialIndex(entry: StandaloneExitEntry): void;
|
|
58
|
+
findRoomAtMapPoint(mapX: number, mapY: number): MapData.Room | null;
|
|
59
|
+
markExitBoundsStale(): void;
|
|
60
|
+
setExitBoundsRoomSize(): void;
|
|
61
|
+
private collectRoomCandidates;
|
|
62
|
+
private collectStandaloneExitCandidates;
|
|
63
|
+
private refreshStandaloneExitBoundsIfNeeded;
|
|
64
|
+
scheduleCulling(): void;
|
|
65
|
+
updateCulling(): void;
|
|
66
|
+
private cullExits;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
package/dist/ExitRenderer.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { default as Exit } from './reader/Exit';
|
|
2
2
|
import { default as MapReader } from './reader/MapReader';
|
|
3
|
-
import { Settings
|
|
3
|
+
import { Settings } from './types/Settings';
|
|
4
4
|
export type ExitDrawLine = {
|
|
5
5
|
points: number[];
|
|
6
6
|
stroke: string;
|
|
@@ -39,23 +39,17 @@ export type ExitDrawData = {
|
|
|
39
39
|
};
|
|
40
40
|
export default class ExitRenderer {
|
|
41
41
|
private mapReader;
|
|
42
|
-
private mapRenderer;
|
|
43
42
|
private readonly settings;
|
|
44
|
-
constructor(mapReader: MapReader,
|
|
43
|
+
constructor(mapReader: MapReader, settings: Settings);
|
|
45
44
|
/**
|
|
46
|
-
* Get the edge point of a room based on its shape
|
|
45
|
+
* Get the edge point of a room based on its shape.
|
|
46
|
+
* The inset accounts for the inner border so exit lines reach the visible room edge.
|
|
47
47
|
*/
|
|
48
48
|
private getRoomEdgePoint;
|
|
49
49
|
renderData(exit: Exit, zIndex: number): ExitDrawData | undefined;
|
|
50
50
|
renderDataWithColor(exit: Exit, color: string, zIndex: number): ExitDrawData | undefined;
|
|
51
51
|
private renderTwoWayExitData;
|
|
52
52
|
private renderOneWayExitData;
|
|
53
|
-
render(exit: Exit, zIndex: number): import('konva/lib/Group').Group | import('konva/lib/shapes/Arrow').Arrow | undefined;
|
|
54
|
-
renderWithColor(exit: Exit, color: string, zIndex: number): import('konva/lib/Group').Group | import('konva/lib/shapes/Arrow').Arrow | undefined;
|
|
55
|
-
private renderTwoWayExit;
|
|
56
|
-
private renderOneWayExit;
|
|
57
|
-
renderAreaExit(room: MapData.Room, dir: MapData.direction, color?: string): import('konva/lib/shapes/Arrow').Arrow;
|
|
58
|
-
renderSpecialExits(room: MapData.Room, overrideColor?: string): (import('konva/lib/shapes/Arrow').Arrow | import('konva/lib/shapes/Rect').Rect | import('konva/lib/shapes/Line').Line<import('konva/lib/shapes/Line').LineConfig>)[];
|
|
59
53
|
/**
|
|
60
54
|
* Returns hit-zone bounds for special exits (custom lines) that lead to rooms in another area.
|
|
61
55
|
*/
|
|
@@ -68,14 +62,4 @@ export default class ExitRenderer {
|
|
|
68
62
|
};
|
|
69
63
|
targetRoomId: number;
|
|
70
64
|
}[];
|
|
71
|
-
renderStubs(room: MapData.Room, color?: string): import('konva/lib/shapes/Line').Line<{
|
|
72
|
-
points: number[];
|
|
73
|
-
stroke: string;
|
|
74
|
-
strokeWidth: number;
|
|
75
|
-
perfectDrawEnabled: false;
|
|
76
|
-
listening: false;
|
|
77
|
-
}>[];
|
|
78
|
-
private getSymbolColor;
|
|
79
|
-
renderInnerExits(room: MapData.Room): import('konva/lib/Group').Group[];
|
|
80
|
-
renderDoor(points: number[], type: 1 | 2 | 3): import('konva/lib/shapes/Rect').Rect;
|
|
81
65
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Settings, ViewportBounds } from './types/Settings';
|
|
2
|
+
import { DrawingBackend, LayerNode } from './backend/DrawingBackend';
|
|
3
|
+
/**
|
|
4
|
+
* Renders and caches the background grid on a dedicated layer.
|
|
5
|
+
* Grid lines are only recreated when the visible bounds change enough
|
|
6
|
+
* to cross a grid-line boundary. No direct Konva dependency.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GridRenderer {
|
|
9
|
+
private readonly layer;
|
|
10
|
+
private readonly settings;
|
|
11
|
+
private readonly backend;
|
|
12
|
+
private cachedBounds;
|
|
13
|
+
constructor(layer: LayerNode, settings: Settings, backend: DrawingBackend);
|
|
14
|
+
invalidateCache(): void;
|
|
15
|
+
render(viewportBounds: ViewportBounds): void;
|
|
16
|
+
}
|
|
@@ -1,41 +1,50 @@
|
|
|
1
1
|
import { default as MapReader } from './reader/MapReader';
|
|
2
2
|
import { default as Area } from './reader/Area';
|
|
3
|
-
import { Settings } from './
|
|
4
|
-
import { MapRenderer } from './MapRenderer';
|
|
5
|
-
import { SvgExportOptions } from './
|
|
6
|
-
|
|
3
|
+
import { Settings } from './types/Settings';
|
|
4
|
+
import { MapRenderer as MapRendererInterface } from './MapRenderer';
|
|
5
|
+
import { SvgExportOptions } from './SvgTypes';
|
|
6
|
+
export type CanvasExportOptions = {
|
|
7
|
+
/** Width of the output image in pixels. */
|
|
8
|
+
width: number;
|
|
9
|
+
/** Height of the output image in pixels. */
|
|
10
|
+
height: number;
|
|
11
|
+
/** Room ID to center the export on. If omitted, exports the full area. */
|
|
12
|
+
roomId?: number;
|
|
13
|
+
/** Padding in map units around the exported region. Default: 3 */
|
|
14
|
+
padding?: number;
|
|
15
|
+
};
|
|
16
|
+
export type CanvasExportOverlays = {
|
|
17
|
+
position?: {
|
|
18
|
+
roomId: number;
|
|
19
|
+
};
|
|
20
|
+
highlights?: Array<{
|
|
21
|
+
roomId: number;
|
|
22
|
+
color: string;
|
|
23
|
+
}>;
|
|
24
|
+
paths?: Array<{
|
|
25
|
+
locations: number[];
|
|
26
|
+
color: string;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
7
29
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* position tracking, path rendering, and highlights — then exports to SVG or
|
|
11
|
-
* renders to a caller-provided Canvas2D context for PNG output.
|
|
30
|
+
* Backward-compatible headless renderer.
|
|
31
|
+
* Delegates to the unified MapRenderer in headless mode (no container).
|
|
12
32
|
*
|
|
13
|
-
*
|
|
33
|
+
* @deprecated Use MapRenderer directly (omit the container argument for headless mode):
|
|
14
34
|
* ```
|
|
15
|
-
*
|
|
35
|
+
* import { MapRenderer, createSettings } from 'mudlet-map-renderer';
|
|
36
|
+
*
|
|
37
|
+
* const renderer = new MapRenderer(mapReader, settings);
|
|
16
38
|
* renderer.drawArea(areaId, 0);
|
|
17
39
|
* renderer.setPosition(playerRoomId);
|
|
18
|
-
* renderer.renderPath([roomA, roomB, roomC], '#ff0000');
|
|
19
40
|
*
|
|
20
|
-
* // SVG export (always available)
|
|
21
41
|
* const svg = renderer.exportSvg({ padding: 5 });
|
|
22
|
-
*
|
|
23
|
-
* // PNG export (Node.js with 'canvas' package)
|
|
24
|
-
* import { createCanvas } from 'canvas';
|
|
25
|
-
* const canvas = createCanvas(1920, 1080);
|
|
26
|
-
* renderer.renderToCanvas(canvas.getContext('2d'), { width: 1920, height: 1080 });
|
|
27
|
-
* fs.writeFileSync('map.png', canvas.toBuffer('image/png'));
|
|
42
|
+
* const canvas = renderer.renderToCanvas({ width: 1920, height: 1080 });
|
|
28
43
|
* ```
|
|
29
44
|
*/
|
|
30
|
-
export declare class HeadlessRenderer implements
|
|
31
|
-
private readonly
|
|
32
|
-
|
|
33
|
-
private currentArea?;
|
|
34
|
-
private currentAreaInstance?;
|
|
35
|
-
private currentZIndex?;
|
|
36
|
-
private positionRoomId?;
|
|
37
|
-
private highlights;
|
|
38
|
-
private paths;
|
|
45
|
+
export declare class HeadlessRenderer implements MapRendererInterface {
|
|
46
|
+
private readonly renderer;
|
|
47
|
+
get settings(): Settings;
|
|
39
48
|
constructor(mapReader: MapReader, settings?: Settings);
|
|
40
49
|
drawArea(id: number, zIndex: number): void;
|
|
41
50
|
getCurrentArea(): Area | undefined;
|
|
@@ -47,17 +56,9 @@ export declare class HeadlessRenderer implements MapRenderer {
|
|
|
47
56
|
removeHighlight(roomId: number): void;
|
|
48
57
|
hasHighlight(roomId: number): boolean;
|
|
49
58
|
clearHighlights(): void;
|
|
50
|
-
/**
|
|
51
|
-
* Export the current area as an SVG string.
|
|
52
|
-
*/
|
|
53
59
|
exportSvg(options?: SvgExportOptions): string | undefined;
|
|
54
|
-
|
|
55
|
-
* Render the current area to a Canvas2D context (for PNG export).
|
|
56
|
-
* The caller provides the canvas and context — works with `canvas` npm package (Node.js)
|
|
57
|
-
* or OffscreenCanvas (browser).
|
|
58
|
-
*/
|
|
59
|
-
renderToCanvas(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, options: Omit<CanvasExportOptions, 'overlays'> & {
|
|
60
|
+
renderToCanvas(options: CanvasExportOptions & {
|
|
60
61
|
overlays?: CanvasExportOverlays;
|
|
61
|
-
}):
|
|
62
|
-
|
|
62
|
+
}): any;
|
|
63
|
+
destroy(): void;
|
|
63
64
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Settings, RendererEventMap } from './types/Settings';
|
|
2
|
+
import { TypedEventEmitter } from './TypedEventEmitter';
|
|
3
|
+
import { Viewport } from './Viewport';
|
|
4
|
+
import { MapState } from './MapState';
|
|
5
|
+
type Bounds = {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
type AreaExitHitZone = {
|
|
12
|
+
bounds: Bounds;
|
|
13
|
+
targetRoomId: number;
|
|
14
|
+
};
|
|
15
|
+
export type HitTestCallbacks = {
|
|
16
|
+
clientToMapPoint: (clientX: number, clientY: number) => {
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
} | null;
|
|
20
|
+
findRoomAtPoint: (mapX: number, mapY: number) => MapData.Room | null;
|
|
21
|
+
getAreaExitHitZones: () => AreaExitHitZone[];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Handles all DOM interaction on the map container:
|
|
25
|
+
* - Viewport: drag (mouse + touch), wheel zoom, pinch zoom, resize
|
|
26
|
+
* - Map: hover cursor, click, right-click, long-press, area exit clicks
|
|
27
|
+
*
|
|
28
|
+
* No Konva dependency — works with any rendering backend.
|
|
29
|
+
*/
|
|
30
|
+
export declare class InteractionHandler {
|
|
31
|
+
private readonly container;
|
|
32
|
+
private readonly settings;
|
|
33
|
+
private readonly viewport;
|
|
34
|
+
private readonly state;
|
|
35
|
+
private readonly hitTest;
|
|
36
|
+
private readonly events;
|
|
37
|
+
private lastPinchDistance?;
|
|
38
|
+
private readonly cleanupFns;
|
|
39
|
+
private destroyed;
|
|
40
|
+
constructor(container: HTMLDivElement, viewport: Viewport, state: MapState, settings: Settings, hitTest: HitTestCallbacks, events: TypedEventEmitter<RendererEventMap>);
|
|
41
|
+
destroy(): void;
|
|
42
|
+
private listen;
|
|
43
|
+
private initViewportEvents;
|
|
44
|
+
private handlePinch;
|
|
45
|
+
private initMapEvents;
|
|
46
|
+
private findRoomAtClientPoint;
|
|
47
|
+
private findAreaExitAtClientPoint;
|
|
48
|
+
private emitRoomClickEvent;
|
|
49
|
+
private emitRoomContextEvent;
|
|
50
|
+
private emitAreaExitClickEvent;
|
|
51
|
+
private emitMapClickEvent;
|
|
52
|
+
}
|
|
53
|
+
export {};
|