mudlet-map-renderer 2.1.0 → 2.2.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.
- package/README.md +14 -0
- package/dist/{MapReader-mU_4JWv_.js → MapReader-Brzg-X7T.js} +1 -1
- package/dist/{MapReader-mU_4JWv_.js.map → MapReader-Brzg-X7T.js.map} +1 -1
- package/dist/assets/worker-DtQws85m.js.map +1 -0
- package/dist/binary.mjs +1 -1
- package/dist/export/flushSceneShapes.d.ts +1 -1
- package/dist/flushSceneShapes-Duhi0OQv.js +2937 -0
- package/dist/flushSceneShapes-Duhi0OQv.js.map +1 -0
- package/dist/hit/HitTester.d.ts +19 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +855 -3445
- package/dist/index.mjs.map +1 -1
- package/dist/offscreen.mjs +355 -0
- package/dist/offscreen.mjs.map +1 -0
- package/dist/overlay/SceneOverlay.d.ts +16 -3
- package/dist/rendering/KonvaRenderBackend.d.ts +4 -0
- package/dist/rendering/MapRenderer.d.ts +12 -3
- package/dist/rendering/offscreen/OffscreenCanvasBackend.d.ts +109 -0
- package/dist/rendering/offscreen/index.d.ts +11 -0
- package/dist/rendering/offscreen/protocol.d.ts +100 -0
- package/dist/rendering/offscreen/renderFrame.d.ts +28 -0
- package/dist/rendering/offscreen/serializeTransform.d.ts +20 -0
- package/dist/rendering/offscreen/worker.d.ts +15 -0
- package/dist/style/Style.d.ts +12 -0
- package/dist/style/index.d.ts +10 -1
- package/dist/style/shape/GraphPaperStyle.d.ts +16 -0
- package/dist/style/shape/StainedGlassStyle.d.ts +15 -0
- package/dist/style/shape/TopographicStyle.d.ts +14 -0
- package/dist/style/shape/WatercolorStyle.d.ts +32 -0
- package/dist/style/shape/index.d.ts +5 -0
- package/dist/style/shape/paintMap.d.ts +8 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -221,8 +221,15 @@ Built-in styles:
|
|
|
221
221
|
| `Parchment` | Warm sepia / aged-paper palette |
|
|
222
222
|
| `Blueprint` | White lines on deep blue |
|
|
223
223
|
| `Neon` | Glowing neon outlines on dark background |
|
|
224
|
+
| `Construction` | Safety-yellow hazard stripes on asphalt, orange exits |
|
|
225
|
+
| `SciFi` | Holographic cyan glow on void black |
|
|
226
|
+
| `StainedGlass` | Saturated jewel panes framed by fat near-black leading |
|
|
227
|
+
| `GraphPaper` | Pale rooms inked in navy — old-school D&D graph paper |
|
|
228
|
+
| `Topographic` | Earthy elevation palette with concentric contour rings |
|
|
224
229
|
| `Sketchy({ jitter, color })` | Hand-drawn pencil wobble |
|
|
230
|
+
| `Watercolor({ bleed?, layers?, alpha? })` | Translucent edge-bled washes that pool on overlap |
|
|
225
231
|
| `Isometric({ rotation?, depth? })` | 2:1 iso projection with optional cubes |
|
|
232
|
+
| `GradientRooms(opts?)` | Replace flat fills with a vertical gradient |
|
|
226
233
|
|
|
227
234
|
Custom styles extend `BaseStyle<Inner>` and override only the draw calls they
|
|
228
235
|
transform — see the built-ins for examples.
|
|
@@ -493,8 +500,15 @@ MudletMapReader.exportJson(map, 'map.json');
|
|
|
493
500
|
| `Parchment` | `setStyle(Parchment)` |
|
|
494
501
|
| `Blueprint` | `setStyle(Blueprint)` |
|
|
495
502
|
| `Neon` | `setStyle(Neon)` |
|
|
503
|
+
| `Construction` | `setStyle(Construction)` |
|
|
504
|
+
| `SciFi` | `setStyle(SciFi)` |
|
|
505
|
+
| `StainedGlass` | `setStyle(StainedGlass)` |
|
|
506
|
+
| `GraphPaper` | `setStyle(GraphPaper)` |
|
|
507
|
+
| `Topographic` | `setStyle(Topographic)` |
|
|
496
508
|
| `Sketchy(opts)` | `setStyle(Sketchy({ jitter, color }))` |
|
|
509
|
+
| `Watercolor(opts?)` | `setStyle(Watercolor({ bleed?, layers?, alpha? }))` |
|
|
497
510
|
| `Isometric(opts)` | `setStyle(Isometric({ rotation?, depth? }))` |
|
|
511
|
+
| `GradientRooms(opts?)` | `setStyle(GradientRooms())` |
|
|
498
512
|
| `compose(...)` | Chain multiple styles into one |
|
|
499
513
|
| `identityStyle` | Pass-through; equivalent to `clearStyle()` |
|
|
500
514
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MapReader-mU_4JWv_.js","names":[],"sources":["../src/reader/Plane.ts","../src/reader/Area.ts","../src/reader/MapReader.ts"],"sourcesContent":["/**\n * Public, renderer-facing surface of a z-level slice of an area.\n *\n * The renderer (and other library consumers) only depend on this interface;\n * the concrete {@link Plane} class is one implementation of it. Downstream\n * apps that want to drive the renderer from their own data model can hand it\n * any `IPlane`-shaped object (typically returned from an {@link IArea}).\n */\nexport interface IPlane {\n getRooms(): MapData.Room[];\n getLabels(): MapData.Label[];\n getBounds(): { minX: number; maxX: number; minY: number; maxY: number };\n}\n\nexport default class Plane implements IPlane {\n\n private readonly bounds: { minX: number, maxX: number, minY: number, maxY: number }\n private readonly rooms: MapData.Room[] = [];\n private readonly labels: MapData.Label[] = [];\n\n constructor(rooms: MapData.Room[], labels: MapData.Label[]) {\n this.rooms = rooms;\n this.labels = labels;\n this.bounds = this.createBounds();\n }\n\n getRooms() {\n return this.rooms;\n }\n\n getLabels() {\n return this.labels;\n }\n\n getBounds() {\n return this.bounds;\n }\n\n private createBounds() {\n const b = this.rooms.reduce(\n (acc, r) => ({\n minX: Math.min(acc.minX, r.x),\n maxX: Math.max(acc.maxX, r.x),\n minY: Math.min(acc.minY, r.y),\n maxY: Math.max(acc.maxY, r.y),\n }),\n {\n minX: Number.POSITIVE_INFINITY,\n maxX: Number.NEGATIVE_INFINITY,\n minY: Number.POSITIVE_INFINITY,\n maxY: Number.NEGATIVE_INFINITY,\n }\n );\n for (const label of this.labels) {\n const lx = label.X;\n const ly = -label.Y;\n b.minX = Math.min(b.minX, lx);\n b.maxX = Math.max(b.maxX, lx + label.Width);\n b.minY = Math.min(b.minY, ly);\n b.maxY = Math.max(b.maxY, ly + label.Height);\n }\n return b;\n }\n\n}\n","import Plane, {IPlane} from \"./Plane\";\n\nimport IExit from \"./Exit\";\n\n/**\n * Public, renderer-facing surface of an area. The renderer never inspects\n * private state of {@link Area}; it talks to this interface only. Custom data\n * models (e.g. an app that owns its own room store) can satisfy `IArea` and\n * hand the result to {@link MapRenderer} without subclassing.\n *\n * Mutability lives on the concrete implementation: {@link Area.markDirty} is\n * `protected`, kept off the public interface. Consumers signal \"redraw me\" by\n * bumping {@link getVersion}; the renderer reads the version to decide\n * whether to rebuild.\n */\nexport interface IArea {\n getAreaName(): string;\n getAreaId(): number;\n /**\n * Monotonically increasing version. The renderer compares the value it\n * cached on the last build against the current value to decide whether\n * the area needs a rebuild.\n */\n getVersion(): number;\n getPlane(zIndex: number): IPlane;\n getPlanes(): IPlane[];\n getZLevels(): number[];\n getRooms(): MapData.Room[];\n getFullBounds(): { minX: number; maxX: number; minY: number; maxY: number };\n /**\n * Inter-room exits drawn on the given z-level. Each exit is bidirectional\n * with optional one-way fallback; see {@link IExit}.\n */\n getLinkExits(zIndex: number): IExit[];\n}\n\nexport default class Area implements IArea {\n\n private readonly planes: Record<number, Plane> = {};\n private readonly area: MapData.Area;\n private readonly exits: Map<string, IExit> = new Map();\n private version = 0;\n\n constructor(area: MapData.Area) {\n this.area = area;\n this.planes = this.createPlanes();\n this.createExits();\n }\n\n getAreaName() {\n return this.area.areaName\n }\n\n getAreaId() {\n return parseInt(this.area.areaId)\n }\n\n getVersion() {\n return this.version;\n }\n\n protected markDirty() {\n this.version++;\n }\n\n getPlane(zIndex: number) {\n return this.planes[zIndex];\n }\n\n getPlanes() {\n return Object.values(this.planes);\n }\n\n getZLevels(): number[] {\n return Object.keys(this.planes).map(Number).sort((a, b) => a - b);\n }\n\n getRooms() {\n return this.area.rooms\n }\n\n getFullBounds(): { minX: number; maxX: number; minY: number; maxY: number } {\n return this.getPlanes().reduce(\n (acc, plane) => {\n const b = plane.getBounds();\n return {\n minX: Math.min(acc.minX, b.minX),\n maxX: Math.max(acc.maxX, b.maxX),\n minY: Math.min(acc.minY, b.minY),\n maxY: Math.max(acc.maxY, b.maxY),\n };\n },\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }\n );\n }\n\n getLinkExits(zIndex: number) {\n return Array.from(this.exits.values()).filter(e => e.zIndex.includes(zIndex));\n }\n\n private createPlanes() {\n const grouped = this.area.rooms.reduce<Record<number, MapData.Room[]>>((acc, room) => {\n if (!acc[room.z]) {\n acc[room.z] = [];\n }\n // @ts-ignore\n acc[room.z].push(room);\n return acc;\n }, {});\n return Object.entries(grouped).reduce(\n (acc, [z, rooms]) => {\n acc[+z] = new Plane(rooms, this.area.labels.filter(label => label.Z === +z));\n return acc;\n },\n {} as Record<number, Plane>\n );\n }\n\n private static readonly oppositeDir: Partial<Record<MapData.direction, MapData.direction>> = {\n \"north\": \"south\", \"south\": \"north\",\n \"east\": \"west\", \"west\": \"east\",\n \"northeast\": \"southwest\", \"southwest\": \"northeast\",\n \"northwest\": \"southeast\", \"southeast\": \"northwest\",\n \"up\": \"down\", \"down\": \"up\",\n \"in\": \"out\", \"out\": \"in\",\n };\n\n private createExits() {\n type HalfExit = { origin: number, target: number, z: number, dir: MapData.direction };\n const halfExitsByPair = new Map<string, HalfExit[]>();\n\n this.area.rooms.forEach(room => {\n Object.entries(room.exits).forEach(([direction, targetRoomId]) => {\n if (room.id === targetRoomId) return;\n const a = Math.min(room.id, targetRoomId);\n const b = Math.max(room.id, targetRoomId);\n const key = `${a}-${b}`;\n if (!halfExitsByPair.has(key)) halfExitsByPair.set(key, []);\n halfExitsByPair.get(key)!.push({\n origin: room.id, target: targetRoomId, z: room.z, dir: direction as MapData.direction\n });\n });\n });\n\n for (const [pairKey, halves] of halfExitsByPair) {\n const [aStr, bStr] = pairKey.split('-');\n const a = parseInt(aStr), b = parseInt(bStr);\n\n const aSide = halves.filter(h => h.origin === a);\n const bSide = halves.filter(h => h.origin === b);\n\n const usedB = new Set<number>();\n\n for (const aHalf of aSide) {\n let bestIdx = -1;\n for (let i = 0; i < bSide.length; i++) {\n if (usedB.has(i)) continue;\n if (bSide[i].dir === Area.oppositeDir[aHalf.dir]) {\n bestIdx = i;\n break;\n }\n }\n\n if (bestIdx !== -1) {\n usedB.add(bestIdx);\n const bHalf = bSide[bestIdx];\n this.exits.set(`${pairKey}-${aHalf.dir}`, {\n a, b, aDir: aHalf.dir, bDir: bHalf.dir, zIndex: [aHalf.z, bHalf.z]\n });\n } else {\n this.exits.set(`${pairKey}-a:${aHalf.dir}`, {\n a, b, aDir: aHalf.dir, zIndex: [aHalf.z]\n });\n }\n }\n\n for (let i = 0; i < bSide.length; i++) {\n if (!usedB.has(i)) {\n const bHalf = bSide[i];\n this.exits.set(`${pairKey}-b:${bHalf.dir}`, {\n a, b, bDir: bHalf.dir, zIndex: [bHalf.z]\n });\n }\n }\n }\n }\n\n}","import Area, {IArea} from \"./Area\";\n\n/**\n * Public, renderer-facing surface for map data. Everything the renderer\n * (and other library consumers) call on `MapReader` is on this interface —\n * private state and internal helpers are not.\n *\n * Downstream apps with their own room/area store can implement `IMapReader`\n * directly (no need to subclass {@link MapReader}) and hand the result to\n * {@link MapRenderer}. Visibility filtering (exploration, scope overlays,\n * etc.) lives on the renderer's lens — it is intentionally not on this\n * interface.\n */\nexport interface IMapReader {\n getArea(areaId: number): IArea;\n getAreas(): IArea[];\n getRooms(): MapData.Room[];\n getRoom(roomId: number): MapData.Room;\n /** Returns the env's `rgb(r,g,b)` string, or a default colour if the env id is unknown. */\n getColorValue(envId: number): string;\n /** Returns a contrasting symbol colour for the env, optionally with the given alpha. */\n getSymbolColor(envId: number, opacity?: number): string;\n}\n\ninterface Color {\n rgb: number[];\n rgbValue: string;\n symbolColor: number[];\n symbolColorValue: string,\n}\n\nconst defaultColor: Color = {\n rgb: [114, 1, 0],\n rgbValue: 'rgb(114, 1, 0)',\n symbolColor: [225, 225, 225],\n symbolColorValue: 'rgb(225,225,225)'\n}\n\nfunction calculateLuminance(rgb: number[]) {\n const rn = rgb[0] / 255;\n const gn = rgb[1] / 255;\n const bn = rgb[2] / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n\n return (max + min) / 2;\n}\n\nexport default class MapReader implements IMapReader {\n\n private rooms: Record<number, MapData.Room> = {};\n private areas: Record<number, Area> = {};\n private colors: Record<number, Color> = {};\n\n constructor(map: MapData.Map, envs: MapData.Env[]) {\n map.forEach(area => {\n const clonedArea: MapData.Area = {\n ...area,\n rooms: area.rooms.map(room => ({ ...room, y: -room.y })),\n };\n clonedArea.rooms.forEach(room => {\n this.rooms[room.id] = room;\n });\n const areaId = parseInt(area.areaId);\n this.areas[areaId] = new Area(clonedArea);\n })\n this.colors = envs.reduce((acc, c) => ({\n ...acc,\n [c.envId]: {\n rgb: c.colors,\n rgbValue: `rgb(${c.colors.join(',')})`,\n symbolColor: calculateLuminance(c.colors) > 0.41 ? [25, 25, 25] : [225, 255, 255],\n symbolColorValue: calculateLuminance(c.colors) > 0.41 ? 'rgb(25,25,25)' : 'rgb(225,255,255)'\n }\n }), {});\n }\n\n getArea(areaId: number) {\n return this.areas[areaId];\n }\n\n getAreas() {\n return Object.values(this.areas);\n }\n\n getRooms() {\n return Object.values(this.rooms);\n }\n\n getRoom(roomId: number) {\n return this.rooms[roomId];\n }\n\n getColorValue(envId: number): string {\n return this.colors[envId]?.rgbValue ?? defaultColor.rgbValue;\n }\n\n getSymbolColor(envId: number, opacity?: number): string {\n const color = this.colors[envId]?.symbolColor ?? defaultColor.symbolColor;\n const normalizedOpacity = Math.min(Math.max(opacity ?? 1, 0), 1);\n const value = color.join(',');\n if (normalizedOpacity != 1) {\n return `rgba(${value}, ${normalizedOpacity})`;\n }\n return `rgba(${value})`;\n }\n\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MapReader-Brzg-X7T.js","names":[],"sources":["../src/reader/Plane.ts","../src/reader/Area.ts","../src/reader/MapReader.ts"],"sourcesContent":["/**\n * Public, renderer-facing surface of a z-level slice of an area.\n *\n * The renderer (and other library consumers) only depend on this interface;\n * the concrete {@link Plane} class is one implementation of it. Downstream\n * apps that want to drive the renderer from their own data model can hand it\n * any `IPlane`-shaped object (typically returned from an {@link IArea}).\n */\nexport interface IPlane {\n getRooms(): MapData.Room[];\n getLabels(): MapData.Label[];\n getBounds(): { minX: number; maxX: number; minY: number; maxY: number };\n}\n\nexport default class Plane implements IPlane {\n\n private readonly bounds: { minX: number, maxX: number, minY: number, maxY: number }\n private readonly rooms: MapData.Room[] = [];\n private readonly labels: MapData.Label[] = [];\n\n constructor(rooms: MapData.Room[], labels: MapData.Label[]) {\n this.rooms = rooms;\n this.labels = labels;\n this.bounds = this.createBounds();\n }\n\n getRooms() {\n return this.rooms;\n }\n\n getLabels() {\n return this.labels;\n }\n\n getBounds() {\n return this.bounds;\n }\n\n private createBounds() {\n const b = this.rooms.reduce(\n (acc, r) => ({\n minX: Math.min(acc.minX, r.x),\n maxX: Math.max(acc.maxX, r.x),\n minY: Math.min(acc.minY, r.y),\n maxY: Math.max(acc.maxY, r.y),\n }),\n {\n minX: Number.POSITIVE_INFINITY,\n maxX: Number.NEGATIVE_INFINITY,\n minY: Number.POSITIVE_INFINITY,\n maxY: Number.NEGATIVE_INFINITY,\n }\n );\n for (const label of this.labels) {\n const lx = label.X;\n const ly = -label.Y;\n b.minX = Math.min(b.minX, lx);\n b.maxX = Math.max(b.maxX, lx + label.Width);\n b.minY = Math.min(b.minY, ly);\n b.maxY = Math.max(b.maxY, ly + label.Height);\n }\n return b;\n }\n\n}\n","import Plane, {IPlane} from \"./Plane\";\n\nimport IExit from \"./Exit\";\n\n/**\n * Public, renderer-facing surface of an area. The renderer never inspects\n * private state of {@link Area}; it talks to this interface only. Custom data\n * models (e.g. an app that owns its own room store) can satisfy `IArea` and\n * hand the result to {@link MapRenderer} without subclassing.\n *\n * Mutability lives on the concrete implementation: {@link Area.markDirty} is\n * `protected`, kept off the public interface. Consumers signal \"redraw me\" by\n * bumping {@link getVersion}; the renderer reads the version to decide\n * whether to rebuild.\n */\nexport interface IArea {\n getAreaName(): string;\n getAreaId(): number;\n /**\n * Monotonically increasing version. The renderer compares the value it\n * cached on the last build against the current value to decide whether\n * the area needs a rebuild.\n */\n getVersion(): number;\n getPlane(zIndex: number): IPlane;\n getPlanes(): IPlane[];\n getZLevels(): number[];\n getRooms(): MapData.Room[];\n getFullBounds(): { minX: number; maxX: number; minY: number; maxY: number };\n /**\n * Inter-room exits drawn on the given z-level. Each exit is bidirectional\n * with optional one-way fallback; see {@link IExit}.\n */\n getLinkExits(zIndex: number): IExit[];\n}\n\nexport default class Area implements IArea {\n\n private readonly planes: Record<number, Plane> = {};\n private readonly area: MapData.Area;\n private readonly exits: Map<string, IExit> = new Map();\n private version = 0;\n\n constructor(area: MapData.Area) {\n this.area = area;\n this.planes = this.createPlanes();\n this.createExits();\n }\n\n getAreaName() {\n return this.area.areaName\n }\n\n getAreaId() {\n return parseInt(this.area.areaId)\n }\n\n getVersion() {\n return this.version;\n }\n\n protected markDirty() {\n this.version++;\n }\n\n getPlane(zIndex: number) {\n return this.planes[zIndex];\n }\n\n getPlanes() {\n return Object.values(this.planes);\n }\n\n getZLevels(): number[] {\n return Object.keys(this.planes).map(Number).sort((a, b) => a - b);\n }\n\n getRooms() {\n return this.area.rooms\n }\n\n getFullBounds(): { minX: number; maxX: number; minY: number; maxY: number } {\n return this.getPlanes().reduce(\n (acc, plane) => {\n const b = plane.getBounds();\n return {\n minX: Math.min(acc.minX, b.minX),\n maxX: Math.max(acc.maxX, b.maxX),\n minY: Math.min(acc.minY, b.minY),\n maxY: Math.max(acc.maxY, b.maxY),\n };\n },\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }\n );\n }\n\n getLinkExits(zIndex: number) {\n return Array.from(this.exits.values()).filter(e => e.zIndex.includes(zIndex));\n }\n\n private createPlanes() {\n const grouped = this.area.rooms.reduce<Record<number, MapData.Room[]>>((acc, room) => {\n if (!acc[room.z]) {\n acc[room.z] = [];\n }\n // @ts-ignore\n acc[room.z].push(room);\n return acc;\n }, {});\n return Object.entries(grouped).reduce(\n (acc, [z, rooms]) => {\n acc[+z] = new Plane(rooms, this.area.labels.filter(label => label.Z === +z));\n return acc;\n },\n {} as Record<number, Plane>\n );\n }\n\n private static readonly oppositeDir: Partial<Record<MapData.direction, MapData.direction>> = {\n \"north\": \"south\", \"south\": \"north\",\n \"east\": \"west\", \"west\": \"east\",\n \"northeast\": \"southwest\", \"southwest\": \"northeast\",\n \"northwest\": \"southeast\", \"southeast\": \"northwest\",\n \"up\": \"down\", \"down\": \"up\",\n \"in\": \"out\", \"out\": \"in\",\n };\n\n private createExits() {\n type HalfExit = { origin: number, target: number, z: number, dir: MapData.direction };\n const halfExitsByPair = new Map<string, HalfExit[]>();\n\n this.area.rooms.forEach(room => {\n Object.entries(room.exits).forEach(([direction, targetRoomId]) => {\n if (room.id === targetRoomId) return;\n const a = Math.min(room.id, targetRoomId);\n const b = Math.max(room.id, targetRoomId);\n const key = `${a}-${b}`;\n if (!halfExitsByPair.has(key)) halfExitsByPair.set(key, []);\n halfExitsByPair.get(key)!.push({\n origin: room.id, target: targetRoomId, z: room.z, dir: direction as MapData.direction\n });\n });\n });\n\n for (const [pairKey, halves] of halfExitsByPair) {\n const [aStr, bStr] = pairKey.split('-');\n const a = parseInt(aStr), b = parseInt(bStr);\n\n const aSide = halves.filter(h => h.origin === a);\n const bSide = halves.filter(h => h.origin === b);\n\n const usedB = new Set<number>();\n\n for (const aHalf of aSide) {\n let bestIdx = -1;\n for (let i = 0; i < bSide.length; i++) {\n if (usedB.has(i)) continue;\n if (bSide[i].dir === Area.oppositeDir[aHalf.dir]) {\n bestIdx = i;\n break;\n }\n }\n\n if (bestIdx !== -1) {\n usedB.add(bestIdx);\n const bHalf = bSide[bestIdx];\n this.exits.set(`${pairKey}-${aHalf.dir}`, {\n a, b, aDir: aHalf.dir, bDir: bHalf.dir, zIndex: [aHalf.z, bHalf.z]\n });\n } else {\n this.exits.set(`${pairKey}-a:${aHalf.dir}`, {\n a, b, aDir: aHalf.dir, zIndex: [aHalf.z]\n });\n }\n }\n\n for (let i = 0; i < bSide.length; i++) {\n if (!usedB.has(i)) {\n const bHalf = bSide[i];\n this.exits.set(`${pairKey}-b:${bHalf.dir}`, {\n a, b, bDir: bHalf.dir, zIndex: [bHalf.z]\n });\n }\n }\n }\n }\n\n}","import Area, {IArea} from \"./Area\";\n\n/**\n * Public, renderer-facing surface for map data. Everything the renderer\n * (and other library consumers) call on `MapReader` is on this interface —\n * private state and internal helpers are not.\n *\n * Downstream apps with their own room/area store can implement `IMapReader`\n * directly (no need to subclass {@link MapReader}) and hand the result to\n * {@link MapRenderer}. Visibility filtering (exploration, scope overlays,\n * etc.) lives on the renderer's lens — it is intentionally not on this\n * interface.\n */\nexport interface IMapReader {\n getArea(areaId: number): IArea;\n getAreas(): IArea[];\n getRooms(): MapData.Room[];\n getRoom(roomId: number): MapData.Room;\n /** Returns the env's `rgb(r,g,b)` string, or a default colour if the env id is unknown. */\n getColorValue(envId: number): string;\n /** Returns a contrasting symbol colour for the env, optionally with the given alpha. */\n getSymbolColor(envId: number, opacity?: number): string;\n}\n\ninterface Color {\n rgb: number[];\n rgbValue: string;\n symbolColor: number[];\n symbolColorValue: string,\n}\n\nconst defaultColor: Color = {\n rgb: [114, 1, 0],\n rgbValue: 'rgb(114, 1, 0)',\n symbolColor: [225, 225, 225],\n symbolColorValue: 'rgb(225,225,225)'\n}\n\nfunction calculateLuminance(rgb: number[]) {\n const rn = rgb[0] / 255;\n const gn = rgb[1] / 255;\n const bn = rgb[2] / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n\n return (max + min) / 2;\n}\n\nexport default class MapReader implements IMapReader {\n\n private rooms: Record<number, MapData.Room> = {};\n private areas: Record<number, Area> = {};\n private colors: Record<number, Color> = {};\n\n constructor(map: MapData.Map, envs: MapData.Env[]) {\n map.forEach(area => {\n const clonedArea: MapData.Area = {\n ...area,\n rooms: area.rooms.map(room => ({ ...room, y: -room.y })),\n };\n clonedArea.rooms.forEach(room => {\n this.rooms[room.id] = room;\n });\n const areaId = parseInt(area.areaId);\n this.areas[areaId] = new Area(clonedArea);\n })\n this.colors = envs.reduce((acc, c) => ({\n ...acc,\n [c.envId]: {\n rgb: c.colors,\n rgbValue: `rgb(${c.colors.join(',')})`,\n symbolColor: calculateLuminance(c.colors) > 0.41 ? [25, 25, 25] : [225, 255, 255],\n symbolColorValue: calculateLuminance(c.colors) > 0.41 ? 'rgb(25,25,25)' : 'rgb(225,255,255)'\n }\n }), {});\n }\n\n getArea(areaId: number) {\n return this.areas[areaId];\n }\n\n getAreas() {\n return Object.values(this.areas);\n }\n\n getRooms() {\n return Object.values(this.rooms);\n }\n\n getRoom(roomId: number) {\n return this.rooms[roomId];\n }\n\n getColorValue(envId: number): string {\n return this.colors[envId]?.rgbValue ?? defaultColor.rgbValue;\n }\n\n getSymbolColor(envId: number, opacity?: number): string {\n const color = this.colors[envId]?.symbolColor ?? defaultColor.symbolColor;\n const normalizedOpacity = Math.min(Math.max(opacity ?? 1, 0), 1);\n const value = color.join(',');\n if (normalizedOpacity != 1) {\n return `rgba(${value}, ${normalizedOpacity})`;\n }\n return `rgba(${value})`;\n }\n\n}\n"],"mappings":";AAcA,IAAqB,IAArB,MAA6C;CAMzC,YAAY,GAAuB,GAAyB;AAGxD,eANqC,EAAE,gBACA,EAAE,EAGzC,KAAK,QAAQ,GACb,KAAK,SAAS,GACd,KAAK,SAAS,KAAK,cAAc;;CAGrC,WAAW;AACP,SAAO,KAAK;;CAGhB,YAAY;AACR,SAAO,KAAK;;CAGhB,YAAY;AACR,SAAO,KAAK;;CAGhB,eAAuB;EACnB,IAAM,IAAI,KAAK,MAAM,QAChB,GAAK,OAAO;GACT,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,EAAE;GAC7B,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,EAAE;GAC7B,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,EAAE;GAC7B,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,EAAE;GAChC,GACD;GACI,MAAM;GACN,MAAM;GACN,MAAM;GACN,MAAM;GACT,CACJ;AACD,OAAK,IAAM,KAAS,KAAK,QAAQ;GAC7B,IAAM,IAAK,EAAM,GACX,IAAK,CAAC,EAAM;AAIlB,GAHA,EAAE,OAAO,KAAK,IAAI,EAAE,MAAM,EAAG,EAC7B,EAAE,OAAO,KAAK,IAAI,EAAE,MAAM,IAAK,EAAM,MAAM,EAC3C,EAAE,OAAO,KAAK,IAAI,EAAE,MAAM,EAAG,EAC7B,EAAE,OAAO,KAAK,IAAI,EAAE,MAAM,IAAK,EAAM,OAAO;;AAEhD,SAAO;;GCzBM,IAArB,MAAqB,EAAsB;CAOvC,YAAY,GAAoB;AAG5B,gBAR6C,EAAE,+BAEN,IAAI,KAAK,iBACpC,GAGd,KAAK,OAAO,GACZ,KAAK,SAAS,KAAK,cAAc,EACjC,KAAK,aAAa;;CAGtB,cAAc;AACV,SAAO,KAAK,KAAK;;CAGrB,YAAY;AACR,SAAO,SAAS,KAAK,KAAK,OAAO;;CAGrC,aAAa;AACT,SAAO,KAAK;;CAGhB,YAAsB;AAClB,OAAK;;CAGT,SAAS,GAAgB;AACrB,SAAO,KAAK,OAAO;;CAGvB,YAAY;AACR,SAAO,OAAO,OAAO,KAAK,OAAO;;CAGrC,aAAuB;AACnB,SAAO,OAAO,KAAK,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;;CAGrE,WAAW;AACP,SAAO,KAAK,KAAK;;CAGrB,gBAA4E;AACxE,SAAO,KAAK,WAAW,CAAC,QACnB,GAAK,MAAU;GACZ,IAAM,IAAI,EAAM,WAAW;AAC3B,UAAO;IACH,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,KAAK;IAChC,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,KAAK;IAChC,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,KAAK;IAChC,MAAM,KAAK,IAAI,EAAI,MAAM,EAAE,KAAK;IACnC;KAEL;GAAE,MAAM;GAAU,MAAM;GAAW,MAAM;GAAU,MAAM;GAAW,CACvE;;CAGL,aAAa,GAAgB;AACzB,SAAO,MAAM,KAAK,KAAK,MAAM,QAAQ,CAAC,CAAC,QAAO,MAAK,EAAE,OAAO,SAAS,EAAO,CAAC;;CAGjF,eAAuB;EACnB,IAAM,IAAU,KAAK,KAAK,MAAM,QAAwC,GAAK,OACpE,EAAI,EAAK,OACV,EAAI,EAAK,KAAK,EAAE,GAGpB,EAAI,EAAK,GAAG,KAAK,EAAK,EACf,IACR,EAAE,CAAC;AACN,SAAO,OAAO,QAAQ,EAAQ,CAAC,QAC1B,GAAK,CAAC,GAAG,QACN,EAAI,CAAC,KAAK,IAAI,EAAM,GAAO,KAAK,KAAK,OAAO,QAAO,MAAS,EAAM,MAAM,CAAC,EAAE,CAAC,EACrE,IAEX,EAAE,CACL;;;qBAGwF;GACzF,OAAS;GAAS,OAAS;GAC3B,MAAQ;GAAQ,MAAQ;GACxB,WAAa;GAAa,WAAa;GACvC,WAAa;GAAa,WAAa;GACvC,IAAM;GAAQ,MAAQ;GACtB,IAAM;GAAO,KAAO;GACvB;;CAED,cAAsB;EAElB,IAAM,oBAAkB,IAAI,KAAyB;AAErD,OAAK,KAAK,MAAM,SAAQ,MAAQ;AAC5B,UAAO,QAAQ,EAAK,MAAM,CAAC,SAAS,CAAC,GAAW,OAAkB;AAC9D,QAAI,EAAK,OAAO,EAAc;IAG9B,IAAM,IAAM,GAFF,KAAK,IAAI,EAAK,IAAI,EAAa,CAExB,GADP,KAAK,IAAI,EAAK,IAAI,EAAa;AAGzC,IADK,EAAgB,IAAI,EAAI,IAAE,EAAgB,IAAI,GAAK,EAAE,CAAC,EAC3D,EAAgB,IAAI,EAAI,CAAE,KAAK;KAC3B,QAAQ,EAAK;KAAI,QAAQ;KAAc,GAAG,EAAK;KAAG,KAAK;KAC1D,CAAC;KACJ;IACJ;AAEF,OAAK,IAAM,CAAC,GAAS,MAAW,GAAiB;GAC7C,IAAM,CAAC,GAAM,KAAQ,EAAQ,MAAM,IAAI,EACjC,IAAI,SAAS,EAAK,EAAE,IAAI,SAAS,EAAK,EAEtC,IAAQ,EAAO,QAAO,MAAK,EAAE,WAAW,EAAE,EAC1C,IAAQ,EAAO,QAAO,MAAK,EAAE,WAAW,EAAE,EAE1C,oBAAQ,IAAI,KAAa;AAE/B,QAAK,IAAM,KAAS,GAAO;IACvB,IAAI,IAAU;AACd,SAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,IAC1B,QAAM,IAAI,EAAE,IACZ,EAAM,GAAG,QAAQ,EAAK,YAAY,EAAM,MAAM;AAC9C,SAAU;AACV;;AAIR,QAAI,MAAY,IAAI;AAChB,OAAM,IAAI,EAAQ;KAClB,IAAM,IAAQ,EAAM;AACpB,UAAK,MAAM,IAAI,GAAG,EAAQ,GAAG,EAAM,OAAO;MACtC;MAAG;MAAG,MAAM,EAAM;MAAK,MAAM,EAAM;MAAK,QAAQ,CAAC,EAAM,GAAG,EAAM,EAAE;MACrE,CAAC;UAEF,MAAK,MAAM,IAAI,GAAG,EAAQ,KAAK,EAAM,OAAO;KACxC;KAAG;KAAG,MAAM,EAAM;KAAK,QAAQ,CAAC,EAAM,EAAE;KAC3C,CAAC;;AAIV,QAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,IAC9B,KAAI,CAAC,EAAM,IAAI,EAAE,EAAE;IACf,IAAM,IAAQ,EAAM;AACpB,SAAK,MAAM,IAAI,GAAG,EAAQ,KAAK,EAAM,OAAO;KACxC;KAAG;KAAG,MAAM,EAAM;KAAK,QAAQ,CAAC,EAAM,EAAE;KAC3C,CAAC;;;;GCtJhB,IAAsB;CACxB,KAAK;EAAC;EAAK;EAAG;EAAE;CAChB,UAAU;CACV,aAAa;EAAC;EAAK;EAAK;EAAI;CAC5B,kBAAkB;CACrB;AAED,SAAS,EAAmB,GAAe;CACvC,IAAM,IAAK,EAAI,KAAK,KACd,IAAK,EAAI,KAAK,KACd,IAAK,EAAI,KAAK;AAKpB,SAHY,KAAK,IAAI,GAAI,GAAI,EAAG,GACpB,KAAK,IAAI,GAAI,GAAI,EAAG,IAEX;;AAGzB,IAAqB,IAArB,MAAqD;CAMjD,YAAY,GAAkB,GAAqB;AAY/C,eAhB0C,EAAE,eACV,EAAE,gBACA,EAAE,EAGtC,EAAI,SAAQ,MAAQ;GAChB,IAAM,IAA2B;IAC7B,GAAG;IACH,OAAO,EAAK,MAAM,KAAI,OAAS;KAAE,GAAG;KAAM,GAAG,CAAC,EAAK;KAAG,EAAE;IAC3D;AACD,KAAW,MAAM,SAAQ,MAAQ;AAC7B,SAAK,MAAM,EAAK,MAAM;KACxB;GACF,IAAM,IAAS,SAAS,EAAK,OAAO;AACpC,QAAK,MAAM,KAAU,IAAI,EAAK,EAAW;IAC3C,EACF,KAAK,SAAS,EAAK,QAAQ,GAAK,OAAO;GACnC,GAAG;IACF,EAAE,QAAQ;IACP,KAAK,EAAE;IACP,UAAU,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,aAAa,EAAmB,EAAE,OAAO,GAAG,MAAO;KAAC;KAAI;KAAI;KAAG,GAAG;KAAC;KAAK;KAAK;KAAI;IACjF,kBAAkB,EAAmB,EAAE,OAAO,GAAG,MAAO,kBAAkB;IAC7E;GACJ,GAAG,EAAE,CAAC;;CAGX,QAAQ,GAAgB;AACpB,SAAO,KAAK,MAAM;;CAGtB,WAAW;AACP,SAAO,OAAO,OAAO,KAAK,MAAM;;CAGpC,WAAW;AACP,SAAO,OAAO,OAAO,KAAK,MAAM;;CAGpC,QAAQ,GAAgB;AACpB,SAAO,KAAK,MAAM;;CAGtB,cAAc,GAAuB;AACjC,SAAO,KAAK,OAAO,IAAQ,YAAY,EAAa;;CAGxD,eAAe,GAAe,GAA0B;EACpD,IAAM,IAAQ,KAAK,OAAO,IAAQ,eAAe,EAAa,aACxD,IAAoB,KAAK,IAAI,KAAK,IAAI,KAAW,GAAG,EAAE,EAAE,EAAE,EAC1D,IAAQ,EAAM,KAAK,IAAI;AAI7B,SAHI,KAAqB,IAGlB,QAAQ,EAAM,KAFV,QAAQ,EAAM,IAAI,EAAkB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-DtQws85m.js","names":[],"sources":["../../src/scene/elements/GridLayout.ts","../../src/scene/Shape.ts","../../src/draw/DrawCommandBuilder.ts","../../src/render/canvasGradient.ts","../../src/render/CanvasRenderer.ts","../../src/rendering/offscreen/serializeTransform.ts","../../src/rendering/offscreen/renderFrame.ts","../../src/rendering/offscreen/worker.ts"],"sourcesContent":["import type {Settings, ViewportBounds} from \"../../types/Settings\";\nimport type {LineShape, Shape} from \"../Shape\";\n\nexport interface GridLayoutOptions {\n /** Inverse coordinate transform (rendered → Cartesian) for warped pipelines. */\n inverseTransform?: (x: number, y: number) => {x: number; y: number};\n}\n\n/**\n * Pure layout for the background grid. Returns the line shapes that span the\n * given viewport bounds with one extra grid step of buffer on every edge.\n *\n * Returns an empty array when grids are disabled. Caching is the caller's\n * responsibility (the previous renderer cached by snapped bounds).\n */\nexport function layoutGrid(\n viewportBounds: ViewportBounds,\n settings: Settings,\n options: GridLayoutOptions = {},\n): LineShape[] {\n if (!settings.gridEnabled) return [];\n\n const inv = options.inverseTransform ?? ((x, y) => ({x, y}));\n const {minX: vMinX, maxX: vMaxX, minY: vMinY, maxY: vMaxY} = viewportBounds;\n const c1 = inv(vMinX, vMinY);\n const c2 = inv(vMaxX, vMinY);\n const c3 = inv(vMaxX, vMaxY);\n const c4 = inv(vMinX, vMaxY);\n const cartMinX = Math.min(c1.x, c2.x, c3.x, c4.x);\n const cartMaxX = Math.max(c1.x, c2.x, c3.x, c4.x);\n const cartMinY = Math.min(c1.y, c2.y, c3.y, c4.y);\n const cartMaxY = Math.max(c1.y, c2.y, c3.y, c4.y);\n\n const buffer = settings.gridSize * 2;\n const left = Math.floor((cartMinX - buffer) / settings.gridSize) * settings.gridSize;\n const right = Math.ceil((cartMaxX + buffer) / settings.gridSize) * settings.gridSize;\n const top = Math.floor((cartMinY - buffer) / settings.gridSize) * settings.gridSize;\n const bottom = Math.ceil((cartMaxY + buffer) / settings.gridSize) * settings.gridSize;\n\n const lines: LineShape[] = [];\n const paint = {stroke: settings.gridColor, strokeWidth: settings.gridLineWidth};\n\n for (let x = left; x <= right; x += settings.gridSize) {\n lines.push({\n type: \"line\",\n points: [x, top, x, bottom],\n paint,\n grid: true,\n layer: \"grid\",\n });\n }\n for (let y = top; y <= bottom; y += settings.gridSize) {\n lines.push({\n type: \"line\",\n points: [left, y, right, y],\n paint,\n grid: true,\n layer: \"grid\",\n });\n }\n return lines;\n}\n\n/** Snapped bounds — for cache-key comparison by callers. */\nexport function gridSnappedBounds(\n viewportBounds: ViewportBounds,\n settings: Settings,\n options: GridLayoutOptions = {},\n): {left: number; right: number; top: number; bottom: number} | null {\n if (!settings.gridEnabled) return null;\n\n const inv = options.inverseTransform ?? ((x, y) => ({x, y}));\n const {minX: vMinX, maxX: vMaxX, minY: vMinY, maxY: vMaxY} = viewportBounds;\n const c1 = inv(vMinX, vMinY);\n const c2 = inv(vMaxX, vMinY);\n const c3 = inv(vMaxX, vMaxY);\n const c4 = inv(vMinX, vMaxY);\n const cartMinX = Math.min(c1.x, c2.x, c3.x, c4.x);\n const cartMaxX = Math.max(c1.x, c2.x, c3.x, c4.x);\n const cartMinY = Math.min(c1.y, c2.y, c3.y, c4.y);\n const cartMaxY = Math.max(c1.y, c2.y, c3.y, c4.y);\n\n const buffer = settings.gridSize * 2;\n return {\n left: Math.floor((cartMinX - buffer) / settings.gridSize) * settings.gridSize,\n right: Math.ceil((cartMaxX + buffer) / settings.gridSize) * settings.gridSize,\n top: Math.floor((cartMinY - buffer) / settings.gridSize) * settings.gridSize,\n bottom: Math.ceil((cartMaxY + buffer) / settings.gridSize) * settings.gridSize,\n };\n}\n","/**\n * SceneIR — pure data shapes that {@link ScenePipeline} produces.\n *\n * Shapes carry world-space coordinates and engine-agnostic paint information.\n * They are consumed by:\n * - {@link CullingManager} (visibility queries against a camera viewport)\n * - {@link HitTester} (point→shape lookup)\n * - {@link buildDrawCommands} (translation to engine {@link DrawCommand}s)\n *\n * Shapes know nothing about Konva, SVG, or Canvas2D.\n */\n\n/** Logical layer for stacking. Mapped to engine-specific layers by renderers. */\nexport type LayerId =\n | \"grid\"\n | \"link\"\n | \"room\"\n | \"position\"\n | \"overlay\"\n | \"top\";\n\n/** One gradient stop. `offset` is in 0..1; `color` is any CSS colour string. */\nexport interface GradientStop {\n offset: number;\n color: string;\n}\n\n/**\n * Linear gradient between two points in the shape's LOCAL coordinate space\n * (same frame as shape.x/y). Stop colours are sampled along the line\n * (x0,y0) → (x1,y1).\n */\nexport interface LinearGradient {\n type: \"linear\";\n x0: number;\n y0: number;\n x1: number;\n y1: number;\n stops: GradientStop[];\n}\n\n/**\n * Radial gradient in the shape's LOCAL coordinate space. The end circle is\n * (cx, cy, r); the optional start circle is (fx, fy, fr) — defaults to a\n * zero-radius circle at (cx, cy) when omitted.\n */\nexport interface RadialGradient {\n type: \"radial\";\n cx: number;\n cy: number;\n r: number;\n fx?: number;\n fy?: number;\n fr?: number;\n stops: GradientStop[];\n}\n\n/** A fill is either a flat colour string or a gradient. */\nexport type FillStyle = string | LinearGradient | RadialGradient;\n\n/** Engine-agnostic paint description. */\nexport interface Paint {\n /**\n * Colour string (`#rrggbb`, `rgb(...)`, named colour) or a gradient.\n * Gradient coords are in the same local frame as the shape's geometry.\n */\n fill?: FillStyle;\n stroke?: string;\n strokeWidth?: number;\n dash?: number[];\n dashEnabled?: boolean;\n /** 0..1 multiplier on fill+stroke. */\n alpha?: number;\n}\n\n/** True when `fill` is a gradient object rather than a colour string. */\nexport function isGradientFill(fill: FillStyle | undefined): fill is LinearGradient | RadialGradient {\n return typeof fill === \"object\" && fill !== null;\n}\n\n/**\n * Resolve a paint's effective dash pattern. Returns the dash only when it is\n * present AND not explicitly disabled (`dashEnabled === false` blanks it).\n * Renderers call this so every shape kind — rect, circle, line — honours\n * `dashEnabled` identically.\n */\nexport function resolveDash(\n dash: number[] | undefined,\n dashEnabled: boolean | undefined,\n): number[] | undefined {\n return (dashEnabled !== false && dash) ? dash : undefined;\n}\n\n/**\n * Apply a camera-style affine (translate by world origin, scale uniformly,\n * translate by render offset) to a fill. Strings pass through unchanged.\n * Gradient stops are colour-only and never transformed.\n */\nexport function transformFill(\n fill: FillStyle | undefined,\n worldX: number,\n worldY: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n): FillStyle | undefined {\n if (!isGradientFill(fill)) return fill;\n if (fill.type === \"linear\") {\n return {\n type: \"linear\",\n x0: (worldX + fill.x0) * scale + offsetX,\n y0: (worldY + fill.y0) * scale + offsetY,\n x1: (worldX + fill.x1) * scale + offsetX,\n y1: (worldY + fill.y1) * scale + offsetY,\n stops: fill.stops,\n };\n }\n return {\n type: \"radial\",\n cx: (worldX + fill.cx) * scale + offsetX,\n cy: (worldY + fill.cy) * scale + offsetY,\n r: fill.r * scale,\n fx: fill.fx !== undefined ? (worldX + fill.fx) * scale + offsetX : undefined,\n fy: fill.fy !== undefined ? (worldY + fill.fy) * scale + offsetY : undefined,\n fr: fill.fr !== undefined ? fill.fr * scale : undefined,\n stops: fill.stops,\n };\n}\n\n/** Hit-test annotation. Set on shapes that should be pickable. */\nexport interface HitInfo {\n /** What the shape represents at the model layer. */\n kind: \"room\" | \"exit\" | \"specialExit\" | \"stub\" | \"label\" | \"areaExit\" | string;\n /** Identifier of the owning model entity (room id, exit id, …). */\n id?: number | string;\n /** Free-form payload returned to callers from {@link HitTester}. */\n payload?: unknown;\n /**\n * Multiplier on the current `roomSize` for the maximum pick distance.\n * Smaller = stricter (used for thin geometry like exits and stubs).\n * Defaults to a per-kind value when omitted (rooms 1.0, exits 0.35, stubs 0.3, …).\n */\n margin?: number;\n /**\n * Tiebreaker when multiple hits are within range. Higher = on top.\n * Defaults to a per-kind value when omitted (rooms over exits over stubs, …).\n */\n priority?: number;\n}\n\n/** Common fields on every shape. */\nexport interface ShapeBase {\n /** Logical layer; defaults to \"room\" when omitted. */\n layer?: LayerId;\n /** Set on shapes that participate in hit testing. */\n hit?: HitInfo;\n /**\n * If true, the shape renders at a fixed pixel size regardless of camera\n * zoom. Mirrors the legacy `GroupNode.noScaling` flag.\n */\n noScale?: boolean;\n}\n\nexport interface RectShape extends ShapeBase {\n type: \"rect\";\n x: number;\n y: number;\n width: number;\n height: number;\n cornerRadius?: number;\n paint: Paint;\n}\n\nexport interface CircleShape extends ShapeBase {\n type: \"circle\";\n cx: number;\n cy: number;\n radius: number;\n paint: Paint;\n}\n\nexport interface LineShape extends ShapeBase {\n type: \"line\";\n /** Flat list of [x0, y0, x1, y1, …]. */\n points: number[];\n paint: Paint;\n lineCap?: \"butt\" | \"round\" | \"square\";\n lineJoin?: \"miter\" | \"round\" | \"bevel\";\n /**\n * Marks lines that are infrastructure (grid). Styles use this to skip\n * decoration on grid (cheaper rendering) while still recolouring it.\n */\n grid?: boolean;\n}\n\nexport interface PolygonShape extends ShapeBase {\n type: \"polygon\";\n /** Flat list of [x0, y0, x1, y1, …]. Closed implicitly. */\n vertices: number[];\n paint: Paint;\n}\n\nexport interface TextShape extends ShapeBase {\n type: \"text\";\n x: number;\n y: number;\n text: string;\n fontSize: number;\n fontFamily?: string;\n fontStyle?: string;\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n align?: \"left\" | \"center\" | \"right\";\n verticalAlign?: \"top\" | \"middle\" | \"bottom\";\n width?: number;\n height?: number;\n /** Fraction of fontSize from top to baseline (SVG positioning). */\n baselineRatio?: number;\n /** Konva offsetY ratio applied to centre text vertically. */\n konvaCorrectionRatio?: number;\n /** Optional 2D affine transform [a, b, c, d, e, f]. */\n transform?: [number, number, number, number, number, number];\n}\n\nexport interface ImageShape extends ShapeBase {\n type: \"image\";\n x: number;\n y: number;\n width: number;\n height: number;\n src: string;\n /** Optional 2D affine transform [a, b, c, d, e, f]. */\n transform?: [number, number, number, number, number, number];\n}\n\n/**\n * Positional container. Children render relative to {@link x}, {@link y}.\n * Groups can nest; the final world position of a child is the cumulative\n * sum of group origins plus the child's own offset.\n */\nexport interface GroupShape extends ShapeBase {\n type: \"group\";\n x: number;\n y: number;\n children: Shape[];\n}\n\nexport type Shape =\n | RectShape\n | CircleShape\n | LineShape\n | PolygonShape\n | TextShape\n | ImageShape\n | GroupShape;\n\n/** Axis-aligned bounding box in world space. */\nexport interface Bbox {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\n/**\n * Full output of one {@link ScenePipeline} build pass. Consumers slice this by\n * layer and feed the relevant subset into culling, hit-testing, and rendering.\n */\nexport interface SceneIR {\n /** All shapes for the current scene, world-space. */\n shapes: Shape[];\n /** Optional pre-computed bbox for whole-scene framing. */\n bounds?: Bbox;\n}\n","/**\n * {@link buildDrawCommands} — single source of truth for camera transform math.\n *\n * Walks a {@link SceneIR}-shaped tree (already passed through styles) and\n * produces a flat {@link DrawCommand} list per logical layer. Coordinates\n * arrive in world space; commands leave in render space (camera scale and\n * pan applied), with stroke widths and corner radii already scaled.\n *\n * `noScale` group shapes are emitted as `pushTransform`/`popTransform`\n * pairs: the group origin is placed at its world-mapped screen position,\n * but the children render at fixed pixel size regardless of zoom — the\n * legacy `RecordingLayerNode.canScaleNoScalingGroups` invariant.\n */\n\nimport type {\n DrawCommand,\n DrawCommandBatch,\n LineCommand,\n PolygonCommand,\n RectCommand,\n TextCommand,\n} from \"./DrawCommand\";\nimport {\n transformFill,\n type GroupShape,\n type LayerId,\n type Shape,\n} from \"../scene/Shape\";\n\n/** Camera state needed to project world coordinates into render space. */\nexport interface CameraTransform {\n /** Pixels per world unit. */\n scale: number;\n /** Render-space origin in pixels. */\n offsetX: number;\n offsetY: number;\n}\n\nconst DEFAULT_LAYER: LayerId = \"room\";\n\n/**\n * Build per-layer {@link DrawCommandBatch}es from a list of world-space shapes\n * and an active camera transform.\n *\n * Layer order in the result follows first-appearance order; renderers iterate\n * the array as-is (z-order is the caller's responsibility — typically scene\n * layout uses the LayerId taxonomy to pre-sort).\n */\nexport function buildDrawCommands(\n shapes: Shape[],\n camera: CameraTransform,\n): DrawCommandBatch[] {\n const batches = new Map<LayerId, DrawCommand[]>();\n\n function commandsFor(layer: LayerId): DrawCommand[] {\n let bucket = batches.get(layer);\n if (!bucket) {\n bucket = [];\n batches.set(layer, bucket);\n }\n return bucket;\n }\n\n function emitShape(\n shape: Shape,\n worldX: number,\n worldY: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n layerOverride: LayerId | undefined,\n ) {\n const layer = shape.layer ?? layerOverride ?? DEFAULT_LAYER;\n\n switch (shape.type) {\n case \"rect\": {\n const x = (worldX + shape.x) * scale + offsetX;\n const y = (worldY + shape.y) * scale + offsetY;\n const w = shape.width * scale;\n const h = shape.height * scale;\n const cmd: RectCommand = {\n type: \"rect\",\n x, y, w, h,\n fill: transformFill(shape.paint.fill, worldX, worldY, scale, offsetX, offsetY),\n stroke: shape.paint.stroke,\n sw: (shape.paint.strokeWidth ?? 0) * scale,\n cr: (shape.cornerRadius ?? 0) * scale,\n dash: scaleDash(shape.paint.dash, scale, shape.paint.dashEnabled),\n };\n commandsFor(layer).push(cmd);\n return;\n }\n case \"circle\": {\n commandsFor(layer).push({\n type: \"circle\",\n cx: (worldX + shape.cx) * scale + offsetX,\n cy: (worldY + shape.cy) * scale + offsetY,\n r: shape.radius * scale,\n fill: transformFill(shape.paint.fill, worldX, worldY, scale, offsetX, offsetY),\n stroke: shape.paint.stroke,\n sw: (shape.paint.strokeWidth ?? 0) * scale,\n dash: scaleDash(shape.paint.dash, scale, shape.paint.dashEnabled),\n });\n return;\n }\n case \"line\": {\n const cmd: LineCommand = {\n type: \"line\",\n points: scalePoints(shape.points, worldX, worldY, scale, offsetX, offsetY),\n stroke: shape.paint.stroke,\n sw: (shape.paint.strokeWidth ?? 0) * scale,\n dash: scaleDash(shape.paint.dash, scale, shape.paint.dashEnabled),\n lineCap: shape.lineCap,\n lineJoin: shape.lineJoin,\n alpha: shape.paint.alpha,\n };\n commandsFor(layer).push(cmd);\n return;\n }\n case \"polygon\": {\n const cmd: PolygonCommand = {\n type: \"polygon\",\n vertices: scalePoints(shape.vertices, worldX, worldY, scale, offsetX, offsetY),\n fill: transformFill(shape.paint.fill, worldX, worldY, scale, offsetX, offsetY),\n stroke: shape.paint.stroke,\n sw: (shape.paint.strokeWidth ?? 0) * scale,\n };\n commandsFor(layer).push(cmd);\n return;\n }\n case \"text\": {\n const t = scaleTransform(shape.transform, worldX, worldY, scale, offsetX, offsetY);\n // When a transform is set the matrix carries position; the\n // command's x/y is zero so renderers don't double-translate.\n const x = t ? 0 : (worldX + shape.x) * scale + offsetX;\n const y = t ? 0 : (worldY + shape.y) * scale + offsetY;\n const cmd: TextCommand = {\n type: \"text\",\n x, y,\n text: shape.text,\n fontSize: shape.fontSize * scale,\n fontFamily: shape.fontFamily ?? \"sans-serif\",\n fontStyle: shape.fontStyle ?? \"normal\",\n fill: shape.fill ?? \"black\",\n stroke: shape.stroke,\n sw: (shape.strokeWidth ?? 0) * scale,\n align: shape.align ?? \"left\",\n vAlign: shape.verticalAlign ?? \"top\",\n w: (shape.width ?? 0) * scale,\n h: (shape.height ?? 0) * scale,\n baselineRatio: shape.baselineRatio,\n konvaCorrectionRatio: shape.konvaCorrectionRatio,\n transform: t,\n };\n commandsFor(layer).push(cmd);\n return;\n }\n case \"image\": {\n const t = scaleTransform(shape.transform, worldX, worldY, scale, offsetX, offsetY);\n const x = t ? 0 : (worldX + shape.x) * scale + offsetX;\n const y = t ? 0 : (worldY + shape.y) * scale + offsetY;\n commandsFor(layer).push({\n type: \"image\",\n x, y,\n w: shape.width * scale,\n h: shape.height * scale,\n src: shape.src,\n transform: t,\n });\n return;\n }\n case \"group\":\n emitGroup(shape, worldX, worldY, scale, offsetX, offsetY, layer);\n return;\n }\n }\n\n function emitGroup(\n group: GroupShape,\n worldX: number,\n worldY: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n layerOverride: LayerId | undefined,\n ) {\n const childLayer = group.layer ?? layerOverride;\n\n if (group.noScale) {\n // Anchor the group at its scaled world position, then render\n // children in pixel space (scale = 1, offset = 0).\n const px = (worldX + group.x) * scale + offsetX;\n const py = (worldY + group.y) * scale + offsetY;\n\n const pushLayer = childLayer ?? DEFAULT_LAYER;\n const bucket = commandsFor(pushLayer);\n bucket.push({type: \"pushTransform\", matrix: [1, 0, 0, 1, px, py]});\n for (const child of group.children) {\n emitShape(child, 0, 0, 1, 0, 0, childLayer);\n }\n // popTransform must land in the same bucket as its push so\n // renderers can replay them in order. Children whose `layer`\n // diverts them elsewhere are pulled out of this push/pop scope\n // — that's the intended behaviour, since cross-layer children\n // are independent draws (e.g. a noScale label group has a\n // pixmap on the link layer plus a rect on the top layer).\n bucket.push({type: \"popTransform\"});\n return;\n }\n\n const nextX = worldX + group.x;\n const nextY = worldY + group.y;\n for (const child of group.children) {\n emitShape(child, nextX, nextY, scale, offsetX, offsetY, childLayer);\n }\n }\n\n for (const shape of shapes) {\n emitShape(shape, 0, 0, camera.scale, camera.offsetX, camera.offsetY, undefined);\n }\n\n const out: DrawCommandBatch[] = [];\n for (const [layer, commands] of batches) {\n out.push({layer, commands});\n }\n return out;\n}\n\nfunction scalePoints(\n pts: number[],\n worldX: number,\n worldY: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n): number[] {\n const out = new Array<number>(pts.length);\n for (let i = 0; i < pts.length; i += 2) {\n out[i] = (worldX + pts[i]) * scale + offsetX;\n out[i + 1] = (worldY + pts[i + 1]) * scale + offsetY;\n }\n return out;\n}\n\n/** Scale dash lengths from world units to render units. `dashEnabled === false` blanks the dash. */\nfunction scaleDash(\n dash: number[] | undefined,\n scale: number,\n dashEnabled: boolean | undefined,\n): number[] | undefined {\n if (dashEnabled === false) return undefined;\n if (!dash || dash.length === 0) return dash;\n const out = new Array<number>(dash.length);\n for (let i = 0; i < dash.length; i++) out[i] = dash[i] * scale;\n return out;\n}\n\n/**\n * Apply the camera's scale + offset to a 2D affine matrix originally\n * authored in world space. Result composes camera ∘ shape on world points.\n */\nfunction scaleTransform(\n matrix: [number, number, number, number, number, number] | undefined,\n worldX: number,\n worldY: number,\n scale: number,\n offsetX: number,\n offsetY: number,\n): [number, number, number, number, number, number] | undefined {\n if (!matrix) return undefined;\n const [a, b, c, d, e, f] = matrix;\n // Camera: [scale, 0, 0, scale, worldX*scale + offsetX, worldY*scale + offsetY]\n // Composed = Camera * Shape (column-major affine).\n return [\n a * scale,\n b * scale,\n c * scale,\n d * scale,\n e * scale + worldX * scale + offsetX,\n f * scale + worldY * scale + offsetY,\n ];\n}\n","/**\n * Shared Canvas2D fill resolver — turns a {@link FillStyle} (string or\n * gradient) into something assignable to `ctx.fillStyle`.\n *\n * Used by both {@link RecordingLayerNode} (interactive Konva replay) and\n * {@link renderToCanvas} (PNG export) so gradient semantics stay identical\n * across pipelines.\n */\n\nimport type {FillStyle} from \"../scene/Shape\";\n\nexport function resolveFill(\n ctx: CanvasRenderingContext2D,\n fill: FillStyle,\n): string | CanvasGradient {\n if (typeof fill === \"string\") return fill;\n if (fill.type === \"linear\") {\n const g = ctx.createLinearGradient(fill.x0, fill.y0, fill.x1, fill.y1);\n for (const s of fill.stops) g.addColorStop(s.offset, s.color);\n return g;\n }\n const fx = fill.fx ?? fill.cx;\n const fy = fill.fy ?? fill.cy;\n const fr = fill.fr ?? 0;\n const g = ctx.createRadialGradient(fx, fy, fr, fill.cx, fill.cy, fill.r);\n for (const s of fill.stops) g.addColorStop(s.offset, s.color);\n return g;\n}\n","/**\n * CanvasRenderer — replays {@link DrawCommandBatch}es onto a Canvas2D\n * context.\n *\n * Coordinates in commands are already in render space (camera transform\n * applied), so the renderer issues calls directly without applying any extra\n * scale or translation. Stack commands push/pop matching `ctx.save()` /\n * `ctx.restore()` pairs and apply the transform/clip on the way in.\n *\n * Used by {@link CanvasExporter} (and {@link PngBytesExporter}) so headless\n * exports rasterize through the shared {@link buildDrawCommands} pipeline\n * instead of grabbing the live Konva stage.\n */\n\nimport type {\n DrawCommand,\n DrawCommandBatch,\n PrimitiveDrawCommand,\n} from \"../draw/DrawCommand\";\nimport {resolveFill} from \"./canvasGradient\";\n\n/**\n * Image loader hook. Canvas2D's `drawImage` needs an `HTMLImageElement` (or\n * compatible). Browsers create one via `new Image()`; Node-canvas provides\n * its own `Image` constructor. The default tries both — callers can override\n * for tests or unusual targets.\n */\nexport type ImageFactory = (src: string) => unknown;\n\nconst defaultImageFactory: ImageFactory = (src) => {\n if (typeof Image !== \"undefined\") {\n const img = new Image();\n img.src = src;\n return img;\n }\n return null;\n};\n\nexport interface CanvasRenderOptions {\n imageFactory?: ImageFactory;\n}\n\n/**\n * Replay every batch onto `ctx`, in order. Each batch's commands are flushed\n * sequentially; transform / clip stacks are independent per batch so a stray\n * push in one layer cannot leak into the next.\n */\nexport function renderToCanvas(\n ctx: CanvasRenderingContext2D,\n batches: DrawCommandBatch[],\n options: CanvasRenderOptions = {},\n): void {\n const imageFactory = options.imageFactory ?? defaultImageFactory;\n for (const batch of batches) {\n replayCommands(ctx, batch.commands, imageFactory);\n }\n}\n\nfunction replayCommands(\n ctx: CanvasRenderingContext2D,\n commands: DrawCommand[],\n imageFactory: ImageFactory,\n): void {\n let stackDepth = 0;\n\n for (const cmd of commands) {\n switch (cmd.type) {\n case \"pushTransform\": {\n ctx.save();\n stackDepth++;\n ctx.transform(...cmd.matrix);\n break;\n }\n case \"pushClip\": {\n ctx.save();\n stackDepth++;\n ctx.beginPath();\n ctx.rect(cmd.x, cmd.y, cmd.w, cmd.h);\n ctx.clip();\n break;\n }\n case \"popTransform\":\n case \"popClip\": {\n if (stackDepth > 0) {\n ctx.restore();\n stackDepth--;\n }\n break;\n }\n default:\n replayPrimitive(ctx, cmd, imageFactory);\n break;\n }\n }\n\n while (stackDepth > 0) {\n ctx.restore();\n stackDepth--;\n }\n}\n\nfunction replayPrimitive(\n ctx: CanvasRenderingContext2D,\n cmd: PrimitiveDrawCommand,\n imageFactory: ImageFactory,\n): void {\n switch (cmd.type) {\n case \"rect\": {\n ctx.beginPath();\n if (cmd.cr > 0 && typeof ctx.roundRect === \"function\") {\n ctx.roundRect(cmd.x, cmd.y, cmd.w, cmd.h, cmd.cr);\n } else {\n ctx.rect(cmd.x, cmd.y, cmd.w, cmd.h);\n }\n if (cmd.fill) {\n ctx.fillStyle = resolveFill(ctx, cmd.fill);\n ctx.fill();\n }\n if (cmd.stroke && cmd.sw > 0) {\n ctx.strokeStyle = cmd.stroke;\n ctx.lineWidth = cmd.sw;\n ctx.setLineDash(cmd.dash ?? []);\n ctx.stroke();\n }\n break;\n }\n case \"circle\": {\n ctx.beginPath();\n ctx.arc(cmd.cx, cmd.cy, cmd.r, 0, Math.PI * 2);\n if (cmd.fill) {\n ctx.fillStyle = resolveFill(ctx, cmd.fill);\n ctx.fill();\n }\n if (cmd.stroke && cmd.sw > 0) {\n ctx.strokeStyle = cmd.stroke;\n ctx.lineWidth = cmd.sw;\n ctx.setLineDash(cmd.dash ?? []);\n ctx.stroke();\n }\n break;\n }\n case \"line\": {\n if (cmd.points.length < 4) break;\n const savedAlpha = ctx.globalAlpha;\n if (cmd.alpha !== undefined) ctx.globalAlpha = cmd.alpha;\n ctx.beginPath();\n ctx.moveTo(cmd.points[0], cmd.points[1]);\n for (let i = 2; i < cmd.points.length; i += 2) {\n ctx.lineTo(cmd.points[i], cmd.points[i + 1]);\n }\n if (cmd.stroke) ctx.strokeStyle = cmd.stroke;\n ctx.lineWidth = cmd.sw;\n ctx.setLineDash(cmd.dash ?? []);\n if (cmd.lineCap) ctx.lineCap = cmd.lineCap;\n if (cmd.lineJoin) ctx.lineJoin = cmd.lineJoin;\n ctx.stroke();\n if (cmd.alpha !== undefined) ctx.globalAlpha = savedAlpha;\n break;\n }\n case \"polygon\": {\n if (cmd.vertices.length < 4) break;\n ctx.beginPath();\n ctx.moveTo(cmd.vertices[0], cmd.vertices[1]);\n for (let i = 2; i < cmd.vertices.length; i += 2) {\n ctx.lineTo(cmd.vertices[i], cmd.vertices[i + 1]);\n }\n ctx.closePath();\n if (cmd.fill) {\n ctx.fillStyle = resolveFill(ctx, cmd.fill);\n ctx.fill();\n }\n if (cmd.stroke && cmd.sw > 0) {\n ctx.strokeStyle = cmd.stroke;\n ctx.lineWidth = cmd.sw;\n ctx.setLineDash([]);\n ctx.stroke();\n }\n break;\n }\n case \"text\": {\n // Sub-pixel font sizes break Canvas2D text metrics on some\n // engines (notably node-canvas). Render at TEXT_SCALE × the\n // requested size and counter-scale the matrix so output stays\n // pixel-correct.\n const TEXT_SCALE = 100;\n const scaledSize = cmd.fontSize * TEXT_SCALE;\n const font = `${cmd.fontStyle} ${scaledSize}px ${cmd.fontFamily}`;\n ctx.save();\n ctx.font = font;\n ctx.fillStyle = cmd.fill;\n if (cmd.stroke && cmd.sw > 0) {\n ctx.strokeStyle = cmd.stroke;\n ctx.lineWidth = cmd.sw * TEXT_SCALE;\n ctx.lineJoin = \"round\";\n }\n const hasBaselineRatio = cmd.baselineRatio !== undefined;\n if (cmd.transform) {\n ctx.transform(...cmd.transform);\n ctx.scale(1 / TEXT_SCALE, 1 / TEXT_SCALE);\n ctx.textAlign = \"center\";\n if (hasBaselineRatio) {\n ctx.textBaseline = \"alphabetic\";\n const by = (cmd.h / 2 + cmd.baselineRatio! * cmd.fontSize) * TEXT_SCALE;\n if (cmd.stroke && cmd.sw > 0) ctx.strokeText(cmd.text, cmd.w * TEXT_SCALE / 2, by);\n ctx.fillText(cmd.text, cmd.w * TEXT_SCALE / 2, by);\n } else {\n ctx.textBaseline = \"middle\";\n const mx = cmd.w * TEXT_SCALE / 2;\n const my = cmd.h * TEXT_SCALE / 2;\n if (cmd.stroke && cmd.sw > 0) ctx.strokeText(cmd.text, mx, my);\n ctx.fillText(cmd.text, mx, my);\n }\n } else if (cmd.w > 0 && cmd.h > 0) {\n ctx.textAlign = (cmd.align || \"left\");\n const tx = cmd.align === \"center\"\n ? cmd.x + cmd.w / 2\n : cmd.align === \"right\"\n ? cmd.x + cmd.w\n : cmd.x;\n ctx.scale(1 / TEXT_SCALE, 1 / TEXT_SCALE);\n if (cmd.vAlign === \"middle\" && hasBaselineRatio) {\n ctx.textBaseline = \"alphabetic\";\n const ty = cmd.y + cmd.h / 2 + cmd.baselineRatio! * cmd.fontSize;\n if (cmd.stroke && cmd.sw > 0) ctx.strokeText(cmd.text, tx * TEXT_SCALE, ty * TEXT_SCALE);\n ctx.fillText(cmd.text, tx * TEXT_SCALE, ty * TEXT_SCALE);\n } else {\n ctx.textBaseline = cmd.vAlign === \"middle\" ? \"middle\" : \"top\";\n const ty = cmd.vAlign === \"middle\" ? cmd.y + cmd.h / 2 : cmd.y;\n if (cmd.stroke && cmd.sw > 0) ctx.strokeText(cmd.text, tx * TEXT_SCALE, ty * TEXT_SCALE);\n ctx.fillText(cmd.text, tx * TEXT_SCALE, ty * TEXT_SCALE);\n }\n } else {\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"top\";\n ctx.scale(1 / TEXT_SCALE, 1 / TEXT_SCALE);\n if (cmd.stroke && cmd.sw > 0) ctx.strokeText(cmd.text, cmd.x * TEXT_SCALE, cmd.y * TEXT_SCALE);\n ctx.fillText(cmd.text, cmd.x * TEXT_SCALE, cmd.y * TEXT_SCALE);\n }\n ctx.restore();\n break;\n }\n case \"image\": {\n const image = imageFactory(cmd.src) as CanvasImageSource | null;\n if (!image) break;\n if (cmd.transform) {\n ctx.save();\n ctx.transform(...cmd.transform);\n ctx.drawImage(image, 0, 0, cmd.w, cmd.h);\n ctx.restore();\n } else {\n ctx.drawImage(image, cmd.x, cmd.y, cmd.w, cmd.h);\n }\n break;\n }\n }\n}\n","/**\n * Flatten a {@link Style}'s coordinate warp into a {@link SerializableTransform}\n * the worker can use without the (non-serialisable) style closures.\n *\n * All current built-in styles are either flat (`worldToScene` absent → identity)\n * or affine (Isometric). We reconstruct the affine matrix by sampling the\n * closure at three basis points — exact for affine maps. A future non-affine\n * style would be mis-sampled here; callers should fall back to main-thread\n * culling for such styles (see OffscreenCanvasBackend).\n */\n\nimport type {Style} from \"../../style/Style\";\nimport type {AffineMatrix, SerializableTransform} from \"./protocol\";\n\ntype TransformFn = (x: number, y: number) => {x: number; y: number};\n\n/** Recover the affine matrix `[a,b,c,d,e,f]` of a (presumed affine) map. */\nfunction sampleAffine(fn: TransformFn): AffineMatrix {\n const o = fn(0, 0);\n const x = fn(1, 0);\n const y = fn(0, 1);\n return [x.x - o.x, x.y - o.y, y.x - o.x, y.y - o.y, o.x, o.y];\n}\n\n/** Build the flattened transform for a style. Identity styles short-circuit. */\nexport function serializeTransform(style: Style): SerializableTransform {\n if (!style.worldToScene || !style.sceneToWorld) {\n return {kind: \"identity\"};\n }\n return {\n kind: \"affine\",\n forward: sampleAffine((x, y) => style.worldToScene!(x, y)),\n inverse: sampleAffine((x, y) => style.sceneToWorld!(x, y)),\n };\n}\n\nfunction applyMatrix(m: AffineMatrix, x: number, y: number): {x: number; y: number} {\n return {x: m[0] * x + m[2] * y + m[4], y: m[1] * x + m[3] * y + m[5]};\n}\n\n/**\n * World → scene projector for the worker's cull-bounds test. Returns\n * `undefined` for the identity transform so callers can skip projection.\n */\nexport function forwardFn(transform: SerializableTransform): TransformFn | undefined {\n if (transform.kind === \"identity\") return undefined;\n const m = transform.forward;\n return (x, y) => applyMatrix(m, x, y);\n}\n\n/**\n * Scene → world projector for the worker's grid Cartesian-bounds computation.\n * Returns `undefined` for the identity transform (grid layout treats absent\n * inverse as identity).\n */\nexport function inverseFn(transform: SerializableTransform): TransformFn | undefined {\n if (transform.kind === \"identity\") return undefined;\n const m = transform.inverse;\n return (x, y) => applyMatrix(m, x, y);\n}\n","/**\n * renderFrame — the worker's pure render heart, decoupled from `self.onmessage`.\n *\n * Replays the latest scene + overlays onto a 2D context for the current camera,\n * using the SAME pipeline (`layoutGrid` → cull → `buildDrawCommands` →\n * `renderToCanvas`) and the SAME draw order as the headless {@link CanvasExporter}\n * (background → grid → link → room → overlays → topLabel). Keeping this a plain\n * function — no worker globals — makes it unit-testable against a recording\n * mock context, with no `OffscreenCanvas`/`Worker` required.\n *\n * Imports only worker-safe modules (no Konva, no `document`). Image shapes are\n * skipped in a worker because {@link renderToCanvas}'s default image factory\n * needs `new Image()` (absent in workers) — a documented v1 limitation.\n */\n\nimport type {Settings, ViewportBounds} from \"../../types/Settings\";\nimport type {Shape} from \"../../scene/Shape\";\nimport {layoutGrid} from \"../../scene/elements/GridLayout\";\nimport {buildDrawCommands} from \"../../draw/DrawCommandBuilder\";\nimport {renderToCanvas, type ImageFactory} from \"../../render/CanvasRenderer\";\nimport type {LayerPayload, RenderCamera, SerializableTransform} from \"./protocol\";\nimport {forwardFn, inverseFn} from \"./serializeTransform\";\n\nexport interface FrameScene {\n link: LayerPayload;\n room: LayerPayload;\n topLabel: Shape[];\n transform: SerializableTransform;\n}\n\nexport interface FrameInput {\n scene: FrameScene | null;\n overlays: Shape[];\n camera: RenderCamera;\n viewport: ViewportBounds;\n settings: Settings;\n /** Logical (CSS) pixel dimensions; HiDPI scaling is applied by the caller. */\n width: number;\n height: number;\n /**\n * Resolves an image shape's `src` to a drawable source. In the worker this\n * returns a cached {@link ImageBitmap} (or `null` while it decodes), so\n * data-URL labels and the ambient-light vignette render off-thread.\n */\n imageFactory?: ImageFactory;\n}\n\ntype TransformFn = (x: number, y: number) => {x: number; y: number};\n\nfunction transformedBbox(\n minX: number, minY: number, maxX: number, maxY: number,\n fn: TransformFn,\n): {minX: number; minY: number; maxX: number; maxY: number} {\n const c1 = fn(minX, minY);\n const c2 = fn(maxX, minY);\n const c3 = fn(maxX, maxY);\n const c4 = fn(minX, maxY);\n return {\n minX: Math.min(c1.x, c2.x, c3.x, c4.x),\n minY: Math.min(c1.y, c2.y, c3.y, c4.y),\n maxX: Math.max(c1.x, c2.x, c3.x, c4.x),\n maxY: Math.max(c1.y, c2.y, c3.y, c4.y),\n };\n}\n\n/**\n * Filter a layer's shapes to those intersecting the viewport. Mirrors\n * {@link buildCullingVisibilityMap}: `null` bounds are unmanaged pass-throughs\n * (always drawn), and the forward transform projects world bounds into scene\n * space before the intersection test.\n */\nfunction cullLayer(\n layer: LayerPayload,\n viewport: ViewportBounds,\n fwd: TransformFn | undefined,\n): Shape[] {\n const {minX, maxX, minY, maxY} = viewport;\n const out: Shape[] = [];\n for (let i = 0; i < layer.shapes.length; i++) {\n const b = layer.bounds[i];\n if (!b) {\n out.push(layer.shapes[i]);\n continue;\n }\n let bMinX = b.x, bMinY = b.y, bMaxX = b.x + b.width, bMaxY = b.y + b.height;\n if (fwd) {\n const t = transformedBbox(bMinX, bMinY, bMaxX, bMaxY, fwd);\n bMinX = t.minX; bMinY = t.minY; bMaxX = t.maxX; bMaxY = t.maxY;\n }\n if (bMaxX >= minX && bMinX <= maxX && bMaxY >= minY && bMinY <= maxY) {\n out.push(layer.shapes[i]);\n }\n }\n return out;\n}\n\n/** Render one frame of the scene + overlays onto `ctx`. */\nexport function renderFrame(ctx: CanvasRenderingContext2D, input: FrameInput): void {\n const {scene, overlays, camera, viewport, settings, width, height, imageFactory} = input;\n const opts = imageFactory ? {imageFactory} : undefined;\n\n ctx.fillStyle = settings.backgroundColor;\n ctx.fillRect(0, 0, width, height);\n\n if (!scene) return;\n\n const fwd = forwardFn(scene.transform);\n const inv = inverseFn(scene.transform);\n\n const grid = layoutGrid(viewport, settings, {inverseTransform: inv});\n if (grid.length > 0) renderToCanvas(ctx, buildDrawCommands(grid, camera), opts);\n\n const link = settings.cullingEnabled ? cullLayer(scene.link, viewport, fwd) : scene.link.shapes;\n if (link.length > 0) renderToCanvas(ctx, buildDrawCommands(link, camera), opts);\n\n const room = settings.cullingEnabled ? cullLayer(scene.room, viewport, fwd) : scene.room.shapes;\n if (room.length > 0) renderToCanvas(ctx, buildDrawCommands(room, camera), opts);\n\n if (overlays.length > 0) renderToCanvas(ctx, buildDrawCommands(overlays, camera), opts);\n\n if (scene.topLabel.length > 0) renderToCanvas(ctx, buildDrawCommands(scene.topLabel, camera), opts);\n}\n","/**\n * OffscreenCanvas worker entry.\n *\n * Thin dispatcher around {@link renderFrame}: owns the transferred\n * `OffscreenCanvas`, stores the latest scene / overlays / camera / settings,\n * and coalesces redraws onto one animation frame (only the latest camera\n * matters, so queued messages are harmless). Resizing a transferred canvas can\n * only happen here, so size/DPR changes are applied worker-side.\n *\n * This module is bundled standalone (see vite.config.ts). It must import ONLY\n * worker-safe code — never Konva, `textMeasure`, or anything touching\n * `document`. A stray such import would surface as a runtime error in the\n * worker and a Konva chunk in the build.\n */\n\nimport type {MainToWorkerMessage, RenderCamera, WorkerToMainMessage} from \"./protocol\";\nimport {renderFrame, type FrameScene} from \"./renderFrame\";\nimport type {Settings, ViewportBounds} from \"../../types/Settings\";\nimport type {Shape} from \"../../scene/Shape\";\n\n/**\n * Minimal dedicated-worker surface. We cast `globalThis` to this instead of\n * using `DedicatedWorkerGlobalScope` so the worker compiles under the project's\n * DOM lib without pulling in (and conflicting with) the WebWorker lib.\n */\ninterface WorkerScope {\n onmessage: ((ev: MessageEvent<MainToWorkerMessage>) => void) | null;\n postMessage(message: WorkerToMainMessage, transfer: Transferable[]): void;\n}\n\nconst scope = globalThis as unknown as WorkerScope;\n\nlet canvas: OffscreenCanvas | null = null;\nlet ctx: OffscreenCanvasRenderingContext2D | null = null;\nlet dpr = 1;\nlet cssWidth = 1;\nlet cssHeight = 1;\nlet settings: Settings | null = null;\nlet scene: FrameScene | null = null;\nlet overlays: Shape[] = [];\nlet camera: RenderCamera = {scale: 1, offsetX: 0, offsetY: 0};\nlet viewport: ViewportBounds = {minX: 0, maxX: 0, minY: 0, maxY: 0};\n\nlet dirty = false;\nlet rafScheduled = false;\n\n/**\n * src → decoded bitmap cache. `null` means \"decoding in flight or failed\" so we\n * don't kick off the async decode twice. Workers have no `new Image()`, but\n * `fetch` + `createImageBitmap` decode data URLs (labels, ambient-light\n * vignette) and CORS-permitted URLs into a drawable bitmap.\n */\nconst images = new Map<string, ImageBitmap | null>();\nconst MAX_IMAGES = 64;\n\nfunction requestImage(src: string): CanvasImageSource | null {\n const hit = images.get(src);\n if (hit !== undefined) return hit;\n images.set(src, null);\n fetch(src)\n .then((r) => r.blob())\n .then((blob) => createImageBitmap(blob))\n .then((bmp) => {\n // Evict oldest if the cache grows (e.g. the ambient-light vignette\n // src changes as its settings change).\n if (images.size > MAX_IMAGES) {\n const oldest = images.keys().next().value;\n if (oldest !== undefined && oldest !== src) {\n const stale = images.get(oldest);\n if (stale) stale.close();\n images.delete(oldest);\n }\n }\n images.set(src, bmp);\n scheduleDraw();\n })\n .catch(() => {/* leave null — don't retry a broken src */});\n return null;\n}\n\nconst raf: (cb: (t: number) => void) => void =\n typeof requestAnimationFrame !== \"undefined\"\n ? (cb) => { requestAnimationFrame(cb); }\n : (cb) => { setTimeout(() => cb(0), 16); };\n\nfunction scheduleDraw(): void {\n dirty = true;\n if (rafScheduled) return;\n rafScheduled = true;\n raf(() => {\n rafScheduled = false;\n if (dirty) draw();\n });\n}\n\nfunction applyCanvasSize(): void {\n if (!canvas) return;\n canvas.width = Math.max(1, Math.round(cssWidth * dpr));\n canvas.height = Math.max(1, Math.round(cssHeight * dpr));\n ctx = canvas.getContext(\"2d\");\n}\n\nfunction draw(): void {\n dirty = false;\n if (!ctx || !settings) return;\n // Map logical (CSS) coordinates to device pixels; renderFrame draws in CSS\n // space and renderToCanvas balances its own save/restore, so this base\n // transform persists across the frame.\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n renderFrame(ctx as unknown as CanvasRenderingContext2D, {\n scene,\n overlays,\n camera,\n viewport,\n settings,\n width: cssWidth,\n height: cssHeight,\n imageFactory: requestImage,\n });\n}\n\nasync function handleExport(requestId: number): Promise<void> {\n if (!canvas) return;\n // Draw synchronously so the snapshot reflects the latest state.\n if (dirty) draw();\n const bitmap = await createImageBitmap(canvas);\n scope.postMessage({type: \"export\", requestId, bitmap}, [bitmap]);\n}\n\nscope.onmessage = (ev: MessageEvent<MainToWorkerMessage>) => {\n const msg = ev.data;\n switch (msg.type) {\n case \"init\": {\n canvas = msg.canvas;\n dpr = msg.dpr;\n cssWidth = msg.width;\n cssHeight = msg.height;\n settings = msg.settings;\n applyCanvasSize();\n scope.postMessage({type: \"ready\"}, []);\n scheduleDraw();\n break;\n }\n case \"settings\": {\n settings = msg.settings;\n scheduleDraw();\n break;\n }\n case \"scene\": {\n scene = {link: msg.link, room: msg.room, topLabel: msg.topLabel, transform: msg.transform};\n scheduleDraw();\n break;\n }\n case \"overlays\": {\n overlays = msg.shapes;\n scheduleDraw();\n break;\n }\n case \"camera\": {\n camera = msg.camera;\n viewport = msg.viewport;\n if (msg.width !== cssWidth || msg.height !== cssHeight) {\n cssWidth = msg.width;\n cssHeight = msg.height;\n applyCanvasSize();\n }\n scheduleDraw();\n break;\n }\n case \"resize\": {\n cssWidth = msg.width;\n cssHeight = msg.height;\n dpr = msg.dpr;\n applyCanvasSize();\n scheduleDraw();\n break;\n }\n case \"export\": {\n void handleExport(msg.requestId);\n break;\n }\n case \"destroy\": {\n canvas = null;\n ctx = null;\n scene = null;\n overlays = [];\n settings = null;\n for (const bmp of images.values()) bmp?.close();\n images.clear();\n break;\n }\n }\n};\n"],"mappings":"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"}
|
package/dist/binary.mjs
CHANGED
|
@@ -17,5 +17,5 @@ export interface SceneFlushContext {
|
|
|
17
17
|
* Single source of truth for what static exporters must render — adding a new
|
|
18
18
|
* exporter no longer means re-typing the sequence and risking a silent omission.
|
|
19
19
|
*/
|
|
20
|
-
export declare function flushSceneShapes(sceneShapes: SceneShapesByLayer, context: SceneFlushContext, flush: (shapes: Shape[]) => void): void;
|
|
20
|
+
export declare function flushSceneShapes(sceneShapes: SceneShapesByLayer, context: SceneFlushContext, flush: (shapes: Shape[], sceneSpace?: boolean) => void): void;
|
|
21
21
|
export declare function buildBuiltInOverlayShapes(state: MapState, overlaysInput?: SvgOverlays): Shape[];
|