@pascal-app/viewer 0.7.0 → 0.9.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/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +38 -22
- package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
- package/dist/components/renderers/parametric-node-renderer.js +34 -0
- package/dist/components/renderers/scene-renderer.d.ts +1 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +5 -1
- package/dist/components/viewer/ground-occluder.d.ts +1 -1
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +12 -24
- package/dist/components/viewer/index.d.ts +38 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +183 -60
- package/dist/components/viewer/lights.d.ts +1 -1
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +170 -43
- package/dist/components/viewer/perf-monitor.d.ts +1 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +99 -11
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +217 -61
- package/dist/components/viewer/registered-systems.d.ts +12 -0
- package/dist/components/viewer/registered-systems.d.ts.map +1 -0
- package/dist/components/viewer/registered-systems.js +47 -0
- package/dist/components/viewer/scene-bvh.d.ts +17 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.js +94 -0
- package/dist/components/viewer/selection-manager.d.ts +1 -1
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +20 -5
- package/dist/components/viewer/viewer-camera.d.ts +1 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-node-events.d.ts +5 -73
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +19 -7
- package/dist/index.d.ts +36 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -3
- package/dist/lib/csg-utils.d.ts +16 -0
- package/dist/lib/csg-utils.d.ts.map +1 -0
- package/dist/lib/csg-utils.js +33 -0
- package/dist/lib/drawable-geometry.d.ts +21 -0
- package/dist/lib/drawable-geometry.d.ts.map +1 -0
- package/dist/lib/drawable-geometry.js +22 -0
- package/dist/lib/edge-style.d.ts +3 -0
- package/dist/lib/edge-style.d.ts.map +1 -0
- package/dist/lib/edge-style.js +11 -0
- package/dist/lib/gpu-perf.d.ts +4 -0
- package/dist/lib/gpu-perf.d.ts.map +1 -0
- package/dist/lib/gpu-perf.js +23 -0
- package/dist/lib/ink-edges.d.ts +9 -0
- package/dist/lib/ink-edges.d.ts.map +1 -0
- package/dist/lib/ink-edges.js +43 -0
- package/dist/lib/isolation.d.ts +39 -0
- package/dist/lib/isolation.d.ts.map +1 -0
- package/dist/lib/isolation.js +103 -0
- package/dist/lib/layers.d.ts +18 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -0
- package/dist/lib/materials.d.ts +25 -15
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +277 -63
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +11 -11
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +289 -0
- package/dist/lib/polygon-union.test.d.ts +2 -0
- package/dist/lib/polygon-union.test.d.ts.map +1 -0
- package/dist/lib/polygon-union.test.js +105 -0
- package/dist/lib/scene-themes.d.ts +29 -0
- package/dist/lib/scene-themes.d.ts.map +1 -0
- package/dist/lib/scene-themes.js +193 -0
- package/dist/store/use-viewer.d.ts +42 -4
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +26 -3
- package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +62 -12
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +373 -22
- package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-interaction-system.js +62 -0
- package/dist/systems/fence/fence-system.d.ts +3 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +10 -6
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
- package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
- package/dist/systems/geometry/geometry-system.d.ts +34 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.js +279 -0
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +1 -26
- package/dist/systems/interactive/interactive-system.d.ts +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.d.ts +7 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +8 -11
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +18 -5
- package/dist/systems/roof/roof-materials.d.ts +3 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +38 -14
- package/dist/systems/roof/roof-system.d.ts +27 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +411 -71
- package/dist/systems/scan/scan-system.d.ts.map +1 -1
- package/dist/systems/scan/scan-system.js +1 -26
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +167 -49
- package/dist/systems/slab/slab-system.test.d.ts +2 -0
- package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.test.js +62 -0
- package/dist/systems/stair/stair-materials.d.ts +4 -3
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +30 -15
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +58 -71
- package/dist/systems/surface-hole-geometry.d.ts +3 -0
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
- package/dist/systems/surface-hole-geometry.js +4 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +18 -2
- package/dist/systems/wall/wall-materials.d.ts +4 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +52 -28
- package/dist/systems/wall/wall-system.d.ts +1 -1
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +179 -31
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -7
- package/package.json +4 -5
- package/dist/components/renderers/building/building-renderer.d.ts +0 -5
- package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
- package/dist/components/renderers/building/building-renderer.js +0 -11
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
- package/dist/components/renderers/column/column-renderer.d.ts +0 -5
- package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
- package/dist/components/renderers/column/column-renderer.js +0 -594
- package/dist/components/renderers/door/door-renderer.d.ts +0 -5
- package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
- package/dist/components/renderers/door/door-renderer.js +0 -16
- package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
- package/dist/components/renderers/fence/fence-renderer.js +0 -29
- package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
- package/dist/components/renderers/guide/guide-renderer.js +0 -38
- package/dist/components/renderers/item/item-renderer.d.ts +0 -5
- package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
- package/dist/components/renderers/item/item-renderer.js +0 -172
- package/dist/components/renderers/level/level-renderer.d.ts +0 -5
- package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
- package/dist/components/renderers/level/level-renderer.js +0 -11
- package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
- package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-materials.js +0 -16
- package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-renderer.js +0 -32
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
- package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
- package/dist/components/renderers/scan/scan-renderer.js +0 -54
- package/dist/components/renderers/site/site-renderer.d.ts +0 -5
- package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
- package/dist/components/renderers/site/site-renderer.js +0 -106
- package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
- package/dist/components/renderers/slab/slab-renderer.js +0 -58
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
- package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
- package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair/stair-renderer.js +0 -603
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -32
- package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
- package/dist/components/renderers/wall/wall-renderer.js +0 -16
- package/dist/components/renderers/window/window-renderer.d.ts +0 -5
- package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
- package/dist/components/renderers/window/window-renderer.js +0 -21
- package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
- package/dist/components/renderers/zone/zone-renderer.js +0 -181
- package/dist/lib/suppress-three-clock-warning.d.ts +0 -25
- package/dist/lib/suppress-three-clock-warning.d.ts.map +0 -1
- package/dist/lib/suppress-three-clock-warning.js +0 -51
- package/dist/systems/export/export-system.d.ts +0 -2
- package/dist/systems/export/export-system.d.ts.map +0 -1
- package/dist/systems/export/export-system.js +0 -60
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +0 -14
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +0 -155
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +0 -6
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +0 -75
- package/dist/systems/item-mesh-metadata/sync-request.d.ts +0 -7
- package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/sync-request.js +0 -25
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const EPSILON = 1e-7;
|
|
2
|
+
const KEY_SCALE = 1e6;
|
|
3
|
+
function pointsEqual(a, b, tolerance = EPSILON) {
|
|
4
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]) <= tolerance;
|
|
5
|
+
}
|
|
6
|
+
function pointKey(point) {
|
|
7
|
+
return `${Math.round(point[0] * KEY_SCALE)}:${Math.round(point[1] * KEY_SCALE)}`;
|
|
8
|
+
}
|
|
9
|
+
function interpolate(a, b, t) {
|
|
10
|
+
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
11
|
+
}
|
|
12
|
+
function cross(ax, ay, bx, by) {
|
|
13
|
+
return ax * by - ay * bx;
|
|
14
|
+
}
|
|
15
|
+
function polygonArea(points) {
|
|
16
|
+
let area = 0;
|
|
17
|
+
for (let i = 0; i < points.length; i++) {
|
|
18
|
+
const current = points[i];
|
|
19
|
+
const next = points[(i + 1) % points.length];
|
|
20
|
+
area += current[0] * next[1] - next[0] * current[1];
|
|
21
|
+
}
|
|
22
|
+
return area / 2;
|
|
23
|
+
}
|
|
24
|
+
function pointOnSegment(point, start, end) {
|
|
25
|
+
const dx = end[0] - start[0];
|
|
26
|
+
const dz = end[1] - start[1];
|
|
27
|
+
const crossValue = cross(point[0] - start[0], point[1] - start[1], dx, dz);
|
|
28
|
+
if (Math.abs(crossValue) > EPSILON)
|
|
29
|
+
return false;
|
|
30
|
+
const dot = (point[0] - start[0]) * (point[0] - end[0]) + (point[1] - start[1]) * (point[1] - end[1]);
|
|
31
|
+
return dot <= EPSILON;
|
|
32
|
+
}
|
|
33
|
+
function pointInPolygonOrOnBoundary(point, polygon) {
|
|
34
|
+
if (polygon.some((start, index) => pointOnSegment(point, start, polygon[(index + 1) % polygon.length]))) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return pointInPolygon(point, polygon);
|
|
38
|
+
}
|
|
39
|
+
function pointInPolygon(point, polygon) {
|
|
40
|
+
let inside = false;
|
|
41
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
42
|
+
const pi = polygon[i];
|
|
43
|
+
const pj = polygon[j];
|
|
44
|
+
if (pointOnSegment(point, pj, pi))
|
|
45
|
+
return false;
|
|
46
|
+
const intersects = pi[1] > point[1] !== pj[1] > point[1] &&
|
|
47
|
+
point[0] < ((pj[0] - pi[0]) * (point[1] - pi[1])) / (pj[1] - pi[1]) + pi[0];
|
|
48
|
+
if (intersects)
|
|
49
|
+
inside = !inside;
|
|
50
|
+
}
|
|
51
|
+
return inside;
|
|
52
|
+
}
|
|
53
|
+
function normalizeRing(ring) {
|
|
54
|
+
const normalized = [];
|
|
55
|
+
for (const [x, z] of ring) {
|
|
56
|
+
if (!Number.isFinite(x) || !Number.isFinite(z))
|
|
57
|
+
continue;
|
|
58
|
+
const point = [x, z];
|
|
59
|
+
const previous = normalized[normalized.length - 1];
|
|
60
|
+
if (!previous || !pointsEqual(previous, point)) {
|
|
61
|
+
normalized.push(point);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const first = normalized[0];
|
|
65
|
+
const last = normalized[normalized.length - 1];
|
|
66
|
+
if (first && last && pointsEqual(first, last)) {
|
|
67
|
+
normalized.pop();
|
|
68
|
+
}
|
|
69
|
+
if (normalized.length < 3 || Math.abs(polygonArea(normalized)) <= EPSILON)
|
|
70
|
+
return [];
|
|
71
|
+
return polygonArea(normalized) < 0 ? [...normalized].reverse() : normalized;
|
|
72
|
+
}
|
|
73
|
+
function addSplit(edge, t) {
|
|
74
|
+
if (t < -EPSILON || t > 1 + EPSILON)
|
|
75
|
+
return;
|
|
76
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
77
|
+
if (edge.splits.some((split) => Math.abs(split - clamped) <= EPSILON))
|
|
78
|
+
return;
|
|
79
|
+
edge.splits.push(clamped);
|
|
80
|
+
}
|
|
81
|
+
function parameterOnEdge(point, edge) {
|
|
82
|
+
const dx = edge.end[0] - edge.start[0];
|
|
83
|
+
const dz = edge.end[1] - edge.start[1];
|
|
84
|
+
const lengthSquared = dx * dx + dz * dz;
|
|
85
|
+
if (lengthSquared <= EPSILON)
|
|
86
|
+
return 0;
|
|
87
|
+
return ((point[0] - edge.start[0]) * dx + (point[1] - edge.start[1]) * dz) / lengthSquared;
|
|
88
|
+
}
|
|
89
|
+
function addIntersectionSplits(left, right) {
|
|
90
|
+
const rx = left.end[0] - left.start[0];
|
|
91
|
+
const rz = left.end[1] - left.start[1];
|
|
92
|
+
const sx = right.end[0] - right.start[0];
|
|
93
|
+
const sz = right.end[1] - right.start[1];
|
|
94
|
+
const qpx = right.start[0] - left.start[0];
|
|
95
|
+
const qpz = right.start[1] - left.start[1];
|
|
96
|
+
const denominator = cross(rx, rz, sx, sz);
|
|
97
|
+
const numerator = cross(qpx, qpz, rx, rz);
|
|
98
|
+
if (Math.abs(denominator) <= EPSILON) {
|
|
99
|
+
if (Math.abs(numerator) > EPSILON)
|
|
100
|
+
return;
|
|
101
|
+
for (const point of [left.start, left.end, right.start, right.end]) {
|
|
102
|
+
if (pointOnSegment(point, left.start, left.end) &&
|
|
103
|
+
pointOnSegment(point, right.start, right.end)) {
|
|
104
|
+
addSplit(left, parameterOnEdge(point, left));
|
|
105
|
+
addSplit(right, parameterOnEdge(point, right));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const t = cross(qpx, qpz, sx, sz) / denominator;
|
|
111
|
+
const u = cross(qpx, qpz, rx, rz) / denominator;
|
|
112
|
+
if (t < -EPSILON || t > 1 + EPSILON || u < -EPSILON || u > 1 + EPSILON)
|
|
113
|
+
return;
|
|
114
|
+
addSplit(left, t);
|
|
115
|
+
addSplit(right, u);
|
|
116
|
+
}
|
|
117
|
+
function buildEdges(polygons) {
|
|
118
|
+
const edges = [];
|
|
119
|
+
polygons.forEach((polygon, polygonIndex) => {
|
|
120
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
121
|
+
edges.push({
|
|
122
|
+
start: polygon[i],
|
|
123
|
+
end: polygon[(i + 1) % polygon.length],
|
|
124
|
+
polygonIndex,
|
|
125
|
+
splits: [0, 1],
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
for (let i = 0; i < edges.length; i++) {
|
|
130
|
+
for (let j = i + 1; j < edges.length; j++) {
|
|
131
|
+
const left = edges[i];
|
|
132
|
+
const right = edges[j];
|
|
133
|
+
if (left.polygonIndex === right.polygonIndex)
|
|
134
|
+
continue;
|
|
135
|
+
addIntersectionSplits(left, right);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return edges;
|
|
139
|
+
}
|
|
140
|
+
function buildBoundarySegments(edges, polygons) {
|
|
141
|
+
const segments = [];
|
|
142
|
+
for (const edge of edges) {
|
|
143
|
+
const splits = [...edge.splits].sort((a, b) => a - b);
|
|
144
|
+
for (let i = 0; i < splits.length - 1; i++) {
|
|
145
|
+
const startT = splits[i];
|
|
146
|
+
const endT = splits[i + 1];
|
|
147
|
+
if (endT - startT <= EPSILON)
|
|
148
|
+
continue;
|
|
149
|
+
const start = interpolate(edge.start, edge.end, startT);
|
|
150
|
+
const end = interpolate(edge.start, edge.end, endT);
|
|
151
|
+
const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
|
|
152
|
+
const insideAnother = polygons.some((polygon, index) => index !== edge.polygonIndex && pointInPolygon(mid, polygon));
|
|
153
|
+
if (!insideAnother) {
|
|
154
|
+
segments.push({ start, end, used: false });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return removeDuplicateInteriorSegments(segments);
|
|
159
|
+
}
|
|
160
|
+
function segmentKey(segment) {
|
|
161
|
+
const start = pointKey(segment.start);
|
|
162
|
+
const end = pointKey(segment.end);
|
|
163
|
+
return start < end ? `${start}|${end}` : `${end}|${start}`;
|
|
164
|
+
}
|
|
165
|
+
function removeDuplicateInteriorSegments(segments) {
|
|
166
|
+
const groups = new Map();
|
|
167
|
+
for (const segment of segments) {
|
|
168
|
+
const key = segmentKey(segment);
|
|
169
|
+
const group = groups.get(key);
|
|
170
|
+
if (group) {
|
|
171
|
+
group.push(segment);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
groups.set(key, [segment]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const result = [];
|
|
178
|
+
for (const group of groups.values()) {
|
|
179
|
+
if (group.length === 1) {
|
|
180
|
+
result.push(group[0]);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const firstStart = pointKey(group[0].start);
|
|
184
|
+
const firstEnd = pointKey(group[0].end);
|
|
185
|
+
const hasOppositeDirection = group.some((segment) => pointKey(segment.start) === firstEnd && pointKey(segment.end) === firstStart);
|
|
186
|
+
if (!hasOppositeDirection) {
|
|
187
|
+
result.push(group[0]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
function assembleRings(segments) {
|
|
193
|
+
const byStart = new Map();
|
|
194
|
+
for (const segment of segments) {
|
|
195
|
+
const key = pointKey(segment.start);
|
|
196
|
+
const group = byStart.get(key);
|
|
197
|
+
if (group) {
|
|
198
|
+
group.push(segment);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
byStart.set(key, [segment]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const rings = [];
|
|
205
|
+
for (const firstSegment of segments) {
|
|
206
|
+
if (firstSegment.used)
|
|
207
|
+
continue;
|
|
208
|
+
firstSegment.used = true;
|
|
209
|
+
const ring = [firstSegment.start, firstSegment.end];
|
|
210
|
+
const startKey = pointKey(firstSegment.start);
|
|
211
|
+
let currentKey = pointKey(firstSegment.end);
|
|
212
|
+
while (currentKey !== startKey) {
|
|
213
|
+
const next = byStart.get(currentKey)?.find((segment) => !segment.used);
|
|
214
|
+
if (!next)
|
|
215
|
+
break;
|
|
216
|
+
next.used = true;
|
|
217
|
+
ring.push(next.end);
|
|
218
|
+
currentKey = pointKey(next.end);
|
|
219
|
+
}
|
|
220
|
+
if (currentKey !== startKey)
|
|
221
|
+
continue;
|
|
222
|
+
const last = ring[ring.length - 1];
|
|
223
|
+
if (last && pointsEqual(ring[0], last)) {
|
|
224
|
+
ring.pop();
|
|
225
|
+
}
|
|
226
|
+
const normalized = normalizeRing(ring);
|
|
227
|
+
if (normalized.length >= 3) {
|
|
228
|
+
rings.push(normalized);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return rings;
|
|
232
|
+
}
|
|
233
|
+
export function unionPolygons(polygons) {
|
|
234
|
+
const validPolygons = polygons.map(normalizeRing).filter((polygon) => polygon.length >= 3);
|
|
235
|
+
if (validPolygons.length <= 1)
|
|
236
|
+
return validPolygons;
|
|
237
|
+
const edges = buildEdges(validPolygons);
|
|
238
|
+
const segments = buildBoundarySegments(edges, validPolygons);
|
|
239
|
+
const rings = assembleRings(segments);
|
|
240
|
+
return rings.length > 0 ? rings : validPolygons;
|
|
241
|
+
}
|
|
242
|
+
function buildDifferenceBoundarySegments(edges, polygons) {
|
|
243
|
+
const subject = polygons[0];
|
|
244
|
+
const cutters = polygons.slice(1);
|
|
245
|
+
if (!subject)
|
|
246
|
+
return [];
|
|
247
|
+
const segments = [];
|
|
248
|
+
for (const edge of edges) {
|
|
249
|
+
const splits = [...edge.splits].sort((a, b) => a - b);
|
|
250
|
+
for (let i = 0; i < splits.length - 1; i++) {
|
|
251
|
+
const startT = splits[i];
|
|
252
|
+
const endT = splits[i + 1];
|
|
253
|
+
if (endT - startT <= EPSILON)
|
|
254
|
+
continue;
|
|
255
|
+
const start = interpolate(edge.start, edge.end, startT);
|
|
256
|
+
const end = interpolate(edge.start, edge.end, endT);
|
|
257
|
+
const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
|
|
258
|
+
if (edge.polygonIndex === 0) {
|
|
259
|
+
const insideCutter = cutters.some((cutter) => pointInPolygonOrOnBoundary(mid, cutter));
|
|
260
|
+
if (!insideCutter) {
|
|
261
|
+
segments.push({ start, end, used: false });
|
|
262
|
+
}
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const insideSubject = pointInPolygon(mid, subject);
|
|
266
|
+
const insideAnotherCutter = cutters.some((cutter, cutterIndex) => cutterIndex !== edge.polygonIndex - 1 && pointInPolygonOrOnBoundary(mid, cutter));
|
|
267
|
+
if (insideSubject && !insideAnotherCutter) {
|
|
268
|
+
segments.push({ start: end, end: start, used: false });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return removeDuplicateInteriorSegments(segments);
|
|
273
|
+
}
|
|
274
|
+
export function subtractPolygonsFromPolygon(subject, cutters) {
|
|
275
|
+
const validSubject = normalizeRing(subject);
|
|
276
|
+
if (validSubject.length < 3)
|
|
277
|
+
return [];
|
|
278
|
+
const validCutters = cutters.map(normalizeRing).filter((polygon) => polygon.length >= 3);
|
|
279
|
+
if (validCutters.length === 0)
|
|
280
|
+
return [validSubject];
|
|
281
|
+
const polygons = [validSubject, ...validCutters];
|
|
282
|
+
const edges = buildEdges(polygons);
|
|
283
|
+
const segments = buildDifferenceBoundarySegments(edges, polygons);
|
|
284
|
+
const rings = assembleRings(segments);
|
|
285
|
+
if (rings.length > 0)
|
|
286
|
+
return rings;
|
|
287
|
+
const fullyCovered = validSubject.every((point) => validCutters.some((cutter) => pointInPolygonOrOnBoundary(point, cutter)));
|
|
288
|
+
return fullyCovered ? [] : [validSubject];
|
|
289
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polygon-union.test.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-union.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
+
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
+
import { describe, expect, test } from 'bun:test';
|
|
4
|
+
import { subtractPolygonsFromPolygon, unionPolygons } from './polygon-union';
|
|
5
|
+
function polygonArea(points) {
|
|
6
|
+
let area = 0;
|
|
7
|
+
for (let i = 0; i < points.length; i++) {
|
|
8
|
+
const current = points[i];
|
|
9
|
+
const next = points[(i + 1) % points.length];
|
|
10
|
+
area += current[0] * next[1] - next[0] * current[1];
|
|
11
|
+
}
|
|
12
|
+
return Math.abs(area / 2);
|
|
13
|
+
}
|
|
14
|
+
describe('unionPolygons', () => {
|
|
15
|
+
test('collapses a contained polygon into the containing polygon', () => {
|
|
16
|
+
const small = [
|
|
17
|
+
[0, 0],
|
|
18
|
+
[1, 0],
|
|
19
|
+
[1, 1],
|
|
20
|
+
[0, 1],
|
|
21
|
+
];
|
|
22
|
+
const large = [
|
|
23
|
+
[-1, -1],
|
|
24
|
+
[2, -1],
|
|
25
|
+
[2, 2],
|
|
26
|
+
[-1, 2],
|
|
27
|
+
];
|
|
28
|
+
const result = unionPolygons([small, large]);
|
|
29
|
+
expect(result).toHaveLength(1);
|
|
30
|
+
expect(polygonArea(result[0])).toBeCloseTo(9);
|
|
31
|
+
});
|
|
32
|
+
test('combines overlapping rectangles into one boundary', () => {
|
|
33
|
+
const left = [
|
|
34
|
+
[0, 0],
|
|
35
|
+
[2, 0],
|
|
36
|
+
[2, 2],
|
|
37
|
+
[0, 2],
|
|
38
|
+
];
|
|
39
|
+
const right = [
|
|
40
|
+
[1, 1],
|
|
41
|
+
[3, 1],
|
|
42
|
+
[3, 3],
|
|
43
|
+
[1, 3],
|
|
44
|
+
];
|
|
45
|
+
const result = unionPolygons([left, right]);
|
|
46
|
+
expect(result).toHaveLength(1);
|
|
47
|
+
expect(result[0]).toHaveLength(8);
|
|
48
|
+
expect(polygonArea(result[0])).toBeCloseTo(7);
|
|
49
|
+
});
|
|
50
|
+
test('keeps disjoint polygons as separate boundaries', () => {
|
|
51
|
+
const left = [
|
|
52
|
+
[0, 0],
|
|
53
|
+
[1, 0],
|
|
54
|
+
[1, 1],
|
|
55
|
+
[0, 1],
|
|
56
|
+
];
|
|
57
|
+
const right = [
|
|
58
|
+
[2, 0],
|
|
59
|
+
[3, 0],
|
|
60
|
+
[3, 1],
|
|
61
|
+
[2, 1],
|
|
62
|
+
];
|
|
63
|
+
const result = unionPolygons([left, right]);
|
|
64
|
+
expect(result).toHaveLength(2);
|
|
65
|
+
expect(result.map(polygonArea)).toEqual([1, 1]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('subtractPolygonsFromPolygon', () => {
|
|
69
|
+
test('turns a boundary-overlapping cutter into an indentation', () => {
|
|
70
|
+
const slab = [
|
|
71
|
+
[0, 0],
|
|
72
|
+
[4, 0],
|
|
73
|
+
[4, 3],
|
|
74
|
+
[0, 3],
|
|
75
|
+
];
|
|
76
|
+
const cutout = [
|
|
77
|
+
[1, -0.5],
|
|
78
|
+
[3, -0.5],
|
|
79
|
+
[3, 1],
|
|
80
|
+
[1, 1],
|
|
81
|
+
];
|
|
82
|
+
const result = subtractPolygonsFromPolygon(slab, [cutout]);
|
|
83
|
+
expect(result).toHaveLength(1);
|
|
84
|
+
expect(result[0]).toContainEqual([1, 1]);
|
|
85
|
+
expect(result[0]).toContainEqual([3, 1]);
|
|
86
|
+
expect(polygonArea(result[0])).toBeCloseTo(10);
|
|
87
|
+
});
|
|
88
|
+
test('returns separate contours when a cutter splits the subject', () => {
|
|
89
|
+
const slab = [
|
|
90
|
+
[0, 0],
|
|
91
|
+
[4, 0],
|
|
92
|
+
[4, 3],
|
|
93
|
+
[0, 3],
|
|
94
|
+
];
|
|
95
|
+
const cutout = [
|
|
96
|
+
[1.5, -1],
|
|
97
|
+
[2.5, -1],
|
|
98
|
+
[2.5, 4],
|
|
99
|
+
[1.5, 4],
|
|
100
|
+
];
|
|
101
|
+
const result = subtractPolygonsFromPolygon(slab, [cutout]);
|
|
102
|
+
expect(result).toHaveLength(2);
|
|
103
|
+
expect(result.map(polygonArea).sort((a, b) => a - b)).toEqual([4.5, 4.5]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SurfaceRole } from '@pascal-app/core';
|
|
2
|
+
export type SceneTheme = {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
appearance: 'light' | 'dark';
|
|
6
|
+
background: string;
|
|
7
|
+
ground: string;
|
|
8
|
+
ambient: {
|
|
9
|
+
color: string;
|
|
10
|
+
intensity: number;
|
|
11
|
+
};
|
|
12
|
+
hemi?: {
|
|
13
|
+
sky: string;
|
|
14
|
+
ground: string;
|
|
15
|
+
intensity: number;
|
|
16
|
+
};
|
|
17
|
+
lights: Array<{
|
|
18
|
+
position: [number, number, number];
|
|
19
|
+
color: string;
|
|
20
|
+
intensity: number;
|
|
21
|
+
castShadow?: boolean;
|
|
22
|
+
}>;
|
|
23
|
+
toneMappingExposure: number;
|
|
24
|
+
clayTints?: Partial<Record<SurfaceRole, string>>;
|
|
25
|
+
};
|
|
26
|
+
export declare const SCENE_THEMES: SceneTheme[];
|
|
27
|
+
export declare const SCENE_THEME_IDS: string[];
|
|
28
|
+
export declare function getSceneTheme(id: string): SceneTheme;
|
|
29
|
+
//# sourceMappingURL=scene-themes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-themes.d.ts","sourceRoot":"","sources":["../../src/lib/scene-themes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IAIZ,UAAU,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAIlB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAClC,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAC,CAAA;IACF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;CACjD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,UAAU,EA2LpC,CAAA;AAED,eAAO,MAAM,eAAe,UAAwC,CAAA;AAIpE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,CAEpD"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
export const SCENE_THEMES = [
|
|
2
|
+
{
|
|
3
|
+
id: 'studio',
|
|
4
|
+
name: 'Studio',
|
|
5
|
+
appearance: 'light',
|
|
6
|
+
background: '#ffffff',
|
|
7
|
+
ground: '#f4f4f2',
|
|
8
|
+
ambient: { color: '#ffffff', intensity: 0.15 },
|
|
9
|
+
hemi: { sky: '#ffffff', ground: '#aaa49a', intensity: 0.6 },
|
|
10
|
+
lights: [
|
|
11
|
+
{ position: [10, 10, 10], color: '#ffffff', intensity: 4, castShadow: true },
|
|
12
|
+
{ position: [-10, 10, -10], color: '#ffffff', intensity: 0.75 },
|
|
13
|
+
],
|
|
14
|
+
toneMappingExposure: 0.9,
|
|
15
|
+
clayTints: {
|
|
16
|
+
wall: '#e9e5db',
|
|
17
|
+
floor: '#d8d2c4',
|
|
18
|
+
ceiling: '#f1ede4',
|
|
19
|
+
roof: '#c4bba6',
|
|
20
|
+
glazing: '#cdd8df',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'paper',
|
|
25
|
+
name: 'Paper',
|
|
26
|
+
appearance: 'light',
|
|
27
|
+
background: '#ede9df',
|
|
28
|
+
ground: '#e7e1d3',
|
|
29
|
+
ambient: { color: '#fff9eb', intensity: 0.55 },
|
|
30
|
+
hemi: { sky: '#fff5d9', ground: '#c2b89c', intensity: 0.35 },
|
|
31
|
+
lights: [
|
|
32
|
+
{ position: [16, 22, 12], color: '#fff1c8', intensity: 2.6, castShadow: true },
|
|
33
|
+
{ position: [-14, 10, -6], color: '#dde5ff', intensity: 0.35 },
|
|
34
|
+
],
|
|
35
|
+
toneMappingExposure: 1,
|
|
36
|
+
clayTints: {
|
|
37
|
+
wall: '#efe9da',
|
|
38
|
+
floor: '#ddd4bf',
|
|
39
|
+
ceiling: '#f5efe0',
|
|
40
|
+
roof: '#b9b09a',
|
|
41
|
+
glazing: '#cdd5d8',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'sunset',
|
|
46
|
+
name: 'Sunset',
|
|
47
|
+
appearance: 'light',
|
|
48
|
+
background: '#f6e8d4',
|
|
49
|
+
ground: '#ecd9bf',
|
|
50
|
+
ambient: { color: '#ffd9a8', intensity: 0.45 },
|
|
51
|
+
hemi: { sky: '#ffd9a8', ground: '#5b4634', intensity: 0.4 },
|
|
52
|
+
lights: [
|
|
53
|
+
{ position: [22, 8, 8], color: '#ffb070', intensity: 3.4, castShadow: true },
|
|
54
|
+
{ position: [-14, 16, -10], color: '#a4b8ff', intensity: 0.4 },
|
|
55
|
+
],
|
|
56
|
+
toneMappingExposure: 1,
|
|
57
|
+
clayTints: {
|
|
58
|
+
wall: '#f3e3cf',
|
|
59
|
+
floor: '#e2cdab',
|
|
60
|
+
ceiling: '#f6e7d2',
|
|
61
|
+
roof: '#a6764f',
|
|
62
|
+
glazing: '#e7c9a8',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'overcast',
|
|
67
|
+
name: 'Overcast',
|
|
68
|
+
appearance: 'light',
|
|
69
|
+
background: '#e6e7e6',
|
|
70
|
+
ground: '#dadcd9',
|
|
71
|
+
ambient: { color: '#eef0ef', intensity: 1.1 },
|
|
72
|
+
hemi: { sky: '#f4f5f3', ground: '#bcbfbb', intensity: 0.9 },
|
|
73
|
+
lights: [{ position: [12, 28, 10], color: '#f4f5f3', intensity: 0.8, castShadow: true }],
|
|
74
|
+
toneMappingExposure: 0.95,
|
|
75
|
+
clayTints: {
|
|
76
|
+
wall: '#dedfdc',
|
|
77
|
+
floor: '#cdcec9',
|
|
78
|
+
ceiling: '#e8e9e6',
|
|
79
|
+
roof: '#a3a49e',
|
|
80
|
+
glazing: '#c6cdd0',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'blueprint',
|
|
85
|
+
name: 'Blueprint',
|
|
86
|
+
appearance: 'light',
|
|
87
|
+
background: '#dde6ef',
|
|
88
|
+
ground: '#c9d6e6',
|
|
89
|
+
ambient: { color: '#cfdcec', intensity: 0.7 },
|
|
90
|
+
hemi: { sky: '#dfeaf6', ground: '#5b6b80', intensity: 0.55 },
|
|
91
|
+
lights: [
|
|
92
|
+
{ position: [16, 24, 12], color: '#e6efff', intensity: 1.8, castShadow: true },
|
|
93
|
+
{ position: [-12, 10, -8], color: '#9fb6d8', intensity: 0.4 },
|
|
94
|
+
],
|
|
95
|
+
toneMappingExposure: 0.95,
|
|
96
|
+
clayTints: {
|
|
97
|
+
wall: '#9fb6d2',
|
|
98
|
+
floor: '#8ba2c2',
|
|
99
|
+
ceiling: '#aec0d8',
|
|
100
|
+
roof: '#5f789b',
|
|
101
|
+
glazing: '#b6d7ea',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: 'mediterranean',
|
|
106
|
+
name: 'Mediterranean',
|
|
107
|
+
appearance: 'light',
|
|
108
|
+
background: '#bdd6e8',
|
|
109
|
+
ground: '#ddd2bb',
|
|
110
|
+
ambient: { color: '#d6e6f3', intensity: 0.5 },
|
|
111
|
+
hemi: { sky: '#a8c8e2', ground: '#d8c9a4', intensity: 0.6 },
|
|
112
|
+
lights: [
|
|
113
|
+
{ position: [18, 20, 12], color: '#fff4d4', intensity: 3.6, castShadow: true },
|
|
114
|
+
{ position: [-12, 8, -8], color: '#8fb3d8', intensity: 0.7 },
|
|
115
|
+
],
|
|
116
|
+
toneMappingExposure: 0.9,
|
|
117
|
+
clayTints: {
|
|
118
|
+
wall: '#f6f1e6',
|
|
119
|
+
floor: '#e0d6c2',
|
|
120
|
+
ceiling: '#f3ede0',
|
|
121
|
+
roof: '#3e6585',
|
|
122
|
+
glazing: '#bcd3e2',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'twilight',
|
|
127
|
+
name: 'Twilight',
|
|
128
|
+
appearance: 'dark',
|
|
129
|
+
background: '#3a3550',
|
|
130
|
+
ground: '#4a4566',
|
|
131
|
+
ambient: { color: '#a89cc8', intensity: 0.35 },
|
|
132
|
+
hemi: { sky: '#d8a8c0', ground: '#1c1830', intensity: 0.5 },
|
|
133
|
+
lights: [
|
|
134
|
+
{ position: [-14, 22, -10], color: '#a4b6e8', intensity: 1.4, castShadow: true },
|
|
135
|
+
{ position: [14, 6, 8], color: '#ffb070', intensity: 0.9 },
|
|
136
|
+
],
|
|
137
|
+
toneMappingExposure: 1.1,
|
|
138
|
+
clayTints: {
|
|
139
|
+
wall: '#c5b9cf',
|
|
140
|
+
floor: '#ad9fbb',
|
|
141
|
+
ceiling: '#d2c6dc',
|
|
142
|
+
roof: '#5b4f74',
|
|
143
|
+
glazing: '#c3b6d4',
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'night',
|
|
148
|
+
name: 'Night',
|
|
149
|
+
appearance: 'dark',
|
|
150
|
+
background: '#1f2433',
|
|
151
|
+
ground: '#2b3247',
|
|
152
|
+
ambient: { color: '#a0b0ff', intensity: 0.07 },
|
|
153
|
+
hemi: { sky: '#3a4666', ground: '#0e111c', intensity: 0.4 },
|
|
154
|
+
lights: [
|
|
155
|
+
{ position: [10, 10, 10], color: '#e0e5ff', intensity: 0.8, castShadow: true },
|
|
156
|
+
{ position: [-10, 10, -10], color: '#8090ff', intensity: 0.2 },
|
|
157
|
+
],
|
|
158
|
+
toneMappingExposure: 0.9,
|
|
159
|
+
clayTints: {
|
|
160
|
+
wall: '#aab3c6',
|
|
161
|
+
floor: '#98a1b5',
|
|
162
|
+
ceiling: '#b7bfd0',
|
|
163
|
+
roof: '#5b6680',
|
|
164
|
+
glazing: '#aebbd0',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'verdant',
|
|
169
|
+
name: 'Verdant',
|
|
170
|
+
appearance: 'light',
|
|
171
|
+
background: '#d6e4d2',
|
|
172
|
+
ground: '#c7d6b4',
|
|
173
|
+
ambient: { color: '#e3efdd', intensity: 0.5 },
|
|
174
|
+
hemi: { sky: '#cfe6cf', ground: '#8ea06f', intensity: 0.65 },
|
|
175
|
+
lights: [
|
|
176
|
+
{ position: [16, 22, 12], color: '#fff6d8', intensity: 3, castShadow: true },
|
|
177
|
+
{ position: [-12, 10, -8], color: '#bfe0c2', intensity: 0.5 },
|
|
178
|
+
],
|
|
179
|
+
toneMappingExposure: 0.95,
|
|
180
|
+
clayTints: {
|
|
181
|
+
wall: '#eef0e6',
|
|
182
|
+
floor: '#d8ddc6',
|
|
183
|
+
ceiling: '#f1f3ea',
|
|
184
|
+
roof: '#6f8a5a',
|
|
185
|
+
glazing: '#c4dcd0',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
export const SCENE_THEME_IDS = SCENE_THEMES.map((theme) => theme.id);
|
|
190
|
+
const SCENE_THEME_BY_ID = new Map(SCENE_THEMES.map((theme) => [theme.id, theme]));
|
|
191
|
+
export function getSceneTheme(id) {
|
|
192
|
+
return SCENE_THEME_BY_ID.get(id) ?? SCENE_THEMES[0];
|
|
193
|
+
}
|