mudlet-map-renderer 1.0.0 → 1.1.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 +520 -0
- package/dist/AreaMapRenderer.d.ts +125 -0
- package/dist/CullingManager.d.ts +15 -0
- package/dist/ExitRenderer.d.ts +109 -0
- package/dist/InteractionHandler.d.ts +47 -0
- package/dist/MapGraph.d.ts +28 -0
- package/dist/MapReader-mU_4JWv_.js +224 -0
- package/dist/MapReader-mU_4JWv_.js.map +1 -0
- package/dist/MapState.d.ts +110 -0
- package/dist/PathData.d.ts +18 -0
- package/dist/PathFinder.d.ts +12 -0
- package/dist/ScenePipeline.d.ts +226 -0
- package/dist/SvgTypes.d.ts +21 -0
- package/dist/TypedEventEmitter.d.ts +17 -0
- package/dist/binary/BinaryMapReader.d.ts +36 -0
- package/dist/binary/index.d.ts +2 -0
- package/dist/binary.mjs +34 -0
- package/dist/binary.mjs.map +1 -0
- package/dist/camera/Camera.d.ts +106 -0
- package/dist/coord/CoordFn.d.ts +13 -0
- package/dist/directions.d.ts +25 -0
- package/dist/draw/DrawCommand.d.ts +104 -0
- package/dist/draw/DrawCommandBuilder.d.ts +19 -0
- package/dist/export/CanvasExporter.d.ts +71 -0
- package/dist/export/Exporter.d.ts +58 -0
- package/dist/export/PngExporter.d.ts +23 -0
- package/dist/export/SvgExporter.d.ts +19 -0
- package/dist/export/canvasToBytes.d.ts +19 -0
- package/dist/export/clipSceneToViewport.d.ts +48 -0
- package/dist/export/flushSceneShapes.d.ts +21 -0
- package/dist/export/sceneBounds.d.ts +24 -0
- package/dist/hit/HitTester.d.ts +101 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.mjs +6656 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lens/ExplorationLens.d.ts +29 -0
- package/dist/lens/RoomLens.d.ts +47 -0
- package/dist/lens/composeLenses.d.ts +31 -0
- package/dist/lens/index.d.ts +5 -0
- package/dist/overlay/AmbientLightOverlay.d.ts +32 -0
- package/dist/overlay/LiveEffect.d.ts +33 -0
- package/dist/overlay/SceneOverlay.d.ts +68 -0
- package/dist/reader/Area.d.ts +63 -0
- package/dist/reader/Exit.d.ts +15 -0
- package/dist/reader/MapReader.d.ts +34 -0
- package/dist/reader/Plane.d.ts +33 -0
- package/dist/render/CanvasRenderer.d.ts +17 -0
- package/dist/render/RecordingLayer.d.ts +158 -0
- package/dist/render/SvgRenderer.d.ts +7 -0
- package/dist/render/shapeToRecording.d.ts +3 -0
- package/dist/rendering/KonvaRenderBackend.d.ts +122 -0
- package/dist/rendering/MapRenderer.d.ts +213 -0
- package/dist/rendering/SceneManager.d.ts +54 -0
- package/dist/scene/AmbientLightStyle.d.ts +29 -0
- package/dist/scene/GridStyle.d.ts +21 -0
- package/dist/scene/InnerExitStyle.d.ts +24 -0
- package/dist/scene/OverlayStyle.d.ts +47 -0
- package/dist/scene/RoomStyle.d.ts +32 -0
- package/dist/scene/Shape.d.ts +151 -0
- package/dist/scene/SpecialExitStyle.d.ts +37 -0
- package/dist/scene/StubStyle.d.ts +15 -0
- package/dist/scene/elements/ExitLayout.d.ts +22 -0
- package/dist/scene/elements/GridLayout.d.ts +24 -0
- package/dist/scene/elements/LabelLayout.d.ts +11 -0
- package/dist/scene/elements/OverlayLayout.d.ts +14 -0
- package/dist/scene/elements/RoomLayout.d.ts +19 -0
- package/dist/scene/elements/SpecialExitLayout.d.ts +19 -0
- package/dist/scene/elements/StubLayout.d.ts +19 -0
- package/dist/style/Style.d.ts +60 -0
- package/dist/style/applyStyle.d.ts +3 -0
- package/dist/style/index.d.ts +21 -0
- package/dist/style/shape/BlueprintStyle.d.ts +11 -0
- package/dist/style/shape/ConstructionStyle.d.ts +17 -0
- package/dist/style/shape/IsometricStyle.d.ts +32 -0
- package/dist/style/shape/NeonStyle.d.ts +17 -0
- package/dist/style/shape/ParchmentStyle.d.ts +13 -0
- package/dist/style/shape/SciFiStyle.d.ts +16 -0
- package/dist/style/shape/SketchyStyle.d.ts +22 -0
- package/dist/style/shape/index.d.ts +25 -0
- package/dist/style/shape/paintMap.d.ts +25 -0
- package/dist/style/shape/wobble.d.ts +24 -0
- package/{src/types/MapData.ts → dist/types/MapData.d.ts} +70 -90
- package/dist/types/Settings.d.ts +206 -0
- package/dist/utils/color.d.ts +12 -0
- package/dist/utils/textMeasure.d.ts +38 -0
- package/package.json +67 -37
- package/demo/colors.json +0 -1
- package/demo/index.html +0 -102
- package/demo/main.ts +0 -389
- package/demo/mapExport.json +0 -1
- package/demo/tsconfig.json +0 -11
- package/src/ExitRenderer.ts +0 -275
- package/src/PathFinder.ts +0 -93
- package/src/PathRenderer.ts +0 -129
- package/src/Renderer.ts +0 -374
- package/src/directions.ts +0 -66
- package/src/index.ts +0 -4
- package/src/reader/Area.ts +0 -96
- package/src/reader/Exit.ts +0 -36
- package/src/reader/ExplorationArea.ts +0 -132
- package/src/reader/MapReader.ts +0 -123
- package/src/reader/Plane.ts +0 -42
- package/tsconfig.json +0 -17
- package/vercel.json +0 -5
- package/vite.config.ts +0 -29
- package/vite.demo.config.ts +0 -15
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { default as IExit } from './reader/Exit';
|
|
2
|
+
import { IMapReader } from './reader/MapReader';
|
|
3
|
+
import { Settings } from './types/Settings';
|
|
4
|
+
export type ExitDrawLine = {
|
|
5
|
+
points: number[];
|
|
6
|
+
stroke: string;
|
|
7
|
+
strokeWidth: number;
|
|
8
|
+
dash?: number[];
|
|
9
|
+
};
|
|
10
|
+
export type ExitDrawArrow = {
|
|
11
|
+
points: number[];
|
|
12
|
+
pointerLength: number;
|
|
13
|
+
pointerWidth: number;
|
|
14
|
+
stroke: string;
|
|
15
|
+
strokeWidth: number;
|
|
16
|
+
fill: string;
|
|
17
|
+
dash?: number[];
|
|
18
|
+
};
|
|
19
|
+
export type ExitDrawDoor = {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
stroke: string;
|
|
25
|
+
strokeWidth: number;
|
|
26
|
+
};
|
|
27
|
+
export type ExitDrawData = {
|
|
28
|
+
lines: ExitDrawLine[];
|
|
29
|
+
arrows: ExitDrawArrow[];
|
|
30
|
+
doors: ExitDrawDoor[];
|
|
31
|
+
bounds: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
};
|
|
37
|
+
/** Set when this exit leads to a room in a different area (cross-area exit). */
|
|
38
|
+
targetRoomId?: number;
|
|
39
|
+
/** Source room center in map coords — used to compute label direction. */
|
|
40
|
+
from?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
44
|
+
/** Arrow tip / far endpoint in map coords — anchor for placed labels. */
|
|
45
|
+
tip?: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
};
|
|
49
|
+
/** Stroke colour of the rendered arrow — used to colour area-exit labels. */
|
|
50
|
+
arrowColor?: string;
|
|
51
|
+
};
|
|
52
|
+
export default class ExitRenderer {
|
|
53
|
+
private mapReader;
|
|
54
|
+
private readonly settings;
|
|
55
|
+
constructor(mapReader: IMapReader, settings: Settings);
|
|
56
|
+
/**
|
|
57
|
+
* Get the edge point of a room based on its shape.
|
|
58
|
+
* The inset accounts for the inner border so exit lines reach the visible room edge.
|
|
59
|
+
*/
|
|
60
|
+
private getRoomEdgePoint;
|
|
61
|
+
renderData(exit: IExit, zIndex: number): ExitDrawData | undefined;
|
|
62
|
+
renderDataWithColor(exit: IExit, color: string, zIndex: number): ExitDrawData | undefined;
|
|
63
|
+
private renderTwoWayExitData;
|
|
64
|
+
private renderOneWayExitData;
|
|
65
|
+
/**
|
|
66
|
+
* Returns hit-zone bounds for special exits (custom lines) that lead to rooms in another area.
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Hit zones for cross-area inner exits (up/down/in/out). These are drawn as
|
|
70
|
+
* triangles inside the room, not through the exit pipeline, so they need
|
|
71
|
+
* their own plumbing for area-exit labelling and clickability.
|
|
72
|
+
*/
|
|
73
|
+
getInnerExitAreaTargets(room: MapData.Room): {
|
|
74
|
+
bounds: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
};
|
|
80
|
+
targetRoomId: number;
|
|
81
|
+
from: {
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
};
|
|
85
|
+
tip: {
|
|
86
|
+
x: number;
|
|
87
|
+
y: number;
|
|
88
|
+
};
|
|
89
|
+
arrowColor: string;
|
|
90
|
+
}[];
|
|
91
|
+
getSpecialExitAreaTargets(room: MapData.Room): {
|
|
92
|
+
bounds: {
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
};
|
|
98
|
+
targetRoomId: number;
|
|
99
|
+
from: {
|
|
100
|
+
x: number;
|
|
101
|
+
y: number;
|
|
102
|
+
};
|
|
103
|
+
tip: {
|
|
104
|
+
x: number;
|
|
105
|
+
y: number;
|
|
106
|
+
};
|
|
107
|
+
arrowColor: string;
|
|
108
|
+
}[];
|
|
109
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { RendererEventMap } from './types/Settings';
|
|
2
|
+
import { TypedEventEmitter } from './TypedEventEmitter';
|
|
3
|
+
import { Camera } from './camera/Camera';
|
|
4
|
+
import { MapState } from './MapState';
|
|
5
|
+
import { HitResult } from './hit/HitTester';
|
|
6
|
+
export type HitTestCallbacks = {
|
|
7
|
+
clientToMapPoint: (clientX: number, clientY: number) => {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
} | null;
|
|
11
|
+
/**
|
|
12
|
+
* Pick the topmost hit-annotated shape at a point in rendered space. The
|
|
13
|
+
* unified entry point for all DOM-level interaction — rooms, area-exit
|
|
14
|
+
* labels, and any future pickable kinds all flow through one query.
|
|
15
|
+
*/
|
|
16
|
+
pickAtPoint: (renderedX: number, renderedY: number) => HitResult | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Handles all DOM interaction on the map container:
|
|
20
|
+
* - Camera: drag (mouse + touch), wheel zoom, pinch zoom, resize
|
|
21
|
+
* - Map: hover cursor, click, right-click, long-press, area exit clicks
|
|
22
|
+
*
|
|
23
|
+
* No Konva dependency — works with any rendering backend.
|
|
24
|
+
*/
|
|
25
|
+
export declare class InteractionHandler {
|
|
26
|
+
private readonly container;
|
|
27
|
+
private readonly camera;
|
|
28
|
+
private readonly state;
|
|
29
|
+
private readonly hitTest;
|
|
30
|
+
private readonly events;
|
|
31
|
+
private lastPinchDistance?;
|
|
32
|
+
private readonly cleanupFns;
|
|
33
|
+
private destroyed;
|
|
34
|
+
/** Skip cursor changes when an external consumer (e.g. the editor) owns cursor management. */
|
|
35
|
+
private setCursor;
|
|
36
|
+
constructor(container: HTMLDivElement, camera: Camera, state: MapState, hitTest: HitTestCallbacks, events: TypedEventEmitter<RendererEventMap>);
|
|
37
|
+
destroy(): void;
|
|
38
|
+
private listen;
|
|
39
|
+
private initViewportEvents;
|
|
40
|
+
private handlePinch;
|
|
41
|
+
private initMapEvents;
|
|
42
|
+
private pickAtClientPoint;
|
|
43
|
+
private emitRoomClickEvent;
|
|
44
|
+
private emitRoomContextEvent;
|
|
45
|
+
private emitAreaExitClickEvent;
|
|
46
|
+
private emitMapClickEvent;
|
|
47
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IMapReader } from './reader/MapReader';
|
|
2
|
+
export interface Edge {
|
|
3
|
+
id: number;
|
|
4
|
+
weight: number;
|
|
5
|
+
}
|
|
6
|
+
export interface GraphData {
|
|
7
|
+
adj: Map<number, Edge[]>;
|
|
8
|
+
/** For node-dijkstra library */
|
|
9
|
+
graphDefinition: Record<string, Record<string, number>>;
|
|
10
|
+
maxEdgeDistance: number;
|
|
11
|
+
minEdgeWeight: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Builds a weighted adjacency graph from MapReader room/exit data.
|
|
15
|
+
* Separated from PathFinder so the graph can be reused and tested independently.
|
|
16
|
+
*/
|
|
17
|
+
export declare class MapGraph {
|
|
18
|
+
private readonly mapReader;
|
|
19
|
+
private readonly data;
|
|
20
|
+
constructor(mapReader: IMapReader);
|
|
21
|
+
getAdj(): Map<number, Edge[]>;
|
|
22
|
+
getGraphDefinition(): Record<string, Record<string, number>>;
|
|
23
|
+
getMaxEdgeDistance(): number;
|
|
24
|
+
getMinEdgeWeight(): number;
|
|
25
|
+
getRoom(roomId: number): MapData.Room;
|
|
26
|
+
private resolveEdgeWeight;
|
|
27
|
+
private buildGraph;
|
|
28
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
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 = class t {
|
|
34
|
+
constructor(e) {
|
|
35
|
+
this.planes = {}, this.exits = /* @__PURE__ */ new Map(), this.version = 0, this.area = e, this.planes = this.createPlanes(), this.createExits();
|
|
36
|
+
}
|
|
37
|
+
getAreaName() {
|
|
38
|
+
return this.area.areaName;
|
|
39
|
+
}
|
|
40
|
+
getAreaId() {
|
|
41
|
+
return parseInt(this.area.areaId);
|
|
42
|
+
}
|
|
43
|
+
getVersion() {
|
|
44
|
+
return this.version;
|
|
45
|
+
}
|
|
46
|
+
markDirty() {
|
|
47
|
+
this.version++;
|
|
48
|
+
}
|
|
49
|
+
getPlane(e) {
|
|
50
|
+
return this.planes[e];
|
|
51
|
+
}
|
|
52
|
+
getPlanes() {
|
|
53
|
+
return Object.values(this.planes);
|
|
54
|
+
}
|
|
55
|
+
getZLevels() {
|
|
56
|
+
return Object.keys(this.planes).map(Number).sort((e, t) => e - t);
|
|
57
|
+
}
|
|
58
|
+
getRooms() {
|
|
59
|
+
return this.area.rooms;
|
|
60
|
+
}
|
|
61
|
+
getFullBounds() {
|
|
62
|
+
return this.getPlanes().reduce((e, t) => {
|
|
63
|
+
let n = t.getBounds();
|
|
64
|
+
return {
|
|
65
|
+
minX: Math.min(e.minX, n.minX),
|
|
66
|
+
maxX: Math.max(e.maxX, n.maxX),
|
|
67
|
+
minY: Math.min(e.minY, n.minY),
|
|
68
|
+
maxY: Math.max(e.maxY, n.maxY)
|
|
69
|
+
};
|
|
70
|
+
}, {
|
|
71
|
+
minX: Infinity,
|
|
72
|
+
maxX: -Infinity,
|
|
73
|
+
minY: Infinity,
|
|
74
|
+
maxY: -Infinity
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
getLinkExits(e) {
|
|
78
|
+
return Array.from(this.exits.values()).filter((t) => t.zIndex.includes(e));
|
|
79
|
+
}
|
|
80
|
+
createPlanes() {
|
|
81
|
+
let t = this.area.rooms.reduce((e, t) => (e[t.z] || (e[t.z] = []), e[t.z].push(t), e), {});
|
|
82
|
+
return Object.entries(t).reduce((t, [n, r]) => (t[+n] = new e(r, this.area.labels.filter((e) => e.Z === +n)), t), {});
|
|
83
|
+
}
|
|
84
|
+
static {
|
|
85
|
+
this.oppositeDir = {
|
|
86
|
+
north: "south",
|
|
87
|
+
south: "north",
|
|
88
|
+
east: "west",
|
|
89
|
+
west: "east",
|
|
90
|
+
northeast: "southwest",
|
|
91
|
+
southwest: "northeast",
|
|
92
|
+
northwest: "southeast",
|
|
93
|
+
southeast: "northwest",
|
|
94
|
+
up: "down",
|
|
95
|
+
down: "up",
|
|
96
|
+
in: "out",
|
|
97
|
+
out: "in"
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
createExits() {
|
|
101
|
+
let e = /* @__PURE__ */ new Map();
|
|
102
|
+
this.area.rooms.forEach((t) => {
|
|
103
|
+
Object.entries(t.exits).forEach(([n, r]) => {
|
|
104
|
+
if (t.id === r) return;
|
|
105
|
+
let i = `${Math.min(t.id, r)}-${Math.max(t.id, r)}`;
|
|
106
|
+
e.has(i) || e.set(i, []), e.get(i).push({
|
|
107
|
+
origin: t.id,
|
|
108
|
+
target: r,
|
|
109
|
+
z: t.z,
|
|
110
|
+
dir: n
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
for (let [n, r] of e) {
|
|
115
|
+
let [e, i] = n.split("-"), a = parseInt(e), o = parseInt(i), s = r.filter((e) => e.origin === a), c = r.filter((e) => e.origin === o), l = /* @__PURE__ */ new Set();
|
|
116
|
+
for (let e of s) {
|
|
117
|
+
let r = -1;
|
|
118
|
+
for (let n = 0; n < c.length; n++) if (!l.has(n) && c[n].dir === t.oppositeDir[e.dir]) {
|
|
119
|
+
r = n;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (r !== -1) {
|
|
123
|
+
l.add(r);
|
|
124
|
+
let t = c[r];
|
|
125
|
+
this.exits.set(`${n}-${e.dir}`, {
|
|
126
|
+
a,
|
|
127
|
+
b: o,
|
|
128
|
+
aDir: e.dir,
|
|
129
|
+
bDir: t.dir,
|
|
130
|
+
zIndex: [e.z, t.z]
|
|
131
|
+
});
|
|
132
|
+
} else this.exits.set(`${n}-a:${e.dir}`, {
|
|
133
|
+
a,
|
|
134
|
+
b: o,
|
|
135
|
+
aDir: e.dir,
|
|
136
|
+
zIndex: [e.z]
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
for (let e = 0; e < c.length; e++) if (!l.has(e)) {
|
|
140
|
+
let t = c[e];
|
|
141
|
+
this.exits.set(`${n}-b:${t.dir}`, {
|
|
142
|
+
a,
|
|
143
|
+
b: o,
|
|
144
|
+
bDir: t.dir,
|
|
145
|
+
zIndex: [t.z]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}, n = {
|
|
151
|
+
rgb: [
|
|
152
|
+
114,
|
|
153
|
+
1,
|
|
154
|
+
0
|
|
155
|
+
],
|
|
156
|
+
rgbValue: "rgb(114, 1, 0)",
|
|
157
|
+
symbolColor: [
|
|
158
|
+
225,
|
|
159
|
+
225,
|
|
160
|
+
225
|
|
161
|
+
],
|
|
162
|
+
symbolColorValue: "rgb(225,225,225)"
|
|
163
|
+
};
|
|
164
|
+
function r(e) {
|
|
165
|
+
let t = e[0] / 255, n = e[1] / 255, r = e[2] / 255;
|
|
166
|
+
return (Math.max(t, n, r) + Math.min(t, n, r)) / 2;
|
|
167
|
+
}
|
|
168
|
+
var i = class {
|
|
169
|
+
constructor(e, n) {
|
|
170
|
+
this.rooms = {}, this.areas = {}, this.colors = {}, e.forEach((e) => {
|
|
171
|
+
let n = {
|
|
172
|
+
...e,
|
|
173
|
+
rooms: e.rooms.map((e) => ({
|
|
174
|
+
...e,
|
|
175
|
+
y: -e.y
|
|
176
|
+
}))
|
|
177
|
+
};
|
|
178
|
+
n.rooms.forEach((e) => {
|
|
179
|
+
this.rooms[e.id] = e;
|
|
180
|
+
});
|
|
181
|
+
let r = parseInt(e.areaId);
|
|
182
|
+
this.areas[r] = new t(n);
|
|
183
|
+
}), this.colors = n.reduce((e, t) => ({
|
|
184
|
+
...e,
|
|
185
|
+
[t.envId]: {
|
|
186
|
+
rgb: t.colors,
|
|
187
|
+
rgbValue: `rgb(${t.colors.join(",")})`,
|
|
188
|
+
symbolColor: r(t.colors) > .41 ? [
|
|
189
|
+
25,
|
|
190
|
+
25,
|
|
191
|
+
25
|
|
192
|
+
] : [
|
|
193
|
+
225,
|
|
194
|
+
255,
|
|
195
|
+
255
|
|
196
|
+
],
|
|
197
|
+
symbolColorValue: r(t.colors) > .41 ? "rgb(25,25,25)" : "rgb(225,255,255)"
|
|
198
|
+
}
|
|
199
|
+
}), {});
|
|
200
|
+
}
|
|
201
|
+
getArea(e) {
|
|
202
|
+
return this.areas[e];
|
|
203
|
+
}
|
|
204
|
+
getAreas() {
|
|
205
|
+
return Object.values(this.areas);
|
|
206
|
+
}
|
|
207
|
+
getRooms() {
|
|
208
|
+
return Object.values(this.rooms);
|
|
209
|
+
}
|
|
210
|
+
getRoom(e) {
|
|
211
|
+
return this.rooms[e];
|
|
212
|
+
}
|
|
213
|
+
getColorValue(e) {
|
|
214
|
+
return this.colors[e]?.rgbValue ?? n.rgbValue;
|
|
215
|
+
}
|
|
216
|
+
getSymbolColor(e, t) {
|
|
217
|
+
let r = this.colors[e]?.symbolColor ?? n.symbolColor, i = Math.min(Math.max(t ?? 1, 0), 1), a = r.join(",");
|
|
218
|
+
return i == 1 ? `rgba(${a})` : `rgba(${a}, ${i})`;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
//#endregion
|
|
222
|
+
export { t as n, e as r, i as t };
|
|
223
|
+
|
|
224
|
+
//# sourceMappingURL=MapReader-mU_4JWv_.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { IMapReader } from './reader/MapReader';
|
|
2
|
+
import { IArea } from './reader/Area';
|
|
3
|
+
import { IPlane } from './reader/Plane';
|
|
4
|
+
import { Settings } from './types/Settings';
|
|
5
|
+
import { SvgOverlays } from './SvgTypes';
|
|
6
|
+
import { TypedEventEmitter } from './TypedEventEmitter';
|
|
7
|
+
import { RoomLens } from './lens/RoomLens';
|
|
8
|
+
export type HighlightEntry = {
|
|
9
|
+
color: string;
|
|
10
|
+
area: number;
|
|
11
|
+
z: number;
|
|
12
|
+
};
|
|
13
|
+
export type PathEntry = {
|
|
14
|
+
locations: number[];
|
|
15
|
+
color: string;
|
|
16
|
+
};
|
|
17
|
+
export type MapStateEventMap = {
|
|
18
|
+
area: {
|
|
19
|
+
area: IArea;
|
|
20
|
+
zIndex: number;
|
|
21
|
+
};
|
|
22
|
+
position: {
|
|
23
|
+
roomId: number | undefined;
|
|
24
|
+
center: boolean;
|
|
25
|
+
areaChanged: boolean;
|
|
26
|
+
};
|
|
27
|
+
center: {
|
|
28
|
+
roomId: number;
|
|
29
|
+
instant: boolean;
|
|
30
|
+
};
|
|
31
|
+
highlight: {
|
|
32
|
+
roomId: number;
|
|
33
|
+
color: string | undefined;
|
|
34
|
+
};
|
|
35
|
+
path: undefined;
|
|
36
|
+
clear: undefined;
|
|
37
|
+
lens: {
|
|
38
|
+
lens: RoomLens;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Pure data + events layer for map state.
|
|
43
|
+
* No Konva, no DOM — just tracks what area is displayed,
|
|
44
|
+
* where the player is, which rooms are highlighted, and active paths.
|
|
45
|
+
*
|
|
46
|
+
* Rendering backends subscribe to events and sync their visual state.
|
|
47
|
+
*/
|
|
48
|
+
export declare class MapState {
|
|
49
|
+
readonly mapReader: IMapReader;
|
|
50
|
+
readonly settings: Settings;
|
|
51
|
+
readonly events: TypedEventEmitter<MapStateEventMap>;
|
|
52
|
+
currentArea?: number;
|
|
53
|
+
currentAreaInstance?: IArea;
|
|
54
|
+
currentZIndex?: number;
|
|
55
|
+
currentAreaVersion?: number;
|
|
56
|
+
positionRoomId?: number;
|
|
57
|
+
centerRoomId?: number;
|
|
58
|
+
highlights: Map<number, HighlightEntry>;
|
|
59
|
+
paths: PathEntry[];
|
|
60
|
+
lens: RoomLens;
|
|
61
|
+
constructor(mapReader: IMapReader, settings: Settings);
|
|
62
|
+
setLens(lens: RoomLens): void;
|
|
63
|
+
/**
|
|
64
|
+
* Set the displayed area and z-level.
|
|
65
|
+
* Returns true if the area/z actually changed (or area version bumped).
|
|
66
|
+
*/
|
|
67
|
+
setArea(id: number, zIndex: number): boolean;
|
|
68
|
+
setPosition(roomId: number, center?: boolean): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Update position marker without centering or switching area.
|
|
71
|
+
* Used when you want to show where the player is without moving the viewport.
|
|
72
|
+
*/
|
|
73
|
+
updatePositionMarker(roomId: number): void;
|
|
74
|
+
/**
|
|
75
|
+
* Re-emit position for the current room (e.g. after settings change).
|
|
76
|
+
*/
|
|
77
|
+
refreshPosition(): void;
|
|
78
|
+
clearPosition(): void;
|
|
79
|
+
setCenterRoom(roomId: number, instant?: boolean): boolean;
|
|
80
|
+
needsAreaRedraw(room: MapData.Room): boolean;
|
|
81
|
+
addHighlight(roomId: number, color: string): boolean;
|
|
82
|
+
removeHighlight(roomId: number): void;
|
|
83
|
+
hasHighlight(roomId: number): boolean;
|
|
84
|
+
clearHighlights(): void;
|
|
85
|
+
addPath(locations: number[], color?: string): void;
|
|
86
|
+
clearPaths(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Build the overlay descriptor for the current area/z,
|
|
89
|
+
* merging stateful overlays with any extra overlays passed in.
|
|
90
|
+
*/
|
|
91
|
+
getOverlaysForArea(extra?: SvgOverlays): SvgOverlays;
|
|
92
|
+
/**
|
|
93
|
+
* Get the effective plane bounds (respects uniformLevelSize setting).
|
|
94
|
+
*/
|
|
95
|
+
getEffectiveBounds(area: IArea, plane: IPlane): {
|
|
96
|
+
minX: number;
|
|
97
|
+
maxX: number;
|
|
98
|
+
minY: number;
|
|
99
|
+
maxY: number;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Compute export bounds for a given area/plane, optionally centered on a room.
|
|
103
|
+
*/
|
|
104
|
+
computeExportBounds(area: IArea, plane: IPlane, roomId: number | undefined, padding: number): {
|
|
105
|
+
x: number;
|
|
106
|
+
y: number;
|
|
107
|
+
w: number;
|
|
108
|
+
h: number;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IMapReader } from './reader/MapReader';
|
|
2
|
+
import { Settings } from './types/Settings';
|
|
3
|
+
export interface PathSegment {
|
|
4
|
+
points: number[];
|
|
5
|
+
}
|
|
6
|
+
export interface PathInnerMarker {
|
|
7
|
+
room: MapData.Room;
|
|
8
|
+
direction: MapData.direction;
|
|
9
|
+
}
|
|
10
|
+
export interface PathCustomLine {
|
|
11
|
+
points: number[];
|
|
12
|
+
}
|
|
13
|
+
export interface PathResult {
|
|
14
|
+
segments: PathSegment[];
|
|
15
|
+
innerMarkers: PathInnerMarker[];
|
|
16
|
+
customLines: PathCustomLine[];
|
|
17
|
+
}
|
|
18
|
+
export declare function computePathData(mapReader: IMapReader, settings: Settings, locations: number[], currentArea: number, currentZIndex: number): PathResult;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IMapReader } from './reader/MapReader';
|
|
2
|
+
export type PathFindingAlgorithm = 'dijkstra' | 'astar';
|
|
3
|
+
export default class PathFinder {
|
|
4
|
+
private readonly mapGraph;
|
|
5
|
+
private readonly dijkstraGraph;
|
|
6
|
+
private _algorithm;
|
|
7
|
+
private readonly cache;
|
|
8
|
+
constructor(mapReader: IMapReader, algorithm?: PathFindingAlgorithm);
|
|
9
|
+
get algorithm(): PathFindingAlgorithm;
|
|
10
|
+
setAlgorithm(algorithm: PathFindingAlgorithm): void;
|
|
11
|
+
findPath(from: number, to: number): number[] | null;
|
|
12
|
+
}
|