mudlet-map-renderer 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Optional capability interface for readers that can resolve a Mudlet room
3
+ * content hash (`MapData.Room.hash`) directly to a room id.
4
+ *
5
+ * `SkeletonMapReader.getRooms()` deliberately always returns `[]` (see
6
+ * {@link ViewportDataSource} — never materialise the full room list), so a
7
+ * linear `getRooms().find(r => r.hash === hash)` scan — the only option
8
+ * `IMapReader` supports on its own — can never find anything on a big/
9
+ * streamed map. This interface gives such readers a way to opt into an O(1)
10
+ * (or otherwise scan-free) hash lookup instead.
11
+ */
12
+ export interface HashLookupCapable {
13
+ readonly hashLookupCapable: true;
14
+ /** Resolve a room's content hash to its id, or `undefined` if unknown. */
15
+ getRoomIdByHash(hash: string): number | undefined;
16
+ }
17
+ export declare function isHashLookupCapable(reader: unknown): reader is HashLookupCapable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mudlet-map-renderer",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "url": "https://github.com/Delwing/mudlet-map-renderer"
@@ -1 +0,0 @@
1
- {"version":3,"file":"SkeletonMapReader-B6PzEWGP.js","names":[],"sources":["../src/bigmap/Skeleton.ts","../src/bigmap/PlaneIndex.ts","../src/bigmap/SkeletonMapReader.ts"],"sourcesContent":["/**\n * Compact, fully-resident skeleton of an entire map: one slot per room across\n * parallel typed arrays. This is what lets a multi-million-room map stay in\n * memory — positions/env/area/id as flat columns, no per-room object graph.\n *\n * @public seam — this is the stable contract between skeleton producers and\n * {@link SkeletonMapReader}. Producers include {@link buildSkeleton} (eager,\n * over an already-parsed map) and out-of-process sources such as a Web Worker\n * streaming a Mudlet `.dat` (see `demo/streaming/streaming-worker.ts`). New\n * fields must be optional.\n *\n * Coordinates are RAW Mudlet map space: `y` is stored exactly as it appears in\n * the map data. `SkeletonMapReader` converts to renderer space (negated y)\n * once at construction — producers never need to know renderer conventions.\n *\n * Array ownership transfers to the consumer: `SkeletonMapReader` mutates the\n * arrays in place (the renderer-space conversion). Producers hand over freshly\n * built (or structured-clone-transferred) arrays and must not reuse them.\n */\nexport interface MapSkeleton {\n count: number;\n x: Int32Array;\n y: Int32Array;\n z: Int32Array;\n area: Int32Array;\n env: Int32Array;\n id: Int32Array;\n /** 12 cardinal exit targets per room (row-major: room i → exits[i*12 + d]); -1 = none. */\n exits: Int32Array;\n areaNames: Record<number, string>;\n /** Areas drawn as adjacency grids — cardinal exit lines are suppressed for these. */\n areaGridMode: Record<number, boolean>;\n customEnvColors: Record<number, {r: number; g: number; b: number}>;\n /** Per-room name in slot order (parallel to the typed arrays). */\n names?: string[];\n /** userData only for rooms that have any (sparse). */\n userData?: {id: number; data: Record<string, string>}[];\n /**\n * Fully-materialised rooms that carry visual detail (custom lines, symbols,\n * special exits, doors, stubs, exit locks). The bulk of the map stays\n * skeleton-only; these override the synthesised room for their id.\n * Raw map space, like the arrays.\n */\n detailRooms?: MapData.Room[];\n labels?: MapData.Label[];\n}\n\n/**\n * Cardinal exit slots per room, in packing order: room i's exit in direction\n * `SKELETON_DIRS[d]` lives at `exits[i * 12 + d]`.\n */\nexport const SKELETON_DIRS: readonly MapData.direction[] = [\n \"north\", \"northeast\", \"east\", \"southeast\", \"south\", \"southwest\",\n \"west\", \"northwest\", \"up\", \"down\", \"in\", \"out\",\n];\n","import type {ViewportBounds} from \"../types/Settings\";\nimport type {MapSkeleton} from \"./Skeleton\";\n\n/**\n * A (area, z) plane's room slots bucketed into a uniform grid via counting\n * sort. Queries touch only the cells a viewport overlaps, so visiting the\n * rooms in view is O(occupied cells + hits) instead of O(plane).\n */\nexport interface PlaneIndex {\n /** Skeleton slot indices of every room on this plane. */\n indices: Int32Array;\n minX: number;\n minY: number;\n /** Cell size in map units. */\n cs: number;\n cols: number;\n rows: number;\n /** Prefix sums: cell c's members are order[cellStart[c] .. cellStart[c+1]). */\n cellStart: Int32Array;\n order: Int32Array;\n /** Full plane bounds (same coordinate space as the skeleton). */\n bounds: ViewportBounds;\n}\n\n/**\n * Bucket every room of one (area, z) plane into a uniform grid. The cell size\n * targets a 128×128 grid over the plane's extent — coarse enough that the\n * cellStart array stays small, fine enough that a viewport query skips the\n * overwhelming majority of a dense plane.\n */\nexport function buildPlaneIndex(sk: MapSkeleton, areaId: number, z: number): PlaneIndex {\n const tmp: number[] = [];\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\n for (let i = 0; i < sk.count; i++) {\n if (sk.area[i] !== areaId || sk.z[i] !== z) continue;\n tmp.push(i);\n const x = sk.x[i], y = sk.y[i];\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n const indices = Int32Array.from(tmp);\n if (indices.length === 0) {\n return {indices, minX: 0, minY: 0, cs: 1, cols: 1, rows: 1,\n cellStart: new Int32Array(2), order: indices,\n bounds: {minX: 0, maxX: 0, minY: 0, maxY: 0}};\n }\n\n const extent = Math.max(maxX - minX, maxY - minY, 1);\n const cs = Math.max(1, Math.ceil(extent / 128));\n const cols = Math.floor((maxX - minX) / cs) + 1;\n const rows = Math.floor((maxY - minY) / cs) + 1;\n const cellOf = (i: number) =>\n Math.floor((sk.y[i] - minY) / cs) * cols + Math.floor((sk.x[i] - minX) / cs);\n\n const counts = new Int32Array(cols * rows);\n for (let k = 0; k < indices.length; k++) counts[cellOf(indices[k])]++;\n const cellStart = new Int32Array(cols * rows + 1);\n for (let c = 0; c < cols * rows; c++) cellStart[c + 1] = cellStart[c] + counts[c];\n const cursor = cellStart.slice(0, cols * rows);\n const order = new Int32Array(indices.length);\n for (let k = 0; k < indices.length; k++) {\n const i = indices[k];\n const c = cellOf(i);\n order[cursor[c]++] = i;\n }\n\n return {indices, minX, minY, cs, cols, rows, cellStart, order, bounds: {minX, maxX, minY, maxY}};\n}\n\nfunction cellRange(p: PlaneIndex, b: ViewportBounds):\n {cx0: number; cx1: number; cy0: number; cy1: number} | null {\n const cx0 = Math.max(0, Math.floor((b.minX - p.minX) / p.cs));\n const cx1 = Math.min(p.cols - 1, Math.floor((b.maxX - p.minX) / p.cs));\n const cy0 = Math.max(0, Math.floor((b.minY - p.minY) / p.cs));\n const cy1 = Math.min(p.rows - 1, Math.floor((b.maxY - p.minY) / p.cs));\n return cx1 < cx0 || cy1 < cy0 ? null : {cx0, cx1, cy0, cy1};\n}\n\n/**\n * Visit the skeleton slot of every room whose centre lies inside `b` — exact\n * (each candidate is bounds-tested), uncapped. This is the hot path for both\n * room materialisation and raster painting; it never allocates.\n */\nexport function forEachInBounds(\n sk: MapSkeleton, p: PlaneIndex, b: ViewportBounds, fn: (i: number) => void,\n): void {\n const r = cellRange(p, b);\n if (!r) return;\n const {cols, cellStart, order} = p;\n for (let cy = r.cy0; cy <= r.cy1; cy++) {\n const base = cy * cols;\n for (let cx = r.cx0; cx <= r.cx1; cx++) {\n const c = base + cx;\n for (let q = cellStart[c]; q < cellStart[c + 1]; q++) {\n const i = order[q];\n if (sk.x[i] >= b.minX && sk.x[i] <= b.maxX &&\n sk.y[i] >= b.minY && sk.y[i] <= b.maxY) fn(i);\n }\n }\n }\n}\n\n/**\n * Cheap UPPER BOUND on the rooms inside `b`: sums the occupancy of every cell\n * the bounds overlap without per-room tests. Cells at the edge contribute\n * rooms that are actually outside — do not use this where exactness matters\n * (an earlier LOD used it as an exact count and flipped modes spuriously).\n */\nexport function countInBounds(p: PlaneIndex, b: ViewportBounds): number {\n const r = cellRange(p, b);\n if (!r) return 0;\n let total = 0;\n for (let cy = r.cy0; cy <= r.cy1; cy++) {\n const base = cy * p.cols;\n for (let cx = r.cx0; cx <= r.cx1; cx++) {\n const c = base + cx;\n total += p.cellStart[c + 1] - p.cellStart[c];\n }\n }\n return total;\n}\n","import type {IMapReader} from \"../reader/MapReader\";\nimport {type IArea, pairLinkExits} from \"../reader/Area\";\nimport type {IPlane} from \"../reader/Plane\";\nimport type IExit from \"../reader/Exit\";\nimport type {ViewportDataSource} from \"../reader/ViewportDataSource\";\nimport type {ViewportBounds} from \"../types/Settings\";\nimport type {MapSkeleton} from \"./Skeleton\";\nimport {SKELETON_DIRS} from \"./Skeleton\";\nimport type {PlaneIndex} from \"./PlaneIndex\";\nimport {buildPlaneIndex, countInBounds, forEachInBounds} from \"./PlaneIndex\";\n\nconst INFINITE_VIEWPORT: ViewportBounds = {\n minX: -Infinity, maxX: Infinity, minY: -Infinity, maxY: Infinity,\n};\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n let r = 0, g = 0, b = 0;\n if (h < 60) [r, g, b] = [c, x, 0];\n else if (h < 120) [r, g, b] = [x, c, 0];\n else if (h < 180) [r, g, b] = [0, c, x];\n else if (h < 240) [r, g, b] = [0, x, c];\n else if (h < 300) [r, g, b] = [x, 0, c];\n else [r, g, b] = [c, 0, x];\n return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];\n}\n\n/**\n * {@link IMapReader} over a {@link MapSkeleton}: rooms are synthesised on\n * demand from compact typed-array columns instead of held as an object graph,\n * so maps far too large for the concrete `MapReader` stay renderable.\n *\n * It is also a {@link ViewportDataSource}: planes materialise only the rooms\n * inside the current viewport. The interactive backend detects this and pushes\n * padded camera bounds before every build (rebuild-on-pan); standalone use\n * (e.g. `SvgExporter`) works too — the default viewport is infinite, so\n * everything materialises unless {@link setViewport} narrows it.\n *\n * Construction takes ownership of the skeleton: coordinates are converted to\n * renderer space (y negated, matching `MapReader`) in place.\n */\nexport default class SkeletonMapReader implements IMapReader, ViewportDataSource {\n readonly viewportAware = true as const;\n\n private readonly sk: MapSkeleton;\n private viewport: ViewportBounds = INFINITE_VIEWPORT;\n private version = 0;\n\n private readonly planeCache = new Map<string, PlaneIndex>();\n private readonly areaCache = new Map<number, SkeletonArea>();\n private readonly rgbCache = new Map<number, [number, number, number]>();\n private idIndex?: Map<number, number>;\n // Per-viewport materialisation cache so getRooms() and getLinkExits()\n // share one synthesized set within a single scene build.\n private readonly visibleCache = new Map<string, MapData.Room[]>();\n\n /** Fully-converted rooms with visual detail, keyed by id (override the skeleton synth). */\n private readonly detail = new Map<number, MapData.Room>();\n private readonly userDataMap = new Map<number, Record<string, string>>();\n\n constructor(skeleton: MapSkeleton) {\n this.sk = skeleton;\n // To renderer space, once: MapReader stores rooms as y = -room.y.\n const y = skeleton.y;\n for (let i = 0; i < skeleton.count; i++) y[i] = -y[i];\n for (const u of skeleton.userData ?? []) this.userDataMap.set(u.id, u.data);\n for (const r of skeleton.detailRooms ?? []) {\n r.y = -r.y;\n if (this.isGrid(r.area)) {\n // gridMode suppresses cardinal exit visuals; keep special exits / lines / symbol.\n r.exits = {} as Record<MapData.direction, number>;\n r.stubs = [];\n }\n this.detail.set(r.id, r);\n }\n }\n\n skeleton(): MapSkeleton {\n return this.sk;\n }\n\n // --- ViewportDataSource ---\n\n setViewport(bounds: ViewportBounds): void {\n const v = this.viewport;\n if (bounds.minX === v.minX && bounds.maxX === v.maxX &&\n bounds.minY === v.minY && bounds.maxY === v.maxY) return;\n this.viewport = {...bounds};\n this.visibleCache.clear();\n this.version++;\n }\n\n getViewport(): ViewportBounds {\n return this.viewport;\n }\n\n getPlaneRoomCount(areaId: number, z: number): number {\n return this.planeIndex(areaId, z).indices.length;\n }\n\n estimateVisibleCount(areaId: number, z: number, bounds: ViewportBounds): number {\n return countInBounds(this.planeIndex(areaId, z), bounds);\n }\n\n forEachInBounds(\n areaId: number, z: number, bounds: ViewportBounds,\n fn: (x: number, y: number, envId: number) => void,\n ): void {\n const sk = this.sk;\n forEachInBounds(sk, this.planeIndex(areaId, z), bounds,\n i => fn(sk.x[i], sk.y[i], sk.env[i]));\n }\n\n // --- IMapReader ---\n\n getArea(areaId: number): IArea {\n let a = this.areaCache.get(areaId);\n if (!a) {\n a = new SkeletonArea(areaId, this);\n this.areaCache.set(areaId, a);\n }\n return a;\n }\n\n getAreas(): IArea[] {\n return [...new Set(Array.from(this.sk.area))].map(id => this.getArea(id));\n }\n\n getRooms(): MapData.Room[] {\n // Never materialise all rooms — nothing in the hot path needs it.\n return [];\n }\n\n getRoom(roomId: number): MapData.Room {\n const d = this.detail.get(roomId);\n if (d) return d;\n if (!this.idIndex) {\n this.idIndex = new Map();\n for (let i = 0; i < this.sk.count; i++) this.idIndex.set(this.sk.id[i], i);\n }\n const i = this.idIndex.get(roomId);\n return i === undefined ? (undefined as unknown as MapData.Room) : this.makeRoom(i);\n }\n\n getColorValue(envId: number): string {\n const [r, g, b] = this.rgb(envId);\n return `rgb(${r},${g},${b})`;\n }\n\n getSymbolColor(envId: number, opacity?: number): string {\n const [r, g, b] = this.rgb(envId);\n const lum = (Math.max(r, g, b) + Math.min(r, g, b)) / 2 / 255;\n // Format matches MapReader.getSymbolColor exactly (incl. the space).\n const c = lum > 0.41 ? '25,25,25' : '225,255,255';\n const o = Math.min(Math.max(opacity ?? 1, 0), 1);\n return o !== 1 ? `rgba(${c}, ${o})` : `rgba(${c})`;\n }\n\n // --- internals shared with SkeletonArea / SkeletonPlane ---\n\n getReaderVersion(): number {\n return this.version;\n }\n\n labelsFor(areaId: number, z: number): MapData.Label[] {\n return (this.sk.labels ?? []).filter(l => l.areaId === areaId && l.Z === z);\n }\n\n isGrid(areaId: number): boolean {\n return !!this.sk.areaGridMode[areaId];\n }\n\n private rgb(envId: number): [number, number, number] {\n let v = this.rgbCache.get(envId);\n if (!v) {\n const c = this.sk.customEnvColors[envId];\n v = c ? [c.r, c.g, c.b] : hslToRgb(((envId * 2654435761) >>> 0) % 360, 0.5, 0.55);\n this.rgbCache.set(envId, v);\n }\n return v;\n }\n\n private makeRoom(i: number): MapData.Room {\n const sk = this.sk;\n const d = this.detail.get(sk.id[i]);\n if (d) return d; // full detail (special exits, custom lines, symbol, …)\n // Exits are only meaningful for non-grid areas; grid areas draw rooms as\n // adjacency with no exit lines (and would otherwise emit millions).\n const exits: Record<string, number> = {};\n if (!this.isGrid(sk.area[i])) {\n const base = i * 12;\n for (let dir = 0; dir < 12; dir++) {\n const t = sk.exits[base + dir];\n if (t !== -1) exits[SKELETON_DIRS[dir]] = t;\n }\n }\n return {\n id: sk.id[i],\n area: sk.area[i],\n x: sk.x[i],\n y: sk.y[i],\n z: sk.z[i],\n areaId: String(sk.area[i]),\n weight: 1,\n roomChar: '',\n name: sk.names?.[i] ?? '',\n userData: this.userDataMap.get(sk.id[i]) ?? {},\n customLines: {},\n stubs: [],\n hash: '',\n env: sk.env[i],\n exits: exits as Record<MapData.direction, number>,\n doors: {},\n specialExits: {},\n };\n }\n\n planeIndex(areaId: number, z: number): PlaneIndex {\n const key = `${areaId}:${z}`;\n let p = this.planeCache.get(key);\n if (!p) {\n p = buildPlaneIndex(this.sk, areaId, z);\n this.planeCache.set(key, p);\n }\n return p;\n }\n\n /**\n * Every room of the plane inside the current viewport — exact, uncapped\n * (the backend's LOD budget bounds how many this can be in vector mode),\n * memoised until the viewport changes.\n */\n visibleRooms(areaId: number, z: number): MapData.Room[] {\n const key = `${areaId}:${z}`;\n let rooms = this.visibleCache.get(key);\n if (!rooms) {\n const collected: MapData.Room[] = [];\n forEachInBounds(this.sk, this.planeIndex(areaId, z), this.viewport,\n i => collected.push(this.makeRoom(i)));\n rooms = collected;\n this.visibleCache.set(key, rooms);\n }\n return rooms;\n }\n}\n\nclass SkeletonArea implements IArea {\n private readonly planeCache = new Map<number, SkeletonPlane>();\n\n constructor(\n private readonly areaId: number,\n private readonly reader: SkeletonMapReader,\n ) {}\n\n getAreaName(): string {\n return this.reader.skeleton().areaNames[this.areaId] ?? `#${this.areaId}`;\n }\n getAreaId(): number {\n return this.areaId;\n }\n getVersion(): number {\n // Viewport-dependent content: the reader bumps its version on every\n // setViewport() with new bounds, which marks all cached-area state stale.\n return this.reader.getReaderVersion();\n }\n getPlane(zIndex: number): IPlane {\n let p = this.planeCache.get(zIndex);\n if (!p) {\n p = new SkeletonPlane(this.areaId, zIndex, this.reader);\n this.planeCache.set(zIndex, p);\n }\n return p;\n }\n getPlanes(): IPlane[] {\n return this.getZLevels().map(z => this.getPlane(z));\n }\n getZLevels(): number[] {\n const sk = this.reader.skeleton();\n const zs = new Set<number>();\n for (let i = 0; i < sk.count; i++) if (sk.area[i] === this.areaId) zs.add(sk.z[i]);\n return [...zs].sort((a, b) => a - b);\n }\n getRooms(): MapData.Room[] {\n return [];\n }\n getFullBounds(): {minX: number; maxX: number; minY: number; maxY: number} {\n const zs = this.getZLevels();\n let b = {minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity};\n for (const z of zs) {\n const pb = this.reader.planeIndex(this.areaId, z).bounds;\n b = {\n minX: Math.min(b.minX, pb.minX), maxX: Math.max(b.maxX, pb.maxX),\n minY: Math.min(b.minY, pb.minY), maxY: Math.max(b.maxY, pb.maxY),\n };\n }\n return b;\n }\n getLinkExits(zIndex: number): IExit[] {\n // Grid areas are adjacency grids — Mudlet draws no exit lines for them\n // (and a dense grid would emit millions).\n if (this.reader.isGrid(this.areaId)) return [];\n const rooms = this.reader.visibleRooms(this.areaId, zIndex);\n if (rooms.length === 0) return [];\n // Pair exits directly over the visible rooms (no throwaway Area/Plane —\n // that also grouped-by-z and computed bounds we never use, pure waste\n // since visibleRooms is already single-z). Exits to off-screen/other-area\n // rooms become one-way and still draw — the pipeline resolves the far\n // endpoint through reader.getRoom().\n return Array.from(pairLinkExits(rooms).values())\n .filter(e => e.zIndex.includes(zIndex));\n }\n}\n\nclass SkeletonPlane implements IPlane {\n constructor(\n private readonly areaId: number,\n private readonly z: number,\n private readonly reader: SkeletonMapReader,\n ) {}\n\n getRooms(): MapData.Room[] {\n return this.reader.visibleRooms(this.areaId, this.z);\n }\n getLabels(): MapData.Label[] {\n return this.reader.labelsFor(this.areaId, this.z);\n }\n getBounds(): {minX: number; maxX: number; minY: number; maxY: number} {\n // Full plane bounds (NOT the viewport) so fitArea frames the whole map.\n // Labels extend the bounds exactly like the concrete Plane's do.\n const b = {...this.reader.planeIndex(this.areaId, this.z).bounds};\n for (const label of this.reader.labelsFor(this.areaId, this.z)) {\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"],"mappings":"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"}