@kerros/viewer 0.0.0-stage → 0.1.0-rc.2
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/LICENSE +21 -0
- package/README.md +31 -2
- package/dist/adapters/basemap.js +11 -0
- package/dist/adapters/basemap.js.map +1 -0
- package/dist/adapters/persistence.js +121 -0
- package/dist/adapters/persistence.js.map +1 -0
- package/dist/adapters/status.js +18 -0
- package/dist/adapters/status.js.map +1 -0
- package/dist/components/ElevatorPanel.js +120 -0
- package/dist/components/ElevatorPanel.js.map +1 -0
- package/dist/components/Icons.js +57 -0
- package/dist/components/Icons.js.map +1 -0
- package/dist/components/Inspector.js +1184 -0
- package/dist/components/Inspector.js.map +1 -0
- package/dist/components/NavigatePanel.js +301 -0
- package/dist/components/NavigatePanel.js.map +1 -0
- package/dist/components/NavigationGraph.js +234 -0
- package/dist/components/NavigationGraph.js.map +1 -0
- package/dist/components/StructureExplorer.js +510 -0
- package/dist/components/StructureExplorer.js.map +1 -0
- package/dist/components/StructureView.js +286 -0
- package/dist/components/StructureView.js.map +1 -0
- package/dist/components/controls.js +170 -0
- package/dist/components/controls.js.map +1 -0
- package/dist/components/graphLayout.js +27 -0
- package/dist/components/graphLayout.js.map +1 -0
- package/dist/host.js +169 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n.js +88 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/map/CabinMaterials.js +81 -0
- package/dist/map/CabinMaterials.js.map +1 -0
- package/dist/map/CabinMirror.js +43 -0
- package/dist/map/CabinMirror.js.map +1 -0
- package/dist/map/Doors.js +80 -0
- package/dist/map/Doors.js.map +1 -0
- package/dist/map/ElevatorCabin.js +93 -0
- package/dist/map/ElevatorCabin.js.map +1 -0
- package/dist/map/FixtureLights.js +117 -0
- package/dist/map/FixtureLights.js.map +1 -0
- package/dist/map/MapCanvas.js +2161 -0
- package/dist/map/MapCanvas.js.map +1 -0
- package/dist/map/Passenger.js +69 -0
- package/dist/map/Passenger.js.map +1 -0
- package/dist/map/SceneLayer.js +1534 -0
- package/dist/map/SceneLayer.js.map +1 -0
- package/dist/map/UndergroundContext.js +153 -0
- package/dist/map/UndergroundContext.js.map +1 -0
- package/dist/map/ambience.js +194 -0
- package/dist/map/ambience.js.map +1 -0
- package/dist/map/architecture.js +305 -0
- package/dist/map/architecture.js.map +1 -0
- package/dist/map/bathMusic.js +92 -0
- package/dist/map/bathMusic.js.map +1 -0
- package/dist/map/cameraAim.js +33 -0
- package/dist/map/cameraAim.js.map +1 -0
- package/dist/map/elevators.js +56 -0
- package/dist/map/elevators.js.map +1 -0
- package/dist/map/excavation.js +52 -0
- package/dist/map/excavation.js.map +1 -0
- package/dist/map/exteriors.js +25 -0
- package/dist/map/exteriors.js.map +1 -0
- package/dist/map/features.js +218 -0
- package/dist/map/features.js.map +1 -0
- package/dist/map/fences.js +29 -0
- package/dist/map/fences.js.map +1 -0
- package/dist/map/journey.js +112 -0
- package/dist/map/journey.js.map +1 -0
- package/dist/map/levels.js +6 -0
- package/dist/map/levels.js.map +1 -0
- package/dist/map/lighting.js +142 -0
- package/dist/map/lighting.js.map +1 -0
- package/dist/map/loadBasemap.js +26 -0
- package/dist/map/loadBasemap.js.map +1 -0
- package/dist/map/materials.js +183 -0
- package/dist/map/materials.js.map +1 -0
- package/dist/map/poolLighting.js +39 -0
- package/dist/map/poolLighting.js.map +1 -0
- package/dist/map/projection.js +24 -0
- package/dist/map/projection.js.map +1 -0
- package/dist/map/rampGeometry.js +38 -0
- package/dist/map/rampGeometry.js.map +1 -0
- package/dist/map/route.js +107 -0
- package/dist/map/route.js.map +1 -0
- package/dist/map/stairSurfaces.js +115 -0
- package/dist/map/stairSurfaces.js.map +1 -0
- package/dist/map/surfaces.js +136 -0
- package/dist/map/surfaces.js.map +1 -0
- package/dist/map/textures.js +95 -0
- package/dist/map/textures.js.map +1 -0
- package/dist/map/underground.js +34 -0
- package/dist/map/underground.js.map +1 -0
- package/dist/map/walk.js +433 -0
- package/dist/map/walk.js.map +1 -0
- package/dist/map/walkContext.js +46 -0
- package/dist/map/walkContext.js.map +1 -0
- package/dist/map/walkDimensions.js +7 -0
- package/dist/map/walkDimensions.js.map +1 -0
- package/dist/map/walkJourney.js +85 -0
- package/dist/map/walkJourney.js.map +1 -0
- package/dist/map/walkSurfaces.js +106 -0
- package/dist/map/walkSurfaces.js.map +1 -0
- package/dist/map/wallJoins.js +70 -0
- package/dist/map/wallJoins.js.map +1 -0
- package/dist/map/water.js +274 -0
- package/dist/map/water.js.map +1 -0
- package/dist/model/authoring.js +80 -0
- package/dist/model/authoring.js.map +1 -0
- package/dist/model/boundaries.js +548 -0
- package/dist/model/boundaries.js.map +1 -0
- package/dist/model/coverage.js +30 -0
- package/dist/model/coverage.js.map +1 -0
- package/dist/model/document.js +52 -0
- package/dist/model/document.js.map +1 -0
- package/dist/model/doors.js +47 -0
- package/dist/model/doors.js.map +1 -0
- package/dist/model/factory.js +56 -0
- package/dist/model/factory.js.map +1 -0
- package/dist/model/geometry.js +394 -0
- package/dist/model/geometry.js.map +1 -0
- package/dist/model/inference.js +182 -0
- package/dist/model/inference.js.map +1 -0
- package/dist/model/materials.js +11 -0
- package/dist/model/materials.js.map +1 -0
- package/dist/model/mutations.js +157 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/navigation.js +275 -0
- package/dist/model/navigation.js.map +1 -0
- package/dist/model/ontology.js +109 -0
- package/dist/model/ontology.js.map +1 -0
- package/dist/model/passages.js +5 -0
- package/dist/model/passages.js.map +1 -0
- package/dist/model/portals.js +42 -0
- package/dist/model/portals.js.map +1 -0
- package/dist/model/precision.js +11 -0
- package/dist/model/precision.js.map +1 -0
- package/dist/model/project.js +17 -0
- package/dist/model/project.js.map +1 -0
- package/dist/model/ramps.js +10 -0
- package/dist/model/ramps.js.map +1 -0
- package/dist/model/roomNavigation.js +87 -0
- package/dist/model/roomNavigation.js.map +1 -0
- package/dist/model/routeClearance.js +72 -0
- package/dist/model/routeClearance.js.map +1 -0
- package/dist/model/spaces.js +104 -0
- package/dist/model/spaces.js.map +1 -0
- package/dist/model/stairGeometry.js +40 -0
- package/dist/model/stairGeometry.js.map +1 -0
- package/dist/model/stairTraversal.js +48 -0
- package/dist/model/stairTraversal.js.map +1 -0
- package/dist/model/topology.js +63 -0
- package/dist/model/topology.js.map +1 -0
- package/dist/model/types.js +35 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validate.js +240 -0
- package/dist/model/validate.js.map +1 -0
- package/dist/model/vertical.js +129 -0
- package/dist/model/vertical.js.map +1 -0
- package/dist/model/walls.js +71 -0
- package/dist/model/walls.js.map +1 -0
- package/dist/styles.css +4931 -0
- package/dist/theme/index.js +34 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/types/adapters/basemap.d.ts +2 -0
- package/dist/types/adapters/persistence.d.ts +29 -0
- package/dist/types/adapters/status.d.ts +4 -0
- package/dist/types/components/ElevatorPanel.d.ts +18 -0
- package/dist/types/components/Icons.d.ts +8 -0
- package/dist/types/components/Inspector.d.ts +38 -0
- package/dist/types/components/NavigatePanel.d.ts +27 -0
- package/dist/types/components/NavigationGraph.d.ts +10 -0
- package/dist/types/components/StructureExplorer.d.ts +8 -0
- package/dist/types/components/StructureView.d.ts +22 -0
- package/dist/types/components/controls.d.ts +52 -0
- package/dist/types/components/graphLayout.d.ts +9 -0
- package/dist/types/i18n.d.ts +85 -0
- package/dist/types/import/analysisPreview.d.ts +27 -0
- package/dist/types/import/brief.d.ts +22 -0
- package/dist/types/import/checkpoint.d.ts +49 -0
- package/dist/types/import/instructions.d.ts +7 -0
- package/dist/types/import/usage.d.ts +10 -0
- package/dist/types/map/CabinMaterials.d.ts +18 -0
- package/dist/types/map/CabinMirror.d.ts +13 -0
- package/dist/types/map/Doors.d.ts +11 -0
- package/dist/types/map/ElevatorCabin.d.ts +20 -0
- package/dist/types/map/FixtureLights.d.ts +35 -0
- package/dist/types/map/MapCanvas.d.ts +87 -0
- package/dist/types/map/Passenger.d.ts +18 -0
- package/dist/types/map/SceneLayer.d.ts +306 -0
- package/dist/types/map/UndergroundContext.d.ts +18 -0
- package/dist/types/map/ambience.d.ts +29 -0
- package/dist/types/map/architecture.d.ts +8 -0
- package/dist/types/map/bathMusic.d.ts +14 -0
- package/dist/types/map/cameraAim.d.ts +20 -0
- package/dist/types/map/elevators.d.ts +16 -0
- package/dist/types/map/excavation.d.ts +9 -0
- package/dist/types/map/exteriors.d.ts +3 -0
- package/dist/types/map/features.d.ts +33 -0
- package/dist/types/map/fences.d.ts +4 -0
- package/dist/types/map/journey.d.ts +36 -0
- package/dist/types/map/levels.d.ts +4 -0
- package/dist/types/map/lighting.d.ts +103 -0
- package/dist/types/map/loadBasemap.d.ts +4 -0
- package/dist/types/map/materials.d.ts +38 -0
- package/dist/types/map/poolLighting.d.ts +11 -0
- package/dist/types/map/projection.d.ts +9 -0
- package/dist/types/map/rampGeometry.d.ts +8 -0
- package/dist/types/map/route.d.ts +59 -0
- package/dist/types/map/stairSurfaces.d.ts +29 -0
- package/dist/types/map/surfaces.d.ts +36 -0
- package/dist/types/map/textures.d.ts +8 -0
- package/dist/types/map/underground.d.ts +30 -0
- package/dist/types/map/walk.d.ts +232 -0
- package/dist/types/map/walkContext.d.ts +11 -0
- package/dist/types/map/walkDimensions.d.ts +5 -0
- package/dist/types/map/walkJourney.d.ts +33 -0
- package/dist/types/map/walkSurfaces.d.ts +27 -0
- package/dist/types/map/wallJoins.d.ts +6 -0
- package/dist/types/map/water.d.ts +12 -0
- package/dist/types/model/authoring.d.ts +38 -0
- package/dist/types/model/boundaries.d.ts +41 -0
- package/dist/types/model/coverage.d.ts +8 -0
- package/dist/types/model/document.d.ts +9 -0
- package/dist/types/model/doors.d.ts +19 -0
- package/dist/types/model/factory.d.ts +5 -0
- package/dist/types/model/geometry.d.ts +99 -0
- package/dist/types/model/host.d.ts +154 -0
- package/dist/types/model/inference.d.ts +46 -0
- package/dist/types/model/live.d.ts +60 -0
- package/dist/types/model/materials.d.ts +28 -0
- package/dist/types/model/mutations.d.ts +178 -0
- package/dist/types/model/navigation.d.ts +70 -0
- package/dist/types/model/ontology.d.ts +43 -0
- package/dist/types/model/passages.d.ts +2 -0
- package/dist/types/model/portals.d.ts +9 -0
- package/dist/types/model/precision.d.ts +11 -0
- package/dist/types/model/project.d.ts +8 -0
- package/dist/types/model/ramps.d.ts +7 -0
- package/dist/types/model/roomNavigation.d.ts +15 -0
- package/dist/types/model/routeClearance.d.ts +11 -0
- package/dist/types/model/spaces.d.ts +46 -0
- package/dist/types/model/stairGeometry.d.ts +27 -0
- package/dist/types/model/stairTraversal.d.ts +8 -0
- package/dist/types/model/topology.d.ts +14 -0
- package/dist/types/model/types.d.ts +369 -0
- package/dist/types/model/validate.d.ts +38 -0
- package/dist/types/model/vertical.d.ts +145 -0
- package/dist/types/model/walls.d.ts +71 -0
- package/dist/types/schema/index.d.ts +25 -0
- package/dist/types/theme/index.d.ts +55 -0
- package/dist/types/viewer/FloorViewer.d.ts +76 -0
- package/dist/types/viewer/ViewerWorkspace.d.ts +2 -0
- package/dist/types/viewer/host.d.ts +11 -0
- package/dist/types/viewer/index.d.ts +2 -0
- package/dist/types/viewer/panels.d.ts +12 -0
- package/dist/viewer/FloorViewer.js +104 -0
- package/dist/viewer/FloorViewer.js.map +1 -0
- package/dist/viewer/ViewerWorkspace.js +370 -0
- package/dist/viewer/ViewerWorkspace.js.map +1 -0
- package/dist/viewer/panels.js +35 -0
- package/dist/viewer/panels.js.map +1 -0
- package/package.json +58 -4
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ringArea as p } from "../model/geometry.js";
|
|
2
|
+
import { primaryShafts as I, isVertical as v, servedFloors as M } from "../model/vertical.js";
|
|
3
|
+
const m = 96, f = 24, w = 32;
|
|
4
|
+
function E(s, c, u) {
|
|
5
|
+
const n = s.floors.find((e) => e.id === c), i = n ? s.floors.filter((e) => e.buildingId === n.buildingId) : s.floors, t = ((n == null ? void 0 : n.elevation) ?? 0) < 0 || u && i.length > 0 && i.every((e) => e.elevation < 0), r = i.reduce((e, g) => Math.min(e, g.elevation), 0), d = t && r < -m ? (m - f) / (-r - f) : 1, o = (e) => t && e < -f ? -f + (e + f) * d : e, a = o((n == null ? void 0 : n.elevation) ?? 0), l = d < 1 && (u || ((n == null ? void 0 : n.elevation) ?? 0) < -f);
|
|
6
|
+
return { active: n, levels: i, buried: t, depthScale: d, elevation: o, focusElevation: a, compressed: l };
|
|
7
|
+
}
|
|
8
|
+
const h = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
function A(s) {
|
|
10
|
+
const c = h.get(s);
|
|
11
|
+
if (c) return c;
|
|
12
|
+
const u = new Map(s.floors.map((o) => [o.id, o])), n = I(s), i = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
|
|
13
|
+
for (const o of s.objects) {
|
|
14
|
+
const a = i.get(o.floorId) ?? [];
|
|
15
|
+
if (a.push(o), i.set(o.floorId, a), v(o.kind) && n.has(o.id))
|
|
16
|
+
for (const e of M(s, o)) {
|
|
17
|
+
if (e.id === o.floorId) continue;
|
|
18
|
+
const g = r.get(e.id) ?? [];
|
|
19
|
+
g.push(o), r.set(e.id, g);
|
|
20
|
+
}
|
|
21
|
+
if (o.floorId === null || !o.rings || !["zone", "room"].includes(o.kind)) continue;
|
|
22
|
+
const l = t.get(o.floorId);
|
|
23
|
+
(!l || o.kind === "zone" && l.kind !== "zone" || o.kind === l.kind && p(o.rings[0]) > p(l.rings[0])) && t.set(o.floorId, o);
|
|
24
|
+
}
|
|
25
|
+
const d = { floors: u, objects: i, outlines: t, reaching: r, primary: n };
|
|
26
|
+
return h.set(s, d), d;
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
m as DEPTH_CAP,
|
|
30
|
+
w as MAX_CAGE_LEVELS,
|
|
31
|
+
A as floorIndex,
|
|
32
|
+
E as undergroundView
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=underground.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"underground.js","sources":["../../../../src/map/underground.ts"],"sourcesContent":["import type { Floor, ProjectDocument, SiteObject } from '../model/types';\nimport { ringArea } from '../model/geometry';\nimport { isVertical, primaryShafts, servedFloors } from '../model/vertical';\n\nexport const DEPTH_CAP = 96;\nconst SHALLOW_DEPTH = 24;\nexport const MAX_CAGE_LEVELS = 32;\n\n/** The camera, model, overlays and depth label must use the same presentation transform. */\nexport function undergroundView(project: ProjectDocument, floorId: string | null, stack: boolean) {\n const active = project.floors.find(f => f.id === floorId);\n const levels = active ? project.floors.filter(f => f.buildingId === active.buildingId) : project.floors;\n const buried = (active?.elevation ?? 0) < 0 || (stack && levels.length > 0 && levels.every(f => f.elevation < 0));\n const deepest = levels.reduce((z, f) => Math.min(z, f.elevation), 0);\n // One building-wide mapping in both modes: selecting L50 must not reset it to the same\n // displayed depth as L100. Preserve shallow basements; compress only the deeper shaft.\n const depthScale = buried && deepest < -DEPTH_CAP ? (DEPTH_CAP - SHALLOW_DEPTH) / (-deepest - SHALLOW_DEPTH) : 1;\n const elevation = (z: number) =>\n buried && z < -SHALLOW_DEPTH ? -SHALLOW_DEPTH + (z + SHALLOW_DEPTH) * depthScale : z;\n const focusElevation = elevation(active?.elevation ?? 0);\n const compressed = depthScale < 1 && (stack || (active?.elevation ?? 0) < -SHALLOW_DEPTH);\n return { active, levels, buried, depthScale, elevation, focusElevation, compressed };\n}\nexport type UndergroundView = ReturnType<typeof undergroundView>;\n\ninterface FloorIndex {\n floors: Map<string, Floor>;\n objects: Map<string | null, SiteObject[]>;\n outlines: Map<string, SiteObject>;\n /** Ids of the objects that stand for their shaft — see primaryShafts. Anything vertical NOT in\n * here is a twin of one that is, and is left to it to draw. */\n primary: Set<string>;\n /** Shafts that reach a floor without being filed under it. A stair lives on one storey and climbs\n * to another, and the storey it arrives at has to draw it or the flight appears to come from\n * nowhere — which is exactly what it did. Kept apart from `objects` so that everything already\n * counting a floor's own objects keeps counting the same ones. */\n reaching: Map<string, SiteObject[]>;\n}\nconst indices = new WeakMap<ProjectDocument, FloorIndex>();\n\n/** Project documents are immutable between edits. Floor navigation reuses this index. */\nexport function floorIndex(project: ProjectDocument): FloorIndex {\n const cached = indices.get(project);\n if (cached) return cached;\n const floors = new Map(project.floors.map(f => [f.id, f]));\n // Twins of one shaft draw once, from the lowest of them; the rest are the same lift seen again.\n const primary = primaryShafts(project);\n const objects = new Map<string | null, SiteObject[]>(),\n outlines = new Map<string, SiteObject>(),\n reaching = new Map<string, SiteObject[]>();\n for (const object of project.objects) {\n const group = objects.get(object.floorId) ?? [];\n group.push(object);\n objects.set(object.floorId, group);\n if (isVertical(object.kind) && primary.has(object.id))\n for (const f of servedFloors(project, object)) {\n if (f.id === object.floorId) continue;\n const arriving = reaching.get(f.id) ?? [];\n arriving.push(object);\n reaching.set(f.id, arriving);\n }\n if (object.floorId === null || !object.rings || !['zone', 'room'].includes(object.kind)) continue;\n const previous = outlines.get(object.floorId);\n if (\n !previous ||\n (object.kind === 'zone' && previous.kind !== 'zone') ||\n (object.kind === previous.kind && ringArea(object.rings[0]) > ringArea(previous.rings![0]))\n )\n outlines.set(object.floorId, object);\n }\n const result = { floors, objects, outlines, reaching, primary };\n indices.set(project, result);\n return result;\n}\n"],"names":["DEPTH_CAP","SHALLOW_DEPTH","MAX_CAGE_LEVELS","undergroundView","project","floorId","stack","active","f","levels","buried","deepest","z","depthScale","elevation","focusElevation","compressed","indices","floorIndex","cached","floors","primary","primaryShafts","objects","outlines","reaching","object","group","isVertical","servedFloors","arriving","previous","ringArea","result"],"mappings":";;AAIO,MAAMA,IAAY,IACnBC,IAAgB,IACTC,IAAkB;AAGxB,SAASC,EAAgBC,GAA0BC,GAAwBC,GAAgB;AAChG,QAAMC,IAASH,EAAQ,OAAO,KAAK,CAAAI,MAAKA,EAAE,OAAOH,CAAO,GAClDI,IAASF,IAASH,EAAQ,OAAO,OAAO,CAAAI,MAAKA,EAAE,eAAeD,EAAO,UAAU,IAAIH,EAAQ,QAC3FM,MAAUH,KAAA,gBAAAA,EAAQ,cAAa,KAAK,KAAMD,KAASG,EAAO,SAAS,KAAKA,EAAO,MAAM,CAAAD,MAAKA,EAAE,YAAY,CAAC,GACzGG,IAAUF,EAAO,OAAO,CAACG,GAAGJ,MAAM,KAAK,IAAII,GAAGJ,EAAE,SAAS,GAAG,CAAC,GAG7DK,IAAaH,KAAUC,IAAU,CAACX,KAAaA,IAAYC,MAAkB,CAACU,IAAUV,KAAiB,GACzGa,IAAY,CAACF,MACjBF,KAAUE,IAAI,CAACX,IAAgB,CAACA,KAAiBW,IAAIX,KAAiBY,IAAaD,GAC/EG,IAAiBD,GAAUP,KAAA,gBAAAA,EAAQ,cAAa,CAAC,GACjDS,IAAaH,IAAa,MAAMP,OAAUC,KAAA,gBAAAA,EAAQ,cAAa,KAAK,CAACN;AAC3E,SAAO,EAAE,QAAAM,GAAQ,QAAAE,GAAQ,QAAAC,GAAQ,YAAAG,GAAY,WAAAC,GAAW,gBAAAC,GAAgB,YAAAC,EAAA;AAC1E;AAgBA,MAAMC,wBAAc,QAAA;AAGb,SAASC,EAAWd,GAAsC;AAC/D,QAAMe,IAASF,EAAQ,IAAIb,CAAO;AAClC,MAAIe,EAAQ,QAAOA;AACnB,QAAMC,IAAS,IAAI,IAAIhB,EAAQ,OAAO,IAAI,CAAAI,MAAK,CAACA,EAAE,IAAIA,CAAC,CAAC,CAAC,GAEnDa,IAAUC,EAAclB,CAAO,GAC/BmB,IAAU,oBAAI,OAClBC,wBAAe,IAAA,GACfC,IAAW,oBAAI,IAAA;AACjB,aAAWC,KAAUtB,EAAQ,SAAS;AACpC,UAAMuB,IAAQJ,EAAQ,IAAIG,EAAO,OAAO,KAAK,CAAA;AAG7C,QAFAC,EAAM,KAAKD,CAAM,GACjBH,EAAQ,IAAIG,EAAO,SAASC,CAAK,GAC7BC,EAAWF,EAAO,IAAI,KAAKL,EAAQ,IAAIK,EAAO,EAAE;AAClD,iBAAWlB,KAAKqB,EAAazB,GAASsB,CAAM,GAAG;AAC7C,YAAIlB,EAAE,OAAOkB,EAAO,QAAS;AAC7B,cAAMI,IAAWL,EAAS,IAAIjB,EAAE,EAAE,KAAK,CAAA;AACvC,QAAAsB,EAAS,KAAKJ,CAAM,GACpBD,EAAS,IAAIjB,EAAE,IAAIsB,CAAQ;AAAA,MAC7B;AACF,QAAIJ,EAAO,YAAY,QAAQ,CAACA,EAAO,SAAS,CAAC,CAAC,QAAQ,MAAM,EAAE,SAASA,EAAO,IAAI,EAAG;AACzF,UAAMK,IAAWP,EAAS,IAAIE,EAAO,OAAO;AAC5C,KACE,CAACK,KACAL,EAAO,SAAS,UAAUK,EAAS,SAAS,UAC5CL,EAAO,SAASK,EAAS,QAAQC,EAASN,EAAO,MAAM,CAAC,CAAC,IAAIM,EAASD,EAAS,MAAO,CAAC,CAAC,MAEzFP,EAAS,IAAIE,EAAO,SAASA,CAAM;AAAA,EACvC;AACA,QAAMO,IAAS,EAAE,QAAAb,GAAQ,SAAAG,GAAS,UAAAC,GAAU,UAAAC,GAAU,SAAAJ,EAAA;AACtD,SAAAJ,EAAQ,IAAIb,GAAS6B,CAAM,GACpBA;AACT;"}
|
package/dist/map/walk.js
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
var D = Object.defineProperty;
|
|
2
|
+
var k = (s, t, i) => t in s ? D(s, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : s[t] = i;
|
|
3
|
+
var r = (s, t, i) => k(s, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import * as x from "maplibre-gl";
|
|
5
|
+
import { toLngLat as C } from "../model/geometry.js";
|
|
6
|
+
import { StairWalker as _ } from "./stairSurfaces.js";
|
|
7
|
+
import { cameraToCenterDistance as K } from "./cameraAim.js";
|
|
8
|
+
import { BODY as S, EYE as T } from "./walkDimensions.js";
|
|
9
|
+
import { HEAD_ROOM as ft } from "./walkDimensions.js";
|
|
10
|
+
import { supportedStep as V } from "./walkSurfaces.js";
|
|
11
|
+
const z = 3, Z = 6, W = 3.5, U = 120, H = 2.6, O = 35, v = 89, E = 86, ht = 26, N = 28, L = 1.25, R = 100, X = 60, Y = 120;
|
|
12
|
+
function A(s) {
|
|
13
|
+
return Number.isFinite(s) ? Math.min(Y, Math.max(X, s)) : R;
|
|
14
|
+
}
|
|
15
|
+
function j(s, t) {
|
|
16
|
+
return 2 * Math.atan(Math.tan(A(s) * Math.PI / 360) / t) * 180 / Math.PI;
|
|
17
|
+
}
|
|
18
|
+
const q = 0.22, B = 120, M = (s, t, i) => Math.min(i, Math.max(t, s)), m = (s) => (s % 360 + 360) % 360;
|
|
19
|
+
function Q(s, t, i) {
|
|
20
|
+
const e = i[0] - t[0], n = i[1] - t[1], o = e * e + n * n;
|
|
21
|
+
if (o < 1e-12) return t;
|
|
22
|
+
const a = M(((s[0] - t[0]) * e + (s[1] - t[1]) * n) / o, 0, 1);
|
|
23
|
+
return [t[0] + a * e, t[1] + a * n];
|
|
24
|
+
}
|
|
25
|
+
function I(s, t) {
|
|
26
|
+
let i = !1;
|
|
27
|
+
for (let e = 0, n = t.length - 1; e < t.length; n = e++) {
|
|
28
|
+
const [o, a] = t[e], [h, p] = t[n];
|
|
29
|
+
a > s[1] != p > s[1] && s[0] < (h - o) * (s[1] - a) / (p - a) + o && (i = !i);
|
|
30
|
+
}
|
|
31
|
+
return i;
|
|
32
|
+
}
|
|
33
|
+
function b(s, t, i = S) {
|
|
34
|
+
let e = s;
|
|
35
|
+
for (let n = 0; n < 2; n++) {
|
|
36
|
+
let o = !1;
|
|
37
|
+
for (const a of t) {
|
|
38
|
+
let h = null, p = 1 / 0;
|
|
39
|
+
for (let c = 0; c < a.length; c++) {
|
|
40
|
+
const u = Q(e, a[c], a[(c + 1) % a.length]), f = Math.hypot(e[0] - u[0], e[1] - u[1]);
|
|
41
|
+
f < p && (p = f, h = u);
|
|
42
|
+
}
|
|
43
|
+
if (!h || p >= i && !I(e, a)) continue;
|
|
44
|
+
let d = e[0] - h[0], l = e[1] - h[1];
|
|
45
|
+
I(e, a) && (d = -d, l = -l);
|
|
46
|
+
const g = Math.hypot(d, l);
|
|
47
|
+
g < 1e-9 || (e = [h[0] + d / g * i, h[1] + l / g * i], o = !0);
|
|
48
|
+
}
|
|
49
|
+
if (!o) break;
|
|
50
|
+
}
|
|
51
|
+
return e;
|
|
52
|
+
}
|
|
53
|
+
function G(s, t, i, e, n) {
|
|
54
|
+
const o = Math.min(n, v) * Math.PI / 180, a = e * Math.PI / 180, h = i / Math.cos(o), p = Math.sin(o) * Math.sin(a), d = -Math.sin(o) * Math.cos(a), l = x.MercatorCoordinate.fromLngLat({ lng: t[0], lat: t[1] }, i);
|
|
55
|
+
let g = l.meterInMercatorCoordinateUnits(), c = l;
|
|
56
|
+
for (let f = 0; f < 10; f++) {
|
|
57
|
+
const y = h * g;
|
|
58
|
+
c = new x.MercatorCoordinate(l.x + p * y, l.y + d * y, 0);
|
|
59
|
+
const P = c.meterInMercatorCoordinateUnits();
|
|
60
|
+
if (Math.abs(P - g) < 1e-18) break;
|
|
61
|
+
g = P;
|
|
62
|
+
}
|
|
63
|
+
const u = c.toLngLat();
|
|
64
|
+
return {
|
|
65
|
+
center: [u.lng, u.lat],
|
|
66
|
+
zoom: Math.log2(s.cameraToCenterDistance / (h * g) / s.tileSize)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function J(s, t, i, e, n = q) {
|
|
70
|
+
return { heading: m(s + i * n), pitch: M(t - e * n, O, v) };
|
|
71
|
+
}
|
|
72
|
+
function $(s, t, i) {
|
|
73
|
+
let e = t;
|
|
74
|
+
s.has("turnLeft") !== s.has("turnRight") && (e = m(e + (s.has("turnRight") ? 1 : -1) * B * i));
|
|
75
|
+
const n = (s.has("ahead") ? 1 : 0) - (s.has("back") ? 1 : 0), o = (s.has("right") ? 1 : 0) - (s.has("left") ? 1 : 0);
|
|
76
|
+
if (!n && !o) return { heading: e };
|
|
77
|
+
const a = e * Math.PI / 180, h = (s.has("fast") ? Z : z) * i * (n && o ? Math.SQRT1_2 : 1);
|
|
78
|
+
return {
|
|
79
|
+
heading: e,
|
|
80
|
+
step: [
|
|
81
|
+
(n * Math.sin(a) + o * Math.cos(a)) * h,
|
|
82
|
+
(n * Math.cos(a) - o * Math.sin(a)) * h
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function w(s, t, i = H) {
|
|
87
|
+
const e = [];
|
|
88
|
+
let n = 0;
|
|
89
|
+
for (let c = 1; c < s.length; c++) {
|
|
90
|
+
const u = Math.hypot(s[c][0] - s[c - 1][0], s[c][1] - s[c - 1][1]);
|
|
91
|
+
e.push(u), n += u;
|
|
92
|
+
}
|
|
93
|
+
const o = (c) => {
|
|
94
|
+
let u = M(c, 0, n);
|
|
95
|
+
for (let f = 0; f < e.length; f++) {
|
|
96
|
+
if (u <= e[f] || f === e.length - 1) {
|
|
97
|
+
const y = e[f] > 1e-9 ? M(u / e[f], 0, 1) : 0;
|
|
98
|
+
return [
|
|
99
|
+
s[f][0] + (s[f + 1][0] - s[f][0]) * y,
|
|
100
|
+
s[f][1] + (s[f + 1][1] - s[f][1]) * y
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
u -= e[f];
|
|
104
|
+
}
|
|
105
|
+
return s[s.length - 1];
|
|
106
|
+
}, a = o(t), h = o(Math.min(t, n - 1e-3)), p = o(Math.min(n, t + i)), d = p[0] - h[0], l = p[1] - h[1], g = Math.hypot(d, l) < 1e-6 ? 0 : m(Math.atan2(d, l) * 180 / Math.PI);
|
|
107
|
+
return { at: a, heading: g, done: t >= n, left: Math.max(0, n - t) };
|
|
108
|
+
}
|
|
109
|
+
function tt(s, t, i) {
|
|
110
|
+
const e = ((t - s + 540) % 360 + 360) % 360 - 180;
|
|
111
|
+
return m(s + M(e, -i, i));
|
|
112
|
+
}
|
|
113
|
+
const it = ["dragPan", "dragRotate", "scrollZoom", "keyboard", "touchZoomRotate", "doubleClickZoom"], F = {
|
|
114
|
+
KeyW: "ahead",
|
|
115
|
+
ArrowUp: "ahead",
|
|
116
|
+
KeyS: "back",
|
|
117
|
+
ArrowDown: "back",
|
|
118
|
+
KeyA: "turnLeft",
|
|
119
|
+
ArrowLeft: "turnLeft",
|
|
120
|
+
KeyD: "turnRight",
|
|
121
|
+
ArrowRight: "turnRight",
|
|
122
|
+
KeyQ: "left",
|
|
123
|
+
KeyE: "right",
|
|
124
|
+
ShiftLeft: "fast",
|
|
125
|
+
ShiftRight: "fast"
|
|
126
|
+
};
|
|
127
|
+
class lt {
|
|
128
|
+
constructor(t) {
|
|
129
|
+
r(this, "map");
|
|
130
|
+
r(this, "origin", [0, 0]);
|
|
131
|
+
r(this, "terrain", { walls: [], eye: T });
|
|
132
|
+
r(this, "keys", /* @__PURE__ */ new Set());
|
|
133
|
+
r(this, "frame", 0);
|
|
134
|
+
r(this, "last", 0);
|
|
135
|
+
r(this, "handlers", []);
|
|
136
|
+
r(this, "maxPitch", 60);
|
|
137
|
+
r(this, "maxZoom", 22);
|
|
138
|
+
r(this, "pixelRatio", 1);
|
|
139
|
+
r(this, "planFov", 0);
|
|
140
|
+
r(this, "fieldOfView", R);
|
|
141
|
+
r(this, "running", !1);
|
|
142
|
+
r(this, "dragPointer", null);
|
|
143
|
+
r(this, "drag", [0, 0]);
|
|
144
|
+
r(this, "path", null);
|
|
145
|
+
r(this, "travelled", 0);
|
|
146
|
+
r(this, "arrive");
|
|
147
|
+
r(this, "followOptions");
|
|
148
|
+
r(this, "fall");
|
|
149
|
+
r(this, "stairs", new _());
|
|
150
|
+
r(this, "position", [0, 0]);
|
|
151
|
+
r(this, "heading", 0);
|
|
152
|
+
r(this, "pitch", E);
|
|
153
|
+
r(this, "resize", () => {
|
|
154
|
+
const t = this.map;
|
|
155
|
+
if (!t) return;
|
|
156
|
+
const i = t.getCanvas();
|
|
157
|
+
i.clientWidth > 0 && i.clientHeight > 0 && t.setVerticalFieldOfView(j(this.fieldOfView, i.clientWidth / i.clientHeight)), this.apply();
|
|
158
|
+
});
|
|
159
|
+
/** Drag to look. Capture only delivers the remainder of this gesture if it leaves the canvas;
|
|
160
|
+
* the cursor stays visible and is free to operate panels as soon as the button is released. */
|
|
161
|
+
r(this, "grab", (t) => {
|
|
162
|
+
var e;
|
|
163
|
+
if (t.button !== 0 || t.pointerType === "touch" || this.dragPointer !== null) return;
|
|
164
|
+
const i = (e = this.map) == null ? void 0 : e.getCanvas();
|
|
165
|
+
if (i) {
|
|
166
|
+
t.preventDefault(), i.focus({ preventScroll: !0 }), this.dragPointer = t.pointerId, this.drag = [t.clientX, t.clientY], i.style.cursor = "grabbing";
|
|
167
|
+
try {
|
|
168
|
+
i.setPointerCapture(t.pointerId);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
this.on.onPose(this.pose);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
r(this, "drop", (t) => {
|
|
175
|
+
t.pointerId === this.dragPointer && this.endDrag();
|
|
176
|
+
});
|
|
177
|
+
r(this, "endDrag", () => {
|
|
178
|
+
var e;
|
|
179
|
+
const t = this.dragPointer;
|
|
180
|
+
if (t === null) return;
|
|
181
|
+
this.dragPointer = null;
|
|
182
|
+
const i = (e = this.map) == null ? void 0 : e.getCanvas();
|
|
183
|
+
i && (i.hasPointerCapture(t) && i.releasePointerCapture(t), i.style.cursor = "grab"), this.on.onPose(this.pose);
|
|
184
|
+
});
|
|
185
|
+
r(this, "release", () => {
|
|
186
|
+
this.keys.clear(), this.endDrag();
|
|
187
|
+
});
|
|
188
|
+
r(this, "look", (t) => {
|
|
189
|
+
if (t.pointerId !== this.dragPointer) return;
|
|
190
|
+
if (!(t.buttons & 1)) return this.endDrag();
|
|
191
|
+
const i = t.clientX - this.drag[0], e = t.clientY - this.drag[1];
|
|
192
|
+
if (this.drag = [t.clientX, t.clientY], !i && !e) return;
|
|
193
|
+
const n = J(this.heading, this.pitch, i, e);
|
|
194
|
+
this.heading = n.heading, this.pitch = n.pitch, this.apply();
|
|
195
|
+
});
|
|
196
|
+
r(this, "down", (t) => {
|
|
197
|
+
var n, o;
|
|
198
|
+
if (t.metaKey || t.ctrlKey || t.altKey) return;
|
|
199
|
+
const i = t.target;
|
|
200
|
+
if (i && (i.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(i.tagName))) return;
|
|
201
|
+
if (t.code === "Escape") {
|
|
202
|
+
t.preventDefault(), t.stopImmediatePropagation(), this.release(), this.on.onExit();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (t.code === "KeyF" || t.code === "Enter") {
|
|
206
|
+
t.preventDefault(), t.stopImmediatePropagation(), this.on.onUse(t.shiftKey);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (t.code === "KeyM") {
|
|
210
|
+
t.preventDefault(), t.stopImmediatePropagation(), (o = (n = this.on).onSound) == null || o.call(n);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const e = F[t.code];
|
|
214
|
+
e && (t.preventDefault(), t.stopImmediatePropagation(), this.keys.add(e));
|
|
215
|
+
});
|
|
216
|
+
r(this, "up", (t) => {
|
|
217
|
+
const i = F[t.code];
|
|
218
|
+
i && this.keys.delete(i);
|
|
219
|
+
});
|
|
220
|
+
r(this, "tick", (t) => {
|
|
221
|
+
var h, p, d;
|
|
222
|
+
if (!this.running) return;
|
|
223
|
+
this.frame = requestAnimationFrame(this.tick);
|
|
224
|
+
const i = Math.min((t - this.last) / 1e3, 0.25);
|
|
225
|
+
if (this.last = t, this.fall) {
|
|
226
|
+
this.fall.ready && (this.fall.remaining = Math.max(0, this.fall.remaining - this.fall.speed * i - 0.5 * 9.81 * i * i), this.fall.speed += 9.81 * i, this.fall.remaining === 0 && (this.fall = void 0), this.apply());
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (this.path)
|
|
230
|
+
if (this.keys.size) this.cancelFollow();
|
|
231
|
+
else return this.advance(i);
|
|
232
|
+
if (this.stairs.pending) return;
|
|
233
|
+
const e = (h = this.stairs.active) == null ? void 0 : h.drive;
|
|
234
|
+
if (!this.keys.size && !(e != null && e.some(Boolean))) return;
|
|
235
|
+
const n = $(this.keys, this.heading, i);
|
|
236
|
+
this.heading = n.heading;
|
|
237
|
+
const o = [
|
|
238
|
+
(((p = n.step) == null ? void 0 : p[0]) ?? 0) + ((e == null ? void 0 : e[0]) ?? 0) * i,
|
|
239
|
+
(((d = n.step) == null ? void 0 : d[1]) ?? 0) + ((e == null ? void 0 : e[1]) ?? 0) * i
|
|
240
|
+
], a = Math.max(1, Math.ceil(Math.hypot(...o) / 0.12));
|
|
241
|
+
for (let l = 0; l < a; l++) {
|
|
242
|
+
const g = b(
|
|
243
|
+
[this.position[0] + o[0] / a, this.position[1] + o[1] / a],
|
|
244
|
+
this.terrain.walls
|
|
245
|
+
);
|
|
246
|
+
if (this.position = this.stairs.step(
|
|
247
|
+
this.position,
|
|
248
|
+
g,
|
|
249
|
+
this.terrain.stairs ?? [],
|
|
250
|
+
this.terrain.floorId ?? null,
|
|
251
|
+
this.terrain.elevation ?? 0,
|
|
252
|
+
this.terrain.supports
|
|
253
|
+
), this.stairs.pending) break;
|
|
254
|
+
if (this.beginFall()) return;
|
|
255
|
+
}
|
|
256
|
+
if (this.stairs.pending && this.on.onFloor) {
|
|
257
|
+
this.apply(), this.on.onFloor(this.stairs.pending);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
this.apply();
|
|
261
|
+
});
|
|
262
|
+
this.on = t;
|
|
263
|
+
}
|
|
264
|
+
attach(t, i, e) {
|
|
265
|
+
var o;
|
|
266
|
+
this.map = t, this.origin = i, this.position = e.position, this.fall = void 0, this.heading = m(e.heading), this.pitch = M(e.pitch ?? E, O, v), this.running = !0, this.handlers = it.map((a) => {
|
|
267
|
+
var h;
|
|
268
|
+
return [a, ((h = t[a]) == null ? void 0 : h.isEnabled()) ?? !1];
|
|
269
|
+
});
|
|
270
|
+
for (const [a] of this.handlers) (o = t[a]) == null || o.disable();
|
|
271
|
+
this.maxPitch = t.getMaxPitch(), this.maxZoom = t.getMaxZoom(), this.planFov = t.getVerticalFieldOfView(), t.setMaxPitch(Math.max(this.maxPitch, v)), t.setMaxZoom(Math.max(this.maxZoom, N)), this.pixelRatio = t.getPixelRatio(), this.pixelRatio > L && t.setPixelRatio(L);
|
|
272
|
+
const n = t.getCanvas();
|
|
273
|
+
n.addEventListener("pointerdown", this.grab), n.addEventListener("pointermove", this.look), n.addEventListener("pointerup", this.drop), n.addEventListener("pointercancel", this.drop), n.addEventListener("lostpointercapture", this.drop), window.addEventListener("keydown", this.down, !0), window.addEventListener("keyup", this.up, !0), window.addEventListener("blur", this.release), t.on("resize", this.resize), this.last = performance.now(), this.frame = requestAnimationFrame(this.tick), n.style.cursor = "grab", this.resize();
|
|
274
|
+
}
|
|
275
|
+
detach() {
|
|
276
|
+
var e;
|
|
277
|
+
const t = this.map;
|
|
278
|
+
if (this.running = !1, this.cancelFollow(), cancelAnimationFrame(this.frame), this.keys.clear(), window.removeEventListener("keydown", this.down, !0), window.removeEventListener("keyup", this.up, !0), window.removeEventListener("blur", this.release), !t) return;
|
|
279
|
+
t.off("resize", this.resize);
|
|
280
|
+
const i = t.getCanvas();
|
|
281
|
+
i.removeEventListener("pointerdown", this.grab), i.removeEventListener("pointermove", this.look), i.removeEventListener("pointerup", this.drop), i.removeEventListener("pointercancel", this.drop), i.removeEventListener("lostpointercapture", this.drop), this.endDrag(), i.style.cursor = "";
|
|
282
|
+
for (const [n, o] of this.handlers) o && ((e = t[n]) == null || e.enable());
|
|
283
|
+
this.handlers = [], t.setMaxPitch(this.maxPitch), t.setMaxZoom(this.maxZoom), t.setVerticalFieldOfView(this.planFov), t.getPixelRatio() !== this.pixelRatio && t.setPixelRatio(this.pixelRatio), this.map = void 0;
|
|
284
|
+
}
|
|
285
|
+
/** The floor under the walker changed, or its walls were edited. */
|
|
286
|
+
setTerrain(t) {
|
|
287
|
+
this.terrain = t, this.stairs.sync(t.floorId ?? null, t.elevation ?? 0), this.stairs.refresh(t.stairs ?? [], this.position), this.fall && (t.floorId === this.fall.floorId ? this.fall.ready = !0 : this.fall = void 0), this.running && this.apply();
|
|
288
|
+
}
|
|
289
|
+
/** Widen the lens without changing the person's position, heading or eye height. */
|
|
290
|
+
setFieldOfView(t) {
|
|
291
|
+
this.fieldOfView = A(t), this.running && this.resize();
|
|
292
|
+
}
|
|
293
|
+
/** Walk a path on its own, as far as it goes on this floor, then call `onArrive`. Taking any
|
|
294
|
+
* control cancels it: a tour you cannot interrupt is a video. */
|
|
295
|
+
follow(t, i, e) {
|
|
296
|
+
const n = t.filter((a, h) => !h || Math.hypot(a[0] - t[h - 1][0], a[1] - t[h - 1][1]) > 0.01);
|
|
297
|
+
if (n.length < 2) return i();
|
|
298
|
+
this.path = n, this.travelled = 0, this.arrive = i, this.followOptions = e;
|
|
299
|
+
const o = w(n, 0);
|
|
300
|
+
this.position = o.at, this.running || (this.heading = o.heading), this.running && this.apply();
|
|
301
|
+
}
|
|
302
|
+
cancelFollow(t = !0) {
|
|
303
|
+
var e;
|
|
304
|
+
const i = (e = this.followOptions) == null ? void 0 : e.onCancel;
|
|
305
|
+
this.path = null, this.arrive = void 0, this.followOptions = void 0, t && (i == null || i());
|
|
306
|
+
}
|
|
307
|
+
get following() {
|
|
308
|
+
return !!this.path;
|
|
309
|
+
}
|
|
310
|
+
/** Put the walker somewhere — arriving at a new floor, or jumping to a route's start. */
|
|
311
|
+
place(t, i = this.heading) {
|
|
312
|
+
this.fall = void 0, this.stairs.reset(this.terrain.elevation ?? 0), this.position = t, this.heading = m(i), this.running && this.apply();
|
|
313
|
+
}
|
|
314
|
+
/** Inspect something from the current position. Only the walker writes a POV camera: panning
|
|
315
|
+
* its ground target like a plan camera would move the eye far behind the selected object. */
|
|
316
|
+
lookAt(t) {
|
|
317
|
+
const i = t[0] - this.position[0], e = t[1] - this.position[1];
|
|
318
|
+
Math.hypot(i, e) < 1e-6 || (this.heading = m(Math.atan2(i, e) * 180 / Math.PI), this.running && this.apply());
|
|
319
|
+
}
|
|
320
|
+
get pose() {
|
|
321
|
+
return {
|
|
322
|
+
position: this.position,
|
|
323
|
+
heading: this.heading,
|
|
324
|
+
pitch: this.pitch,
|
|
325
|
+
looking: this.dragPointer !== null
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
get onStairs() {
|
|
329
|
+
return !!this.stairs.active;
|
|
330
|
+
}
|
|
331
|
+
/** Stop at the first substep entering a hole, even when running over a narrow opening. */
|
|
332
|
+
beginFall() {
|
|
333
|
+
var i, e;
|
|
334
|
+
const t = !this.stairs.active && ((e = (i = this.terrain).dropAt) == null ? void 0 : e.call(i, this.position));
|
|
335
|
+
return t && this.on.onDrop ? (this.cancelFollow(), this.fall = { floorId: t.floorId, remaining: t.distance, speed: 0, ready: !1 }, this.on.onDrop(t.floorId), !0) : !1;
|
|
336
|
+
}
|
|
337
|
+
/** One frame of being carried along a route. The heading is rate-limited towards a point further
|
|
338
|
+
* up the path rather than snapped to the segment underfoot, which is what turns a corner into a
|
|
339
|
+
* turn instead of a cut. */
|
|
340
|
+
advance(t) {
|
|
341
|
+
var o, a, h, p;
|
|
342
|
+
if (!this.path || this.stairs.pending) return;
|
|
343
|
+
const i = (((o = this.followOptions) == null ? void 0 : o.speed) ?? W) * t, e = Math.max(1, Math.ceil(i / 0.08));
|
|
344
|
+
let n = w(this.path, this.travelled);
|
|
345
|
+
for (let d = 0; d < e; d++) {
|
|
346
|
+
if (this.travelled += i / e, n = w(this.path, this.travelled), (a = this.followOptions) != null && a.physical) {
|
|
347
|
+
const l = this.stairs.step(
|
|
348
|
+
this.position,
|
|
349
|
+
b(n.at, this.terrain.walls),
|
|
350
|
+
this.terrain.stairs ?? [],
|
|
351
|
+
this.terrain.floorId ?? null,
|
|
352
|
+
this.terrain.elevation ?? 0,
|
|
353
|
+
this.terrain.supports
|
|
354
|
+
);
|
|
355
|
+
if (Math.hypot(l[0] - n.at[0], l[1] - n.at[1]) > 0.04) {
|
|
356
|
+
this.cancelFollow();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (this.position = l, this.stairs.pending) {
|
|
360
|
+
(p = (h = this.on).onFloor) == null || p.call(h, this.stairs.pending);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
const l = V(
|
|
365
|
+
this.position,
|
|
366
|
+
n.at,
|
|
367
|
+
(g) => {
|
|
368
|
+
var c, u;
|
|
369
|
+
return ((u = (c = this.terrain).supports) == null ? void 0 : u.call(c, g, this.terrain.floorId ?? null)) ?? !0;
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
if (Math.hypot(l[0] - n.at[0], l[1] - n.at[1]) > 1e-3) {
|
|
373
|
+
this.cancelFollow();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
this.position = l;
|
|
377
|
+
}
|
|
378
|
+
if (this.beginFall()) return;
|
|
379
|
+
}
|
|
380
|
+
if (this.heading = tt(this.heading, n.heading, U * t), this.apply(), n.done) {
|
|
381
|
+
const d = this.arrive;
|
|
382
|
+
this.cancelFollow(!1), d == null || d();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/** One camera write per frame. The eye is where the walker stands, `terrain.eye` metres up, facing
|
|
386
|
+
* `heading` and tilted `pitch`; eyeCamera() turns that into the ground point the camera looks at
|
|
387
|
+
* and how far away it is, which is all a MapLibre camera can be told. */
|
|
388
|
+
apply() {
|
|
389
|
+
var o;
|
|
390
|
+
const t = this.map;
|
|
391
|
+
if (!t) return;
|
|
392
|
+
const i = m(this.heading + (this.origin[2] ?? 0)), e = C(this.position, this.origin), n = G(
|
|
393
|
+
{ cameraToCenterDistance: K(t), tileSize: 512 },
|
|
394
|
+
[e[0], e[1]],
|
|
395
|
+
this.terrain.eye + ((o = this.fall) != null && o.ready ? this.fall.remaining : this.stairs.height - (this.terrain.elevation ?? 0)),
|
|
396
|
+
i,
|
|
397
|
+
this.pitch
|
|
398
|
+
);
|
|
399
|
+
t.jumpTo({ center: n.center, zoom: n.zoom, bearing: i, pitch: this.pitch }), this.on.onPose(this.pose);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
export {
|
|
403
|
+
S as BODY,
|
|
404
|
+
R as DEFAULT_WALK_FOV,
|
|
405
|
+
q as DRAG_LOOK,
|
|
406
|
+
T as EYE,
|
|
407
|
+
ft as HEAD_ROOM,
|
|
408
|
+
F as KEYS,
|
|
409
|
+
H as LOOK_AHEAD,
|
|
410
|
+
ht as MAX_MAP_ZOOM,
|
|
411
|
+
v as MAX_PITCH,
|
|
412
|
+
Y as MAX_WALK_FOV,
|
|
413
|
+
O as MIN_PITCH,
|
|
414
|
+
X as MIN_WALK_FOV,
|
|
415
|
+
E as REST_PITCH,
|
|
416
|
+
Z as RUN_SPEED,
|
|
417
|
+
W as TOUR_SPEED,
|
|
418
|
+
U as TURN_RATE,
|
|
419
|
+
B as TURN_SPEED,
|
|
420
|
+
L as WALK_PIXEL_RATIO,
|
|
421
|
+
z as WALK_SPEED,
|
|
422
|
+
N as WALK_ZOOM,
|
|
423
|
+
lt as WalkController,
|
|
424
|
+
J as aim,
|
|
425
|
+
w as alongPath,
|
|
426
|
+
tt as easeHeading,
|
|
427
|
+
G as eyeCamera,
|
|
428
|
+
$ as stride,
|
|
429
|
+
b as unstick,
|
|
430
|
+
j as verticalFieldOfView,
|
|
431
|
+
A as walkFieldOfView
|
|
432
|
+
};
|
|
433
|
+
//# sourceMappingURL=walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sources":["../../../../src/map/walk.ts"],"sourcesContent":["// First-person walk mode: the MapLibre camera becomes a person standing on the active floor, driven\n// the way every first-person game drives one — W A S D and the arrows to move, the mouse to aim.\n//\n// MapLibre has no eye of its own: it has a ground point it looks AT, a pitch, and a zoom. Where the\n// eye ends up is a consequence of the three, and solving them by hand with the wrong tile size is\n// how the previous walk came to stand at half its own height. eyeCamera() is that solve done the\n// way MapLibre's own calculateCameraOptionsFromCameraLngLatAltRotation() does it — the same mercator\n// arithmetic, the same zoom formula — so this module only has to say where the person is and which\n// way they face, plus the geometry a map has never needed: a body that cannot walk through a wall.\n//\n// Deliberately free of React and of the document model: it takes plan-coordinate wall rectangles and\n// gives back a pose, so everything below the controller is testable without a GL context.\nimport * as maplibregl from 'maplibre-gl';\nimport type { Map as GLMap } from 'maplibre-gl';\nimport type { Origin, Point, Ring } from '../model/types';\nimport { toLngLat } from '../model/geometry';\nimport { StairWalker, type StairSurface } from './stairSurfaces';\nimport { cameraToCenterDistance } from './cameraAim';\n\n/** Eye above the slab it stands on. A constant, not the floor's height: a mezzanine and a hall are\n * different rooms but the same visitor. */\nexport { EYE, BODY, HEAD_ROOM } from './walkDimensions';\nimport { EYE, BODY } from './walkDimensions';\nimport { supportedStep } from './walkSurfaces';\n/** Shoulder radius. Generous enough that you do not clip a corner, tight enough for a 0.9 m door. */\n\n/** Faster than anyone walks a corridor, because on a screen the corridor is the size of a hand and\n * a literal 1.4 m/s reads as wading. Games settle around 3–4 m/s for the same reason. */\nexport const WALK_SPEED = 3;\nexport const RUN_SPEED = 6; // Shift: covering a department without waiting for it\n/** Following a route on its own. A touch over walking pace, because nobody watches a demonstration\n * dawdle, and short of the run — a tour is for looking about you, and at eye level 6 m/s is a\n * sprint. */\nexport const TOUR_SPEED = 3.5;\n/** How fast the head can swing while a route is driving. A corner taken at the full rate still\n * reads as a turn rather than as a cut, and anything quicker is the sharp turn this replaced. */\nexport const TURN_RATE = 120;\n/** How far up the path to look while following it. Aiming at a point ahead rather than at the\n * segment you are on is what makes the camera begin its turn before the corner instead of at it —\n * it is also what a person does, which is why it looks right. */\nexport const LOOK_AHEAD = 2.6;\n/** Pitch is MapLibre's: 0 looks straight down at the ground, 90 is level with it. The head can drop\n * to 55° below level — enough to read a floor marking at your feet — and rise to a degree short of\n * level. Not level itself: at 90° the point the map looks at leaves the ground, and MapLibre then\n * has to invent a target in the sky, which pulls its zoom (and every zoom-gated layer) down to\n * what a satellite would use. At 89° the target is a hundred-odd metres up the corridor, the\n * horizon is a degree above the middle of the frame, and nothing has to be invented. */\nexport const MIN_PITCH = 35,\n MAX_PITCH = 89;\n/** Where the head rests: a few degrees below level, so the near floor is in frame and walking reads\n * as walking, with room to look up from. */\nexport const REST_PITCH = 86;\n/** The plan camera's zoom ceiling, and the one the walk raises it to while it has the map. MapLibre\n * puts its camera at the eye by choosing a zoom for the ground point the eye looks at, and jumpTo\n * clamps that zoom to the ceiling without a word — a ceiling the solve can hit is a ceiling that\n * silently lifts the eye off the floor. A 2 m eye looking 55° down on a 4K display at the equator\n * needs 26.6, so the walk's ceiling is 28. Kept here so MapCanvas and the solve agree. */\nexport const MAX_MAP_ZOOM = 26,\n WALK_ZOOM = 28;\n/** The most device pixels per CSS pixel the walk will draw. A retina display asks for two, which\n * is four times the pixels of one, and at eye level every one of them is lit by the sun, the sky\n * and four lamps with shadows: a dense floor measured at 11 frames a second at two and 35 at one.\n * A plan is read; a walk is watched, and a watched picture has to move. 1.25 keeps most of the\n * crispness of a retina display for a third of its cost. */\nexport const WALK_PIXEL_RATIO = 1.25;\n/** Horizontal field of view in degrees. Kept stable when side panels resize the viewport. */\nexport const DEFAULT_WALK_FOV = 100,\n MIN_WALK_FOV = 60,\n MAX_WALK_FOV = 120;\n\nexport function walkFieldOfView(value: number): number {\n return Number.isFinite(value) ? Math.min(MAX_WALK_FOV, Math.max(MIN_WALK_FOV, value)) : DEFAULT_WALK_FOV;\n}\n\n/** MapLibre takes a vertical angle; tan(h/2) = aspect · tan(v/2). */\nexport function verticalFieldOfView(horizontal: number, aspect: number): number {\n return (2 * Math.atan(Math.tan((walkFieldOfView(horizontal) * Math.PI) / 360) / aspect) * 180) / Math.PI;\n}\n/** Degrees the view turns per pixel while dragging to look around. */\nexport const DRAG_LOOK = 0.22;\nexport const TURN_SPEED = 120; // degrees per second for A, D and the arrow keys\n\n/** Where the walker is, kept apart from the camera: the camera is whatever mode you are in, the\n * avatar is the person, and they persist across leaving the walk and coming back to it. */\nexport interface WalkAvatar {\n floorId: string | null;\n /** Plan metres. */\n position: Point;\n /** Clockwise from plan +Y, as WalkPose.heading. */\n heading: number;\n pitch?: number;\n}\n\nexport interface WalkPose {\n /** Where the walker stands, in plan metres. */\n position: Point;\n /** Clockwise from plan +Y, matching the plan's own grid rather than the compass. */\n heading: number;\n pitch: number;\n /** True while the mouse is held down and dragged to aim the view. */\n looking: boolean;\n}\n\n/* ------------------------------------------------------------------ geometry */\n\nconst clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));\nconst wrap = (deg: number) => ((deg % 360) + 360) % 360;\n\n/** Nearest point to `p` on the segment ab. */\nfunction onSegment(p: Point, a: Point, b: Point): Point {\n const dx = b[0] - a[0],\n dy = b[1] - a[1];\n const len2 = dx * dx + dy * dy;\n if (len2 < 1e-12) return a;\n const t = clamp(((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2, 0, 1);\n return [a[0] + t * dx, a[1] + t * dy];\n}\n\nfunction inside(p: Point, ring: Ring): boolean {\n let hit = false;\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const [xi, yi] = ring[i],\n [xj, yj] = ring[j];\n if (yi > p[1] !== yj > p[1] && p[0] < ((xj - xi) * (p[1] - yi)) / (yj - yi) + xi) hit = !hit;\n }\n return hit;\n}\n\n/** Push a walker of radius `r` out of every wall it overlaps.\n *\n * Resolving each wall independently and in sequence is what produces sliding for free: walking into\n * a wall at an angle leaves the along-wall component of the move untouched and cancels only the\n * component that would have gone through it. Two passes settle inside corners, where pushing out of\n * one wall can push you into its neighbour. */\nexport function unstick(p: Point, walls: Ring[], r = BODY): Point {\n let out = p;\n for (let pass = 0; pass < 2; pass++) {\n let moved = false;\n for (const ring of walls) {\n let near: Point | null = null,\n nearD = Infinity;\n for (let i = 0; i < ring.length; i++) {\n const c = onSegment(out, ring[i], ring[(i + 1) % ring.length]);\n const d = Math.hypot(out[0] - c[0], out[1] - c[1]);\n if (d < nearD) {\n nearD = d;\n near = c;\n }\n }\n if (!near || (nearD >= r && !inside(out, ring))) continue;\n let dx = out[0] - near[0],\n dy = out[1] - near[1];\n if (inside(out, ring)) {\n // Standing in the wall — a door frame skimmed at speed, or a start point placed badly. Leave\n // by the face we are nearest, which is the one we came in through.\n dx = -dx;\n dy = -dy;\n }\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) continue; // exactly on the centreline: no direction to prefer, let the next frame decide\n out = [near[0] + (dx / len) * r, near[1] + (dy / len) * r];\n moved = true;\n }\n if (!moved) break;\n }\n return out;\n}\n\n/** The MapLibre camera that stands at `eye`, `alt` metres up, facing `bearing` and tilted `pitch`:\n * the ground point it looks at and the zoom that puts it that far away.\n *\n * This is MapLibre's calculateCenterFromCameraLngLatAlt() for a camera that looks at the ground —\n * the ray from the eye meets the ground alt/cos(pitch) away, and the zoom is the one at which that\n * distance fills cameraToCenterDistance pixels. MapLibre's own copy gives up within six degrees of\n * level and invents a target ten kilometres off instead, which is fine for a plane and is a 700 m\n * eye for a person; this one holds to the ground right up to MAX_PITCH. The mercator scale is\n * taken at the target rather than the eye, iterated, because a metre is a different fraction of\n * the world at each latitude and the target is the point the zoom is measured at. */\nexport function eyeCamera(\n view: { cameraToCenterDistance: number; tileSize: number },\n eye: [number, number],\n alt: number,\n bearing: number,\n pitch: number,\n): { center: [number, number]; zoom: number } {\n const pitchRad = (Math.min(pitch, MAX_PITCH) * Math.PI) / 180,\n bearingRad = (bearing * Math.PI) / 180;\n const distance = alt / Math.cos(pitchRad); // eye to target, along the view\n const dx = Math.sin(pitchRad) * Math.sin(bearingRad),\n dy = -Math.sin(pitchRad) * Math.cos(bearingRad); // mercator y grows southward\n const from = maplibregl.MercatorCoordinate.fromLngLat({ lng: eye[0], lat: eye[1] }, alt);\n let perMetre = from.meterInMercatorCoordinateUnits();\n let target = from;\n for (let i = 0; i < 10; i++) {\n const d = distance * perMetre;\n target = new maplibregl.MercatorCoordinate(from.x + dx * d, from.y + dy * d, 0);\n const next = target.meterInMercatorCoordinateUnits();\n if (Math.abs(next - perMetre) < 1e-18) break;\n perMetre = next;\n }\n const ll = target.toLngLat();\n return {\n center: [ll.lng, ll.lat],\n zoom: Math.log2(view.cameraToCenterDistance / (distance * perMetre) / view.tileSize),\n };\n}\n\n/** What a mouse movement does to where you are looking. Right turns you right; up looks up — which\n * in MapLibre's terms is a LARGER pitch, 90 being level and 0 the floor. The head stops at the\n * limits rather than wrapping: nobody looks at the ceiling by looking too far at their feet. */\nexport function aim(\n heading: number,\n pitch: number,\n dx: number,\n dy: number,\n perPixel = DRAG_LOOK,\n): { heading: number; pitch: number } {\n return { heading: wrap(heading + dx * perPixel), pitch: clamp(pitch - dy * perPixel, MIN_PITCH, MAX_PITCH) };\n}\n\n/** One frame of walking: what the held keys do to a heading and a position over `dt` seconds.\n *\n * Pulled out of the controller because it is the part with an opinion in it — which key turns you\n * and which key carries you — and that opinion is worth stating somewhere a test can read it.\n * Returns the new heading and the step to take, in plan metres, before any wall gets a say. */\nexport function stride(keys: ReadonlySet<string>, heading: number, dt: number): { heading: number; step?: Point } {\n // Turning is a turn on the spot: A, D and the arrows swing you round your own axis and move you\n // nowhere, which is what a person does when they look down a different corridor. Strafing —\n // sidling along without turning — is Q and E, and is a different thing that wants a different key.\n let facing = heading;\n if (keys.has('turnLeft') !== keys.has('turnRight'))\n facing = wrap(facing + (keys.has('turnRight') ? 1 : -1) * TURN_SPEED * dt);\n const ahead = (keys.has('ahead') ? 1 : 0) - (keys.has('back') ? 1 : 0);\n const side = (keys.has('right') ? 1 : 0) - (keys.has('left') ? 1 : 0);\n if (!ahead && !side) return { heading: facing };\n const rad = (facing * Math.PI) / 180;\n // Plan +Y is the walker's forward at heading 0, so forward is [sin, cos] and right is its\n // perpendicular. Diagonals are normalised — strafing must not be faster than walking.\n const scale = (keys.has('fast') ? RUN_SPEED : WALK_SPEED) * dt * (ahead && side ? Math.SQRT1_2 : 1);\n return {\n heading: facing,\n step: [\n (ahead * Math.sin(rad) + side * Math.cos(rad)) * scale,\n (ahead * Math.cos(rad) - side * Math.sin(rad)) * scale,\n ],\n };\n}\n\n/** Where you are after walking `travelled` metres along a polyline, and which way the path is\n * heading `ahead` metres further on. Clamped at both ends, and `done` once the path runs out. */\nexport function alongPath(\n points: Point[],\n travelled: number,\n ahead = LOOK_AHEAD,\n): { at: Point; heading: number; done: boolean; left: number } {\n const seg: number[] = [];\n let total = 0;\n for (let i = 1; i < points.length; i++) {\n const d = Math.hypot(points[i][0] - points[i - 1][0], points[i][1] - points[i - 1][1]);\n seg.push(d);\n total += d;\n }\n const pick = (distance: number): Point => {\n let left = clamp(distance, 0, total);\n for (let i = 0; i < seg.length; i++) {\n if (left <= seg[i] || i === seg.length - 1) {\n const t = seg[i] > 1e-9 ? clamp(left / seg[i], 0, 1) : 0;\n return [\n points[i][0] + (points[i + 1][0] - points[i][0]) * t,\n points[i][1] + (points[i + 1][1] - points[i][1]) * t,\n ];\n }\n left -= seg[i];\n }\n return points[points.length - 1];\n };\n const at = pick(travelled);\n // The aim point is capped at the end of the path, and the point it is measured FROM is held a\n // hair short of the end — otherwise the two coincide on the last step, the direction collapses to\n // nothing and the walker finishes facing plan north instead of facing the way they arrived.\n const from = pick(Math.min(travelled, total - 1e-3));\n const aimAt = pick(Math.min(total, travelled + ahead));\n const dx = aimAt[0] - from[0],\n dy = aimAt[1] - from[1];\n const heading = Math.hypot(dx, dy) < 1e-6 ? 0 : wrap((Math.atan2(dx, dy) * 180) / Math.PI);\n return { at, heading, done: travelled >= total, left: Math.max(0, total - travelled) };\n}\n\n/** Turn `from` towards `to` by at most `step` degrees, the short way round. */\nexport function easeHeading(from: number, to: number, step: number): number {\n const delta = ((((to - from + 540) % 360) + 360) % 360) - 180;\n return wrap(from + clamp(delta, -step, step));\n}\n\n/* ------------------------------------------------------------------ controller */\n\nexport interface WalkTerrain {\n /** Solid wall bodies on the floor being walked, in plan metres. */\n walls: Ring[];\n /** Eye height above the presented ground plane: EYE plus whatever lifts the slab. */\n eye: number;\n floorId?: string | null;\n elevation?: number;\n stairs?: StairSurface[];\n /** Whether a storey's slab supports the point, or an intentional drop has a landing below. */\n supports?: (at: Point, floorId: string | null) => boolean;\n dropAt?: (at: Point) => { floorId: string; distance: number } | undefined;\n}\n\nexport interface WalkCallbacks {\n /** Rebase at a stair landing, or before descending below the active scene's datum. */\n onFloor?(floorId: string): void;\n /** A real opening has a landing below. Rebase the scene onto that floor before animating descent. */\n onDrop?(floorId: string): void;\n /** Fired when the position, heading, pitch or look gesture changes. */\n onPose(pose: WalkPose): void;\n /** The walker asked to change level (F, or Shift+F for down). The host decides whether there is\n * anywhere to go from where they are standing. */\n onUse(down: boolean): void;\n /** Escape: the host leaves walk mode. */\n onExit(): void;\n /** M: the walker wants the sound on or off. */\n onSound?(): void;\n}\n\nconst HANDLERS = ['dragPan', 'dragRotate', 'scrollZoom', 'keyboard', 'touchZoomRotate', 'doubleClickZoom'] as const;\ntype Handler = (typeof HANDLERS)[number];\n\n/** The keys, by physical position rather than by letter so the layout survives a French or a Dvorak\n * keyboard. W and S walk, A and D turn — the way Doom bound the arrows, and what a person\n * walking a floor plan reaches for: the point of A is to look down the other corridor, not to\n * sidle towards it. Q and E sidestep for whoever wants it, the arrows mirror the letters, and Shift\n * runs. */\nexport const KEYS: Record<string, string> = {\n KeyW: 'ahead',\n ArrowUp: 'ahead',\n KeyS: 'back',\n ArrowDown: 'back',\n KeyA: 'turnLeft',\n ArrowLeft: 'turnLeft',\n KeyD: 'turnRight',\n ArrowRight: 'turnRight',\n KeyQ: 'left',\n KeyE: 'right',\n ShiftLeft: 'fast',\n ShiftRight: 'fast',\n};\n\nexport class WalkController {\n private map?: GLMap;\n private origin: Origin = [0, 0];\n private terrain: WalkTerrain = { walls: [], eye: EYE };\n private keys = new Set<string>();\n private frame = 0;\n private last = 0;\n private handlers: [Handler, boolean][] = [];\n private maxPitch = 60;\n private maxZoom = 22;\n private pixelRatio = 1;\n private planFov = 0;\n private fieldOfView = DEFAULT_WALK_FOV;\n private running = false;\n private dragPointer: number | null = null;\n private drag: Point = [0, 0];\n private path: Point[] | null = null;\n private travelled = 0;\n private arrive?: () => void;\n private followOptions?: { speed?: number; physical?: boolean; onCancel?: () => void };\n private fall?: { floorId: string; remaining: number; speed: number; ready: boolean };\n private stairs = new StairWalker();\n position: Point = [0, 0];\n heading = 0;\n pitch = REST_PITCH;\n\n constructor(private readonly on: WalkCallbacks) {}\n\n attach(map: GLMap, origin: Origin, start: { position: Point; heading: number; pitch?: number }) {\n this.map = map;\n this.origin = origin;\n this.position = start.position;\n this.fall = undefined;\n this.heading = wrap(start.heading);\n this.pitch = clamp(start.pitch ?? REST_PITCH, MIN_PITCH, MAX_PITCH);\n this.running = true;\n // MapLibre's own handlers would fight every jumpTo we make, and its keyboard handler eats the\n // very keys we walk with. Remember which were on rather than re-enabling a fixed list on the way\n // out: the host has its own opinions about dragRotate and doubleClickZoom, and handing back a\n // different map than we were given is how a mode leaves damage behind it.\n this.handlers = HANDLERS.map(h => [h, map[h]?.isEnabled() ?? false]);\n for (const [h] of this.handlers) map[h]?.disable();\n // jumpTo clamps to the map's pitch and zoom ceilings without a word, and the plan camera's are\n // below where a walker looks and how near the floor they stand. Raise both for the walk, and\n // put them back with the handlers.\n this.maxPitch = map.getMaxPitch();\n this.maxZoom = map.getMaxZoom();\n this.planFov = map.getVerticalFieldOfView();\n map.setMaxPitch(Math.max(this.maxPitch, MAX_PITCH));\n map.setMaxZoom(Math.max(this.maxZoom, WALK_ZOOM));\n this.pixelRatio = map.getPixelRatio();\n if (this.pixelRatio > WALK_PIXEL_RATIO) map.setPixelRatio(WALK_PIXEL_RATIO);\n const canvas = map.getCanvas();\n canvas.addEventListener('pointerdown', this.grab);\n canvas.addEventListener('pointermove', this.look);\n canvas.addEventListener('pointerup', this.drop);\n canvas.addEventListener('pointercancel', this.drop);\n canvas.addEventListener('lostpointercapture', this.drop);\n window.addEventListener('keydown', this.down, true);\n window.addEventListener('keyup', this.up, true);\n window.addEventListener('blur', this.release);\n map.on('resize', this.resize);\n this.last = performance.now();\n this.frame = requestAnimationFrame(this.tick);\n canvas.style.cursor = 'grab';\n this.resize();\n }\n\n detach() {\n const map = this.map;\n this.running = false;\n this.cancelFollow();\n cancelAnimationFrame(this.frame);\n this.keys.clear();\n window.removeEventListener('keydown', this.down, true);\n window.removeEventListener('keyup', this.up, true);\n window.removeEventListener('blur', this.release);\n if (!map) return;\n map.off('resize', this.resize);\n const canvas = map.getCanvas();\n canvas.removeEventListener('pointerdown', this.grab);\n canvas.removeEventListener('pointermove', this.look);\n canvas.removeEventListener('pointerup', this.drop);\n canvas.removeEventListener('pointercancel', this.drop);\n canvas.removeEventListener('lostpointercapture', this.drop);\n this.endDrag();\n canvas.style.cursor = '';\n for (const [h, was] of this.handlers) if (was) map[h]?.enable();\n this.handlers = [];\n map.setMaxPitch(this.maxPitch);\n map.setMaxZoom(this.maxZoom);\n map.setVerticalFieldOfView(this.planFov);\n if (map.getPixelRatio() !== this.pixelRatio) map.setPixelRatio(this.pixelRatio);\n this.map = undefined;\n }\n\n /** The floor under the walker changed, or its walls were edited. */\n setTerrain(terrain: WalkTerrain) {\n this.terrain = terrain;\n this.stairs.sync(terrain.floorId ?? null, terrain.elevation ?? 0);\n this.stairs.refresh(terrain.stairs ?? [], this.position);\n if (this.fall) {\n if (terrain.floorId === this.fall.floorId) this.fall.ready = true;\n else this.fall = undefined;\n }\n if (this.running) this.apply();\n }\n\n /** Widen the lens without changing the person's position, heading or eye height. */\n setFieldOfView(value: number) {\n this.fieldOfView = walkFieldOfView(value);\n if (this.running) this.resize();\n }\n\n private resize = () => {\n const map = this.map;\n if (!map) return;\n const canvas = map.getCanvas();\n if (canvas.clientWidth > 0 && canvas.clientHeight > 0)\n map.setVerticalFieldOfView(verticalFieldOfView(this.fieldOfView, canvas.clientWidth / canvas.clientHeight));\n // A new lens changes cameraToCenterDistance; solve zoom again to keep the eye in place.\n this.apply();\n };\n\n /** Walk a path on its own, as far as it goes on this floor, then call `onArrive`. Taking any\n * control cancels it: a tour you cannot interrupt is a video. */\n follow(\n points: Point[],\n onArrive: () => void,\n options?: { speed?: number; physical?: boolean; onCancel?: () => void },\n ) {\n const pts = points.filter((p, i) => !i || Math.hypot(p[0] - points[i - 1][0], p[1] - points[i - 1][1]) > 0.01);\n if (pts.length < 2) return onArrive();\n this.path = pts;\n this.travelled = 0;\n this.arrive = onArrive;\n this.followOptions = options;\n const start = alongPath(pts, 0);\n this.position = start.at;\n if (!this.running) this.heading = start.heading;\n if (this.running) this.apply();\n }\n\n cancelFollow(notify = true) {\n const cancelled = this.followOptions?.onCancel;\n this.path = null;\n this.arrive = undefined;\n this.followOptions = undefined;\n if (notify) cancelled?.();\n }\n\n get following() {\n return !!this.path;\n }\n\n /** Put the walker somewhere — arriving at a new floor, or jumping to a route's start. */\n place(position: Point, heading = this.heading) {\n this.fall = undefined;\n this.stairs.reset(this.terrain.elevation ?? 0);\n this.position = position;\n this.heading = wrap(heading);\n if (this.running) this.apply();\n }\n\n /** Inspect something from the current position. Only the walker writes a POV camera: panning\n * its ground target like a plan camera would move the eye far behind the selected object. */\n lookAt(point: Point) {\n const dx = point[0] - this.position[0],\n dy = point[1] - this.position[1];\n if (Math.hypot(dx, dy) < 1e-6) return;\n this.heading = wrap((Math.atan2(dx, dy) * 180) / Math.PI);\n if (this.running) this.apply();\n }\n\n get pose(): WalkPose {\n return {\n position: this.position,\n heading: this.heading,\n pitch: this.pitch,\n looking: this.dragPointer !== null,\n };\n }\n\n get onStairs() {\n return !!this.stairs.active;\n }\n\n /** Drag to look. Capture only delivers the remainder of this gesture if it leaves the canvas;\n * the cursor stays visible and is free to operate panels as soon as the button is released. */\n private grab = (e: PointerEvent) => {\n if (e.button !== 0 || e.pointerType === 'touch' || this.dragPointer !== null) return;\n const canvas = this.map?.getCanvas();\n if (!canvas) return;\n e.preventDefault();\n canvas.focus({ preventScroll: true });\n this.dragPointer = e.pointerId;\n this.drag = [e.clientX, e.clientY];\n canvas.style.cursor = 'grabbing';\n try {\n canvas.setPointerCapture(e.pointerId);\n } catch {\n /* the pointer went away between down and here; the drag ends with the next up */\n }\n this.on.onPose(this.pose);\n };\n\n private drop = (e: PointerEvent) => {\n if (e.pointerId === this.dragPointer) this.endDrag();\n };\n\n private endDrag = () => {\n const pointer = this.dragPointer;\n if (pointer === null) return;\n this.dragPointer = null;\n const canvas = this.map?.getCanvas();\n if (canvas) {\n if (canvas.hasPointerCapture(pointer)) canvas.releasePointerCapture(pointer);\n canvas.style.cursor = 'grab';\n }\n this.on.onPose(this.pose);\n };\n\n private release = () => {\n this.keys.clear();\n this.endDrag();\n };\n\n private look = (e: PointerEvent) => {\n if (e.pointerId !== this.dragPointer) return;\n // Recover if a mouse-up was lost while changing window focus.\n if (!(e.buttons & 1)) return this.endDrag();\n const dx = e.clientX - this.drag[0],\n dy = e.clientY - this.drag[1];\n this.drag = [e.clientX, e.clientY];\n if (!dx && !dy) return;\n const turned = aim(this.heading, this.pitch, dx, dy);\n this.heading = turned.heading;\n this.pitch = turned.pitch;\n this.apply();\n };\n\n private down = (e: KeyboardEvent) => {\n if (e.metaKey || e.ctrlKey || e.altKey) return;\n const target = e.target as HTMLElement | null;\n if (target && (target.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName))) return;\n if (e.code === 'Escape') {\n e.preventDefault();\n e.stopImmediatePropagation();\n this.release();\n this.on.onExit();\n return;\n }\n if (e.code === 'KeyF' || e.code === 'Enter') {\n e.preventDefault();\n e.stopImmediatePropagation();\n this.on.onUse(e.shiftKey);\n return;\n }\n if (e.code === 'KeyM') {\n e.preventDefault();\n e.stopImmediatePropagation();\n this.on.onSound?.();\n return;\n }\n const action = KEYS[e.code];\n if (!action) return;\n e.preventDefault();\n // stopPropagation is not enough: the host registers its own shortcuts on window too, and\n // stopping propagation does nothing to listeners on the same target. W and S would pitch the\n // map, the arrows would pan it, and both would fight the walk on every step.\n e.stopImmediatePropagation();\n this.keys.add(action);\n };\n\n private up = (e: KeyboardEvent) => {\n const action = KEYS[e.code];\n if (action) this.keys.delete(action);\n };\n\n private tick = (now: number) => {\n if (!this.running) return;\n this.frame = requestAnimationFrame(this.tick);\n // Capped, so a backgrounded tab does not teleport you when it comes back — but not at a frame's\n // worth: a dense floor can render at a few frames a second, and a cap of 100 ms then takes the\n // missing time out of your stride and makes the whole walk slow.\n const dt = Math.min((now - this.last) / 1000, 0.25);\n this.last = now;\n if (this.fall) {\n if (this.fall.ready) {\n this.fall.remaining = Math.max(0, this.fall.remaining - this.fall.speed * dt - 0.5 * 9.81 * dt * dt);\n this.fall.speed += 9.81 * dt;\n if (this.fall.remaining === 0) this.fall = undefined;\n this.apply();\n }\n return;\n }\n if (this.path) {\n // Touching a movement key takes the tour back off the route; aiming the view does not,\n // because looking about you while the route carries you on is the whole point of watching it.\n if (this.keys.size) this.cancelFollow();\n else return this.advance(dt);\n }\n if (this.stairs.pending) return;\n const drive = this.stairs.active?.drive;\n if (!this.keys.size && !drive?.some(Boolean)) return;\n const moved = stride(this.keys, this.heading, dt);\n this.heading = moved.heading;\n const delta: Point = [\n (moved.step?.[0] ?? 0) + (drive?.[0] ?? 0) * dt,\n (moved.step?.[1] ?? 0) + (drive?.[1] ?? 0) * dt,\n ];\n // Small substeps cannot tunnel through a riser or rail when a frame is slow or Shift is held.\n const steps = Math.max(1, Math.ceil(Math.hypot(...delta) / 0.12));\n for (let i = 0; i < steps; i++) {\n const next = unstick(\n [this.position[0] + delta[0] / steps, this.position[1] + delta[1] / steps],\n this.terrain.walls,\n );\n this.position = this.stairs.step(\n this.position,\n next,\n this.terrain.stairs ?? [],\n this.terrain.floorId ?? null,\n this.terrain.elevation ?? 0,\n this.terrain.supports,\n );\n if (this.stairs.pending) break;\n if (this.beginFall()) return;\n }\n if (this.stairs.pending && this.on.onFloor) {\n this.apply();\n this.on.onFloor(this.stairs.pending);\n return;\n }\n this.apply();\n };\n\n /** Stop at the first substep entering a hole, even when running over a narrow opening. */\n private beginFall() {\n const drop = !this.stairs.active && this.terrain.dropAt?.(this.position);\n if (drop && this.on.onDrop) {\n this.cancelFollow();\n this.fall = { floorId: drop.floorId, remaining: drop.distance, speed: 0, ready: false };\n this.on.onDrop(drop.floorId);\n return true;\n }\n return false;\n }\n\n /** One frame of being carried along a route. The heading is rate-limited towards a point further\n * up the path rather than snapped to the segment underfoot, which is what turns a corner into a\n * turn instead of a cut. */\n private advance(dt: number) {\n if (!this.path) return;\n if (this.stairs.pending) return;\n const delta = (this.followOptions?.speed ?? TOUR_SPEED) * dt;\n const count = Math.max(1, Math.ceil(delta / 0.08));\n let step = alongPath(this.path, this.travelled);\n for (let i = 0; i < count; i++) {\n this.travelled += delta / count;\n step = alongPath(this.path, this.travelled);\n if (this.followOptions?.physical) {\n const next = this.stairs.step(\n this.position,\n unstick(step.at, this.terrain.walls),\n this.terrain.stairs ?? [],\n this.terrain.floorId ?? null,\n this.terrain.elevation ?? 0,\n this.terrain.supports,\n );\n if (Math.hypot(next[0] - step.at[0], next[1] - step.at[1]) > 0.04) {\n this.cancelFollow();\n return;\n }\n this.position = next;\n if (this.stairs.pending) {\n this.on.onFloor?.(this.stairs.pending);\n break;\n }\n } else {\n const next = supportedStep(\n this.position,\n step.at,\n at => this.terrain.supports?.(at, this.terrain.floorId ?? null) ?? true,\n );\n if (Math.hypot(next[0] - step.at[0], next[1] - step.at[1]) > 0.001) {\n this.cancelFollow();\n return;\n }\n this.position = next;\n }\n if (this.beginFall()) return;\n }\n this.heading = easeHeading(this.heading, step.heading, TURN_RATE * dt);\n this.apply();\n if (step.done) {\n const arrived = this.arrive;\n this.cancelFollow(false);\n arrived?.();\n }\n }\n\n /** One camera write per frame. The eye is where the walker stands, `terrain.eye` metres up, facing\n * `heading` and tilted `pitch`; eyeCamera() turns that into the ground point the camera looks at\n * and how far away it is, which is all a MapLibre camera can be told. */\n private apply() {\n const map = this.map;\n if (!map) return;\n const bearing = wrap(this.heading + (this.origin[2] ?? 0));\n const eye = toLngLat(this.position, this.origin);\n const camera = eyeCamera(\n { cameraToCenterDistance: cameraToCenterDistance(map), tileSize: 512 },\n [eye[0], eye[1]],\n this.terrain.eye + (this.fall?.ready ? this.fall.remaining : this.stairs.height - (this.terrain.elevation ?? 0)),\n bearing,\n this.pitch,\n );\n map.jumpTo({ center: camera.center, zoom: camera.zoom, bearing, pitch: this.pitch });\n this.on.onPose(this.pose);\n }\n}\n"],"names":["WALK_SPEED","RUN_SPEED","TOUR_SPEED","TURN_RATE","LOOK_AHEAD","MIN_PITCH","MAX_PITCH","REST_PITCH","MAX_MAP_ZOOM","WALK_ZOOM","WALK_PIXEL_RATIO","DEFAULT_WALK_FOV","MIN_WALK_FOV","MAX_WALK_FOV","walkFieldOfView","value","verticalFieldOfView","horizontal","aspect","DRAG_LOOK","TURN_SPEED","clamp","v","lo","hi","wrap","deg","onSegment","p","a","b","dx","dy","len2","t","inside","ring","hit","i","j","xi","yi","xj","yj","unstick","walls","r","BODY","out","pass","moved","near","nearD","c","d","len","eyeCamera","view","eye","alt","bearing","pitch","pitchRad","bearingRad","distance","from","maplibregl","perMetre","target","next","ll","aim","heading","perPixel","stride","keys","dt","facing","ahead","side","rad","scale","alongPath","points","travelled","seg","total","pick","left","at","aimAt","easeHeading","to","step","delta","HANDLERS","KEYS","WalkController","on","__publicField","EYE","StairWalker","map","canvas","e","_a","pointer","turned","_b","action","now","drive","_c","steps","origin","start","h","was","terrain","onArrive","options","pts","notify","cancelled","position","point","drop","count","_d","supportedStep","arrived","toLngLat","camera","cameraToCenterDistance"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ringArea as w, centroid as I, pointInRing as v } from "../model/geometry.js";
|
|
2
|
+
import { flights as x, primaryShafts as C } from "../model/vertical.js";
|
|
3
|
+
import { rampFloors as M } from "../model/ramps.js";
|
|
4
|
+
function A(l, f, g = C(l)) {
|
|
5
|
+
const a = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const i of l.objects) {
|
|
7
|
+
const c = i.kind === "stairs" && g.has(i.id) ? x(l, i).map((s) => [s.from, s.to]) : i.slope ? [Object.values(M(l, i))] : [];
|
|
8
|
+
for (const s of c)
|
|
9
|
+
if (s.some((e) => (e == null ? void 0 : e.id) === f.id))
|
|
10
|
+
for (const e of s) e && e.id !== f.id && a.set(e.id, e);
|
|
11
|
+
}
|
|
12
|
+
return [...a.values()];
|
|
13
|
+
}
|
|
14
|
+
function R(l, f, g) {
|
|
15
|
+
const a = l.floors.filter((o) => o.buildingId === f.buildingId).sort((o, n) => o.elevation - n.elevation), i = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const o of a) {
|
|
17
|
+
const n = l.objects.filter((t) => {
|
|
18
|
+
var h;
|
|
19
|
+
return t.floorId === o.id && ((h = t.rings) == null ? void 0 : h.length) && !t.slope;
|
|
20
|
+
}), d = n.filter((t) => t.kind === "zone" && !t.parentId);
|
|
21
|
+
i.set(o.id, d.length ? d : n.filter((t) => t.kind === "room"));
|
|
22
|
+
}
|
|
23
|
+
const c = (o, n) => {
|
|
24
|
+
const d = I(n);
|
|
25
|
+
return (i.get(o.id) ?? []).some(
|
|
26
|
+
(t) => v(d, t.rings[0]) && !t.rings.slice(1).some((h) => v(d, h))
|
|
27
|
+
);
|
|
28
|
+
}, s = (o) => (i.get(o.id) ?? []).flatMap((n) => n.rings.slice(1)).filter((n) => w(n) > 0.01 && !c(o, n)), e = a.filter((o) => o.elevation >= g.elevation + g.height - 0.01), m = e.length ? s(e[0]) : [], u = s(f), b = [...u, ...m], p = [];
|
|
29
|
+
let r = b;
|
|
30
|
+
for (const o of e) {
|
|
31
|
+
if (!r.length) break;
|
|
32
|
+
p.push(o), r = r.filter((n) => !c(o, n));
|
|
33
|
+
}
|
|
34
|
+
const k = r.length ? p.at(-1) : void 0;
|
|
35
|
+
r = u;
|
|
36
|
+
for (const o of a.filter((n) => n.elevation < f.elevation).reverse()) {
|
|
37
|
+
if (!r.length) break;
|
|
38
|
+
p.push(o), r = r.filter((n) => !c(o, n));
|
|
39
|
+
}
|
|
40
|
+
return { levels: p, roof: k, ceilingOpenings: m };
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
A as walkConnectionContext,
|
|
44
|
+
R as walkVoidContext
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=walkContext.js.map
|