@toolpath/viewer 1.3.0 → 1.5.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 +290 -55
- package/dist/assets/banana.glb +0 -0
- package/dist/{chunk-HWAEUJ4H.js → chunk-4TBLMR4Z.js} +744 -175
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +225 -22
- package/dist/index.js +755 -405
- package/dist/{normalize-DHOSNiuO.d.ts → normalize-CvnV9Ggh.d.ts} +55 -18
- package/dist/toolbar.css +108 -0
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ANGLE_ARC_FRACTION,
|
|
3
|
+
ANGLE_ARC_SEGMENTS,
|
|
2
4
|
AXES_PLANE_OFFSET,
|
|
3
5
|
AXES_PLANE_SCALE,
|
|
6
|
+
AXIS_COLORS,
|
|
4
7
|
CAD_CAMERA_UP,
|
|
5
8
|
CANDIDATE_WEIGHT,
|
|
6
9
|
CAP_HATCH,
|
|
7
10
|
CHAMFER,
|
|
8
11
|
CONE_AXIS,
|
|
12
|
+
CONTROL_SCHEME_OPTIONS,
|
|
9
13
|
CadCameraControls,
|
|
10
14
|
DEFAULT_FIT_MARGIN,
|
|
11
15
|
DEFAULT_SECTION_NORMAL,
|
|
12
16
|
DEFAULT_THEME,
|
|
17
|
+
DELTA_DASH_PIXELS,
|
|
13
18
|
DIRECTION_COLORS,
|
|
14
19
|
DISABLED_SECTION,
|
|
15
20
|
DOUBLE_TAP_MS,
|
|
@@ -24,6 +29,9 @@ import {
|
|
|
24
29
|
HIGHLIGHT_WEIGHT,
|
|
25
30
|
HOVER_WEIGHT,
|
|
26
31
|
MAX_FRAME_RATIO,
|
|
32
|
+
MEASURE_LINE_PIXELS,
|
|
33
|
+
MEASURE_MARKER_PIXELS,
|
|
34
|
+
MEASURE_RENDER_ORDER,
|
|
27
35
|
MIN_FRAME_RATIO,
|
|
28
36
|
NO_MODIFIERS,
|
|
29
37
|
ORBIT_TARGET_COLOR,
|
|
@@ -37,12 +45,17 @@ import {
|
|
|
37
45
|
OUTLINE_SCALE,
|
|
38
46
|
PERSPECTIVE_FOV,
|
|
39
47
|
PICKED_SURFACE_LABEL,
|
|
48
|
+
POINTS_PER_MEASUREMENT,
|
|
40
49
|
PREVIEW_SCALE,
|
|
41
50
|
PartMesh,
|
|
42
51
|
PartReportFormatError,
|
|
43
52
|
REGION_ATTRIBUTE,
|
|
53
|
+
SECTION_GIZMO_FRAME_SCALE,
|
|
44
54
|
SECTION_RENDER_ORDER,
|
|
45
55
|
SHAFT_RADIUS,
|
|
56
|
+
SNAP_MARKER_PIXELS,
|
|
57
|
+
SNAP_PIXELS,
|
|
58
|
+
STOCK_OBJECT,
|
|
46
59
|
SectionView,
|
|
47
60
|
TAP_SLOP,
|
|
48
61
|
UnsupportedKernelVersionError,
|
|
@@ -50,6 +63,9 @@ import {
|
|
|
50
63
|
VIEW_SIGNS,
|
|
51
64
|
Viewer,
|
|
52
65
|
adaptedUp,
|
|
66
|
+
angleArc,
|
|
67
|
+
angleArcRadius,
|
|
68
|
+
angleAt,
|
|
53
69
|
applyCapTheme,
|
|
54
70
|
applyHighlightLayers,
|
|
55
71
|
applyProjection,
|
|
@@ -76,19 +92,30 @@ import {
|
|
|
76
92
|
currentViewDirection,
|
|
77
93
|
cycleOwner,
|
|
78
94
|
defaultBounds,
|
|
95
|
+
deltaBetween,
|
|
96
|
+
deltaLegs,
|
|
79
97
|
directionColor,
|
|
80
98
|
directionIndexOf,
|
|
81
99
|
directionLabel,
|
|
100
|
+
distanceBetween,
|
|
82
101
|
dragPlane,
|
|
83
102
|
excludedFromFrame,
|
|
84
103
|
featureTypeRank,
|
|
85
104
|
fitDistance,
|
|
86
105
|
focusForPick,
|
|
106
|
+
formatDegrees,
|
|
107
|
+
formatMillimetres,
|
|
87
108
|
groupByDirection,
|
|
88
109
|
hitUnderRay,
|
|
89
110
|
labelGeometry,
|
|
90
111
|
labelTexture,
|
|
112
|
+
lockToAxis,
|
|
113
|
+
measurementFromPoints,
|
|
114
|
+
measurementLabel,
|
|
115
|
+
measurementLabelAnchor,
|
|
116
|
+
midpoint,
|
|
91
117
|
movedFar,
|
|
118
|
+
nextMeasurementId,
|
|
92
119
|
normalizePartReport,
|
|
93
120
|
orbitTargetOpacity,
|
|
94
121
|
orthographicHalfHeight,
|
|
@@ -103,6 +130,7 @@ import {
|
|
|
103
130
|
retargetPose,
|
|
104
131
|
sameDirection,
|
|
105
132
|
screenLength,
|
|
133
|
+
screenPoint,
|
|
106
134
|
sectionAnchor,
|
|
107
135
|
sectionBounds,
|
|
108
136
|
sectionConstant,
|
|
@@ -110,10 +138,12 @@ import {
|
|
|
110
138
|
sectionDepthConstant,
|
|
111
139
|
sectionDepthRange,
|
|
112
140
|
sectionFromPick,
|
|
141
|
+
sectionMeasurement,
|
|
113
142
|
sectionOffset,
|
|
114
143
|
sectionOptionsFromState,
|
|
115
144
|
sectionPlane,
|
|
116
145
|
smoothRegionNormals,
|
|
146
|
+
snapAt,
|
|
117
147
|
squaredUp,
|
|
118
148
|
startPosition,
|
|
119
149
|
surfaceUnderRay,
|
|
@@ -131,21 +161,505 @@ import {
|
|
|
131
161
|
viewUp,
|
|
132
162
|
viewVector,
|
|
133
163
|
visualSurfaces
|
|
134
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-4TBLMR4Z.js";
|
|
165
|
+
|
|
166
|
+
// src/hover-card.tsx
|
|
167
|
+
import { useEffect, useState } from "react";
|
|
168
|
+
import { jsx } from "react/jsx-runtime";
|
|
169
|
+
var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
|
|
170
|
+
const [pointer, setPointer] = useState(pick?.pointer);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
setPointer(pick?.pointer);
|
|
173
|
+
if (pick === null) return;
|
|
174
|
+
const follow = (event) => setPointer({ clientX: event.clientX, clientY: event.clientY });
|
|
175
|
+
window.addEventListener("pointermove", follow);
|
|
176
|
+
return () => window.removeEventListener("pointermove", follow);
|
|
177
|
+
}, [pick]);
|
|
178
|
+
if (pick === null || pointer === void 0) return null;
|
|
179
|
+
const style = {
|
|
180
|
+
position: "fixed",
|
|
181
|
+
left: pointer.clientX + offset,
|
|
182
|
+
top: pointer.clientY + offset,
|
|
183
|
+
pointerEvents: "none",
|
|
184
|
+
zIndex: 4
|
|
185
|
+
};
|
|
186
|
+
return /* @__PURE__ */ jsx(
|
|
187
|
+
"div",
|
|
188
|
+
{
|
|
189
|
+
className: ["viewer-hover-card", className2].filter(Boolean).join(" "),
|
|
190
|
+
"data-viewer-hover-card": "true",
|
|
191
|
+
role: "tooltip",
|
|
192
|
+
style,
|
|
193
|
+
children: children(pick)
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/viewer-toolbar.tsx
|
|
199
|
+
import { createContext, useContext } from "react";
|
|
200
|
+
|
|
201
|
+
// src/toolbar-icons.tsx
|
|
202
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
203
|
+
var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */ jsx2(
|
|
204
|
+
"svg",
|
|
205
|
+
{
|
|
206
|
+
className: "viewer-toolbar-icon",
|
|
207
|
+
"data-viewer-toolbar-icon": "true",
|
|
208
|
+
viewBox,
|
|
209
|
+
fill,
|
|
210
|
+
stroke: fill === "none" ? "currentColor" : "none",
|
|
211
|
+
strokeWidth: "1.5",
|
|
212
|
+
strokeLinecap: "round",
|
|
213
|
+
strokeLinejoin: "round",
|
|
214
|
+
"aria-hidden": "true",
|
|
215
|
+
children
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
var FitIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8 3H3v5m13-5h5v5M3 16v5h5m13-5v5h-5M8 8h8v8H8z" }) });
|
|
219
|
+
var ResetIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 10a9 9 0 1 1 2 8M3 4v6h6" }) });
|
|
220
|
+
var TopIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4-8 4zM4 7v10l8 4 8-4V7M12 11v10" }) });
|
|
221
|
+
var StockIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 6l9-4 9 4v12l-9 4-9-4zM3 6l9 4 9-4M12 10v12M7 8v8l5 2 5-2V8" }) });
|
|
222
|
+
var AxesIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M5 19V3m0 16h16M5 19l10-10M2 6l3-3 3 3m10 10 3 3-3 3M11 9h4v4" }) });
|
|
223
|
+
var GridIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 8l9-5 9 5-9 5zM3 8v8l9 5 9-5V8M12 13v8M7.5 5.5v8M16.5 5.5v8" }) });
|
|
224
|
+
var BananaIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { viewBox: "0 0 576 512", fill: "currentColor", children: /* @__PURE__ */ jsx2("path", { d: "M284.2 245.6c12.99 6.929 25.35 15.14 36.08 25.8L334.5 285.6l65.75-23.47c14.75-5.265 30.18-7.849 45.81-8.389c1.154-10.73 1.764-21.38 1.764-31.87c0-118.5-81.33-221.9-119.7-221.9c-21.01 0-40.91 17.04-40.91 39.25c0 16.18 16.74 41.9 16.74 103C303.1 170.1 300.6 203.1 284.2 245.6zM575.1 389.6c0-3.687-.8637-7.429-2.687-10.93l-15.12-29.11c-21.05-40.53-63.08-64.51-106.9-64.51c-13.43 0-27.02 2.252-40.22 6.969l-84.84 30.27l-28.59-28.41C274.4 270.9 243.7 258.1 212.8 258.1c-23.72 0-47.57 6.97-68.29 21.2L106.3 306.4c-6.732 4.631-10.35 12.07-10.35 19.63c0 14.93 12.7 23.87 24.04 23.87c4.695 0 9.443-1.376 13.61-4.26l38.13-26.23c12.43-8.525 26.71-12.69 40.91-12.69c10.64 0 21.24 2.339 30.97 6.934c-50.62 62.23-128.3 99.85-211.4 99.85C14.42 413.5 0 427.8 0 445.5v31.38c0 17.68 14.66 32.02 32.46 32.02l28.98.0009c14.15 0 34.69 1.098 59.07 1.098c93.51 0 243.4-16.15 304.8-172.4c9.021-3.22 17.95-4.712 26.53-4.712c27.65 0 51.7 15.49 63.7 38.55l15.12 29.11c3.484 6.723 11.74 12.98 21.41 12.98C564.1 413.6 575.1 403.8 575.1 389.6z" }) });
|
|
225
|
+
var DirectionsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
|
|
226
|
+
var HoverIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
|
|
227
|
+
var FocusIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
|
|
228
|
+
var WireframeIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
|
|
229
|
+
var SectionIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 6h9v12H3zM12 3v18M16 6h2m3 3v2m0 3v2m-5 3h-2" }) });
|
|
230
|
+
var MeasureIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M3 16L16 3l5 5L8 21zM8 11l3 3m1-7 3 3m-11 5 3 3" }) });
|
|
231
|
+
var ICONS = {
|
|
232
|
+
fit: FitIcon,
|
|
233
|
+
reset: ResetIcon,
|
|
234
|
+
top: TopIcon,
|
|
235
|
+
stock: StockIcon,
|
|
236
|
+
axes: AxesIcon,
|
|
237
|
+
grid: GridIcon,
|
|
238
|
+
banana: BananaIcon,
|
|
239
|
+
directions: DirectionsIcon,
|
|
240
|
+
hover: HoverIcon,
|
|
241
|
+
focus: FocusIcon,
|
|
242
|
+
wireframe: WireframeIcon,
|
|
243
|
+
section: SectionIcon,
|
|
244
|
+
measure: MeasureIcon
|
|
245
|
+
};
|
|
246
|
+
var ToolbarIcon = ({ name }) => {
|
|
247
|
+
const Icon = ICONS[name];
|
|
248
|
+
return /* @__PURE__ */ jsx2(Icon, {});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// src/viewer-toolbar.tsx
|
|
252
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
253
|
+
var ViewerToolbarContext = createContext(null);
|
|
254
|
+
var ViewerToolbarProvider = ({
|
|
255
|
+
controls,
|
|
256
|
+
children
|
|
257
|
+
}) => /* @__PURE__ */ jsx3(ViewerToolbarContext.Provider, { value: controls, children });
|
|
258
|
+
var useViewerToolbar = () => {
|
|
259
|
+
const controls = useContext(ViewerToolbarContext);
|
|
260
|
+
if (!controls) throw new Error("useViewerToolbar must be used inside <ViewerToolbarProvider>");
|
|
261
|
+
return controls;
|
|
262
|
+
};
|
|
263
|
+
var buttonName = (action) => `${action.slice(0, 1).toUpperCase()}${action.slice(1)}Button`;
|
|
264
|
+
var ToolbarButton = ({ action, icon, label, toggle = true }) => {
|
|
265
|
+
const control = useViewerToolbar()[action];
|
|
266
|
+
if (!control) {
|
|
267
|
+
throw new Error(`<ViewerToolbar.${buttonName(action)}> requires a '${action}' toolbar control`);
|
|
268
|
+
}
|
|
269
|
+
const pressed = control.pressed ?? false;
|
|
270
|
+
return /* @__PURE__ */ jsxs(
|
|
271
|
+
"button",
|
|
272
|
+
{
|
|
273
|
+
className: "viewer-toolbar-button",
|
|
274
|
+
"data-viewer-toolbar-action": action,
|
|
275
|
+
type: "button",
|
|
276
|
+
"aria-label": label(pressed),
|
|
277
|
+
"aria-pressed": toggle ? pressed : void 0,
|
|
278
|
+
onClick: control.onClick,
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ jsx3(ToolbarIcon, { name: icon }),
|
|
281
|
+
/* @__PURE__ */ jsx3("span", { className: "viewer-toolbar-tooltip", "data-viewer-toolbar-tooltip": "true", role: "tooltip", children: label(pressed) })
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
var StockButton = () => /* @__PURE__ */ jsx3(
|
|
287
|
+
ToolbarButton,
|
|
288
|
+
{
|
|
289
|
+
action: "stock",
|
|
290
|
+
icon: "stock",
|
|
291
|
+
label: (pressed) => pressed ? "Hide stock" : "Show stock"
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
var AxesButton = () => /* @__PURE__ */ jsx3(
|
|
295
|
+
ToolbarButton,
|
|
296
|
+
{
|
|
297
|
+
action: "axes",
|
|
298
|
+
icon: "axes",
|
|
299
|
+
label: (pressed) => pressed ? "Hide axis" : "Show axis"
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
var GridButton = () => /* @__PURE__ */ jsx3(
|
|
303
|
+
ToolbarButton,
|
|
304
|
+
{
|
|
305
|
+
action: "grid",
|
|
306
|
+
icon: "grid",
|
|
307
|
+
label: (pressed) => pressed ? "Hide grid" : "Show grid"
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
var BananaButton = () => /* @__PURE__ */ jsx3(
|
|
311
|
+
ToolbarButton,
|
|
312
|
+
{
|
|
313
|
+
action: "banana",
|
|
314
|
+
icon: "banana",
|
|
315
|
+
label: (pressed) => pressed ? "Banana for scale (on)" : "Banana for scale"
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
var DirectionsButton = () => /* @__PURE__ */ jsx3(
|
|
319
|
+
ToolbarButton,
|
|
320
|
+
{
|
|
321
|
+
action: "directions",
|
|
322
|
+
icon: "directions",
|
|
323
|
+
label: () => "Highlight faces by direction"
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
var HoverButton = () => /* @__PURE__ */ jsx3(
|
|
327
|
+
ToolbarButton,
|
|
328
|
+
{
|
|
329
|
+
action: "hover",
|
|
330
|
+
icon: "hover",
|
|
331
|
+
label: (pressed) => pressed ? "Disable feature hover" : "Enable feature hover"
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
var FocusButton = () => /* @__PURE__ */ jsx3(
|
|
335
|
+
ToolbarButton,
|
|
336
|
+
{
|
|
337
|
+
action: "focus",
|
|
338
|
+
icon: "focus",
|
|
339
|
+
label: (pressed) => pressed ? "Show full part" : "Focus selection"
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
var WireframeButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "wireframe", icon: "wireframe", label: () => "Wireframe" });
|
|
343
|
+
var SectionButton = () => /* @__PURE__ */ jsx3(
|
|
344
|
+
ToolbarButton,
|
|
345
|
+
{
|
|
346
|
+
action: "section",
|
|
347
|
+
icon: "section",
|
|
348
|
+
label: (pressed) => pressed ? "Exit section" : "Section"
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
var MeasureButton = () => /* @__PURE__ */ jsx3(
|
|
352
|
+
ToolbarButton,
|
|
353
|
+
{
|
|
354
|
+
action: "measure",
|
|
355
|
+
icon: "measure",
|
|
356
|
+
label: (pressed) => pressed ? "Exit measure" : "Measure"
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
var FitButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "fit", icon: "fit", label: () => "Fit", toggle: false });
|
|
360
|
+
var ResetButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "reset", icon: "reset", label: () => "Reset", toggle: false });
|
|
361
|
+
var TopButton = () => /* @__PURE__ */ jsx3(ToolbarButton, { action: "top", icon: "top", label: () => "Top view", toggle: false });
|
|
362
|
+
var Divider = () => /* @__PURE__ */ jsx3("span", { className: "viewer-toolbar-divider", "data-viewer-toolbar-divider": "true" });
|
|
363
|
+
var Controls = ({ children, className: className2 }) => /* @__PURE__ */ jsx3(
|
|
364
|
+
"div",
|
|
365
|
+
{
|
|
366
|
+
className: ["viewer-toolbar", className2].filter(Boolean).join(" "),
|
|
367
|
+
"data-viewer-toolbar-controls": "true",
|
|
368
|
+
role: "group",
|
|
369
|
+
"aria-label": "Viewer controls",
|
|
370
|
+
children
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
var DefaultControls = () => {
|
|
374
|
+
const controls = useViewerToolbar();
|
|
375
|
+
const display = [
|
|
376
|
+
controls.stock ? /* @__PURE__ */ jsx3(StockButton, {}, "stock") : null,
|
|
377
|
+
controls.axes ? /* @__PURE__ */ jsx3(AxesButton, {}, "axes") : null,
|
|
378
|
+
controls.grid ? /* @__PURE__ */ jsx3(GridButton, {}, "grid") : null,
|
|
379
|
+
controls.banana ? /* @__PURE__ */ jsx3(BananaButton, {}, "banana") : null
|
|
380
|
+
].filter(Boolean);
|
|
381
|
+
const analysis = [
|
|
382
|
+
controls.directions ? /* @__PURE__ */ jsx3(DirectionsButton, {}, "directions") : null,
|
|
383
|
+
controls.hover ? /* @__PURE__ */ jsx3(HoverButton, {}, "hover") : null,
|
|
384
|
+
controls.focus ? /* @__PURE__ */ jsx3(FocusButton, {}, "focus") : null,
|
|
385
|
+
controls.wireframe ? /* @__PURE__ */ jsx3(WireframeButton, {}, "wireframe") : null,
|
|
386
|
+
controls.section ? /* @__PURE__ */ jsx3(SectionButton, {}, "section") : null,
|
|
387
|
+
controls.measure ? /* @__PURE__ */ jsx3(MeasureButton, {}, "measure") : null
|
|
388
|
+
].filter(Boolean);
|
|
389
|
+
const camera = [
|
|
390
|
+
controls.fit ? /* @__PURE__ */ jsx3(FitButton, {}, "fit") : null,
|
|
391
|
+
controls.reset ? /* @__PURE__ */ jsx3(ResetButton, {}, "reset") : null,
|
|
392
|
+
controls.top ? /* @__PURE__ */ jsx3(TopButton, {}, "top") : null
|
|
393
|
+
].filter(Boolean);
|
|
394
|
+
return /* @__PURE__ */ jsxs(Controls, { children: [
|
|
395
|
+
display,
|
|
396
|
+
display.length && analysis.length ? /* @__PURE__ */ jsx3(Divider, {}) : null,
|
|
397
|
+
analysis,
|
|
398
|
+
(display.length || analysis.length) && camera.length ? /* @__PURE__ */ jsx3(Divider, {}) : null,
|
|
399
|
+
camera
|
|
400
|
+
] });
|
|
401
|
+
};
|
|
402
|
+
var ViewerToolbarRoot = ({ className: className2, children }) => {
|
|
403
|
+
useViewerToolbar();
|
|
404
|
+
return /* @__PURE__ */ jsx3(
|
|
405
|
+
"div",
|
|
406
|
+
{
|
|
407
|
+
className: ["viewer-toolbar-stack", className2].filter(Boolean).join(" "),
|
|
408
|
+
"data-viewer-toolbar": "true",
|
|
409
|
+
children: children ?? /* @__PURE__ */ jsx3(DefaultControls, {})
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
};
|
|
413
|
+
var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
|
|
414
|
+
Controls,
|
|
415
|
+
Divider,
|
|
416
|
+
StockButton,
|
|
417
|
+
AxesButton,
|
|
418
|
+
GridButton,
|
|
419
|
+
BananaButton,
|
|
420
|
+
DirectionsButton,
|
|
421
|
+
HoverButton,
|
|
422
|
+
FocusButton,
|
|
423
|
+
WireframeButton,
|
|
424
|
+
SectionButton,
|
|
425
|
+
MeasureButton,
|
|
426
|
+
FitButton,
|
|
427
|
+
ResetButton,
|
|
428
|
+
TopButton
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// src/stock.tsx
|
|
432
|
+
import { useThree } from "@react-three/fiber";
|
|
433
|
+
import { useEffect as useEffect2, useLayoutEffect, useMemo } from "react";
|
|
434
|
+
import { BoxGeometry, Vector3 as Vector32 } from "three";
|
|
435
|
+
|
|
436
|
+
// src/render/stock.ts
|
|
437
|
+
import {
|
|
438
|
+
Box3,
|
|
439
|
+
EdgesGeometry,
|
|
440
|
+
Group,
|
|
441
|
+
LineBasicMaterial,
|
|
442
|
+
LineSegments,
|
|
443
|
+
Mesh,
|
|
444
|
+
MeshLambertMaterial,
|
|
445
|
+
Vector3
|
|
446
|
+
} from "three";
|
|
447
|
+
function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
448
|
+
if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
|
|
449
|
+
throw new RangeError("Fixed stock dimensions must be finite and positive.");
|
|
450
|
+
}
|
|
451
|
+
if (!Number.isFinite(positionOffset)) {
|
|
452
|
+
throw new RangeError("Fixed stock position offset must be finite.");
|
|
453
|
+
}
|
|
454
|
+
if (![offset.x, offset.y, offset.z].every(Number.isFinite)) {
|
|
455
|
+
throw new RangeError("Fixed stock offset must be finite.");
|
|
456
|
+
}
|
|
457
|
+
const part = partBounds(geometry);
|
|
458
|
+
const center = part.getCenter(new Vector3());
|
|
459
|
+
const z = position === "offset_from_top" ? part.max.z + positionOffset - dimensions.z / 2 : position === "offset_from_bottom" ? part.min.z - positionOffset + dimensions.z / 2 : center.z;
|
|
460
|
+
const half = new Vector3(dimensions.x / 2, dimensions.y / 2, dimensions.z / 2);
|
|
461
|
+
return new Box3(
|
|
462
|
+
new Vector3(center.x, center.y, z).sub(half),
|
|
463
|
+
new Vector3(center.x, center.y, z).add(half)
|
|
464
|
+
).translate(new Vector3(offset.x, offset.y, offset.z));
|
|
465
|
+
}
|
|
466
|
+
function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
467
|
+
const padding = typeof allowance === "number" ? new Vector3(allowance, allowance, allowance) : "wall" in allowance ? new Vector3(allowance.wall, allowance.wall, allowance.floor) : new Vector3(allowance.x, allowance.y, allowance.z);
|
|
468
|
+
if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
|
|
469
|
+
throw new RangeError("Stock allowance must be finite and non-negative.");
|
|
470
|
+
}
|
|
471
|
+
if (![offset.x, offset.y, offset.z].every(Number.isFinite)) {
|
|
472
|
+
throw new RangeError("Stock offset must be finite.");
|
|
473
|
+
}
|
|
474
|
+
const box = partBounds(geometry);
|
|
475
|
+
box.expandByVector(padding).translate(new Vector3(offset.x, offset.y, offset.z));
|
|
476
|
+
const size = box.getSize(new Vector3());
|
|
477
|
+
if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
|
|
478
|
+
throw new RangeError("Stock dimensions must be finite and positive.");
|
|
479
|
+
}
|
|
480
|
+
return box;
|
|
481
|
+
}
|
|
482
|
+
function partBounds(geometry) {
|
|
483
|
+
const position = geometry.getAttribute("position");
|
|
484
|
+
if (!position || position.count === 0)
|
|
485
|
+
throw new RangeError("Stock needs non-empty part geometry.");
|
|
486
|
+
const box = new Box3();
|
|
487
|
+
const point = new Vector3();
|
|
488
|
+
for (let index = 0; index < position.count; index += 1) {
|
|
489
|
+
point.fromBufferAttribute(position, index);
|
|
490
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
491
|
+
throw new RangeError("Stock needs finite part coordinates.");
|
|
492
|
+
}
|
|
493
|
+
box.expandByPoint(point);
|
|
494
|
+
}
|
|
495
|
+
return box;
|
|
496
|
+
}
|
|
497
|
+
function createStock(geometry) {
|
|
498
|
+
const material = new MeshLambertMaterial({ transparent: true, depthWrite: false });
|
|
499
|
+
const edgeMaterial = new LineBasicMaterial({ transparent: true, depthWrite: false });
|
|
500
|
+
const mesh = new Mesh(geometry, material);
|
|
501
|
+
const edgeGeometry = new EdgesGeometry(geometry, 15);
|
|
502
|
+
const edges = new LineSegments(edgeGeometry, edgeMaterial);
|
|
503
|
+
const object = new Group();
|
|
504
|
+
object.userData[STOCK_OBJECT] = true;
|
|
505
|
+
mesh.renderOrder = 5;
|
|
506
|
+
edges.renderOrder = 6;
|
|
507
|
+
mesh.raycast = () => {
|
|
508
|
+
};
|
|
509
|
+
edges.raycast = () => {
|
|
510
|
+
};
|
|
511
|
+
object.add(mesh, edges);
|
|
512
|
+
return {
|
|
513
|
+
object,
|
|
514
|
+
material,
|
|
515
|
+
edgeMaterial,
|
|
516
|
+
edges,
|
|
517
|
+
dispose() {
|
|
518
|
+
material.dispose();
|
|
519
|
+
edgeMaterial.dispose();
|
|
520
|
+
edgeGeometry.dispose();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// src/stock.tsx
|
|
526
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
527
|
+
var Stock = ({
|
|
528
|
+
geometry,
|
|
529
|
+
color = 12176354,
|
|
530
|
+
opacity = 0.2,
|
|
531
|
+
edgeColor = 11058648,
|
|
532
|
+
edgeOpacity = 0.75,
|
|
533
|
+
showEdges = true
|
|
534
|
+
}) => {
|
|
535
|
+
const invalidate = useThree((state) => state.invalidate);
|
|
536
|
+
const stock = useMemo(() => createStock(geometry), [geometry]);
|
|
537
|
+
useEffect2(() => () => stock.dispose(), [stock]);
|
|
538
|
+
useLayoutEffect(() => {
|
|
539
|
+
stock.material.color.setHex(color);
|
|
540
|
+
stock.material.opacity = opacity;
|
|
541
|
+
stock.edgeMaterial.color.setHex(edgeColor);
|
|
542
|
+
stock.edgeMaterial.opacity = edgeOpacity;
|
|
543
|
+
stock.edges.visible = showEdges;
|
|
544
|
+
invalidate();
|
|
545
|
+
}, [color, edgeColor, edgeOpacity, invalidate, opacity, showEdges, stock]);
|
|
546
|
+
return /* @__PURE__ */ jsx4("primitive", { object: stock.object, dispose: null });
|
|
547
|
+
};
|
|
548
|
+
var BoxStock = ({
|
|
549
|
+
partGeometry,
|
|
550
|
+
dimensions,
|
|
551
|
+
position = "model_centered",
|
|
552
|
+
positionOffset = 0,
|
|
553
|
+
allowance = 0,
|
|
554
|
+
offset,
|
|
555
|
+
...props
|
|
556
|
+
}) => {
|
|
557
|
+
const ox = offset?.x ?? 0;
|
|
558
|
+
const oy = offset?.y ?? 0;
|
|
559
|
+
const oz = offset?.z ?? 0;
|
|
560
|
+
const geometry = useMemo(() => {
|
|
561
|
+
const box = dimensions ? fixedBoxStockBounds(partGeometry, dimensions, position, positionOffset, {
|
|
562
|
+
x: ox,
|
|
563
|
+
y: oy,
|
|
564
|
+
z: oz
|
|
565
|
+
}) : boxStockBounds(partGeometry, allowance, { x: ox, y: oy, z: oz });
|
|
566
|
+
const size = box.getSize(new Vector32());
|
|
567
|
+
const center = box.getCenter(new Vector32());
|
|
568
|
+
return new BoxGeometry(size.x, size.y, size.z).translate(center.x, center.y, center.z);
|
|
569
|
+
}, [allowance, dimensions, ox, oy, oz, partGeometry, position, positionOffset]);
|
|
570
|
+
useEffect2(() => () => geometry.dispose(), [geometry]);
|
|
571
|
+
return /* @__PURE__ */ jsx4(Stock, { geometry, ...props });
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// src/banana.tsx
|
|
575
|
+
import { useGLTF } from "@react-three/drei";
|
|
576
|
+
import { useThree as useThree2 } from "@react-three/fiber";
|
|
577
|
+
import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef } from "react";
|
|
578
|
+
|
|
579
|
+
// src/render/banana.ts
|
|
580
|
+
import { Vector3 as Vector33 } from "three";
|
|
581
|
+
var BANANA_GAP = 0.1;
|
|
582
|
+
function bananaPosition(part, banana) {
|
|
583
|
+
const size = part.getSize(new Vector33());
|
|
584
|
+
const reach = Math.max(size.x, size.y, size.z);
|
|
585
|
+
const centre = part.getCenter(new Vector33());
|
|
586
|
+
return new Vector33(
|
|
587
|
+
part.max.x + reach * BANANA_GAP - banana.min.x,
|
|
588
|
+
centre.y - (banana.min.y + banana.max.y) / 2,
|
|
589
|
+
part.min.z - banana.min.z
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
function bananaFrameBounds(part, banana, position) {
|
|
593
|
+
return part.clone().union(banana.clone().translate(position));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/banana.tsx
|
|
597
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
598
|
+
var BANANA_MODEL_URL = new URL("./assets/banana.glb", import.meta.url).href;
|
|
599
|
+
var SKIN = "#e8b530";
|
|
600
|
+
var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
|
|
601
|
+
var Banana = ({ onPlaced }) => {
|
|
602
|
+
const part = useContentBox();
|
|
603
|
+
const invalidate = useThree2((state) => state.invalidate);
|
|
604
|
+
const gltf = useGLTF(BANANA_MODEL_URL);
|
|
605
|
+
const group = useRef(null);
|
|
606
|
+
const geometry = useMemo2(() => {
|
|
607
|
+
let found = null;
|
|
608
|
+
gltf.scene.traverse((object) => {
|
|
609
|
+
const mesh = object;
|
|
610
|
+
if (found === null && mesh.isMesh) found = mesh.geometry;
|
|
611
|
+
});
|
|
612
|
+
const source = found;
|
|
613
|
+
if (!source) return null;
|
|
614
|
+
const own = source.clone();
|
|
615
|
+
own.computeVertexNormals();
|
|
616
|
+
own.rotateY(Math.PI / 2);
|
|
617
|
+
own.rotateX(Math.PI / 2);
|
|
618
|
+
own.computeBoundingBox();
|
|
619
|
+
return own;
|
|
620
|
+
}, [gltf]);
|
|
621
|
+
useLayoutEffect2(() => {
|
|
622
|
+
const node = group.current;
|
|
623
|
+
const own = geometry?.boundingBox;
|
|
624
|
+
if (!node || !own || part.isEmpty()) return;
|
|
625
|
+
const position = bananaPosition(part, own);
|
|
626
|
+
node.position.copy(position);
|
|
627
|
+
node.visible = true;
|
|
628
|
+
node.updateWorldMatrix(true, true);
|
|
629
|
+
onPlaced?.(bananaFrameBounds(part, own, position));
|
|
630
|
+
invalidate();
|
|
631
|
+
}, [geometry, invalidate, onPlaced, part]);
|
|
632
|
+
if (!geometry) return null;
|
|
633
|
+
return /* @__PURE__ */ jsx5("group", { ref: group, userData: FURNITURE, visible: false, children: /* @__PURE__ */ jsx5("mesh", { geometry, children: /* @__PURE__ */ jsx5("meshStandardMaterial", { color: SKIN, roughness: 0.85, metalness: 0 }) }) });
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/render/direction-highlights.ts
|
|
637
|
+
function directionHighlights(model, activeDirection = null) {
|
|
638
|
+
const features = model.features.map((feature) => ({ feature, index: directionIndexOf(model, feature.machiningDirection) })).filter(({ index }) => index >= 0 && (activeDirection === null || index === activeDirection)).sort(
|
|
639
|
+
(a, b) => featureTypeRank(a.feature.featureType) - featureTypeRank(b.feature.featureType) || a.index - b.index || (a.feature.tag < b.feature.tag ? -1 : a.feature.tag > b.feature.tag ? 1 : 0)
|
|
640
|
+
);
|
|
641
|
+
const colors = /* @__PURE__ */ new Map();
|
|
642
|
+
for (const { feature, index } of features) {
|
|
643
|
+
for (const region of model.regionIndex.regionsForFeature(feature.tag)) {
|
|
644
|
+
if (!colors.has(region)) colors.set(region, { region, color: directionColor(index) });
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return [...colors.values()].sort((a, b) => a.region - b.region);
|
|
648
|
+
}
|
|
135
649
|
|
|
136
650
|
// src/primitives.tsx
|
|
137
651
|
import { GizmoHelper } from "@react-three/drei";
|
|
138
|
-
import { useThree } from "@react-three/fiber";
|
|
139
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
652
|
+
import { useThree as useThree3 } from "@react-three/fiber";
|
|
653
|
+
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState2 } from "react";
|
|
140
654
|
|
|
141
655
|
// src/render/grid.ts
|
|
142
|
-
import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
|
|
656
|
+
import { BufferGeometry, Float32BufferAttribute, Vector3 as Vector34 } from "three";
|
|
143
657
|
var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
|
|
144
658
|
var TARGET_CELLS = 10;
|
|
145
659
|
var OVERHANG = 1.6;
|
|
146
660
|
function gridSpec(box) {
|
|
147
|
-
const size = box.getSize(new
|
|
148
|
-
const center = box.getCenter(new
|
|
661
|
+
const size = box.getSize(new Vector34());
|
|
662
|
+
const center = box.getCenter(new Vector34());
|
|
149
663
|
const largest = Math.max(size.x, size.y, 1e-6);
|
|
150
664
|
const ideal = largest / TARGET_CELLS;
|
|
151
665
|
const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
|
|
@@ -189,26 +703,26 @@ function gridFor(box, options = {}) {
|
|
|
189
703
|
}
|
|
190
704
|
|
|
191
705
|
// src/primitives.tsx
|
|
192
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
193
|
-
var
|
|
706
|
+
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
707
|
+
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
194
708
|
var Grid = ({ step, extent, color, opacity = 0.35 }) => {
|
|
195
709
|
const theme = resolveTheme();
|
|
196
710
|
const box = useContentBox();
|
|
197
|
-
const geometry =
|
|
198
|
-
|
|
711
|
+
const geometry = useMemo3(() => gridFor(box, { step, extent }), [box, extent, step]);
|
|
712
|
+
useEffect3(() => () => geometry?.dispose(), [geometry]);
|
|
199
713
|
if (!geometry) return null;
|
|
200
|
-
return /* @__PURE__ */
|
|
714
|
+
return /* @__PURE__ */ jsx6("lineSegments", { geometry, renderOrder: -1, raycast: () => null, userData: FURNITURE2, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: color ?? theme.cubeEdge, transparent: true, opacity }) });
|
|
201
715
|
};
|
|
202
|
-
var Axes = ({ size = 25 }) => /* @__PURE__ */
|
|
716
|
+
var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx6("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
|
|
203
717
|
var ViewCube = ({
|
|
204
718
|
alignment = "top-right",
|
|
205
719
|
margin = [80, 80],
|
|
206
720
|
theme,
|
|
207
721
|
onViewChange
|
|
208
722
|
}) => {
|
|
209
|
-
const resolved =
|
|
210
|
-
const zones =
|
|
211
|
-
return /* @__PURE__ */
|
|
723
|
+
const resolved = useMemo3(() => resolveTheme(theme), [theme]);
|
|
724
|
+
const zones = useMemo3(() => cubeZones(), []);
|
|
725
|
+
return /* @__PURE__ */ jsx6(GizmoHelper, { alignment, margin, children: /* @__PURE__ */ jsx6(CubePanels, { zones, theme: resolved, onViewChange }) });
|
|
212
726
|
};
|
|
213
727
|
var CubePanels = ({
|
|
214
728
|
zones,
|
|
@@ -216,15 +730,15 @@ var CubePanels = ({
|
|
|
216
730
|
onViewChange
|
|
217
731
|
}) => {
|
|
218
732
|
const controls = useViewerControls();
|
|
219
|
-
const invalidate =
|
|
220
|
-
const groupRef =
|
|
221
|
-
const [hovered, setHovered] =
|
|
222
|
-
const panels =
|
|
733
|
+
const invalidate = useThree3((state) => state.invalidate);
|
|
734
|
+
const groupRef = useRef2(null);
|
|
735
|
+
const [hovered, setHovered] = useState2(null);
|
|
736
|
+
const panels = useMemo3(
|
|
223
737
|
() => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
|
|
224
738
|
[zones]
|
|
225
739
|
);
|
|
226
|
-
const outline =
|
|
227
|
-
const labels =
|
|
740
|
+
const outline = useMemo3(() => cubeOutlineGeometry(zones), [zones]);
|
|
741
|
+
const labels = useMemo3(
|
|
228
742
|
() => zones.filter((zone) => zone.kind === "face").map((zone) => ({
|
|
229
743
|
zone,
|
|
230
744
|
geometry: labelGeometry(zone),
|
|
@@ -232,7 +746,7 @@ var CubePanels = ({
|
|
|
232
746
|
})),
|
|
233
747
|
[theme.cubeLabel, zones]
|
|
234
748
|
);
|
|
235
|
-
|
|
749
|
+
useEffect3(
|
|
236
750
|
() => () => {
|
|
237
751
|
for (const panel of panels) panel.geometry.dispose();
|
|
238
752
|
for (const label of labels) {
|
|
@@ -251,8 +765,8 @@ var CubePanels = ({
|
|
|
251
765
|
setHovered(name);
|
|
252
766
|
invalidate();
|
|
253
767
|
};
|
|
254
|
-
return /* @__PURE__ */
|
|
255
|
-
panels.map(({ zone, geometry }) => /* @__PURE__ */
|
|
768
|
+
return /* @__PURE__ */ jsxs2("group", { ref: groupRef, scale: 38, children: [
|
|
769
|
+
panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx6(
|
|
256
770
|
"mesh",
|
|
257
771
|
{
|
|
258
772
|
geometry,
|
|
@@ -265,7 +779,7 @@ var CubePanels = ({
|
|
|
265
779
|
event.stopPropagation();
|
|
266
780
|
choose(zone);
|
|
267
781
|
},
|
|
268
|
-
children: /* @__PURE__ */
|
|
782
|
+
children: /* @__PURE__ */ jsx6(
|
|
269
783
|
"meshLambertMaterial",
|
|
270
784
|
{
|
|
271
785
|
color: hovered === zone.name ? theme.hover : theme.cube,
|
|
@@ -277,19 +791,19 @@ var CubePanels = ({
|
|
|
277
791
|
},
|
|
278
792
|
zone.name
|
|
279
793
|
)),
|
|
280
|
-
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */
|
|
281
|
-
/* @__PURE__ */
|
|
282
|
-
/* @__PURE__ */
|
|
283
|
-
/* @__PURE__ */
|
|
794
|
+
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */ jsx6("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsx6("meshBasicMaterial", { map: texture, transparent: true }) }, `${zone.name}-label`)),
|
|
795
|
+
/* @__PURE__ */ jsx6("lineSegments", { geometry: outline, raycast: () => null, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: theme.cubeEdge }) }),
|
|
796
|
+
/* @__PURE__ */ jsx6("ambientLight", { intensity: 1.4 }),
|
|
797
|
+
/* @__PURE__ */ jsx6("directionalLight", { position: [viewVector("top-front-right").x, 1, 2], intensity: 1.1 })
|
|
284
798
|
] });
|
|
285
799
|
};
|
|
286
800
|
|
|
287
801
|
// src/direction-arrows.tsx
|
|
288
|
-
import { useThree as
|
|
289
|
-
import { useMemo as
|
|
290
|
-
import { Quaternion, Vector3 as
|
|
291
|
-
import { jsx as
|
|
292
|
-
var
|
|
802
|
+
import { useThree as useThree4 } from "@react-three/fiber";
|
|
803
|
+
import { useMemo as useMemo4 } from "react";
|
|
804
|
+
import { Quaternion, Vector3 as Vector35 } from "three";
|
|
805
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
806
|
+
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
293
807
|
var DirectionArrows = ({
|
|
294
808
|
directions,
|
|
295
809
|
activeDirection = null,
|
|
@@ -301,7 +815,7 @@ var DirectionArrows = ({
|
|
|
301
815
|
visible = true
|
|
302
816
|
}) => {
|
|
303
817
|
const box = useContentBox();
|
|
304
|
-
const resolved =
|
|
818
|
+
const resolved = useMemo4(() => resolveTheme(theme), [theme]);
|
|
305
819
|
const shown = shownDirection === void 0 ? activeDirection : shownDirection;
|
|
306
820
|
const drawn = (index) => {
|
|
307
821
|
if (shown === null) return true;
|
|
@@ -313,10 +827,10 @@ var DirectionArrows = ({
|
|
|
313
827
|
// Excluded from framing: the arrows are deliberately placed outside the
|
|
314
828
|
// part, so a Fit that measured them would frame the arrows and leave the
|
|
315
829
|
// part small in the middle of them.
|
|
316
|
-
/* @__PURE__ */
|
|
830
|
+
/* @__PURE__ */ jsxs3("group", { userData: FURNITURE3, children: [
|
|
317
831
|
directions.map((direction, index) => {
|
|
318
832
|
if (!drawn(index)) return null;
|
|
319
|
-
return /* @__PURE__ */
|
|
833
|
+
return /* @__PURE__ */ jsx7(
|
|
320
834
|
Arrow,
|
|
321
835
|
{
|
|
322
836
|
direction,
|
|
@@ -328,7 +842,7 @@ var DirectionArrows = ({
|
|
|
328
842
|
`candidate-${index}`
|
|
329
843
|
);
|
|
330
844
|
}),
|
|
331
|
-
namedDirections.map((named) => /* @__PURE__ */
|
|
845
|
+
namedDirections.map((named) => /* @__PURE__ */ jsx7(
|
|
332
846
|
Arrow,
|
|
333
847
|
{
|
|
334
848
|
direction: named.direction,
|
|
@@ -339,17 +853,17 @@ var DirectionArrows = ({
|
|
|
339
853
|
},
|
|
340
854
|
`named-${named.index}`
|
|
341
855
|
)),
|
|
342
|
-
previewDirection ? /* @__PURE__ */
|
|
856
|
+
previewDirection ? /* @__PURE__ */ jsx7(Arrow, { direction: previewDirection, box, color: resolved.hover, opacity: 0.95, onTop: true }) : null
|
|
343
857
|
] })
|
|
344
858
|
);
|
|
345
859
|
};
|
|
346
860
|
var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
347
|
-
const invalidate =
|
|
348
|
-
const { tip, length, quaternion } =
|
|
861
|
+
const invalidate = useThree4((state) => state.invalidate);
|
|
862
|
+
const { tip, length, quaternion } = useMemo4(() => {
|
|
349
863
|
const placement = arrowPlacement(direction, box);
|
|
350
864
|
const aim = new Quaternion().setFromUnitVectors(
|
|
351
865
|
CONE_AXIS,
|
|
352
|
-
new
|
|
866
|
+
new Vector35(direction.x, direction.y, direction.z).normalize()
|
|
353
867
|
);
|
|
354
868
|
return { tip: placement.tip, length: placement.length, quaternion: aim };
|
|
355
869
|
}, [box, direction]);
|
|
@@ -362,234 +876,32 @@ var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
|
362
876
|
invalidate();
|
|
363
877
|
}
|
|
364
878
|
} : {};
|
|
365
|
-
return /* @__PURE__ */
|
|
366
|
-
/* @__PURE__ */
|
|
367
|
-
/* @__PURE__ */
|
|
368
|
-
/* @__PURE__ */
|
|
879
|
+
return /* @__PURE__ */ jsxs3("group", { position: tip, quaternion, renderOrder: onTop ? 10 : 3, children: [
|
|
880
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, head * 0.5, 0], rotation: [Math.PI, 0, 0], ...press, children: [
|
|
881
|
+
/* @__PURE__ */ jsx7("coneGeometry", { args: [length * HEAD_RADIUS, head, 20] }),
|
|
882
|
+
/* @__PURE__ */ jsx7("meshBasicMaterial", { color, transparent: true, opacity, depthTest: !onTop })
|
|
369
883
|
] }),
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
/* @__PURE__ */
|
|
372
|
-
/* @__PURE__ */
|
|
884
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, head + shaft * 0.5, 0], ...press, children: [
|
|
885
|
+
/* @__PURE__ */ jsx7("cylinderGeometry", { args: [length * SHAFT_RADIUS, length * SHAFT_RADIUS, shaft, 12] }),
|
|
886
|
+
/* @__PURE__ */ jsx7("meshBasicMaterial", { color, transparent: true, opacity, depthTest: !onTop })
|
|
373
887
|
] })
|
|
374
888
|
] });
|
|
375
889
|
};
|
|
376
890
|
|
|
377
891
|
// src/section-tool.tsx
|
|
378
|
-
import { useFrame, useThree as
|
|
379
|
-
import { useEffect as
|
|
892
|
+
import { useFrame, useThree as useThree5 } from "@react-three/fiber";
|
|
893
|
+
import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as useState3, useSyncExternalStore } from "react";
|
|
380
894
|
import {
|
|
381
895
|
BufferGeometry as BufferGeometry2,
|
|
382
896
|
DoubleSide,
|
|
383
897
|
Quaternion as Quaternion2,
|
|
384
898
|
Raycaster,
|
|
385
|
-
Vector2
|
|
386
|
-
Vector3 as
|
|
899
|
+
Vector2,
|
|
900
|
+
Vector3 as Vector36
|
|
387
901
|
} from "three";
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
var MEASURE_RENDER_ORDER = 7;
|
|
392
|
-
var AXIS_COLORS = { x: 16739179, y: 7331978, z: 7314431 };
|
|
393
|
-
var SNAP_PIXELS = {
|
|
394
|
-
vertex: 14,
|
|
395
|
-
midpoint: 12,
|
|
396
|
-
edge: 9
|
|
397
|
-
};
|
|
398
|
-
var SNAP_MARKER_PIXELS = {
|
|
399
|
-
vertex: 6,
|
|
400
|
-
midpoint: 5,
|
|
401
|
-
edge: 4,
|
|
402
|
-
face: 3.5
|
|
403
|
-
};
|
|
404
|
-
var MEASURE_MARKER_PIXELS = 3.5;
|
|
405
|
-
var MEASURE_LINE_PIXELS = {
|
|
406
|
-
line: 2,
|
|
407
|
-
leg: 1.5,
|
|
408
|
-
edge: 3
|
|
409
|
-
};
|
|
410
|
-
var DELTA_DASH_PIXELS = 6;
|
|
411
|
-
var ANGLE_ARC_FRACTION = 0.3;
|
|
412
|
-
var ANGLE_ARC_SEGMENTS = 24;
|
|
413
|
-
function snapAt(hit, edges, camera, viewport) {
|
|
414
|
-
const face = {
|
|
415
|
-
kind: "face",
|
|
416
|
-
point: hit.point.clone(),
|
|
417
|
-
normal: hit.normal.clone(),
|
|
418
|
-
edge: null
|
|
419
|
-
};
|
|
420
|
-
if (!edges) return face;
|
|
421
|
-
const positions = edges.positions;
|
|
422
|
-
const segments = Math.floor(positions.length / 6);
|
|
423
|
-
if (segments === 0) return face;
|
|
424
|
-
const inverse = edges.matrixWorld.clone().invert();
|
|
425
|
-
const p = hit.point.clone().applyMatrix4(inverse);
|
|
426
|
-
const n = hit.normal.clone().transformDirection(inverse);
|
|
427
|
-
const reach = screenLength(camera, hit.point, viewport, SNAP_PIXELS.vertex) * 1.5;
|
|
428
|
-
const reachSq = reach * reach;
|
|
429
|
-
const pointer = screenPoint(camera, hit.point, viewport);
|
|
430
|
-
const best = {
|
|
431
|
-
vertex: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 },
|
|
432
|
-
midpoint: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 },
|
|
433
|
-
edge: { distance: Number.POSITIVE_INFINITY, index: -1, t: 0 }
|
|
434
|
-
};
|
|
435
|
-
const a = new Vector33();
|
|
436
|
-
const b = new Vector33();
|
|
437
|
-
const ab = new Vector33();
|
|
438
|
-
const ap = new Vector33();
|
|
439
|
-
const q = new Vector33();
|
|
440
|
-
const pixel = new Vector2();
|
|
441
|
-
const consider = (kind, point, index, t) => {
|
|
442
|
-
const distance = screenPoint(
|
|
443
|
-
camera,
|
|
444
|
-
q.copy(point).applyMatrix4(edges.matrixWorld),
|
|
445
|
-
viewport,
|
|
446
|
-
pixel
|
|
447
|
-
).distanceTo(pointer);
|
|
448
|
-
if (distance <= SNAP_PIXELS[kind] && distance < best[kind].distance) {
|
|
449
|
-
best[kind] = { distance, index, t };
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
|
-
for (let index = 0; index < segments; index += 1) {
|
|
453
|
-
const offset = index * 6;
|
|
454
|
-
a.set(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
455
|
-
b.set(positions[offset + 3], positions[offset + 4], positions[offset + 5]);
|
|
456
|
-
if (Math.abs(ap.subVectors(a, p).dot(n)) > reach) continue;
|
|
457
|
-
if (Math.abs(ap.subVectors(b, p).dot(n)) > reach) continue;
|
|
458
|
-
ab.subVectors(b, a);
|
|
459
|
-
ap.subVectors(p, a);
|
|
460
|
-
const lengthSq = ab.lengthSq();
|
|
461
|
-
const t = lengthSq === 0 ? 0 : Math.min(1, Math.max(0, ap.dot(ab) / lengthSq));
|
|
462
|
-
q.copy(a).addScaledVector(ab, t);
|
|
463
|
-
if (q.distanceToSquared(p) > reachSq) continue;
|
|
464
|
-
consider("vertex", a, index, 0);
|
|
465
|
-
consider("vertex", b, index, 1);
|
|
466
|
-
consider("midpoint", q.copy(a).addScaledVector(ab, 0.5), index, 0.5);
|
|
467
|
-
consider("edge", q.copy(a).addScaledVector(ab, t), index, t);
|
|
468
|
-
}
|
|
469
|
-
for (const kind of ["vertex", "midpoint", "edge"]) {
|
|
470
|
-
const found = best[kind];
|
|
471
|
-
if (found.index < 0) continue;
|
|
472
|
-
const offset = found.index * 6;
|
|
473
|
-
const start = new Vector33(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
474
|
-
const end = new Vector33(positions[offset + 3], positions[offset + 4], positions[offset + 5]);
|
|
475
|
-
const point = start.clone().lerp(end, found.t);
|
|
476
|
-
start.applyMatrix4(edges.matrixWorld);
|
|
477
|
-
end.applyMatrix4(edges.matrixWorld);
|
|
478
|
-
point.applyMatrix4(edges.matrixWorld);
|
|
479
|
-
return {
|
|
480
|
-
kind,
|
|
481
|
-
point,
|
|
482
|
-
normal: hit.normal.clone(),
|
|
483
|
-
edge: kind === "vertex" ? null : [start, end]
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
return face;
|
|
487
|
-
}
|
|
488
|
-
function screenPoint(camera, point, viewport, into = new Vector2()) {
|
|
489
|
-
const ndc = point.clone().project(camera);
|
|
490
|
-
return into.set((ndc.x + 1) / 2 * viewport.width, (1 - ndc.y) / 2 * viewport.height);
|
|
491
|
-
}
|
|
492
|
-
var POINTS_PER_MEASUREMENT = {
|
|
493
|
-
distance: 2,
|
|
494
|
-
angle: 3
|
|
495
|
-
};
|
|
496
|
-
function measurementFromPoints(kind, points, id) {
|
|
497
|
-
if (points.length < POINTS_PER_MEASUREMENT[kind]) return null;
|
|
498
|
-
const [a, b, c] = points;
|
|
499
|
-
if (kind === "distance") return { id, kind, points: [copy(a), copy(b)] };
|
|
500
|
-
return { id, kind, points: [copy(a), copy(b), copy(c)] };
|
|
501
|
-
}
|
|
502
|
-
function nextMeasurementId(held) {
|
|
503
|
-
return held.reduce((top, entry) => Math.max(top, entry.id), 0) + 1;
|
|
504
|
-
}
|
|
505
|
-
function distanceBetween(a, b) {
|
|
506
|
-
return Math.hypot(b.x - a.x, b.y - a.y, b.z - a.z);
|
|
507
|
-
}
|
|
508
|
-
function deltaBetween(a, b) {
|
|
509
|
-
return { x: b.x - a.x, y: b.y - a.y, z: b.z - a.z };
|
|
510
|
-
}
|
|
511
|
-
function midpoint(a, b) {
|
|
512
|
-
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2, z: (a.z + b.z) / 2 };
|
|
513
|
-
}
|
|
514
|
-
function angleAt(vertex, a, b) {
|
|
515
|
-
const u = new Vector33(a.x - vertex.x, a.y - vertex.y, a.z - vertex.z);
|
|
516
|
-
const w = new Vector33(b.x - vertex.x, b.y - vertex.y, b.z - vertex.z);
|
|
517
|
-
if (u.lengthSq() === 0 || w.lengthSq() === 0) return 0;
|
|
518
|
-
return u.angleTo(w) * 180 / Math.PI;
|
|
519
|
-
}
|
|
520
|
-
function lockToAxis(from, to) {
|
|
521
|
-
const delta = deltaBetween(from, to);
|
|
522
|
-
let axis = "x";
|
|
523
|
-
for (const candidate of ["y", "z"]) {
|
|
524
|
-
if (Math.abs(delta[candidate]) > Math.abs(delta[axis])) axis = candidate;
|
|
525
|
-
}
|
|
526
|
-
return { axis, point: { ...from, [axis]: to[axis] } };
|
|
527
|
-
}
|
|
528
|
-
function deltaLegs(a, b) {
|
|
529
|
-
const delta = deltaBetween(a, b);
|
|
530
|
-
const threshold = Math.max(1e-9, distanceBetween(a, b) * 1e-6);
|
|
531
|
-
const legs = [];
|
|
532
|
-
let cursor = copy(a);
|
|
533
|
-
for (const axis of ["x", "y", "z"]) {
|
|
534
|
-
if (Math.abs(delta[axis]) <= threshold) continue;
|
|
535
|
-
const to = { ...cursor, [axis]: b[axis] };
|
|
536
|
-
legs.push({ axis, from: cursor, to, length: Math.abs(delta[axis]) });
|
|
537
|
-
cursor = to;
|
|
538
|
-
}
|
|
539
|
-
return legs.length > 1 ? legs : [];
|
|
540
|
-
}
|
|
541
|
-
function angleArc(vertex, a, b, radius, segments = ANGLE_ARC_SEGMENTS) {
|
|
542
|
-
const v = new Vector33(vertex.x, vertex.y, vertex.z);
|
|
543
|
-
const u = new Vector33(a.x, a.y, a.z).sub(v);
|
|
544
|
-
const w = new Vector33(b.x, b.y, b.z).sub(v);
|
|
545
|
-
if (u.lengthSq() === 0 || w.lengthSq() === 0) return [];
|
|
546
|
-
u.normalize();
|
|
547
|
-
w.normalize();
|
|
548
|
-
const theta = u.angleTo(w);
|
|
549
|
-
const sine = Math.sin(theta);
|
|
550
|
-
if (sine < 1e-6) {
|
|
551
|
-
return [v.clone().addScaledVector(u, radius), v.clone().addScaledVector(w, radius)];
|
|
552
|
-
}
|
|
553
|
-
const points = [];
|
|
554
|
-
for (let i = 0; i <= segments; i += 1) {
|
|
555
|
-
const t = i / segments;
|
|
556
|
-
const direction = u.clone().multiplyScalar(Math.sin((1 - t) * theta) / sine).addScaledVector(w, Math.sin(t * theta) / sine);
|
|
557
|
-
points.push(v.clone().addScaledVector(direction, radius));
|
|
558
|
-
}
|
|
559
|
-
return points;
|
|
560
|
-
}
|
|
561
|
-
function angleArcRadius(vertex, a, b) {
|
|
562
|
-
return Math.min(distanceBetween(vertex, a), distanceBetween(vertex, b)) * ANGLE_ARC_FRACTION;
|
|
563
|
-
}
|
|
564
|
-
function formatMillimetres(mm, digits = 2) {
|
|
565
|
-
return `${mm.toFixed(digits)} mm`;
|
|
566
|
-
}
|
|
567
|
-
function formatDegrees(degrees, digits = 1) {
|
|
568
|
-
return `${degrees.toFixed(digits)}\xB0`;
|
|
569
|
-
}
|
|
570
|
-
function measurementLabel(measurement, format = formatMillimetres) {
|
|
571
|
-
if (measurement.kind === "distance") {
|
|
572
|
-
const [a2, b2] = measurement.points;
|
|
573
|
-
return format(distanceBetween(a2, b2));
|
|
574
|
-
}
|
|
575
|
-
const [a, vertex, b] = measurement.points;
|
|
576
|
-
return formatDegrees(angleAt(vertex, a, b));
|
|
577
|
-
}
|
|
578
|
-
function measurementLabelAnchor(measurement) {
|
|
579
|
-
if (measurement.kind === "distance") return midpoint(measurement.points[0], measurement.points[1]);
|
|
580
|
-
const [a, vertex, b] = measurement.points;
|
|
581
|
-
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b) * 1.6, 2);
|
|
582
|
-
const middle = arc[1] ?? arc[0];
|
|
583
|
-
return middle ? { x: middle.x, y: middle.y, z: middle.z } : copy(vertex);
|
|
584
|
-
}
|
|
585
|
-
function copy(v) {
|
|
586
|
-
return { x: v.x, y: v.y, z: v.z };
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// src/section-tool.tsx
|
|
590
|
-
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
591
|
-
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
592
|
-
var Z = new Vector34(0, 0, 1);
|
|
902
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
903
|
+
var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
|
|
904
|
+
var Z = new Vector36(0, 0, 1);
|
|
593
905
|
var AXIS_ROTATIONS = {
|
|
594
906
|
x: [0, Math.PI / 2, 0],
|
|
595
907
|
y: [Math.PI / 2, 0, 0],
|
|
@@ -601,12 +913,12 @@ var SectionTool = ({ theme }) => {
|
|
|
601
913
|
const store = useSectionStore();
|
|
602
914
|
const section = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
603
915
|
const box = useContentBox();
|
|
604
|
-
const resolved =
|
|
605
|
-
|
|
916
|
+
const resolved = useMemo5(() => resolveTheme(theme), [theme]);
|
|
917
|
+
useEffect4(() => {
|
|
606
918
|
store.setEngaged(true);
|
|
607
919
|
return () => store.setEngaged(false);
|
|
608
920
|
}, [store]);
|
|
609
|
-
|
|
921
|
+
useEffect4(() => {
|
|
610
922
|
if (!section) return void 0;
|
|
611
923
|
const clear = (event) => {
|
|
612
924
|
if (event.key === "Escape") store.set(null);
|
|
@@ -615,39 +927,43 @@ var SectionTool = ({ theme }) => {
|
|
|
615
927
|
return () => window.removeEventListener("keydown", clear);
|
|
616
928
|
}, [section, store]);
|
|
617
929
|
if (box.isEmpty()) return null;
|
|
618
|
-
return /* @__PURE__ */
|
|
930
|
+
return /* @__PURE__ */ jsx8("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx8(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx8(Picker, { box, theme: resolved }) });
|
|
619
931
|
};
|
|
620
932
|
var useSquare = () => {
|
|
621
|
-
const square =
|
|
933
|
+
const square = useMemo5(
|
|
622
934
|
() => new BufferGeometry2().setFromPoints([
|
|
623
|
-
new
|
|
624
|
-
new
|
|
625
|
-
new
|
|
626
|
-
new
|
|
935
|
+
new Vector36(-1, 1, 0),
|
|
936
|
+
new Vector36(1, 1, 0),
|
|
937
|
+
new Vector36(1, -1, 0),
|
|
938
|
+
new Vector36(-1, -1, 0)
|
|
627
939
|
]),
|
|
628
940
|
[]
|
|
629
941
|
);
|
|
630
|
-
|
|
942
|
+
useEffect4(() => () => square.dispose(), [square]);
|
|
631
943
|
return square;
|
|
632
944
|
};
|
|
633
945
|
var Picker = ({ box, theme }) => {
|
|
634
946
|
const store = useSectionStore();
|
|
635
|
-
const camera =
|
|
636
|
-
const scene =
|
|
637
|
-
const domElement =
|
|
638
|
-
const invalidate =
|
|
947
|
+
const camera = useThree5((state) => state.camera);
|
|
948
|
+
const scene = useThree5((state) => state.scene);
|
|
949
|
+
const domElement = useThree5((state) => state.gl.domElement);
|
|
950
|
+
const invalidate = useThree5((state) => state.invalidate);
|
|
639
951
|
const isTap = useTapGuard();
|
|
640
952
|
const square = useSquare();
|
|
641
|
-
const previewRef =
|
|
642
|
-
const hover =
|
|
643
|
-
const [hoveredPlane, setHoveredPlane] =
|
|
644
|
-
const span =
|
|
645
|
-
const centre =
|
|
953
|
+
const previewRef = useRef3(null);
|
|
954
|
+
const hover = useRef3(null);
|
|
955
|
+
const [hoveredPlane, setHoveredPlane] = useState3(null);
|
|
956
|
+
const span = useMemo5(() => box.getSize(new Vector36()).length(), [box]);
|
|
957
|
+
const centre = useMemo5(() => box.getCenter(new Vector36()), [box]);
|
|
646
958
|
const previewSize = span * PREVIEW_SCALE;
|
|
647
|
-
|
|
959
|
+
useEffect4(() => {
|
|
960
|
+
store.setPicking(true);
|
|
961
|
+
return () => store.setPicking(false);
|
|
962
|
+
}, [store]);
|
|
963
|
+
useEffect4(() => {
|
|
648
964
|
const raycaster = new Raycaster();
|
|
649
|
-
const pointer = new
|
|
650
|
-
const aim = new
|
|
965
|
+
const pointer = new Vector2();
|
|
966
|
+
const aim = new Vector36();
|
|
651
967
|
const hide = () => {
|
|
652
968
|
if (!hover.current) return;
|
|
653
969
|
hover.current = null;
|
|
@@ -674,6 +990,7 @@ var Picker = ({ box, theme }) => {
|
|
|
674
990
|
invalidate();
|
|
675
991
|
};
|
|
676
992
|
const up = (event) => {
|
|
993
|
+
move(event);
|
|
677
994
|
const hit = hover.current;
|
|
678
995
|
if (event.button !== 0 || !hit || !isTap(event)) return;
|
|
679
996
|
store.set({
|
|
@@ -691,7 +1008,7 @@ var Picker = ({ box, theme }) => {
|
|
|
691
1008
|
domElement.removeEventListener("pointerup", up);
|
|
692
1009
|
};
|
|
693
1010
|
}, [box, camera, domElement, invalidate, isTap, scene, span, store]);
|
|
694
|
-
const [planes, setPlanes] =
|
|
1011
|
+
const [planes, setPlanes] = useState3(() => axesPlanes(box, camera.position));
|
|
695
1012
|
const key = planes.map((plane) => plane.sign).join();
|
|
696
1013
|
useFrame(() => {
|
|
697
1014
|
const next = [camera.position.x, camera.position.y, camera.position.z].map((eye, axis) => eye - [centre.x, centre.y, centre.z][axis] < 0 ? -1 : 1).join();
|
|
@@ -717,11 +1034,11 @@ var Picker = ({ box, theme }) => {
|
|
|
717
1034
|
store.set(plane.options);
|
|
718
1035
|
}
|
|
719
1036
|
});
|
|
720
|
-
return /* @__PURE__ */
|
|
721
|
-
/* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
723
|
-
/* @__PURE__ */
|
|
724
|
-
/* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1038
|
+
/* @__PURE__ */ jsxs4("group", { ref: previewRef, visible: false, children: [
|
|
1039
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
1040
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [previewSize, previewSize] }),
|
|
1041
|
+
/* @__PURE__ */ jsx8(
|
|
725
1042
|
"meshBasicMaterial",
|
|
726
1043
|
{
|
|
727
1044
|
color: theme.sectionOutline,
|
|
@@ -732,14 +1049,14 @@ var Picker = ({ box, theme }) => {
|
|
|
732
1049
|
}
|
|
733
1050
|
)
|
|
734
1051
|
] }),
|
|
735
|
-
/* @__PURE__ */
|
|
1052
|
+
/* @__PURE__ */ jsx8(
|
|
736
1053
|
"lineLoop",
|
|
737
1054
|
{
|
|
738
1055
|
geometry: square,
|
|
739
1056
|
scale: previewSize / 2,
|
|
740
1057
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
741
1058
|
raycast: () => null,
|
|
742
|
-
children: /* @__PURE__ */
|
|
1059
|
+
children: /* @__PURE__ */ jsx8(
|
|
743
1060
|
"lineBasicMaterial",
|
|
744
1061
|
{
|
|
745
1062
|
color: theme.sectionOutline,
|
|
@@ -752,15 +1069,15 @@ var Picker = ({ box, theme }) => {
|
|
|
752
1069
|
}
|
|
753
1070
|
)
|
|
754
1071
|
] }),
|
|
755
|
-
planes.map((plane) => /* @__PURE__ */
|
|
1072
|
+
planes.map((plane) => /* @__PURE__ */ jsxs4(
|
|
756
1073
|
"group",
|
|
757
1074
|
{
|
|
758
1075
|
position: [plane.position.x, plane.position.y, plane.position.z],
|
|
759
1076
|
rotation: AXIS_ROTATIONS[plane.axis],
|
|
760
1077
|
children: [
|
|
761
|
-
/* @__PURE__ */
|
|
762
|
-
/* @__PURE__ */
|
|
763
|
-
/* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
|
|
1079
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [plane.size, plane.size] }),
|
|
1080
|
+
/* @__PURE__ */ jsx8(
|
|
764
1081
|
"meshBasicMaterial",
|
|
765
1082
|
{
|
|
766
1083
|
color: AXIS_COLORS[plane.axis],
|
|
@@ -771,14 +1088,14 @@ var Picker = ({ box, theme }) => {
|
|
|
771
1088
|
}
|
|
772
1089
|
)
|
|
773
1090
|
] }),
|
|
774
|
-
/* @__PURE__ */
|
|
1091
|
+
/* @__PURE__ */ jsx8(
|
|
775
1092
|
"lineLoop",
|
|
776
1093
|
{
|
|
777
1094
|
geometry: square,
|
|
778
1095
|
scale: plane.size / 2,
|
|
779
1096
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
780
1097
|
raycast: () => null,
|
|
781
|
-
children: /* @__PURE__ */
|
|
1098
|
+
children: /* @__PURE__ */ jsx8("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
|
|
782
1099
|
}
|
|
783
1100
|
)
|
|
784
1101
|
]
|
|
@@ -789,9 +1106,9 @@ var Picker = ({ box, theme }) => {
|
|
|
789
1106
|
};
|
|
790
1107
|
var CutSheet = ({ box, section, theme }) => {
|
|
791
1108
|
const square = useSquare();
|
|
792
|
-
const cut =
|
|
793
|
-
const span =
|
|
794
|
-
const placement =
|
|
1109
|
+
const cut = useMemo5(() => resolveSectionPlane(section, box), [box, section]);
|
|
1110
|
+
const span = useMemo5(() => box.getSize(new Vector36()).length(), [box]);
|
|
1111
|
+
const placement = useMemo5(() => {
|
|
795
1112
|
if (!cut) return null;
|
|
796
1113
|
const position = sectionAnchor(box, cut.plane).addScaledVector(
|
|
797
1114
|
cut.plane.normal,
|
|
@@ -801,10 +1118,10 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
801
1118
|
}, [box, cut, span]);
|
|
802
1119
|
if (!placement) return null;
|
|
803
1120
|
const size = span * OUTLINE_SCALE;
|
|
804
|
-
return /* @__PURE__ */
|
|
805
|
-
/* @__PURE__ */
|
|
806
|
-
/* @__PURE__ */
|
|
807
|
-
/* @__PURE__ */
|
|
1121
|
+
return /* @__PURE__ */ jsxs4("group", { position: placement.position, quaternion: placement.quaternion, children: [
|
|
1122
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
1123
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [size, size] }),
|
|
1124
|
+
/* @__PURE__ */ jsx8(
|
|
808
1125
|
"meshBasicMaterial",
|
|
809
1126
|
{
|
|
810
1127
|
color: theme.sectionOutline,
|
|
@@ -815,14 +1132,14 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
815
1132
|
}
|
|
816
1133
|
)
|
|
817
1134
|
] }),
|
|
818
|
-
/* @__PURE__ */
|
|
1135
|
+
/* @__PURE__ */ jsx8(
|
|
819
1136
|
"lineLoop",
|
|
820
1137
|
{
|
|
821
1138
|
geometry: square,
|
|
822
1139
|
scale: size / 2,
|
|
823
1140
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
824
1141
|
raycast: () => null,
|
|
825
|
-
children: /* @__PURE__ */
|
|
1142
|
+
children: /* @__PURE__ */ jsx8(
|
|
826
1143
|
"lineBasicMaterial",
|
|
827
1144
|
{
|
|
828
1145
|
color: theme.sectionOutline,
|
|
@@ -839,25 +1156,32 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
839
1156
|
|
|
840
1157
|
// src/measure-tool.tsx
|
|
841
1158
|
import { Html, Line } from "@react-three/drei";
|
|
842
|
-
import { useFrame as useFrame2, useThree as
|
|
843
|
-
import {
|
|
1159
|
+
import { useFrame as useFrame2, useThree as useThree6 } from "@react-three/fiber";
|
|
1160
|
+
import {
|
|
1161
|
+
useEffect as useEffect5,
|
|
1162
|
+
useLayoutEffect as useLayoutEffect3,
|
|
1163
|
+
useMemo as useMemo6,
|
|
1164
|
+
useRef as useRef4,
|
|
1165
|
+
useState as useState4,
|
|
1166
|
+
useSyncExternalStore as useSyncExternalStore2
|
|
1167
|
+
} from "react";
|
|
844
1168
|
import {
|
|
845
1169
|
Matrix4,
|
|
846
1170
|
Raycaster as Raycaster2,
|
|
847
1171
|
SphereGeometry,
|
|
848
|
-
Vector2 as
|
|
849
|
-
Vector3 as
|
|
1172
|
+
Vector2 as Vector22,
|
|
1173
|
+
Vector3 as Vector38
|
|
850
1174
|
} from "three";
|
|
851
1175
|
|
|
852
1176
|
// src/render/resample.ts
|
|
853
|
-
import { Vector3 as
|
|
1177
|
+
import { Vector3 as Vector37 } from "three";
|
|
854
1178
|
var ON_PLANE = 1e-7;
|
|
855
1179
|
var STITCH = 1e-5;
|
|
856
1180
|
var COLLINEAR = 1e-6;
|
|
857
1181
|
function clipSegments(positions, plane) {
|
|
858
1182
|
const out = [];
|
|
859
|
-
const a = new
|
|
860
|
-
const b = new
|
|
1183
|
+
const a = new Vector37();
|
|
1184
|
+
const b = new Vector37();
|
|
861
1185
|
const count = Math.floor(positions.length / 6);
|
|
862
1186
|
for (let i = 0; i < count; i += 1) {
|
|
863
1187
|
a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
|
|
@@ -872,11 +1196,11 @@ function clipSegments(positions, plane) {
|
|
|
872
1196
|
return Float32Array.from(out);
|
|
873
1197
|
}
|
|
874
1198
|
function sectionContour(positions, regionOf, plane) {
|
|
875
|
-
const a = new
|
|
876
|
-
const b = new
|
|
877
|
-
const c = new
|
|
878
|
-
const p = new
|
|
879
|
-
const q = new
|
|
1199
|
+
const a = new Vector37();
|
|
1200
|
+
const b = new Vector37();
|
|
1201
|
+
const c = new Vector37();
|
|
1202
|
+
const p = new Vector37();
|
|
1203
|
+
const q = new Vector37();
|
|
880
1204
|
const byRegion = /* @__PURE__ */ new Map();
|
|
881
1205
|
const triangleCount = Math.floor(positions.length / 9);
|
|
882
1206
|
for (let t = 0; t < triangleCount; t += 1) {
|
|
@@ -934,17 +1258,17 @@ function stitch(segments, out) {
|
|
|
934
1258
|
return into.set(segments[o], segments[o + 1], segments[o + 2]);
|
|
935
1259
|
};
|
|
936
1260
|
const direction = (i, into) => {
|
|
937
|
-
const s = new
|
|
938
|
-
const e = new
|
|
1261
|
+
const s = new Vector37();
|
|
1262
|
+
const e = new Vector37();
|
|
939
1263
|
point(i, 0, s);
|
|
940
1264
|
point(i, 1, e);
|
|
941
1265
|
return into.subVectors(e, s).normalize();
|
|
942
1266
|
};
|
|
943
1267
|
const consumed = new Uint8Array(count);
|
|
944
|
-
const d = new
|
|
945
|
-
const n = new
|
|
1268
|
+
const d = new Vector37();
|
|
1269
|
+
const n = new Vector37();
|
|
946
1270
|
const next = (i, end) => {
|
|
947
|
-
const at = point(i, end, new
|
|
1271
|
+
const at = point(i, end, new Vector37());
|
|
948
1272
|
const list = ends.get(key(at.x, at.y, at.z)) ?? [];
|
|
949
1273
|
if (list.length !== 2) return null;
|
|
950
1274
|
const other = list.find((entry) => entry >> 1 !== i);
|
|
@@ -973,8 +1297,8 @@ function stitch(segments, out) {
|
|
|
973
1297
|
};
|
|
974
1298
|
const back = walk(0);
|
|
975
1299
|
const forward = walk(1);
|
|
976
|
-
const s = point(back.index, back.end, new
|
|
977
|
-
const e = point(forward.index, forward.end, new
|
|
1300
|
+
const s = point(back.index, back.end, new Vector37());
|
|
1301
|
+
const e = point(forward.index, forward.end, new Vector37());
|
|
978
1302
|
out.push(s.x, s.y, s.z, e.x, e.y, e.z);
|
|
979
1303
|
}
|
|
980
1304
|
}
|
|
@@ -982,8 +1306,8 @@ function pointInContour(point, plane, contour) {
|
|
|
982
1306
|
const [u, v] = planeAxes(plane);
|
|
983
1307
|
const px = point.dot(u);
|
|
984
1308
|
const py = point.dot(v);
|
|
985
|
-
const a = new
|
|
986
|
-
const b = new
|
|
1309
|
+
const a = new Vector37();
|
|
1310
|
+
const b = new Vector37();
|
|
987
1311
|
let inside = false;
|
|
988
1312
|
const count = Math.floor(contour.length / 6);
|
|
989
1313
|
for (let i = 0; i < count; i += 1) {
|
|
@@ -1000,15 +1324,15 @@ function pointInContour(point, plane, contour) {
|
|
|
1000
1324
|
return inside;
|
|
1001
1325
|
}
|
|
1002
1326
|
function capHit(ray, plane, contour) {
|
|
1003
|
-
const point = ray.intersectPlane(plane, new
|
|
1327
|
+
const point = ray.intersectPlane(plane, new Vector37());
|
|
1004
1328
|
if (!point) return null;
|
|
1005
1329
|
return pointInContour(point, plane, contour) ? point : null;
|
|
1006
1330
|
}
|
|
1007
1331
|
function planeAxes(plane) {
|
|
1008
1332
|
const n = plane.normal;
|
|
1009
|
-
const seed = Math.abs(n.x) < 0.9 ? new
|
|
1010
|
-
const u = new
|
|
1011
|
-
const v = new
|
|
1333
|
+
const seed = Math.abs(n.x) < 0.9 ? new Vector37(1, 0, 0) : new Vector37(0, 1, 0);
|
|
1334
|
+
const u = new Vector37().crossVectors(n, seed).normalize();
|
|
1335
|
+
const v = new Vector37().crossVectors(n, u).normalize();
|
|
1012
1336
|
return [u, v];
|
|
1013
1337
|
}
|
|
1014
1338
|
function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
@@ -1021,8 +1345,8 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
|
1021
1345
|
}
|
|
1022
1346
|
|
|
1023
1347
|
// src/measure-tool.tsx
|
|
1024
|
-
import { Fragment as Fragment2, jsx as
|
|
1025
|
-
var
|
|
1348
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1349
|
+
var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1026
1350
|
var MEASURE_LABEL_CLASS = "toolpath-measure-label";
|
|
1027
1351
|
var MeasureTool = ({
|
|
1028
1352
|
mode = "distance",
|
|
@@ -1035,25 +1359,26 @@ var MeasureTool = ({
|
|
|
1035
1359
|
}) => {
|
|
1036
1360
|
const store = useSectionStore();
|
|
1037
1361
|
const box = useContentBox();
|
|
1038
|
-
const resolved =
|
|
1039
|
-
const
|
|
1362
|
+
const resolved = useMemo6(() => resolveTheme(theme), [theme]);
|
|
1363
|
+
const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
|
|
1364
|
+
const [held, setHeld] = useState4([]);
|
|
1040
1365
|
const controlled = measurements !== void 0;
|
|
1041
1366
|
const list = controlled ? measurements : held;
|
|
1042
|
-
const [draft, setDraft] =
|
|
1043
|
-
|
|
1367
|
+
const [draft, setDraft] = useState4([]);
|
|
1368
|
+
useEffect5(() => {
|
|
1044
1369
|
store.setEngaged(true);
|
|
1045
1370
|
return () => store.setEngaged(false);
|
|
1046
1371
|
}, [store]);
|
|
1047
|
-
|
|
1372
|
+
useEffect5(() => {
|
|
1048
1373
|
setDraft([]);
|
|
1049
1374
|
}, [mode]);
|
|
1050
1375
|
const commit = (next) => {
|
|
1051
1376
|
if (!controlled) setHeld(next);
|
|
1052
1377
|
onChange?.(next);
|
|
1053
1378
|
};
|
|
1054
|
-
const latest =
|
|
1379
|
+
const latest = useRef4({ list, commit });
|
|
1055
1380
|
latest.current = { list, commit };
|
|
1056
|
-
|
|
1381
|
+
useEffect5(() => {
|
|
1057
1382
|
const onKey = (event) => {
|
|
1058
1383
|
if (event.key === "Escape") {
|
|
1059
1384
|
setDraft([]);
|
|
@@ -1081,8 +1406,8 @@ var MeasureTool = ({
|
|
|
1081
1406
|
};
|
|
1082
1407
|
if (box.isEmpty()) return null;
|
|
1083
1408
|
const label = className(labelClassName);
|
|
1084
|
-
return /* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsxs5("group", { userData: FURNITURE5, children: [
|
|
1410
|
+
picking ? null : /* @__PURE__ */ jsx9(
|
|
1086
1411
|
Snapper,
|
|
1087
1412
|
{
|
|
1088
1413
|
mode,
|
|
@@ -1093,7 +1418,7 @@ var MeasureTool = ({
|
|
|
1093
1418
|
label
|
|
1094
1419
|
}
|
|
1095
1420
|
),
|
|
1096
|
-
draft.map((point, index) => /* @__PURE__ */
|
|
1421
|
+
draft.map((point, index) => /* @__PURE__ */ jsx9(
|
|
1097
1422
|
ScreenDot,
|
|
1098
1423
|
{
|
|
1099
1424
|
point,
|
|
@@ -1102,8 +1427,8 @@ var MeasureTool = ({
|
|
|
1102
1427
|
},
|
|
1103
1428
|
index
|
|
1104
1429
|
)),
|
|
1105
|
-
draft.length === 2 ? /* @__PURE__ */
|
|
1106
|
-
list.map((measurement) => /* @__PURE__ */
|
|
1430
|
+
draft.length === 2 ? /* @__PURE__ */ jsx9(Segment, { points: [draft[0], draft[1]], color: resolved.measure }) : null,
|
|
1431
|
+
list.map((measurement) => /* @__PURE__ */ jsx9(
|
|
1107
1432
|
MeasurementView,
|
|
1108
1433
|
{
|
|
1109
1434
|
measurement,
|
|
@@ -1117,30 +1442,30 @@ var MeasureTool = ({
|
|
|
1117
1442
|
] });
|
|
1118
1443
|
};
|
|
1119
1444
|
var NO_POINTER = { pointerEvents: "none" };
|
|
1120
|
-
var NOWHERE = [new
|
|
1445
|
+
var NOWHERE = [new Vector38(), new Vector38()];
|
|
1121
1446
|
var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
1122
|
-
const camera =
|
|
1123
|
-
const scene =
|
|
1124
|
-
const size =
|
|
1125
|
-
const domElement =
|
|
1126
|
-
const invalidate =
|
|
1447
|
+
const camera = useThree6((state) => state.camera);
|
|
1448
|
+
const scene = useThree6((state) => state.scene);
|
|
1449
|
+
const size = useThree6((state) => state.size);
|
|
1450
|
+
const domElement = useThree6((state) => state.gl.domElement);
|
|
1451
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
1127
1452
|
const isTap = useTapGuard();
|
|
1128
|
-
const marker =
|
|
1129
|
-
const edgeLine =
|
|
1130
|
-
const band =
|
|
1131
|
-
const bandLabel =
|
|
1132
|
-
const bandText =
|
|
1133
|
-
const snap =
|
|
1453
|
+
const marker = useRef4(null);
|
|
1454
|
+
const edgeLine = useRef4(null);
|
|
1455
|
+
const band = useRef4(null);
|
|
1456
|
+
const bandLabel = useRef4(null);
|
|
1457
|
+
const bandText = useRef4(null);
|
|
1458
|
+
const snap = useRef4(null);
|
|
1134
1459
|
const dot = useDot();
|
|
1135
|
-
|
|
1460
|
+
useLayoutEffect3(() => {
|
|
1136
1461
|
if (edgeLine.current) edgeLine.current.visible = false;
|
|
1137
1462
|
if (band.current) band.current.visible = false;
|
|
1138
1463
|
}, []);
|
|
1139
|
-
const current =
|
|
1464
|
+
const current = useRef4({ mode, draft, onPlace, format, size });
|
|
1140
1465
|
current.current = { mode, draft, onPlace, format, size };
|
|
1141
|
-
|
|
1466
|
+
useEffect5(() => {
|
|
1142
1467
|
const raycaster = new Raycaster2();
|
|
1143
|
-
const pointer = new
|
|
1468
|
+
const pointer = new Vector22();
|
|
1144
1469
|
const samples = new SampleCache();
|
|
1145
1470
|
const hide = () => {
|
|
1146
1471
|
if (!snap.current) return;
|
|
@@ -1170,7 +1495,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1170
1495
|
const snapped = snapAt(target, target.edges, camera, viewport);
|
|
1171
1496
|
const from = placed[placed.length - 1];
|
|
1172
1497
|
const lock = shift && from ? lockToAxis(from, snapped.point) : null;
|
|
1173
|
-
const found = lock ? { ...snapped, point: new
|
|
1498
|
+
const found = lock ? { ...snapped, point: new Vector38(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
|
|
1174
1499
|
snap.current = found;
|
|
1175
1500
|
const node = marker.current;
|
|
1176
1501
|
if (node) {
|
|
@@ -1212,8 +1537,14 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1212
1537
|
if (event.key !== "Shift" || !last || !snap.current) return;
|
|
1213
1538
|
update(last.x, last.y, event.type === "keydown");
|
|
1214
1539
|
};
|
|
1540
|
+
let pressed = false;
|
|
1541
|
+
const down = () => {
|
|
1542
|
+
pressed = true;
|
|
1543
|
+
};
|
|
1215
1544
|
const up = (event) => {
|
|
1216
|
-
|
|
1545
|
+
const seen = pressed;
|
|
1546
|
+
pressed = false;
|
|
1547
|
+
if (!seen || event.button !== 0 || !isTap(event)) return;
|
|
1217
1548
|
update(event.clientX, event.clientY, event.shiftKey);
|
|
1218
1549
|
const found = snap.current;
|
|
1219
1550
|
if (!found) return;
|
|
@@ -1225,18 +1556,20 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1225
1556
|
};
|
|
1226
1557
|
domElement.addEventListener("pointermove", move);
|
|
1227
1558
|
domElement.addEventListener("pointerleave", leave);
|
|
1559
|
+
domElement.addEventListener("pointerdown", down);
|
|
1228
1560
|
domElement.addEventListener("pointerup", up);
|
|
1229
1561
|
window.addEventListener("keydown", key);
|
|
1230
1562
|
window.addEventListener("keyup", key);
|
|
1231
1563
|
return () => {
|
|
1232
1564
|
domElement.removeEventListener("pointermove", move);
|
|
1233
1565
|
domElement.removeEventListener("pointerleave", leave);
|
|
1566
|
+
domElement.removeEventListener("pointerdown", down);
|
|
1234
1567
|
domElement.removeEventListener("pointerup", up);
|
|
1235
1568
|
window.removeEventListener("keydown", key);
|
|
1236
1569
|
window.removeEventListener("keyup", key);
|
|
1237
1570
|
};
|
|
1238
1571
|
}, [camera, domElement, invalidate, isTap, scene, theme.measure]);
|
|
1239
|
-
|
|
1572
|
+
useEffect5(() => {
|
|
1240
1573
|
if (draft.length === 0 && band.current) {
|
|
1241
1574
|
band.current.visible = false;
|
|
1242
1575
|
if (bandLabel.current) bandLabel.current.visible = false;
|
|
@@ -1247,9 +1580,9 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1247
1580
|
invalidate();
|
|
1248
1581
|
}
|
|
1249
1582
|
}, [draft, invalidate]);
|
|
1250
|
-
return /* @__PURE__ */
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1583
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1584
|
+
/* @__PURE__ */ jsx9("group", { ref: marker, visible: false, children: /* @__PURE__ */ jsx9("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx9("meshBasicMaterial", { color: theme.measureSnap, depthTest: false, depthWrite: false }) }) }),
|
|
1585
|
+
/* @__PURE__ */ jsx9(
|
|
1253
1586
|
Line,
|
|
1254
1587
|
{
|
|
1255
1588
|
ref: edgeLine,
|
|
@@ -1263,7 +1596,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1263
1596
|
raycast: () => null
|
|
1264
1597
|
}
|
|
1265
1598
|
),
|
|
1266
|
-
/* @__PURE__ */
|
|
1599
|
+
/* @__PURE__ */ jsx9(
|
|
1267
1600
|
Line,
|
|
1268
1601
|
{
|
|
1269
1602
|
ref: band,
|
|
@@ -1277,7 +1610,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1277
1610
|
raycast: () => null
|
|
1278
1611
|
}
|
|
1279
1612
|
),
|
|
1280
|
-
/* @__PURE__ */
|
|
1613
|
+
/* @__PURE__ */ jsx9("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx9(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx9(
|
|
1281
1614
|
"div",
|
|
1282
1615
|
{
|
|
1283
1616
|
ref: bandText,
|
|
@@ -1442,35 +1775,35 @@ var MeasurementView = ({
|
|
|
1442
1775
|
const anchor = measurementLabelAnchor(measurement);
|
|
1443
1776
|
if (measurement.kind === "distance") {
|
|
1444
1777
|
const [a2, b2] = measurement.points;
|
|
1445
|
-
return /* @__PURE__ */
|
|
1446
|
-
/* @__PURE__ */
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
/* @__PURE__ */
|
|
1449
|
-
showDeltas ? /* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1778
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1779
|
+
/* @__PURE__ */ jsx9(Segment, { points: [a2, b2], color: theme.measure }),
|
|
1780
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
1781
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
1782
|
+
showDeltas ? /* @__PURE__ */ jsx9(DeltaLegs, { a: a2, b: b2, id: measurement.id, format, label }) : null,
|
|
1783
|
+
/* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "distance", id: measurement.id, className: label })
|
|
1451
1784
|
] });
|
|
1452
1785
|
}
|
|
1453
1786
|
const [a, vertex, b] = measurement.points;
|
|
1454
1787
|
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
|
|
1455
|
-
return /* @__PURE__ */
|
|
1456
|
-
/* @__PURE__ */
|
|
1457
|
-
/* @__PURE__ */
|
|
1458
|
-
/* @__PURE__ */
|
|
1459
|
-
/* @__PURE__ */
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1788
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1789
|
+
/* @__PURE__ */ jsx9(Segment, { points: [vertex, a], color: theme.measure }),
|
|
1790
|
+
/* @__PURE__ */ jsx9(Segment, { points: [vertex, b], color: theme.measure }),
|
|
1791
|
+
/* @__PURE__ */ jsx9(Polyline, { points: arc, color: theme.measure }),
|
|
1792
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: a, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
1793
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: vertex, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
1794
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: b, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
1795
|
+
/* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "angle", id: measurement.id, className: label })
|
|
1463
1796
|
] });
|
|
1464
1797
|
};
|
|
1465
1798
|
var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
1466
|
-
const camera =
|
|
1467
|
-
const size =
|
|
1468
|
-
const invalidate =
|
|
1469
|
-
const legs =
|
|
1470
|
-
const lines =
|
|
1471
|
-
const centre =
|
|
1799
|
+
const camera = useThree6((state) => state.camera);
|
|
1800
|
+
const size = useThree6((state) => state.size);
|
|
1801
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
1802
|
+
const legs = useMemo6(() => deltaLegs(a, b), [a, b]);
|
|
1803
|
+
const lines = useRef4([]);
|
|
1804
|
+
const centre = useMemo6(() => {
|
|
1472
1805
|
const at = midpoint(a, b);
|
|
1473
|
-
return new
|
|
1806
|
+
return new Vector38(at.x, at.y, at.z);
|
|
1474
1807
|
}, [a, b]);
|
|
1475
1808
|
useFrame2(() => {
|
|
1476
1809
|
const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
|
|
@@ -1484,16 +1817,16 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1484
1817
|
}
|
|
1485
1818
|
if (changed) invalidate();
|
|
1486
1819
|
});
|
|
1487
|
-
return /* @__PURE__ */
|
|
1488
|
-
legs.map((leg, index) => /* @__PURE__ */
|
|
1820
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1821
|
+
legs.map((leg, index) => /* @__PURE__ */ jsx9(
|
|
1489
1822
|
Line,
|
|
1490
1823
|
{
|
|
1491
1824
|
ref: (line) => {
|
|
1492
1825
|
lines.current[index] = line;
|
|
1493
1826
|
},
|
|
1494
1827
|
points: [
|
|
1495
|
-
new
|
|
1496
|
-
new
|
|
1828
|
+
new Vector38(leg.from.x, leg.from.y, leg.from.z),
|
|
1829
|
+
new Vector38(leg.to.x, leg.to.y, leg.to.z)
|
|
1497
1830
|
],
|
|
1498
1831
|
color: AXIS_COLORS[leg.axis],
|
|
1499
1832
|
lineWidth: MEASURE_LINE_PIXELS.leg,
|
|
@@ -1508,7 +1841,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1508
1841
|
},
|
|
1509
1842
|
leg.axis
|
|
1510
1843
|
)),
|
|
1511
|
-
legs.map((leg) => /* @__PURE__ */
|
|
1844
|
+
legs.map((leg) => /* @__PURE__ */ jsx9(
|
|
1512
1845
|
Label,
|
|
1513
1846
|
{
|
|
1514
1847
|
at: midpoint(leg.from, leg.to),
|
|
@@ -1522,14 +1855,14 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1522
1855
|
))
|
|
1523
1856
|
] });
|
|
1524
1857
|
};
|
|
1525
|
-
var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */
|
|
1858
|
+
var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */ jsx9("group", { position: [at.x, at.y, at.z], children: /* @__PURE__ */ jsx9(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx9("div", { className: name, "data-measure-label": kind, "data-measurement-id": id, "data-axis": axis, children: text }) }) });
|
|
1526
1859
|
var ScreenDot = ({ point, pixels, color }) => {
|
|
1527
|
-
const camera =
|
|
1528
|
-
const size =
|
|
1529
|
-
const invalidate =
|
|
1530
|
-
const node =
|
|
1860
|
+
const camera = useThree6((state) => state.camera);
|
|
1861
|
+
const size = useThree6((state) => state.size);
|
|
1862
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
1863
|
+
const node = useRef4(null);
|
|
1531
1864
|
const dot = useDot();
|
|
1532
|
-
const position =
|
|
1865
|
+
const position = useMemo6(() => new Vector38(point.x, point.y, point.z), [point]);
|
|
1533
1866
|
useFrame2(() => {
|
|
1534
1867
|
const group = node.current;
|
|
1535
1868
|
if (!group) return;
|
|
@@ -1539,21 +1872,21 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
1539
1872
|
invalidate();
|
|
1540
1873
|
}
|
|
1541
1874
|
});
|
|
1542
|
-
return /* @__PURE__ */
|
|
1875
|
+
return /* @__PURE__ */ jsx9("group", { ref: node, position, children: /* @__PURE__ */ jsx9("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx9("meshBasicMaterial", { color, depthTest: false, depthWrite: false }) }) });
|
|
1543
1876
|
};
|
|
1544
1877
|
var Segment = ({ points, color }) => {
|
|
1545
1878
|
const [a, b] = points;
|
|
1546
|
-
const ends =
|
|
1547
|
-
() => [new
|
|
1879
|
+
const ends = useMemo6(
|
|
1880
|
+
() => [new Vector38(a.x, a.y, a.z), new Vector38(b.x, b.y, b.z)],
|
|
1548
1881
|
// Keyed on the numbers: a draft's tuple is a fresh array on every render,
|
|
1549
1882
|
// and a rebuilt array would rebuild the line's geometry with it.
|
|
1550
1883
|
[a.x, a.y, a.z, b.x, b.y, b.z]
|
|
1551
1884
|
);
|
|
1552
|
-
return /* @__PURE__ */
|
|
1885
|
+
return /* @__PURE__ */ jsx9(Polyline, { points: ends, color });
|
|
1553
1886
|
};
|
|
1554
1887
|
var Polyline = ({ points, color }) => {
|
|
1555
1888
|
if (points.length < 2) return null;
|
|
1556
|
-
return /* @__PURE__ */
|
|
1889
|
+
return /* @__PURE__ */ jsx9(
|
|
1557
1890
|
Line,
|
|
1558
1891
|
{
|
|
1559
1892
|
points,
|
|
@@ -1568,8 +1901,8 @@ var Polyline = ({ points, color }) => {
|
|
|
1568
1901
|
);
|
|
1569
1902
|
};
|
|
1570
1903
|
function useDot() {
|
|
1571
|
-
const geometry =
|
|
1572
|
-
|
|
1904
|
+
const geometry = useMemo6(() => new SphereGeometry(1, 16, 12), []);
|
|
1905
|
+
useEffect5(() => () => geometry.dispose(), [geometry]);
|
|
1573
1906
|
return geometry;
|
|
1574
1907
|
}
|
|
1575
1908
|
function isEditing(target) {
|
|
@@ -1620,10 +1953,15 @@ export {
|
|
|
1620
1953
|
AXES_PLANE_SCALE,
|
|
1621
1954
|
AXIS_COLORS,
|
|
1622
1955
|
Axes,
|
|
1956
|
+
BANANA_GAP,
|
|
1957
|
+
BANANA_MODEL_URL,
|
|
1958
|
+
Banana,
|
|
1959
|
+
BoxStock,
|
|
1623
1960
|
CAD_CAMERA_UP,
|
|
1624
1961
|
CANDIDATE_WEIGHT,
|
|
1625
1962
|
CAP_HATCH,
|
|
1626
1963
|
CHAMFER,
|
|
1964
|
+
CONTROL_SCHEME_OPTIONS,
|
|
1627
1965
|
CadCameraControls,
|
|
1628
1966
|
DEFAULT_FIT_MARGIN,
|
|
1629
1967
|
DEFAULT_SECTION_NORMAL,
|
|
@@ -1642,6 +1980,7 @@ export {
|
|
|
1642
1980
|
HIGHLIGHT_COLORS,
|
|
1643
1981
|
HIGHLIGHT_WEIGHT,
|
|
1644
1982
|
HOVER_WEIGHT,
|
|
1983
|
+
HoverCard,
|
|
1645
1984
|
MAX_FRAME_RATIO,
|
|
1646
1985
|
MEASURE_LABEL_CLASS,
|
|
1647
1986
|
MEASURE_LINE_PIXELS,
|
|
@@ -1666,17 +2005,21 @@ export {
|
|
|
1666
2005
|
PartMesh,
|
|
1667
2006
|
PartReportFormatError,
|
|
1668
2007
|
REGION_ATTRIBUTE,
|
|
2008
|
+
SECTION_GIZMO_FRAME_SCALE,
|
|
1669
2009
|
SECTION_RENDER_ORDER,
|
|
1670
2010
|
SNAP_MARKER_PIXELS,
|
|
1671
2011
|
SNAP_PIXELS,
|
|
1672
2012
|
SectionTool,
|
|
1673
2013
|
SectionView,
|
|
2014
|
+
Stock,
|
|
1674
2015
|
TAP_SLOP,
|
|
1675
2016
|
UnsupportedKernelVersionError,
|
|
1676
2017
|
VIEW_NAMES,
|
|
1677
2018
|
VIEW_SIGNS,
|
|
1678
2019
|
ViewCube,
|
|
1679
2020
|
Viewer,
|
|
2021
|
+
ViewerToolbar,
|
|
2022
|
+
ViewerToolbarProvider,
|
|
1680
2023
|
adaptedUp,
|
|
1681
2024
|
angleArc,
|
|
1682
2025
|
angleArcRadius,
|
|
@@ -1687,8 +2030,11 @@ export {
|
|
|
1687
2030
|
arrowPlacement,
|
|
1688
2031
|
aspectRatio,
|
|
1689
2032
|
axesPlanes,
|
|
2033
|
+
bananaFrameBounds,
|
|
2034
|
+
bananaPosition,
|
|
1690
2035
|
bestOwner,
|
|
1691
2036
|
boundsFromBox,
|
|
2037
|
+
boxStockBounds,
|
|
1692
2038
|
buildPick,
|
|
1693
2039
|
buildRegionAttribute,
|
|
1694
2040
|
buildRegionIndex,
|
|
@@ -1712,6 +2058,7 @@ export {
|
|
|
1712
2058
|
deltaBetween,
|
|
1713
2059
|
deltaLegs,
|
|
1714
2060
|
directionColor,
|
|
2061
|
+
directionHighlights,
|
|
1715
2062
|
directionIndexOf,
|
|
1716
2063
|
directionLabel,
|
|
1717
2064
|
distanceBetween,
|
|
@@ -1719,6 +2066,7 @@ export {
|
|
|
1719
2066
|
excludedFromFrame,
|
|
1720
2067
|
featureTypeRank,
|
|
1721
2068
|
fitDistance,
|
|
2069
|
+
fixedBoxStockBounds,
|
|
1722
2070
|
focusForPick,
|
|
1723
2071
|
formatDegrees,
|
|
1724
2072
|
formatMillimetres,
|
|
@@ -1761,6 +2109,7 @@ export {
|
|
|
1761
2109
|
sectionDepthConstant,
|
|
1762
2110
|
sectionDepthRange,
|
|
1763
2111
|
sectionFromPick,
|
|
2112
|
+
sectionMeasurement,
|
|
1764
2113
|
sectionOffset,
|
|
1765
2114
|
sectionOptionsFromState,
|
|
1766
2115
|
sectionPlane,
|
|
@@ -1778,6 +2127,7 @@ export {
|
|
|
1778
2127
|
useSectionStore,
|
|
1779
2128
|
useTapGuard,
|
|
1780
2129
|
useViewerControls,
|
|
2130
|
+
useViewerToolbar,
|
|
1781
2131
|
viewDirection,
|
|
1782
2132
|
viewKind,
|
|
1783
2133
|
viewUp,
|