mudlet-map-renderer 0.32.0-konva → 0.34.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 +141 -21
- package/dist/ExitRenderer.d.ts +44 -0
- package/dist/ScenePipeline.d.ts +15 -2
- package/dist/Viewport.d.ts +12 -0
- package/dist/backend/CanvasBackend.d.ts +129 -0
- package/dist/backend/DrawingBackend.d.ts +55 -6
- package/dist/backend/KonvaBackend.d.ts +1 -0
- package/dist/export/CanvasExporter.d.ts +67 -0
- package/dist/export/Exporter.d.ts +57 -0
- package/dist/export/PngExporter.d.ts +23 -0
- package/dist/export/SvgExporter.d.ts +15 -0
- package/dist/export/canvasToBytes.d.ts +19 -0
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +20 -17
- package/dist/index.mjs +1354 -1042
- package/dist/index.mjs.map +1 -1
- package/dist/overlay/AmbientLightOverlay.d.ts +32 -0
- package/dist/overlay/LiveEffect.d.ts +33 -0
- package/dist/overlay/SceneOverlay.d.ts +63 -0
- package/dist/rendering/KonvaRenderBackend.d.ts +31 -9
- package/dist/rendering/MapRenderer.d.ts +64 -36
- package/dist/scene/AmbientLightStyle.d.ts +7 -2
- package/dist/{backend/BlueprintBackend.d.ts → style/BlueprintStyle.d.ts} +2 -12
- package/dist/{backend/IsometricBackend.d.ts → style/IsometricStyle.d.ts} +5 -6
- package/dist/{backend/NeonBackend.d.ts → style/NeonStyle.d.ts} +2 -12
- package/dist/{backend/ParchmentBackend.d.ts → style/ParchmentStyle.d.ts} +6 -16
- package/dist/{backend/SketchyBackend.d.ts → style/SketchyStyle.d.ts} +4 -13
- package/dist/style/index.d.ts +32 -0
- package/dist/types/Settings.d.ts +3 -17
- package/package.json +1 -1
- package/dist/HeadlessRenderer.d.ts +0 -64
- package/dist/MapRenderer.d.ts +0 -23
- package/dist/Renderer.d.ts +0 -56
- package/dist/rendering/SvgRenderBackend.d.ts +0 -21
- package/dist/types/OverlayPlugin.d.ts +0 -44
package/README.md
CHANGED
|
@@ -186,37 +186,97 @@ For large maps, spatial culling hides off-screen rooms for better performance:
|
|
|
186
186
|
renderer.setCullingMode('indexed');
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
###
|
|
189
|
+
### Styles
|
|
190
|
+
|
|
191
|
+
A `Style` is a target-agnostic visual transformer. One style drives the
|
|
192
|
+
interactive canvas *and* every exporter — set it once and it applies to SVG,
|
|
193
|
+
PNG, and anything else you export.
|
|
190
194
|
|
|
191
195
|
```ts
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
import {
|
|
197
|
+
compose, identityStyle,
|
|
198
|
+
Parchment, Blueprint, Neon, Sketchy, Isometric,
|
|
199
|
+
} from 'mudlet-map-renderer';
|
|
200
|
+
|
|
201
|
+
// Single style
|
|
202
|
+
renderer.setStyle(Parchment);
|
|
203
|
+
|
|
204
|
+
// Compose a chain (left → right = inner → outer)
|
|
205
|
+
renderer.setStyle(compose(
|
|
206
|
+
Parchment,
|
|
207
|
+
Sketchy({ jitter: 0.012, color: '#4a3728' }),
|
|
208
|
+
Isometric({ rotation: 30, depth: 0.18 }),
|
|
209
|
+
));
|
|
210
|
+
|
|
211
|
+
// Clear the current style
|
|
212
|
+
renderer.setStyle(identityStyle);
|
|
213
|
+
// or
|
|
214
|
+
renderer.clearStyle();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Built-in styles:
|
|
218
|
+
|
|
219
|
+
| Style | Effect |
|
|
220
|
+
|---|---|
|
|
221
|
+
| `Parchment` | Warm sepia / aged-paper palette |
|
|
222
|
+
| `Blueprint` | White lines on deep blue |
|
|
223
|
+
| `Neon` | Glowing neon outlines on dark background |
|
|
224
|
+
| `Sketchy({ jitter, color })` | Hand-drawn pencil wobble |
|
|
225
|
+
| `Isometric({ rotation?, depth? })` | 2:1 iso projection with optional cubes |
|
|
226
|
+
|
|
227
|
+
Custom styles extend `BaseStyle<Inner>` and override only the draw calls they
|
|
228
|
+
transform — see the built-ins for examples.
|
|
229
|
+
|
|
230
|
+
### Export
|
|
195
231
|
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
Exporters are plug-ins: a new output format is a new `Exporter<T>` class,
|
|
233
|
+
not a new `MapRenderer` method.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
import {
|
|
237
|
+
SvgExporter, PngExporter, PngBlobExporter, CanvasExporter,
|
|
238
|
+
} from 'mudlet-map-renderer';
|
|
239
|
+
|
|
240
|
+
// SVG (string)
|
|
241
|
+
const svg = renderer.export(new SvgExporter());
|
|
242
|
+
const svgCentered = renderer.export(new SvgExporter({ roomId: 1234, padding: 5 }));
|
|
243
|
+
const svgTyped = renderer.export(new SvgExporter({
|
|
198
244
|
overlays: {
|
|
199
245
|
position: { roomId: 1234 },
|
|
200
246
|
highlights: [{ roomId: 100, color: '#ff0000' }],
|
|
201
247
|
paths: [{ locations: [101, 102, 103], color: '#00ff00' }],
|
|
202
248
|
},
|
|
203
|
-
});
|
|
249
|
+
}));
|
|
204
250
|
|
|
205
|
-
// PNG
|
|
206
|
-
const
|
|
251
|
+
// PNG data URL
|
|
252
|
+
const pngUrl = renderer.export(new PngExporter({ pixelRatio: 2 }));
|
|
207
253
|
|
|
208
|
-
// PNG
|
|
209
|
-
const blob = await renderer.
|
|
254
|
+
// PNG as Blob
|
|
255
|
+
const blob = await renderer.export(new PngBlobExporter({ pixelRatio: 2 }));
|
|
210
256
|
|
|
211
|
-
//
|
|
212
|
-
const
|
|
257
|
+
// Headless PNG bytes at a specific size (portable — works in browser + Node)
|
|
258
|
+
const png = renderer.export(new PngBytesExporter({
|
|
213
259
|
width: 1920,
|
|
214
260
|
height: 1080,
|
|
215
261
|
roomId: 1234,
|
|
216
262
|
padding: 5,
|
|
217
|
-
});
|
|
263
|
+
}));
|
|
264
|
+
// fs.writeFileSync('out.png', png!); // Node
|
|
265
|
+
// new Blob([png!], { type: 'image/png' }); // Browser
|
|
266
|
+
|
|
267
|
+
// Canvas handle (if you need to draw more on it, attach to DOM, etc.)
|
|
268
|
+
const canvas = renderer.export(new CanvasExporter({
|
|
269
|
+
width: 1920,
|
|
270
|
+
height: 1080,
|
|
271
|
+
}));
|
|
218
272
|
```
|
|
219
273
|
|
|
274
|
+
Style + export compose: the style currently applied with `setStyle` is passed
|
|
275
|
+
to every exporter, so the SVG, the PNG, and the on-screen canvas stay in sync.
|
|
276
|
+
|
|
277
|
+
Writing a new exporter — e.g. PDF — means shipping a class that implements
|
|
278
|
+
`Exporter<Uint8Array>`. No changes to `MapRenderer`.
|
|
279
|
+
|
|
220
280
|
### Headless rendering (no DOM)
|
|
221
281
|
|
|
222
282
|
For server-side or offscreen rendering, omit the container argument:
|
|
@@ -225,10 +285,45 @@ For server-side or offscreen rendering, omit the container argument:
|
|
|
225
285
|
const renderer = new MapRenderer(mapReader, createSettings());
|
|
226
286
|
|
|
227
287
|
renderer.drawArea(42, 0);
|
|
228
|
-
renderer.
|
|
288
|
+
renderer.state.positionRoomId = 1234; // mark player position without auto-centering
|
|
229
289
|
|
|
230
|
-
const svg = renderer.
|
|
231
|
-
const
|
|
290
|
+
const svg = renderer.export(new SvgExporter({ padding: 5 }));
|
|
291
|
+
const png = renderer.export(new PngBytesExporter({ width: 1920, height: 1080 }));
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Overlays
|
|
295
|
+
|
|
296
|
+
Two kinds, picked by what you need:
|
|
297
|
+
|
|
298
|
+
- **`SceneOverlay`** — target-agnostic, appears in every output (interactive
|
|
299
|
+
canvas + every exporter). Use for static scene content (badges, annotations).
|
|
300
|
+
- **`LiveEffect`** — interactive canvas only, receives a Konva layer and
|
|
301
|
+
viewport updates for animation. Skipped by exporters.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
import type { SceneOverlay, LiveEffect } from 'mudlet-map-renderer';
|
|
305
|
+
|
|
306
|
+
// Scene overlay — uses target-agnostic draw primitives
|
|
307
|
+
class BadgeOverlay implements SceneOverlay {
|
|
308
|
+
render(target, state, bounds) {
|
|
309
|
+
const g = target.createGroup(0, 0);
|
|
310
|
+
target.addCircle(g, { cx: 5, cy: 5, radius: 0.4, fill: '#ff0' });
|
|
311
|
+
return g;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
renderer.addSceneOverlay('badge', new BadgeOverlay());
|
|
316
|
+
renderer.removeSceneOverlay('badge');
|
|
317
|
+
|
|
318
|
+
// Live effect — gets a Konva layer, interactive only
|
|
319
|
+
class Pulse implements LiveEffect {
|
|
320
|
+
attach(layer) { /* add Konva shapes */ }
|
|
321
|
+
updateViewport(bounds, scale) { /* react to pan/zoom */ }
|
|
322
|
+
destroy() { /* cleanup */ }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
renderer.addLiveEffect('pulse', new Pulse());
|
|
326
|
+
renderer.removeLiveEffect('pulse');
|
|
232
327
|
```
|
|
233
328
|
|
|
234
329
|
### Cleanup
|
|
@@ -360,16 +455,41 @@ MudletMapReader.exportJson(map, 'map.json');
|
|
|
360
455
|
| `setZoom(zoom)` | Set zoom level |
|
|
361
456
|
| `zoomToCenter(zoom)` | Zoom keeping center fixed |
|
|
362
457
|
| `fitArea()` | Fit the full area in view |
|
|
363
|
-
| `
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
458
|
+
| `setStyle(style)` | Apply a visual style (interactive + exporters) |
|
|
459
|
+
| `clearStyle()` | Remove the current style |
|
|
460
|
+
| `export(exporter)` | Run an `Exporter<T>` and return its output |
|
|
461
|
+
| `addSceneOverlay(id, overlay)` | Add a target-agnostic overlay |
|
|
462
|
+
| `removeSceneOverlay(id)` | Remove a scene overlay |
|
|
463
|
+
| `addLiveEffect(id, effect)` | Add an interactive-only animated effect |
|
|
464
|
+
| `removeLiveEffect(id)` | Remove a live effect |
|
|
367
465
|
| `refresh()` | Force a full re-render |
|
|
368
466
|
| `on(event, handler)` | Subscribe to an event |
|
|
369
467
|
| `off(event, handler)` | Unsubscribe from an event |
|
|
370
468
|
| `setCullingMode(mode)` | Set culling strategy |
|
|
371
469
|
| `destroy()` | Release all resources |
|
|
372
470
|
|
|
471
|
+
### Exporters
|
|
472
|
+
|
|
473
|
+
| Exporter | Output |
|
|
474
|
+
|---|---|
|
|
475
|
+
| `SvgExporter({ roomId?, padding?, overlays? })` | `string` — SVG document |
|
|
476
|
+
| `PngExporter({ pixelRatio? })` | `string` — PNG data URL (current viewport) |
|
|
477
|
+
| `PngBlobExporter({ pixelRatio? })` | `Promise<Blob>` — PNG Blob (browser only) |
|
|
478
|
+
| `PngBytesExporter({ width, height, roomId?, padding?, overlays?, mimeType?, quality? })` | `Uint8Array` — headless PNG/JPEG bytes; portable (browser + Node) |
|
|
479
|
+
| `CanvasExporter({ width, height, roomId?, padding?, overlays? })` | `ExportCanvas` — canvas handle; reframes to fit |
|
|
480
|
+
|
|
481
|
+
### Styles
|
|
482
|
+
|
|
483
|
+
| Style | Constructor / Usage |
|
|
484
|
+
|---|---|
|
|
485
|
+
| `Parchment` | `setStyle(Parchment)` |
|
|
486
|
+
| `Blueprint` | `setStyle(Blueprint)` |
|
|
487
|
+
| `Neon` | `setStyle(Neon)` |
|
|
488
|
+
| `Sketchy(opts)` | `setStyle(Sketchy({ jitter, color }))` |
|
|
489
|
+
| `Isometric(opts)` | `setStyle(Isometric({ rotation?, depth? }))` |
|
|
490
|
+
| `compose(...)` | Chain multiple styles into one |
|
|
491
|
+
| `identityStyle` | Pass-through; equivalent to `clearStyle()` |
|
|
492
|
+
|
|
373
493
|
### `PathFinder`
|
|
374
494
|
|
|
375
495
|
| Method | Description |
|
package/dist/ExitRenderer.d.ts
CHANGED
|
@@ -36,6 +36,18 @@ export type ExitDrawData = {
|
|
|
36
36
|
};
|
|
37
37
|
/** Set when this exit leads to a room in a different area (cross-area exit). */
|
|
38
38
|
targetRoomId?: number;
|
|
39
|
+
/** Source room center in map coords — used to compute label direction. */
|
|
40
|
+
from?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
44
|
+
/** Arrow tip / far endpoint in map coords — anchor for placed labels. */
|
|
45
|
+
tip?: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
};
|
|
49
|
+
/** Stroke colour of the rendered arrow — used to colour area-exit labels. */
|
|
50
|
+
arrowColor?: string;
|
|
39
51
|
};
|
|
40
52
|
export default class ExitRenderer {
|
|
41
53
|
private mapReader;
|
|
@@ -53,6 +65,29 @@ export default class ExitRenderer {
|
|
|
53
65
|
/**
|
|
54
66
|
* Returns hit-zone bounds for special exits (custom lines) that lead to rooms in another area.
|
|
55
67
|
*/
|
|
68
|
+
/**
|
|
69
|
+
* Hit zones for cross-area inner exits (up/down/in/out). These are drawn as
|
|
70
|
+
* triangles inside the room, not through the exit pipeline, so they need
|
|
71
|
+
* their own plumbing for area-exit labelling and clickability.
|
|
72
|
+
*/
|
|
73
|
+
getInnerExitAreaTargets(room: MapData.Room): {
|
|
74
|
+
bounds: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
};
|
|
80
|
+
targetRoomId: number;
|
|
81
|
+
from: {
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
};
|
|
85
|
+
tip: {
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
};
|
|
89
|
+
arrowColor: string;
|
|
90
|
+
}[];
|
|
56
91
|
getSpecialExitAreaTargets(room: MapData.Room): {
|
|
57
92
|
bounds: {
|
|
58
93
|
x: number;
|
|
@@ -61,5 +96,14 @@ export default class ExitRenderer {
|
|
|
61
96
|
height: number;
|
|
62
97
|
};
|
|
63
98
|
targetRoomId: number;
|
|
99
|
+
from: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
tip: {
|
|
104
|
+
x: number;
|
|
105
|
+
y: number;
|
|
106
|
+
};
|
|
107
|
+
arrowColor: string;
|
|
64
108
|
}[];
|
|
65
109
|
}
|
package/dist/ScenePipeline.d.ts
CHANGED
|
@@ -24,6 +24,18 @@ export type StandaloneExitEntry = {
|
|
|
24
24
|
export type AreaExitHitZone = {
|
|
25
25
|
bounds: Bounds;
|
|
26
26
|
targetRoomId: number;
|
|
27
|
+
/** Source room center — used to compute arrow direction for labels. Optional for back-compat. */
|
|
28
|
+
from?: {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
};
|
|
32
|
+
/** Arrow tip / far endpoint — anchor for placed labels. Optional for back-compat. */
|
|
33
|
+
tip?: {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
};
|
|
37
|
+
/** Stroke colour of the rendered arrow — used to colour area-exit labels. */
|
|
38
|
+
arrowColor?: string;
|
|
27
39
|
};
|
|
28
40
|
export type SceneBuildResult = {
|
|
29
41
|
roomNodes: Map<number, RoomNodeEntry>;
|
|
@@ -34,8 +46,8 @@ export type SceneBuildResult = {
|
|
|
34
46
|
* Backend-agnostic scene composition pipeline.
|
|
35
47
|
* Drives a DrawingBackend + LayerNode to render the full map scene.
|
|
36
48
|
*
|
|
37
|
-
* Both KonvaRenderBackend and
|
|
38
|
-
* with their respective DrawingBackend
|
|
49
|
+
* Both the interactive KonvaRenderBackend and exporters (SvgExporter,
|
|
50
|
+
* CanvasExporter, …) drive this pipeline with their respective DrawingBackend.
|
|
39
51
|
*/
|
|
40
52
|
export declare class ScenePipeline {
|
|
41
53
|
private readonly mapReader;
|
|
@@ -70,6 +82,7 @@ export declare class ScenePipeline {
|
|
|
70
82
|
renderExitData(data: ExitDrawData): GroupNode;
|
|
71
83
|
private renderArrow;
|
|
72
84
|
private renderLabels;
|
|
85
|
+
private renderAreaExitLabels;
|
|
73
86
|
private renderAreaName;
|
|
74
87
|
}
|
|
75
88
|
export {};
|
package/dist/Viewport.d.ts
CHANGED
|
@@ -52,6 +52,18 @@ export declare class Viewport {
|
|
|
52
52
|
* Center on a map coordinate, with optional animation.
|
|
53
53
|
*/
|
|
54
54
|
panToMapPointAnimated(x: number, y: number, instant: boolean): void;
|
|
55
|
+
/**
|
|
56
|
+
* Compute the zoom level that would fit the given map bounds in the current
|
|
57
|
+
* viewport (with the same padding/insets as {@link fitToMapBounds}). Useful
|
|
58
|
+
* for updating `minZoom` to lock zoom-out to an area without changing the
|
|
59
|
+
* current zoom or position.
|
|
60
|
+
*/
|
|
61
|
+
computeFitZoom(minX: number, maxX: number, minY: number, maxY: number, insets?: {
|
|
62
|
+
top?: number;
|
|
63
|
+
right?: number;
|
|
64
|
+
bottom?: number;
|
|
65
|
+
left?: number;
|
|
66
|
+
}): number;
|
|
55
67
|
/**
|
|
56
68
|
* Fit the viewport to show the given map bounds with padding.
|
|
57
69
|
* Optional `insets` (screen pixels) reserve space at each edge — content
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { default as Konva } from 'konva';
|
|
2
|
+
import { DrawingBackend, GroupNode, LayerNode, CoordFn, RectConfig, CircleConfig, LineConfig, PolygonConfig, TextConfig, ImageConfig } from './DrawingBackend';
|
|
3
|
+
type RectCommand = {
|
|
4
|
+
type: 'rect';
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
fill?: string;
|
|
10
|
+
stroke?: string;
|
|
11
|
+
sw: number;
|
|
12
|
+
cr: number;
|
|
13
|
+
dash?: number[];
|
|
14
|
+
};
|
|
15
|
+
type CircleCommand = {
|
|
16
|
+
type: 'circle';
|
|
17
|
+
cx: number;
|
|
18
|
+
cy: number;
|
|
19
|
+
r: number;
|
|
20
|
+
fill?: string;
|
|
21
|
+
stroke?: string;
|
|
22
|
+
sw: number;
|
|
23
|
+
dash?: number[];
|
|
24
|
+
};
|
|
25
|
+
type LineCommand = {
|
|
26
|
+
type: 'line';
|
|
27
|
+
points: number[];
|
|
28
|
+
stroke?: string;
|
|
29
|
+
sw: number;
|
|
30
|
+
dash?: number[];
|
|
31
|
+
lineCap?: string;
|
|
32
|
+
lineJoin?: string;
|
|
33
|
+
alpha?: number;
|
|
34
|
+
};
|
|
35
|
+
type PolygonCommand = {
|
|
36
|
+
type: 'polygon';
|
|
37
|
+
vertices: number[];
|
|
38
|
+
fill?: string;
|
|
39
|
+
stroke?: string;
|
|
40
|
+
sw: number;
|
|
41
|
+
};
|
|
42
|
+
type TextCommand = {
|
|
43
|
+
type: 'text';
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
text: string;
|
|
47
|
+
fontSize: number;
|
|
48
|
+
fontFamily: string;
|
|
49
|
+
fontStyle: string;
|
|
50
|
+
fill: string;
|
|
51
|
+
align: string;
|
|
52
|
+
vAlign: string;
|
|
53
|
+
w: number;
|
|
54
|
+
h: number;
|
|
55
|
+
baselineRatio?: number;
|
|
56
|
+
transform?: [number, number, number, number, number, number];
|
|
57
|
+
};
|
|
58
|
+
type ImageCommand = {
|
|
59
|
+
type: 'image';
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
w: number;
|
|
63
|
+
h: number;
|
|
64
|
+
image: HTMLImageElement | any;
|
|
65
|
+
transform?: [number, number, number, number, number, number];
|
|
66
|
+
};
|
|
67
|
+
type DrawCommand = RectCommand | CircleCommand | LineCommand | PolygonCommand | TextCommand | ImageCommand;
|
|
68
|
+
export declare class RecordingGroupNode implements GroupNode {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
_visible: boolean;
|
|
72
|
+
readonly commands: DrawCommand[];
|
|
73
|
+
/** Lazily created when this group is materialized for a KonvaLayerNode. */
|
|
74
|
+
_konvaGroup?: Konva.Group;
|
|
75
|
+
constructor(x: number, y: number);
|
|
76
|
+
setVisible(visible: boolean): void;
|
|
77
|
+
isVisible(): boolean;
|
|
78
|
+
destroy(): void;
|
|
79
|
+
setPosition(x: number, y: number): void;
|
|
80
|
+
getPosition(): {
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
};
|
|
84
|
+
moveToTop(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Materialize this recording as a Konva.Group + Konva.Shape.
|
|
87
|
+
* Used when the group is added to a KonvaLayerNode (overlay/position layers).
|
|
88
|
+
*/
|
|
89
|
+
materialize(): Konva.Group;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A LayerNode backed by a single Konva.Shape whose sceneFunc replays
|
|
93
|
+
* all recorded groups. Much faster than individual Konva nodes.
|
|
94
|
+
*/
|
|
95
|
+
export declare class RecordingLayerNode implements LayerNode {
|
|
96
|
+
private groups;
|
|
97
|
+
private readonly konvaLayer;
|
|
98
|
+
private konvaShape;
|
|
99
|
+
constructor(konvaLayer: Konva.Layer);
|
|
100
|
+
private ensureShape;
|
|
101
|
+
addNode(node: GroupNode): void;
|
|
102
|
+
destroyChildren(): void;
|
|
103
|
+
batchDraw(): void;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* DrawingBackend that records draw commands into RecordingGroupNodes.
|
|
107
|
+
* Commands are replayed via Canvas2D in a single Konva.Shape sceneFunc
|
|
108
|
+
* per layer, eliminating per-node Konva overhead.
|
|
109
|
+
*
|
|
110
|
+
* Drop-in replacement for KonvaBackend. Decorator backends wrap this
|
|
111
|
+
* the same way they wrap KonvaBackend.
|
|
112
|
+
*/
|
|
113
|
+
export declare class CanvasBackend implements DrawingBackend {
|
|
114
|
+
createGroup(x: number, y: number): RecordingGroupNode;
|
|
115
|
+
addRect(parent: GroupNode, config: RectConfig): void;
|
|
116
|
+
addCircle(parent: GroupNode, config: CircleConfig): void;
|
|
117
|
+
addLine(parent: GroupNode, config: LineConfig): void;
|
|
118
|
+
addPolygon(parent: GroupNode, config: PolygonConfig): void;
|
|
119
|
+
addText(parent: GroupNode, config: TextConfig): void;
|
|
120
|
+
addImage(parent: GroupNode, config: ImageConfig): void;
|
|
121
|
+
supportsBatchExitRendering(): boolean;
|
|
122
|
+
getExitDepthOffset(): {
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
125
|
+
};
|
|
126
|
+
getTransform(): CoordFn;
|
|
127
|
+
getInverseTransform(): CoordFn;
|
|
128
|
+
}
|
|
129
|
+
export {};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Low-level drawing primitives. A {@link DrawingBackend} is a thin abstraction
|
|
3
|
+
* over per-node draw calls. Leaf implementations target a specific rendering
|
|
4
|
+
* engine (Konva, raw Canvas2D, SVG strings, …); decorator implementations
|
|
5
|
+
* ({@link BaseStyle}) wrap another backend and transform the calls.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* End users should rarely touch this directly — prefer {@link Style} factories
|
|
8
|
+
* (Parchment, Sketchy(…), …) with {@link MapRenderer.setStyle}, and
|
|
9
|
+
* {@link Exporter} implementations with {@link MapRenderer.export}.
|
|
7
10
|
*/
|
|
8
11
|
export interface GroupNode {
|
|
9
12
|
setVisible(visible: boolean): void;
|
|
@@ -103,6 +106,12 @@ export interface DrawingBackend {
|
|
|
103
106
|
addPolygon(parent: GroupNode, config: PolygonConfig): void;
|
|
104
107
|
addText(parent: GroupNode, config: TextConfig): void;
|
|
105
108
|
addImage(parent: GroupNode, config: ImageConfig): void;
|
|
109
|
+
/**
|
|
110
|
+
* Whether this backend supports batch exit rendering via a single Canvas2D shape.
|
|
111
|
+
* When true, link exits are collected as ExitDrawData and drawn in one batched
|
|
112
|
+
* Konva.Shape sceneFunc instead of creating individual nodes per exit.
|
|
113
|
+
*/
|
|
114
|
+
supportsBatchExitRendering?(): boolean;
|
|
106
115
|
/**
|
|
107
116
|
* Cartesian offset for exit line groups so they connect at the cube base
|
|
108
117
|
* instead of the top face. Returns {x:0, y:0} for flat backends.
|
|
@@ -113,11 +122,51 @@ export interface DrawingBackend {
|
|
|
113
122
|
};
|
|
114
123
|
/**
|
|
115
124
|
* Map-space → render-space transform. Identity for flat backends; non-identity
|
|
116
|
-
* for
|
|
125
|
+
* for styles that warp coordinates (e.g. `IsometricStyle`).
|
|
117
126
|
* Decorators delegate to their inner backend.
|
|
118
|
-
* MapRenderer auto-applies this to culling and grid rendering when the backend is set.
|
|
119
127
|
*/
|
|
120
128
|
getTransform(): CoordFn;
|
|
121
129
|
/** Inverse of {@link getTransform}. */
|
|
122
130
|
getInverseTransform(): CoordFn;
|
|
123
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Abstract base for style (decorator) backends. Forwards every
|
|
134
|
+
* {@link DrawingBackend} method to `this.inner` by default; subclasses override
|
|
135
|
+
* only the methods they transform. Generic over the wrapped inner type so
|
|
136
|
+
* tooling preserves specific types through chains where useful.
|
|
137
|
+
*/
|
|
138
|
+
export declare abstract class BaseStyle<Inner extends DrawingBackend = DrawingBackend> implements DrawingBackend {
|
|
139
|
+
protected readonly inner: Inner;
|
|
140
|
+
constructor(inner: Inner);
|
|
141
|
+
createGroup(x: number, y: number): GroupNode;
|
|
142
|
+
addRect(parent: GroupNode, config: RectConfig): void;
|
|
143
|
+
addCircle(parent: GroupNode, config: CircleConfig): void;
|
|
144
|
+
addLine(parent: GroupNode, config: LineConfig): void;
|
|
145
|
+
addPolygon(parent: GroupNode, config: PolygonConfig): void;
|
|
146
|
+
addText(parent: GroupNode, config: TextConfig): void;
|
|
147
|
+
addImage(parent: GroupNode, config: ImageConfig): void;
|
|
148
|
+
supportsBatchExitRendering(): boolean;
|
|
149
|
+
getExitDepthOffset(): {
|
|
150
|
+
x: number;
|
|
151
|
+
y: number;
|
|
152
|
+
};
|
|
153
|
+
getTransform(): CoordFn;
|
|
154
|
+
getInverseTransform(): CoordFn;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A {@link Style} is a target-agnostic transformer: given a {@link DrawingBackend}
|
|
158
|
+
* it returns a decorated one. The same style drives interactive canvas, SVG
|
|
159
|
+
* export, and any future target.
|
|
160
|
+
*
|
|
161
|
+
* Compose via {@link compose}; built-in styles live in `src/style`.
|
|
162
|
+
*/
|
|
163
|
+
export type Style = (target: DrawingBackend) => DrawingBackend;
|
|
164
|
+
/** Identity style — passes the target through unchanged. Useful as a default. */
|
|
165
|
+
export declare const identityStyle: Style;
|
|
166
|
+
/**
|
|
167
|
+
* Compose a chain of {@link Style}s into a single Style.
|
|
168
|
+
*
|
|
169
|
+
* `compose(Parchment, Sketchy)` wraps with Parchment first, then Sketchy —
|
|
170
|
+
* Sketchy is the outermost decorator, i.e. its methods run first during rendering.
|
|
171
|
+
*/
|
|
172
|
+
export declare function compose(...styles: Style[]): Style;
|
|
@@ -34,6 +34,7 @@ export declare class KonvaBackend implements DrawingBackend {
|
|
|
34
34
|
addPolygon(parent: GroupNode, config: PolygonConfig): void;
|
|
35
35
|
addText(parent: GroupNode, config: TextConfig): void;
|
|
36
36
|
addImage(parent: GroupNode, config: ImageConfig): void;
|
|
37
|
+
supportsBatchExitRendering(): boolean;
|
|
37
38
|
getExitDepthOffset(): {
|
|
38
39
|
x: number;
|
|
39
40
|
y: number;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Exporter, ExportContext, ExportCanvas } from './Exporter';
|
|
2
|
+
export interface CanvasExportOptions {
|
|
3
|
+
/** Width of the output image in pixels. */
|
|
4
|
+
width: number;
|
|
5
|
+
/** Height of the output image in pixels. */
|
|
6
|
+
height: number;
|
|
7
|
+
/** Room ID to center the export on. If omitted, exports the full area. */
|
|
8
|
+
roomId?: number;
|
|
9
|
+
/** Padding in map units around the exported region. Default: 3 */
|
|
10
|
+
padding?: number;
|
|
11
|
+
/** Overlays to render over the scene (position marker, highlights, paths). */
|
|
12
|
+
overlays?: {
|
|
13
|
+
position?: {
|
|
14
|
+
roomId: number;
|
|
15
|
+
};
|
|
16
|
+
highlights?: Array<{
|
|
17
|
+
roomId: number;
|
|
18
|
+
color: string;
|
|
19
|
+
}>;
|
|
20
|
+
paths?: Array<{
|
|
21
|
+
locations: number[];
|
|
22
|
+
color: string;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Renders the current scene into a canvas at the requested width/height,
|
|
28
|
+
* reframing the viewport to fit the area (or a specific room) with padding.
|
|
29
|
+
*
|
|
30
|
+
* The returned {@link ExportCanvas} is the node-canvas-compatible object
|
|
31
|
+
* produced by Konva. Serialize with `.toBuffer('image/png')` in Node or
|
|
32
|
+
* `.toDataURL('image/png')` / `.toBlob(cb)` in the browser.
|
|
33
|
+
*
|
|
34
|
+
* Unlike {@link PngExporter} (which rasterizes the current on-screen viewport),
|
|
35
|
+
* `CanvasExporter` is the headless/programmatic path.
|
|
36
|
+
*/
|
|
37
|
+
export declare class CanvasExporter implements Exporter<ExportCanvas | undefined> {
|
|
38
|
+
private readonly options;
|
|
39
|
+
constructor(options: CanvasExportOptions);
|
|
40
|
+
render({ backend }: ExportContext): ExportCanvas | undefined;
|
|
41
|
+
}
|
|
42
|
+
export interface PngBytesExportOptions extends CanvasExportOptions {
|
|
43
|
+
/** MIME type to encode. Defaults to `'image/png'`. */
|
|
44
|
+
mimeType?: string;
|
|
45
|
+
/** Encoder quality (0..1). Only used for lossy formats like `'image/jpeg'`. */
|
|
46
|
+
quality?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Headless PNG/JPEG bytes at a specific width × height.
|
|
50
|
+
*
|
|
51
|
+
* Composes {@link CanvasExporter} with a portable `toDataURL` → `Uint8Array`
|
|
52
|
+
* decode, so callers get bytes directly without touching a canvas or casting
|
|
53
|
+
* to platform-specific types:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* const png = renderer.export(new PngBytesExporter({ width: 1920, height: 1080 }));
|
|
57
|
+
* fs.writeFileSync('out.png', png!); // Node
|
|
58
|
+
* new Blob([png!], { type: 'image/png' }); // Browser
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* For JPEG: `new PngBytesExporter({ width, height, mimeType: 'image/jpeg', quality: 0.9 })`.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PngBytesExporter implements Exporter<Uint8Array | undefined> {
|
|
64
|
+
private readonly options;
|
|
65
|
+
constructor(options: PngBytesExportOptions);
|
|
66
|
+
render(context: ExportContext): Uint8Array | undefined;
|
|
67
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MapState } from '../MapState';
|
|
2
|
+
import { Style } from '../backend/DrawingBackend';
|
|
3
|
+
import { SceneOverlay } from '../overlay/SceneOverlay';
|
|
4
|
+
import { InteractiveBackend } from '../rendering/MapRenderer';
|
|
5
|
+
/**
|
|
6
|
+
* Context handed to every {@link Exporter} by {@link MapRenderer.export}.
|
|
7
|
+
* Exporters pull whatever they need — state + style for SVG, backend for
|
|
8
|
+
* canvas/PNG rasterization — so callers never have to wire `renderer.backend`
|
|
9
|
+
* (or any other internal) into an exporter themselves.
|
|
10
|
+
*/
|
|
11
|
+
export interface ExportContext {
|
|
12
|
+
readonly state: MapState;
|
|
13
|
+
readonly backend: InteractiveBackend;
|
|
14
|
+
readonly style: Style;
|
|
15
|
+
readonly sceneOverlays: Iterable<SceneOverlay>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pluggable output format. An {@link Exporter} consumes an {@link ExportContext}
|
|
19
|
+
* and returns an output of type `T` (a string, a Blob promise, a canvas, bytes…).
|
|
20
|
+
*
|
|
21
|
+
* Adding a new output format means implementing `Exporter<T>`. `MapRenderer`'s
|
|
22
|
+
* surface does not change — users just pass the new exporter to `renderer.export`.
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* renderer.export(new SvgExporter({ padding: 5 })); // string
|
|
26
|
+
* renderer.export(new PngExporter({ pixelRatio: 2 })); // string (data URL)
|
|
27
|
+
* renderer.export(new CanvasExporter({ width, height })); // ExportCanvas
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export interface Exporter<T> {
|
|
31
|
+
render(context: ExportContext): T;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Canvas returned by {@link InteractiveBackend.toCanvas} and
|
|
35
|
+
* {@link CanvasExporter}. Describes the portable surface common to the
|
|
36
|
+
* browser `HTMLCanvasElement` and the `canvas` package's Node-side Canvas:
|
|
37
|
+
*
|
|
38
|
+
* - Portable: `width`, `height`, `getContext`, `toDataURL`.
|
|
39
|
+
* - Browser: `toBlob(cb)` (optional here; use when serializing to a Blob).
|
|
40
|
+
*
|
|
41
|
+
* Platform-specific serializers (e.g. node-canvas's `toBuffer`) are intentionally
|
|
42
|
+
* not part of this interface. Cast when you need them:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* import type { Canvas } from 'canvas';
|
|
46
|
+
* const canvas = renderer.export(new CanvasExporter({width, height})) as unknown as Canvas;
|
|
47
|
+
* const png = canvas.toBuffer('image/png');
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export interface ExportCanvas {
|
|
51
|
+
readonly width: number;
|
|
52
|
+
readonly height: number;
|
|
53
|
+
getContext(contextId: '2d', options?: any): CanvasRenderingContext2D | null;
|
|
54
|
+
toDataURL(type?: string, quality?: any): string;
|
|
55
|
+
/** Browser only; undefined in Node. */
|
|
56
|
+
toBlob?(callback: (blob: Blob | null) => void, type?: string, quality?: any): void;
|
|
57
|
+
}
|