@layoutit/polycss-react 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.cjs +2312 -475
- package/dist/index.d.cts +159 -18
- package/dist/index.d.ts +159 -18
- package/dist/index.js +2284 -440
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -20,17 +20,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
BASE_TILE: () =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
BASE_TILE: () => import_polycss_core14.BASE_TILE,
|
|
24
|
+
CAMERA_BACKFACE_CULL_EPS: () => import_polycss_core14.CAMERA_BACKFACE_CULL_EPS,
|
|
25
|
+
DEFAULT_CAMERA_STATE: () => import_polycss_core14.DEFAULT_CAMERA_STATE,
|
|
26
|
+
DEFAULT_PROJECTION: () => import_polycss_core14.DEFAULT_PROJECTION,
|
|
27
|
+
LoopOnce: () => import_polycss_core14.LoopOnce,
|
|
28
|
+
LoopPingPong: () => import_polycss_core14.LoopPingPong,
|
|
29
|
+
LoopRepeat: () => import_polycss_core14.LoopRepeat,
|
|
29
30
|
Poly: () => Poly,
|
|
30
31
|
PolyAxesHelper: () => PolyAxesHelper,
|
|
31
32
|
PolyCamera: () => PolyPerspectiveCamera,
|
|
32
33
|
PolyCameraContext: () => PolyCameraContext,
|
|
33
34
|
PolyDirectionalLightHelper: () => PolyDirectionalLightHelper,
|
|
35
|
+
PolyFirstPersonControls: () => PolyFirstPersonControls,
|
|
36
|
+
PolyGround: () => PolyGround,
|
|
34
37
|
PolyMapControls: () => PolyMapControls,
|
|
35
38
|
PolyMesh: () => PolyMesh,
|
|
36
39
|
PolyOrbitControls: () => PolyOrbitControls,
|
|
@@ -39,42 +42,56 @@ __export(src_exports, {
|
|
|
39
42
|
PolyScene: () => PolyScene,
|
|
40
43
|
PolySelect: () => PolySelect,
|
|
41
44
|
PolyTransformControls: () => PolyTransformControls,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
VOXEL_CAMERA_CULL_AXIS_EPS: () => import_polycss_core14.VOXEL_CAMERA_CULL_AXIS_EPS,
|
|
46
|
+
VOXEL_CAMERA_CULL_NORMAL_LIMIT: () => import_polycss_core14.VOXEL_CAMERA_CULL_NORMAL_LIMIT,
|
|
47
|
+
arrowPolygons: () => import_polycss_core14.arrowPolygons,
|
|
48
|
+
axesHelperPolygons: () => import_polycss_core14.axesHelperPolygons,
|
|
49
|
+
bakeSolidTextureSampledPolygons: () => import_polycss_core14.bakeSolidTextureSampledPolygons,
|
|
50
|
+
bakeSolidTextureSamples: () => import_polycss_core14.bakeSolidTextureSamples,
|
|
51
|
+
boxPolygons: () => import_polycss_core14.boxPolygons,
|
|
52
|
+
buildSceneContext: () => import_polycss_core14.buildSceneContext,
|
|
53
|
+
cameraCullNormalGroups: () => import_polycss_core14.cameraCullNormalGroups,
|
|
54
|
+
cameraCullNormalGroupsFromPolygons: () => import_polycss_core14.cameraCullNormalGroupsFromPolygons,
|
|
55
|
+
cameraCullNormalKey: () => import_polycss_core14.cameraCullNormalKey,
|
|
56
|
+
cameraCullVisibleSignature: () => import_polycss_core14.cameraCullVisibleSignature,
|
|
57
|
+
cameraFacingDepth: () => import_polycss_core14.cameraFacingDepth,
|
|
58
|
+
clampChannel: () => import_polycss_core14.clampChannel,
|
|
59
|
+
computeSceneBbox: () => import_polycss_core14.computeSceneBbox,
|
|
60
|
+
computeShapeLighting: () => import_polycss_core14.computeShapeLighting,
|
|
61
|
+
computeTexturePaintMetrics: () => import_polycss_core14.computeTexturePaintMetrics,
|
|
62
|
+
coverPlanarPolygons: () => import_polycss_core14.coverPlanarPolygons,
|
|
63
|
+
createIsometricCamera: () => import_polycss_core14.createIsometricCamera,
|
|
64
|
+
createPolyAnimationMixer: () => import_polycss_core14.createPolyAnimationMixer,
|
|
65
|
+
cullInteriorPolygons: () => import_polycss_core14.cullInteriorPolygons,
|
|
55
66
|
findMeshUnderPoint: () => findMeshUnderPoint,
|
|
56
67
|
findPolyMeshHandle: () => findPolyMeshHandle,
|
|
57
|
-
formatColor: () =>
|
|
68
|
+
formatColor: () => import_polycss_core14.formatColor,
|
|
58
69
|
injectPolyBaseStyles: () => injectPolyBaseStyles,
|
|
59
|
-
inverseRotateVec3: () =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
inverseRotateVec3: () => import_polycss_core14.inverseRotateVec3,
|
|
71
|
+
isAxisAlignedSurfaceNormal: () => import_polycss_core14.isAxisAlignedSurfaceNormal,
|
|
72
|
+
isVoxelCameraCullableNormalGroups: () => import_polycss_core14.isVoxelCameraCullableNormalGroups,
|
|
73
|
+
loadMesh: () => import_polycss_core14.loadMesh,
|
|
74
|
+
mergePolygons: () => import_polycss_core14.mergePolygons,
|
|
75
|
+
normalFacesCamera: () => import_polycss_core14.normalFacesCamera,
|
|
76
|
+
normalizeInvertMultiplier: () => import_polycss_core14.normalizeInvertMultiplier,
|
|
77
|
+
normalizePolygons: () => import_polycss_core14.normalizePolygons,
|
|
78
|
+
octahedronPolygons: () => import_polycss_core14.octahedronPolygons,
|
|
79
|
+
optimizeMeshPolygons: () => import_polycss_core14.optimizeMeshPolygons,
|
|
80
|
+
parseColor: () => import_polycss_core14.parseColor,
|
|
81
|
+
parseGltf: () => import_polycss_core14.parseGltf,
|
|
82
|
+
parseHexColor: () => import_polycss_core14.parseHexColor,
|
|
83
|
+
parseMtl: () => import_polycss_core14.parseMtl,
|
|
84
|
+
parseObj: () => import_polycss_core14.parseObj,
|
|
85
|
+
parsePureColor: () => import_polycss_core14.parsePureColor,
|
|
86
|
+
parseRgbColor: () => import_polycss_core14.parseRgbColor,
|
|
87
|
+
parseVox: () => import_polycss_core14.parseVox,
|
|
73
88
|
pointInMeshElement: () => pointInMeshElement,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
polygonCssSurfaceNormal: () => import_polycss_core14.polygonCssSurfaceNormal,
|
|
90
|
+
polygonFaces: () => import_polycss_core14.polygonFaces,
|
|
91
|
+
polygonFacesCamera: () => import_polycss_core14.polygonFacesCamera,
|
|
92
|
+
ringPolygons: () => import_polycss_core14.ringPolygons,
|
|
93
|
+
rotateVec3: () => import_polycss_core14.rotateVec3,
|
|
94
|
+
shadeColor: () => import_polycss_core14.shadeColor,
|
|
78
95
|
useCameraContext: () => useCameraContext,
|
|
79
96
|
usePolyAnimation: () => usePolyAnimation,
|
|
80
97
|
usePolyCamera: () => usePolyCamera,
|
|
@@ -108,7 +125,8 @@ var import_polycss_core = require("@layoutit/polycss-core");
|
|
|
108
125
|
var import_react2 = require("react");
|
|
109
126
|
function createSceneStore(initial) {
|
|
110
127
|
let state = {
|
|
111
|
-
cameraState: { ...initial }
|
|
128
|
+
cameraState: { ...initial },
|
|
129
|
+
autoCenterOffset: [0, 0, 0]
|
|
112
130
|
};
|
|
113
131
|
const listeners = /* @__PURE__ */ new Set();
|
|
114
132
|
function notify() {
|
|
@@ -127,15 +145,24 @@ function createSceneStore(initial) {
|
|
|
127
145
|
return () => listeners.delete(listener);
|
|
128
146
|
},
|
|
129
147
|
updateCameraFromRef(handle) {
|
|
130
|
-
state = { cameraState: { ...handle.state } };
|
|
148
|
+
state = { ...state, cameraState: { ...handle.state } };
|
|
131
149
|
notify();
|
|
132
150
|
return true;
|
|
133
151
|
},
|
|
134
152
|
notifyAll() {
|
|
135
153
|
notify();
|
|
154
|
+
},
|
|
155
|
+
setAutoCenterOffset(offset) {
|
|
156
|
+
state = { ...state, autoCenterOffset: offset };
|
|
136
157
|
}
|
|
137
158
|
};
|
|
138
159
|
}
|
|
160
|
+
function useStoreSelector(store, selector) {
|
|
161
|
+
const selectorRef = (0, import_react2.useRef)(selector);
|
|
162
|
+
selectorRef.current = selector;
|
|
163
|
+
const getSnapshot = (0, import_react2.useCallback)(() => selectorRef.current(store.getState()), [store]);
|
|
164
|
+
return (0, import_react2.useSyncExternalStore)(store.subscribe, getSnapshot);
|
|
165
|
+
}
|
|
139
166
|
|
|
140
167
|
// src/camera/useCamera.ts
|
|
141
168
|
function usePolyCamera(options) {
|
|
@@ -168,11 +195,14 @@ function usePolyCamera(options) {
|
|
|
168
195
|
const el = sceneElRef.current;
|
|
169
196
|
if (el) {
|
|
170
197
|
const s = handle.state;
|
|
198
|
+
const [ox, oy, oz] = store.getState().autoCenterOffset;
|
|
171
199
|
const tileSize = import_polycss_core.BASE_TILE;
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
const
|
|
200
|
+
const wx = s.target[0] + ox;
|
|
201
|
+
const wy = s.target[1] + oy;
|
|
202
|
+
const wz = s.target[2] + oz;
|
|
203
|
+
const cssX = wy * tileSize;
|
|
204
|
+
const cssY = wx * tileSize;
|
|
205
|
+
const cssZ = wz * tileSize;
|
|
176
206
|
const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
|
|
177
207
|
el.style.transform = `${distancePart}scale(${s.zoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
|
|
178
208
|
}
|
|
@@ -185,14 +215,17 @@ function usePolyCamera(options) {
|
|
|
185
215
|
if (!el) return;
|
|
186
216
|
const handle = handleRef.current;
|
|
187
217
|
const s = handle.state;
|
|
218
|
+
const [ox, oy, oz] = store.getState().autoCenterOffset;
|
|
188
219
|
const tileSize = import_polycss_core.BASE_TILE;
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
const
|
|
220
|
+
const wx = s.target[0] + ox;
|
|
221
|
+
const wy = s.target[1] + oy;
|
|
222
|
+
const wz = s.target[2] + oz;
|
|
223
|
+
const cssX = wy * tileSize;
|
|
224
|
+
const cssY = wx * tileSize;
|
|
225
|
+
const cssZ = wz * tileSize;
|
|
193
226
|
const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
|
|
194
227
|
el.style.transform = `${distancePart}scale(${s.zoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
|
|
195
|
-
}, []);
|
|
228
|
+
}, [store]);
|
|
196
229
|
return {
|
|
197
230
|
store,
|
|
198
231
|
cameraRef: handleRef,
|
|
@@ -330,12 +363,6 @@ var CORE_BASE_STYLES = `
|
|
|
330
363
|
height: 0;
|
|
331
364
|
transform-style: preserve-3d;
|
|
332
365
|
perspective: none;
|
|
333
|
-
transform: var(--scene-transform);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.polycss-offset {
|
|
337
|
-
transform-style: preserve-3d;
|
|
338
|
-
transform: var(--offset-transform);
|
|
339
366
|
}
|
|
340
367
|
|
|
341
368
|
/* \u2500\u2500 Camera wrapper (perspective + interactive drag) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
@@ -358,6 +385,22 @@ var CORE_BASE_STYLES = `
|
|
|
358
385
|
position: absolute;
|
|
359
386
|
}
|
|
360
387
|
|
|
388
|
+
/* \u2500\u2500 First-person controls perspective context \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
389
|
+
|
|
390
|
+
/* PolyFirstPersonControls toggles this class on its host element (the camera
|
|
391
|
+
wrapper). FPV needs a real perspective context so scene Z translation
|
|
392
|
+
produces visible depth motion - without it, walking forward looks like a
|
|
393
|
+
planar pan. The class wins over inline perspective styles (e.g.
|
|
394
|
+
PolyOrthographicCamera's perspective: none) via !important. The actual
|
|
395
|
+
perspective value is set inline by the controls as the
|
|
396
|
+
--polycss-fpv-perspective custom property; the default of 2000px matches
|
|
397
|
+
the controls' lookOffset fallback so the FPV math and visual perspective
|
|
398
|
+
stay in sync. */
|
|
399
|
+
.polycss-fpv-host {
|
|
400
|
+
perspective: var(--polycss-fpv-perspective, 2000px) !important;
|
|
401
|
+
transform-style: preserve-3d !important;
|
|
402
|
+
}
|
|
403
|
+
|
|
361
404
|
/* \u2500\u2500 Polygon leaf element \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
362
405
|
|
|
363
406
|
/*
|
|
@@ -385,24 +428,37 @@ var CORE_BASE_STYLES = `
|
|
|
385
428
|
background-repeat: no-repeat;
|
|
386
429
|
}
|
|
387
430
|
|
|
431
|
+
.polycss-scene b,
|
|
432
|
+
.polycss-scene i,
|
|
433
|
+
.polycss-scene u {
|
|
434
|
+
color: var(--polycss-paint, currentColor);
|
|
435
|
+
}
|
|
436
|
+
|
|
388
437
|
.polycss-scene b {
|
|
438
|
+
background: currentColor;
|
|
439
|
+
width: 64px;
|
|
440
|
+
height: 64px;
|
|
389
441
|
}
|
|
390
442
|
|
|
391
443
|
.polycss-scene i {
|
|
392
|
-
|
|
393
|
-
|
|
444
|
+
width: 16px;
|
|
445
|
+
height: 16px;
|
|
394
446
|
border-color: currentColor;
|
|
395
447
|
}
|
|
396
448
|
|
|
397
449
|
.polycss-scene s {
|
|
450
|
+
width: var(--polycss-atlas-size, 64px);
|
|
451
|
+
height: var(--polycss-atlas-size, 64px);
|
|
398
452
|
}
|
|
399
453
|
|
|
400
454
|
.polycss-scene u {
|
|
401
|
-
width:
|
|
402
|
-
height:
|
|
455
|
+
width: 0;
|
|
456
|
+
height: 0;
|
|
403
457
|
background: transparent;
|
|
404
458
|
box-sizing: content-box;
|
|
405
459
|
border: 0 solid transparent;
|
|
460
|
+
border-color: transparent transparent currentColor transparent;
|
|
461
|
+
border-width: 0 32px 64px 32px;
|
|
406
462
|
}
|
|
407
463
|
|
|
408
464
|
/* \u2500\u2500 Gizmo override \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
@@ -415,17 +471,43 @@ var CORE_BASE_STYLES = `
|
|
|
415
471
|
* up half-culled (you see only the side faces, not the caps), and the
|
|
416
472
|
* arrow looks like a flat strip instead of a 3D bar.
|
|
417
473
|
*
|
|
418
|
-
* Transitions on border-color and background-color smooth the
|
|
419
|
-
* idle \u2192 hover \u2192 drag alpha changes
|
|
420
|
-
*
|
|
421
|
-
* (rgb-with-alpha CSS calc); transitioning both covers either path.
|
|
474
|
+
* Transitions on color, border-color, and background-color smooth the
|
|
475
|
+
* idle \u2192 hover \u2192 drag alpha changes across rect, border-shape, triangle,
|
|
476
|
+
* and atlas paths.
|
|
422
477
|
*/
|
|
423
478
|
.polycss-transform-controls i,
|
|
424
479
|
.polycss-transform-controls b,
|
|
425
480
|
.polycss-transform-controls s,
|
|
426
481
|
.polycss-transform-controls u {
|
|
427
482
|
backface-visibility: visible;
|
|
428
|
-
transition: border-color 150ms ease-out, background-color 150ms ease-out;
|
|
483
|
+
transition: color 150ms ease-out, border-color 150ms ease-out, background-color 150ms ease-out;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/*
|
|
487
|
+
* Rotate rings are rendered as a single square quad per ring, then masked
|
|
488
|
+
* to a donut via a radial-gradient. --ring-inner-ratio is set inline by
|
|
489
|
+
* <PolyTransformControls> (= innerR / outerR, where outerR is the edge of
|
|
490
|
+
* the quad mapped to 50%). Hit-testing also uses the donut shape \u2014 see
|
|
491
|
+
* the ring-aware path in TransformControls.tsx. Single DOM node per ring.
|
|
492
|
+
*/
|
|
493
|
+
.polycss-transform-ring i,
|
|
494
|
+
.polycss-transform-ring b,
|
|
495
|
+
.polycss-transform-ring s,
|
|
496
|
+
.polycss-transform-ring u {
|
|
497
|
+
--ring-inner-r: calc(var(--ring-inner-ratio, 0.92) * 50%);
|
|
498
|
+
--ring-outer-r: calc(var(--ring-outer-ratio, 1) * 50%);
|
|
499
|
+
-webkit-mask: radial-gradient(circle at 50% 50%,
|
|
500
|
+
transparent 0%,
|
|
501
|
+
transparent var(--ring-inner-r),
|
|
502
|
+
black var(--ring-inner-r),
|
|
503
|
+
black var(--ring-outer-r),
|
|
504
|
+
transparent var(--ring-outer-r));
|
|
505
|
+
mask: radial-gradient(circle at 50% 50%,
|
|
506
|
+
transparent 0%,
|
|
507
|
+
transparent var(--ring-inner-r),
|
|
508
|
+
black var(--ring-inner-r),
|
|
509
|
+
black var(--ring-outer-r),
|
|
510
|
+
transparent var(--ring-outer-r));
|
|
429
511
|
}
|
|
430
512
|
|
|
431
513
|
/* \u2500\u2500 Dynamic lighting cascade vars (scene root \u2192 polygons) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
@@ -445,6 +527,20 @@ var CORE_BASE_STYLES = `
|
|
|
445
527
|
@property --plx { syntax: "<number>"; inherits: true; initial-value: 0; }
|
|
446
528
|
@property --ply { syntax: "<number>"; inherits: true; initial-value: 0; }
|
|
447
529
|
@property --plz { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
530
|
+
|
|
531
|
+
/* CSS-space light components (world-Y\u2192cssX, world-X\u2192cssY, world-Z\u2192cssZ).
|
|
532
|
+
Used by the shadow projection matrix. --clz is clamped away from 0 in JS
|
|
533
|
+
to avoid divide-by-zero when the light is near-horizontal. */
|
|
534
|
+
@property --clx { syntax: "<number>"; inherits: true; initial-value: 0.01; }
|
|
535
|
+
@property --cly { syntax: "<number>"; inherits: true; initial-value: 0; }
|
|
536
|
+
@property --clz { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
537
|
+
|
|
538
|
+
/* Ground-plane position in CSS pixels along the CSS-Z axis (= world-Z, the
|
|
539
|
+
up axis in polycss's world convention). Stored as a <number> so it can be
|
|
540
|
+
used directly inside matrix3d() calc() expressions (matrix3d requires
|
|
541
|
+
dimensionless entries \u2014 no px units).
|
|
542
|
+
Set by PolyScene from the min world-Z of casting meshes. */
|
|
543
|
+
@property --shadow-ground-cssz { syntax: "<number>"; inherits: true; initial-value: 0; }
|
|
448
544
|
@property --plr { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
449
545
|
@property --plg { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
450
546
|
@property --plb { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
@@ -454,14 +550,14 @@ var CORE_BASE_STYLES = `
|
|
|
454
550
|
@property --pab { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
455
551
|
@property --pai { syntax: "<number>"; inherits: true; initial-value: 0.4; }
|
|
456
552
|
|
|
457
|
-
/* Per-polygon surface normal \u2014 set inline by the renderer.
|
|
458
|
-
|
|
553
|
+
/* Per-polygon surface normal \u2014 set inline by the renderer. Base RGB channels
|
|
554
|
+
may be hoisted to a mesh wrapper, so they inherit unless overridden inline. */
|
|
459
555
|
@property --pnx { syntax: "<number>"; inherits: false; initial-value: 0; }
|
|
460
556
|
@property --pny { syntax: "<number>"; inherits: false; initial-value: 0; }
|
|
461
557
|
@property --pnz { syntax: "<number>"; inherits: false; initial-value: 1; }
|
|
462
|
-
@property --psr { syntax: "<number>"; inherits:
|
|
463
|
-
@property --psg { syntax: "<number>"; inherits:
|
|
464
|
-
@property --psb { syntax: "<number>"; inherits:
|
|
558
|
+
@property --psr { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
559
|
+
@property --psg { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
560
|
+
@property --psb { syntax: "<number>"; inherits: true; initial-value: 1; }
|
|
465
561
|
|
|
466
562
|
/* Calc-driven Lambert + tint, scoped to dynamic-lighting scenes. Lives
|
|
467
563
|
here (not inline per polygon) so each leaf only carries its tiny normal
|
|
@@ -487,8 +583,9 @@ var CORE_BASE_STYLES = `
|
|
|
487
583
|
background-blend-mode: multiply;
|
|
488
584
|
}
|
|
489
585
|
|
|
586
|
+
.polycss-scene[data-polycss-lighting="dynamic"] b,
|
|
490
587
|
.polycss-scene[data-polycss-lighting="dynamic"] u {
|
|
491
|
-
|
|
588
|
+
color: rgb(
|
|
492
589
|
calc(255 * var(--psr) * (var(--par) * var(--pai)
|
|
493
590
|
+ var(--plr) * var(--pli) * max(0,
|
|
494
591
|
var(--pnx) * var(--plx) +
|
|
@@ -506,6 +603,92 @@ var CORE_BASE_STYLES = `
|
|
|
506
603
|
var(--pnz) * var(--plz))))
|
|
507
604
|
);
|
|
508
605
|
}
|
|
606
|
+
|
|
607
|
+
/* <q> \u2014 dedicated shadow leaf. Same border-shape rendering trick as <i>
|
|
608
|
+
(border-color: currentColor fills the polygon outline) but with its
|
|
609
|
+
own tag so we don't have to thread :not(.polycss-shadow) exclusions
|
|
610
|
+
through every dynamic-mode color rule. backface-visibility must be
|
|
611
|
+
visible because the projection matrix is near-rank-deficient and the
|
|
612
|
+
resulting plane's normal can read as back-facing under some camera
|
|
613
|
+
angles; the leaf is intentionally always painted. Strip the UA's
|
|
614
|
+
default ::before/::after open-/close-quote so the element is just a
|
|
615
|
+
styled box. */
|
|
616
|
+
.polycss-scene q {
|
|
617
|
+
position: absolute;
|
|
618
|
+
display: block;
|
|
619
|
+
transform-origin: 0 0;
|
|
620
|
+
transform-style: preserve-3d;
|
|
621
|
+
margin: 0;
|
|
622
|
+
padding: 0;
|
|
623
|
+
font: inherit;
|
|
624
|
+
font-weight: normal;
|
|
625
|
+
font-style: normal;
|
|
626
|
+
line-height: 0;
|
|
627
|
+
text-decoration: none;
|
|
628
|
+
backface-visibility: visible;
|
|
629
|
+
border-color: currentColor;
|
|
630
|
+
pointer-events: none;
|
|
631
|
+
will-change: transform;
|
|
632
|
+
}
|
|
633
|
+
.polycss-scene q::before,
|
|
634
|
+
.polycss-scene q::after {
|
|
635
|
+
content: none;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* \u2500\u2500 Cast shadows (dynamic mode only) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
639
|
+
|
|
640
|
+
/*
|
|
641
|
+
* Shadow projection matrix. Projects any 3D point P onto the horizontal
|
|
642
|
+
* ground plane (cssZ \u2248 G) along the CSS-space light direction (--clx/y/z).
|
|
643
|
+
*
|
|
644
|
+
* In polycss's world convention world Z is up (red-green plane is the
|
|
645
|
+
* floor in the axes helper). After the world\u2192CSS swap (Y\u2194X), world Z stays
|
|
646
|
+
* as CSS Z, so the ground plane normal in CSS space is +cssZ.
|
|
647
|
+
*
|
|
648
|
+
* The strict projection formula would set m22=0 (output.z is a constant G,
|
|
649
|
+
* the polygon is exactly flat). But Chromium SKIPS rendering elements
|
|
650
|
+
* whose composed transform matrix is non-invertible (singular). m22=0
|
|
651
|
+
* makes the matrix singular, so the shadow paints nothing even though it
|
|
652
|
+
* has a valid layout box. The fix: collapse along z by a near-zero
|
|
653
|
+
* scale (Z_SQUASH = 0.01) instead of exactly zero \u2014 output.z is then
|
|
654
|
+
* approximately G with ~1% drift from the input, full-rank and renderable.
|
|
655
|
+
* The shadow still looks flat to the eye (the drift is sub-pixel for
|
|
656
|
+
* any realistic scene size).
|
|
657
|
+
*
|
|
658
|
+
* out.cssX = P.cssX - (--clx/--clz) * (P.cssZ - G)
|
|
659
|
+
* out.cssY = P.cssY - (--cly/--clz) * (P.cssZ - G)
|
|
660
|
+
* out.cssZ = Z_SQUASH * P.cssZ + (1 - Z_SQUASH) * G
|
|
661
|
+
*
|
|
662
|
+
* As column-major 4\xD74 (CSS matrix3d order):
|
|
663
|
+
* col1: [1, 0, 0, 0]
|
|
664
|
+
* col2: [0, 1, 0, 0]
|
|
665
|
+
* col3: [-(--clx/--clz), -(--cly/--clz), Z_SQUASH, 0]
|
|
666
|
+
* col4: [G*(--clx/--clz), G*(--cly/--clz), G*(1-Z_SQUASH), 1]
|
|
667
|
+
*/
|
|
668
|
+
.polycss-scene[data-polycss-lighting="dynamic"] {
|
|
669
|
+
--shadow-proj: matrix3d(
|
|
670
|
+
1, 0, 0, 0,
|
|
671
|
+
0, 1, 0, 0,
|
|
672
|
+
calc(-1 * var(--clx) / var(--clz)),
|
|
673
|
+
calc(-1 * var(--cly) / var(--clz)),
|
|
674
|
+
0.01,
|
|
675
|
+
0,
|
|
676
|
+
calc(var(--shadow-ground-cssz) * var(--clx) / var(--clz)),
|
|
677
|
+
calc(var(--shadow-ground-cssz) * var(--cly) / var(--clz)),
|
|
678
|
+
calc(var(--shadow-ground-cssz) * 0.99),
|
|
679
|
+
1
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* <q> shadow leaf \u2014 Lambert-gated opacity. Polygons facing the light cast
|
|
684
|
+
full shadow; polygons facing away cast zero shadow (their projection
|
|
685
|
+
would stack inside the silhouette and produce ugly overdraw). The
|
|
686
|
+
* 10 multiplier sharpens the cutoff so small positive Lambert values
|
|
687
|
+
jump quickly to 1, giving a near-binary visibility decision with a
|
|
688
|
+
smooth edge transition. Pure CSS calc \u2014 no JS at light-change time. */
|
|
689
|
+
.polycss-scene q {
|
|
690
|
+
opacity: clamp(0, calc((var(--pnx) * var(--clx) + var(--pny) * var(--cly) + var(--pnz) * var(--clz)) * 10), 1);
|
|
691
|
+
}
|
|
509
692
|
`;
|
|
510
693
|
|
|
511
694
|
// src/scene/textureAtlas.tsx
|
|
@@ -525,15 +708,30 @@ var MAX_ATLAS_SCALE = 1;
|
|
|
525
708
|
var AUTO_ATLAS_LOW_AREA = ATLAS_MAX_SIZE * ATLAS_MAX_SIZE;
|
|
526
709
|
var AUTO_ATLAS_MEDIUM_AREA = AUTO_ATLAS_LOW_AREA * 3;
|
|
527
710
|
var AUTO_ATLAS_MAX_BITMAP_SIDE = 2048;
|
|
528
|
-
var
|
|
711
|
+
var AUTO_ATLAS_MAX_DECODED_BYTES_MOBILE = 4 * 1024 * 1024;
|
|
712
|
+
var AUTO_ATLAS_MAX_DECODED_BYTES_DESKTOP = 16 * 1024 * 1024;
|
|
529
713
|
var AUTO_ATLAS_SCALE_GUARD = 0.995;
|
|
530
714
|
var DEFAULT_MATRIX_DECIMALS = 3;
|
|
531
715
|
var DEFAULT_BORDER_SHAPE_DECIMALS = 2;
|
|
716
|
+
var DEFAULT_ATLAS_CSS_DECIMALS = 4;
|
|
717
|
+
var SOLID_QUAD_CANONICAL_SIZE = 64;
|
|
718
|
+
var SOLID_TRIANGLE_CANONICAL_SIZE = 64;
|
|
719
|
+
var ATLAS_CANONICAL_SIZE_EXPLICIT = 64;
|
|
720
|
+
var ATLAS_CANONICAL_SIZE_AUTO_DESKTOP = 128;
|
|
721
|
+
var BORDER_SHAPE_CENTER_PERCENT = 50;
|
|
722
|
+
var BORDER_SHAPE_POINT_EPS = 1e-7;
|
|
723
|
+
var BORDER_SHAPE_CANONICAL_SIZE = 16;
|
|
724
|
+
var PROJECTIVE_QUAD_DENOM_EPS = 0.05;
|
|
725
|
+
var PROJECTIVE_QUAD_MAX_WEIGHT_RATIO = 4;
|
|
726
|
+
var PROJECTIVE_QUAD_BLEED = 0.6;
|
|
532
727
|
var BASIS_EPS = 1e-9;
|
|
533
|
-
var SOLID_TRIANGLE_BLEED = 0.
|
|
728
|
+
var SOLID_TRIANGLE_BLEED = 0.6;
|
|
534
729
|
var TEXTURE_IMAGE_CACHE = /* @__PURE__ */ new Map();
|
|
535
730
|
var RECT_EPS = 1e-3;
|
|
536
731
|
var TEXTURE_TRIANGLE_BLEED = 0.75;
|
|
732
|
+
var TEXTURE_EDGE_REPAIR_ALPHA_MIN = 1;
|
|
733
|
+
var TEXTURE_EDGE_REPAIR_SOURCE_ALPHA_MIN = 250;
|
|
734
|
+
var TEXTURE_EDGE_REPAIR_RADIUS = 1.5;
|
|
537
735
|
function loadTextureImage(url) {
|
|
538
736
|
let p = TEXTURE_IMAGE_CACHE.get(url);
|
|
539
737
|
if (!p) {
|
|
@@ -565,6 +763,10 @@ function roundDecimal(value, decimals) {
|
|
|
565
763
|
const next = value.toFixed(decimals).replace(/\.?0+$/, "");
|
|
566
764
|
return Object.is(Number(next), -0) ? "0" : next;
|
|
567
765
|
}
|
|
766
|
+
function formatCssLength(value, decimals = DEFAULT_ATLAS_CSS_DECIMALS) {
|
|
767
|
+
const next = roundDecimal(value, decimals);
|
|
768
|
+
return Number(next) === 0 || Object.is(Number(next), -0) ? "0" : `${next}px`;
|
|
769
|
+
}
|
|
568
770
|
function formatMatrix3d(matrix, decimals = DEFAULT_MATRIX_DECIMALS) {
|
|
569
771
|
return `matrix3d(${matrix.split(",").map((value) => {
|
|
570
772
|
const parsed = Number(value.trim());
|
|
@@ -572,12 +774,31 @@ function formatMatrix3d(matrix, decimals = DEFAULT_MATRIX_DECIMALS) {
|
|
|
572
774
|
}).join(",")})`;
|
|
573
775
|
}
|
|
574
776
|
function formatPercent(value, decimals = DEFAULT_BORDER_SHAPE_DECIMALS) {
|
|
575
|
-
|
|
777
|
+
const next = roundDecimal(value, decimals);
|
|
778
|
+
return Number(next) === 0 ? "0" : `${next}%`;
|
|
779
|
+
}
|
|
780
|
+
function pointOnSegment(px, py, ax, ay, bx, by) {
|
|
781
|
+
const cross = (px - ax) * (by - ay) - (py - ay) * (bx - ax);
|
|
782
|
+
if (Math.abs(cross) > BORDER_SHAPE_POINT_EPS) return false;
|
|
783
|
+
const dot = (px - ax) * (px - bx) + (py - ay) * (py - by);
|
|
784
|
+
return dot <= BORDER_SHAPE_POINT_EPS;
|
|
785
|
+
}
|
|
786
|
+
function polygonContainsPoint(points, px = BORDER_SHAPE_CENTER_PERCENT, py = BORDER_SHAPE_CENTER_PERCENT) {
|
|
787
|
+
let inside = false;
|
|
788
|
+
for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
789
|
+
const [xi, yi] = points[i];
|
|
790
|
+
const [xj, yj] = points[j];
|
|
791
|
+
if (pointOnSegment(px, py, xi, yi, xj, yj)) return true;
|
|
792
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
|
|
793
|
+
inside = !inside;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return inside;
|
|
576
797
|
}
|
|
577
798
|
function atlasArea(pages) {
|
|
578
799
|
return pages.reduce((sum, page) => sum + page.width * page.height, 0);
|
|
579
800
|
}
|
|
580
|
-
function autoAtlasScaleCap(pages) {
|
|
801
|
+
function autoAtlasScaleCap(pages, maxDecodedBytes) {
|
|
581
802
|
const area = atlasArea(pages);
|
|
582
803
|
if (area <= 0) return 1;
|
|
583
804
|
const maxSide = Math.max(
|
|
@@ -585,15 +806,52 @@ function autoAtlasScaleCap(pages) {
|
|
|
585
806
|
...pages.map((page) => Math.max(page.width, page.height))
|
|
586
807
|
);
|
|
587
808
|
const sideScale = AUTO_ATLAS_MAX_BITMAP_SIDE / maxSide;
|
|
588
|
-
const memoryScale = Math.sqrt(
|
|
809
|
+
const memoryScale = Math.sqrt(maxDecodedBytes / (area * 4));
|
|
589
810
|
return normalizeAtlasScale(Math.min(sideScale, memoryScale));
|
|
590
811
|
}
|
|
591
|
-
function
|
|
812
|
+
function isMobileDocument(doc) {
|
|
813
|
+
if (!doc) return false;
|
|
814
|
+
const win = doc.defaultView ?? (typeof window !== "undefined" ? window : void 0);
|
|
815
|
+
const media = win?.matchMedia;
|
|
816
|
+
if (!media) return false;
|
|
817
|
+
return media("(pointer: coarse)").matches || media("(hover: none)").matches;
|
|
818
|
+
}
|
|
819
|
+
function autoAtlasMaxDecodedBytes(doc) {
|
|
820
|
+
return isMobileDocument(doc) ? AUTO_ATLAS_MAX_DECODED_BYTES_MOBILE : AUTO_ATLAS_MAX_DECODED_BYTES_DESKTOP;
|
|
821
|
+
}
|
|
822
|
+
function atlasCanonicalSizeForTextureQuality(textureQualityInput, doc) {
|
|
823
|
+
if (textureQualityInput !== void 0 && textureQualityInput !== "auto") {
|
|
824
|
+
return ATLAS_CANONICAL_SIZE_EXPLICIT;
|
|
825
|
+
}
|
|
826
|
+
return isMobileDocument(doc) ? ATLAS_CANONICAL_SIZE_EXPLICIT : ATLAS_CANONICAL_SIZE_AUTO_DESKTOP;
|
|
827
|
+
}
|
|
828
|
+
function formatAtlasMatrix(entry, atlasCanonicalSize) {
|
|
829
|
+
const values = entry.matrix.split(",").map((value) => Number(value));
|
|
830
|
+
if (values.length !== 16 || values.some((value) => !Number.isFinite(value))) {
|
|
831
|
+
return entry.canonicalMatrix;
|
|
832
|
+
}
|
|
833
|
+
values[0] *= entry.canvasW / atlasCanonicalSize;
|
|
834
|
+
values[1] *= entry.canvasW / atlasCanonicalSize;
|
|
835
|
+
values[2] *= entry.canvasW / atlasCanonicalSize;
|
|
836
|
+
values[4] *= entry.canvasH / atlasCanonicalSize;
|
|
837
|
+
values[5] *= entry.canvasH / atlasCanonicalSize;
|
|
838
|
+
values[6] *= entry.canvasH / atlasCanonicalSize;
|
|
839
|
+
return values.join(",");
|
|
840
|
+
}
|
|
841
|
+
function applyPackedAtlasCanonicalSize(packed, atlasCanonicalSize) {
|
|
842
|
+
for (const entry of packed.entries) {
|
|
843
|
+
if (!entry) continue;
|
|
844
|
+
entry.atlasCanonicalSize = atlasCanonicalSize;
|
|
845
|
+
entry.atlasMatrix = formatAtlasMatrix(entry, atlasCanonicalSize);
|
|
846
|
+
}
|
|
847
|
+
return packed;
|
|
848
|
+
}
|
|
849
|
+
function autoAtlasScale(pages, maxDecodedBytes) {
|
|
592
850
|
const area = atlasArea(pages);
|
|
593
851
|
let atlasScale = 0.5;
|
|
594
852
|
if (area <= AUTO_ATLAS_LOW_AREA) atlasScale = 1;
|
|
595
853
|
else if (area <= AUTO_ATLAS_MEDIUM_AREA) atlasScale = 0.75;
|
|
596
|
-
return normalizeAtlasScale(Math.min(atlasScale, autoAtlasScaleCap(pages)));
|
|
854
|
+
return normalizeAtlasScale(Math.min(atlasScale, autoAtlasScaleCap(pages, maxDecodedBytes)));
|
|
597
855
|
}
|
|
598
856
|
function atlasBitmapMaxSide(pages, atlasScale) {
|
|
599
857
|
return pages.reduce((max, page) => Math.max(
|
|
@@ -608,18 +866,18 @@ function atlasDecodedBytes(pages, atlasScale) {
|
|
|
608
866
|
0
|
|
609
867
|
);
|
|
610
868
|
}
|
|
611
|
-
function autoAtlasBudgetFactor(pages, atlasScale) {
|
|
869
|
+
function autoAtlasBudgetFactor(pages, atlasScale, maxDecodedBytes) {
|
|
612
870
|
const maxSide = atlasBitmapMaxSide(pages, atlasScale);
|
|
613
871
|
const decodedBytes = atlasDecodedBytes(pages, atlasScale);
|
|
614
872
|
const sideFactor = maxSide > AUTO_ATLAS_MAX_BITMAP_SIDE ? AUTO_ATLAS_MAX_BITMAP_SIDE / maxSide : 1;
|
|
615
|
-
const memoryFactor = decodedBytes >
|
|
873
|
+
const memoryFactor = decodedBytes > maxDecodedBytes ? Math.sqrt(maxDecodedBytes / decodedBytes) : 1;
|
|
616
874
|
return Math.min(sideFactor, memoryFactor);
|
|
617
875
|
}
|
|
618
|
-
function packTextureAtlasPlansAuto(plans, fullScalePacked) {
|
|
619
|
-
let atlasScale = autoAtlasScale(fullScalePacked.pages);
|
|
876
|
+
function packTextureAtlasPlansAuto(plans, fullScalePacked, maxDecodedBytes) {
|
|
877
|
+
let atlasScale = autoAtlasScale(fullScalePacked.pages, maxDecodedBytes);
|
|
620
878
|
let packed = atlasScale === 1 ? fullScalePacked : packTextureAtlasPlans(plans, atlasScale);
|
|
621
879
|
for (let i = 0; i < 4; i++) {
|
|
622
|
-
const factor = autoAtlasBudgetFactor(packed.pages, atlasScale);
|
|
880
|
+
const factor = autoAtlasBudgetFactor(packed.pages, atlasScale, maxDecodedBytes);
|
|
623
881
|
if (factor >= 1) break;
|
|
624
882
|
const nextAtlasScale = normalizeAtlasScale(atlasScale * factor * AUTO_ATLAS_SCALE_GUARD);
|
|
625
883
|
if (nextAtlasScale >= atlasScale) break;
|
|
@@ -628,13 +886,23 @@ function packTextureAtlasPlansAuto(plans, fullScalePacked) {
|
|
|
628
886
|
}
|
|
629
887
|
return { packed, atlasScale };
|
|
630
888
|
}
|
|
631
|
-
function packTextureAtlasPlansWithScale(plans,
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
889
|
+
function packTextureAtlasPlansWithScale(plans, textureQualityInput, doc) {
|
|
890
|
+
const atlasCanonicalSize = atlasCanonicalSizeForTextureQuality(textureQualityInput, doc);
|
|
891
|
+
if (textureQualityInput !== void 0 && textureQualityInput !== "auto") {
|
|
892
|
+
const atlasScale = normalizeAtlasScale(textureQualityInput);
|
|
893
|
+
return {
|
|
894
|
+
packed: applyPackedAtlasCanonicalSize(packTextureAtlasPlans(plans, atlasScale), atlasCanonicalSize),
|
|
895
|
+
atlasScale,
|
|
896
|
+
atlasCanonicalSize
|
|
897
|
+
};
|
|
635
898
|
}
|
|
636
899
|
const fullScalePacked = packTextureAtlasPlans(plans, 1);
|
|
637
|
-
|
|
900
|
+
const autoPacked = packTextureAtlasPlansAuto(plans, fullScalePacked, autoAtlasMaxDecodedBytes(doc));
|
|
901
|
+
return {
|
|
902
|
+
packed: applyPackedAtlasCanonicalSize(autoPacked.packed, atlasCanonicalSize),
|
|
903
|
+
atlasScale: autoPacked.atlasScale,
|
|
904
|
+
atlasCanonicalSize
|
|
905
|
+
};
|
|
638
906
|
}
|
|
639
907
|
function atlasPadding(atlasScale) {
|
|
640
908
|
return Math.max(ATLAS_PADDING, Math.ceil(ATLAS_PADDING / atlasScale));
|
|
@@ -654,6 +922,9 @@ function parseHex(hex) {
|
|
|
654
922
|
if (!parsed) return { r: 255, g: 255, b: 255 };
|
|
655
923
|
return { r: parsed.rgb[0], g: parsed.rgb[1], b: parsed.rgb[2] };
|
|
656
924
|
}
|
|
925
|
+
function rgbKey({ r, g, b }) {
|
|
926
|
+
return `${r},${g},${b}`;
|
|
927
|
+
}
|
|
657
928
|
function parseAlpha(input) {
|
|
658
929
|
return (0, import_polycss_core3.parsePureColor)(input)?.alpha ?? 1;
|
|
659
930
|
}
|
|
@@ -689,60 +960,369 @@ function isFullRectSolid(entry) {
|
|
|
689
960
|
function isSolidTrianglePlan(entry) {
|
|
690
961
|
return !entry.texture && entry.polygon.vertices.length === 3;
|
|
691
962
|
}
|
|
963
|
+
function isProjectiveQuadPlan(entry) {
|
|
964
|
+
return !entry.texture && !!entry.projectiveMatrix && !isFullRectSolid(entry);
|
|
965
|
+
}
|
|
692
966
|
function borderShapeSupported() {
|
|
693
967
|
const supportsBorderShape = !!globalThis.CSS?.supports?.(
|
|
694
968
|
"border-shape",
|
|
695
|
-
"polygon(0 0, 100% 0, 0 100%)
|
|
969
|
+
"polygon(0 0, 100% 0, 0 100%) circle(0)"
|
|
696
970
|
);
|
|
697
971
|
if (!supportsBorderShape) return false;
|
|
698
972
|
const media = globalThis.matchMedia;
|
|
699
973
|
if (typeof media !== "function") return true;
|
|
700
974
|
return media("(pointer: fine)").matches && media("(hover: hover)").matches;
|
|
701
975
|
}
|
|
702
|
-
function
|
|
703
|
-
const
|
|
976
|
+
function solidTriangleSupported() {
|
|
977
|
+
const userAgent = (typeof window !== "undefined" ? window.navigator : globalThis.navigator)?.userAgent ?? "";
|
|
978
|
+
if (!userAgent) return true;
|
|
979
|
+
const isChromiumFamily = /\b(?:Chrome|HeadlessChrome|Chromium|Edg|OPR)\//.test(userAgent);
|
|
980
|
+
const isSafariFamily = /\bVersion\/[\d.]+.*\bSafari\//.test(userAgent);
|
|
981
|
+
return !isSafariFamily || isChromiumFamily;
|
|
982
|
+
}
|
|
983
|
+
function incrementCount(map, key) {
|
|
984
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
985
|
+
}
|
|
986
|
+
function dominantCountKey(map) {
|
|
987
|
+
let bestKey;
|
|
988
|
+
let bestCount = 1;
|
|
989
|
+
for (const [key, count] of map) {
|
|
990
|
+
if (count > bestCount) {
|
|
991
|
+
bestKey = key;
|
|
992
|
+
bestCount = count;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
return bestKey;
|
|
996
|
+
}
|
|
997
|
+
var BRUSH_INLINE_STYLE_ORDER = /* @__PURE__ */ new Map([
|
|
998
|
+
["transform", 0],
|
|
999
|
+
["border-shape", 1],
|
|
1000
|
+
["border-width", 2],
|
|
1001
|
+
["width", 3],
|
|
1002
|
+
["height", 4],
|
|
1003
|
+
["color", 5]
|
|
1004
|
+
]);
|
|
1005
|
+
function orderBrushInlineStyle(el) {
|
|
1006
|
+
const current = el.getAttribute("style");
|
|
1007
|
+
if (!current) return;
|
|
1008
|
+
const declarations = current.split(";").map((declaration) => declaration.trim()).filter(Boolean);
|
|
1009
|
+
const next = declarations.map((declaration, index) => {
|
|
1010
|
+
const property = declaration.slice(0, declaration.indexOf(":")).trim().toLowerCase();
|
|
1011
|
+
return {
|
|
1012
|
+
declaration,
|
|
1013
|
+
index,
|
|
1014
|
+
order: BRUSH_INLINE_STYLE_ORDER.get(property) ?? Number.POSITIVE_INFINITY
|
|
1015
|
+
};
|
|
1016
|
+
}).sort((a, b) => a.order - b.order || a.index - b.index).map(({ declaration }) => declaration).join(";");
|
|
1017
|
+
if (next !== current) el.setAttribute("style", next);
|
|
1018
|
+
}
|
|
1019
|
+
function getSolidPaintDefaults(plans, textureLighting) {
|
|
1020
|
+
const paintCounts = /* @__PURE__ */ new Map();
|
|
1021
|
+
const dynamicCounts = /* @__PURE__ */ new Map();
|
|
1022
|
+
const dynamicColors = /* @__PURE__ */ new Map();
|
|
1023
|
+
const useStableTriangle = solidTriangleSupported();
|
|
1024
|
+
const useBorderShape = textureLighting !== "dynamic" && borderShapeSupported();
|
|
1025
|
+
for (const plan of plans) {
|
|
1026
|
+
if (!plan || plan.texture) continue;
|
|
1027
|
+
if (textureLighting === "dynamic") {
|
|
1028
|
+
if (!(useStableTriangle && isSolidTrianglePlan(plan)) && !isFullRectSolid(plan)) continue;
|
|
1029
|
+
const color = parseHex(plan.polygon.color ?? "#cccccc");
|
|
1030
|
+
const key = rgbKey(color);
|
|
1031
|
+
incrementCount(dynamicCounts, key);
|
|
1032
|
+
if (!dynamicColors.has(key)) dynamicColors.set(key, color);
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
if (!(useStableTriangle && isSolidTrianglePlan(plan)) && !isFullRectSolid(plan) && !useBorderShape) continue;
|
|
1036
|
+
incrementCount(paintCounts, plan.shadedColor);
|
|
1037
|
+
}
|
|
1038
|
+
const paintColor = dominantCountKey(paintCounts);
|
|
1039
|
+
const dynamicColorKey = dominantCountKey(dynamicCounts);
|
|
1040
|
+
return {
|
|
1041
|
+
paintColor,
|
|
1042
|
+
dynamicColorKey,
|
|
1043
|
+
dynamicColor: dynamicColorKey ? dynamicColors.get(dynamicColorKey) : void 0
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function borderShapePointsForPlan(entry) {
|
|
1047
|
+
const points = [];
|
|
704
1048
|
const width = entry.canvasW || 1;
|
|
705
1049
|
const height = entry.canvasH || 1;
|
|
706
1050
|
for (let i = 0; i < entry.screenPts.length; i += 2) {
|
|
707
1051
|
const x = Math.max(0, Math.min(100, entry.screenPts[i] / width * 100));
|
|
708
1052
|
const y = Math.max(0, Math.min(100, entry.screenPts[i + 1] / height * 100));
|
|
709
|
-
|
|
1053
|
+
points.push([x, y]);
|
|
710
1054
|
}
|
|
711
|
-
return
|
|
1055
|
+
return points;
|
|
1056
|
+
}
|
|
1057
|
+
function cssBorderShapePoint([x, y]) {
|
|
1058
|
+
return `${formatPercent(x)} ${formatPercent(y)}`;
|
|
1059
|
+
}
|
|
1060
|
+
function cssPolygonShapeForPoints(points) {
|
|
1061
|
+
return `polygon(${points.map(cssBorderShapePoint).join(",")})`;
|
|
712
1062
|
}
|
|
713
|
-
function
|
|
1063
|
+
function cssCollapsedInnerShapeForPoints(points) {
|
|
1064
|
+
if (polygonContainsPoint(points)) return "circle(0)";
|
|
714
1065
|
let xSum = 0;
|
|
715
1066
|
let ySum = 0;
|
|
716
|
-
const
|
|
717
|
-
for (
|
|
718
|
-
xSum +=
|
|
719
|
-
ySum +=
|
|
1067
|
+
const pointCount = Math.max(1, points.length);
|
|
1068
|
+
for (const [x2, y2] of points) {
|
|
1069
|
+
xSum += x2;
|
|
1070
|
+
ySum += y2;
|
|
720
1071
|
}
|
|
721
|
-
const
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
const y = formatPercent(Math.max(0, Math.min(100, ySum / points / height * 100)));
|
|
725
|
-
return `polygon(${Array.from({ length: points }, () => `${x} ${y}`).join(", ")})`;
|
|
1072
|
+
const x = formatPercent(Math.max(0, Math.min(100, xSum / pointCount)));
|
|
1073
|
+
const y = formatPercent(Math.max(0, Math.min(100, ySum / pointCount)));
|
|
1074
|
+
return `circle(0 at ${x} ${y})`;
|
|
726
1075
|
}
|
|
727
1076
|
function cssBorderShapeForPlan(entry) {
|
|
728
|
-
|
|
1077
|
+
const points = borderShapePointsForPlan(entry);
|
|
1078
|
+
return `${cssPolygonShapeForPoints(points)} ${cssCollapsedInnerShapeForPoints(points)}`;
|
|
729
1079
|
}
|
|
730
1080
|
function formatMatrix3dValues(values, decimals = DEFAULT_MATRIX_DECIMALS) {
|
|
731
1081
|
return values.map((value) => roundDecimal(value, decimals)).join(",");
|
|
732
1082
|
}
|
|
1083
|
+
function formatScaledMatrixFromPlan(entry, scaleX, scaleY) {
|
|
1084
|
+
const values = entry.matrix.split(",").map((value) => Number(value));
|
|
1085
|
+
if (values.length !== 16 || values.some((value) => !Number.isFinite(value))) {
|
|
1086
|
+
return entry.matrix;
|
|
1087
|
+
}
|
|
1088
|
+
values[0] *= scaleX;
|
|
1089
|
+
values[1] *= scaleX;
|
|
1090
|
+
values[2] *= scaleX;
|
|
1091
|
+
values[4] *= scaleY;
|
|
1092
|
+
values[5] *= scaleY;
|
|
1093
|
+
values[6] *= scaleY;
|
|
1094
|
+
return formatMatrix3dValues(values);
|
|
1095
|
+
}
|
|
1096
|
+
function formatBorderShapeMatrix(entry) {
|
|
1097
|
+
return formatScaledMatrixFromPlan(
|
|
1098
|
+
entry,
|
|
1099
|
+
(entry.canvasW || 1) / BORDER_SHAPE_CANONICAL_SIZE,
|
|
1100
|
+
(entry.canvasH || 1) / BORDER_SHAPE_CANONICAL_SIZE
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
function formatSolidQuadMatrix(entry) {
|
|
1104
|
+
return formatScaledMatrixFromPlan(
|
|
1105
|
+
entry,
|
|
1106
|
+
(entry.canvasW || 1) / SOLID_QUAD_CANONICAL_SIZE,
|
|
1107
|
+
(entry.canvasH || 1) / SOLID_QUAD_CANONICAL_SIZE
|
|
1108
|
+
);
|
|
1109
|
+
}
|
|
1110
|
+
function isConvexPolygonPoints(points) {
|
|
1111
|
+
if (points.length < 3) return false;
|
|
1112
|
+
let sign = 0;
|
|
1113
|
+
for (let i = 0; i < points.length; i++) {
|
|
1114
|
+
const a = points[i];
|
|
1115
|
+
const b = points[(i + 1) % points.length];
|
|
1116
|
+
const c = points[(i + 2) % points.length];
|
|
1117
|
+
const cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]);
|
|
1118
|
+
if (Math.abs(cross) <= BASIS_EPS) return false;
|
|
1119
|
+
const nextSign = Math.sign(cross);
|
|
1120
|
+
if (sign === 0) sign = nextSign;
|
|
1121
|
+
else if (nextSign !== sign) return false;
|
|
1122
|
+
}
|
|
1123
|
+
return true;
|
|
1124
|
+
}
|
|
1125
|
+
function signedArea2D(points) {
|
|
1126
|
+
let area = 0;
|
|
1127
|
+
for (let i = 0; i < points.length; i++) {
|
|
1128
|
+
const a = points[i];
|
|
1129
|
+
const b = points[(i + 1) % points.length];
|
|
1130
|
+
area += a[0] * b[1] - a[1] * b[0];
|
|
1131
|
+
}
|
|
1132
|
+
return area / 2;
|
|
1133
|
+
}
|
|
1134
|
+
function intersect2DLines(a0, a1, b0, b1) {
|
|
1135
|
+
const rx = a1[0] - a0[0];
|
|
1136
|
+
const ry = a1[1] - a0[1];
|
|
1137
|
+
const sx = b1[0] - b0[0];
|
|
1138
|
+
const sy = b1[1] - b0[1];
|
|
1139
|
+
const det = rx * sy - ry * sx;
|
|
1140
|
+
if (Math.abs(det) <= BASIS_EPS) return null;
|
|
1141
|
+
const qpx = b0[0] - a0[0];
|
|
1142
|
+
const qpy = b0[1] - a0[1];
|
|
1143
|
+
const t = (qpx * sy - qpy * sx) / det;
|
|
1144
|
+
return [a0[0] + t * rx, a0[1] + t * ry];
|
|
1145
|
+
}
|
|
1146
|
+
function offsetConvexPolygonPoints(points, amount) {
|
|
1147
|
+
if (points.length < 6 || points.length % 2 !== 0 || amount <= 0) return points;
|
|
1148
|
+
const q = [];
|
|
1149
|
+
for (let i = 0; i < points.length; i += 2) q.push([points[i], points[i + 1]]);
|
|
1150
|
+
if (!isConvexPolygonPoints(q)) return expandClipPoints(points, amount);
|
|
1151
|
+
const area = signedArea2D(q);
|
|
1152
|
+
if (Math.abs(area) <= BASIS_EPS) return expandClipPoints(points, amount);
|
|
1153
|
+
const outwardSign = area > 0 ? 1 : -1;
|
|
1154
|
+
const offsetLines = [];
|
|
1155
|
+
for (let i = 0; i < q.length; i++) {
|
|
1156
|
+
const a = q[i];
|
|
1157
|
+
const b = q[(i + 1) % q.length];
|
|
1158
|
+
const dx = b[0] - a[0];
|
|
1159
|
+
const dy = b[1] - a[1];
|
|
1160
|
+
const length = Math.hypot(dx, dy);
|
|
1161
|
+
if (length <= BASIS_EPS) return expandClipPoints(points, amount);
|
|
1162
|
+
const ox = outwardSign * (dy / length) * amount;
|
|
1163
|
+
const oy = outwardSign * (-dx / length) * amount;
|
|
1164
|
+
offsetLines.push({
|
|
1165
|
+
a: [a[0] + ox, a[1] + oy],
|
|
1166
|
+
b: [b[0] + ox, b[1] + oy]
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
const expanded = [];
|
|
1170
|
+
const maxMiter = Math.max(2, amount * 4);
|
|
1171
|
+
for (let i = 0; i < q.length; i++) {
|
|
1172
|
+
const prev = offsetLines[(i + q.length - 1) % q.length];
|
|
1173
|
+
const next = offsetLines[i];
|
|
1174
|
+
const intersection = intersect2DLines(prev.a, prev.b, next.a, next.b);
|
|
1175
|
+
if (!intersection) return expandClipPoints(points, amount);
|
|
1176
|
+
const original = q[i];
|
|
1177
|
+
const dx = intersection[0] - original[0];
|
|
1178
|
+
const dy = intersection[1] - original[1];
|
|
1179
|
+
const miter = Math.hypot(dx, dy);
|
|
1180
|
+
if (miter > maxMiter) {
|
|
1181
|
+
expanded.push(
|
|
1182
|
+
original[0] + dx / miter * maxMiter,
|
|
1183
|
+
original[1] + dy / miter * maxMiter
|
|
1184
|
+
);
|
|
1185
|
+
} else {
|
|
1186
|
+
expanded.push(intersection[0], intersection[1]);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return expanded;
|
|
1190
|
+
}
|
|
1191
|
+
function computeProjectiveQuadCoefficients(q) {
|
|
1192
|
+
if (q.length !== 4 || !isConvexPolygonPoints(q)) return null;
|
|
1193
|
+
const [q0, q1, q2, q3] = q;
|
|
1194
|
+
const sx = q0[0] - q1[0] + q2[0] - q3[0];
|
|
1195
|
+
const sy = q0[1] - q1[1] + q2[1] - q3[1];
|
|
1196
|
+
const dx1 = q1[0] - q2[0];
|
|
1197
|
+
const dx2 = q3[0] - q2[0];
|
|
1198
|
+
const dy1 = q1[1] - q2[1];
|
|
1199
|
+
const dy2 = q3[1] - q2[1];
|
|
1200
|
+
const det = dx1 * dy2 - dy1 * dx2;
|
|
1201
|
+
if (Math.abs(det) <= BASIS_EPS) return null;
|
|
1202
|
+
const g = (sx * dy2 - sy * dx2) / det;
|
|
1203
|
+
const h = (dx1 * sy - dy1 * sx) / det;
|
|
1204
|
+
const weights = [1, 1 + g, 1 + g + h, 1 + h];
|
|
1205
|
+
if (weights.some((weight) => !Number.isFinite(weight) || weight <= PROJECTIVE_QUAD_DENOM_EPS)) {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
const minWeight = Math.min(...weights);
|
|
1209
|
+
const maxWeight = Math.max(...weights);
|
|
1210
|
+
if (maxWeight / minWeight > PROJECTIVE_QUAD_MAX_WEIGHT_RATIO) return null;
|
|
1211
|
+
return {
|
|
1212
|
+
g,
|
|
1213
|
+
h,
|
|
1214
|
+
w1: 1 + g,
|
|
1215
|
+
w3: 1 + h
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
function computeProjectiveQuadMatrix(screenPts, xAxis, yAxis, normal, tx, ty, tz) {
|
|
1219
|
+
if (screenPts.length !== 8) return null;
|
|
1220
|
+
const rawQ = [
|
|
1221
|
+
[screenPts[0], screenPts[1]],
|
|
1222
|
+
[screenPts[2], screenPts[3]],
|
|
1223
|
+
[screenPts[4], screenPts[5]],
|
|
1224
|
+
[screenPts[6], screenPts[7]]
|
|
1225
|
+
];
|
|
1226
|
+
if (!computeProjectiveQuadCoefficients(rawQ)) return null;
|
|
1227
|
+
const expandedPts = offsetConvexPolygonPoints(screenPts, PROJECTIVE_QUAD_BLEED);
|
|
1228
|
+
const q = [
|
|
1229
|
+
[expandedPts[0], expandedPts[1]],
|
|
1230
|
+
[expandedPts[2], expandedPts[3]],
|
|
1231
|
+
[expandedPts[4], expandedPts[5]],
|
|
1232
|
+
[expandedPts[6], expandedPts[7]]
|
|
1233
|
+
];
|
|
1234
|
+
const coeffs = computeProjectiveQuadCoefficients(q);
|
|
1235
|
+
if (!coeffs) return null;
|
|
1236
|
+
const { g, h, w1, w3 } = coeffs;
|
|
1237
|
+
const [q0, q1, , q3] = q;
|
|
1238
|
+
const toCssPoint = ([x, y]) => [
|
|
1239
|
+
tx + x * xAxis[0] + y * yAxis[0],
|
|
1240
|
+
ty + x * xAxis[1] + y * yAxis[1],
|
|
1241
|
+
tz + x * xAxis[2] + y * yAxis[2]
|
|
1242
|
+
];
|
|
1243
|
+
const p0 = toCssPoint(q0);
|
|
1244
|
+
const p1 = toCssPoint(q1);
|
|
1245
|
+
const p3 = toCssPoint(q3);
|
|
1246
|
+
const xCol = [
|
|
1247
|
+
p1[0] * w1 - p0[0],
|
|
1248
|
+
p1[1] * w1 - p0[1],
|
|
1249
|
+
p1[2] * w1 - p0[2]
|
|
1250
|
+
];
|
|
1251
|
+
const yCol = [
|
|
1252
|
+
p3[0] * w3 - p0[0],
|
|
1253
|
+
p3[1] * w3 - p0[1],
|
|
1254
|
+
p3[2] * w3 - p0[2]
|
|
1255
|
+
];
|
|
1256
|
+
const values = [
|
|
1257
|
+
xCol[0],
|
|
1258
|
+
xCol[1],
|
|
1259
|
+
xCol[2],
|
|
1260
|
+
g,
|
|
1261
|
+
yCol[0],
|
|
1262
|
+
yCol[1],
|
|
1263
|
+
yCol[2],
|
|
1264
|
+
h,
|
|
1265
|
+
normal[0],
|
|
1266
|
+
normal[1],
|
|
1267
|
+
normal[2],
|
|
1268
|
+
0,
|
|
1269
|
+
p0[0],
|
|
1270
|
+
p0[1],
|
|
1271
|
+
p0[2],
|
|
1272
|
+
1
|
|
1273
|
+
];
|
|
1274
|
+
for (let i = 0; i < 8; i += 1) values[i] /= SOLID_QUAD_CANONICAL_SIZE;
|
|
1275
|
+
return values.join(",");
|
|
1276
|
+
}
|
|
733
1277
|
function cssPoints(vertices, tile, elev) {
|
|
734
1278
|
return vertices.map((v) => [v[1] * tile, v[0] * tile, v[2] * elev]);
|
|
735
1279
|
}
|
|
1280
|
+
function pointKey(point) {
|
|
1281
|
+
return `${point[0]},${point[1]},${point[2]}`;
|
|
1282
|
+
}
|
|
1283
|
+
function edgeKey(a, b) {
|
|
1284
|
+
const ak = pointKey(a);
|
|
1285
|
+
const bk = pointKey(b);
|
|
1286
|
+
return ak < bk ? `${ak}|${bk}` : `${bk}|${ak}`;
|
|
1287
|
+
}
|
|
1288
|
+
function buildTextureEdgeRepairSets(polygons) {
|
|
1289
|
+
const edgeOwners = /* @__PURE__ */ new Map();
|
|
1290
|
+
for (let polygonIndex = 0; polygonIndex < polygons.length; polygonIndex++) {
|
|
1291
|
+
const vertices = polygons[polygonIndex].vertices;
|
|
1292
|
+
if (!vertices || vertices.length < 3 || !polygons[polygonIndex].texture) continue;
|
|
1293
|
+
for (let edgeIndex = 0; edgeIndex < vertices.length; edgeIndex++) {
|
|
1294
|
+
const key = edgeKey(vertices[edgeIndex], vertices[(edgeIndex + 1) % vertices.length]);
|
|
1295
|
+
const owner = { polygon: polygonIndex, edge: edgeIndex };
|
|
1296
|
+
const owners = edgeOwners.get(key);
|
|
1297
|
+
if (owners) owners.push(owner);
|
|
1298
|
+
else edgeOwners.set(key, [owner]);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
const repairEdges = polygons.map(() => /* @__PURE__ */ new Set());
|
|
1302
|
+
for (const owners of edgeOwners.values()) {
|
|
1303
|
+
if (owners.length < 2) continue;
|
|
1304
|
+
for (let i = 0; i < owners.length; i++) {
|
|
1305
|
+
for (let j = i + 1; j < owners.length; j++) {
|
|
1306
|
+
repairEdges[owners[i].polygon].add(owners[i].edge);
|
|
1307
|
+
repairEdges[owners[j].polygon].add(owners[j].edge);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return repairEdges.map((edges) => edges.size > 0 ? edges : void 0);
|
|
1312
|
+
}
|
|
736
1313
|
function computeSurfaceNormal(pts) {
|
|
737
1314
|
if (pts.length < 3) return null;
|
|
738
|
-
const p0 = pts[0]
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1315
|
+
const p0 = pts[0];
|
|
1316
|
+
const normal = [0, 0, 0];
|
|
1317
|
+
for (let i = 1; i + 1 < pts.length; i++) {
|
|
1318
|
+
const p1 = pts[i];
|
|
1319
|
+
const p2 = pts[i + 1];
|
|
1320
|
+
const e1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]];
|
|
1321
|
+
const e2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]];
|
|
1322
|
+
normal[0] -= e1[1] * e2[2] - e1[2] * e2[1];
|
|
1323
|
+
normal[1] -= e1[2] * e2[0] - e1[0] * e2[2];
|
|
1324
|
+
normal[2] -= e1[0] * e2[1] - e1[1] * e2[0];
|
|
1325
|
+
}
|
|
746
1326
|
const len = Math.hypot(normal[0], normal[1], normal[2]);
|
|
747
1327
|
if (len <= BASIS_EPS) return null;
|
|
748
1328
|
return [normal[0] / len, normal[1] / len, normal[2] / len];
|
|
@@ -757,7 +1337,7 @@ function crossVec(a, b) {
|
|
|
757
1337
|
a[0] * b[1] - a[1] * b[0]
|
|
758
1338
|
];
|
|
759
1339
|
}
|
|
760
|
-
function solidTriangleStyle(entry, textureLighting, pointerEvents) {
|
|
1340
|
+
function solidTriangleStyle(entry, textureLighting, pointerEvents, solidPaintDefaults) {
|
|
761
1341
|
if (!isSolidTrianglePlan(entry)) return null;
|
|
762
1342
|
const pts = cssPoints(entry.polygon.vertices, entry.tileSize, entry.layerElevation);
|
|
763
1343
|
const normal = computeSurfaceNormal(pts);
|
|
@@ -841,47 +1421,89 @@ function solidTriangleStyle(entry, textureLighting, pointerEvents) {
|
|
|
841
1421
|
}
|
|
842
1422
|
const left = Math.max(0, Math.min(baseLength, apexX));
|
|
843
1423
|
const right = Math.max(0, baseLength - left);
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
const rightPx = right + bleed;
|
|
847
|
-
const heightPx = height + bleed * 2;
|
|
848
|
-
const tx = cv[0] - leftPx * xAxis[0] - bleed * yAxis[0];
|
|
849
|
-
const ty = cv[1] - leftPx * xAxis[1] - bleed * yAxis[1];
|
|
850
|
-
const tz = cv[2] - leftPx * xAxis[2] - bleed * yAxis[2];
|
|
851
|
-
const matrix = formatMatrix3dValues([
|
|
852
|
-
xAxis[0],
|
|
853
|
-
xAxis[1],
|
|
854
|
-
xAxis[2],
|
|
855
|
-
0,
|
|
856
|
-
yAxis[0],
|
|
857
|
-
yAxis[1],
|
|
858
|
-
yAxis[2],
|
|
1424
|
+
const expanded = offsetConvexPolygonPoints([
|
|
1425
|
+
left,
|
|
859
1426
|
0,
|
|
860
|
-
normal[0],
|
|
861
|
-
normal[1],
|
|
862
|
-
normal[2],
|
|
863
1427
|
0,
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
]
|
|
1428
|
+
height,
|
|
1429
|
+
left + right,
|
|
1430
|
+
height
|
|
1431
|
+
], SOLID_TRIANGLE_BLEED);
|
|
1432
|
+
const apex2 = [expanded[0], expanded[1]];
|
|
1433
|
+
const baseLeft2 = [expanded[2], expanded[3]];
|
|
1434
|
+
const baseRight2 = [expanded[4], expanded[5]];
|
|
1435
|
+
const baseY = (baseLeft2[1] + baseRight2[1]) / 2;
|
|
1436
|
+
const leftPx = apex2[0] - baseLeft2[0];
|
|
1437
|
+
const rightPx = baseRight2[0] - apex2[0];
|
|
1438
|
+
const heightPx = baseY - apex2[1];
|
|
1439
|
+
if (leftPx <= BASIS_EPS || rightPx <= BASIS_EPS || heightPx <= BASIS_EPS || !Number.isFinite(leftPx + rightPx + heightPx)) {
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
869
1442
|
const dynamic = textureLighting === "dynamic";
|
|
870
1443
|
const base = parseHex(entry.polygon.color ?? "#cccccc");
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
borderBottomColor: dynamic ? void 0 : entry.shadedColor,
|
|
1444
|
+
const useDefaultDynamicColor = dynamic && rgbKey(base) === solidPaintDefaults?.dynamicColorKey;
|
|
1445
|
+
const sharedStyle = {
|
|
1446
|
+
color: dynamic || entry.shadedColor === solidPaintDefaults?.paintColor ? void 0 : entry.shadedColor,
|
|
875
1447
|
pointerEvents: pointerEvents === "none" ? "none" : void 0,
|
|
876
|
-
...dynamic ? {
|
|
1448
|
+
...dynamic && !useDefaultDynamicColor ? {
|
|
877
1449
|
["--pnx"]: normal[0].toFixed(4),
|
|
878
1450
|
["--pny"]: normal[1].toFixed(4),
|
|
879
1451
|
["--pnz"]: normal[2].toFixed(4),
|
|
880
1452
|
["--psr"]: (base.r / 255).toFixed(4),
|
|
881
1453
|
["--psg"]: (base.g / 255).toFixed(4),
|
|
882
1454
|
["--psb"]: (base.b / 255).toFixed(4)
|
|
1455
|
+
} : dynamic ? {
|
|
1456
|
+
["--pnx"]: normal[0].toFixed(4),
|
|
1457
|
+
["--pny"]: normal[1].toFixed(4),
|
|
1458
|
+
["--pnz"]: normal[2].toFixed(4)
|
|
883
1459
|
} : null
|
|
884
1460
|
};
|
|
1461
|
+
const worldPoint = ([x, y]) => [
|
|
1462
|
+
cv[0] + (x - left) * xAxis[0] + y * yAxis[0],
|
|
1463
|
+
cv[1] + (x - left) * xAxis[1] + y * yAxis[1],
|
|
1464
|
+
cv[2] + (x - left) * xAxis[2] + y * yAxis[2]
|
|
1465
|
+
];
|
|
1466
|
+
const apex = worldPoint(apex2);
|
|
1467
|
+
const baseLeft = worldPoint([baseLeft2[0], baseY]);
|
|
1468
|
+
const baseRight = worldPoint([baseRight2[0], baseY]);
|
|
1469
|
+
const halfBase = SOLID_TRIANGLE_CANONICAL_SIZE / 2;
|
|
1470
|
+
const xCol = [
|
|
1471
|
+
(baseRight[0] - baseLeft[0]) / SOLID_TRIANGLE_CANONICAL_SIZE,
|
|
1472
|
+
(baseRight[1] - baseLeft[1]) / SOLID_TRIANGLE_CANONICAL_SIZE,
|
|
1473
|
+
(baseRight[2] - baseLeft[2]) / SOLID_TRIANGLE_CANONICAL_SIZE
|
|
1474
|
+
];
|
|
1475
|
+
const txCol = [
|
|
1476
|
+
apex[0] - xCol[0] * halfBase,
|
|
1477
|
+
apex[1] - xCol[1] * halfBase,
|
|
1478
|
+
apex[2] - xCol[2] * halfBase
|
|
1479
|
+
];
|
|
1480
|
+
const yCol = [
|
|
1481
|
+
(baseLeft[0] - txCol[0]) / SOLID_TRIANGLE_CANONICAL_SIZE,
|
|
1482
|
+
(baseLeft[1] - txCol[1]) / SOLID_TRIANGLE_CANONICAL_SIZE,
|
|
1483
|
+
(baseLeft[2] - txCol[2]) / SOLID_TRIANGLE_CANONICAL_SIZE
|
|
1484
|
+
];
|
|
1485
|
+
const canonicalMatrix = formatMatrix3dValues([
|
|
1486
|
+
xCol[0],
|
|
1487
|
+
xCol[1],
|
|
1488
|
+
xCol[2],
|
|
1489
|
+
0,
|
|
1490
|
+
yCol[0],
|
|
1491
|
+
yCol[1],
|
|
1492
|
+
yCol[2],
|
|
1493
|
+
0,
|
|
1494
|
+
normal[0],
|
|
1495
|
+
normal[1],
|
|
1496
|
+
normal[2],
|
|
1497
|
+
0,
|
|
1498
|
+
txCol[0],
|
|
1499
|
+
txCol[1],
|
|
1500
|
+
txCol[2],
|
|
1501
|
+
1
|
|
1502
|
+
]);
|
|
1503
|
+
return {
|
|
1504
|
+
transform: `matrix3d(${canonicalMatrix})`,
|
|
1505
|
+
...sharedStyle
|
|
1506
|
+
};
|
|
885
1507
|
}
|
|
886
1508
|
function rgbToHex({ r, g, b }) {
|
|
887
1509
|
const f = (n) => Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, "0");
|
|
@@ -1024,6 +1646,15 @@ function expandClipPoints(points, amount) {
|
|
|
1024
1646
|
}
|
|
1025
1647
|
return expanded;
|
|
1026
1648
|
}
|
|
1649
|
+
function tracePolygonPath(ctx, x, y, points) {
|
|
1650
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
1651
|
+
const px = x + points[i];
|
|
1652
|
+
const py = y + points[i + 1];
|
|
1653
|
+
if (i === 0) ctx.moveTo(px, py);
|
|
1654
|
+
else ctx.lineTo(px, py);
|
|
1655
|
+
}
|
|
1656
|
+
ctx.closePath();
|
|
1657
|
+
}
|
|
1027
1658
|
function canvasToUrl(canvas) {
|
|
1028
1659
|
if (typeof canvas.toBlob === "function") {
|
|
1029
1660
|
return new Promise((resolve) => {
|
|
@@ -1067,6 +1698,176 @@ function drawImageUvSample(ctx, img, rect, x, y, width, height, atlasScale) {
|
|
|
1067
1698
|
setCssTransform(ctx, atlasScale);
|
|
1068
1699
|
ctx.drawImage(img, sx, sy, sw, sh, x, y, width, height);
|
|
1069
1700
|
}
|
|
1701
|
+
function traceOffsetPolygonPath(ctx, x, y, points, offsetX, offsetY) {
|
|
1702
|
+
for (let i = 0; i < points.length; i += 2) {
|
|
1703
|
+
const px = x + points[i] + offsetX;
|
|
1704
|
+
const py = y + points[i + 1] + offsetY;
|
|
1705
|
+
if (i === 0) ctx.moveTo(px, py);
|
|
1706
|
+
else ctx.lineTo(px, py);
|
|
1707
|
+
}
|
|
1708
|
+
ctx.closePath();
|
|
1709
|
+
}
|
|
1710
|
+
function drawTexturedAtlasEntry(ctx, entry, srcImg, atlasScale, offsetX = 0, offsetY = 0) {
|
|
1711
|
+
if (entry.textureTriangles?.length) {
|
|
1712
|
+
const imgW = srcImg.naturalWidth || srcImg.width || 1;
|
|
1713
|
+
const imgH = srcImg.naturalHeight || srcImg.height || 1;
|
|
1714
|
+
for (const triangle of entry.textureTriangles) {
|
|
1715
|
+
const clipPts = expandClipPoints(triangle.screenPts, TEXTURE_TRIANGLE_BLEED);
|
|
1716
|
+
ctx.save();
|
|
1717
|
+
setCssTransform(ctx, atlasScale);
|
|
1718
|
+
ctx.beginPath();
|
|
1719
|
+
traceOffsetPolygonPath(ctx, entry.x, entry.y, clipPts, offsetX, offsetY);
|
|
1720
|
+
ctx.clip();
|
|
1721
|
+
if (triangle.uvAffine) {
|
|
1722
|
+
setCssTransform(
|
|
1723
|
+
ctx,
|
|
1724
|
+
atlasScale,
|
|
1725
|
+
triangle.uvAffine.a / imgW,
|
|
1726
|
+
triangle.uvAffine.c / imgW,
|
|
1727
|
+
triangle.uvAffine.b / imgH,
|
|
1728
|
+
triangle.uvAffine.d / imgH,
|
|
1729
|
+
entry.x + triangle.uvAffine.e + offsetX,
|
|
1730
|
+
entry.y + triangle.uvAffine.f + offsetY
|
|
1731
|
+
);
|
|
1732
|
+
ctx.drawImage(srcImg, 0, 0);
|
|
1733
|
+
} else if (triangle.uvSampleRect) {
|
|
1734
|
+
drawImageUvSample(
|
|
1735
|
+
ctx,
|
|
1736
|
+
srcImg,
|
|
1737
|
+
triangle.uvSampleRect,
|
|
1738
|
+
entry.x + offsetX,
|
|
1739
|
+
entry.y + offsetY,
|
|
1740
|
+
entry.canvasW,
|
|
1741
|
+
entry.canvasH,
|
|
1742
|
+
atlasScale
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
ctx.restore();
|
|
1746
|
+
}
|
|
1747
|
+
} else if (entry.uvAffine) {
|
|
1748
|
+
const imgW = srcImg.naturalWidth || srcImg.width || 1;
|
|
1749
|
+
const imgH = srcImg.naturalHeight || srcImg.height || 1;
|
|
1750
|
+
setCssTransform(
|
|
1751
|
+
ctx,
|
|
1752
|
+
atlasScale,
|
|
1753
|
+
entry.uvAffine.a / imgW,
|
|
1754
|
+
entry.uvAffine.c / imgW,
|
|
1755
|
+
entry.uvAffine.b / imgH,
|
|
1756
|
+
entry.uvAffine.d / imgH,
|
|
1757
|
+
entry.x + entry.uvAffine.e + offsetX,
|
|
1758
|
+
entry.y + entry.uvAffine.f + offsetY
|
|
1759
|
+
);
|
|
1760
|
+
ctx.drawImage(srcImg, 0, 0);
|
|
1761
|
+
} else if (entry.uvSampleRect) {
|
|
1762
|
+
drawImageUvSample(
|
|
1763
|
+
ctx,
|
|
1764
|
+
srcImg,
|
|
1765
|
+
entry.uvSampleRect,
|
|
1766
|
+
entry.x + offsetX,
|
|
1767
|
+
entry.y + offsetY,
|
|
1768
|
+
entry.canvasW,
|
|
1769
|
+
entry.canvasH,
|
|
1770
|
+
atlasScale
|
|
1771
|
+
);
|
|
1772
|
+
} else {
|
|
1773
|
+
drawImageCover(
|
|
1774
|
+
ctx,
|
|
1775
|
+
srcImg,
|
|
1776
|
+
entry.x + offsetX,
|
|
1777
|
+
entry.y + offsetY,
|
|
1778
|
+
entry.canvasW,
|
|
1779
|
+
entry.canvasH,
|
|
1780
|
+
atlasScale
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
function distanceToSegment(px, py, ax, ay, bx, by) {
|
|
1785
|
+
const dx = bx - ax;
|
|
1786
|
+
const dy = by - ay;
|
|
1787
|
+
const lenSq = dx * dx + dy * dy;
|
|
1788
|
+
if (lenSq <= BASIS_EPS) return Math.hypot(px - ax, py - ay);
|
|
1789
|
+
const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lenSq));
|
|
1790
|
+
return Math.hypot(px - (ax + dx * t), py - (ay + dy * t));
|
|
1791
|
+
}
|
|
1792
|
+
function distanceToPolygonEdges(px, py, points, edgeIndices) {
|
|
1793
|
+
let best = Infinity;
|
|
1794
|
+
const count = points.length / 2;
|
|
1795
|
+
for (const edgeIndex of edgeIndices) {
|
|
1796
|
+
if (edgeIndex < 0 || edgeIndex >= count) continue;
|
|
1797
|
+
const i = edgeIndex * 2;
|
|
1798
|
+
const next = (edgeIndex + 1) % count * 2;
|
|
1799
|
+
best = Math.min(
|
|
1800
|
+
best,
|
|
1801
|
+
distanceToSegment(px, py, points[i], points[i + 1], points[next], points[next + 1])
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
return best;
|
|
1805
|
+
}
|
|
1806
|
+
function nearestOpaquePixelOffset(data, width, height, x, y, radius) {
|
|
1807
|
+
const minX = Math.max(0, x - radius);
|
|
1808
|
+
const maxX = Math.min(width - 1, x + radius);
|
|
1809
|
+
const minY = Math.max(0, y - radius);
|
|
1810
|
+
const maxY = Math.min(height - 1, y + radius);
|
|
1811
|
+
let bestOffset = null;
|
|
1812
|
+
let bestDistanceSq = Infinity;
|
|
1813
|
+
for (let yy = minY; yy <= maxY; yy++) {
|
|
1814
|
+
for (let xx = minX; xx <= maxX; xx++) {
|
|
1815
|
+
if (xx === x && yy === y) continue;
|
|
1816
|
+
const dx = xx - x;
|
|
1817
|
+
const dy = yy - y;
|
|
1818
|
+
const distanceSq = dx * dx + dy * dy;
|
|
1819
|
+
if (distanceSq > radius * radius || distanceSq >= bestDistanceSq) continue;
|
|
1820
|
+
const offset = (yy * width + xx) * 4;
|
|
1821
|
+
if (data[offset + 3] < TEXTURE_EDGE_REPAIR_SOURCE_ALPHA_MIN) continue;
|
|
1822
|
+
bestOffset = offset;
|
|
1823
|
+
bestDistanceSq = distanceSq;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
return bestOffset;
|
|
1827
|
+
}
|
|
1828
|
+
function repairTextureEdgeAlpha(ctx, entry, atlasScale) {
|
|
1829
|
+
if (!entry.textureEdgeRepair || !entry.texture) return;
|
|
1830
|
+
if (!entry.textureEdgeRepairEdges || entry.textureEdgeRepairEdges.size === 0) return;
|
|
1831
|
+
const canvas = ctx.canvas;
|
|
1832
|
+
if (!canvas) return;
|
|
1833
|
+
const pixelX = Math.max(0, Math.floor(entry.x * atlasScale));
|
|
1834
|
+
const pixelY = Math.max(0, Math.floor(entry.y * atlasScale));
|
|
1835
|
+
const pixelW = Math.max(1, Math.min(canvas.width - pixelX, Math.ceil(entry.canvasW * atlasScale)));
|
|
1836
|
+
const pixelH = Math.max(1, Math.min(canvas.height - pixelY, Math.ceil(entry.canvasH * atlasScale)));
|
|
1837
|
+
if (pixelW <= 0 || pixelH <= 0) return;
|
|
1838
|
+
let imageData;
|
|
1839
|
+
try {
|
|
1840
|
+
imageData = ctx.getImageData(pixelX, pixelY, pixelW, pixelH);
|
|
1841
|
+
} catch {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const data = imageData.data;
|
|
1845
|
+
const source = new Uint8ClampedArray(data);
|
|
1846
|
+
const radius = Math.max(TEXTURE_EDGE_REPAIR_RADIUS, TEXTURE_EDGE_REPAIR_RADIUS / atlasScale);
|
|
1847
|
+
const sourceRadius = Math.max(2, Math.ceil(radius * atlasScale) + 1);
|
|
1848
|
+
let changed = false;
|
|
1849
|
+
for (let y = 0; y < pixelH; y++) {
|
|
1850
|
+
for (let x = 0; x < pixelW; x++) {
|
|
1851
|
+
const offset = (y * pixelW + x) * 4;
|
|
1852
|
+
const alpha = data[offset + 3];
|
|
1853
|
+
if (alpha < TEXTURE_EDGE_REPAIR_ALPHA_MIN || alpha === 255) continue;
|
|
1854
|
+
const localX = (pixelX + x + 0.5) / atlasScale - entry.x;
|
|
1855
|
+
const localY = (pixelY + y + 0.5) / atlasScale - entry.y;
|
|
1856
|
+
if (distanceToPolygonEdges(localX, localY, entry.screenPts, entry.textureEdgeRepairEdges) > radius) {
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
const sourceOffset = nearestOpaquePixelOffset(source, pixelW, pixelH, x, y, sourceRadius);
|
|
1860
|
+
if (sourceOffset === null) continue;
|
|
1861
|
+
data[offset] = source[sourceOffset];
|
|
1862
|
+
data[offset + 1] = source[sourceOffset + 1];
|
|
1863
|
+
data[offset + 2] = source[sourceOffset + 2];
|
|
1864
|
+
data[offset + 3] = 255;
|
|
1865
|
+
changed = true;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
if (!changed) return;
|
|
1869
|
+
ctx.putImageData(imageData, pixelX, pixelY);
|
|
1870
|
+
}
|
|
1070
1871
|
function computeTextureAtlasPlan(polygon, index, options = {}) {
|
|
1071
1872
|
const { vertices, texture, uvs } = polygon;
|
|
1072
1873
|
if (!vertices || vertices.length < 3) return null;
|
|
@@ -1113,18 +1914,20 @@ function computeTextureAtlasPlan(polygon, index, options = {}) {
|
|
|
1113
1914
|
if (y < yMin) yMin = y;
|
|
1114
1915
|
if (y > yMax) yMax = y;
|
|
1115
1916
|
}
|
|
1116
|
-
const shiftX = -xMin;
|
|
1117
|
-
const shiftY = -yMin;
|
|
1118
1917
|
const w = xMax - xMin;
|
|
1119
1918
|
const h = yMax - yMin;
|
|
1120
1919
|
if (!Number.isFinite(w) || !Number.isFinite(h)) return null;
|
|
1920
|
+
const textureEdgeRepairEdges = texture && options.textureEdgeRepairEdges?.size ? options.textureEdgeRepairEdges : null;
|
|
1921
|
+
const textureEdgeRepair = Boolean(texture && textureEdgeRepairEdges);
|
|
1922
|
+
const shiftX = -xMin;
|
|
1923
|
+
const shiftY = -yMin;
|
|
1121
1924
|
const screenPts = [];
|
|
1122
1925
|
for (let i = 0; i < local2D.length; i++) {
|
|
1123
1926
|
const [x, y] = local2D[i];
|
|
1124
|
-
|
|
1125
|
-
const sy = y + shiftY;
|
|
1126
|
-
screenPts.push(sx, sy);
|
|
1927
|
+
screenPts.push(x + shiftX, y + shiftY);
|
|
1127
1928
|
}
|
|
1929
|
+
const canvasW = Math.max(1, Math.ceil(w));
|
|
1930
|
+
const canvasH = Math.max(1, Math.ceil(h));
|
|
1128
1931
|
const tx = p0[0] - shiftX * xAxis[0] - shiftY * yAxis[0];
|
|
1129
1932
|
const ty = p0[1] - shiftX * xAxis[1] - shiftY * yAxis[1];
|
|
1130
1933
|
const tz = p0[2] - shiftX * xAxis[2] - shiftY * yAxis[2];
|
|
@@ -1146,6 +1949,44 @@ function computeTextureAtlasPlan(polygon, index, options = {}) {
|
|
|
1146
1949
|
tz,
|
|
1147
1950
|
1
|
|
1148
1951
|
].join(",");
|
|
1952
|
+
const canonicalMatrix = [
|
|
1953
|
+
xAxis[0] * canvasW,
|
|
1954
|
+
xAxis[1] * canvasW,
|
|
1955
|
+
xAxis[2] * canvasW,
|
|
1956
|
+
0,
|
|
1957
|
+
yAxis[0] * canvasH,
|
|
1958
|
+
yAxis[1] * canvasH,
|
|
1959
|
+
yAxis[2] * canvasH,
|
|
1960
|
+
0,
|
|
1961
|
+
nx,
|
|
1962
|
+
ny,
|
|
1963
|
+
nz,
|
|
1964
|
+
0,
|
|
1965
|
+
tx,
|
|
1966
|
+
ty,
|
|
1967
|
+
tz,
|
|
1968
|
+
1
|
|
1969
|
+
].join(",");
|
|
1970
|
+
const atlasMatrix = [
|
|
1971
|
+
xAxis[0] * canvasW / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1972
|
+
xAxis[1] * canvasW / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1973
|
+
xAxis[2] * canvasW / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1974
|
+
0,
|
|
1975
|
+
yAxis[0] * canvasH / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1976
|
+
yAxis[1] * canvasH / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1977
|
+
yAxis[2] * canvasH / ATLAS_CANONICAL_SIZE_EXPLICIT,
|
|
1978
|
+
0,
|
|
1979
|
+
nx,
|
|
1980
|
+
ny,
|
|
1981
|
+
nz,
|
|
1982
|
+
0,
|
|
1983
|
+
tx,
|
|
1984
|
+
ty,
|
|
1985
|
+
tz,
|
|
1986
|
+
1
|
|
1987
|
+
].join(",");
|
|
1988
|
+
const normal = [nx, ny, nz];
|
|
1989
|
+
const projectiveMatrix = !texture && vertices.length === 4 ? computeProjectiveQuadMatrix(screenPts, xAxis, yAxis, normal, tx, ty, tz) : null;
|
|
1149
1990
|
const directionalCfg = options.directionalLight;
|
|
1150
1991
|
const ambientCfg = options.ambientLight;
|
|
1151
1992
|
const lightDir = directionalCfg?.direction ?? DEFAULT_LIGHT_DIR;
|
|
@@ -1177,13 +2018,18 @@ function computeTextureAtlasPlan(polygon, index, options = {}) {
|
|
|
1177
2018
|
tileSize: tile,
|
|
1178
2019
|
layerElevation: elev,
|
|
1179
2020
|
matrix,
|
|
1180
|
-
|
|
1181
|
-
|
|
2021
|
+
canonicalMatrix,
|
|
2022
|
+
atlasMatrix,
|
|
2023
|
+
projectiveMatrix,
|
|
2024
|
+
canvasW,
|
|
2025
|
+
canvasH,
|
|
1182
2026
|
screenPts,
|
|
1183
2027
|
uvAffine,
|
|
1184
2028
|
uvSampleRect,
|
|
1185
2029
|
textureTriangles,
|
|
1186
|
-
|
|
2030
|
+
textureEdgeRepairEdges,
|
|
2031
|
+
textureEdgeRepair,
|
|
2032
|
+
normal,
|
|
1187
2033
|
textureTint,
|
|
1188
2034
|
shadedColor
|
|
1189
2035
|
};
|
|
@@ -1267,7 +2113,10 @@ async function buildAtlasPage(page, textureLighting, doc, atlasScale) {
|
|
|
1267
2113
|
const canvas = doc.createElement("canvas");
|
|
1268
2114
|
canvas.width = Math.max(1, Math.ceil(page.width * atlasScale));
|
|
1269
2115
|
canvas.height = Math.max(1, Math.ceil(page.height * atlasScale));
|
|
1270
|
-
const
|
|
2116
|
+
const needsReadback = page.entries.some(
|
|
2117
|
+
(entry) => entry.textureEdgeRepair && entry.texture && entry.textureEdgeRepairEdges && entry.textureEdgeRepairEdges.size > 0
|
|
2118
|
+
);
|
|
2119
|
+
const ctx = canvas.getContext("2d", needsReadback ? { willReadFrequently: true } : void 0);
|
|
1271
2120
|
if (!ctx) return { width: page.width, height: page.height, url: null };
|
|
1272
2121
|
const uniqueTextures = Array.from(new Set(
|
|
1273
2122
|
page.entries.flatMap((entry) => entry.texture ? [entry.texture] : [])
|
|
@@ -1278,97 +2127,36 @@ async function buildAtlasPage(page, textureLighting, doc, atlasScale) {
|
|
|
1278
2127
|
}));
|
|
1279
2128
|
for (const entry of page.entries) {
|
|
1280
2129
|
const srcImg = entry.texture ? loaded.get(entry.texture) : null;
|
|
1281
|
-
ctx.save();
|
|
1282
|
-
setCssTransform(ctx, atlasScale);
|
|
1283
|
-
ctx.beginPath();
|
|
1284
|
-
for (let i = 0; i < entry.screenPts.length; i += 2) {
|
|
1285
|
-
const px = entry.x + entry.screenPts[i];
|
|
1286
|
-
const py = entry.y + entry.screenPts[i + 1];
|
|
1287
|
-
if (i === 0) ctx.moveTo(px, py);
|
|
1288
|
-
else ctx.lineTo(px, py);
|
|
1289
|
-
}
|
|
1290
|
-
ctx.closePath();
|
|
1291
|
-
ctx.clip();
|
|
1292
2130
|
if (!entry.texture) {
|
|
2131
|
+
ctx.save();
|
|
1293
2132
|
setCssTransform(ctx, atlasScale);
|
|
2133
|
+
ctx.beginPath();
|
|
2134
|
+
tracePolygonPath(ctx, entry.x, entry.y, entry.screenPts);
|
|
2135
|
+
ctx.clip();
|
|
1294
2136
|
ctx.fillStyle = textureLighting === "dynamic" ? entry.polygon.color ?? "#cccccc" : entry.shadedColor;
|
|
1295
2137
|
ctx.fillRect(entry.x, entry.y, entry.canvasW, entry.canvasH);
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
const py = entry.y + clipPts[i + 1];
|
|
1308
|
-
if (i === 0) ctx.moveTo(px, py);
|
|
1309
|
-
else ctx.lineTo(px, py);
|
|
1310
|
-
}
|
|
1311
|
-
ctx.closePath();
|
|
1312
|
-
ctx.clip();
|
|
1313
|
-
if (triangle.uvAffine) {
|
|
1314
|
-
setCssTransform(
|
|
1315
|
-
ctx,
|
|
1316
|
-
atlasScale,
|
|
1317
|
-
triangle.uvAffine.a / imgW,
|
|
1318
|
-
triangle.uvAffine.c / imgW,
|
|
1319
|
-
triangle.uvAffine.b / imgH,
|
|
1320
|
-
triangle.uvAffine.d / imgH,
|
|
1321
|
-
entry.x + triangle.uvAffine.e,
|
|
1322
|
-
entry.y + triangle.uvAffine.f
|
|
1323
|
-
);
|
|
1324
|
-
ctx.drawImage(srcImg, 0, 0);
|
|
1325
|
-
} else if (triangle.uvSampleRect) {
|
|
1326
|
-
drawImageUvSample(
|
|
1327
|
-
ctx,
|
|
1328
|
-
srcImg,
|
|
1329
|
-
triangle.uvSampleRect,
|
|
1330
|
-
entry.x,
|
|
1331
|
-
entry.y,
|
|
1332
|
-
entry.canvasW,
|
|
1333
|
-
entry.canvasH,
|
|
1334
|
-
atlasScale
|
|
1335
|
-
);
|
|
1336
|
-
}
|
|
1337
|
-
ctx.restore();
|
|
1338
|
-
}
|
|
1339
|
-
} else if (srcImg && entry.uvAffine) {
|
|
1340
|
-
const imgW = srcImg.naturalWidth || srcImg.width || 1;
|
|
1341
|
-
const imgH = srcImg.naturalHeight || srcImg.height || 1;
|
|
1342
|
-
setCssTransform(
|
|
1343
|
-
ctx,
|
|
1344
|
-
atlasScale,
|
|
1345
|
-
entry.uvAffine.a / imgW,
|
|
1346
|
-
entry.uvAffine.c / imgW,
|
|
1347
|
-
entry.uvAffine.b / imgH,
|
|
1348
|
-
entry.uvAffine.d / imgH,
|
|
1349
|
-
entry.x + entry.uvAffine.e,
|
|
1350
|
-
entry.y + entry.uvAffine.f
|
|
1351
|
-
);
|
|
1352
|
-
ctx.drawImage(srcImg, 0, 0);
|
|
1353
|
-
} else if (srcImg && entry.uvSampleRect) {
|
|
1354
|
-
drawImageUvSample(
|
|
1355
|
-
ctx,
|
|
1356
|
-
srcImg,
|
|
1357
|
-
entry.uvSampleRect,
|
|
1358
|
-
entry.x,
|
|
1359
|
-
entry.y,
|
|
1360
|
-
entry.canvasW,
|
|
1361
|
-
entry.canvasH,
|
|
1362
|
-
atlasScale
|
|
1363
|
-
);
|
|
1364
|
-
} else if (srcImg) {
|
|
1365
|
-
drawImageCover(ctx, srcImg, entry.x, entry.y, entry.canvasW, entry.canvasH, atlasScale);
|
|
1366
|
-
}
|
|
2138
|
+
ctx.restore();
|
|
2139
|
+
continue;
|
|
2140
|
+
}
|
|
2141
|
+
if (srcImg) {
|
|
2142
|
+
ctx.save();
|
|
2143
|
+
setCssTransform(ctx, atlasScale);
|
|
2144
|
+
ctx.beginPath();
|
|
2145
|
+
tracePolygonPath(ctx, entry.x, entry.y, entry.screenPts);
|
|
2146
|
+
ctx.clip();
|
|
2147
|
+
drawTexturedAtlasEntry(ctx, entry, srcImg, atlasScale);
|
|
2148
|
+
ctx.restore();
|
|
1367
2149
|
}
|
|
1368
2150
|
if (entry.texture && textureLighting === "baked") {
|
|
2151
|
+
ctx.save();
|
|
2152
|
+
setCssTransform(ctx, atlasScale);
|
|
2153
|
+
ctx.beginPath();
|
|
2154
|
+
tracePolygonPath(ctx, entry.x, entry.y, entry.screenPts);
|
|
2155
|
+
ctx.clip();
|
|
1369
2156
|
applyTextureTint(ctx, entry.x, entry.y, entry.canvasW, entry.canvasH, entry.textureTint, atlasScale);
|
|
2157
|
+
ctx.restore();
|
|
1370
2158
|
}
|
|
1371
|
-
ctx
|
|
2159
|
+
repairTextureEdgeAlpha(ctx, entry, atlasScale);
|
|
1372
2160
|
}
|
|
1373
2161
|
const url = await canvasToUrl(canvas);
|
|
1374
2162
|
canvas.width = 1;
|
|
@@ -1387,21 +2175,32 @@ async function buildAtlasPages(pages, textureLighting, doc, atlasScale, isCancel
|
|
|
1387
2175
|
}
|
|
1388
2176
|
return built;
|
|
1389
2177
|
}
|
|
1390
|
-
function useTextureAtlas(plans, textureLighting,
|
|
1391
|
-
const
|
|
2178
|
+
function useTextureAtlas(plans, textureLighting, textureQualityInput, strategies) {
|
|
2179
|
+
const disableB = strategies?.disable?.includes("b") ?? false;
|
|
2180
|
+
const disableI = strategies?.disable?.includes("i") ?? false;
|
|
2181
|
+
const disableU = strategies?.disable?.includes("u") ?? false;
|
|
2182
|
+
const useFullRectSolid = !disableB;
|
|
2183
|
+
const useProjectiveQuad = useFullRectSolid;
|
|
2184
|
+
const useStableTriangle = !disableU && solidTriangleSupported();
|
|
2185
|
+
const useBorderShape = !disableI && textureLighting !== "dynamic" && borderShapeSupported();
|
|
1392
2186
|
const atlasPlans = (0, import_react7.useMemo)(
|
|
1393
2187
|
() => plans.map((plan) => {
|
|
1394
2188
|
if (!plan) return plan;
|
|
1395
2189
|
if (plan.texture) return plan;
|
|
1396
|
-
if (isSolidTrianglePlan(plan)) return null;
|
|
1397
|
-
|
|
2190
|
+
if (useStableTriangle && isSolidTrianglePlan(plan)) return null;
|
|
2191
|
+
const fullRect = isFullRectSolid(plan);
|
|
2192
|
+
if (useFullRectSolid && fullRect || useProjectiveQuad && isProjectiveQuadPlan(plan) || textureLighting !== "dynamic" && useBorderShape && (!fullRect || disableB)) return null;
|
|
1398
2193
|
return plan;
|
|
1399
2194
|
}),
|
|
1400
|
-
[plans, textureLighting, useBorderShape]
|
|
2195
|
+
[plans, textureLighting, useFullRectSolid, useProjectiveQuad, useStableTriangle, useBorderShape]
|
|
1401
2196
|
);
|
|
1402
2197
|
const { packed, atlasScale } = (0, import_react7.useMemo)(
|
|
1403
|
-
() => packTextureAtlasPlansWithScale(
|
|
1404
|
-
|
|
2198
|
+
() => packTextureAtlasPlansWithScale(
|
|
2199
|
+
atlasPlans,
|
|
2200
|
+
textureQualityInput,
|
|
2201
|
+
typeof document !== "undefined" ? document : null
|
|
2202
|
+
),
|
|
2203
|
+
[atlasPlans, textureQualityInput]
|
|
1405
2204
|
);
|
|
1406
2205
|
const [pages, setPages] = (0, import_react7.useState)(
|
|
1407
2206
|
() => packed.pages.map((page) => ({ width: page.width, height: page.height, url: null }))
|
|
@@ -1441,31 +2240,34 @@ function useTextureAtlas(plans, textureLighting, atlasScaleInput) {
|
|
|
1441
2240
|
}
|
|
1442
2241
|
function TextureBorderShapePoly({
|
|
1443
2242
|
entry,
|
|
2243
|
+
solidPaintDefaults,
|
|
1444
2244
|
className,
|
|
1445
2245
|
style: styleProp,
|
|
1446
2246
|
domAttrs,
|
|
1447
2247
|
domEventHandlers,
|
|
1448
|
-
pointerEvents = "auto"
|
|
2248
|
+
pointerEvents = "auto",
|
|
2249
|
+
disabledStrategies
|
|
1449
2250
|
}) {
|
|
1450
2251
|
const fullRect = isFullRectSolid(entry);
|
|
1451
|
-
const
|
|
2252
|
+
const bDisabled = disabledStrategies?.has("b") ?? false;
|
|
2253
|
+
const useIForFullRect = bDisabled && borderShapeSupported();
|
|
2254
|
+
const borderShape = !fullRect || useIForFullRect ? cssBorderShapeForPlan(entry) : null;
|
|
2255
|
+
const useDefaultPaint = entry.shadedColor === solidPaintDefaults?.paintColor;
|
|
1452
2256
|
const setElementRef = (0, import_react7.useCallback)((el) => {
|
|
1453
2257
|
if (!el) return;
|
|
1454
2258
|
if (borderShape) el.style.setProperty("border-shape", borderShape);
|
|
1455
2259
|
else el.style.removeProperty("border-shape");
|
|
2260
|
+
orderBrushInlineStyle(el);
|
|
1456
2261
|
}, [borderShape]);
|
|
2262
|
+
const transform = formatMatrix3d(borderShape ? formatBorderShapeMatrix(entry) : formatSolidQuadMatrix(entry));
|
|
1457
2263
|
const style = fullRect ? {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
transform: formatMatrix3d(entry.matrix),
|
|
1461
|
-
background: entry.shadedColor,
|
|
2264
|
+
transform,
|
|
2265
|
+
color: useDefaultPaint ? void 0 : entry.shadedColor,
|
|
1462
2266
|
pointerEvents: pointerEvents === "none" ? "none" : void 0,
|
|
1463
2267
|
...styleProp
|
|
1464
2268
|
} : {
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
transform: formatMatrix3d(entry.matrix),
|
|
1468
|
-
color: entry.shadedColor,
|
|
2269
|
+
transform,
|
|
2270
|
+
color: useDefaultPaint ? void 0 : entry.shadedColor,
|
|
1469
2271
|
pointerEvents: pointerEvents === "none" ? "none" : void 0,
|
|
1470
2272
|
...styleProp
|
|
1471
2273
|
};
|
|
@@ -1473,7 +2275,7 @@ function TextureBorderShapePoly({
|
|
|
1473
2275
|
Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
|
|
1474
2276
|
) : {};
|
|
1475
2277
|
const elementClassName = className?.trim() || void 0;
|
|
1476
|
-
if (fullRect) {
|
|
2278
|
+
if (fullRect && !useIForFullRect) {
|
|
1477
2279
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1478
2280
|
"b",
|
|
1479
2281
|
{
|
|
@@ -1497,16 +2299,63 @@ function TextureBorderShapePoly({
|
|
|
1497
2299
|
}
|
|
1498
2300
|
);
|
|
1499
2301
|
}
|
|
2302
|
+
function TextureProjectiveSolidPoly({
|
|
2303
|
+
entry,
|
|
2304
|
+
textureLighting,
|
|
2305
|
+
solidPaintDefaults,
|
|
2306
|
+
className,
|
|
2307
|
+
style: styleProp,
|
|
2308
|
+
domAttrs,
|
|
2309
|
+
domEventHandlers,
|
|
2310
|
+
pointerEvents = "auto"
|
|
2311
|
+
}) {
|
|
2312
|
+
const dynamic = textureLighting === "dynamic";
|
|
2313
|
+
const base = parseHex(entry.polygon.color ?? "#cccccc");
|
|
2314
|
+
const useDefaultDynamicColor = dynamic && rgbKey(base) === solidPaintDefaults?.dynamicColorKey;
|
|
2315
|
+
const style = {
|
|
2316
|
+
transform: formatMatrix3d(entry.projectiveMatrix),
|
|
2317
|
+
color: dynamic || entry.shadedColor === solidPaintDefaults?.paintColor ? void 0 : entry.shadedColor,
|
|
2318
|
+
pointerEvents: pointerEvents === "none" ? "none" : void 0,
|
|
2319
|
+
...dynamic && !useDefaultDynamicColor ? {
|
|
2320
|
+
["--pnx"]: entry.normal[0].toFixed(4),
|
|
2321
|
+
["--pny"]: entry.normal[1].toFixed(4),
|
|
2322
|
+
["--pnz"]: entry.normal[2].toFixed(4),
|
|
2323
|
+
["--psr"]: (base.r / 255).toFixed(4),
|
|
2324
|
+
["--psg"]: (base.g / 255).toFixed(4),
|
|
2325
|
+
["--psb"]: (base.b / 255).toFixed(4)
|
|
2326
|
+
} : dynamic ? {
|
|
2327
|
+
["--pnx"]: entry.normal[0].toFixed(4),
|
|
2328
|
+
["--pny"]: entry.normal[1].toFixed(4),
|
|
2329
|
+
["--pnz"]: entry.normal[2].toFixed(4)
|
|
2330
|
+
} : null,
|
|
2331
|
+
...styleProp
|
|
2332
|
+
};
|
|
2333
|
+
const dataAttrs = entry.polygon.data ? Object.fromEntries(
|
|
2334
|
+
Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
|
|
2335
|
+
) : {};
|
|
2336
|
+
const elementClassName = className?.trim() || void 0;
|
|
2337
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2338
|
+
"b",
|
|
2339
|
+
{
|
|
2340
|
+
className: elementClassName,
|
|
2341
|
+
style,
|
|
2342
|
+
...domEventHandlers,
|
|
2343
|
+
...dataAttrs,
|
|
2344
|
+
...domAttrs
|
|
2345
|
+
}
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
1500
2348
|
function TextureTrianglePoly({
|
|
1501
2349
|
entry,
|
|
1502
2350
|
textureLighting,
|
|
2351
|
+
solidPaintDefaults,
|
|
1503
2352
|
className,
|
|
1504
2353
|
style: styleProp,
|
|
1505
2354
|
domAttrs,
|
|
1506
2355
|
domEventHandlers,
|
|
1507
2356
|
pointerEvents = "auto"
|
|
1508
2357
|
}) {
|
|
1509
|
-
const triangleStyle = solidTriangleStyle(entry, textureLighting, pointerEvents);
|
|
2358
|
+
const triangleStyle = solidTriangleStyle(entry, textureLighting, pointerEvents, solidPaintDefaults);
|
|
1510
2359
|
if (!triangleStyle) return null;
|
|
1511
2360
|
const dataAttrs = entry.polygon.data ? Object.fromEntries(
|
|
1512
2361
|
Object.entries(entry.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
|
|
@@ -1534,16 +2383,20 @@ function TextureAtlasPoly({
|
|
|
1534
2383
|
pointerEvents = "auto"
|
|
1535
2384
|
}) {
|
|
1536
2385
|
const dynamic = textureLighting === "dynamic";
|
|
2386
|
+
const atlasCanonicalSize = entry.atlasCanonicalSize ?? ATLAS_CANONICAL_SIZE_EXPLICIT;
|
|
2387
|
+
const atlasWidth = entry.canvasW || 1;
|
|
2388
|
+
const atlasHeight = entry.canvasH || 1;
|
|
2389
|
+
const atlasPosition = page ? `${formatCssLength(-entry.x / atlasWidth * atlasCanonicalSize)} ${formatCssLength(-entry.y / atlasHeight * atlasCanonicalSize)}` : void 0;
|
|
2390
|
+
const atlasSize = page ? `${formatCssLength(page.width / atlasWidth * atlasCanonicalSize)} ${formatCssLength(page.height / atlasHeight * atlasCanonicalSize)}` : void 0;
|
|
1537
2391
|
const dynamicMask = dynamic && page?.url ? `url(${page.url})` : void 0;
|
|
1538
|
-
const background = !dynamic && page?.url ? `url(${page.url})
|
|
2392
|
+
const background = !dynamic && page?.url ? `url(${page.url}) ${atlasPosition} / ${atlasSize} no-repeat` : void 0;
|
|
1539
2393
|
const style = {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
transform: formatMatrix3d(entry.matrix),
|
|
2394
|
+
transform: formatMatrix3d(entry.atlasMatrix),
|
|
2395
|
+
["--polycss-atlas-size"]: `${atlasCanonicalSize}px`,
|
|
1543
2396
|
background,
|
|
1544
2397
|
backgroundImage: dynamic && page?.url ? `url(${page.url})` : void 0,
|
|
1545
|
-
backgroundPosition: dynamic ?
|
|
1546
|
-
backgroundSize: dynamic
|
|
2398
|
+
backgroundPosition: dynamic ? atlasPosition : void 0,
|
|
2399
|
+
backgroundSize: dynamic ? atlasSize : void 0,
|
|
1547
2400
|
...dynamic ? {
|
|
1548
2401
|
["--pnx"]: entry.normal[0].toFixed(4),
|
|
1549
2402
|
["--pny"]: entry.normal[1].toFixed(4),
|
|
@@ -1554,12 +2407,12 @@ function TextureAtlasPoly({
|
|
|
1554
2407
|
// the polygon don't get painted with the tint.
|
|
1555
2408
|
maskImage: dynamicMask,
|
|
1556
2409
|
maskMode: "alpha",
|
|
1557
|
-
maskPosition:
|
|
1558
|
-
maskSize:
|
|
2410
|
+
maskPosition: atlasPosition,
|
|
2411
|
+
maskSize: atlasSize,
|
|
1559
2412
|
maskRepeat: "no-repeat",
|
|
1560
2413
|
WebkitMaskImage: dynamicMask,
|
|
1561
|
-
WebkitMaskPosition:
|
|
1562
|
-
WebkitMaskSize:
|
|
2414
|
+
WebkitMaskPosition: atlasPosition,
|
|
2415
|
+
WebkitMaskSize: atlasSize,
|
|
1563
2416
|
WebkitMaskRepeat: "no-repeat"
|
|
1564
2417
|
} : null,
|
|
1565
2418
|
opacity: page?.url ? void 0 : 0,
|
|
@@ -1601,8 +2454,10 @@ function PolySceneInner({
|
|
|
1601
2454
|
directionalLight,
|
|
1602
2455
|
ambientLight,
|
|
1603
2456
|
textureLighting = "baked",
|
|
1604
|
-
|
|
2457
|
+
textureQuality,
|
|
2458
|
+
strategies,
|
|
1605
2459
|
autoCenter = false,
|
|
2460
|
+
shadow,
|
|
1606
2461
|
className,
|
|
1607
2462
|
style,
|
|
1608
2463
|
children,
|
|
@@ -1646,14 +2501,6 @@ function PolySceneInner({
|
|
|
1646
2501
|
{ directionalLight }
|
|
1647
2502
|
);
|
|
1648
2503
|
const sceneBbox = centerInputPolygons ? centerSceneBbox : renderSceneBbox;
|
|
1649
|
-
const sceneStyle = (0, import_react9.useMemo)(() => {
|
|
1650
|
-
const handle = (0, import_polycss_core4.createIsometricCamera)(cameraState);
|
|
1651
|
-
const cameraStyle = handle.getStyle();
|
|
1652
|
-
return {
|
|
1653
|
-
["--scene-transform"]: cameraStyle.transform
|
|
1654
|
-
};
|
|
1655
|
-
}, [cameraState]);
|
|
1656
|
-
const computedClassName = `polycss-scene${className ? ` ${className}` : ""}`;
|
|
1657
2504
|
const directionalForAtlas = textureLighting === "dynamic" ? void 0 : directionalLight;
|
|
1658
2505
|
const ambientForAtlas = textureLighting === "dynamic" ? void 0 : ambientLight;
|
|
1659
2506
|
const polyContext = (0, import_react9.useMemo)(() => {
|
|
@@ -1666,11 +2513,43 @@ function PolySceneInner({
|
|
|
1666
2513
|
textureLighting
|
|
1667
2514
|
};
|
|
1668
2515
|
}, [directionalForAtlas, ambientForAtlas, textureLighting]);
|
|
2516
|
+
const autoCenterOffset = (0, import_react9.useMemo)(() => {
|
|
2517
|
+
if (!autoCenter) return [0, 0, 0];
|
|
2518
|
+
return [
|
|
2519
|
+
(sceneBbox.min[0] + sceneBbox.max[0]) / 2,
|
|
2520
|
+
(sceneBbox.min[1] + sceneBbox.max[1]) / 2,
|
|
2521
|
+
(sceneBbox.min[2] + sceneBbox.max[2]) / 2
|
|
2522
|
+
];
|
|
2523
|
+
}, [autoCenter, sceneBbox]);
|
|
2524
|
+
(0, import_react9.useEffect)(() => {
|
|
2525
|
+
store.setAutoCenterOffset(autoCenterOffset);
|
|
2526
|
+
}, [store, autoCenterOffset]);
|
|
2527
|
+
const sceneStyle = (0, import_react9.useMemo)(() => {
|
|
2528
|
+
const s = cameraState;
|
|
2529
|
+
const [ox, oy, oz] = autoCenterOffset;
|
|
2530
|
+
const tileSize = import_polycss_core4.BASE_TILE;
|
|
2531
|
+
const wx = s.target[0] + ox;
|
|
2532
|
+
const wy = s.target[1] + oy;
|
|
2533
|
+
const wz = s.target[2] + oz;
|
|
2534
|
+
const cssX = wy * tileSize;
|
|
2535
|
+
const cssY = wx * tileSize;
|
|
2536
|
+
const cssZ = wz * tileSize;
|
|
2537
|
+
const distancePart = s.distance !== 0 ? `translateZ(${-s.distance}px) ` : "";
|
|
2538
|
+
const transform = `${distancePart}scale(${s.zoom}) rotateX(${s.rotX}deg) rotate(${s.rotY}deg) translate3d(${-cssX}px, ${-cssY}px, ${-cssZ}px)`;
|
|
2539
|
+
return { transform };
|
|
2540
|
+
}, [cameraState, autoCenterOffset]);
|
|
2541
|
+
const computedClassName = `polycss-scene${className ? ` ${className}` : ""}`;
|
|
1669
2542
|
const textureAtlasPlans = (0, import_react9.useMemo)(
|
|
1670
|
-
() =>
|
|
2543
|
+
() => {
|
|
2544
|
+
const repairEdges = buildTextureEdgeRepairSets(polygons);
|
|
2545
|
+
return polygons.map((p, i) => computeTextureAtlasPlan(p, i, {
|
|
2546
|
+
...polyContext,
|
|
2547
|
+
textureEdgeRepairEdges: repairEdges[i]
|
|
2548
|
+
}));
|
|
2549
|
+
},
|
|
1671
2550
|
[polygons, polyContext]
|
|
1672
2551
|
);
|
|
1673
|
-
const textureAtlas = useTextureAtlas(textureAtlasPlans, textureLighting,
|
|
2552
|
+
const textureAtlas = useTextureAtlas(textureAtlasPlans, textureLighting, textureQuality, strategies);
|
|
1674
2553
|
const dynamicLightVars = (0, import_react9.useMemo)(() => {
|
|
1675
2554
|
if (textureLighting !== "dynamic") return null;
|
|
1676
2555
|
const dir = directionalLight?.direction ?? [0.4, -0.7, 0.59];
|
|
@@ -1681,6 +2560,8 @@ function PolySceneInner({
|
|
|
1681
2560
|
const lightIntensity = directionalLight?.intensity ?? 1;
|
|
1682
2561
|
const ambientIntensity = ambientLight?.intensity ?? 0.4;
|
|
1683
2562
|
const ch = (n) => (n / 255).toFixed(4);
|
|
2563
|
+
const rawClz = lz;
|
|
2564
|
+
const clz = Math.sign(rawClz || 1) * Math.max(Math.abs(rawClz), 0.01);
|
|
1684
2565
|
return {
|
|
1685
2566
|
["--plx"]: lx.toFixed(4),
|
|
1686
2567
|
["--ply"]: ly.toFixed(4),
|
|
@@ -1692,16 +2573,52 @@ function PolySceneInner({
|
|
|
1692
2573
|
["--par"]: ch(ambRgb[0]),
|
|
1693
2574
|
["--pag"]: ch(ambRgb[1]),
|
|
1694
2575
|
["--pab"]: ch(ambRgb[2]),
|
|
1695
|
-
["--pai"]: ambientIntensity.toFixed(4)
|
|
2576
|
+
["--pai"]: ambientIntensity.toFixed(4),
|
|
2577
|
+
["--clx"]: lx.toFixed(4),
|
|
2578
|
+
["--cly"]: ly.toFixed(4),
|
|
2579
|
+
["--clz"]: clz.toFixed(4)
|
|
1696
2580
|
};
|
|
1697
2581
|
}, [textureLighting, directionalLight, ambientLight]);
|
|
1698
|
-
const
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
2582
|
+
const shadowCastersRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
|
|
2583
|
+
const registerShadowCaster = (0, import_react9.useCallback)((meshId, meshPolygons) => {
|
|
2584
|
+
if (meshPolygons === null) {
|
|
2585
|
+
shadowCastersRef.current.delete(meshId);
|
|
2586
|
+
} else {
|
|
2587
|
+
shadowCastersRef.current.set(meshId, meshPolygons);
|
|
2588
|
+
}
|
|
2589
|
+
const el = sceneElRef.current;
|
|
2590
|
+
if (!el) return;
|
|
2591
|
+
if (textureLighting !== "dynamic") {
|
|
2592
|
+
el.style.removeProperty("--shadow-ground-cssz");
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
let minWorldZ = Infinity;
|
|
2596
|
+
for (const polys of shadowCastersRef.current.values()) {
|
|
2597
|
+
for (const poly of polys) {
|
|
2598
|
+
for (const v of poly.vertices) {
|
|
2599
|
+
if (v[2] < minWorldZ) minWorldZ = v[2];
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if (!Number.isFinite(minWorldZ)) {
|
|
2604
|
+
el.style.removeProperty("--shadow-ground-cssz");
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
const lift = shadow?.lift ?? 0.05;
|
|
2608
|
+
const groundCssZ = (minWorldZ + lift) * import_polycss_core4.BASE_TILE;
|
|
2609
|
+
el.style.setProperty("--shadow-ground-cssz", groundCssZ.toFixed(3));
|
|
2610
|
+
}, [sceneElRef, textureLighting, shadow]);
|
|
2611
|
+
(0, import_react9.useEffect)(() => {
|
|
2612
|
+
const el = sceneElRef.current;
|
|
2613
|
+
if (!el) return;
|
|
2614
|
+
if (textureLighting !== "dynamic") {
|
|
2615
|
+
el.style.removeProperty("--shadow-ground-cssz");
|
|
2616
|
+
}
|
|
2617
|
+
}, [textureLighting, sceneElRef]);
|
|
2618
|
+
const disabledStrategies = (0, import_react9.useMemo)(
|
|
2619
|
+
() => strategies?.disable?.length ? new Set(strategies.disable) : void 0,
|
|
2620
|
+
[strategies]
|
|
2621
|
+
);
|
|
1705
2622
|
const polyChildren = textureAtlas.entries.map((entry, index) => {
|
|
1706
2623
|
if (entry) {
|
|
1707
2624
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1716,13 +2633,21 @@ function PolySceneInner({
|
|
|
1716
2633
|
}
|
|
1717
2634
|
const plan = textureAtlasPlans[index];
|
|
1718
2635
|
if (!plan || plan.texture) return null;
|
|
1719
|
-
|
|
2636
|
+
const useU = !disabledStrategies?.has("u");
|
|
2637
|
+
const useProjectiveSolid = !disabledStrategies?.has("b");
|
|
2638
|
+
if (useU && isSolidTrianglePlan(plan)) {
|
|
2639
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextureTrianglePoly, { entry: plan, textureLighting }, plan.index);
|
|
2640
|
+
}
|
|
2641
|
+
if (useProjectiveSolid && isProjectiveQuadPlan(plan)) {
|
|
2642
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextureProjectiveSolidPoly, { entry: plan, textureLighting }, plan.index);
|
|
2643
|
+
}
|
|
2644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextureBorderShapePoly, { entry: plan, disabledStrategies }, plan.index);
|
|
1720
2645
|
});
|
|
1721
2646
|
const sceneCtxValue = (0, import_react9.useMemo)(
|
|
1722
|
-
() => ({ textureLighting, directionalLight, ambientLight }),
|
|
1723
|
-
[textureLighting, directionalLight, ambientLight]
|
|
2647
|
+
() => ({ textureLighting, directionalLight, ambientLight, shadow, registerShadowCaster }),
|
|
2648
|
+
[textureLighting, directionalLight, ambientLight, shadow, registerShadowCaster]
|
|
1724
2649
|
);
|
|
1725
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
2650
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PolySceneContext.Provider, { value: sceneCtxValue, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1726
2651
|
"div",
|
|
1727
2652
|
{
|
|
1728
2653
|
ref: localSceneRef,
|
|
@@ -1736,13 +2661,10 @@ function PolySceneInner({
|
|
|
1736
2661
|
// No more --polycss-rows / --polycss-cols — CSS Grid was dropped
|
|
1737
2662
|
// in Phase 4 (per §Design.4a).
|
|
1738
2663
|
},
|
|
1739
|
-
children:
|
|
1740
|
-
polyChildren,
|
|
1741
|
-
children
|
|
1742
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
2664
|
+
children: [
|
|
1743
2665
|
polyChildren,
|
|
1744
2666
|
children
|
|
1745
|
-
]
|
|
2667
|
+
]
|
|
1746
2668
|
}
|
|
1747
2669
|
) });
|
|
1748
2670
|
}
|
|
@@ -1881,11 +2803,17 @@ function findMeshUnderPoint(clientX, clientY, filter) {
|
|
|
1881
2803
|
}
|
|
1882
2804
|
|
|
1883
2805
|
// src/scene/PolyMesh.tsx
|
|
1884
|
-
var import_jsx_runtime5 = (
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
)
|
|
2806
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
2807
|
+
function solidPaintVars(defaults) {
|
|
2808
|
+
const out = {};
|
|
2809
|
+
if (defaults.paintColor) out["--polycss-paint"] = defaults.paintColor;
|
|
2810
|
+
if (defaults.dynamicColor) {
|
|
2811
|
+
out["--psr"] = (defaults.dynamicColor.r / 255).toFixed(4);
|
|
2812
|
+
out["--psg"] = (defaults.dynamicColor.g / 255).toFixed(4);
|
|
2813
|
+
out["--psb"] = (defaults.dynamicColor.b / 255).toFixed(4);
|
|
2814
|
+
}
|
|
2815
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
2816
|
+
}
|
|
1889
2817
|
function buildTransform(position, scale, rotation) {
|
|
1890
2818
|
const parts = [];
|
|
1891
2819
|
if (position) {
|
|
@@ -1931,7 +2859,8 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
1931
2859
|
polygons: polygonsProp,
|
|
1932
2860
|
autoCenter,
|
|
1933
2861
|
textureLighting,
|
|
1934
|
-
|
|
2862
|
+
textureQuality,
|
|
2863
|
+
castShadow,
|
|
1935
2864
|
children,
|
|
1936
2865
|
fallback,
|
|
1937
2866
|
errorFallback,
|
|
@@ -1959,12 +2888,43 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
1959
2888
|
return { ...parseOptions ?? {}, ...mtl ? { mtlUrl: mtl } : {} };
|
|
1960
2889
|
}, [mtl, parseOptions]);
|
|
1961
2890
|
const fetched = usePolyMesh(src ?? "", mergedOptions);
|
|
1962
|
-
const
|
|
2891
|
+
const externalPolygons = src ? fetched.polygons : polygonsProp ?? [];
|
|
2892
|
+
const [localPolygons, setLocalPolygons] = (0, import_react11.useState)(null);
|
|
2893
|
+
const prevExternalRef = (0, import_react11.useRef)(externalPolygons);
|
|
2894
|
+
if (prevExternalRef.current !== externalPolygons) {
|
|
2895
|
+
prevExternalRef.current = externalPolygons;
|
|
2896
|
+
if (localPolygons !== null) setLocalPolygons(null);
|
|
2897
|
+
}
|
|
2898
|
+
const sourcePolygons = localPolygons ?? externalPolygons;
|
|
2899
|
+
const hasRenderProp = typeof children === "function";
|
|
2900
|
+
const renderPolygon = hasRenderProp ? children : null;
|
|
2901
|
+
const staticChildren = hasRenderProp ? null : children;
|
|
1963
2902
|
const polygons = (0, import_react11.useMemo)(
|
|
1964
2903
|
() => autoCenter ? recenterPolygons(sourcePolygons) : sourcePolygons,
|
|
1965
2904
|
[sourcePolygons, autoCenter]
|
|
1966
2905
|
);
|
|
1967
2906
|
const transform = buildTransform(position, scale, rotation);
|
|
2907
|
+
const transformOrigin = (0, import_react11.useMemo)(() => {
|
|
2908
|
+
if (polygons.length === 0) return void 0;
|
|
2909
|
+
let minX = Infinity, minY = Infinity, minZ = Infinity;
|
|
2910
|
+
let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
|
|
2911
|
+
for (const poly of polygons) {
|
|
2912
|
+
for (const v of poly.vertices) {
|
|
2913
|
+
if (v[0] < minX) minX = v[0];
|
|
2914
|
+
if (v[0] > maxX) maxX = v[0];
|
|
2915
|
+
if (v[1] < minY) minY = v[1];
|
|
2916
|
+
if (v[1] > maxY) maxY = v[1];
|
|
2917
|
+
if (v[2] < minZ) minZ = v[2];
|
|
2918
|
+
if (v[2] > maxZ) maxZ = v[2];
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (!Number.isFinite(minX)) return void 0;
|
|
2922
|
+
const tile = 50;
|
|
2923
|
+
const x = (minY + maxY) / 2 * tile;
|
|
2924
|
+
const y = (minX + maxX) / 2 * tile;
|
|
2925
|
+
const z = (minZ + maxZ) / 2 * tile;
|
|
2926
|
+
return `${x}px ${y}px ${z}px`;
|
|
2927
|
+
}, [polygons]);
|
|
1968
2928
|
const wrapperRef = (0, import_react11.useRef)(null);
|
|
1969
2929
|
const propsRef = (0, import_react11.useRef)({ position, scale, rotation });
|
|
1970
2930
|
propsRef.current = { position, scale, rotation };
|
|
@@ -1980,7 +2940,14 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
1980
2940
|
getRotation: () => propsRef.current.rotation,
|
|
1981
2941
|
getScale: () => propsRef.current.scale,
|
|
1982
2942
|
getPolygons: () => polygonsRef.current,
|
|
1983
|
-
rebakeAtlas: () => setBakedRotation(propsRef.current.rotation)
|
|
2943
|
+
rebakeAtlas: () => setBakedRotation(propsRef.current.rotation),
|
|
2944
|
+
updatePolygon(target, partial) {
|
|
2945
|
+
const current = polygonsRef.current;
|
|
2946
|
+
const idx = typeof target === "number" ? target : current.indexOf(target);
|
|
2947
|
+
if (idx < 0 || idx >= current.length) return;
|
|
2948
|
+
Object.assign(current[idx], partial);
|
|
2949
|
+
setLocalPolygons([...current]);
|
|
2950
|
+
}
|
|
1984
2951
|
}), [id]);
|
|
1985
2952
|
(0, import_react11.useImperativeHandle)(forwardedRef, () => handle, [handle]);
|
|
1986
2953
|
(0, import_react11.useEffect)(() => {
|
|
@@ -2130,11 +3097,6 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
2130
3097
|
["--plz"]: (localDir[2] / len).toFixed(4)
|
|
2131
3098
|
};
|
|
2132
3099
|
}, [effectiveTextureLighting, rotation, sceneDirectionalLight]);
|
|
2133
|
-
const wrapperStyle = {
|
|
2134
|
-
transform,
|
|
2135
|
-
...dynamicLightOverride,
|
|
2136
|
-
...style
|
|
2137
|
-
};
|
|
2138
3100
|
const bakedDirectional = (0, import_react11.useMemo)(() => {
|
|
2139
3101
|
if (!effectiveDirectional) return effectiveDirectional;
|
|
2140
3102
|
const rot = bakedRotation ?? [0, 0, 0];
|
|
@@ -2145,18 +3107,85 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
2145
3107
|
};
|
|
2146
3108
|
}, [effectiveDirectional, bakedRotation]);
|
|
2147
3109
|
const atlasPlans = (0, import_react11.useMemo)(
|
|
2148
|
-
() =>
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
3110
|
+
() => {
|
|
3111
|
+
if (renderPolygon) return [];
|
|
3112
|
+
const repairEdges = buildTextureEdgeRepairSets(polygons);
|
|
3113
|
+
return polygons.map((p, i) => computeTextureAtlasPlan(p, i, {
|
|
3114
|
+
directionalLight: bakedDirectional,
|
|
3115
|
+
ambientLight: effectiveAmbient,
|
|
3116
|
+
textureEdgeRepairEdges: repairEdges[i]
|
|
3117
|
+
}));
|
|
3118
|
+
},
|
|
3119
|
+
[renderPolygon, polygons, bakedDirectional, effectiveAmbient]
|
|
2153
3120
|
);
|
|
2154
3121
|
const textureAtlas = useTextureAtlas(
|
|
2155
3122
|
atlasPlans,
|
|
2156
3123
|
effectiveTextureLighting,
|
|
2157
|
-
|
|
3124
|
+
textureQuality
|
|
2158
3125
|
);
|
|
2159
|
-
const
|
|
3126
|
+
const solidPaintDefaults = (0, import_react11.useMemo)(
|
|
3127
|
+
() => !renderPolygon ? getSolidPaintDefaults(atlasPlans, effectiveTextureLighting) : {},
|
|
3128
|
+
[renderPolygon, atlasPlans, effectiveTextureLighting]
|
|
3129
|
+
);
|
|
3130
|
+
const defaultPaintVars = (0, import_react11.useMemo)(
|
|
3131
|
+
() => solidPaintVars(solidPaintDefaults),
|
|
3132
|
+
[solidPaintDefaults]
|
|
3133
|
+
);
|
|
3134
|
+
const meshIdRef = (0, import_react11.useRef)(/* @__PURE__ */ Symbol());
|
|
3135
|
+
const sceneRegisterShadowCaster = sceneCtx?.registerShadowCaster;
|
|
3136
|
+
(0, import_react11.useEffect)(() => {
|
|
3137
|
+
if (!sceneRegisterShadowCaster) return;
|
|
3138
|
+
if (castShadow && effectiveTextureLighting === "dynamic") {
|
|
3139
|
+
sceneRegisterShadowCaster(meshIdRef.current, polygons);
|
|
3140
|
+
} else {
|
|
3141
|
+
sceneRegisterShadowCaster(meshIdRef.current, null);
|
|
3142
|
+
}
|
|
3143
|
+
return () => {
|
|
3144
|
+
sceneRegisterShadowCaster(meshIdRef.current, null);
|
|
3145
|
+
};
|
|
3146
|
+
}, [sceneRegisterShadowCaster, castShadow, effectiveTextureLighting, polygons]);
|
|
3147
|
+
const shadowLeaves = (0, import_react11.useMemo)(() => {
|
|
3148
|
+
if (!castShadow || effectiveTextureLighting !== "dynamic" || renderPolygon) return [];
|
|
3149
|
+
const shadowColor = sceneCtx?.shadow?.color ?? "#000000";
|
|
3150
|
+
const shadowOpacity = sceneCtx?.shadow?.opacity ?? 0.25;
|
|
3151
|
+
const parsed = (0, import_polycss_core6.parseHexColor)(shadowColor)?.rgb ?? [0, 0, 0];
|
|
3152
|
+
const shadowColorCss = `rgba(${parsed[0]},${parsed[1]},${parsed[2]},${shadowOpacity})`;
|
|
3153
|
+
const shadowDedupDrop = (0, import_polycss_core6.findOverlappingPolygonDuplicates)(polygons, {
|
|
3154
|
+
normalTolerance: 0.1,
|
|
3155
|
+
distanceTolerance: 0.5,
|
|
3156
|
+
overlapFraction: 0.4
|
|
3157
|
+
});
|
|
3158
|
+
const leaves = [];
|
|
3159
|
+
for (const plan of atlasPlans) {
|
|
3160
|
+
if (!plan) continue;
|
|
3161
|
+
if (shadowDedupDrop.has(plan.index)) continue;
|
|
3162
|
+
const borderShape = cssBorderShapeForPlan(plan);
|
|
3163
|
+
leaves.push(
|
|
3164
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3165
|
+
ShadowLeaf,
|
|
3166
|
+
{
|
|
3167
|
+
plan,
|
|
3168
|
+
shadowColorCss,
|
|
3169
|
+
borderShape
|
|
3170
|
+
},
|
|
3171
|
+
`shadow-${plan.index}`
|
|
3172
|
+
)
|
|
3173
|
+
);
|
|
3174
|
+
}
|
|
3175
|
+
return leaves;
|
|
3176
|
+
}, [castShadow, effectiveTextureLighting, renderPolygon, polygons, atlasPlans, sceneCtx?.shadow]);
|
|
3177
|
+
const wrapperStyle = {
|
|
3178
|
+
transform,
|
|
3179
|
+
...transformOrigin ? { transformOrigin } : null,
|
|
3180
|
+
...dynamicLightOverride,
|
|
3181
|
+
...style,
|
|
3182
|
+
...defaultPaintVars
|
|
3183
|
+
};
|
|
3184
|
+
const renderedPolygons = renderPolygon ? polygons.map((p, i) => (
|
|
3185
|
+
// Render-prop: caller controls how each polygon renders. We still
|
|
3186
|
+
// wrap in a fragment with key so React reconciliation works.
|
|
3187
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RenderPropPolygon, { polygon: p, index: i, children: renderPolygon }, i)
|
|
3188
|
+
)) : textureAtlas.entries.map((entry, index) => {
|
|
2160
3189
|
if (entry) {
|
|
2161
3190
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2162
3191
|
TextureAtlasPoly,
|
|
@@ -2170,7 +3199,22 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
2170
3199
|
}
|
|
2171
3200
|
const plan = atlasPlans[index];
|
|
2172
3201
|
if (!plan || plan.texture) return null;
|
|
2173
|
-
return isSolidTrianglePlan(plan) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3202
|
+
return isSolidTrianglePlan(plan) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3203
|
+
TextureTrianglePoly,
|
|
3204
|
+
{
|
|
3205
|
+
entry: plan,
|
|
3206
|
+
textureLighting: effectiveTextureLighting,
|
|
3207
|
+
solidPaintDefaults
|
|
3208
|
+
},
|
|
3209
|
+
plan.index
|
|
3210
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3211
|
+
TextureBorderShapePoly,
|
|
3212
|
+
{
|
|
3213
|
+
entry: plan,
|
|
3214
|
+
solidPaintDefaults
|
|
3215
|
+
},
|
|
3216
|
+
plan.index
|
|
3217
|
+
);
|
|
2174
3218
|
});
|
|
2175
3219
|
if (src) {
|
|
2176
3220
|
if (fetched.loading && fetched.polygons.length === 0) {
|
|
@@ -2200,7 +3244,7 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
2200
3244
|
);
|
|
2201
3245
|
}
|
|
2202
3246
|
}
|
|
2203
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.
|
|
3247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
2204
3248
|
"div",
|
|
2205
3249
|
{
|
|
2206
3250
|
ref: wrapperRef,
|
|
@@ -2208,7 +3252,11 @@ var PolyMesh = (0, import_react11.forwardRef)(function PolyMesh2({
|
|
|
2208
3252
|
className: `polycss-mesh${className ? ` ${className}` : ""}`,
|
|
2209
3253
|
style: wrapperStyle,
|
|
2210
3254
|
...wrapperHandlers,
|
|
2211
|
-
children:
|
|
3255
|
+
children: [
|
|
3256
|
+
shadowLeaves,
|
|
3257
|
+
renderedPolygons,
|
|
3258
|
+
staticChildren
|
|
3259
|
+
]
|
|
2212
3260
|
}
|
|
2213
3261
|
);
|
|
2214
3262
|
});
|
|
@@ -2219,11 +3267,68 @@ function RenderPropPolygon({
|
|
|
2219
3267
|
}) {
|
|
2220
3268
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: children(polygon, index) });
|
|
2221
3269
|
}
|
|
3270
|
+
function ShadowLeaf({
|
|
3271
|
+
plan,
|
|
3272
|
+
shadowColorCss,
|
|
3273
|
+
borderShape
|
|
3274
|
+
}) {
|
|
3275
|
+
const setRef = (0, import_react11.useCallback)((el) => {
|
|
3276
|
+
if (!el) return;
|
|
3277
|
+
el.style.setProperty("border-shape", borderShape);
|
|
3278
|
+
}, [borderShape]);
|
|
3279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
3280
|
+
"q",
|
|
3281
|
+
{
|
|
3282
|
+
ref: setRef,
|
|
3283
|
+
className: "polycss-shadow",
|
|
3284
|
+
style: {
|
|
3285
|
+
transform: `var(--shadow-proj) matrix3d(${plan.matrix})`,
|
|
3286
|
+
color: shadowColorCss,
|
|
3287
|
+
width: plan.canvasW,
|
|
3288
|
+
height: plan.canvasH,
|
|
3289
|
+
["--pnx"]: plan.normal[0].toFixed(4),
|
|
3290
|
+
["--pny"]: plan.normal[1].toFixed(4),
|
|
3291
|
+
["--pnz"]: plan.normal[2].toFixed(4)
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
2222
3296
|
|
|
2223
|
-
// src/scene/
|
|
3297
|
+
// src/scene/PolyGround.tsx
|
|
2224
3298
|
var import_react12 = require("react");
|
|
3299
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
3300
|
+
function PolyGround({
|
|
3301
|
+
size = 6,
|
|
3302
|
+
z = 0,
|
|
3303
|
+
center = [0, 0],
|
|
3304
|
+
color = "#7d848e",
|
|
3305
|
+
className
|
|
3306
|
+
}) {
|
|
3307
|
+
const polygons = (0, import_react12.useMemo)(() => {
|
|
3308
|
+
const half = size / 2;
|
|
3309
|
+
const [cx, cy] = center;
|
|
3310
|
+
const vertices = [
|
|
3311
|
+
[cx - half, cy - half, z],
|
|
3312
|
+
[cx + half, cy - half, z],
|
|
3313
|
+
[cx + half, cy + half, z],
|
|
3314
|
+
[cx - half, cy + half, z]
|
|
3315
|
+
];
|
|
3316
|
+
return [{ vertices, color }];
|
|
3317
|
+
}, [size, z, center, color]);
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
3319
|
+
PolyMesh,
|
|
3320
|
+
{
|
|
3321
|
+
polygons,
|
|
3322
|
+
castShadow: false,
|
|
3323
|
+
className: className ? `polycss-ground ${className}` : "polycss-ground"
|
|
3324
|
+
}
|
|
3325
|
+
);
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
// src/scene/usePolyMaterial.ts
|
|
3329
|
+
var import_react13 = require("react");
|
|
2225
3330
|
function usePolyMaterial(options) {
|
|
2226
|
-
return (0,
|
|
3331
|
+
return (0, import_react13.useMemo)(
|
|
2227
3332
|
() => ({ texture: options.texture, key: options.key }),
|
|
2228
3333
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2229
3334
|
[options.texture, options.key]
|
|
@@ -2231,8 +3336,13 @@ function usePolyMaterial(options) {
|
|
|
2231
3336
|
}
|
|
2232
3337
|
|
|
2233
3338
|
// src/shapes/Poly.tsx
|
|
2234
|
-
var
|
|
2235
|
-
var
|
|
3339
|
+
var import_react14 = require("react");
|
|
3340
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
3341
|
+
var DIRECT_TEXTURE_CSS_DECIMALS = 4;
|
|
3342
|
+
function formatCssLength2(value, decimals = DIRECT_TEXTURE_CSS_DECIMALS) {
|
|
3343
|
+
const next = value.toFixed(decimals).replace(/\.?0+$/, "");
|
|
3344
|
+
return Number(next) === 0 || Object.is(Number(next), -0) ? "0" : `${next}px`;
|
|
3345
|
+
}
|
|
2236
3346
|
function isAxisAlignedRectUVs(uvs) {
|
|
2237
3347
|
if (uvs.length !== 4) return null;
|
|
2238
3348
|
const us = [...new Set(uvs.map((uv) => uv[0]))].sort((a, b) => a - b);
|
|
@@ -2262,18 +3372,16 @@ function MaterialDirectPoly({
|
|
|
2262
3372
|
const { u0, u1, v0, v1 } = uvRect;
|
|
2263
3373
|
const du = u1 - u0;
|
|
2264
3374
|
const dv = v1 - v0;
|
|
2265
|
-
const sourceW =
|
|
2266
|
-
const sourceH =
|
|
3375
|
+
const sourceW = 1 / du;
|
|
3376
|
+
const sourceH = 1 / dv;
|
|
2267
3377
|
const vMax = Math.max(v0, v1);
|
|
2268
|
-
const offsetX = u0
|
|
2269
|
-
const offsetY = (1 - vMax)
|
|
3378
|
+
const offsetX = u0 / du;
|
|
3379
|
+
const offsetY = (1 - vMax) / dv;
|
|
2270
3380
|
const style = {
|
|
2271
|
-
|
|
2272
|
-
height: plan.canvasH,
|
|
2273
|
-
transform: `matrix3d(${plan.matrix})`,
|
|
3381
|
+
transform: `matrix3d(${plan.canonicalMatrix})`,
|
|
2274
3382
|
backgroundImage: `url(${material.texture})`,
|
|
2275
|
-
backgroundSize: `${sourceW}
|
|
2276
|
-
backgroundPosition:
|
|
3383
|
+
backgroundSize: `${formatCssLength2(sourceW)} ${formatCssLength2(sourceH)}`,
|
|
3384
|
+
backgroundPosition: `${formatCssLength2(-offsetX)} ${formatCssLength2(-offsetY)}`,
|
|
2277
3385
|
pointerEvents: pointerEvents === "none" ? "none" : void 0,
|
|
2278
3386
|
...styleProp
|
|
2279
3387
|
};
|
|
@@ -2281,7 +3389,7 @@ function MaterialDirectPoly({
|
|
|
2281
3389
|
Object.entries(plan.polygon.data).map(([k, v]) => [`data-${k}`, String(v)])
|
|
2282
3390
|
) : {};
|
|
2283
3391
|
const elementClassName = className?.trim() || void 0;
|
|
2284
|
-
return /* @__PURE__ */ (0,
|
|
3392
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2285
3393
|
"i",
|
|
2286
3394
|
{
|
|
2287
3395
|
className: elementClassName,
|
|
@@ -2324,17 +3432,17 @@ function PolyInner({
|
|
|
2324
3432
|
pointerEvents: pointerEventsProp,
|
|
2325
3433
|
context,
|
|
2326
3434
|
textureLighting: textureLightingProp,
|
|
2327
|
-
|
|
3435
|
+
textureQuality: textureQualityProp,
|
|
2328
3436
|
baseColor: baseColorProp,
|
|
2329
3437
|
...dataAttrs
|
|
2330
3438
|
}) {
|
|
2331
3439
|
const tileSize = context?.tileSize ?? 50;
|
|
2332
3440
|
const layerElevation = context?.layerElevation ?? tileSize;
|
|
2333
3441
|
const textureLighting = textureLightingProp ?? context?.textureLighting ?? "baked";
|
|
2334
|
-
const
|
|
3442
|
+
const textureQuality = textureQualityProp ?? context?.textureQuality;
|
|
2335
3443
|
const polygonColor = baseColorProp ?? color;
|
|
2336
3444
|
const effectiveTexture = material?.texture ?? texture;
|
|
2337
|
-
const atlasPlan = (0,
|
|
3445
|
+
const atlasPlan = (0, import_react14.useMemo)(
|
|
2338
3446
|
() => computeTextureAtlasPlan(
|
|
2339
3447
|
{ vertices, color: polygonColor, texture: effectiveTexture, uvs, data },
|
|
2340
3448
|
0,
|
|
@@ -2355,15 +3463,15 @@ function PolyInner({
|
|
|
2355
3463
|
context?.directionalLight
|
|
2356
3464
|
]
|
|
2357
3465
|
);
|
|
2358
|
-
const materialUvRect = (0,
|
|
3466
|
+
const materialUvRect = (0, import_react14.useMemo)(
|
|
2359
3467
|
() => material && uvs ? isAxisAlignedRectUVs(uvs) : null,
|
|
2360
3468
|
[material, uvs]
|
|
2361
3469
|
);
|
|
2362
|
-
const atlasPlans = (0,
|
|
3470
|
+
const atlasPlans = (0, import_react14.useMemo)(
|
|
2363
3471
|
() => materialUvRect ? [] : [atlasPlan],
|
|
2364
3472
|
[materialUvRect, atlasPlan]
|
|
2365
3473
|
);
|
|
2366
|
-
const textureAtlas = useTextureAtlas(atlasPlans, textureLighting,
|
|
3474
|
+
const textureAtlas = useTextureAtlas(atlasPlans, textureLighting, textureQuality);
|
|
2367
3475
|
const domEventHandlers = {
|
|
2368
3476
|
onClick,
|
|
2369
3477
|
onDoubleClick,
|
|
@@ -2412,7 +3520,7 @@ function PolyInner({
|
|
|
2412
3520
|
const wrapperTransform = transformParts.length > 0 ? transformParts.join(" ") : void 0;
|
|
2413
3521
|
let front = null;
|
|
2414
3522
|
if (materialUvRect && material && atlasPlan) {
|
|
2415
|
-
front = /* @__PURE__ */ (0,
|
|
3523
|
+
front = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2416
3524
|
MaterialDirectPoly,
|
|
2417
3525
|
{
|
|
2418
3526
|
plan: atlasPlan,
|
|
@@ -2428,7 +3536,7 @@ function PolyInner({
|
|
|
2428
3536
|
} else {
|
|
2429
3537
|
const atlasEntry = textureAtlas.entries[0];
|
|
2430
3538
|
if (atlasEntry) {
|
|
2431
|
-
front = /* @__PURE__ */ (0,
|
|
3539
|
+
front = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2432
3540
|
TextureAtlasPoly,
|
|
2433
3541
|
{
|
|
2434
3542
|
entry: atlasEntry,
|
|
@@ -2442,7 +3550,7 @@ function PolyInner({
|
|
|
2442
3550
|
}
|
|
2443
3551
|
);
|
|
2444
3552
|
} else if (atlasPlan && !atlasPlan.texture) {
|
|
2445
|
-
front = isSolidTrianglePlan(atlasPlan) ? /* @__PURE__ */ (0,
|
|
3553
|
+
front = isSolidTrianglePlan(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2446
3554
|
TextureTrianglePoly,
|
|
2447
3555
|
{
|
|
2448
3556
|
entry: atlasPlan,
|
|
@@ -2453,7 +3561,18 @@ function PolyInner({
|
|
|
2453
3561
|
domEventHandlers,
|
|
2454
3562
|
pointerEvents: pointerEventsProp ?? "auto"
|
|
2455
3563
|
}
|
|
2456
|
-
) : /* @__PURE__ */ (0,
|
|
3564
|
+
) : isProjectiveQuadPlan(atlasPlan) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
3565
|
+
TextureProjectiveSolidPoly,
|
|
3566
|
+
{
|
|
3567
|
+
entry: atlasPlan,
|
|
3568
|
+
textureLighting,
|
|
3569
|
+
className,
|
|
3570
|
+
style: styleProp,
|
|
3571
|
+
domAttrs,
|
|
3572
|
+
domEventHandlers,
|
|
3573
|
+
pointerEvents: pointerEventsProp ?? "auto"
|
|
3574
|
+
}
|
|
3575
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2457
3576
|
TextureBorderShapePoly,
|
|
2458
3577
|
{
|
|
2459
3578
|
entry: atlasPlan,
|
|
@@ -2468,26 +3587,441 @@ function PolyInner({
|
|
|
2468
3587
|
}
|
|
2469
3588
|
if (!front) return null;
|
|
2470
3589
|
if (!wrapperTransform) return front;
|
|
2471
|
-
return /* @__PURE__ */ (0,
|
|
3590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2472
3591
|
"div",
|
|
2473
3592
|
{
|
|
2474
3593
|
className: "polycss-poly-wrapper",
|
|
2475
3594
|
style: {
|
|
3595
|
+
transform: wrapperTransform,
|
|
2476
3596
|
position: "absolute",
|
|
2477
|
-
transformStyle: "preserve-3d"
|
|
2478
|
-
transform: wrapperTransform
|
|
3597
|
+
transformStyle: "preserve-3d"
|
|
2479
3598
|
},
|
|
2480
3599
|
children: front
|
|
2481
3600
|
}
|
|
2482
|
-
);
|
|
2483
|
-
}
|
|
2484
|
-
var Poly = (0,
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
var Poly = (0, import_react14.memo)(PolyInner);
|
|
3604
|
+
|
|
3605
|
+
// src/controls/PolyFirstPersonControls.tsx
|
|
3606
|
+
var import_react15 = require("react");
|
|
3607
|
+
var import_polycss_core7 = require("@layoutit/polycss-core");
|
|
3608
|
+
var DEFAULTS = {
|
|
3609
|
+
enabled: true,
|
|
3610
|
+
lookEnabled: true,
|
|
3611
|
+
moveEnabled: true,
|
|
3612
|
+
jumpEnabled: true,
|
|
3613
|
+
crouchEnabled: true,
|
|
3614
|
+
lookSensitivity: 0.15,
|
|
3615
|
+
invertY: false,
|
|
3616
|
+
moveSpeed: 5,
|
|
3617
|
+
jumpVelocity: 7,
|
|
3618
|
+
gravity: 18,
|
|
3619
|
+
eyeHeight: 1.7,
|
|
3620
|
+
crouchHeight: 1,
|
|
3621
|
+
groundZ: 0,
|
|
3622
|
+
minPitch: 5,
|
|
3623
|
+
maxPitch: 175
|
|
3624
|
+
};
|
|
3625
|
+
function resolveOptions(base, partial) {
|
|
3626
|
+
return {
|
|
3627
|
+
enabled: partial.enabled ?? base.enabled,
|
|
3628
|
+
lookEnabled: partial.lookEnabled ?? base.lookEnabled,
|
|
3629
|
+
moveEnabled: partial.moveEnabled ?? base.moveEnabled,
|
|
3630
|
+
jumpEnabled: partial.jumpEnabled ?? base.jumpEnabled,
|
|
3631
|
+
crouchEnabled: partial.crouchEnabled ?? base.crouchEnabled,
|
|
3632
|
+
lookSensitivity: partial.lookSensitivity ?? base.lookSensitivity,
|
|
3633
|
+
invertY: partial.invertY ?? base.invertY,
|
|
3634
|
+
moveSpeed: partial.moveSpeed ?? base.moveSpeed,
|
|
3635
|
+
jumpVelocity: partial.jumpVelocity ?? base.jumpVelocity,
|
|
3636
|
+
gravity: partial.gravity ?? base.gravity,
|
|
3637
|
+
eyeHeight: partial.eyeHeight ?? base.eyeHeight,
|
|
3638
|
+
crouchHeight: partial.crouchHeight ?? base.crouchHeight,
|
|
3639
|
+
groundZ: partial.groundZ ?? base.groundZ,
|
|
3640
|
+
minPitch: partial.minPitch ?? base.minPitch,
|
|
3641
|
+
maxPitch: partial.maxPitch ?? base.maxPitch
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3644
|
+
var FORWARD_KEYS = /* @__PURE__ */ new Set(["KeyW", "ArrowUp"]);
|
|
3645
|
+
var BACK_KEYS = /* @__PURE__ */ new Set(["KeyS", "ArrowDown"]);
|
|
3646
|
+
var LEFT_KEYS = /* @__PURE__ */ new Set(["KeyA", "ArrowLeft"]);
|
|
3647
|
+
var RIGHT_KEYS = /* @__PURE__ */ new Set(["KeyD", "ArrowRight"]);
|
|
3648
|
+
var JUMP_KEYS = /* @__PURE__ */ new Set(["Space"]);
|
|
3649
|
+
var CROUCH_KEYS = /* @__PURE__ */ new Set(["ControlLeft", "ControlRight"]);
|
|
3650
|
+
function isFpvKey(code) {
|
|
3651
|
+
return FORWARD_KEYS.has(code) || BACK_KEYS.has(code) || LEFT_KEYS.has(code) || RIGHT_KEYS.has(code) || JUMP_KEYS.has(code) || CROUCH_KEYS.has(code);
|
|
3652
|
+
}
|
|
3653
|
+
function makeRegistry() {
|
|
3654
|
+
return { change: [], start: [], end: [] };
|
|
3655
|
+
}
|
|
3656
|
+
function emitEvent(registry, type) {
|
|
3657
|
+
const list = [...registry[type]];
|
|
3658
|
+
for (const fn of list) {
|
|
3659
|
+
try {
|
|
3660
|
+
fn();
|
|
3661
|
+
} catch {
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
var PolyFirstPersonControls = (0, import_react15.forwardRef)(function PolyFirstPersonControls2(props, ref) {
|
|
3666
|
+
const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
|
|
3667
|
+
const onChangeRef = (0, import_react15.useRef)(props.onChange);
|
|
3668
|
+
const onInteractionStartRef = (0, import_react15.useRef)(props.onInteractionStart);
|
|
3669
|
+
const onInteractionEndRef = (0, import_react15.useRef)(props.onInteractionEnd);
|
|
3670
|
+
(0, import_react15.useEffect)(() => {
|
|
3671
|
+
onChangeRef.current = props.onChange;
|
|
3672
|
+
onInteractionStartRef.current = props.onInteractionStart;
|
|
3673
|
+
onInteractionEndRef.current = props.onInteractionEnd;
|
|
3674
|
+
});
|
|
3675
|
+
const optsRef = (0, import_react15.useRef)(resolveOptions(DEFAULTS, props));
|
|
3676
|
+
const cameraOriginRef = (0, import_react15.useRef)([0, 0, 0]);
|
|
3677
|
+
const rafIdRef = (0, import_react15.useRef)(null);
|
|
3678
|
+
const lastTimeRef = (0, import_react15.useRef)(0);
|
|
3679
|
+
const stoppedRef = (0, import_react15.useRef)(false);
|
|
3680
|
+
const pointerLockedRef = (0, import_react15.useRef)(false);
|
|
3681
|
+
const interactingRef = (0, import_react15.useRef)(false);
|
|
3682
|
+
const keysHeldRef = (0, import_react15.useRef)(/* @__PURE__ */ new Set());
|
|
3683
|
+
const verticalVelRef = (0, import_react15.useRef)(0);
|
|
3684
|
+
const jumpOffsetRef = (0, import_react15.useRef)(0);
|
|
3685
|
+
const registryRef = (0, import_react15.useRef)(makeRegistry());
|
|
3686
|
+
(0, import_react15.useImperativeHandle)(ref, () => ({
|
|
3687
|
+
update(partial) {
|
|
3688
|
+
const prev = optsRef.current;
|
|
3689
|
+
optsRef.current = resolveOptions(prev, partial);
|
|
3690
|
+
if (!stoppedRef.current) {
|
|
3691
|
+
const host = cameraElRef.current;
|
|
3692
|
+
if (host) host.style.cursor = optsRef.current.lookEnabled ? "crosshair" : "";
|
|
3693
|
+
}
|
|
3694
|
+
},
|
|
3695
|
+
resume() {
|
|
3696
|
+
if (!stoppedRef.current) return;
|
|
3697
|
+
stoppedRef.current = false;
|
|
3698
|
+
const host = cameraElRef.current;
|
|
3699
|
+
if (host) host.style.cursor = optsRef.current.lookEnabled ? "crosshair" : "";
|
|
3700
|
+
startLoop();
|
|
3701
|
+
},
|
|
3702
|
+
pause() {
|
|
3703
|
+
if (stoppedRef.current) return;
|
|
3704
|
+
stoppedRef.current = true;
|
|
3705
|
+
stopLoop();
|
|
3706
|
+
const host = cameraElRef.current;
|
|
3707
|
+
if (host) host.style.cursor = "";
|
|
3708
|
+
if (interactingRef.current) {
|
|
3709
|
+
interactingRef.current = false;
|
|
3710
|
+
emitEvent(registryRef.current, "end");
|
|
3711
|
+
try {
|
|
3712
|
+
onInteractionEndRef.current?.();
|
|
3713
|
+
} catch {
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
},
|
|
3717
|
+
destroy() {
|
|
3718
|
+
stoppedRef.current = true;
|
|
3719
|
+
stopLoop();
|
|
3720
|
+
},
|
|
3721
|
+
lock() {
|
|
3722
|
+
const opts = optsRef.current;
|
|
3723
|
+
if (!opts.enabled || !opts.lookEnabled || stoppedRef.current) return;
|
|
3724
|
+
const host = cameraElRef.current;
|
|
3725
|
+
try {
|
|
3726
|
+
host?.requestPointerLock();
|
|
3727
|
+
} catch {
|
|
3728
|
+
}
|
|
3729
|
+
},
|
|
3730
|
+
unlock() {
|
|
3731
|
+
if (pointerLockedRef.current) {
|
|
3732
|
+
const host = cameraElRef.current;
|
|
3733
|
+
try {
|
|
3734
|
+
host?.ownerDocument?.exitPointerLock();
|
|
3735
|
+
} catch {
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
},
|
|
3739
|
+
isLocked() {
|
|
3740
|
+
return pointerLockedRef.current;
|
|
3741
|
+
},
|
|
3742
|
+
getOrigin() {
|
|
3743
|
+
const o = cameraOriginRef.current;
|
|
3744
|
+
return [o[0], o[1], o[2]];
|
|
3745
|
+
},
|
|
3746
|
+
setOrigin(origin) {
|
|
3747
|
+
cameraOriginRef.current[0] = origin[0];
|
|
3748
|
+
cameraOriginRef.current[1] = origin[1];
|
|
3749
|
+
cameraOriginRef.current[2] = origin[2];
|
|
3750
|
+
syncTargetFromOrigin();
|
|
3751
|
+
emitEvent(registryRef.current, "change");
|
|
3752
|
+
try {
|
|
3753
|
+
onChangeRef.current?.();
|
|
3754
|
+
} catch {
|
|
3755
|
+
}
|
|
3756
|
+
},
|
|
3757
|
+
addEventListener(type, listener) {
|
|
3758
|
+
const arr = registryRef.current[type];
|
|
3759
|
+
if (!arr.includes(listener)) arr.push(listener);
|
|
3760
|
+
},
|
|
3761
|
+
removeEventListener(type, listener) {
|
|
3762
|
+
const arr = registryRef.current[type];
|
|
3763
|
+
const idx = arr.indexOf(listener);
|
|
3764
|
+
if (idx >= 0) arr.splice(idx, 1);
|
|
3765
|
+
},
|
|
3766
|
+
hasEventListener(type, listener) {
|
|
3767
|
+
return registryRef.current[type].includes(listener);
|
|
3768
|
+
}
|
|
3769
|
+
}));
|
|
3770
|
+
function forwardDir(rotX, rotY) {
|
|
3771
|
+
const rx = rotX * Math.PI / 180;
|
|
3772
|
+
const ry = rotY * Math.PI / 180;
|
|
3773
|
+
return [
|
|
3774
|
+
-Math.sin(rx) * Math.cos(ry),
|
|
3775
|
+
-Math.sin(rx) * Math.sin(ry),
|
|
3776
|
+
-Math.cos(rx)
|
|
3777
|
+
];
|
|
3778
|
+
}
|
|
3779
|
+
function lookOffset() {
|
|
3780
|
+
const host = cameraElRef.current;
|
|
3781
|
+
const perspStr = host ? getComputedStyle(host).perspective : "";
|
|
3782
|
+
const n = parseFloat(perspStr);
|
|
3783
|
+
return (Number.isFinite(n) && n > 0 ? n : 8e3) / import_polycss_core7.BASE_TILE;
|
|
3784
|
+
}
|
|
3785
|
+
function deriveTarget() {
|
|
3786
|
+
const s = cameraRef.current.state;
|
|
3787
|
+
const f = forwardDir(s.rotX ?? 90, s.rotY ?? 0);
|
|
3788
|
+
const d = lookOffset();
|
|
3789
|
+
const o = cameraOriginRef.current;
|
|
3790
|
+
return [o[0] + f[0] * d, o[1] + f[1] * d, o[2] + f[2] * d];
|
|
3791
|
+
}
|
|
3792
|
+
function syncTargetFromOrigin() {
|
|
3793
|
+
const t = deriveTarget();
|
|
3794
|
+
const handle = cameraRef.current;
|
|
3795
|
+
handle.update({ target: t });
|
|
3796
|
+
applyTransformDirect();
|
|
3797
|
+
store.updateCameraFromRef(handle);
|
|
3798
|
+
}
|
|
3799
|
+
const ANIM_DT_CLAMP = 0.05;
|
|
3800
|
+
function tick(now) {
|
|
3801
|
+
if (rafIdRef.current === null || stoppedRef.current) return;
|
|
3802
|
+
const dt = Math.min(ANIM_DT_CLAMP, lastTimeRef.current ? (now - lastTimeRef.current) / 1e3 : 0.0167);
|
|
3803
|
+
lastTimeRef.current = now;
|
|
3804
|
+
const opts = optsRef.current;
|
|
3805
|
+
if (opts.enabled) {
|
|
3806
|
+
let dirty = false;
|
|
3807
|
+
const s = cameraRef.current.state;
|
|
3808
|
+
const o = cameraOriginRef.current;
|
|
3809
|
+
if (opts.moveEnabled) {
|
|
3810
|
+
let mf = 0, mr = 0;
|
|
3811
|
+
for (const code of keysHeldRef.current) {
|
|
3812
|
+
if (FORWARD_KEYS.has(code)) mf += 1;
|
|
3813
|
+
else if (BACK_KEYS.has(code)) mf -= 1;
|
|
3814
|
+
else if (RIGHT_KEYS.has(code)) mr += 1;
|
|
3815
|
+
else if (LEFT_KEYS.has(code)) mr -= 1;
|
|
3816
|
+
}
|
|
3817
|
+
if (mf !== 0 || mr !== 0) {
|
|
3818
|
+
const rotY = s.rotY ?? 0;
|
|
3819
|
+
const r = rotY * Math.PI / 180;
|
|
3820
|
+
const fx = -Math.cos(r), fy = -Math.sin(r);
|
|
3821
|
+
const rx = -Math.sin(r), ry = Math.cos(r);
|
|
3822
|
+
const len = Math.hypot(mf, mr) || 1;
|
|
3823
|
+
const step = opts.moveSpeed * dt;
|
|
3824
|
+
o[0] += (fx * mf + rx * mr) / len * step;
|
|
3825
|
+
o[1] += (fy * mf + ry * mr) / len * step;
|
|
3826
|
+
dirty = true;
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
const crouched = opts.crouchEnabled && (keysHeldRef.current.has("ControlLeft") || keysHeldRef.current.has("ControlRight"));
|
|
3830
|
+
const baseHeight = crouched ? opts.crouchHeight : opts.eyeHeight;
|
|
3831
|
+
if (opts.jumpEnabled && (verticalVelRef.current !== 0 || jumpOffsetRef.current > 0)) {
|
|
3832
|
+
verticalVelRef.current -= opts.gravity * dt;
|
|
3833
|
+
jumpOffsetRef.current += verticalVelRef.current * dt;
|
|
3834
|
+
if (jumpOffsetRef.current <= 0) {
|
|
3835
|
+
jumpOffsetRef.current = 0;
|
|
3836
|
+
verticalVelRef.current = 0;
|
|
3837
|
+
}
|
|
3838
|
+
} else if (!opts.jumpEnabled) {
|
|
3839
|
+
jumpOffsetRef.current = 0;
|
|
3840
|
+
verticalVelRef.current = 0;
|
|
3841
|
+
}
|
|
3842
|
+
const originZ = opts.groundZ + baseHeight + jumpOffsetRef.current;
|
|
3843
|
+
if (Math.abs(o[2] - originZ) > 1e-4) {
|
|
3844
|
+
o[2] = originZ;
|
|
3845
|
+
dirty = true;
|
|
3846
|
+
}
|
|
3847
|
+
if (dirty) {
|
|
3848
|
+
const t = deriveTarget();
|
|
3849
|
+
const handle = cameraRef.current;
|
|
3850
|
+
handle.update({ target: t });
|
|
3851
|
+
applyTransformDirect();
|
|
3852
|
+
store.updateCameraFromRef(handle);
|
|
3853
|
+
emitEvent(registryRef.current, "change");
|
|
3854
|
+
try {
|
|
3855
|
+
onChangeRef.current?.();
|
|
3856
|
+
} catch {
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
rafIdRef.current = requestAnimationFrame(tick);
|
|
3861
|
+
}
|
|
3862
|
+
function startLoop() {
|
|
3863
|
+
if (rafIdRef.current !== null || stoppedRef.current) return;
|
|
3864
|
+
lastTimeRef.current = 0;
|
|
3865
|
+
rafIdRef.current = requestAnimationFrame(tick);
|
|
3866
|
+
}
|
|
3867
|
+
function stopLoop() {
|
|
3868
|
+
if (rafIdRef.current === null) return;
|
|
3869
|
+
cancelAnimationFrame(rafIdRef.current);
|
|
3870
|
+
rafIdRef.current = null;
|
|
3871
|
+
}
|
|
3872
|
+
(0, import_react15.useEffect)(() => {
|
|
3873
|
+
const host = cameraElRef.current;
|
|
3874
|
+
if (!host) return;
|
|
3875
|
+
const doc = host.ownerDocument ?? document;
|
|
3876
|
+
const win = doc.defaultView ?? globalThis;
|
|
3877
|
+
stoppedRef.current = false;
|
|
3878
|
+
pointerLockedRef.current = false;
|
|
3879
|
+
interactingRef.current = false;
|
|
3880
|
+
keysHeldRef.current.clear();
|
|
3881
|
+
verticalVelRef.current = 0;
|
|
3882
|
+
jumpOffsetRef.current = 0;
|
|
3883
|
+
const s = cameraRef.current.state;
|
|
3884
|
+
const t = s.target ?? [0, 0, 0];
|
|
3885
|
+
const opts = optsRef.current;
|
|
3886
|
+
cameraOriginRef.current = [t[0], t[1], opts.groundZ + opts.eyeHeight];
|
|
3887
|
+
syncTargetFromOrigin();
|
|
3888
|
+
host.style.cursor = opts.lookEnabled ? "crosshair" : "";
|
|
3889
|
+
const computedPersp = win.getComputedStyle(host).perspective;
|
|
3890
|
+
const persp = parseFloat(computedPersp);
|
|
3891
|
+
const effectivePersp = Number.isFinite(persp) && persp > 0 ? persp : 2e3;
|
|
3892
|
+
host.style.setProperty("--polycss-fpv-perspective", `${effectivePersp}px`);
|
|
3893
|
+
host.classList.add("polycss-fpv-host");
|
|
3894
|
+
const onHostClick = () => {
|
|
3895
|
+
const o = optsRef.current;
|
|
3896
|
+
if (!o.enabled || !o.lookEnabled || stoppedRef.current || pointerLockedRef.current) return;
|
|
3897
|
+
try {
|
|
3898
|
+
host.requestPointerLock();
|
|
3899
|
+
} catch {
|
|
3900
|
+
}
|
|
3901
|
+
};
|
|
3902
|
+
const onPointerLockChange = () => {
|
|
3903
|
+
const locked = doc.pointerLockElement === host;
|
|
3904
|
+
if (locked === pointerLockedRef.current) return;
|
|
3905
|
+
pointerLockedRef.current = locked;
|
|
3906
|
+
if (locked) {
|
|
3907
|
+
interactingRef.current = true;
|
|
3908
|
+
emitEvent(registryRef.current, "start");
|
|
3909
|
+
try {
|
|
3910
|
+
onInteractionStartRef.current?.();
|
|
3911
|
+
} catch {
|
|
3912
|
+
}
|
|
3913
|
+
} else {
|
|
3914
|
+
if (interactingRef.current) {
|
|
3915
|
+
interactingRef.current = false;
|
|
3916
|
+
emitEvent(registryRef.current, "end");
|
|
3917
|
+
try {
|
|
3918
|
+
onInteractionEndRef.current?.();
|
|
3919
|
+
} catch {
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
};
|
|
3924
|
+
const onMouseMove = (e) => {
|
|
3925
|
+
if (!pointerLockedRef.current || stoppedRef.current) return;
|
|
3926
|
+
const o = optsRef.current;
|
|
3927
|
+
if (!o.enabled || !o.lookEnabled) return;
|
|
3928
|
+
const dx = e.movementX ?? 0;
|
|
3929
|
+
const dy = e.movementY ?? 0;
|
|
3930
|
+
if (dx === 0 && dy === 0) return;
|
|
3931
|
+
const handle = cameraRef.current;
|
|
3932
|
+
const sceneOpts = handle.state;
|
|
3933
|
+
const sens = o.lookSensitivity;
|
|
3934
|
+
const dyDir = o.invertY ? -1 : 1;
|
|
3935
|
+
const rotY = (((sceneOpts.rotY ?? 0) - dx * sens) % 360 + 360) % 360;
|
|
3936
|
+
let rotX = (sceneOpts.rotX ?? 90) - dy * sens * dyDir;
|
|
3937
|
+
if (rotX < o.minPitch) rotX = o.minPitch;
|
|
3938
|
+
else if (rotX > o.maxPitch) rotX = o.maxPitch;
|
|
3939
|
+
const f = forwardDir(rotX, rotY);
|
|
3940
|
+
const d = lookOffset();
|
|
3941
|
+
const origin = cameraOriginRef.current;
|
|
3942
|
+
const target = [
|
|
3943
|
+
origin[0] + f[0] * d,
|
|
3944
|
+
origin[1] + f[1] * d,
|
|
3945
|
+
origin[2] + f[2] * d
|
|
3946
|
+
];
|
|
3947
|
+
handle.update({ rotX, rotY, target });
|
|
3948
|
+
applyTransformDirect();
|
|
3949
|
+
store.updateCameraFromRef(handle);
|
|
3950
|
+
emitEvent(registryRef.current, "change");
|
|
3951
|
+
try {
|
|
3952
|
+
onChangeRef.current?.();
|
|
3953
|
+
} catch {
|
|
3954
|
+
}
|
|
3955
|
+
};
|
|
3956
|
+
const onKeyDown = (e) => {
|
|
3957
|
+
const o = optsRef.current;
|
|
3958
|
+
if (!o.enabled || stoppedRef.current) return;
|
|
3959
|
+
if (!isFpvKey(e.code)) return;
|
|
3960
|
+
if (!pointerLockedRef.current && !o.moveEnabled) return;
|
|
3961
|
+
if (JUMP_KEYS.has(e.code)) {
|
|
3962
|
+
if (!o.jumpEnabled) return;
|
|
3963
|
+
e.preventDefault();
|
|
3964
|
+
if (!keysHeldRef.current.has(e.code) && verticalVelRef.current === 0 && jumpOffsetRef.current === 0) {
|
|
3965
|
+
verticalVelRef.current = o.jumpVelocity;
|
|
3966
|
+
}
|
|
3967
|
+
keysHeldRef.current.add(e.code);
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (CROUCH_KEYS.has(e.code) && !o.crouchEnabled) return;
|
|
3971
|
+
if (!o.moveEnabled && !CROUCH_KEYS.has(e.code)) return;
|
|
3972
|
+
e.preventDefault();
|
|
3973
|
+
keysHeldRef.current.add(e.code);
|
|
3974
|
+
};
|
|
3975
|
+
const onKeyUp = (e) => {
|
|
3976
|
+
if (!isFpvKey(e.code)) return;
|
|
3977
|
+
keysHeldRef.current.delete(e.code);
|
|
3978
|
+
};
|
|
3979
|
+
const onBlur = () => {
|
|
3980
|
+
keysHeldRef.current.clear();
|
|
3981
|
+
};
|
|
3982
|
+
host.addEventListener("click", onHostClick);
|
|
3983
|
+
doc.addEventListener("pointerlockchange", onPointerLockChange);
|
|
3984
|
+
doc.addEventListener("mousemove", onMouseMove);
|
|
3985
|
+
win.addEventListener("keydown", onKeyDown);
|
|
3986
|
+
win.addEventListener("keyup", onKeyUp);
|
|
3987
|
+
win.addEventListener("blur", onBlur);
|
|
3988
|
+
startLoop();
|
|
3989
|
+
return () => {
|
|
3990
|
+
stoppedRef.current = true;
|
|
3991
|
+
stopLoop();
|
|
3992
|
+
host.removeEventListener("click", onHostClick);
|
|
3993
|
+
doc.removeEventListener("pointerlockchange", onPointerLockChange);
|
|
3994
|
+
doc.removeEventListener("mousemove", onMouseMove);
|
|
3995
|
+
win.removeEventListener("keydown", onKeyDown);
|
|
3996
|
+
win.removeEventListener("keyup", onKeyUp);
|
|
3997
|
+
win.removeEventListener("blur", onBlur);
|
|
3998
|
+
host.style.cursor = "";
|
|
3999
|
+
host.classList.remove("polycss-fpv-host");
|
|
4000
|
+
host.style.removeProperty("--polycss-fpv-perspective");
|
|
4001
|
+
keysHeldRef.current.clear();
|
|
4002
|
+
if (pointerLockedRef.current) {
|
|
4003
|
+
try {
|
|
4004
|
+
doc.exitPointerLock();
|
|
4005
|
+
} catch {
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
};
|
|
4009
|
+
}, [cameraElRef, cameraRef, applyTransformDirect, store]);
|
|
4010
|
+
(0, import_react15.useEffect)(() => {
|
|
4011
|
+
optsRef.current = resolveOptions(optsRef.current, props);
|
|
4012
|
+
const host = cameraElRef.current;
|
|
4013
|
+
if (host && !stoppedRef.current) {
|
|
4014
|
+
host.style.cursor = optsRef.current.lookEnabled ? "crosshair" : "";
|
|
4015
|
+
}
|
|
4016
|
+
});
|
|
4017
|
+
return null;
|
|
4018
|
+
});
|
|
2485
4019
|
|
|
2486
|
-
// src/controls/
|
|
2487
|
-
var
|
|
4020
|
+
// src/controls/PolyMapControls.tsx
|
|
4021
|
+
var import_react16 = require("react");
|
|
2488
4022
|
|
|
2489
4023
|
// src/controls/sharedControls.ts
|
|
2490
|
-
var
|
|
4024
|
+
var import_polycss_core8 = require("@layoutit/polycss-core");
|
|
2491
4025
|
var POINTER_DRAG_SPEED = 4;
|
|
2492
4026
|
function invertFactor(invert) {
|
|
2493
4027
|
if (invert === true) return -1;
|
|
@@ -2508,7 +4042,7 @@ function applyPan(dx, dy, s, handle, _invert) {
|
|
|
2508
4042
|
const cosRotX = cosRotXRaw >= 0 ? Math.max(0.1, cosRotXRaw) : Math.min(-0.1, cosRotXRaw);
|
|
2509
4043
|
const cZ = Math.cos(s.rotY * Math.PI / 180);
|
|
2510
4044
|
const sZ = Math.sin(s.rotY * Math.PI / 180);
|
|
2511
|
-
const k = z *
|
|
4045
|
+
const k = z * import_polycss_core8.BASE_TILE;
|
|
2512
4046
|
const targetD0 = (dx * sZ - dy * cZ / cosRotX) / k;
|
|
2513
4047
|
const targetD1 = -(dx * cZ + dy * sZ / cosRotX) / k;
|
|
2514
4048
|
const t = s.target;
|
|
@@ -2641,8 +4175,8 @@ function makeAnimateEffect({
|
|
|
2641
4175
|
};
|
|
2642
4176
|
}
|
|
2643
4177
|
|
|
2644
|
-
// src/controls/
|
|
2645
|
-
function
|
|
4178
|
+
// src/controls/PolyMapControls.tsx
|
|
4179
|
+
function PolyMapControls({
|
|
2646
4180
|
drag = true,
|
|
2647
4181
|
wheel = true,
|
|
2648
4182
|
dolly = false,
|
|
@@ -2657,19 +4191,19 @@ function PolyOrbitControls({
|
|
|
2657
4191
|
onInteractionEnd
|
|
2658
4192
|
}) {
|
|
2659
4193
|
const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
|
|
2660
|
-
const dragRef = (0,
|
|
2661
|
-
const wheelRef = (0,
|
|
2662
|
-
const dollyRef = (0,
|
|
2663
|
-
const invertRef = (0,
|
|
2664
|
-
const zoomMinRef = (0,
|
|
2665
|
-
const zoomMaxRef = (0,
|
|
2666
|
-
const distanceMinRef = (0,
|
|
2667
|
-
const distanceMaxRef = (0,
|
|
2668
|
-
const animateRef = (0,
|
|
2669
|
-
const onChangeRef = (0,
|
|
2670
|
-
const onInteractionStartRef = (0,
|
|
2671
|
-
const onInteractionEndRef = (0,
|
|
2672
|
-
(0,
|
|
4194
|
+
const dragRef = (0, import_react16.useRef)(drag);
|
|
4195
|
+
const wheelRef = (0, import_react16.useRef)(wheel);
|
|
4196
|
+
const dollyRef = (0, import_react16.useRef)(dolly);
|
|
4197
|
+
const invertRef = (0, import_react16.useRef)(invert);
|
|
4198
|
+
const zoomMinRef = (0, import_react16.useRef)(minZoom);
|
|
4199
|
+
const zoomMaxRef = (0, import_react16.useRef)(maxZoom);
|
|
4200
|
+
const distanceMinRef = (0, import_react16.useRef)(minDistance);
|
|
4201
|
+
const distanceMaxRef = (0, import_react16.useRef)(maxDistance);
|
|
4202
|
+
const animateRef = (0, import_react16.useRef)(animate);
|
|
4203
|
+
const onChangeRef = (0, import_react16.useRef)(onChange);
|
|
4204
|
+
const onInteractionStartRef = (0, import_react16.useRef)(onInteractionStart);
|
|
4205
|
+
const onInteractionEndRef = (0, import_react16.useRef)(onInteractionEnd);
|
|
4206
|
+
(0, import_react16.useEffect)(() => {
|
|
2673
4207
|
dragRef.current = drag;
|
|
2674
4208
|
wheelRef.current = wheel;
|
|
2675
4209
|
dollyRef.current = dolly;
|
|
@@ -2693,7 +4227,7 @@ function PolyOrbitControls({
|
|
|
2693
4227
|
try {
|
|
2694
4228
|
fn(cameraSnapshot());
|
|
2695
4229
|
} catch (err) {
|
|
2696
|
-
console.error("[polycss/react]
|
|
4230
|
+
console.error("[polycss/react] PolyMapControls onChange threw:", err);
|
|
2697
4231
|
}
|
|
2698
4232
|
};
|
|
2699
4233
|
const fireStart = () => {
|
|
@@ -2702,7 +4236,7 @@ function PolyOrbitControls({
|
|
|
2702
4236
|
try {
|
|
2703
4237
|
fn(cameraSnapshot());
|
|
2704
4238
|
} catch (err) {
|
|
2705
|
-
console.error("[polycss/react]
|
|
4239
|
+
console.error("[polycss/react] PolyMapControls onInteractionStart threw:", err);
|
|
2706
4240
|
}
|
|
2707
4241
|
};
|
|
2708
4242
|
const fireEnd = () => {
|
|
@@ -2711,11 +4245,11 @@ function PolyOrbitControls({
|
|
|
2711
4245
|
try {
|
|
2712
4246
|
fn(cameraSnapshot());
|
|
2713
4247
|
} catch (err) {
|
|
2714
|
-
console.error("[polycss/react]
|
|
4248
|
+
console.error("[polycss/react] PolyMapControls onInteractionEnd threw:", err);
|
|
2715
4249
|
}
|
|
2716
4250
|
};
|
|
2717
|
-
const animationPausedShared = (0,
|
|
2718
|
-
(0,
|
|
4251
|
+
const animationPausedShared = (0, import_react16.useRef)({ value: false }).current;
|
|
4252
|
+
(0, import_react16.useEffect)(() => {
|
|
2719
4253
|
if (!drag) return;
|
|
2720
4254
|
const el = cameraElRef.current;
|
|
2721
4255
|
if (!el) return;
|
|
@@ -2752,9 +4286,9 @@ function PolyOrbitControls({
|
|
|
2752
4286
|
pointer = { x: e.clientX, y: e.clientY };
|
|
2753
4287
|
const handle = cameraRef.current;
|
|
2754
4288
|
if (e.shiftKey) {
|
|
2755
|
-
buildOrbitControls.applyPan(dx, dy, handle.state, handle, invertRef.current);
|
|
2756
|
-
} else {
|
|
2757
4289
|
buildOrbitControls.applyOrbit(dx, dy, handle.state, handle, invertRef.current);
|
|
4290
|
+
} else {
|
|
4291
|
+
buildOrbitControls.applyPan(dx, dy, handle.state, handle, invertRef.current);
|
|
2758
4292
|
}
|
|
2759
4293
|
applyTransformDirect();
|
|
2760
4294
|
store.updateCameraFromRef(handle);
|
|
@@ -2788,7 +4322,7 @@ function PolyOrbitControls({
|
|
|
2788
4322
|
const dy = e.clientY - rightPointer.y;
|
|
2789
4323
|
rightPointer = { x: e.clientX, y: e.clientY };
|
|
2790
4324
|
const handle = cameraRef.current;
|
|
2791
|
-
buildOrbitControls.
|
|
4325
|
+
buildOrbitControls.applyOrbit(dx, dy, handle.state, handle, invertRef.current);
|
|
2792
4326
|
applyTransformDirect();
|
|
2793
4327
|
store.updateCameraFromRef(handle);
|
|
2794
4328
|
fireChange();
|
|
@@ -2825,12 +4359,12 @@ function PolyOrbitControls({
|
|
|
2825
4359
|
el.style.userSelect = "";
|
|
2826
4360
|
};
|
|
2827
4361
|
}, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
|
|
2828
|
-
(0,
|
|
4362
|
+
(0, import_react16.useEffect)(
|
|
2829
4363
|
() => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
|
|
2830
4364
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2831
4365
|
[wheel, applyTransformDirect, cameraElRef, cameraRef, store]
|
|
2832
4366
|
);
|
|
2833
|
-
(0,
|
|
4367
|
+
(0, import_react16.useEffect)(
|
|
2834
4368
|
() => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
|
|
2835
4369
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2836
4370
|
[!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
|
|
@@ -2838,9 +4372,9 @@ function PolyOrbitControls({
|
|
|
2838
4372
|
return null;
|
|
2839
4373
|
}
|
|
2840
4374
|
|
|
2841
|
-
// src/controls/
|
|
2842
|
-
var
|
|
2843
|
-
function
|
|
4375
|
+
// src/controls/PolyOrbitControls.tsx
|
|
4376
|
+
var import_react17 = require("react");
|
|
4377
|
+
function PolyOrbitControls({
|
|
2844
4378
|
drag = true,
|
|
2845
4379
|
wheel = true,
|
|
2846
4380
|
dolly = false,
|
|
@@ -2855,19 +4389,19 @@ function PolyMapControls({
|
|
|
2855
4389
|
onInteractionEnd
|
|
2856
4390
|
}) {
|
|
2857
4391
|
const { store, cameraRef, cameraElRef, applyTransformDirect } = useCameraContext();
|
|
2858
|
-
const dragRef = (0,
|
|
2859
|
-
const wheelRef = (0,
|
|
2860
|
-
const dollyRef = (0,
|
|
2861
|
-
const invertRef = (0,
|
|
2862
|
-
const zoomMinRef = (0,
|
|
2863
|
-
const zoomMaxRef = (0,
|
|
2864
|
-
const distanceMinRef = (0,
|
|
2865
|
-
const distanceMaxRef = (0,
|
|
2866
|
-
const animateRef = (0,
|
|
2867
|
-
const onChangeRef = (0,
|
|
2868
|
-
const onInteractionStartRef = (0,
|
|
2869
|
-
const onInteractionEndRef = (0,
|
|
2870
|
-
(0,
|
|
4392
|
+
const dragRef = (0, import_react17.useRef)(drag);
|
|
4393
|
+
const wheelRef = (0, import_react17.useRef)(wheel);
|
|
4394
|
+
const dollyRef = (0, import_react17.useRef)(dolly);
|
|
4395
|
+
const invertRef = (0, import_react17.useRef)(invert);
|
|
4396
|
+
const zoomMinRef = (0, import_react17.useRef)(minZoom);
|
|
4397
|
+
const zoomMaxRef = (0, import_react17.useRef)(maxZoom);
|
|
4398
|
+
const distanceMinRef = (0, import_react17.useRef)(minDistance);
|
|
4399
|
+
const distanceMaxRef = (0, import_react17.useRef)(maxDistance);
|
|
4400
|
+
const animateRef = (0, import_react17.useRef)(animate);
|
|
4401
|
+
const onChangeRef = (0, import_react17.useRef)(onChange);
|
|
4402
|
+
const onInteractionStartRef = (0, import_react17.useRef)(onInteractionStart);
|
|
4403
|
+
const onInteractionEndRef = (0, import_react17.useRef)(onInteractionEnd);
|
|
4404
|
+
(0, import_react17.useEffect)(() => {
|
|
2871
4405
|
dragRef.current = drag;
|
|
2872
4406
|
wheelRef.current = wheel;
|
|
2873
4407
|
dollyRef.current = dolly;
|
|
@@ -2891,7 +4425,7 @@ function PolyMapControls({
|
|
|
2891
4425
|
try {
|
|
2892
4426
|
fn(cameraSnapshot());
|
|
2893
4427
|
} catch (err) {
|
|
2894
|
-
console.error("[polycss/react]
|
|
4428
|
+
console.error("[polycss/react] PolyOrbitControls onChange threw:", err);
|
|
2895
4429
|
}
|
|
2896
4430
|
};
|
|
2897
4431
|
const fireStart = () => {
|
|
@@ -2900,7 +4434,7 @@ function PolyMapControls({
|
|
|
2900
4434
|
try {
|
|
2901
4435
|
fn(cameraSnapshot());
|
|
2902
4436
|
} catch (err) {
|
|
2903
|
-
console.error("[polycss/react]
|
|
4437
|
+
console.error("[polycss/react] PolyOrbitControls onInteractionStart threw:", err);
|
|
2904
4438
|
}
|
|
2905
4439
|
};
|
|
2906
4440
|
const fireEnd = () => {
|
|
@@ -2909,11 +4443,11 @@ function PolyMapControls({
|
|
|
2909
4443
|
try {
|
|
2910
4444
|
fn(cameraSnapshot());
|
|
2911
4445
|
} catch (err) {
|
|
2912
|
-
console.error("[polycss/react]
|
|
4446
|
+
console.error("[polycss/react] PolyOrbitControls onInteractionEnd threw:", err);
|
|
2913
4447
|
}
|
|
2914
4448
|
};
|
|
2915
|
-
const animationPausedShared = (0,
|
|
2916
|
-
(0,
|
|
4449
|
+
const animationPausedShared = (0, import_react17.useRef)({ value: false }).current;
|
|
4450
|
+
(0, import_react17.useEffect)(() => {
|
|
2917
4451
|
if (!drag) return;
|
|
2918
4452
|
const el = cameraElRef.current;
|
|
2919
4453
|
if (!el) return;
|
|
@@ -2950,9 +4484,9 @@ function PolyMapControls({
|
|
|
2950
4484
|
pointer = { x: e.clientX, y: e.clientY };
|
|
2951
4485
|
const handle = cameraRef.current;
|
|
2952
4486
|
if (e.shiftKey) {
|
|
2953
|
-
buildOrbitControls.applyOrbit(dx, dy, handle.state, handle, invertRef.current);
|
|
2954
|
-
} else {
|
|
2955
4487
|
buildOrbitControls.applyPan(dx, dy, handle.state, handle, invertRef.current);
|
|
4488
|
+
} else {
|
|
4489
|
+
buildOrbitControls.applyOrbit(dx, dy, handle.state, handle, invertRef.current);
|
|
2956
4490
|
}
|
|
2957
4491
|
applyTransformDirect();
|
|
2958
4492
|
store.updateCameraFromRef(handle);
|
|
@@ -2986,7 +4520,7 @@ function PolyMapControls({
|
|
|
2986
4520
|
const dy = e.clientY - rightPointer.y;
|
|
2987
4521
|
rightPointer = { x: e.clientX, y: e.clientY };
|
|
2988
4522
|
const handle = cameraRef.current;
|
|
2989
|
-
buildOrbitControls.
|
|
4523
|
+
buildOrbitControls.applyPan(dx, dy, handle.state, handle, invertRef.current);
|
|
2990
4524
|
applyTransformDirect();
|
|
2991
4525
|
store.updateCameraFromRef(handle);
|
|
2992
4526
|
fireChange();
|
|
@@ -3023,12 +4557,12 @@ function PolyMapControls({
|
|
|
3023
4557
|
el.style.userSelect = "";
|
|
3024
4558
|
};
|
|
3025
4559
|
}, [drag, applyTransformDirect, cameraElRef, cameraRef, store]);
|
|
3026
|
-
(0,
|
|
4560
|
+
(0, import_react17.useEffect)(
|
|
3027
4561
|
() => makeWheelEffect({ wheel, dollyRef, wheelRef, zoomMinRef, zoomMaxRef, distanceMinRef, distanceMaxRef, cameraElRef, cameraRef, applyTransformDirect, store, fireStart, fireChange, fireEnd }),
|
|
3028
4562
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3029
4563
|
[wheel, applyTransformDirect, cameraElRef, cameraRef, store]
|
|
3030
4564
|
);
|
|
3031
|
-
(0,
|
|
4565
|
+
(0, import_react17.useEffect)(
|
|
3032
4566
|
() => makeAnimateEffect({ animateOn: !!animate, animateRef, animationPausedShared, applyTransformDirect, cameraRef, store, fireChange }),
|
|
3033
4567
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3034
4568
|
[!!animate, animationPausedShared, applyTransformDirect, cameraRef, store]
|
|
@@ -3037,9 +4571,10 @@ function PolyMapControls({
|
|
|
3037
4571
|
}
|
|
3038
4572
|
|
|
3039
4573
|
// src/controls/TransformControls.tsx
|
|
3040
|
-
var
|
|
3041
|
-
var
|
|
3042
|
-
var
|
|
4574
|
+
var import_react18 = require("react");
|
|
4575
|
+
var import_polycss_core9 = require("@layoutit/polycss-core");
|
|
4576
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
4577
|
+
var FALLBACK_CAMERA_STORE = createSceneStore(import_polycss_core9.DEFAULT_CAMERA_STATE);
|
|
3043
4578
|
var COLOR_X = "#ff3653";
|
|
3044
4579
|
var COLOR_Y = "#8adb00";
|
|
3045
4580
|
var COLOR_Z = "#2c8fff";
|
|
@@ -3061,8 +4596,10 @@ var SHAFT_HALF_THICKNESS_RATIO = 0.0125;
|
|
|
3061
4596
|
var HEAD_LENGTH_RATIO = 0.15;
|
|
3062
4597
|
var HEAD_HALF_THICKNESS_RATIO = 0.04;
|
|
3063
4598
|
var RING_RADIUS_RATIO = 1;
|
|
3064
|
-
var RING_HALF_THICKNESS_RATIO = 0.
|
|
3065
|
-
var
|
|
4599
|
+
var RING_HALF_THICKNESS_RATIO = 0.02;
|
|
4600
|
+
var RING_QUAD_OUTER_RATIO = 1.04;
|
|
4601
|
+
var PLANE_HALF_SIZE_RATIO = 0.1;
|
|
4602
|
+
var PLANE_OFFSET_RATIO = 0.25;
|
|
3066
4603
|
var SCREEN_AXIS_DEAD_ZONE_SQ = 1e-4;
|
|
3067
4604
|
function gizmoLengthForMesh(polygons) {
|
|
3068
4605
|
if (polygons.length === 0) return FALLBACK_SHAFT_LENGTH;
|
|
@@ -3130,6 +4667,23 @@ function userAxisLetterOf(key) {
|
|
|
3130
4667
|
const last = key.replace("-", "")[0];
|
|
3131
4668
|
return last;
|
|
3132
4669
|
}
|
|
4670
|
+
function isAxisBackFacing(cssAxis, sign, rotXDeg, rotYDeg) {
|
|
4671
|
+
const rx = rotXDeg * Math.PI / 180;
|
|
4672
|
+
const ry = rotYDeg * Math.PI / 180;
|
|
4673
|
+
const a = [0, 0, 0];
|
|
4674
|
+
a[cssAxis] = sign;
|
|
4675
|
+
const bx = a[0] * Math.cos(ry) - a[1] * Math.sin(ry);
|
|
4676
|
+
const by = a[0] * Math.sin(ry) + a[1] * Math.cos(ry);
|
|
4677
|
+
const bz = a[2];
|
|
4678
|
+
const cz = by * Math.sin(rx) + bz * Math.cos(rx);
|
|
4679
|
+
void bx;
|
|
4680
|
+
return cz < 0;
|
|
4681
|
+
}
|
|
4682
|
+
var PLANE_SPECS = [
|
|
4683
|
+
{ perpAxis: 2, axisA: 0, axisB: 1, key: "xy", color: COLOR_Z },
|
|
4684
|
+
{ perpAxis: 1, axisA: 0, axisB: 2, key: "xz", color: COLOR_Y },
|
|
4685
|
+
{ perpAxis: 0, axisA: 1, axisB: 2, key: "yz", color: COLOR_X }
|
|
4686
|
+
];
|
|
3133
4687
|
function startAxisDrag(opts) {
|
|
3134
4688
|
const {
|
|
3135
4689
|
cssAxis,
|
|
@@ -3197,6 +4751,84 @@ function startAxisDrag(opts) {
|
|
|
3197
4751
|
window.addEventListener("pointerup", handleUp);
|
|
3198
4752
|
window.addEventListener("pointercancel", handleUp);
|
|
3199
4753
|
}
|
|
4754
|
+
function startPlaneDrag(opts) {
|
|
4755
|
+
const {
|
|
4756
|
+
axisA,
|
|
4757
|
+
axisB,
|
|
4758
|
+
probeDistanceCss,
|
|
4759
|
+
wrapper,
|
|
4760
|
+
target,
|
|
4761
|
+
startClientX,
|
|
4762
|
+
startClientY,
|
|
4763
|
+
translationSnap,
|
|
4764
|
+
onChange,
|
|
4765
|
+
onObjectChange,
|
|
4766
|
+
onMouseDown,
|
|
4767
|
+
onMouseUp,
|
|
4768
|
+
onDraggingChanged
|
|
4769
|
+
} = opts;
|
|
4770
|
+
const axisAVec = [0, 0, 0];
|
|
4771
|
+
axisAVec[axisA] = 1;
|
|
4772
|
+
const axisBVec = [0, 0, 0];
|
|
4773
|
+
axisBVec[axisB] = 1;
|
|
4774
|
+
function probe(axisVec) {
|
|
4775
|
+
const el = wrapper.ownerDocument.createElement("div");
|
|
4776
|
+
el.style.position = "absolute";
|
|
4777
|
+
el.style.left = "0";
|
|
4778
|
+
el.style.top = "0";
|
|
4779
|
+
el.style.width = "0";
|
|
4780
|
+
el.style.height = "0";
|
|
4781
|
+
el.style.transform = `translate3d(${axisVec[0] * probeDistanceCss}px, ${axisVec[1] * probeDistanceCss}px, ${axisVec[2] * probeDistanceCss}px)`;
|
|
4782
|
+
wrapper.appendChild(el);
|
|
4783
|
+
const wR = wrapper.getBoundingClientRect();
|
|
4784
|
+
const pR = el.getBoundingClientRect();
|
|
4785
|
+
wrapper.removeChild(el);
|
|
4786
|
+
return {
|
|
4787
|
+
x: (pR.left - wR.left) / probeDistanceCss,
|
|
4788
|
+
y: (pR.top - wR.top) / probeDistanceCss
|
|
4789
|
+
};
|
|
4790
|
+
}
|
|
4791
|
+
const pA = probe(axisAVec);
|
|
4792
|
+
const pB = probe(axisBVec);
|
|
4793
|
+
const det = pA.x * pB.y - pB.x * pA.y;
|
|
4794
|
+
if (Math.abs(det) < SCREEN_AXIS_DEAD_ZONE_SQ) return;
|
|
4795
|
+
const startPos = target.getPosition() ?? [0, 0, 0];
|
|
4796
|
+
onMouseDown?.();
|
|
4797
|
+
onDraggingChanged?.(true);
|
|
4798
|
+
const handleMove = (ev) => {
|
|
4799
|
+
const dx = ev.clientX - startClientX;
|
|
4800
|
+
const dy = ev.clientY - startClientY;
|
|
4801
|
+
let tA = (pB.y * dx - pB.x * dy) / det;
|
|
4802
|
+
let tB = (-pA.y * dx + pA.x * dy) / det;
|
|
4803
|
+
if (translationSnap !== null) {
|
|
4804
|
+
tA = Math.round(tA / translationSnap) * translationSnap;
|
|
4805
|
+
tB = Math.round(tB / translationSnap) * translationSnap;
|
|
4806
|
+
}
|
|
4807
|
+
const next = [
|
|
4808
|
+
startPos[0] + tA * axisAVec[0] + tB * axisBVec[0],
|
|
4809
|
+
startPos[1] + tA * axisAVec[1] + tB * axisBVec[1],
|
|
4810
|
+
startPos[2] + tA * axisAVec[2] + tB * axisBVec[2]
|
|
4811
|
+
];
|
|
4812
|
+
onObjectChange?.({ object: target, position: next });
|
|
4813
|
+
onChange?.();
|
|
4814
|
+
};
|
|
4815
|
+
const handleUp = () => {
|
|
4816
|
+
window.removeEventListener("pointermove", handleMove);
|
|
4817
|
+
window.removeEventListener("pointerup", handleUp);
|
|
4818
|
+
window.removeEventListener("pointercancel", handleUp);
|
|
4819
|
+
const swallow = (e) => {
|
|
4820
|
+
e.stopPropagation();
|
|
4821
|
+
e.stopImmediatePropagation();
|
|
4822
|
+
};
|
|
4823
|
+
window.addEventListener("click", swallow, { capture: true, once: true });
|
|
4824
|
+
setTimeout(() => window.removeEventListener("click", swallow, true), 0);
|
|
4825
|
+
onMouseUp?.();
|
|
4826
|
+
onDraggingChanged?.(false);
|
|
4827
|
+
};
|
|
4828
|
+
window.addEventListener("pointermove", handleMove);
|
|
4829
|
+
window.addEventListener("pointerup", handleUp);
|
|
4830
|
+
window.addEventListener("pointercancel", handleUp);
|
|
4831
|
+
}
|
|
3200
4832
|
function startRingDrag(opts) {
|
|
3201
4833
|
const {
|
|
3202
4834
|
cssAxis,
|
|
@@ -3219,6 +4851,7 @@ function startRingDrag(opts) {
|
|
|
3219
4851
|
const startRotation = target.getRotation() ?? [0, 0, 0];
|
|
3220
4852
|
onMouseDown?.();
|
|
3221
4853
|
onDraggingChanged?.(true);
|
|
4854
|
+
const qStart = (0, import_polycss_core9.quatFromEulerXYZ)(startRotation);
|
|
3222
4855
|
const handleMove = (ev) => {
|
|
3223
4856
|
const a = Math.atan2(ev.clientY - centerY, ev.clientX - centerX);
|
|
3224
4857
|
let d = a - lastAngle;
|
|
@@ -3228,12 +4861,10 @@ function startRingDrag(opts) {
|
|
|
3228
4861
|
lastAngle = a;
|
|
3229
4862
|
let degrees = cumulative * 180 / Math.PI;
|
|
3230
4863
|
degrees = snap(degrees, rotationSnap);
|
|
3231
|
-
const
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
];
|
|
3236
|
-
newRotation[cssAxis] = startRotation[cssAxis] + degrees;
|
|
4864
|
+
const axisVec = [0, 0, 0];
|
|
4865
|
+
axisVec[cssAxis] = 1;
|
|
4866
|
+
const qDelta = (0, import_polycss_core9.quatFromAxisAngle)(axisVec, degrees * Math.PI / 180);
|
|
4867
|
+
const newRotation = (0, import_polycss_core9.eulerXYZFromQuat)((0, import_polycss_core9.quatMultiply)(qDelta, qStart));
|
|
3237
4868
|
onObjectChange?.({ object: target, rotation: newRotation });
|
|
3238
4869
|
onChange?.();
|
|
3239
4870
|
};
|
|
@@ -3272,15 +4903,24 @@ function PolyTransformControls({
|
|
|
3272
4903
|
onMouseUp,
|
|
3273
4904
|
onDraggingChanged
|
|
3274
4905
|
}) {
|
|
3275
|
-
const [, forceRender] = (0,
|
|
3276
|
-
(0,
|
|
4906
|
+
const [, forceRender] = (0, import_react18.useState)(0);
|
|
4907
|
+
(0, import_react18.useEffect)(() => {
|
|
3277
4908
|
forceRender((n) => n + 1);
|
|
3278
4909
|
}, [object]);
|
|
3279
|
-
const
|
|
3280
|
-
const
|
|
3281
|
-
|
|
4910
|
+
const cameraCtxForRot = (0, import_react18.useContext)(PolyCameraContext);
|
|
4911
|
+
const rotX = useStoreSelector(
|
|
4912
|
+
cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
|
|
4913
|
+
(s) => s.cameraState.rotX
|
|
4914
|
+
);
|
|
4915
|
+
const rotY = useStoreSelector(
|
|
4916
|
+
cameraCtxForRot?.store ?? FALLBACK_CAMERA_STORE,
|
|
4917
|
+
(s) => s.cameraState.rotY
|
|
4918
|
+
);
|
|
4919
|
+
const [hoveredKey, setHoveredKey] = (0, import_react18.useState)(null);
|
|
4920
|
+
const [draggingKey, setDraggingKey] = (0, import_react18.useState)(null);
|
|
4921
|
+
const cameraCtx = (0, import_react18.useContext)(PolyCameraContext);
|
|
3282
4922
|
const cameraElRef = cameraCtx?.cameraElRef;
|
|
3283
|
-
const dragRef = (0,
|
|
4923
|
+
const dragRef = (0, import_react18.useRef)({
|
|
3284
4924
|
target: null,
|
|
3285
4925
|
mode: "translate",
|
|
3286
4926
|
shaftLengthCss: 0,
|
|
@@ -3289,7 +4929,7 @@ function PolyTransformControls({
|
|
|
3289
4929
|
translationSnap: null,
|
|
3290
4930
|
rotationSnap: null
|
|
3291
4931
|
});
|
|
3292
|
-
(0,
|
|
4932
|
+
(0, import_react18.useEffect)(() => {
|
|
3293
4933
|
const cameraEl = cameraElRef?.current;
|
|
3294
4934
|
if (!cameraEl) return;
|
|
3295
4935
|
const onPointerDown = (event) => {
|
|
@@ -3298,6 +4938,42 @@ function PolyTransformControls({
|
|
|
3298
4938
|
const targetEl = event.target;
|
|
3299
4939
|
if (targetEl?.closest(".polycss-transform-gizmo")) return;
|
|
3300
4940
|
if (state.mode === "translate") {
|
|
4941
|
+
for (const spec of PLANE_SPECS) {
|
|
4942
|
+
const aL = ["x", "y", "z"][spec.axisA];
|
|
4943
|
+
const bL = ["x", "y", "z"][spec.axisB];
|
|
4944
|
+
if (!state.show[aL] || !state.show[bL]) continue;
|
|
4945
|
+
const planeEl = document.querySelector(
|
|
4946
|
+
`.polycss-transform-plane--${spec.key}`
|
|
4947
|
+
);
|
|
4948
|
+
if (!planeEl) continue;
|
|
4949
|
+
if (!pointInMeshElement(planeEl, event.clientX, event.clientY)) continue;
|
|
4950
|
+
event.preventDefault();
|
|
4951
|
+
event.stopPropagation();
|
|
4952
|
+
const wrapper = planeEl.closest(
|
|
4953
|
+
"[data-poly-transform-controls]"
|
|
4954
|
+
);
|
|
4955
|
+
if (!wrapper) return;
|
|
4956
|
+
setDraggingKey(spec.key);
|
|
4957
|
+
startPlaneDrag({
|
|
4958
|
+
axisA: spec.axisA,
|
|
4959
|
+
axisB: spec.axisB,
|
|
4960
|
+
probeDistanceCss: state.shaftLengthCss,
|
|
4961
|
+
wrapper,
|
|
4962
|
+
target: state.target,
|
|
4963
|
+
startClientX: event.clientX,
|
|
4964
|
+
startClientY: event.clientY,
|
|
4965
|
+
translationSnap: state.translationSnap,
|
|
4966
|
+
onChange: state.onChange,
|
|
4967
|
+
onObjectChange: state.onObjectChange,
|
|
4968
|
+
onMouseDown: state.onMouseDown,
|
|
4969
|
+
onMouseUp: state.onMouseUp,
|
|
4970
|
+
onDraggingChanged: (d) => {
|
|
4971
|
+
if (!d) setDraggingKey(null);
|
|
4972
|
+
state.onDraggingChanged?.(d);
|
|
4973
|
+
}
|
|
4974
|
+
});
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
3301
4977
|
for (const spec of ARROW_SPECS) {
|
|
3302
4978
|
if (!state.show[userAxisLetterOf(spec.key)]) continue;
|
|
3303
4979
|
const arrowEl = document.querySelector(
|
|
@@ -3397,9 +5073,9 @@ function PolyTransformControls({
|
|
|
3397
5073
|
onDraggingChanged
|
|
3398
5074
|
};
|
|
3399
5075
|
const wrapperStyle = {
|
|
5076
|
+
transform: `translate3d(${wrapperPos[0]}px, ${wrapperPos[1]}px, ${wrapperPos[2]}px)`,
|
|
3400
5077
|
position: "absolute",
|
|
3401
5078
|
transformStyle: "preserve-3d",
|
|
3402
|
-
transform: `translate3d(${wrapperPos[0]}px, ${wrapperPos[1]}px, ${wrapperPos[2]}px)`,
|
|
3403
5079
|
// No `pointer-events: none` here — that property is inherited, so
|
|
3404
5080
|
// setting it on the wrapper would cascade to every arrow polygon
|
|
3405
5081
|
// and disable native hit-testing on the gizmo entirely. The
|
|
@@ -3407,7 +5083,7 @@ function PolyTransformControls({
|
|
|
3407
5083
|
// own; descendants opt in via the default `auto`.
|
|
3408
5084
|
zIndex: 1e3
|
|
3409
5085
|
};
|
|
3410
|
-
return /* @__PURE__ */ (0,
|
|
5086
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3411
5087
|
"div",
|
|
3412
5088
|
{
|
|
3413
5089
|
className: "polycss-transform-controls",
|
|
@@ -3422,7 +5098,8 @@ function PolyTransformControls({
|
|
|
3422
5098
|
const hovered = hoveredKey === spec.key;
|
|
3423
5099
|
const dragging = draggingKey === spec.key;
|
|
3424
5100
|
const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
|
|
3425
|
-
|
|
5101
|
+
const backFacing = isAxisBackFacing(spec.cssAxis, spec.sign, rotX, rotY);
|
|
5102
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3426
5103
|
TranslateArrow,
|
|
3427
5104
|
{
|
|
3428
5105
|
cssAxis: spec.cssAxis,
|
|
@@ -3430,6 +5107,41 @@ function PolyTransformControls({
|
|
|
3430
5107
|
axisKey: spec.key,
|
|
3431
5108
|
color: withAlpha(spec.color, alpha),
|
|
3432
5109
|
shaftLengthCss,
|
|
5110
|
+
includeShaft: !backFacing,
|
|
5111
|
+
target,
|
|
5112
|
+
enabled,
|
|
5113
|
+
translationSnap,
|
|
5114
|
+
onChange,
|
|
5115
|
+
onObjectChange,
|
|
5116
|
+
onMouseDown,
|
|
5117
|
+
onMouseUp,
|
|
5118
|
+
onDraggingChanged,
|
|
5119
|
+
onHoverChange: (h) => setHoveredKey(h ? spec.key : (cur) => cur === spec.key ? null : cur),
|
|
5120
|
+
onDraggingStart: () => setDraggingKey(spec.key),
|
|
5121
|
+
onDraggingStop: () => setDraggingKey((cur) => cur === spec.key ? null : cur)
|
|
5122
|
+
},
|
|
5123
|
+
spec.key
|
|
5124
|
+
);
|
|
5125
|
+
}),
|
|
5126
|
+
mode === "translate" && PLANE_SPECS.map((spec) => {
|
|
5127
|
+
const aLetter = ["x", "y", "z"][spec.axisA];
|
|
5128
|
+
const bLetter = ["x", "y", "z"][spec.axisB];
|
|
5129
|
+
const show = { x: showX, y: showY, z: showZ }[aLetter] && { x: showX, y: showY, z: showZ }[bLetter];
|
|
5130
|
+
if (!show) return null;
|
|
5131
|
+
const hovered = hoveredKey === spec.key;
|
|
5132
|
+
const dragging = draggingKey === spec.key;
|
|
5133
|
+
const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
|
|
5134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
5135
|
+
TranslatePlane,
|
|
5136
|
+
{
|
|
5137
|
+
axisA: spec.axisA,
|
|
5138
|
+
axisB: spec.axisB,
|
|
5139
|
+
perpAxis: spec.perpAxis,
|
|
5140
|
+
planeKey: spec.key,
|
|
5141
|
+
color: withAlpha(spec.color, alpha),
|
|
5142
|
+
shaftLengthCss,
|
|
5143
|
+
rotX,
|
|
5144
|
+
rotY,
|
|
3433
5145
|
target,
|
|
3434
5146
|
enabled,
|
|
3435
5147
|
translationSnap,
|
|
@@ -3451,7 +5163,7 @@ function PolyTransformControls({
|
|
|
3451
5163
|
const hovered = hoveredKey === spec.key;
|
|
3452
5164
|
const dragging = draggingKey === spec.key;
|
|
3453
5165
|
const alpha = dragging ? ALPHA_DRAGGING : hovered ? ALPHA_HOVER : ALPHA_IDLE;
|
|
3454
|
-
return /* @__PURE__ */ (0,
|
|
5166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3455
5167
|
RotateRing,
|
|
3456
5168
|
{
|
|
3457
5169
|
cssAxis: spec.cssAxis,
|
|
@@ -3483,6 +5195,7 @@ function TranslateArrow({
|
|
|
3483
5195
|
axisKey,
|
|
3484
5196
|
color,
|
|
3485
5197
|
shaftLengthCss,
|
|
5198
|
+
includeShaft,
|
|
3486
5199
|
target,
|
|
3487
5200
|
enabled,
|
|
3488
5201
|
translationSnap,
|
|
@@ -3495,7 +5208,7 @@ function TranslateArrow({
|
|
|
3495
5208
|
onDraggingStart,
|
|
3496
5209
|
onDraggingStop
|
|
3497
5210
|
}) {
|
|
3498
|
-
const cbRef = (0,
|
|
5211
|
+
const cbRef = (0, import_react18.useRef)({
|
|
3499
5212
|
onChange,
|
|
3500
5213
|
onObjectChange,
|
|
3501
5214
|
onMouseDown,
|
|
@@ -3517,19 +5230,20 @@ function TranslateArrow({
|
|
|
3517
5230
|
enabled,
|
|
3518
5231
|
translationSnap
|
|
3519
5232
|
};
|
|
3520
|
-
const polygons = (0,
|
|
5233
|
+
const polygons = (0, import_react18.useMemo)(() => {
|
|
3521
5234
|
const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
|
|
3522
|
-
return (0,
|
|
5235
|
+
return (0, import_polycss_core9.arrowPolygons)({
|
|
3523
5236
|
axis: WORLD_AXIS_FOR_CSS[cssAxis],
|
|
3524
5237
|
sign,
|
|
3525
5238
|
shaftLength: lengthWorld,
|
|
3526
5239
|
shaftHalfThickness: lengthWorld * SHAFT_HALF_THICKNESS_RATIO,
|
|
3527
5240
|
headLength: lengthWorld * HEAD_LENGTH_RATIO,
|
|
3528
5241
|
headHalfThickness: lengthWorld * HEAD_HALF_THICKNESS_RATIO,
|
|
3529
|
-
color
|
|
5242
|
+
color,
|
|
5243
|
+
shaft: includeShaft
|
|
3530
5244
|
});
|
|
3531
|
-
}, [cssAxis, sign, color, shaftLengthCss]);
|
|
3532
|
-
const onPointerDown = (0,
|
|
5245
|
+
}, [cssAxis, sign, color, shaftLengthCss, includeShaft]);
|
|
5246
|
+
const onPointerDown = (0, import_react18.useCallback)(
|
|
3533
5247
|
(e) => {
|
|
3534
5248
|
if (!cbRef.current.enabled) return;
|
|
3535
5249
|
e.stopPropagation();
|
|
@@ -3558,7 +5272,7 @@ function TranslateArrow({
|
|
|
3558
5272
|
},
|
|
3559
5273
|
[cssAxis, sign, target, shaftLengthCss]
|
|
3560
5274
|
);
|
|
3561
|
-
return /* @__PURE__ */ (0,
|
|
5275
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3562
5276
|
PolyMesh,
|
|
3563
5277
|
{
|
|
3564
5278
|
polygons,
|
|
@@ -3570,6 +5284,107 @@ function TranslateArrow({
|
|
|
3570
5284
|
}
|
|
3571
5285
|
);
|
|
3572
5286
|
}
|
|
5287
|
+
function TranslatePlane({
|
|
5288
|
+
axisA,
|
|
5289
|
+
axisB,
|
|
5290
|
+
perpAxis,
|
|
5291
|
+
planeKey,
|
|
5292
|
+
color,
|
|
5293
|
+
shaftLengthCss,
|
|
5294
|
+
rotX,
|
|
5295
|
+
rotY,
|
|
5296
|
+
target,
|
|
5297
|
+
enabled,
|
|
5298
|
+
translationSnap,
|
|
5299
|
+
onChange,
|
|
5300
|
+
onObjectChange,
|
|
5301
|
+
onMouseDown,
|
|
5302
|
+
onMouseUp,
|
|
5303
|
+
onDraggingChanged,
|
|
5304
|
+
onHoverChange,
|
|
5305
|
+
onDraggingStart,
|
|
5306
|
+
onDraggingStop
|
|
5307
|
+
}) {
|
|
5308
|
+
const cbRef = (0, import_react18.useRef)({
|
|
5309
|
+
onChange,
|
|
5310
|
+
onObjectChange,
|
|
5311
|
+
onMouseDown,
|
|
5312
|
+
onMouseUp,
|
|
5313
|
+
onDraggingChanged,
|
|
5314
|
+
onDraggingStart,
|
|
5315
|
+
onDraggingStop,
|
|
5316
|
+
enabled,
|
|
5317
|
+
translationSnap
|
|
5318
|
+
});
|
|
5319
|
+
cbRef.current = {
|
|
5320
|
+
onChange,
|
|
5321
|
+
onObjectChange,
|
|
5322
|
+
onMouseDown,
|
|
5323
|
+
onMouseUp,
|
|
5324
|
+
onDraggingChanged,
|
|
5325
|
+
onDraggingStart,
|
|
5326
|
+
onDraggingStop,
|
|
5327
|
+
enabled,
|
|
5328
|
+
translationSnap
|
|
5329
|
+
};
|
|
5330
|
+
const polygons = (0, import_react18.useMemo)(() => {
|
|
5331
|
+
const lengthWorld = shaftLengthCss / SCENE_TILE_SIZE;
|
|
5332
|
+
const worldPerp = WORLD_AXIS_FOR_CSS[perpAxis];
|
|
5333
|
+
const worldA = (worldPerp + 1) % 3;
|
|
5334
|
+
const worldB = (worldPerp + 2) % 3;
|
|
5335
|
+
const cssAForOffset = WORLD_AXIS_FOR_CSS[worldA];
|
|
5336
|
+
const cssBForOffset = WORLD_AXIS_FOR_CSS[worldB];
|
|
5337
|
+
const signA = isAxisBackFacing(cssAForOffset, 1, rotX, rotY) ? -1 : 1;
|
|
5338
|
+
const signB = isAxisBackFacing(cssBForOffset, 1, rotX, rotY) ? -1 : 1;
|
|
5339
|
+
const mag = lengthWorld * PLANE_OFFSET_RATIO;
|
|
5340
|
+
return (0, import_polycss_core9.planePolygons)({
|
|
5341
|
+
axis: worldPerp,
|
|
5342
|
+
size: lengthWorld * PLANE_HALF_SIZE_RATIO,
|
|
5343
|
+
offset: [signA * mag, signB * mag],
|
|
5344
|
+
color
|
|
5345
|
+
});
|
|
5346
|
+
}, [perpAxis, color, shaftLengthCss, rotX, rotY]);
|
|
5347
|
+
const onPointerDown = (0, import_react18.useCallback)(
|
|
5348
|
+
(e) => {
|
|
5349
|
+
if (!cbRef.current.enabled) return;
|
|
5350
|
+
e.stopPropagation();
|
|
5351
|
+
const meshEl = e.eventObject.element;
|
|
5352
|
+
const wrapper = meshEl?.closest("[data-poly-transform-controls]");
|
|
5353
|
+
if (!wrapper) return;
|
|
5354
|
+
cbRef.current.onDraggingStart?.();
|
|
5355
|
+
startPlaneDrag({
|
|
5356
|
+
axisA,
|
|
5357
|
+
axisB,
|
|
5358
|
+
probeDistanceCss: shaftLengthCss,
|
|
5359
|
+
wrapper,
|
|
5360
|
+
target,
|
|
5361
|
+
startClientX: e.nativeEvent.clientX,
|
|
5362
|
+
startClientY: e.nativeEvent.clientY,
|
|
5363
|
+
translationSnap: cbRef.current.translationSnap,
|
|
5364
|
+
onChange: cbRef.current.onChange,
|
|
5365
|
+
onObjectChange: cbRef.current.onObjectChange,
|
|
5366
|
+
onMouseDown: cbRef.current.onMouseDown,
|
|
5367
|
+
onMouseUp: cbRef.current.onMouseUp,
|
|
5368
|
+
onDraggingChanged: (d) => {
|
|
5369
|
+
if (!d) cbRef.current.onDraggingStop?.();
|
|
5370
|
+
cbRef.current.onDraggingChanged?.(d);
|
|
5371
|
+
}
|
|
5372
|
+
});
|
|
5373
|
+
},
|
|
5374
|
+
[axisA, axisB, target, shaftLengthCss]
|
|
5375
|
+
);
|
|
5376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
5377
|
+
PolyMesh,
|
|
5378
|
+
{
|
|
5379
|
+
polygons,
|
|
5380
|
+
onPointerDown,
|
|
5381
|
+
onPointerOver: () => onHoverChange?.(true),
|
|
5382
|
+
onPointerOut: () => onHoverChange?.(false),
|
|
5383
|
+
className: `polycss-transform-gizmo polycss-transform-plane polycss-transform-plane--${planeKey}`,
|
|
5384
|
+
textureLighting: "baked"
|
|
5385
|
+
}
|
|
5386
|
+
);
|
|
5387
|
+
}
|
|
3573
5388
|
function RotateRing({
|
|
3574
5389
|
cssAxis,
|
|
3575
5390
|
axisKey,
|
|
@@ -3587,7 +5402,7 @@ function RotateRing({
|
|
|
3587
5402
|
onDraggingStart,
|
|
3588
5403
|
onDraggingStop
|
|
3589
5404
|
}) {
|
|
3590
|
-
const cbRef = (0,
|
|
5405
|
+
const cbRef = (0, import_react18.useRef)({
|
|
3591
5406
|
onChange,
|
|
3592
5407
|
onObjectChange,
|
|
3593
5408
|
onMouseDown,
|
|
@@ -3609,17 +5424,18 @@ function RotateRing({
|
|
|
3609
5424
|
enabled,
|
|
3610
5425
|
rotationSnap
|
|
3611
5426
|
};
|
|
3612
|
-
const polygons = (0,
|
|
5427
|
+
const polygons = (0, import_react18.useMemo)(() => {
|
|
3613
5428
|
const radiusWorld = radiusCss / SCENE_TILE_SIZE;
|
|
3614
|
-
|
|
5429
|
+
const outerWorld = radiusWorld * RING_QUAD_OUTER_RATIO;
|
|
5430
|
+
return (0, import_polycss_core9.ringQuadPolygons)({
|
|
3615
5431
|
axis: WORLD_AXIS_FOR_CSS[cssAxis],
|
|
3616
|
-
|
|
3617
|
-
halfThickness: radiusWorld * RING_HALF_THICKNESS_RATIO,
|
|
3618
|
-
segments: RING_SEGMENTS,
|
|
5432
|
+
outerRadius: outerWorld,
|
|
3619
5433
|
color
|
|
3620
5434
|
});
|
|
3621
5435
|
}, [cssAxis, color, radiusCss]);
|
|
3622
|
-
const
|
|
5436
|
+
const ringInnerRatio = (1 - RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
|
|
5437
|
+
const ringOuterRatio = (1 + RING_HALF_THICKNESS_RATIO) / RING_QUAD_OUTER_RATIO;
|
|
5438
|
+
const onPointerDown = (0, import_react18.useCallback)(
|
|
3623
5439
|
(e) => {
|
|
3624
5440
|
if (!cbRef.current.enabled) return;
|
|
3625
5441
|
e.stopPropagation();
|
|
@@ -3646,7 +5462,7 @@ function RotateRing({
|
|
|
3646
5462
|
},
|
|
3647
5463
|
[cssAxis, target]
|
|
3648
5464
|
);
|
|
3649
|
-
return /* @__PURE__ */ (0,
|
|
5465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3650
5466
|
PolyMesh,
|
|
3651
5467
|
{
|
|
3652
5468
|
polygons,
|
|
@@ -3654,15 +5470,19 @@ function RotateRing({
|
|
|
3654
5470
|
onPointerOver: () => onHoverChange?.(true),
|
|
3655
5471
|
onPointerOut: () => onHoverChange?.(false),
|
|
3656
5472
|
className: `polycss-transform-gizmo polycss-transform-ring polycss-transform-ring--${axisKey}`,
|
|
5473
|
+
style: {
|
|
5474
|
+
["--ring-inner-ratio"]: ringInnerRatio,
|
|
5475
|
+
["--ring-outer-ratio"]: ringOuterRatio
|
|
5476
|
+
},
|
|
3657
5477
|
textureLighting: "baked"
|
|
3658
5478
|
}
|
|
3659
5479
|
);
|
|
3660
5480
|
}
|
|
3661
5481
|
|
|
3662
5482
|
// src/select/Select.tsx
|
|
3663
|
-
var
|
|
3664
|
-
var
|
|
3665
|
-
var SelectContext = (0,
|
|
5483
|
+
var import_react19 = require("react");
|
|
5484
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
5485
|
+
var SelectContext = (0, import_react19.createContext)(null);
|
|
3666
5486
|
function PolySelect({
|
|
3667
5487
|
multiple = false,
|
|
3668
5488
|
filter,
|
|
@@ -3673,19 +5493,19 @@ function PolySelect({
|
|
|
3673
5493
|
className,
|
|
3674
5494
|
style
|
|
3675
5495
|
}) {
|
|
3676
|
-
const [selected, setSelectedState] = (0,
|
|
3677
|
-
const filterRef = (0,
|
|
5496
|
+
const [selected, setSelectedState] = (0, import_react19.useState)([]);
|
|
5497
|
+
const filterRef = (0, import_react19.useRef)(filter);
|
|
3678
5498
|
filterRef.current = filter;
|
|
3679
|
-
const onChangeRef = (0,
|
|
5499
|
+
const onChangeRef = (0, import_react19.useRef)(onChange);
|
|
3680
5500
|
onChangeRef.current = onChange;
|
|
3681
|
-
const multipleRef = (0,
|
|
5501
|
+
const multipleRef = (0, import_react19.useRef)(multiple);
|
|
3682
5502
|
multipleRef.current = multiple;
|
|
3683
|
-
const apply = (0,
|
|
5503
|
+
const apply = (0, import_react19.useCallback)((next) => {
|
|
3684
5504
|
const filtered = filterRef.current ? filterRef.current(next) : next;
|
|
3685
5505
|
setSelectedState(filtered);
|
|
3686
5506
|
if (onChangeRef.current) onChangeRef.current(filtered);
|
|
3687
5507
|
}, []);
|
|
3688
|
-
const api = (0,
|
|
5508
|
+
const api = (0, import_react19.useMemo)(() => {
|
|
3689
5509
|
return {
|
|
3690
5510
|
selected,
|
|
3691
5511
|
set: (next) => apply(next),
|
|
@@ -3718,14 +5538,14 @@ function PolySelect({
|
|
|
3718
5538
|
has: (h) => selected.includes(h)
|
|
3719
5539
|
};
|
|
3720
5540
|
}, [selected, apply]);
|
|
3721
|
-
const wrapperRef = (0,
|
|
3722
|
-
const cameraCtx = (0,
|
|
5541
|
+
const wrapperRef = (0, import_react19.useRef)(null);
|
|
5542
|
+
const cameraCtx = (0, import_react19.useContext)(PolyCameraContext);
|
|
3723
5543
|
const cameraElRef = cameraCtx?.cameraElRef;
|
|
3724
|
-
const clearOnMissRef = (0,
|
|
5544
|
+
const clearOnMissRef = (0, import_react19.useRef)(clearOnMiss);
|
|
3725
5545
|
clearOnMissRef.current = clearOnMiss;
|
|
3726
|
-
const onPointerMissedRef = (0,
|
|
5546
|
+
const onPointerMissedRef = (0, import_react19.useRef)(onPointerMissed);
|
|
3727
5547
|
onPointerMissedRef.current = onPointerMissed;
|
|
3728
|
-
const findMeshUnderPoint2 = (0,
|
|
5548
|
+
const findMeshUnderPoint2 = (0, import_react19.useCallback)(
|
|
3729
5549
|
(clientX, clientY) => findMeshUnderPoint(
|
|
3730
5550
|
clientX,
|
|
3731
5551
|
clientY,
|
|
@@ -3733,7 +5553,7 @@ function PolySelect({
|
|
|
3733
5553
|
),
|
|
3734
5554
|
[]
|
|
3735
5555
|
);
|
|
3736
|
-
(0,
|
|
5556
|
+
(0, import_react19.useEffect)(() => {
|
|
3737
5557
|
const cameraEl = cameraElRef?.current;
|
|
3738
5558
|
if (!cameraEl) return;
|
|
3739
5559
|
const onClick = (event) => {
|
|
@@ -3780,7 +5600,7 @@ function PolySelect({
|
|
|
3780
5600
|
}
|
|
3781
5601
|
};
|
|
3782
5602
|
const wrapperStyle = { display: "contents", ...style };
|
|
3783
|
-
return /* @__PURE__ */ (0,
|
|
5603
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(SelectContext.Provider, { value: api, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
3784
5604
|
"div",
|
|
3785
5605
|
{
|
|
3786
5606
|
ref: wrapperRef,
|
|
@@ -3793,10 +5613,10 @@ function PolySelect({
|
|
|
3793
5613
|
) });
|
|
3794
5614
|
}
|
|
3795
5615
|
function usePolySelect() {
|
|
3796
|
-
return (0,
|
|
5616
|
+
return (0, import_react19.useContext)(SelectContext)?.selected ?? [];
|
|
3797
5617
|
}
|
|
3798
5618
|
function usePolySelectionApi() {
|
|
3799
|
-
const ctx = (0,
|
|
5619
|
+
const ctx = (0, import_react19.useContext)(SelectContext);
|
|
3800
5620
|
if (!ctx) {
|
|
3801
5621
|
throw new Error("polycss: usePolySelectionApi must be used inside <PolySelect>.");
|
|
3802
5622
|
}
|
|
@@ -3804,9 +5624,9 @@ function usePolySelectionApi() {
|
|
|
3804
5624
|
}
|
|
3805
5625
|
|
|
3806
5626
|
// src/helpers/PolyAxesHelper.tsx
|
|
3807
|
-
var
|
|
3808
|
-
var
|
|
3809
|
-
var
|
|
5627
|
+
var import_react20 = require("react");
|
|
5628
|
+
var import_polycss_core10 = require("@layoutit/polycss-core");
|
|
5629
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
3810
5630
|
function PolyAxesHelper({
|
|
3811
5631
|
size,
|
|
3812
5632
|
thickness,
|
|
@@ -3815,17 +5635,17 @@ function PolyAxesHelper({
|
|
|
3815
5635
|
yColor,
|
|
3816
5636
|
zColor
|
|
3817
5637
|
}) {
|
|
3818
|
-
const polygons = (0,
|
|
3819
|
-
() => (0,
|
|
5638
|
+
const polygons = (0, import_react20.useMemo)(
|
|
5639
|
+
() => (0, import_polycss_core10.axesHelperPolygons)({ size, thickness, negative, xColor, yColor, zColor }),
|
|
3820
5640
|
[size, thickness, negative, xColor, yColor, zColor]
|
|
3821
5641
|
);
|
|
3822
|
-
return /* @__PURE__ */ (0,
|
|
5642
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PolyMesh, { polygons });
|
|
3823
5643
|
}
|
|
3824
5644
|
|
|
3825
5645
|
// src/helpers/PolyDirectionalLightHelper.tsx
|
|
3826
|
-
var
|
|
3827
|
-
var
|
|
3828
|
-
var
|
|
5646
|
+
var import_react21 = require("react");
|
|
5647
|
+
var import_polycss_core11 = require("@layoutit/polycss-core");
|
|
5648
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
3829
5649
|
var TILE = 50;
|
|
3830
5650
|
function PolyDirectionalLightHelper({
|
|
3831
5651
|
light,
|
|
@@ -3835,11 +5655,11 @@ function PolyDirectionalLightHelper({
|
|
|
3835
5655
|
color
|
|
3836
5656
|
}) {
|
|
3837
5657
|
const swatch = color ?? light.color ?? "#ffd54a";
|
|
3838
|
-
const polygons = (0,
|
|
3839
|
-
() => (0,
|
|
5658
|
+
const polygons = (0, import_react21.useMemo)(
|
|
5659
|
+
() => (0, import_polycss_core11.octahedronPolygons)({ center: [0, 0, 0], size, color: swatch }),
|
|
3840
5660
|
[size, swatch]
|
|
3841
5661
|
);
|
|
3842
|
-
const meshPosition = (0,
|
|
5662
|
+
const meshPosition = (0, import_react21.useMemo)(() => {
|
|
3843
5663
|
const [dx, dy, dz] = light.direction;
|
|
3844
5664
|
const len = Math.hypot(dx, dy, dz) || 1;
|
|
3845
5665
|
const tx = target?.[0] ?? 0;
|
|
@@ -3850,24 +5670,24 @@ function PolyDirectionalLightHelper({
|
|
|
3850
5670
|
const worldZ = tz + dz / len * distance;
|
|
3851
5671
|
return [worldY * TILE, worldX * TILE, worldZ * TILE];
|
|
3852
5672
|
}, [light.direction, target, distance]);
|
|
3853
|
-
return /* @__PURE__ */ (0,
|
|
5673
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PolyMesh, { polygons, position: meshPosition });
|
|
3854
5674
|
}
|
|
3855
5675
|
|
|
3856
5676
|
// src/styles/colorResolver.ts
|
|
3857
|
-
var
|
|
5677
|
+
var import_polycss_core12 = require("@layoutit/polycss-core");
|
|
3858
5678
|
|
|
3859
5679
|
// src/animation/usePolyAnimation.ts
|
|
3860
|
-
var
|
|
3861
|
-
var
|
|
5680
|
+
var import_react22 = require("react");
|
|
5681
|
+
var import_polycss_core13 = require("@layoutit/polycss-core");
|
|
3862
5682
|
function resolveRoot(rootArg) {
|
|
3863
5683
|
if (!rootArg) return null;
|
|
3864
5684
|
if ("current" in rootArg) return rootArg.current;
|
|
3865
5685
|
return rootArg;
|
|
3866
5686
|
}
|
|
3867
5687
|
function usePolyAnimation(clips, controller, root) {
|
|
3868
|
-
const internalRef = (0,
|
|
3869
|
-
const mixerRef = (0,
|
|
3870
|
-
(0,
|
|
5688
|
+
const internalRef = (0, import_react22.useRef)(null);
|
|
5689
|
+
const mixerRef = (0, import_react22.useRef)(null);
|
|
5690
|
+
(0, import_react22.useEffect)(() => {
|
|
3871
5691
|
if (!clips || clips.length === 0 || !controller) {
|
|
3872
5692
|
mixerRef.current = null;
|
|
3873
5693
|
return;
|
|
@@ -3877,14 +5697,14 @@ function usePolyAnimation(clips, controller, root) {
|
|
|
3877
5697
|
mixerRef.current = null;
|
|
3878
5698
|
return;
|
|
3879
5699
|
}
|
|
3880
|
-
mixerRef.current = (0,
|
|
5700
|
+
mixerRef.current = (0, import_polycss_core13.createPolyAnimationMixer)(resolvedRoot, controller);
|
|
3881
5701
|
return () => {
|
|
3882
5702
|
mixerRef.current?.stopAllAction();
|
|
3883
5703
|
mixerRef.current?.uncacheRoot();
|
|
3884
5704
|
mixerRef.current = null;
|
|
3885
5705
|
};
|
|
3886
5706
|
}, [clips, controller]);
|
|
3887
|
-
(0,
|
|
5707
|
+
(0, import_react22.useEffect)(() => {
|
|
3888
5708
|
if (!clips || clips.length === 0 || !controller) return;
|
|
3889
5709
|
let rafId;
|
|
3890
5710
|
let lastTime = null;
|
|
@@ -3907,7 +5727,7 @@ function usePolyAnimation(clips, controller, root) {
|
|
|
3907
5727
|
}, [clips, controller]);
|
|
3908
5728
|
const resolvedClips = clips ?? [];
|
|
3909
5729
|
const resolvedNames = resolvedClips.map((c) => c.name);
|
|
3910
|
-
const actions = (0,
|
|
5730
|
+
const actions = (0, import_react22.useMemo)(() => {
|
|
3911
5731
|
const target = {};
|
|
3912
5732
|
for (const clip of resolvedClips) {
|
|
3913
5733
|
Object.defineProperty(target, clip.name, {
|
|
@@ -3935,10 +5755,11 @@ function usePolyAnimation(clips, controller, root) {
|
|
|
3935
5755
|
}
|
|
3936
5756
|
|
|
3937
5757
|
// src/index.ts
|
|
3938
|
-
var
|
|
5758
|
+
var import_polycss_core14 = require("@layoutit/polycss-core");
|
|
3939
5759
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3940
5760
|
0 && (module.exports = {
|
|
3941
5761
|
BASE_TILE,
|
|
5762
|
+
CAMERA_BACKFACE_CULL_EPS,
|
|
3942
5763
|
DEFAULT_CAMERA_STATE,
|
|
3943
5764
|
DEFAULT_PROJECTION,
|
|
3944
5765
|
LoopOnce,
|
|
@@ -3949,6 +5770,8 @@ var import_polycss_core13 = require("@layoutit/polycss-core");
|
|
|
3949
5770
|
PolyCamera,
|
|
3950
5771
|
PolyCameraContext,
|
|
3951
5772
|
PolyDirectionalLightHelper,
|
|
5773
|
+
PolyFirstPersonControls,
|
|
5774
|
+
PolyGround,
|
|
3952
5775
|
PolyMapControls,
|
|
3953
5776
|
PolyMesh,
|
|
3954
5777
|
PolyOrbitControls,
|
|
@@ -3957,11 +5780,19 @@ var import_polycss_core13 = require("@layoutit/polycss-core");
|
|
|
3957
5780
|
PolyScene,
|
|
3958
5781
|
PolySelect,
|
|
3959
5782
|
PolyTransformControls,
|
|
5783
|
+
VOXEL_CAMERA_CULL_AXIS_EPS,
|
|
5784
|
+
VOXEL_CAMERA_CULL_NORMAL_LIMIT,
|
|
3960
5785
|
arrowPolygons,
|
|
3961
5786
|
axesHelperPolygons,
|
|
3962
5787
|
bakeSolidTextureSampledPolygons,
|
|
3963
5788
|
bakeSolidTextureSamples,
|
|
5789
|
+
boxPolygons,
|
|
3964
5790
|
buildSceneContext,
|
|
5791
|
+
cameraCullNormalGroups,
|
|
5792
|
+
cameraCullNormalGroupsFromPolygons,
|
|
5793
|
+
cameraCullNormalKey,
|
|
5794
|
+
cameraCullVisibleSignature,
|
|
5795
|
+
cameraFacingDepth,
|
|
3965
5796
|
clampChannel,
|
|
3966
5797
|
computeSceneBbox,
|
|
3967
5798
|
computeShapeLighting,
|
|
@@ -3975,11 +5806,15 @@ var import_polycss_core13 = require("@layoutit/polycss-core");
|
|
|
3975
5806
|
formatColor,
|
|
3976
5807
|
injectPolyBaseStyles,
|
|
3977
5808
|
inverseRotateVec3,
|
|
5809
|
+
isAxisAlignedSurfaceNormal,
|
|
5810
|
+
isVoxelCameraCullableNormalGroups,
|
|
3978
5811
|
loadMesh,
|
|
3979
5812
|
mergePolygons,
|
|
5813
|
+
normalFacesCamera,
|
|
3980
5814
|
normalizeInvertMultiplier,
|
|
3981
5815
|
normalizePolygons,
|
|
3982
5816
|
octahedronPolygons,
|
|
5817
|
+
optimizeMeshPolygons,
|
|
3983
5818
|
parseColor,
|
|
3984
5819
|
parseGltf,
|
|
3985
5820
|
parseHexColor,
|
|
@@ -3989,7 +5824,9 @@ var import_polycss_core13 = require("@layoutit/polycss-core");
|
|
|
3989
5824
|
parseRgbColor,
|
|
3990
5825
|
parseVox,
|
|
3991
5826
|
pointInMeshElement,
|
|
5827
|
+
polygonCssSurfaceNormal,
|
|
3992
5828
|
polygonFaces,
|
|
5829
|
+
polygonFacesCamera,
|
|
3993
5830
|
ringPolygons,
|
|
3994
5831
|
rotateVec3,
|
|
3995
5832
|
shadeColor,
|