mudlet-map-renderer 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/Area-MLM4Xe0E.js +157 -0
- package/dist/Area-MLM4Xe0E.js.map +1 -0
- package/dist/MapReader-BeVNpm6y.js +77 -0
- package/dist/MapReader-BeVNpm6y.js.map +1 -0
- package/dist/SkeletonMapReader-B6PzEWGP.js +340 -0
- package/dist/SkeletonMapReader-B6PzEWGP.js.map +1 -0
- package/dist/bigmap/PlaneIndex.d.ts +42 -0
- package/dist/bigmap/Skeleton.d.ts +58 -0
- package/dist/bigmap/SkeletonMapReader.d.ts +59 -0
- package/dist/bigmap/buildSkeleton.d.ts +23 -0
- package/dist/bigmap/index.d.ts +24 -0
- package/dist/bigmap.mjs +60 -0
- package/dist/bigmap.mjs.map +1 -0
- package/dist/binary/convert.d.ts +19 -0
- package/dist/binary/index.d.ts +2 -0
- package/dist/binary/loadMudletMap.d.ts +49 -0
- package/dist/binary.mjs +194 -8
- package/dist/binary.mjs.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.mjs +821 -474
- package/dist/index.mjs.map +1 -1
- package/dist/reader/Area.d.ts +8 -2
- package/dist/reader/ViewportDataSource.d.ts +33 -0
- package/dist/render/CullIndex.d.ts +54 -0
- package/dist/rendering/KonvaRenderBackend.d.ts +68 -0
- package/dist/rendering/SceneManager.d.ts +28 -5
- package/dist/rendering/lod/LodController.d.ts +45 -0
- package/dist/rendering/lod/RasterOverview.d.ts +46 -0
- package/dist/rendering/lod/lodDecision.d.ts +45 -0
- package/dist/rendering/lod/roomsOnlyArea.d.ts +16 -0
- package/dist/types/Settings.d.ts +63 -0
- package/package.json +9 -4
- package/dist/MapReader-Brzg-X7T.js +0 -224
- package/dist/MapReader-Brzg-X7T.js.map +0 -1
package/README.md
CHANGED
|
@@ -186,6 +186,74 @@ For large maps, spatial culling hides off-screen rooms for better performance:
|
|
|
186
186
|
renderer.setCullingMode('indexed');
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
+
### Big maps (LOD + skeleton reader)
|
|
190
|
+
|
|
191
|
+
Very dense planes (tens of thousands to millions of rooms on one z-level)
|
|
192
|
+
can't be drawn as one vector scene. Two core features combine to handle them:
|
|
193
|
+
|
|
194
|
+
**Raster LOD overview** (`settings.lodEnabled`, works with any reader): when
|
|
195
|
+
the current plane holds more rooms than `settings.lodRoomBudget` (default
|
|
196
|
+
16 000) *and* the zoom is far enough out that a viewport could exceed that
|
|
197
|
+
budget, the vector scene is replaced by a pixel overview — one filled box per
|
|
198
|
+
room, painted on an underlay below the grid. Zooming in switches back to full
|
|
199
|
+
vector detail. The switch is zoom-based (pan-invariant, no mid-pan flips) and
|
|
200
|
+
reported through the `lod` renderer event
|
|
201
|
+
(`{mode: 'vector' | 'roomsOnly' | 'raster', planeRoomCount, visibleEstimate, hitTestActive}`).
|
|
202
|
+
Position marker, highlights, paths and overlays stay visible in both `raster`
|
|
203
|
+
and `roomsOnly` modes.
|
|
204
|
+
|
|
205
|
+
Between `settings.lodExitBudget` (default `12 000`) and `lodRoomBudget`, a
|
|
206
|
+
third tier — `roomsOnly` — drops exit lines while rooms keep rendering as
|
|
207
|
+
real vector shapes (env colour, border, shape): a bridge between full detail
|
|
208
|
+
and the flat raster pixels. Exit pairing and exit-line shape building are
|
|
209
|
+
typically the single largest share of a dense rebuild's cost (exit count
|
|
210
|
+
often runs ~2x room count in a well-connected area), so this tier buys back
|
|
211
|
+
most of that cost while still showing actual room geometry. Set
|
|
212
|
+
`lodExitBudget` above `lodRoomBudget` (or to `Infinity`) to disable this tier
|
|
213
|
+
and jump straight from full vector to raster.
|
|
214
|
+
|
|
215
|
+
Above `settings.lodHitTestBudget` (default `10 000` rooms, same unit as
|
|
216
|
+
`lodRoomBudget`) the scene still renders at full vector detail (with or
|
|
217
|
+
without exits) but the hit-test index is skipped, so clicks/hover stop
|
|
218
|
+
resolving to a room until you zoom in a bit further — rebuilding that index
|
|
219
|
+
is a real cost on a large scene, and precise pointer picking rarely matters
|
|
220
|
+
that zoomed out. Rebuild-on-pan also pads the viewport generously (50% per
|
|
221
|
+
side): panning inside that padding is free, so it buys a lot more distance
|
|
222
|
+
between rebuilds without making any single rebuild bigger — the LOD decision
|
|
223
|
+
compensates for the padding so the actual room count at each tier's flip
|
|
224
|
+
stays pinned near its configured budget regardless.
|
|
225
|
+
|
|
226
|
+
**`SkeletonMapReader`** (`mudlet-map-renderer/bigmap`): an `IMapReader` over a
|
|
227
|
+
compact typed-array `MapSkeleton` instead of a room object graph. Planes
|
|
228
|
+
materialise only the rooms inside the current viewport; the interactive
|
|
229
|
+
backend detects this (`ViewportDataSource`) and pushes padded camera bounds
|
|
230
|
+
before every build, rebuilding on pan only when the camera leaves the padding.
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { MapRenderer, createSettings } from 'mudlet-map-renderer';
|
|
234
|
+
import { buildSkeleton, SkeletonMapReader } from 'mudlet-map-renderer/bigmap';
|
|
235
|
+
|
|
236
|
+
// Eager path: the map parses fine, it's the rendering that chokes.
|
|
237
|
+
const reader = new SkeletonMapReader(buildSkeleton(mapData, colors));
|
|
238
|
+
const settings = { ...createSettings(), lodEnabled: true };
|
|
239
|
+
const renderer = new MapRenderer(reader, settings, container);
|
|
240
|
+
renderer.on('lod', ({ mode, visibleEstimate }) => console.log(mode, visibleEstimate));
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
For maps too large to parse eagerly (hundreds of MB), produce the
|
|
244
|
+
`MapSkeleton` out-of-process — e.g. a Web Worker that streams the binary map
|
|
245
|
+
and transfers the typed arrays zero-copy. `demo/streaming/` is a reference
|
|
246
|
+
implementation. `MapSkeleton` is a stable seam: coordinates are raw Mudlet map
|
|
247
|
+
space (the reader converts once at construction), bulk rooms live in parallel
|
|
248
|
+
`Int32Array` columns, and only rooms with visual detail (symbols, custom
|
|
249
|
+
lines, special exits…) are materialised in full.
|
|
250
|
+
|
|
251
|
+
Notes: exports through a viewport-narrowed reader only contain the applied
|
|
252
|
+
viewport (call `reader.setViewport(...)` with larger bounds first if you need
|
|
253
|
+
everything); `lodEnabled` is currently a no-op for the OffscreenCanvas
|
|
254
|
+
backend; decorator styles with coordinate transforms (e.g. isometric) are not
|
|
255
|
+
applied to the raster overview.
|
|
256
|
+
|
|
189
257
|
### OffscreenCanvas rendering (Web Worker)
|
|
190
258
|
|
|
191
259
|
For large maps, an opt-in backend moves per-frame rasterisation into a Web
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
//#region src/reader/Plane.ts
|
|
2
|
+
var e = class {
|
|
3
|
+
constructor(e, t) {
|
|
4
|
+
this.rooms = [], this.labels = [], this.rooms = e, this.labels = t, this.bounds = this.createBounds();
|
|
5
|
+
}
|
|
6
|
+
getRooms() {
|
|
7
|
+
return this.rooms;
|
|
8
|
+
}
|
|
9
|
+
getLabels() {
|
|
10
|
+
return this.labels;
|
|
11
|
+
}
|
|
12
|
+
getBounds() {
|
|
13
|
+
return this.bounds;
|
|
14
|
+
}
|
|
15
|
+
createBounds() {
|
|
16
|
+
let e = this.rooms.reduce((e, t) => ({
|
|
17
|
+
minX: Math.min(e.minX, t.x),
|
|
18
|
+
maxX: Math.max(e.maxX, t.x),
|
|
19
|
+
minY: Math.min(e.minY, t.y),
|
|
20
|
+
maxY: Math.max(e.maxY, t.y)
|
|
21
|
+
}), {
|
|
22
|
+
minX: Infinity,
|
|
23
|
+
maxX: -Infinity,
|
|
24
|
+
minY: Infinity,
|
|
25
|
+
maxY: -Infinity
|
|
26
|
+
});
|
|
27
|
+
for (let t of this.labels) {
|
|
28
|
+
let n = t.X, r = -t.Y;
|
|
29
|
+
e.minX = Math.min(e.minX, n), e.maxX = Math.max(e.maxX, n + t.Width), e.minY = Math.min(e.minY, r), e.maxY = Math.max(e.maxY, r + t.Height);
|
|
30
|
+
}
|
|
31
|
+
return e;
|
|
32
|
+
}
|
|
33
|
+
}, t = {
|
|
34
|
+
north: "south",
|
|
35
|
+
south: "north",
|
|
36
|
+
east: "west",
|
|
37
|
+
west: "east",
|
|
38
|
+
northeast: "southwest",
|
|
39
|
+
southwest: "northeast",
|
|
40
|
+
northwest: "southeast",
|
|
41
|
+
southeast: "northwest",
|
|
42
|
+
up: "down",
|
|
43
|
+
down: "up",
|
|
44
|
+
in: "out",
|
|
45
|
+
out: "in"
|
|
46
|
+
};
|
|
47
|
+
function n(e) {
|
|
48
|
+
let n = /* @__PURE__ */ new Map();
|
|
49
|
+
e.forEach((e) => {
|
|
50
|
+
Object.entries(e.exits).forEach(([t, r]) => {
|
|
51
|
+
if (e.id === r) return;
|
|
52
|
+
let i = `${Math.min(e.id, r)}-${Math.max(e.id, r)}`;
|
|
53
|
+
n.has(i) || n.set(i, []), n.get(i).push({
|
|
54
|
+
origin: e.id,
|
|
55
|
+
target: r,
|
|
56
|
+
z: e.z,
|
|
57
|
+
dir: t
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
let r = /* @__PURE__ */ new Map();
|
|
62
|
+
for (let [e, i] of n) {
|
|
63
|
+
let [n, a] = e.split("-"), o = parseInt(n), s = parseInt(a), c = i.filter((e) => e.origin === o), l = i.filter((e) => e.origin === s), u = /* @__PURE__ */ new Set();
|
|
64
|
+
for (let n of c) {
|
|
65
|
+
let i = -1;
|
|
66
|
+
for (let e = 0; e < l.length; e++) if (!u.has(e) && l[e].dir === t[n.dir]) {
|
|
67
|
+
i = e;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (i !== -1) {
|
|
71
|
+
u.add(i);
|
|
72
|
+
let t = l[i];
|
|
73
|
+
r.set(`${e}-${n.dir}`, {
|
|
74
|
+
a: o,
|
|
75
|
+
b: s,
|
|
76
|
+
aDir: n.dir,
|
|
77
|
+
bDir: t.dir,
|
|
78
|
+
zIndex: [n.z, t.z]
|
|
79
|
+
});
|
|
80
|
+
} else r.set(`${e}-a:${n.dir}`, {
|
|
81
|
+
a: o,
|
|
82
|
+
b: s,
|
|
83
|
+
aDir: n.dir,
|
|
84
|
+
zIndex: [n.z]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
for (let t = 0; t < l.length; t++) if (!u.has(t)) {
|
|
88
|
+
let n = l[t];
|
|
89
|
+
r.set(`${e}-b:${n.dir}`, {
|
|
90
|
+
a: o,
|
|
91
|
+
b: s,
|
|
92
|
+
bDir: n.dir,
|
|
93
|
+
zIndex: [n.z]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return r;
|
|
98
|
+
}
|
|
99
|
+
var r = class {
|
|
100
|
+
constructor(e) {
|
|
101
|
+
this.planes = {}, this.exits = /* @__PURE__ */ new Map(), this.version = 0, this.area = e, this.planes = this.createPlanes(), this.createExits();
|
|
102
|
+
}
|
|
103
|
+
getAreaName() {
|
|
104
|
+
return this.area.areaName;
|
|
105
|
+
}
|
|
106
|
+
getAreaId() {
|
|
107
|
+
return parseInt(this.area.areaId);
|
|
108
|
+
}
|
|
109
|
+
getVersion() {
|
|
110
|
+
return this.version;
|
|
111
|
+
}
|
|
112
|
+
markDirty() {
|
|
113
|
+
this.version++;
|
|
114
|
+
}
|
|
115
|
+
getPlane(e) {
|
|
116
|
+
return this.planes[e];
|
|
117
|
+
}
|
|
118
|
+
getPlanes() {
|
|
119
|
+
return Object.values(this.planes);
|
|
120
|
+
}
|
|
121
|
+
getZLevels() {
|
|
122
|
+
return Object.keys(this.planes).map(Number).sort((e, t) => e - t);
|
|
123
|
+
}
|
|
124
|
+
getRooms() {
|
|
125
|
+
return this.area.rooms;
|
|
126
|
+
}
|
|
127
|
+
getFullBounds() {
|
|
128
|
+
return this.getPlanes().reduce((e, t) => {
|
|
129
|
+
let n = t.getBounds();
|
|
130
|
+
return {
|
|
131
|
+
minX: Math.min(e.minX, n.minX),
|
|
132
|
+
maxX: Math.max(e.maxX, n.maxX),
|
|
133
|
+
minY: Math.min(e.minY, n.minY),
|
|
134
|
+
maxY: Math.max(e.maxY, n.maxY)
|
|
135
|
+
};
|
|
136
|
+
}, {
|
|
137
|
+
minX: Infinity,
|
|
138
|
+
maxX: -Infinity,
|
|
139
|
+
minY: Infinity,
|
|
140
|
+
maxY: -Infinity
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
getLinkExits(e) {
|
|
144
|
+
return Array.from(this.exits.values()).filter((t) => t.zIndex.includes(e));
|
|
145
|
+
}
|
|
146
|
+
createPlanes() {
|
|
147
|
+
let t = this.area.rooms.reduce((e, t) => (e[t.z] || (e[t.z] = []), e[t.z].push(t), e), {});
|
|
148
|
+
return Object.entries(t).reduce((t, [n, r]) => (t[+n] = new e(r, this.area.labels.filter((e) => e.Z === +n)), t), {});
|
|
149
|
+
}
|
|
150
|
+
createExits() {
|
|
151
|
+
this.exits = n(this.area.rooms);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
//#endregion
|
|
155
|
+
export { n, e as r, r as t };
|
|
156
|
+
|
|
157
|
+
//# sourceMappingURL=Area-MLM4Xe0E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Area-MLM4Xe0E.js","names":[],"sources":["../src/reader/Plane.ts","../src/reader/Area.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\nconst 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/**\n * Pair each room's half-exits into bidirectional {@link IExit}s (or one-way\n * fallbacks when a room's neighbour doesn't exit back). Exposed standalone so\n * callers with an already-filtered room list (e.g. a viewport-narrowed set)\n * can pair exits directly, without constructing a full {@link Area}.\n */\nexport function pairLinkExits(rooms: MapData.Room[]): Map<string, IExit> {\n type HalfExit = { origin: number, target: number, z: number, dir: MapData.direction };\n const halfExitsByPair = new Map<string, HalfExit[]>();\n\n 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 const exits = new Map<string, IExit>();\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 === 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 exits.set(`${pairKey}-${aHalf.dir}`, {\n a, b, aDir: aHalf.dir, bDir: bHalf.dir, zIndex: [aHalf.z, bHalf.z]\n });\n } else {\n 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 exits.set(`${pairKey}-b:${bHalf.dir}`, {\n a, b, bDir: bHalf.dir, zIndex: [bHalf.z]\n });\n }\n }\n }\n return exits;\n}\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 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 createExits() {\n this.exits = pairLinkExits(this.area.rooms);\n }\n\n}"],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { t as e } from "./Area-MLM4Xe0E.js";
|
|
2
|
+
//#region src/reader/MapReader.ts
|
|
3
|
+
var t = {
|
|
4
|
+
rgb: [
|
|
5
|
+
114,
|
|
6
|
+
1,
|
|
7
|
+
0
|
|
8
|
+
],
|
|
9
|
+
rgbValue: "rgb(114, 1, 0)",
|
|
10
|
+
symbolColor: [
|
|
11
|
+
225,
|
|
12
|
+
225,
|
|
13
|
+
225
|
|
14
|
+
],
|
|
15
|
+
symbolColorValue: "rgb(225,225,225)"
|
|
16
|
+
};
|
|
17
|
+
function n(e) {
|
|
18
|
+
let t = e[0] / 255, n = e[1] / 255, r = e[2] / 255;
|
|
19
|
+
return (Math.max(t, n, r) + Math.min(t, n, r)) / 2;
|
|
20
|
+
}
|
|
21
|
+
var r = class {
|
|
22
|
+
constructor(t, r) {
|
|
23
|
+
this.rooms = {}, this.areas = {}, this.colors = {}, t.forEach((t) => {
|
|
24
|
+
let n = {
|
|
25
|
+
...t,
|
|
26
|
+
rooms: t.rooms.map((e) => ({
|
|
27
|
+
...e,
|
|
28
|
+
y: -e.y
|
|
29
|
+
}))
|
|
30
|
+
};
|
|
31
|
+
n.rooms.forEach((e) => {
|
|
32
|
+
this.rooms[e.id] = e;
|
|
33
|
+
});
|
|
34
|
+
let r = parseInt(t.areaId);
|
|
35
|
+
this.areas[r] = new e(n);
|
|
36
|
+
}), this.colors = r.reduce((e, t) => ({
|
|
37
|
+
...e,
|
|
38
|
+
[t.envId]: {
|
|
39
|
+
rgb: t.colors,
|
|
40
|
+
rgbValue: `rgb(${t.colors.join(",")})`,
|
|
41
|
+
symbolColor: n(t.colors) > .41 ? [
|
|
42
|
+
25,
|
|
43
|
+
25,
|
|
44
|
+
25
|
|
45
|
+
] : [
|
|
46
|
+
225,
|
|
47
|
+
255,
|
|
48
|
+
255
|
|
49
|
+
],
|
|
50
|
+
symbolColorValue: n(t.colors) > .41 ? "rgb(25,25,25)" : "rgb(225,255,255)"
|
|
51
|
+
}
|
|
52
|
+
}), {});
|
|
53
|
+
}
|
|
54
|
+
getArea(e) {
|
|
55
|
+
return this.areas[e];
|
|
56
|
+
}
|
|
57
|
+
getAreas() {
|
|
58
|
+
return Object.values(this.areas);
|
|
59
|
+
}
|
|
60
|
+
getRooms() {
|
|
61
|
+
return Object.values(this.rooms);
|
|
62
|
+
}
|
|
63
|
+
getRoom(e) {
|
|
64
|
+
return this.rooms[e];
|
|
65
|
+
}
|
|
66
|
+
getColorValue(e) {
|
|
67
|
+
return this.colors[e]?.rgbValue ?? t.rgbValue;
|
|
68
|
+
}
|
|
69
|
+
getSymbolColor(e, n) {
|
|
70
|
+
let r = this.colors[e]?.symbolColor ?? t.symbolColor, i = Math.min(Math.max(n ?? 1, 0), 1), a = r.join(",");
|
|
71
|
+
return i == 1 ? `rgba(${a})` : `rgba(${a}, ${i})`;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
export { r as t };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=MapReader-BeVNpm6y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapReader-BeVNpm6y.js","names":[],"sources":["../src/reader/MapReader.ts"],"sourcesContent":["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":";;AA+BA,IAAM,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,340 @@
|
|
|
1
|
+
import { n as e } from "./Area-MLM4Xe0E.js";
|
|
2
|
+
//#region src/bigmap/Skeleton.ts
|
|
3
|
+
var t = [
|
|
4
|
+
"north",
|
|
5
|
+
"northeast",
|
|
6
|
+
"east",
|
|
7
|
+
"southeast",
|
|
8
|
+
"south",
|
|
9
|
+
"southwest",
|
|
10
|
+
"west",
|
|
11
|
+
"northwest",
|
|
12
|
+
"up",
|
|
13
|
+
"down",
|
|
14
|
+
"in",
|
|
15
|
+
"out"
|
|
16
|
+
];
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/bigmap/PlaneIndex.ts
|
|
19
|
+
function n(e, t, n) {
|
|
20
|
+
let r = [], i = Infinity, a = -Infinity, o = Infinity, s = -Infinity;
|
|
21
|
+
for (let c = 0; c < e.count; c++) {
|
|
22
|
+
if (e.area[c] !== t || e.z[c] !== n) continue;
|
|
23
|
+
r.push(c);
|
|
24
|
+
let l = e.x[c], u = e.y[c];
|
|
25
|
+
l < i && (i = l), l > a && (a = l), u < o && (o = u), u > s && (s = u);
|
|
26
|
+
}
|
|
27
|
+
let c = Int32Array.from(r);
|
|
28
|
+
if (c.length === 0) return {
|
|
29
|
+
indices: c,
|
|
30
|
+
minX: 0,
|
|
31
|
+
minY: 0,
|
|
32
|
+
cs: 1,
|
|
33
|
+
cols: 1,
|
|
34
|
+
rows: 1,
|
|
35
|
+
cellStart: new Int32Array(2),
|
|
36
|
+
order: c,
|
|
37
|
+
bounds: {
|
|
38
|
+
minX: 0,
|
|
39
|
+
maxX: 0,
|
|
40
|
+
minY: 0,
|
|
41
|
+
maxY: 0
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
let l = Math.max(a - i, s - o, 1), u = Math.max(1, Math.ceil(l / 128)), d = Math.floor((a - i) / u) + 1, f = Math.floor((s - o) / u) + 1, p = (t) => Math.floor((e.y[t] - o) / u) * d + Math.floor((e.x[t] - i) / u), m = new Int32Array(d * f);
|
|
45
|
+
for (let e = 0; e < c.length; e++) m[p(c[e])]++;
|
|
46
|
+
let h = new Int32Array(d * f + 1);
|
|
47
|
+
for (let e = 0; e < d * f; e++) h[e + 1] = h[e] + m[e];
|
|
48
|
+
let g = h.slice(0, d * f), _ = new Int32Array(c.length);
|
|
49
|
+
for (let e = 0; e < c.length; e++) {
|
|
50
|
+
let t = c[e], n = p(t);
|
|
51
|
+
_[g[n]++] = t;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
indices: c,
|
|
55
|
+
minX: i,
|
|
56
|
+
minY: o,
|
|
57
|
+
cs: u,
|
|
58
|
+
cols: d,
|
|
59
|
+
rows: f,
|
|
60
|
+
cellStart: h,
|
|
61
|
+
order: _,
|
|
62
|
+
bounds: {
|
|
63
|
+
minX: i,
|
|
64
|
+
maxX: a,
|
|
65
|
+
minY: o,
|
|
66
|
+
maxY: s
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function r(e, t) {
|
|
71
|
+
let n = Math.max(0, Math.floor((t.minX - e.minX) / e.cs)), r = Math.min(e.cols - 1, Math.floor((t.maxX - e.minX) / e.cs)), i = Math.max(0, Math.floor((t.minY - e.minY) / e.cs)), a = Math.min(e.rows - 1, Math.floor((t.maxY - e.minY) / e.cs));
|
|
72
|
+
return r < n || a < i ? null : {
|
|
73
|
+
cx0: n,
|
|
74
|
+
cx1: r,
|
|
75
|
+
cy0: i,
|
|
76
|
+
cy1: a
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function i(e, t, n, i) {
|
|
80
|
+
let a = r(t, n);
|
|
81
|
+
if (!a) return;
|
|
82
|
+
let { cols: o, cellStart: s, order: c } = t;
|
|
83
|
+
for (let t = a.cy0; t <= a.cy1; t++) {
|
|
84
|
+
let r = t * o;
|
|
85
|
+
for (let t = a.cx0; t <= a.cx1; t++) {
|
|
86
|
+
let a = r + t;
|
|
87
|
+
for (let t = s[a]; t < s[a + 1]; t++) {
|
|
88
|
+
let r = c[t];
|
|
89
|
+
e.x[r] >= n.minX && e.x[r] <= n.maxX && e.y[r] >= n.minY && e.y[r] <= n.maxY && i(r);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function a(e, t) {
|
|
95
|
+
let n = r(e, t);
|
|
96
|
+
if (!n) return 0;
|
|
97
|
+
let i = 0;
|
|
98
|
+
for (let t = n.cy0; t <= n.cy1; t++) {
|
|
99
|
+
let r = t * e.cols;
|
|
100
|
+
for (let t = n.cx0; t <= n.cx1; t++) {
|
|
101
|
+
let n = r + t;
|
|
102
|
+
i += e.cellStart[n + 1] - e.cellStart[n];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return i;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/bigmap/SkeletonMapReader.ts
|
|
109
|
+
var o = {
|
|
110
|
+
minX: -Infinity,
|
|
111
|
+
maxX: Infinity,
|
|
112
|
+
minY: -Infinity,
|
|
113
|
+
maxY: Infinity
|
|
114
|
+
};
|
|
115
|
+
function s(e, t, n) {
|
|
116
|
+
let r = (1 - Math.abs(2 * n - 1)) * t, i = r * (1 - Math.abs(e / 60 % 2 - 1)), a = n - r / 2, o = 0, s = 0, c = 0;
|
|
117
|
+
return e < 60 ? [o, s, c] = [
|
|
118
|
+
r,
|
|
119
|
+
i,
|
|
120
|
+
0
|
|
121
|
+
] : e < 120 ? [o, s, c] = [
|
|
122
|
+
i,
|
|
123
|
+
r,
|
|
124
|
+
0
|
|
125
|
+
] : e < 180 ? [o, s, c] = [
|
|
126
|
+
0,
|
|
127
|
+
r,
|
|
128
|
+
i
|
|
129
|
+
] : e < 240 ? [o, s, c] = [
|
|
130
|
+
0,
|
|
131
|
+
i,
|
|
132
|
+
r
|
|
133
|
+
] : e < 300 ? [o, s, c] = [
|
|
134
|
+
i,
|
|
135
|
+
0,
|
|
136
|
+
r
|
|
137
|
+
] : [o, s, c] = [
|
|
138
|
+
r,
|
|
139
|
+
0,
|
|
140
|
+
i
|
|
141
|
+
], [
|
|
142
|
+
Math.round((o + a) * 255),
|
|
143
|
+
Math.round((s + a) * 255),
|
|
144
|
+
Math.round((c + a) * 255)
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
var c = class {
|
|
148
|
+
constructor(e) {
|
|
149
|
+
this.viewportAware = !0, this.viewport = o, this.version = 0, this.planeCache = /* @__PURE__ */ new Map(), this.areaCache = /* @__PURE__ */ new Map(), this.rgbCache = /* @__PURE__ */ new Map(), this.visibleCache = /* @__PURE__ */ new Map(), this.detail = /* @__PURE__ */ new Map(), this.userDataMap = /* @__PURE__ */ new Map(), this.sk = e;
|
|
150
|
+
let t = e.y;
|
|
151
|
+
for (let n = 0; n < e.count; n++) t[n] = -t[n];
|
|
152
|
+
for (let t of e.userData ?? []) this.userDataMap.set(t.id, t.data);
|
|
153
|
+
for (let t of e.detailRooms ?? []) t.y = -t.y, this.isGrid(t.area) && (t.exits = {}, t.stubs = []), this.detail.set(t.id, t);
|
|
154
|
+
}
|
|
155
|
+
skeleton() {
|
|
156
|
+
return this.sk;
|
|
157
|
+
}
|
|
158
|
+
setViewport(e) {
|
|
159
|
+
let t = this.viewport;
|
|
160
|
+
e.minX === t.minX && e.maxX === t.maxX && e.minY === t.minY && e.maxY === t.maxY || (this.viewport = { ...e }, this.visibleCache.clear(), this.version++);
|
|
161
|
+
}
|
|
162
|
+
getViewport() {
|
|
163
|
+
return this.viewport;
|
|
164
|
+
}
|
|
165
|
+
getPlaneRoomCount(e, t) {
|
|
166
|
+
return this.planeIndex(e, t).indices.length;
|
|
167
|
+
}
|
|
168
|
+
estimateVisibleCount(e, t, n) {
|
|
169
|
+
return a(this.planeIndex(e, t), n);
|
|
170
|
+
}
|
|
171
|
+
forEachInBounds(e, t, n, r) {
|
|
172
|
+
let a = this.sk;
|
|
173
|
+
i(a, this.planeIndex(e, t), n, (e) => r(a.x[e], a.y[e], a.env[e]));
|
|
174
|
+
}
|
|
175
|
+
getArea(e) {
|
|
176
|
+
let t = this.areaCache.get(e);
|
|
177
|
+
return t || (t = new l(e, this), this.areaCache.set(e, t)), t;
|
|
178
|
+
}
|
|
179
|
+
getAreas() {
|
|
180
|
+
return [...new Set(Array.from(this.sk.area))].map((e) => this.getArea(e));
|
|
181
|
+
}
|
|
182
|
+
getRooms() {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
getRoom(e) {
|
|
186
|
+
let t = this.detail.get(e);
|
|
187
|
+
if (t) return t;
|
|
188
|
+
if (!this.idIndex) {
|
|
189
|
+
this.idIndex = /* @__PURE__ */ new Map();
|
|
190
|
+
for (let e = 0; e < this.sk.count; e++) this.idIndex.set(this.sk.id[e], e);
|
|
191
|
+
}
|
|
192
|
+
let n = this.idIndex.get(e);
|
|
193
|
+
return n === void 0 ? void 0 : this.makeRoom(n);
|
|
194
|
+
}
|
|
195
|
+
getColorValue(e) {
|
|
196
|
+
let [t, n, r] = this.rgb(e);
|
|
197
|
+
return `rgb(${t},${n},${r})`;
|
|
198
|
+
}
|
|
199
|
+
getSymbolColor(e, t) {
|
|
200
|
+
let [n, r, i] = this.rgb(e), a = (Math.max(n, r, i) + Math.min(n, r, i)) / 2 / 255 > .41 ? "25,25,25" : "225,255,255", o = Math.min(Math.max(t ?? 1, 0), 1);
|
|
201
|
+
return o === 1 ? `rgba(${a})` : `rgba(${a}, ${o})`;
|
|
202
|
+
}
|
|
203
|
+
getReaderVersion() {
|
|
204
|
+
return this.version;
|
|
205
|
+
}
|
|
206
|
+
labelsFor(e, t) {
|
|
207
|
+
return (this.sk.labels ?? []).filter((n) => n.areaId === e && n.Z === t);
|
|
208
|
+
}
|
|
209
|
+
isGrid(e) {
|
|
210
|
+
return !!this.sk.areaGridMode[e];
|
|
211
|
+
}
|
|
212
|
+
rgb(e) {
|
|
213
|
+
let t = this.rgbCache.get(e);
|
|
214
|
+
if (!t) {
|
|
215
|
+
let n = this.sk.customEnvColors[e];
|
|
216
|
+
t = n ? [
|
|
217
|
+
n.r,
|
|
218
|
+
n.g,
|
|
219
|
+
n.b
|
|
220
|
+
] : s((e * 2654435761 >>> 0) % 360, .5, .55), this.rgbCache.set(e, t);
|
|
221
|
+
}
|
|
222
|
+
return t;
|
|
223
|
+
}
|
|
224
|
+
makeRoom(e) {
|
|
225
|
+
let n = this.sk, r = this.detail.get(n.id[e]);
|
|
226
|
+
if (r) return r;
|
|
227
|
+
let i = {};
|
|
228
|
+
if (!this.isGrid(n.area[e])) {
|
|
229
|
+
let r = e * 12;
|
|
230
|
+
for (let e = 0; e < 12; e++) {
|
|
231
|
+
let a = n.exits[r + e];
|
|
232
|
+
a !== -1 && (i[t[e]] = a);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
id: n.id[e],
|
|
237
|
+
area: n.area[e],
|
|
238
|
+
x: n.x[e],
|
|
239
|
+
y: n.y[e],
|
|
240
|
+
z: n.z[e],
|
|
241
|
+
areaId: String(n.area[e]),
|
|
242
|
+
weight: 1,
|
|
243
|
+
roomChar: "",
|
|
244
|
+
name: n.names?.[e] ?? "",
|
|
245
|
+
userData: this.userDataMap.get(n.id[e]) ?? {},
|
|
246
|
+
customLines: {},
|
|
247
|
+
stubs: [],
|
|
248
|
+
hash: "",
|
|
249
|
+
env: n.env[e],
|
|
250
|
+
exits: i,
|
|
251
|
+
doors: {},
|
|
252
|
+
specialExits: {}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
planeIndex(e, t) {
|
|
256
|
+
let r = `${e}:${t}`, i = this.planeCache.get(r);
|
|
257
|
+
return i || (i = n(this.sk, e, t), this.planeCache.set(r, i)), i;
|
|
258
|
+
}
|
|
259
|
+
visibleRooms(e, t) {
|
|
260
|
+
let n = `${e}:${t}`, r = this.visibleCache.get(n);
|
|
261
|
+
if (!r) {
|
|
262
|
+
let a = [];
|
|
263
|
+
i(this.sk, this.planeIndex(e, t), this.viewport, (e) => a.push(this.makeRoom(e))), r = a, this.visibleCache.set(n, r);
|
|
264
|
+
}
|
|
265
|
+
return r;
|
|
266
|
+
}
|
|
267
|
+
}, l = class {
|
|
268
|
+
constructor(e, t) {
|
|
269
|
+
this.areaId = e, this.reader = t, this.planeCache = /* @__PURE__ */ new Map();
|
|
270
|
+
}
|
|
271
|
+
getAreaName() {
|
|
272
|
+
return this.reader.skeleton().areaNames[this.areaId] ?? `#${this.areaId}`;
|
|
273
|
+
}
|
|
274
|
+
getAreaId() {
|
|
275
|
+
return this.areaId;
|
|
276
|
+
}
|
|
277
|
+
getVersion() {
|
|
278
|
+
return this.reader.getReaderVersion();
|
|
279
|
+
}
|
|
280
|
+
getPlane(e) {
|
|
281
|
+
let t = this.planeCache.get(e);
|
|
282
|
+
return t || (t = new u(this.areaId, e, this.reader), this.planeCache.set(e, t)), t;
|
|
283
|
+
}
|
|
284
|
+
getPlanes() {
|
|
285
|
+
return this.getZLevels().map((e) => this.getPlane(e));
|
|
286
|
+
}
|
|
287
|
+
getZLevels() {
|
|
288
|
+
let e = this.reader.skeleton(), t = /* @__PURE__ */ new Set();
|
|
289
|
+
for (let n = 0; n < e.count; n++) e.area[n] === this.areaId && t.add(e.z[n]);
|
|
290
|
+
return [...t].sort((e, t) => e - t);
|
|
291
|
+
}
|
|
292
|
+
getRooms() {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
getFullBounds() {
|
|
296
|
+
let e = this.getZLevels(), t = {
|
|
297
|
+
minX: Infinity,
|
|
298
|
+
maxX: -Infinity,
|
|
299
|
+
minY: Infinity,
|
|
300
|
+
maxY: -Infinity
|
|
301
|
+
};
|
|
302
|
+
for (let n of e) {
|
|
303
|
+
let e = this.reader.planeIndex(this.areaId, n).bounds;
|
|
304
|
+
t = {
|
|
305
|
+
minX: Math.min(t.minX, e.minX),
|
|
306
|
+
maxX: Math.max(t.maxX, e.maxX),
|
|
307
|
+
minY: Math.min(t.minY, e.minY),
|
|
308
|
+
maxY: Math.max(t.maxY, e.maxY)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return t;
|
|
312
|
+
}
|
|
313
|
+
getLinkExits(t) {
|
|
314
|
+
if (this.reader.isGrid(this.areaId)) return [];
|
|
315
|
+
let n = this.reader.visibleRooms(this.areaId, t);
|
|
316
|
+
return n.length === 0 ? [] : Array.from(e(n).values()).filter((e) => e.zIndex.includes(t));
|
|
317
|
+
}
|
|
318
|
+
}, u = class {
|
|
319
|
+
constructor(e, t, n) {
|
|
320
|
+
this.areaId = e, this.z = t, this.reader = n;
|
|
321
|
+
}
|
|
322
|
+
getRooms() {
|
|
323
|
+
return this.reader.visibleRooms(this.areaId, this.z);
|
|
324
|
+
}
|
|
325
|
+
getLabels() {
|
|
326
|
+
return this.reader.labelsFor(this.areaId, this.z);
|
|
327
|
+
}
|
|
328
|
+
getBounds() {
|
|
329
|
+
let e = { ...this.reader.planeIndex(this.areaId, this.z).bounds };
|
|
330
|
+
for (let t of this.reader.labelsFor(this.areaId, this.z)) {
|
|
331
|
+
let n = t.X, r = -t.Y;
|
|
332
|
+
e.minX = Math.min(e.minX, n), e.maxX = Math.max(e.maxX, n + t.Width), e.minY = Math.min(e.minY, r), e.maxY = Math.max(e.maxY, r + t.Height);
|
|
333
|
+
}
|
|
334
|
+
return e;
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
//#endregion
|
|
338
|
+
export { t as a, i, n, a as r, c as t };
|
|
339
|
+
|
|
340
|
+
//# sourceMappingURL=SkeletonMapReader-B6PzEWGP.js.map
|