@kerros/viewer 0.0.0-stage → 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +52 -4
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as x from "three";
|
|
2
|
+
import { mergeGeometries as I } from "three/addons/utils/BufferGeometryUtils.js";
|
|
3
|
+
import { metricUVs as z } from "./surfaces.js";
|
|
4
|
+
function O(o, d, c, a) {
|
|
5
|
+
const n = new x.Group(), t = o.width, S = o.doorSwing ?? 1, E = a.get("veneer", o.color ?? "#cab69a"), M = a.get("plaster", "#e1ded5"), g = a.metal("#aab0b2"), h = a.solid("#414448"), H = a.get("veneer", o.color ?? "#c0aa8d"), r = (i, f, p, e, b, u, T, s) => {
|
|
6
|
+
if (f <= 0 || p <= 0 || e <= 0) return;
|
|
7
|
+
const y = new x.BoxGeometry(f, p, e);
|
|
8
|
+
y.translate(b, u, T), z(y);
|
|
9
|
+
const G = new x.Mesh(y, s);
|
|
10
|
+
G.castShadow = G.receiveShadow = !0, G.userData.entityId = o.id, i.add(G);
|
|
11
|
+
};
|
|
12
|
+
for (const i of [-1, 1]) {
|
|
13
|
+
r(n, 0.035, d + 0.025, c, i * (t / 2 - 0.0175), 0, c / 2, M);
|
|
14
|
+
for (const f of [-1, 1])
|
|
15
|
+
r(
|
|
16
|
+
n,
|
|
17
|
+
0.07,
|
|
18
|
+
0.025,
|
|
19
|
+
c + 0.035,
|
|
20
|
+
i * (t / 2 + 5e-3),
|
|
21
|
+
f * (d / 2 + 0.012),
|
|
22
|
+
(c + 0.035) / 2,
|
|
23
|
+
M
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
r(n, t, d + 0.025, 0.035, 0, 0, c - 0.0175, M);
|
|
27
|
+
for (const i of [-1, 1]) r(n, t + 0.08, 0.025, 0.07, 0, i * (d / 2 + 0.012), c, M);
|
|
28
|
+
r(n, t, d + 0.04, 0.012, 0, 0, 6e-3, g);
|
|
29
|
+
const k = [], m = o.doorType === "sliding", l = o.doorType === "double", v = o.doorHinge === "right" ? 1 : -1, w = (l ? t / 2 : t) - (l ? 0.025 : 0.045);
|
|
30
|
+
for (let i = 0; i < (l ? 2 : 1); i++) {
|
|
31
|
+
const f = l ? i ? -1 : 1 : o.doorHinge === "right" ? -1 : 1, p = new x.Group(), e = new x.Group();
|
|
32
|
+
p.add(e), e.position.x = m ? 0 : -f * t / 2, e.position.y = m ? S * (d / 2 + 0.045) : 0;
|
|
33
|
+
const b = m ? 0 : f * (w / 2 + 0.025), u = c - 0.06, T = 0.02;
|
|
34
|
+
r(e, w, 0.045, u, b, 0, T + u / 2, E);
|
|
35
|
+
for (const s of [-1, 1]) {
|
|
36
|
+
r(
|
|
37
|
+
e,
|
|
38
|
+
Math.max(0.1, w - 0.16),
|
|
39
|
+
6e-3,
|
|
40
|
+
Math.max(0.2, u - 0.38),
|
|
41
|
+
b,
|
|
42
|
+
s * 0.025,
|
|
43
|
+
T + u / 2 + 0.03,
|
|
44
|
+
H
|
|
45
|
+
), r(e, Math.max(0.1, w - 0.1), 8e-3, 0.13, b, s * 0.027, 0.13, g);
|
|
46
|
+
const y = b + (m ? -v : f) * (w / 2 - 0.09);
|
|
47
|
+
m ? r(e, 0.026, 0.035, 0.3, y, s * 0.047, 1.03, g) : (r(e, 0.052, 0.012, 0.12, y, s * 0.033, 1.03, g), r(e, 0.11, 0.024, 0.021, y - f * 0.035, s * 0.067, 1.06, g), r(e, 0.017, 0.012, 0.028, y, s * 0.042, 1, h));
|
|
48
|
+
}
|
|
49
|
+
if (!m) for (const s of [0.25, u / 2, u - 0.22]) r(e, 0.025, 0.058, 0.09, f * 0.025, 0, s, g);
|
|
50
|
+
R(e), p.traverse((s) => {
|
|
51
|
+
s instanceof x.Mesh && (s.userData.entityId = o.id);
|
|
52
|
+
}), p.userData.doorType = o.doorType ?? "hinged", p.userData.entityId = o.id, k.push({
|
|
53
|
+
group: p,
|
|
54
|
+
setOpen: (s) => {
|
|
55
|
+
m ? e.position.x = v * (t + 0.03) * s : e.rotation.z = S * f * x.MathUtils.degToRad(72) * s;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (m) {
|
|
60
|
+
const i = S * (d / 2 + 0.065);
|
|
61
|
+
r(n, t * 2 + 0.12, 0.05, 0.065, v * t / 2, i, c + 0.12, g);
|
|
62
|
+
for (const f of [-t / 2, t / 2, v * t]) r(n, 0.045, 0.08, 0.13, f, i, c + 0.1, h);
|
|
63
|
+
}
|
|
64
|
+
return { frame: n, panels: k };
|
|
65
|
+
}
|
|
66
|
+
function R(o) {
|
|
67
|
+
const d = /* @__PURE__ */ new Map();
|
|
68
|
+
for (const c of [...o.children]) {
|
|
69
|
+
const a = c, n = d.get(a.material) ?? [];
|
|
70
|
+
n.push(a.geometry), d.set(a.material, n), o.remove(a);
|
|
71
|
+
}
|
|
72
|
+
for (const [c, a] of d) {
|
|
73
|
+
const n = new x.Mesh(I(a), c);
|
|
74
|
+
n.castShadow = n.receiveShadow = !0, o.add(n), a.forEach((t) => t.dispose());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
O as makeDoor
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=Doors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Doors.js","sources":["../../../../src/map/Doors.ts"],"sourcesContent":["import * as THREE from 'three';\nimport { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';\nimport type { SiteObject } from '../model/types';\nimport type { MaterialLibrary } from './materials';\nimport { metricUVs } from './surfaces';\n\n/** Local metres: X along the opening, Y toward side A, Z up. Hardware moves with its leaf. */\nexport function makeDoor(door: SiteObject, wallDepth: number, height: number, materials: MaterialLibrary) {\n const frame = new THREE.Group(),\n width = door.width,\n side = door.doorSwing ?? 1;\n const wood = materials.get('veneer', door.color ?? '#cab69a');\n const trim = materials.get('plaster', '#e1ded5'),\n metal = materials.metal('#aab0b2');\n const dark = materials.solid('#414448'),\n inset = materials.get('veneer', door.color ?? '#c0aa8d');\n const box = (\n parent: THREE.Group,\n w: number,\n d: number,\n h: number,\n x: number,\n y: number,\n z: number,\n material: THREE.Material,\n ) => {\n if (w <= 0 || d <= 0 || h <= 0) return;\n const geometry = new THREE.BoxGeometry(w, d, h);\n geometry.translate(x, y, z);\n metricUVs(geometry);\n const mesh = new THREE.Mesh(geometry, material);\n mesh.castShadow = mesh.receiveShadow = true;\n mesh.userData.entityId = door.id;\n parent.add(mesh);\n };\n // Recessed jambs and architraves on both sides, with a metal threshold at floor level.\n for (const sign of [-1, 1]) {\n box(frame, 0.035, wallDepth + 0.025, height, sign * (width / 2 - 0.0175), 0, height / 2, trim);\n for (const face of [-1, 1])\n box(\n frame,\n 0.07,\n 0.025,\n height + 0.035,\n sign * (width / 2 + 0.005),\n face * (wallDepth / 2 + 0.012),\n (height + 0.035) / 2,\n trim,\n );\n }\n box(frame, width, wallDepth + 0.025, 0.035, 0, 0, height - 0.0175, trim);\n for (const face of [-1, 1]) box(frame, width + 0.08, 0.025, 0.07, 0, face * (wallDepth / 2 + 0.012), height, trim);\n box(frame, width, wallDepth + 0.04, 0.012, 0, 0, 0.006, metal);\n\n const panels: { group: THREE.Group; setOpen: (value: number) => void }[] = [];\n const sliding = door.doorType === 'sliding',\n double = door.doorType === 'double';\n const travel = door.doorHinge === 'right' ? 1 : -1;\n const leafWidth = (double ? width / 2 : width) - (double ? 0.025 : 0.045);\n for (let i = 0; i < (double ? 2 : 1); i++) {\n const hand = double ? (i ? -1 : 1) : door.doorHinge === 'right' ? -1 : 1;\n const group = new THREE.Group(),\n pivot = new THREE.Group();\n group.add(pivot);\n pivot.position.x = sliding ? 0 : (-hand * width) / 2;\n pivot.position.y = sliding ? side * (wallDepth / 2 + 0.045) : 0;\n const x = sliding ? 0 : hand * (leafWidth / 2 + 0.025),\n h = height - 0.06,\n bottom = 0.02;\n box(pivot, leafWidth, 0.045, h, x, 0, bottom + h / 2, wood);\n // A shallow inset panel, narrow stile borders and a kick plate give depth at eye level.\n for (const face of [-1, 1]) {\n box(\n pivot,\n Math.max(0.1, leafWidth - 0.16),\n 0.006,\n Math.max(0.2, h - 0.38),\n x,\n face * 0.025,\n bottom + h / 2 + 0.03,\n inset,\n );\n box(pivot, Math.max(0.1, leafWidth - 0.1), 0.008, 0.13, x, face * 0.027, 0.13, metal);\n const latch = x + (sliding ? -travel : hand) * (leafWidth / 2 - 0.09);\n if (sliding) {\n box(pivot, 0.026, 0.035, 0.3, latch, face * 0.047, 1.03, metal);\n } else {\n box(pivot, 0.052, 0.012, 0.12, latch, face * 0.033, 1.03, metal);\n box(pivot, 0.11, 0.024, 0.021, latch - hand * 0.035, face * 0.067, 1.06, metal);\n box(pivot, 0.017, 0.012, 0.028, latch, face * 0.042, 1.0, dark);\n }\n }\n if (!sliding) for (const z of [0.25, h / 2, h - 0.22]) box(pivot, 0.025, 0.058, 0.09, hand * 0.025, 0, z, metal);\n mergeParts(pivot);\n group.traverse(child => {\n if (child instanceof THREE.Mesh) child.userData.entityId = door.id;\n });\n group.userData.doorType = door.doorType ?? 'hinged';\n group.userData.entityId = door.id;\n panels.push({\n group,\n setOpen: value => {\n if (sliding) pivot.position.x = travel * (width + 0.03) * value;\n else pivot.rotation.z = side * hand * THREE.MathUtils.degToRad(72) * value;\n },\n });\n }\n if (sliding) {\n const y = side * (wallDepth / 2 + 0.065);\n box(frame, width * 2 + 0.12, 0.05, 0.065, (travel * width) / 2, y, height + 0.12, metal);\n for (const x of [-width / 2, width / 2, travel * width]) box(frame, 0.045, 0.08, 0.13, x, y, height + 0.1, dark);\n }\n return { frame, panels };\n}\n\n/** Each articulated leaf costs one draw per finish, rather than one per handle or trim strip. */\nfunction mergeParts(group: THREE.Group) {\n const buckets = new Map<THREE.Material, THREE.BufferGeometry[]>();\n for (const child of [...group.children]) {\n const mesh = child as THREE.Mesh<THREE.BufferGeometry, THREE.Material>;\n const list = buckets.get(mesh.material) ?? [];\n list.push(mesh.geometry);\n buckets.set(mesh.material, list);\n group.remove(mesh);\n }\n for (const [material, parts] of buckets) {\n const mesh = new THREE.Mesh(mergeGeometries(parts)!, material);\n mesh.castShadow = mesh.receiveShadow = true;\n group.add(mesh);\n parts.forEach(p => p.dispose());\n }\n}\n"],"names":["makeDoor","door","wallDepth","height","materials","frame","THREE","width","side","wood","trim","metal","dark","inset","box","parent","w","d","h","x","y","z","material","geometry","metricUVs","mesh","sign","face","panels","sliding","double","travel","leafWidth","hand","group","pivot","bottom","latch","mergeParts","child","value","buckets","list","parts","mergeGeometries","p"],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as c from "three";
|
|
2
|
+
import { elevatorMirrorFace as E, elevatorDoorWidth as F } from "./elevators.js";
|
|
3
|
+
import { CabinMirror as P } from "./CabinMirror.js";
|
|
4
|
+
const R = { front: 0, right: 90, back: 180, left: 270 };
|
|
5
|
+
function j(e, r, t, u, m) {
|
|
6
|
+
const n = new c.Group();
|
|
7
|
+
n.name = "Detailed elevator cabin", n.userData.cabin = !0;
|
|
8
|
+
const a = (o, b, d, w, k, M, l, D) => {
|
|
9
|
+
const i = new c.Mesh(new c.BoxGeometry(b, d, w), D);
|
|
10
|
+
return i.position.set(k, M, l), i.castShadow = i.receiveShadow = !0, o.add(i), i;
|
|
11
|
+
}, v = a(n, e.width, e.depth, 0.08, 0, 0, -0.04, t.floor);
|
|
12
|
+
v.name = "Cabin stone floor", v.userData.cabinFloor = !0, a(n, e.width, e.depth, 0.07, 0, 0, r, t.wall);
|
|
13
|
+
for (let o = -e.width / 2 + 0.6; o < e.width / 2; o += 0.6)
|
|
14
|
+
a(n, 8e-3, e.depth, 3e-3, o, 0, 2e-3, t.dark);
|
|
15
|
+
for (let o = -e.depth / 2 + 0.6; o < e.depth / 2; o += 0.6)
|
|
16
|
+
a(n, e.width, 8e-3, 3e-3, 0, o, 2e-3, t.dark);
|
|
17
|
+
for (const o of [-1, 1])
|
|
18
|
+
a(n, 0.22, e.depth * 0.7, 0.025, o * e.width * 0.3, 0, r - 0.05, t.trim), a(n, 0.17, e.depth * 0.67, 0.026, o * e.width * 0.3, 0, r - 0.065, t.lamp);
|
|
19
|
+
for (let o = 0; o < 9; o++) a(n, 0.36, 0.015, 0.012, 0, (o - 4) * 0.045, r - 0.045, t.dark);
|
|
20
|
+
const C = E(e);
|
|
21
|
+
for (const [o, b] of Object.entries(R)) {
|
|
22
|
+
const d = new c.Group();
|
|
23
|
+
d.rotation.z = b * Math.PI / 180, n.add(d);
|
|
24
|
+
const w = b % 180 ? e.depth : e.width, M = (b % 180 ? e.width : e.depth) / 2 - 0.045, l = M - 0.055, D = (e.doorSides ?? ["front"]).includes(o), i = F(e, b);
|
|
25
|
+
if (D) {
|
|
26
|
+
const x = Math.max(0.04, (w - i) / 2);
|
|
27
|
+
for (const f of [-1, 1])
|
|
28
|
+
a(d, x, 0.09, r, f * (i / 2 + x / 2), M, r / 2, t.steel), a(d, 0.032, 0.045, 2.3, f * (i / 2 + 0.025), l - 0.01, 1.15, t.trim);
|
|
29
|
+
a(d, i, 0.09, Math.max(0.05, r - 2.25), 0, M, (r + 2.25) / 2, t.steel), a(d, i, 0.22, 0.015, 0, M - 0.045, 7e-3, t.trim);
|
|
30
|
+
const s = document.createElement("canvas");
|
|
31
|
+
s.width = 256, s.height = 80;
|
|
32
|
+
const p = s.getContext("2d");
|
|
33
|
+
p.fillStyle = "#101a1f", p.fillRect(0, 0, 256, 80), p.fillStyle = "#f8ce82", p.font = "500 48px monospace", p.textAlign = "center", p.textBaseline = "middle", p.fillText(`${m ? "↕ " : ""}${u}`, 128, 42);
|
|
34
|
+
const S = new c.CanvasTexture(s);
|
|
35
|
+
S.colorSpace = c.SRGBColorSpace;
|
|
36
|
+
const y = new c.MeshBasicMaterial({ map: S, toneMapped: !1 });
|
|
37
|
+
y.userData.ownedMap = !0;
|
|
38
|
+
const G = new c.Mesh(new c.PlaneGeometry(0.42, 0.13), y);
|
|
39
|
+
G.position.set(0, l - 0.02, Math.min(r - 0.1, 2.36)), G.rotation.x = Math.PI / 2, d.add(G);
|
|
40
|
+
const h = i / 2 + x / 2;
|
|
41
|
+
a(d, Math.min(0.22, x * 0.85), 0.025, 0.78, h, l - 0.025, 1.26, t.trim);
|
|
42
|
+
for (let f = 0; f < 5; f++) {
|
|
43
|
+
const B = new c.Mesh(
|
|
44
|
+
new c.CylinderGeometry(0.019, 0.019, 0.015, 16),
|
|
45
|
+
f === 0 ? t.display : t.dark
|
|
46
|
+
);
|
|
47
|
+
B.position.set(h, l - 0.047, 1.48 - f * 0.105), d.add(B);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
a(d, w, 0.09, r, 0, M, r / 2, t.wall);
|
|
51
|
+
for (let s = -w / 2 + 0.55; s < w / 2; s += 0.55)
|
|
52
|
+
a(d, 0.012, 8e-3, r - 0.16, s, l, r / 2, t.dark);
|
|
53
|
+
if (a(d, w, 0.014, 0.15, 0, l, 0.075, t.steel), o === C) {
|
|
54
|
+
const s = Math.max(0.35, w - 0.34), p = Math.min(1.93, r - 0.42), S = 0.26 + p / 2;
|
|
55
|
+
a(d, s + 0.05, 0.035, p + 0.05, 0, l - 0.018, S, t.trim);
|
|
56
|
+
const y = new P(s, p);
|
|
57
|
+
y.position.set(0, l - 0.041, S), y.rotation.x = Math.PI / 2, d.add(y);
|
|
58
|
+
}
|
|
59
|
+
const x = new c.Mesh(
|
|
60
|
+
new c.CylinderGeometry(0.025, 0.025, Math.max(0.25, w - 0.38), 12).rotateZ(Math.PI / 2),
|
|
61
|
+
t.trim
|
|
62
|
+
);
|
|
63
|
+
x.position.set(0, l - 0.12, 0.94), d.add(x);
|
|
64
|
+
for (const s of [-1, 1])
|
|
65
|
+
a(d, 0.035, 0.11, 0.035, s * (w / 2 - 0.28), l - 0.065, 0.94, t.trim);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return n;
|
|
69
|
+
}
|
|
70
|
+
function I(e, r, t) {
|
|
71
|
+
const u = e / 2, m = u * (1 - Math.max(0, Math.min(1, t)));
|
|
72
|
+
return { width: Math.max(0, m - 4e-3), center: r * (u - m / 2) };
|
|
73
|
+
}
|
|
74
|
+
function A(e, r, t, u) {
|
|
75
|
+
const m = new c.Group();
|
|
76
|
+
m.name = "Recessed metallic elevator door", m.userData.cabinDoor = !0;
|
|
77
|
+
const n = new c.Mesh(new c.BoxGeometry(1, 0.065, r), u.steel);
|
|
78
|
+
n.position.z = r / 2, n.castShadow = n.receiveShadow = !0, m.add(n);
|
|
79
|
+
const a = new c.Mesh(new c.BoxGeometry(6e-3, 0.069, r), u.trim);
|
|
80
|
+
a.position.z = r / 2, m.add(a);
|
|
81
|
+
const v = (C) => {
|
|
82
|
+
const o = I(e, t, C);
|
|
83
|
+
n.visible = a.visible = o.width > 1e-3, n.scale.x = o.width, n.position.x = o.center, a.position.x = o.center - t * o.width / 2, m.userData.openFraction = C;
|
|
84
|
+
};
|
|
85
|
+
return v(0), { group: m, update: v };
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
R as FACE_TURN,
|
|
89
|
+
I as doorLeafSlice,
|
|
90
|
+
A as makeCabinDoor,
|
|
91
|
+
j as makeElevatorCabin
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=ElevatorCabin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElevatorCabin.js","sources":["../../../../src/map/ElevatorCabin.ts"],"sourcesContent":["import * as THREE from 'three';\nimport type { SiteObject } from '../model/types';\nimport { elevatorDoorWidth, elevatorMirrorFace } from './elevators';\nimport { CabinMirror } from './CabinMirror';\nimport type { CabinMaterials } from './CabinMaterials';\n\nexport const FACE_TURN = { front: 0, right: 90, back: 180, left: 270 };\n\nexport function makeElevatorCabin(\n o: SiteObject,\n height: number,\n finish: CabinMaterials,\n floorCode: string,\n moving: boolean,\n) {\n const group = new THREE.Group();\n group.name = 'Detailed elevator cabin';\n group.userData.cabin = true;\n const box = (\n parent: THREE.Object3D,\n w: number,\n d: number,\n h: number,\n x: number,\n y: number,\n z: number,\n material: THREE.Material,\n ) => {\n const mesh = new THREE.Mesh(new THREE.BoxGeometry(w, d, h), material);\n mesh.position.set(x, y, z);\n mesh.castShadow = mesh.receiveShadow = true;\n parent.add(mesh);\n return mesh;\n };\n const floor = box(group, o.width, o.depth, 0.08, 0, 0, -0.04, finish.floor);\n floor.name = 'Cabin stone floor';\n floor.userData.cabinFloor = true;\n box(group, o.width, o.depth, 0.07, 0, 0, height, finish.wall);\n // Stone floor panels, with dark narrow joints and a steel threshold.\n for (let x = -o.width / 2 + 0.6; x < o.width / 2; x += 0.6)\n box(group, 0.008, o.depth, 0.003, x, 0, 0.002, finish.dark);\n for (let y = -o.depth / 2 + 0.6; y < o.depth / 2; y += 0.6)\n box(group, o.width, 0.008, 0.003, 0, y, 0.002, finish.dark);\n for (const side of [-1, 1]) {\n box(group, 0.22, o.depth * 0.7, 0.025, side * o.width * 0.3, 0, height - 0.05, finish.trim);\n box(group, 0.17, o.depth * 0.67, 0.026, side * o.width * 0.3, 0, height - 0.065, finish.lamp);\n }\n for (let i = 0; i < 9; i++) box(group, 0.36, 0.015, 0.012, 0, (i - 4) * 0.045, height - 0.045, finish.dark);\n const mirrorFace = elevatorMirrorFace(o);\n for (const [face, turn] of Object.entries(FACE_TURN) as [keyof typeof FACE_TURN, number][]) {\n const wall = new THREE.Group();\n wall.rotation.z = (turn * Math.PI) / 180;\n group.add(wall);\n const across = turn % 180 ? o.depth : o.width,\n depth = turn % 180 ? o.width : o.depth;\n const y = depth / 2 - 0.045,\n inside = y - 0.055;\n const hasDoor = (o.doorSides ?? ['front']).includes(face),\n gap = elevatorDoorWidth(o, turn);\n if (hasDoor) {\n const jamb = Math.max(0.04, (across - gap) / 2);\n for (const side of [-1, 1]) {\n box(wall, jamb, 0.09, height, side * (gap / 2 + jamb / 2), y, height / 2, finish.steel);\n box(wall, 0.032, 0.045, 2.3, side * (gap / 2 + 0.025), inside - 0.01, 1.15, finish.trim);\n }\n box(wall, gap, 0.09, Math.max(0.05, height - 2.25), 0, y, (height + 2.25) / 2, finish.steel);\n box(wall, gap, 0.22, 0.015, 0, y - 0.045, 0.007, finish.trim);\n // Backlit floor display above the door; visible both directly and in the mirror.\n const canvas = document.createElement('canvas');\n canvas.width = 256;\n canvas.height = 80;\n const c = canvas.getContext('2d')!;\n c.fillStyle = '#101a1f';\n c.fillRect(0, 0, 256, 80);\n c.fillStyle = '#f8ce82';\n c.font = '500 48px monospace';\n c.textAlign = 'center';\n c.textBaseline = 'middle';\n c.fillText(`${moving ? '↕ ' : ''}${floorCode}`, 128, 42);\n const texture = new THREE.CanvasTexture(canvas);\n texture.colorSpace = THREE.SRGBColorSpace;\n const display = new THREE.MeshBasicMaterial({ map: texture, toneMapped: false });\n display.userData.ownedMap = true;\n const screen = new THREE.Mesh(new THREE.PlaneGeometry(0.42, 0.13), display);\n screen.position.set(0, inside - 0.02, Math.min(height - 0.1, 2.36));\n screen.rotation.x = Math.PI / 2;\n wall.add(screen);\n // A recessed control plate to one side of the entrance. Commands remain in the sidebar.\n const panelX = gap / 2 + jamb / 2;\n box(wall, Math.min(0.22, jamb * 0.85), 0.025, 0.78, panelX, inside - 0.025, 1.26, finish.trim);\n for (let i = 0; i < 5; i++) {\n const button = new THREE.Mesh(\n new THREE.CylinderGeometry(0.019, 0.019, 0.015, 16),\n i === 0 ? finish.display : finish.dark,\n );\n button.position.set(panelX, inside - 0.047, 1.48 - i * 0.105);\n wall.add(button);\n }\n } else {\n box(wall, across, 0.09, height, 0, y, height / 2, finish.wall);\n for (let x = -across / 2 + 0.55; x < across / 2; x += 0.55)\n box(wall, 0.012, 0.008, height - 0.16, x, inside, height / 2, finish.dark);\n box(wall, across, 0.014, 0.15, 0, inside, 0.075, finish.steel);\n if (face === mirrorFace) {\n const width = Math.max(0.35, across - 0.34),\n mirrorHeight = Math.min(1.93, height - 0.42),\n centerZ = 0.26 + mirrorHeight / 2;\n box(wall, width + 0.05, 0.035, mirrorHeight + 0.05, 0, inside - 0.018, centerZ, finish.trim);\n const mirror = new CabinMirror(width, mirrorHeight);\n mirror.position.set(0, inside - 0.041, centerZ);\n mirror.rotation.x = Math.PI / 2;\n wall.add(mirror);\n }\n const rail = new THREE.Mesh(\n new THREE.CylinderGeometry(0.025, 0.025, Math.max(0.25, across - 0.38), 12).rotateZ(Math.PI / 2),\n finish.trim,\n );\n rail.position.set(0, inside - 0.12, 0.94);\n wall.add(rail);\n for (const side of [-1, 1])\n box(wall, 0.035, 0.11, 0.035, side * (across / 2 - 0.28), inside - 0.065, 0.94, finish.trim);\n }\n }\n return group;\n}\n\n/** The visible portion of a leaf as it retracts behind the jamb. Clipping at the opening keeps\n * the concealed leaf inside its pocket even on shafts narrower than a full exposed door travel. */\nexport function doorLeafSlice(gap: number, sign: number, progress: number) {\n const half = gap / 2,\n remaining = half * (1 - Math.max(0, Math.min(1, progress)));\n return { width: Math.max(0, remaining - 0.004), center: sign * (half - remaining / 2) };\n}\nexport function makeCabinDoor(gap: number, height: number, sign: number, finish: CabinMaterials) {\n const group = new THREE.Group();\n group.name = 'Recessed metallic elevator door';\n group.userData.cabinDoor = true;\n const leaf = new THREE.Mesh(new THREE.BoxGeometry(1, 0.065, height), finish.steel);\n leaf.position.z = height / 2;\n leaf.castShadow = leaf.receiveShadow = true;\n group.add(leaf);\n const edge = new THREE.Mesh(new THREE.BoxGeometry(0.006, 0.069, height), finish.trim);\n edge.position.z = height / 2;\n group.add(edge);\n const update = (progress: number) => {\n const slice = doorLeafSlice(gap, sign, progress);\n leaf.visible = edge.visible = slice.width > 0.001;\n leaf.scale.x = slice.width;\n leaf.position.x = slice.center;\n edge.position.x = slice.center - (sign * slice.width) / 2;\n group.userData.openFraction = progress;\n };\n update(0);\n return { group, update };\n}\n"],"names":["FACE_TURN","makeElevatorCabin","o","height","finish","floorCode","moving","group","THREE","box","parent","w","h","x","y","z","material","mesh","floor","side","i","mirrorFace","elevatorMirrorFace","face","turn","wall","across","inside","hasDoor","gap","elevatorDoorWidth","jamb","canvas","c","texture","display","screen","panelX","button","width","mirrorHeight","centerZ","mirror","CabinMirror","rail","doorLeafSlice","sign","progress","half","remaining","makeCabinDoor","leaf","edge","update","slice"],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
var w = Object.defineProperty;
|
|
2
|
+
var M = (l, o, e) => o in l ? w(l, o, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[o] = e;
|
|
3
|
+
var d = (l, o, e) => M(l, typeof o != "symbol" ? o + "" : o, e);
|
|
4
|
+
import * as c from "three";
|
|
5
|
+
import { kelvinColor as S } from "./lighting.js";
|
|
6
|
+
function u(l, o, e = 0) {
|
|
7
|
+
if (e <= 0) return 1;
|
|
8
|
+
let n = 0;
|
|
9
|
+
for (let a = 0; a < l.length; a++) n = Math.imul(n, 31) + l.charCodeAt(a) | 0;
|
|
10
|
+
const h = (o + (n >>> 0) / 1e3) % 7.7, f = h < 0.09 || h > 0.18 && h < 0.25;
|
|
11
|
+
return 1 - e * (f ? 0.95 : 0.015 * (1 + Math.sin(o * 37 + n)));
|
|
12
|
+
}
|
|
13
|
+
const v = 4, p = 2;
|
|
14
|
+
class I {
|
|
15
|
+
constructor(o, e, n, h, f) {
|
|
16
|
+
d(this, "points");
|
|
17
|
+
d(this, "colors");
|
|
18
|
+
d(this, "emitters");
|
|
19
|
+
d(this, "lights");
|
|
20
|
+
d(this, "active", []);
|
|
21
|
+
d(this, "lastFlicker", -1 / 0);
|
|
22
|
+
d(this, "flickering");
|
|
23
|
+
this.fixtures = e, this.points = e.map((t) => {
|
|
24
|
+
var s;
|
|
25
|
+
return new c.Vector3(...n(t.position), h + (((s = t.light) == null ? void 0 : s.mountHeight) ?? t.height));
|
|
26
|
+
}), this.colors = e.map((t) => new c.Color(S(t.light.kelvin))), this.flickering = e.some((t) => {
|
|
27
|
+
var s;
|
|
28
|
+
return !!((s = t.light) != null && s.flicker);
|
|
29
|
+
});
|
|
30
|
+
const a = new c.InstancedMesh(
|
|
31
|
+
new c.BoxGeometry(1, 1, 1),
|
|
32
|
+
f.solid("#b6b4a6"),
|
|
33
|
+
e.length
|
|
34
|
+
);
|
|
35
|
+
this.emitters = new c.InstancedMesh(
|
|
36
|
+
new c.BoxGeometry(1, 1, 1),
|
|
37
|
+
new c.MeshBasicMaterial({ color: "#ffffff", toneMapped: !1 }),
|
|
38
|
+
e.length
|
|
39
|
+
);
|
|
40
|
+
for (const t of [a, this.emitters])
|
|
41
|
+
t.userData.entityIds = e.map((s) => s.id), t.castShadow = !1, o.add(t);
|
|
42
|
+
const i = new c.Object3D();
|
|
43
|
+
e.forEach((t, s) => {
|
|
44
|
+
const r = n(t.position), g = n([
|
|
45
|
+
t.position[0] + Math.cos(t.rotation * Math.PI / 180),
|
|
46
|
+
t.position[1] + Math.sin(t.rotation * Math.PI / 180)
|
|
47
|
+
]);
|
|
48
|
+
i.position.copy(this.points[s]), i.rotation.set(
|
|
49
|
+
t.light.mountHeight !== void 0 && t.light.mountHeight < 0 ? -Math.PI / 2 : 0,
|
|
50
|
+
0,
|
|
51
|
+
Math.atan2(g[1] - r[1], g[0] - r[0]),
|
|
52
|
+
"ZYX"
|
|
53
|
+
), i.scale.set(t.width + 0.08, t.depth + 0.08, 0.09), i.updateMatrix(), a.setMatrixAt(s, i.matrix), i.translateZ(-0.052), i.scale.set(t.width, t.depth, 0.018), i.updateMatrix(), this.emitters.setMatrixAt(s, i.matrix), this.emitters.setColorAt(s, this.colors[s].clone().multiplyScalar(t.light.intensity > 0 ? 1 : 0.08));
|
|
54
|
+
}), this.emitters.instanceColor.setUsage(c.DynamicDrawUsage), this.lights = Array.from({ length: Math.min(v, e.length) }, () => {
|
|
55
|
+
const t = new c.PointLight("#fff", 0, 12, 2);
|
|
56
|
+
return t.castShadow = !1, t.shadow.autoUpdate = !1, t.shadow.mapSize.set(256, 256), t.shadow.camera.near = 0.1, t.shadow.bias = -1e-3, t.shadow.normalBias = 0.025, o.add(t), t;
|
|
57
|
+
}), this.lights.slice(0, p).forEach((t) => {
|
|
58
|
+
t.castShadow = !0;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** The shadows go to the nearest lamps, and stay with them until a lamp without one is clearly
|
|
62
|
+
* nearer — a swap redraws a shadow, so a walker straddling two lamps must not swap on every step.
|
|
63
|
+
* Always exactly SHADOWED_FIXTURE_LIGHTS flags are set, empty slots included: see the constant. */
|
|
64
|
+
assignShadows(o) {
|
|
65
|
+
const e = this.lights.map((i, t) => ({ light: i, slot: t, i: this.active[t] })), n = e.filter((i) => i.i !== void 0), h = (i) => this.points[i.i].distanceToSquared(o), f = [...n].sort((i, t) => h(i) - h(t)), a = new Set(n.filter((i) => i.light.castShadow));
|
|
66
|
+
for (const i of f.slice(0, p)) {
|
|
67
|
+
if (a.has(i)) continue;
|
|
68
|
+
const t = [...a].sort((s, r) => h(r) - h(s))[0];
|
|
69
|
+
t && h(i) * 1.3 < h(t) ? (a.delete(t), a.add(i)) : a.size < p && a.add(i);
|
|
70
|
+
}
|
|
71
|
+
for (const i of e) a.size < p && i.i === void 0 && a.add(i);
|
|
72
|
+
for (const i of e) {
|
|
73
|
+
const t = a.has(i);
|
|
74
|
+
t && !i.light.castShadow && (i.light.shadow.needsUpdate = !0), i.light.castShadow = t;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Every lamp's shadow is stale — the scene changed under them. */
|
|
78
|
+
invalidate() {
|
|
79
|
+
for (const o of this.lights) o.shadow.needsUpdate = !0;
|
|
80
|
+
}
|
|
81
|
+
update(o, e) {
|
|
82
|
+
const n = this.points.map((t, s) => ({ i: s, distance: t.distanceToSquared(o) })).filter(
|
|
83
|
+
({ i: t, distance: s }) => this.fixtures[t].light.intensity > 0 && s < this.fixtures[t].light.range ** 2
|
|
84
|
+
).sort((t, s) => t.distance - s.distance).map((t) => t.i), h = n.slice(0, this.lights.length), f = new Set(n.slice(0, this.lights.length + 1)), a = this.lights.map((t, s) => {
|
|
85
|
+
const r = this.active[s];
|
|
86
|
+
return r !== void 0 && f.has(r) ? r : void 0;
|
|
87
|
+
});
|
|
88
|
+
for (const t of h) {
|
|
89
|
+
if (a.includes(t)) continue;
|
|
90
|
+
const s = a.indexOf(void 0);
|
|
91
|
+
if (s < 0) break;
|
|
92
|
+
a[s] = t;
|
|
93
|
+
}
|
|
94
|
+
let i = !1;
|
|
95
|
+
return this.lights.forEach((t, s) => {
|
|
96
|
+
const r = a[s];
|
|
97
|
+
if (this.active[s] !== r && (i = !0, t.shadow.needsUpdate = !0), r === void 0) {
|
|
98
|
+
t.intensity = 0;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const g = this.fixtures[r], m = g.light;
|
|
102
|
+
t.position.copy(this.points[r]), t.position.z -= 0.12, t.color.copy(this.colors[r]), t.distance = m.range, t.intensity = m.intensity * u(g.id, e, m.flicker);
|
|
103
|
+
}), this.active = a, this.assignShadows(o), this.flickering && e - this.lastFlicker >= 0.04 && (this.fixtures.forEach((t, s) => {
|
|
104
|
+
t.light.flicker && this.emitters.setColorAt(
|
|
105
|
+
s,
|
|
106
|
+
this.colors[s].clone().multiplyScalar(t.light.intensity > 0 ? u(t.id, e, t.light.flicker) : 0.08)
|
|
107
|
+
);
|
|
108
|
+
}), this.emitters.instanceColor.needsUpdate = !0, this.lastFlicker = e), { shadowsChanged: i, animate: this.flickering };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
I as FixtureLights,
|
|
113
|
+
v as MAX_FIXTURE_LIGHTS,
|
|
114
|
+
p as SHADOWED_FIXTURE_LIGHTS,
|
|
115
|
+
u as fixtureOutput
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=FixtureLights.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FixtureLights.js","sources":["../../../../src/map/FixtureLights.ts"],"sourcesContent":["import * as THREE from 'three';\nimport type { Point, SiteObject } from '../model/types';\nimport { kelvinColor } from './lighting';\nimport type { MaterialLibrary } from './materials';\n\n/** Occasional ballast dropouts, not a regular strobe. Stable per fixture and elapsed time. */\nexport function fixtureOutput(id: string, seconds: number, flicker = 0): number {\n if (flicker <= 0) return 1;\n let hash = 0;\n for (let i = 0; i < id.length; i++) hash = (Math.imul(hash, 31) + id.charCodeAt(i)) | 0;\n const phase = (seconds + (hash >>> 0) / 1000) % 7.7;\n const dropout = phase < 0.09 || (phase > 0.18 && phase < 0.25);\n return 1 - flicker * (dropout ? 0.95 : 0.015 * (1 + Math.sin(seconds * 37 + hash)));\n}\nexport const MAX_FIXTURE_LIGHTS = 4;\n/** How many of those throw shadows. A shadowed lamp is six shadow passes when it moves and a cube\n * of shadow taps on every pixel every frame; the two nearest carry the room, and the two beyond\n * them light without shadow, which nobody notices past the first two. The count is held constant\n * — an empty slot keeps its flag — because the shader is compiled for a number of shadowed lights\n * and changing that number recompiles every material in the scene. */\nexport const SHADOWED_FIXTURE_LIGHTS = 2;\n\n/** All fittings are two instanced draws. Only the nearest few cast light/shadows, keeping the\n * shader and shadow-map budget constant whether the document contains ten lamps or ten thousand. */\nexport class FixtureLights {\n private points: THREE.Vector3[];\n private colors: THREE.Color[];\n private emitters: THREE.InstancedMesh;\n private lights: THREE.PointLight[];\n private active: (number | undefined)[] = [];\n private lastFlicker = -Infinity;\n private flickering: boolean;\n constructor(\n scene: THREE.Scene,\n private fixtures: SiteObject[],\n xy: (p: Point) => Point,\n base: number,\n materials: MaterialLibrary,\n ) {\n this.points = fixtures.map(o => new THREE.Vector3(...xy(o.position), base + (o.light?.mountHeight ?? o.height)));\n this.colors = fixtures.map(o => new THREE.Color(kelvinColor(o.light!.kelvin)));\n this.flickering = fixtures.some(o => !!o.light?.flicker);\n const housings = new THREE.InstancedMesh(\n new THREE.BoxGeometry(1, 1, 1),\n materials.solid('#b6b4a6'),\n fixtures.length,\n );\n this.emitters = new THREE.InstancedMesh(\n new THREE.BoxGeometry(1, 1, 1),\n new THREE.MeshBasicMaterial({ color: '#ffffff', toneMapped: false }),\n fixtures.length,\n );\n for (const mesh of [housings, this.emitters]) {\n mesh.userData.entityIds = fixtures.map(o => o.id);\n mesh.castShadow = false;\n scene.add(mesh);\n }\n const transform = new THREE.Object3D();\n fixtures.forEach((o, i) => {\n const origin = xy(o.position),\n along = xy([\n o.position[0] + Math.cos((o.rotation * Math.PI) / 180),\n o.position[1] + Math.sin((o.rotation * Math.PI) / 180),\n ]);\n transform.position.copy(this.points[i]);\n transform.rotation.set(\n o.light!.mountHeight !== undefined && o.light!.mountHeight < 0 ? -Math.PI / 2 : 0,\n 0,\n Math.atan2(along[1] - origin[1], along[0] - origin[0]),\n 'ZYX',\n );\n transform.scale.set(o.width + 0.08, o.depth + 0.08, 0.09);\n transform.updateMatrix();\n housings.setMatrixAt(i, transform.matrix);\n // Offset out of the housing along the fitting's own face: downward for ceiling panels,\n // horizontally into the pool for wall-mounted submerged fittings.\n transform.translateZ(-0.052);\n transform.scale.set(o.width, o.depth, 0.018);\n transform.updateMatrix();\n this.emitters.setMatrixAt(i, transform.matrix);\n this.emitters.setColorAt(i, this.colors[i].clone().multiplyScalar(o.light!.intensity > 0 ? 1 : 0.08));\n });\n this.emitters.instanceColor!.setUsage(THREE.DynamicDrawUsage);\n this.lights = Array.from({ length: Math.min(MAX_FIXTURE_LIGHTS, fixtures.length) }, () => {\n const light = new THREE.PointLight('#fff', 0, 12, 2);\n light.castShadow = false;\n // Each lamp's shadow is six passes over the scene, and they are redrawn only when the lamp\n // itself changes — see update(). Left on automatic, every one of them would be redrawn each\n // time anything at all asked for shadows, the sun included.\n light.shadow.autoUpdate = false;\n light.shadow.mapSize.set(256, 256);\n light.shadow.camera.near = 0.1;\n light.shadow.bias = -0.001;\n light.shadow.normalBias = 0.025;\n scene.add(light);\n return light;\n });\n this.lights.slice(0, SHADOWED_FIXTURE_LIGHTS).forEach(light => {\n light.castShadow = true;\n });\n }\n /** The shadows go to the nearest lamps, and stay with them until a lamp without one is clearly\n * nearer — a swap redraws a shadow, so a walker straddling two lamps must not swap on every step.\n * Always exactly SHADOWED_FIXTURE_LIGHTS flags are set, empty slots included: see the constant. */\n private assignShadows(eye: THREE.Vector3) {\n const active = this.lights.map((light, slot) => ({ light, slot, i: this.active[slot] }));\n const lit = active.filter(a => a.i !== undefined);\n const distance = (a: (typeof lit)[number]) => this.points[a.i!].distanceToSquared(eye);\n const nearest = [...lit].sort((a, b) => distance(a) - distance(b));\n const casters = new Set(lit.filter(a => a.light.castShadow));\n // Promote a lamp only when it is nearer than a current caster by a margin: 1.3 in squared\n // distance is about 14 % in metres, a clear step and not a wobble.\n for (const a of nearest.slice(0, SHADOWED_FIXTURE_LIGHTS)) {\n if (casters.has(a)) continue;\n const worst = [...casters].sort((x, y) => distance(y) - distance(x))[0];\n if (worst && distance(a) * 1.3 < distance(worst)) {\n casters.delete(worst);\n casters.add(a);\n } else if (casters.size < SHADOWED_FIXTURE_LIGHTS) casters.add(a);\n }\n // Pad with empty slots so the number of shadowed lights never changes.\n for (const a of active) if (casters.size < SHADOWED_FIXTURE_LIGHTS && a.i === undefined) casters.add(a);\n for (const a of active) {\n const cast = casters.has(a);\n if (cast && !a.light.castShadow) a.light.shadow.needsUpdate = true;\n a.light.castShadow = cast;\n }\n }\n /** Every lamp's shadow is stale — the scene changed under them. */\n invalidate() {\n for (const light of this.lights) light.shadow.needsUpdate = true;\n }\n update(eye: THREE.Vector3, seconds: number) {\n const ranked = this.points\n .map((at, i) => ({ i, distance: at.distanceToSquared(eye) }))\n .filter(\n ({ i, distance }) => this.fixtures[i].light!.intensity > 0 && distance < this.fixtures[i].light!.range ** 2,\n )\n .sort((a, b) => a.distance - b.distance)\n .map(c => c.i);\n // A lamp keeps its slot for as long as it is lit and near: swapping a lamp means redrawing its\n // shadow, six passes over everything it can reach, so the assignment has to be stable under a\n // walker's ordinary jitter. Re-sorting by distance every frame was not — two lamps at similar\n // range traded slots on every step and every step redrew both. A lamp stays while it ranks\n // within one place of the cut, so a lamp on the boundary does not flicker in and out either.\n const wanted = ranked.slice(0, this.lights.length);\n const keep = new Set(ranked.slice(0, this.lights.length + 1));\n const next: (number | undefined)[] = this.lights.map((_, slot) => {\n const i = this.active[slot];\n return i !== undefined && keep.has(i) ? i : undefined;\n });\n for (const i of wanted) {\n if (next.includes(i)) continue;\n const free = next.indexOf(undefined);\n if (free < 0) break;\n next[free] = i;\n }\n let shadowsChanged = false;\n this.lights.forEach((light, slot) => {\n const i = next[slot];\n if (this.active[slot] !== i) {\n shadowsChanged = true;\n light.shadow.needsUpdate = true;\n }\n if (i === undefined) {\n light.intensity = 0;\n return;\n }\n const o = this.fixtures[i],\n spec = o.light!;\n light.position.copy(this.points[i]);\n light.position.z -= 0.12;\n light.color.copy(this.colors[i]);\n light.distance = spec.range;\n light.intensity = spec.intensity * fixtureOutput(o.id, seconds, spec.flicker);\n });\n this.active = next;\n this.assignShadows(eye);\n if (this.flickering && seconds - this.lastFlicker >= 0.04) {\n this.fixtures.forEach((o, i) => {\n if (o.light!.flicker)\n this.emitters.setColorAt(\n i,\n this.colors[i]\n .clone()\n .multiplyScalar(o.light!.intensity > 0 ? fixtureOutput(o.id, seconds, o.light!.flicker) : 0.08),\n );\n });\n this.emitters.instanceColor!.needsUpdate = true;\n this.lastFlicker = seconds;\n }\n return { shadowsChanged, animate: this.flickering };\n }\n}\n"],"names":["fixtureOutput","id","seconds","flicker","hash","i","phase","dropout","MAX_FIXTURE_LIGHTS","SHADOWED_FIXTURE_LIGHTS","FixtureLights","scene","fixtures","xy","base","materials","__publicField","THREE","o","_a","kelvinColor","housings","mesh","transform","origin","along","light","eye","active","slot","lit","a","distance","nearest","b","casters","worst","x","y","cast","ranked","at","c","wanted","keep","next","_","free","shadowsChanged","spec"],"mappings":"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"}
|