@toolpath/viewer 1.4.0 → 2.0.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 +364 -55
- package/dist/assets/banana.glb +0 -0
- package/dist/{chunk-HH3W5T42.js → chunk-4TBLMR4Z.js} +727 -168
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +281 -19
- package/dist/index.js +999 -415
- 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,760 @@ 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, useState as useState2 } from "react";
|
|
434
|
+
|
|
435
|
+
// src/render/stock-mesh.ts
|
|
436
|
+
import { Mesh } from "three";
|
|
437
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
438
|
+
async function parseStockGlb(data) {
|
|
439
|
+
const { scene } = await new GLTFLoader().parseAsync(data, "");
|
|
440
|
+
try {
|
|
441
|
+
return stockMeshGeometries(scene);
|
|
442
|
+
} finally {
|
|
443
|
+
scene.traverse((object) => {
|
|
444
|
+
if (!(object instanceof Mesh)) return;
|
|
445
|
+
object.geometry.dispose();
|
|
446
|
+
for (const material of Array.isArray(object.material) ? object.material : [object.material]) {
|
|
447
|
+
material.dispose();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function stockMeshGeometries(scene) {
|
|
453
|
+
const geometries = [];
|
|
454
|
+
scene.updateWorldMatrix(true, true);
|
|
455
|
+
scene.traverse((object) => {
|
|
456
|
+
if (!(object instanceof Mesh)) return;
|
|
457
|
+
const geometry = object.geometry.clone().applyMatrix4(object.matrixWorld);
|
|
458
|
+
if (!geometry.hasAttribute("normal")) geometry.computeVertexNormals();
|
|
459
|
+
geometries.push(geometry);
|
|
460
|
+
});
|
|
461
|
+
if (geometries.length === 0) throw new Error("The stock GLB contains no mesh.");
|
|
462
|
+
return geometries;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/render/stock-mesh-buffer.ts
|
|
466
|
+
function createStockMeshBuffer() {
|
|
467
|
+
const owned = /* @__PURE__ */ new Set();
|
|
468
|
+
let disposed = false;
|
|
469
|
+
return {
|
|
470
|
+
request(decode, ready, failed) {
|
|
471
|
+
disposed = false;
|
|
472
|
+
let active = true;
|
|
473
|
+
void decode().then(
|
|
474
|
+
(geometries) => {
|
|
475
|
+
if (!active || disposed) {
|
|
476
|
+
geometries.forEach((geometry) => geometry.dispose());
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
geometries.forEach((geometry) => owned.add(geometry));
|
|
480
|
+
ready(geometries);
|
|
481
|
+
},
|
|
482
|
+
(cause) => {
|
|
483
|
+
if (active && !disposed) {
|
|
484
|
+
failed(cause);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
return () => {
|
|
489
|
+
active = false;
|
|
490
|
+
};
|
|
491
|
+
},
|
|
492
|
+
commit(geometries) {
|
|
493
|
+
const visible = new Set(geometries);
|
|
494
|
+
for (const geometry of owned) {
|
|
495
|
+
if (!visible.has(geometry)) {
|
|
496
|
+
owned.delete(geometry);
|
|
497
|
+
geometry.dispose();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
},
|
|
501
|
+
dispose() {
|
|
502
|
+
disposed = true;
|
|
503
|
+
owned.forEach((geometry) => geometry.dispose());
|
|
504
|
+
owned.clear();
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/render/stock.ts
|
|
510
|
+
import {
|
|
511
|
+
Box3,
|
|
512
|
+
BoxGeometry,
|
|
513
|
+
CylinderGeometry,
|
|
514
|
+
EdgesGeometry,
|
|
515
|
+
Group,
|
|
516
|
+
LineBasicMaterial,
|
|
517
|
+
LineSegments,
|
|
518
|
+
Matrix4,
|
|
519
|
+
Mesh as Mesh2,
|
|
520
|
+
MeshLambertMaterial,
|
|
521
|
+
Quaternion,
|
|
522
|
+
Vector3
|
|
523
|
+
} from "three";
|
|
524
|
+
function boxFigure(bounds) {
|
|
525
|
+
return {
|
|
526
|
+
lower: { x: bounds.min.x, y: bounds.min.y, z: bounds.min.z },
|
|
527
|
+
upper: { x: bounds.max.x, y: bounds.max.y, z: bounds.max.z }
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
var CYLINDER_SEGMENTS = 64;
|
|
531
|
+
var CYLINDER_AXIS = new Vector3(0, 1, 0);
|
|
532
|
+
function fixedBoxStockBounds(geometry, dimensions, position = "model_centered", positionOffset = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
533
|
+
if (![dimensions.x, dimensions.y, dimensions.z].every(Number.isFinite) || [dimensions.x, dimensions.y, dimensions.z].some((value) => value <= 0)) {
|
|
534
|
+
throw new RangeError("Fixed stock dimensions must be finite and positive.");
|
|
535
|
+
}
|
|
536
|
+
if (!Number.isFinite(positionOffset)) {
|
|
537
|
+
throw new RangeError("Fixed stock position offset must be finite.");
|
|
538
|
+
}
|
|
539
|
+
if (![offset.x, offset.y, offset.z].every(Number.isFinite)) {
|
|
540
|
+
throw new RangeError("Fixed stock offset must be finite.");
|
|
541
|
+
}
|
|
542
|
+
const part = partBounds(geometry);
|
|
543
|
+
const center = part.getCenter(new Vector3());
|
|
544
|
+
const z = fixedStockLowerZ(part, dimensions.z, position, positionOffset);
|
|
545
|
+
return new Box3(
|
|
546
|
+
new Vector3(center.x - dimensions.x / 2, center.y - dimensions.y / 2, z),
|
|
547
|
+
new Vector3(center.x + dimensions.x / 2, center.y + dimensions.y / 2, z + dimensions.z)
|
|
548
|
+
).translate(new Vector3(offset.x, offset.y, offset.z));
|
|
549
|
+
}
|
|
550
|
+
function fixedCylinderStock(geometry, size, position = "model_centered", positionOffset = 0) {
|
|
551
|
+
if (![size.diameter, size.length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
552
|
+
throw new RangeError("Fixed stock diameter and length must be finite and positive.");
|
|
553
|
+
}
|
|
554
|
+
if (!Number.isFinite(positionOffset)) {
|
|
555
|
+
throw new RangeError("Fixed stock position offset must be finite.");
|
|
556
|
+
}
|
|
557
|
+
const part = partBounds(geometry);
|
|
558
|
+
const center = part.getCenter(new Vector3());
|
|
559
|
+
return {
|
|
560
|
+
origin: {
|
|
561
|
+
x: center.x,
|
|
562
|
+
y: center.y,
|
|
563
|
+
z: fixedStockLowerZ(part, size.length, position, positionOffset)
|
|
564
|
+
},
|
|
565
|
+
axis: { x: 0, y: 0, z: 1 },
|
|
566
|
+
diameter: size.diameter,
|
|
567
|
+
length: size.length
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function fixedStockLowerZ(part, length, position, positionOffset) {
|
|
571
|
+
if (position === "offset_from_top") return part.max.z + positionOffset - length;
|
|
572
|
+
if (position === "offset_from_bottom") return part.min.z - positionOffset;
|
|
573
|
+
return (part.min.z + part.max.z - length) / 2;
|
|
574
|
+
}
|
|
575
|
+
function cylinderStockGeometry(input) {
|
|
576
|
+
const figure = input.partGeometry ? fixedCylinderStock(input.partGeometry, input, input.position, input.positionOffset) : input;
|
|
577
|
+
const { origin, diameter, length } = figure;
|
|
578
|
+
if (![origin.x, origin.y, origin.z].every(Number.isFinite)) {
|
|
579
|
+
throw new RangeError("Cylinder stock origin must be finite.");
|
|
580
|
+
}
|
|
581
|
+
if (![diameter, length].every((value) => Number.isFinite(value) && value > 0)) {
|
|
582
|
+
throw new RangeError("Cylinder stock diameter and length must be finite and positive.");
|
|
583
|
+
}
|
|
584
|
+
const axis = new Vector3(figure.axis.x, figure.axis.y, figure.axis.z);
|
|
585
|
+
const norm = axis.length();
|
|
586
|
+
if (!Number.isFinite(norm) || norm === 0) {
|
|
587
|
+
throw new RangeError("Cylinder stock axis must be finite and non-zero.");
|
|
588
|
+
}
|
|
589
|
+
axis.divideScalar(norm);
|
|
590
|
+
const center = axis.clone().multiplyScalar(length / 2).add(new Vector3(origin.x, origin.y, origin.z));
|
|
591
|
+
return new CylinderGeometry(diameter / 2, diameter / 2, length, CYLINDER_SEGMENTS).applyQuaternion(new Quaternion().setFromUnitVectors(CYLINDER_AXIS, axis)).translate(center.x, center.y, center.z);
|
|
592
|
+
}
|
|
593
|
+
function boxStockGeometry(input) {
|
|
594
|
+
const figure = input.partGeometry ? boxFigure(
|
|
595
|
+
input.dimensions ? fixedBoxStockBounds(
|
|
596
|
+
input.partGeometry,
|
|
597
|
+
input.dimensions,
|
|
598
|
+
input.position,
|
|
599
|
+
input.positionOffset,
|
|
600
|
+
input.offset
|
|
601
|
+
) : boxStockBounds(input.partGeometry, input.allowance, input.offset)
|
|
602
|
+
) : input;
|
|
603
|
+
return orientedBoxStockGeometry(figure);
|
|
604
|
+
}
|
|
605
|
+
function orientedBoxStockGeometry(figure) {
|
|
606
|
+
const { lower, upper } = figure;
|
|
607
|
+
const frame = figure.frame ?? {
|
|
608
|
+
location: { x: 0, y: 0, z: 0 },
|
|
609
|
+
axis: { x: 0, y: 0, z: 1 },
|
|
610
|
+
refDirection: { x: 1, y: 0, z: 0 }
|
|
611
|
+
};
|
|
612
|
+
const { location, axis, refDirection } = frame;
|
|
613
|
+
if (![location, axis, refDirection, lower, upper].every(
|
|
614
|
+
(vector) => [vector.x, vector.y, vector.z].every(Number.isFinite)
|
|
615
|
+
)) {
|
|
616
|
+
throw new RangeError("Box stock frame and corners must be finite.");
|
|
617
|
+
}
|
|
618
|
+
const size = new Vector3(upper.x - lower.x, upper.y - lower.y, upper.z - lower.z);
|
|
619
|
+
if (size.toArray().some((value) => !(value > 0))) {
|
|
620
|
+
throw new RangeError("Box stock upper corner must be above its lower corner on every axis.");
|
|
621
|
+
}
|
|
622
|
+
const z = new Vector3(axis.x, axis.y, axis.z).normalize();
|
|
623
|
+
const x = new Vector3(refDirection.x, refDirection.y, refDirection.z);
|
|
624
|
+
const reference = x.length();
|
|
625
|
+
x.addScaledVector(z, -x.dot(z));
|
|
626
|
+
if (!(z.lengthSq() > 0 && x.length() > 1e-9 * reference)) {
|
|
627
|
+
throw new RangeError(
|
|
628
|
+
"Box stock frame axis and reference direction must be non-zero and not parallel."
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
x.normalize();
|
|
632
|
+
const y = new Vector3().crossVectors(z, x);
|
|
633
|
+
return new BoxGeometry(size.x, size.y, size.z).translate((lower.x + upper.x) / 2, (lower.y + upper.y) / 2, (lower.z + upper.z) / 2).applyMatrix4(new Matrix4().makeBasis(x, y, z).setPosition(location.x, location.y, location.z));
|
|
634
|
+
}
|
|
635
|
+
function boxStockBounds(geometry, allowance = 0, offset = { x: 0, y: 0, z: 0 }) {
|
|
636
|
+
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);
|
|
637
|
+
if (padding.toArray().some((value) => !Number.isFinite(value) || value < 0)) {
|
|
638
|
+
throw new RangeError("Stock allowance must be finite and non-negative.");
|
|
639
|
+
}
|
|
640
|
+
if (![offset.x, offset.y, offset.z].every(Number.isFinite)) {
|
|
641
|
+
throw new RangeError("Stock offset must be finite.");
|
|
642
|
+
}
|
|
643
|
+
const box = partBounds(geometry);
|
|
644
|
+
box.expandByVector(padding).translate(new Vector3(offset.x, offset.y, offset.z));
|
|
645
|
+
const size = box.getSize(new Vector3());
|
|
646
|
+
if (size.toArray().some((value) => !Number.isFinite(value) || value <= 0)) {
|
|
647
|
+
throw new RangeError("Stock dimensions must be finite and positive.");
|
|
648
|
+
}
|
|
649
|
+
return box;
|
|
650
|
+
}
|
|
651
|
+
function partBounds(geometry) {
|
|
652
|
+
const position = geometry.getAttribute("position");
|
|
653
|
+
if (!position || position.count === 0)
|
|
654
|
+
throw new RangeError("Stock needs non-empty part geometry.");
|
|
655
|
+
const box = new Box3();
|
|
656
|
+
const point = new Vector3();
|
|
657
|
+
for (let index = 0; index < position.count; index += 1) {
|
|
658
|
+
point.fromBufferAttribute(position, index);
|
|
659
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y) || !Number.isFinite(point.z)) {
|
|
660
|
+
throw new RangeError("Stock needs finite part coordinates.");
|
|
661
|
+
}
|
|
662
|
+
box.expandByPoint(point);
|
|
663
|
+
}
|
|
664
|
+
return box;
|
|
665
|
+
}
|
|
666
|
+
function createStock(geometry, legacyWorkpiece = false) {
|
|
667
|
+
const material = new MeshLambertMaterial({ transparent: true, depthWrite: false });
|
|
668
|
+
const edgeMaterial = new LineBasicMaterial({ transparent: true, depthWrite: false });
|
|
669
|
+
const mesh = new Mesh2(geometry, material);
|
|
670
|
+
const object = new Group();
|
|
671
|
+
object.userData[STOCK_OBJECT] = true;
|
|
672
|
+
mesh.renderOrder = legacyWorkpiece ? 2 : 5;
|
|
673
|
+
mesh.raycast = () => {
|
|
674
|
+
};
|
|
675
|
+
object.add(mesh);
|
|
676
|
+
if (legacyWorkpiece) {
|
|
677
|
+
const shadow = mesh.clone();
|
|
678
|
+
shadow.raycast = () => {
|
|
679
|
+
};
|
|
680
|
+
object.add(shadow);
|
|
681
|
+
edgeMaterial.depthWrite = true;
|
|
682
|
+
}
|
|
683
|
+
let edges = null;
|
|
684
|
+
return {
|
|
685
|
+
object,
|
|
686
|
+
material,
|
|
687
|
+
edgeMaterial,
|
|
688
|
+
/**
|
|
689
|
+
* Shows or hides the outline, building it the first time it is shown: the
|
|
690
|
+
* edge pass reads every triangle, and stock cut by a toolpath has a great
|
|
691
|
+
* many of them.
|
|
692
|
+
*/
|
|
693
|
+
showEdges(show) {
|
|
694
|
+
if (show && !edges) {
|
|
695
|
+
edges = new LineSegments(new EdgesGeometry(geometry, 15), edgeMaterial);
|
|
696
|
+
edges.renderOrder = legacyWorkpiece ? 1 : 6;
|
|
697
|
+
edges.raycast = () => {
|
|
698
|
+
};
|
|
699
|
+
object.add(edges);
|
|
700
|
+
}
|
|
701
|
+
if (edges) edges.visible = show;
|
|
702
|
+
},
|
|
703
|
+
dispose() {
|
|
704
|
+
material.dispose();
|
|
705
|
+
edgeMaterial.dispose();
|
|
706
|
+
edges?.geometry.dispose();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/stock.tsx
|
|
712
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
713
|
+
var Stock = ({ geometry, glb, box, cylinder, ...appearance }) => geometry ? /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }) : glb ? /* @__PURE__ */ jsx4(GlbStock, { glb, ...appearance }) : /* @__PURE__ */ jsx4(ShapeStock, { box, cylinder, ...appearance });
|
|
714
|
+
var GeometryStock = ({
|
|
715
|
+
geometry,
|
|
716
|
+
renderStyle = "overlay",
|
|
717
|
+
color = renderStyle === "legacy-workpiece" ? 16777215 : 12176354,
|
|
718
|
+
emissive = renderStyle === "legacy-workpiece" ? 3948625 : 0,
|
|
719
|
+
flatShading = renderStyle === "legacy-workpiece",
|
|
720
|
+
opacity = 0.2,
|
|
721
|
+
edgeColor = renderStyle === "legacy-workpiece" ? 0 : 11058648,
|
|
722
|
+
edgeOpacity = renderStyle === "legacy-workpiece" ? 0.5 : 0.75,
|
|
723
|
+
showEdges = true
|
|
724
|
+
}) => {
|
|
725
|
+
const invalidate = useThree((state) => state.invalidate);
|
|
726
|
+
const stock = useMemo(
|
|
727
|
+
() => createStock(geometry, renderStyle === "legacy-workpiece"),
|
|
728
|
+
[geometry, renderStyle]
|
|
729
|
+
);
|
|
730
|
+
useEffect2(() => () => stock.dispose(), [stock]);
|
|
731
|
+
useLayoutEffect(() => {
|
|
732
|
+
stock.material.color.setHex(color);
|
|
733
|
+
stock.material.emissive.setHex(emissive);
|
|
734
|
+
if (stock.material.flatShading !== flatShading) {
|
|
735
|
+
stock.material.flatShading = flatShading;
|
|
736
|
+
stock.material.needsUpdate = true;
|
|
737
|
+
}
|
|
738
|
+
stock.material.opacity = opacity;
|
|
739
|
+
stock.material.depthWrite = renderStyle === "legacy-workpiece" && opacity === 1;
|
|
740
|
+
stock.material.polygonOffset = renderStyle === "legacy-workpiece" && opacity === 1;
|
|
741
|
+
stock.material.polygonOffsetFactor = 1;
|
|
742
|
+
stock.material.polygonOffsetUnits = 1;
|
|
743
|
+
stock.edgeMaterial.color.setHex(edgeColor);
|
|
744
|
+
stock.edgeMaterial.opacity = edgeOpacity;
|
|
745
|
+
stock.showEdges(showEdges);
|
|
746
|
+
invalidate();
|
|
747
|
+
}, [
|
|
748
|
+
color,
|
|
749
|
+
emissive,
|
|
750
|
+
flatShading,
|
|
751
|
+
edgeColor,
|
|
752
|
+
edgeOpacity,
|
|
753
|
+
invalidate,
|
|
754
|
+
opacity,
|
|
755
|
+
renderStyle,
|
|
756
|
+
showEdges,
|
|
757
|
+
stock
|
|
758
|
+
]);
|
|
759
|
+
return /* @__PURE__ */ jsx4("primitive", { object: stock.object, dispose: null });
|
|
760
|
+
};
|
|
761
|
+
var GlbStock = ({
|
|
762
|
+
glb,
|
|
763
|
+
retainPrevious = false,
|
|
764
|
+
...appearance
|
|
765
|
+
}) => {
|
|
766
|
+
const buffer = useMemo(() => createStockMeshBuffer(), []);
|
|
767
|
+
const [decoded, setDecoded] = useState2(null);
|
|
768
|
+
useEffect2(() => () => buffer.dispose(), [buffer]);
|
|
769
|
+
useLayoutEffect(() => {
|
|
770
|
+
buffer.commit(decoded?.geometries ?? []);
|
|
771
|
+
}, [buffer, decoded]);
|
|
772
|
+
useEffect2(
|
|
773
|
+
() => buffer.request(
|
|
774
|
+
() => parseStockGlb(glb),
|
|
775
|
+
(geometries) => {
|
|
776
|
+
setDecoded({ source: glb, geometries });
|
|
777
|
+
},
|
|
778
|
+
(cause) => {
|
|
779
|
+
setDecoded({
|
|
780
|
+
source: glb,
|
|
781
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
),
|
|
785
|
+
[buffer, glb]
|
|
786
|
+
);
|
|
787
|
+
if (!decoded || !retainPrevious && decoded.source !== glb) return null;
|
|
788
|
+
if (decoded.error) throw decoded.error;
|
|
789
|
+
return decoded.geometries?.map((geometry) => /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance }, geometry.uuid));
|
|
790
|
+
};
|
|
791
|
+
var keepNonFinite = (_key, value) => typeof value === "number" && !Number.isFinite(value) ? String(value) : value;
|
|
792
|
+
var restoreNonFinite = (_key, value) => value === "NaN" || value === "Infinity" || value === "-Infinity" ? Number(value) : value;
|
|
793
|
+
var ShapeStock = ({
|
|
794
|
+
box,
|
|
795
|
+
cylinder,
|
|
796
|
+
...appearance
|
|
797
|
+
}) => {
|
|
798
|
+
const isBox = box !== void 0;
|
|
799
|
+
const partGeometry = box?.partGeometry ?? cylinder?.partGeometry;
|
|
800
|
+
const figure = JSON.stringify(
|
|
801
|
+
box ? box.partGeometry ? {
|
|
802
|
+
dimensions: box.dimensions,
|
|
803
|
+
allowance: box.allowance,
|
|
804
|
+
offset: box.offset,
|
|
805
|
+
position: box.position,
|
|
806
|
+
positionOffset: box.positionOffset
|
|
807
|
+
} : { frame: box.frame, lower: box.lower, upper: box.upper } : cylinder.partGeometry ? {
|
|
808
|
+
diameter: cylinder.diameter,
|
|
809
|
+
length: cylinder.length,
|
|
810
|
+
position: cylinder.position,
|
|
811
|
+
positionOffset: cylinder.positionOffset
|
|
812
|
+
} : {
|
|
813
|
+
origin: cylinder.origin,
|
|
814
|
+
axis: cylinder.axis,
|
|
815
|
+
diameter: cylinder.diameter,
|
|
816
|
+
length: cylinder.length
|
|
817
|
+
},
|
|
818
|
+
keepNonFinite
|
|
819
|
+
);
|
|
820
|
+
const geometry = useMemo(() => {
|
|
821
|
+
const options = JSON.parse(figure, restoreNonFinite);
|
|
822
|
+
const input = partGeometry ? { ...options, partGeometry } : options;
|
|
823
|
+
return isBox ? boxStockGeometry(input) : cylinderStockGeometry(input);
|
|
824
|
+
}, [figure, isBox, partGeometry]);
|
|
825
|
+
useEffect2(() => () => geometry.dispose(), [geometry]);
|
|
826
|
+
return /* @__PURE__ */ jsx4(GeometryStock, { geometry, ...appearance });
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// src/banana.tsx
|
|
830
|
+
import { useGLTF } from "@react-three/drei";
|
|
831
|
+
import { useThree as useThree2 } from "@react-three/fiber";
|
|
832
|
+
import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2, useRef } from "react";
|
|
833
|
+
|
|
834
|
+
// src/render/banana.ts
|
|
835
|
+
import { Vector3 as Vector32 } from "three";
|
|
836
|
+
var BANANA_GAP = 0.1;
|
|
837
|
+
function bananaPosition(part, banana) {
|
|
838
|
+
const size = part.getSize(new Vector32());
|
|
839
|
+
const reach = Math.max(size.x, size.y, size.z);
|
|
840
|
+
const centre = part.getCenter(new Vector32());
|
|
841
|
+
return new Vector32(
|
|
842
|
+
part.max.x + reach * BANANA_GAP - banana.min.x,
|
|
843
|
+
centre.y - (banana.min.y + banana.max.y) / 2,
|
|
844
|
+
part.min.z - banana.min.z
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
function bananaFrameBounds(part, banana, position) {
|
|
848
|
+
return part.clone().union(banana.clone().translate(position));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/banana.tsx
|
|
852
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
853
|
+
var BANANA_MODEL_URL = new URL("./assets/banana.glb", import.meta.url).href;
|
|
854
|
+
var SKIN = "#e8b530";
|
|
855
|
+
var FURNITURE = { [EXCLUDE_FROM_FRAME]: true };
|
|
856
|
+
var Banana = ({ onPlaced }) => {
|
|
857
|
+
const part = useContentBox();
|
|
858
|
+
const invalidate = useThree2((state) => state.invalidate);
|
|
859
|
+
const gltf = useGLTF(BANANA_MODEL_URL);
|
|
860
|
+
const group = useRef(null);
|
|
861
|
+
const geometry = useMemo2(() => {
|
|
862
|
+
let found = null;
|
|
863
|
+
gltf.scene.traverse((object) => {
|
|
864
|
+
const mesh = object;
|
|
865
|
+
if (found === null && mesh.isMesh) found = mesh.geometry;
|
|
866
|
+
});
|
|
867
|
+
const source = found;
|
|
868
|
+
if (!source) return null;
|
|
869
|
+
const own = source.clone();
|
|
870
|
+
own.computeVertexNormals();
|
|
871
|
+
own.rotateY(Math.PI / 2);
|
|
872
|
+
own.rotateX(Math.PI / 2);
|
|
873
|
+
own.computeBoundingBox();
|
|
874
|
+
return own;
|
|
875
|
+
}, [gltf]);
|
|
876
|
+
useLayoutEffect2(() => {
|
|
877
|
+
const node = group.current;
|
|
878
|
+
const own = geometry?.boundingBox;
|
|
879
|
+
if (!node || !own || part.isEmpty()) return;
|
|
880
|
+
const position = bananaPosition(part, own);
|
|
881
|
+
node.position.copy(position);
|
|
882
|
+
node.visible = true;
|
|
883
|
+
node.updateWorldMatrix(true, true);
|
|
884
|
+
onPlaced?.(bananaFrameBounds(part, own, position));
|
|
885
|
+
invalidate();
|
|
886
|
+
}, [geometry, invalidate, onPlaced, part]);
|
|
887
|
+
if (!geometry) return null;
|
|
888
|
+
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 }) }) });
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
// src/render/direction-highlights.ts
|
|
892
|
+
function directionHighlights(model, activeDirection = null) {
|
|
893
|
+
const features = model.features.map((feature) => ({ feature, index: directionIndexOf(model, feature.machiningDirection) })).filter(({ index }) => index >= 0 && (activeDirection === null || index === activeDirection)).sort(
|
|
894
|
+
(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)
|
|
895
|
+
);
|
|
896
|
+
const colors = /* @__PURE__ */ new Map();
|
|
897
|
+
for (const { feature, index } of features) {
|
|
898
|
+
for (const region of model.regionIndex.regionsForFeature(feature.tag)) {
|
|
899
|
+
if (!colors.has(region)) colors.set(region, { region, color: directionColor(index) });
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return [...colors.values()].sort((a, b) => a.region - b.region);
|
|
903
|
+
}
|
|
135
904
|
|
|
136
905
|
// src/primitives.tsx
|
|
137
906
|
import { GizmoHelper } from "@react-three/drei";
|
|
138
|
-
import { useThree } from "@react-three/fiber";
|
|
139
|
-
import { useEffect, useMemo, useRef, useState } from "react";
|
|
907
|
+
import { useThree as useThree3 } from "@react-three/fiber";
|
|
908
|
+
import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef2, useState as useState3 } from "react";
|
|
140
909
|
|
|
141
910
|
// src/render/grid.ts
|
|
142
|
-
import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
|
|
911
|
+
import { BufferGeometry, Float32BufferAttribute, Vector3 as Vector33 } from "three";
|
|
143
912
|
var STEPS = [0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1e3];
|
|
144
913
|
var TARGET_CELLS = 10;
|
|
145
914
|
var OVERHANG = 1.6;
|
|
146
915
|
function gridSpec(box) {
|
|
147
|
-
const size = box.getSize(new
|
|
148
|
-
const center = box.getCenter(new
|
|
916
|
+
const size = box.getSize(new Vector33());
|
|
917
|
+
const center = box.getCenter(new Vector33());
|
|
149
918
|
const largest = Math.max(size.x, size.y, 1e-6);
|
|
150
919
|
const ideal = largest / TARGET_CELLS;
|
|
151
920
|
const step = [...STEPS].reverse().find((candidate) => candidate <= ideal) ?? STEPS[0];
|
|
@@ -189,26 +958,26 @@ function gridFor(box, options = {}) {
|
|
|
189
958
|
}
|
|
190
959
|
|
|
191
960
|
// src/primitives.tsx
|
|
192
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
193
|
-
var
|
|
961
|
+
import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
962
|
+
var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
|
|
194
963
|
var Grid = ({ step, extent, color, opacity = 0.35 }) => {
|
|
195
964
|
const theme = resolveTheme();
|
|
196
965
|
const box = useContentBox();
|
|
197
|
-
const geometry =
|
|
198
|
-
|
|
966
|
+
const geometry = useMemo3(() => gridFor(box, { step, extent }), [box, extent, step]);
|
|
967
|
+
useEffect3(() => () => geometry?.dispose(), [geometry]);
|
|
199
968
|
if (!geometry) return null;
|
|
200
|
-
return /* @__PURE__ */
|
|
969
|
+
return /* @__PURE__ */ jsx6("lineSegments", { geometry, renderOrder: -1, raycast: () => null, userData: FURNITURE2, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: color ?? theme.cubeEdge, transparent: true, opacity }) });
|
|
201
970
|
};
|
|
202
|
-
var Axes = ({ size = 25 }) => /* @__PURE__ */
|
|
971
|
+
var Axes = ({ size = 25 }) => /* @__PURE__ */ jsx6("axesHelper", { args: [size], userData: FURNITURE2, raycast: () => null });
|
|
203
972
|
var ViewCube = ({
|
|
204
973
|
alignment = "top-right",
|
|
205
974
|
margin = [80, 80],
|
|
206
975
|
theme,
|
|
207
976
|
onViewChange
|
|
208
977
|
}) => {
|
|
209
|
-
const resolved =
|
|
210
|
-
const zones =
|
|
211
|
-
return /* @__PURE__ */
|
|
978
|
+
const resolved = useMemo3(() => resolveTheme(theme), [theme]);
|
|
979
|
+
const zones = useMemo3(() => cubeZones(), []);
|
|
980
|
+
return /* @__PURE__ */ jsx6(GizmoHelper, { alignment, margin, children: /* @__PURE__ */ jsx6(CubePanels, { zones, theme: resolved, onViewChange }) });
|
|
212
981
|
};
|
|
213
982
|
var CubePanels = ({
|
|
214
983
|
zones,
|
|
@@ -216,15 +985,15 @@ var CubePanels = ({
|
|
|
216
985
|
onViewChange
|
|
217
986
|
}) => {
|
|
218
987
|
const controls = useViewerControls();
|
|
219
|
-
const invalidate =
|
|
220
|
-
const groupRef =
|
|
221
|
-
const [hovered, setHovered] =
|
|
222
|
-
const panels =
|
|
988
|
+
const invalidate = useThree3((state) => state.invalidate);
|
|
989
|
+
const groupRef = useRef2(null);
|
|
990
|
+
const [hovered, setHovered] = useState3(null);
|
|
991
|
+
const panels = useMemo3(
|
|
223
992
|
() => zones.map((zone) => ({ zone, geometry: panelGeometry(zone) })),
|
|
224
993
|
[zones]
|
|
225
994
|
);
|
|
226
|
-
const outline =
|
|
227
|
-
const labels =
|
|
995
|
+
const outline = useMemo3(() => cubeOutlineGeometry(zones), [zones]);
|
|
996
|
+
const labels = useMemo3(
|
|
228
997
|
() => zones.filter((zone) => zone.kind === "face").map((zone) => ({
|
|
229
998
|
zone,
|
|
230
999
|
geometry: labelGeometry(zone),
|
|
@@ -232,7 +1001,7 @@ var CubePanels = ({
|
|
|
232
1001
|
})),
|
|
233
1002
|
[theme.cubeLabel, zones]
|
|
234
1003
|
);
|
|
235
|
-
|
|
1004
|
+
useEffect3(
|
|
236
1005
|
() => () => {
|
|
237
1006
|
for (const panel of panels) panel.geometry.dispose();
|
|
238
1007
|
for (const label of labels) {
|
|
@@ -251,8 +1020,8 @@ var CubePanels = ({
|
|
|
251
1020
|
setHovered(name);
|
|
252
1021
|
invalidate();
|
|
253
1022
|
};
|
|
254
|
-
return /* @__PURE__ */
|
|
255
|
-
panels.map(({ zone, geometry }) => /* @__PURE__ */
|
|
1023
|
+
return /* @__PURE__ */ jsxs2("group", { ref: groupRef, scale: 38, children: [
|
|
1024
|
+
panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx6(
|
|
256
1025
|
"mesh",
|
|
257
1026
|
{
|
|
258
1027
|
geometry,
|
|
@@ -265,7 +1034,7 @@ var CubePanels = ({
|
|
|
265
1034
|
event.stopPropagation();
|
|
266
1035
|
choose(zone);
|
|
267
1036
|
},
|
|
268
|
-
children: /* @__PURE__ */
|
|
1037
|
+
children: /* @__PURE__ */ jsx6(
|
|
269
1038
|
"meshLambertMaterial",
|
|
270
1039
|
{
|
|
271
1040
|
color: hovered === zone.name ? theme.hover : theme.cube,
|
|
@@ -277,19 +1046,19 @@ var CubePanels = ({
|
|
|
277
1046
|
},
|
|
278
1047
|
zone.name
|
|
279
1048
|
)),
|
|
280
|
-
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */
|
|
281
|
-
/* @__PURE__ */
|
|
282
|
-
/* @__PURE__ */
|
|
283
|
-
/* @__PURE__ */
|
|
1049
|
+
labels.map(({ zone, geometry, texture }) => /* @__PURE__ */ jsx6("mesh", { geometry, raycast: () => null, children: /* @__PURE__ */ jsx6("meshBasicMaterial", { map: texture, transparent: true }) }, `${zone.name}-label`)),
|
|
1050
|
+
/* @__PURE__ */ jsx6("lineSegments", { geometry: outline, raycast: () => null, children: /* @__PURE__ */ jsx6("lineBasicMaterial", { color: theme.cubeEdge }) }),
|
|
1051
|
+
/* @__PURE__ */ jsx6("ambientLight", { intensity: 1.4 }),
|
|
1052
|
+
/* @__PURE__ */ jsx6("directionalLight", { position: [viewVector("top-front-right").x, 1, 2], intensity: 1.1 })
|
|
284
1053
|
] });
|
|
285
1054
|
};
|
|
286
1055
|
|
|
287
1056
|
// src/direction-arrows.tsx
|
|
288
|
-
import { useThree as
|
|
289
|
-
import { useMemo as
|
|
290
|
-
import { Quaternion, Vector3 as
|
|
291
|
-
import { jsx as
|
|
292
|
-
var
|
|
1057
|
+
import { useThree as useThree4 } from "@react-three/fiber";
|
|
1058
|
+
import { useMemo as useMemo4 } from "react";
|
|
1059
|
+
import { Quaternion as Quaternion2, Vector3 as Vector34 } from "three";
|
|
1060
|
+
import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1061
|
+
var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
|
|
293
1062
|
var DirectionArrows = ({
|
|
294
1063
|
directions,
|
|
295
1064
|
activeDirection = null,
|
|
@@ -301,7 +1070,7 @@ var DirectionArrows = ({
|
|
|
301
1070
|
visible = true
|
|
302
1071
|
}) => {
|
|
303
1072
|
const box = useContentBox();
|
|
304
|
-
const resolved =
|
|
1073
|
+
const resolved = useMemo4(() => resolveTheme(theme), [theme]);
|
|
305
1074
|
const shown = shownDirection === void 0 ? activeDirection : shownDirection;
|
|
306
1075
|
const drawn = (index) => {
|
|
307
1076
|
if (shown === null) return true;
|
|
@@ -313,10 +1082,10 @@ var DirectionArrows = ({
|
|
|
313
1082
|
// Excluded from framing: the arrows are deliberately placed outside the
|
|
314
1083
|
// part, so a Fit that measured them would frame the arrows and leave the
|
|
315
1084
|
// part small in the middle of them.
|
|
316
|
-
/* @__PURE__ */
|
|
1085
|
+
/* @__PURE__ */ jsxs3("group", { userData: FURNITURE3, children: [
|
|
317
1086
|
directions.map((direction, index) => {
|
|
318
1087
|
if (!drawn(index)) return null;
|
|
319
|
-
return /* @__PURE__ */
|
|
1088
|
+
return /* @__PURE__ */ jsx7(
|
|
320
1089
|
Arrow,
|
|
321
1090
|
{
|
|
322
1091
|
direction,
|
|
@@ -328,7 +1097,7 @@ var DirectionArrows = ({
|
|
|
328
1097
|
`candidate-${index}`
|
|
329
1098
|
);
|
|
330
1099
|
}),
|
|
331
|
-
namedDirections.map((named) => /* @__PURE__ */
|
|
1100
|
+
namedDirections.map((named) => /* @__PURE__ */ jsx7(
|
|
332
1101
|
Arrow,
|
|
333
1102
|
{
|
|
334
1103
|
direction: named.direction,
|
|
@@ -339,17 +1108,17 @@ var DirectionArrows = ({
|
|
|
339
1108
|
},
|
|
340
1109
|
`named-${named.index}`
|
|
341
1110
|
)),
|
|
342
|
-
previewDirection ? /* @__PURE__ */
|
|
1111
|
+
previewDirection ? /* @__PURE__ */ jsx7(Arrow, { direction: previewDirection, box, color: resolved.hover, opacity: 0.95, onTop: true }) : null
|
|
343
1112
|
] })
|
|
344
1113
|
);
|
|
345
1114
|
};
|
|
346
1115
|
var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
347
|
-
const invalidate =
|
|
348
|
-
const { tip, length, quaternion } =
|
|
1116
|
+
const invalidate = useThree4((state) => state.invalidate);
|
|
1117
|
+
const { tip, length, quaternion } = useMemo4(() => {
|
|
349
1118
|
const placement = arrowPlacement(direction, box);
|
|
350
|
-
const aim = new
|
|
1119
|
+
const aim = new Quaternion2().setFromUnitVectors(
|
|
351
1120
|
CONE_AXIS,
|
|
352
|
-
new
|
|
1121
|
+
new Vector34(direction.x, direction.y, direction.z).normalize()
|
|
353
1122
|
);
|
|
354
1123
|
return { tip: placement.tip, length: placement.length, quaternion: aim };
|
|
355
1124
|
}, [box, direction]);
|
|
@@ -362,234 +1131,32 @@ var Arrow = ({ direction, box, color, opacity, onPick, onTop = false }) => {
|
|
|
362
1131
|
invalidate();
|
|
363
1132
|
}
|
|
364
1133
|
} : {};
|
|
365
|
-
return /* @__PURE__ */
|
|
366
|
-
/* @__PURE__ */
|
|
367
|
-
/* @__PURE__ */
|
|
368
|
-
/* @__PURE__ */
|
|
1134
|
+
return /* @__PURE__ */ jsxs3("group", { position: tip, quaternion, renderOrder: onTop ? 10 : 3, children: [
|
|
1135
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, head * 0.5, 0], rotation: [Math.PI, 0, 0], ...press, children: [
|
|
1136
|
+
/* @__PURE__ */ jsx7("coneGeometry", { args: [length * HEAD_RADIUS, head, 20] }),
|
|
1137
|
+
/* @__PURE__ */ jsx7("meshBasicMaterial", { color, transparent: true, opacity, depthTest: !onTop })
|
|
369
1138
|
] }),
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
/* @__PURE__ */
|
|
372
|
-
/* @__PURE__ */
|
|
1139
|
+
/* @__PURE__ */ jsxs3("mesh", { position: [0, head + shaft * 0.5, 0], ...press, children: [
|
|
1140
|
+
/* @__PURE__ */ jsx7("cylinderGeometry", { args: [length * SHAFT_RADIUS, length * SHAFT_RADIUS, shaft, 12] }),
|
|
1141
|
+
/* @__PURE__ */ jsx7("meshBasicMaterial", { color, transparent: true, opacity, depthTest: !onTop })
|
|
373
1142
|
] })
|
|
374
1143
|
] });
|
|
375
1144
|
};
|
|
376
1145
|
|
|
377
1146
|
// src/section-tool.tsx
|
|
378
|
-
import { useFrame, useThree as
|
|
379
|
-
import { useEffect as
|
|
1147
|
+
import { useFrame, useThree as useThree5 } from "@react-three/fiber";
|
|
1148
|
+
import { useEffect as useEffect4, useMemo as useMemo5, useRef as useRef3, useState as useState4, useSyncExternalStore } from "react";
|
|
380
1149
|
import {
|
|
381
1150
|
BufferGeometry as BufferGeometry2,
|
|
382
1151
|
DoubleSide,
|
|
383
|
-
Quaternion as
|
|
1152
|
+
Quaternion as Quaternion3,
|
|
384
1153
|
Raycaster,
|
|
385
|
-
Vector2
|
|
386
|
-
Vector3 as
|
|
1154
|
+
Vector2,
|
|
1155
|
+
Vector3 as Vector35
|
|
387
1156
|
} 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);
|
|
1157
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1158
|
+
var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1159
|
+
var Z = new Vector35(0, 0, 1);
|
|
593
1160
|
var AXIS_ROTATIONS = {
|
|
594
1161
|
x: [0, Math.PI / 2, 0],
|
|
595
1162
|
y: [Math.PI / 2, 0, 0],
|
|
@@ -601,12 +1168,12 @@ var SectionTool = ({ theme }) => {
|
|
|
601
1168
|
const store = useSectionStore();
|
|
602
1169
|
const section = useSyncExternalStore(store.subscribe, store.get, store.get);
|
|
603
1170
|
const box = useContentBox();
|
|
604
|
-
const resolved =
|
|
605
|
-
|
|
1171
|
+
const resolved = useMemo5(() => resolveTheme(theme), [theme]);
|
|
1172
|
+
useEffect4(() => {
|
|
606
1173
|
store.setEngaged(true);
|
|
607
1174
|
return () => store.setEngaged(false);
|
|
608
1175
|
}, [store]);
|
|
609
|
-
|
|
1176
|
+
useEffect4(() => {
|
|
610
1177
|
if (!section) return void 0;
|
|
611
1178
|
const clear = (event) => {
|
|
612
1179
|
if (event.key === "Escape") store.set(null);
|
|
@@ -615,43 +1182,43 @@ var SectionTool = ({ theme }) => {
|
|
|
615
1182
|
return () => window.removeEventListener("keydown", clear);
|
|
616
1183
|
}, [section, store]);
|
|
617
1184
|
if (box.isEmpty()) return null;
|
|
618
|
-
return /* @__PURE__ */
|
|
1185
|
+
return /* @__PURE__ */ jsx8("group", { userData: FURNITURE4, children: section ? /* @__PURE__ */ jsx8(CutSheet, { box, section, theme: resolved }) : /* @__PURE__ */ jsx8(Picker, { box, theme: resolved }) });
|
|
619
1186
|
};
|
|
620
1187
|
var useSquare = () => {
|
|
621
|
-
const square =
|
|
1188
|
+
const square = useMemo5(
|
|
622
1189
|
() => new BufferGeometry2().setFromPoints([
|
|
623
|
-
new
|
|
624
|
-
new
|
|
625
|
-
new
|
|
626
|
-
new
|
|
1190
|
+
new Vector35(-1, 1, 0),
|
|
1191
|
+
new Vector35(1, 1, 0),
|
|
1192
|
+
new Vector35(1, -1, 0),
|
|
1193
|
+
new Vector35(-1, -1, 0)
|
|
627
1194
|
]),
|
|
628
1195
|
[]
|
|
629
1196
|
);
|
|
630
|
-
|
|
1197
|
+
useEffect4(() => () => square.dispose(), [square]);
|
|
631
1198
|
return square;
|
|
632
1199
|
};
|
|
633
1200
|
var Picker = ({ box, theme }) => {
|
|
634
1201
|
const store = useSectionStore();
|
|
635
|
-
const camera =
|
|
636
|
-
const scene =
|
|
637
|
-
const domElement =
|
|
638
|
-
const invalidate =
|
|
1202
|
+
const camera = useThree5((state) => state.camera);
|
|
1203
|
+
const scene = useThree5((state) => state.scene);
|
|
1204
|
+
const domElement = useThree5((state) => state.gl.domElement);
|
|
1205
|
+
const invalidate = useThree5((state) => state.invalidate);
|
|
639
1206
|
const isTap = useTapGuard();
|
|
640
1207
|
const square = useSquare();
|
|
641
|
-
const previewRef =
|
|
642
|
-
const hover =
|
|
643
|
-
const [hoveredPlane, setHoveredPlane] =
|
|
644
|
-
const span =
|
|
645
|
-
const centre =
|
|
1208
|
+
const previewRef = useRef3(null);
|
|
1209
|
+
const hover = useRef3(null);
|
|
1210
|
+
const [hoveredPlane, setHoveredPlane] = useState4(null);
|
|
1211
|
+
const span = useMemo5(() => box.getSize(new Vector35()).length(), [box]);
|
|
1212
|
+
const centre = useMemo5(() => box.getCenter(new Vector35()), [box]);
|
|
646
1213
|
const previewSize = span * PREVIEW_SCALE;
|
|
647
|
-
|
|
1214
|
+
useEffect4(() => {
|
|
648
1215
|
store.setPicking(true);
|
|
649
1216
|
return () => store.setPicking(false);
|
|
650
1217
|
}, [store]);
|
|
651
|
-
|
|
1218
|
+
useEffect4(() => {
|
|
652
1219
|
const raycaster = new Raycaster();
|
|
653
|
-
const pointer = new
|
|
654
|
-
const aim = new
|
|
1220
|
+
const pointer = new Vector2();
|
|
1221
|
+
const aim = new Vector35();
|
|
655
1222
|
const hide = () => {
|
|
656
1223
|
if (!hover.current) return;
|
|
657
1224
|
hover.current = null;
|
|
@@ -678,6 +1245,7 @@ var Picker = ({ box, theme }) => {
|
|
|
678
1245
|
invalidate();
|
|
679
1246
|
};
|
|
680
1247
|
const up = (event) => {
|
|
1248
|
+
move(event);
|
|
681
1249
|
const hit = hover.current;
|
|
682
1250
|
if (event.button !== 0 || !hit || !isTap(event)) return;
|
|
683
1251
|
store.set({
|
|
@@ -695,7 +1263,7 @@ var Picker = ({ box, theme }) => {
|
|
|
695
1263
|
domElement.removeEventListener("pointerup", up);
|
|
696
1264
|
};
|
|
697
1265
|
}, [box, camera, domElement, invalidate, isTap, scene, span, store]);
|
|
698
|
-
const [planes, setPlanes] =
|
|
1266
|
+
const [planes, setPlanes] = useState4(() => axesPlanes(box, camera.position));
|
|
699
1267
|
const key = planes.map((plane) => plane.sign).join();
|
|
700
1268
|
useFrame(() => {
|
|
701
1269
|
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();
|
|
@@ -721,11 +1289,11 @@ var Picker = ({ box, theme }) => {
|
|
|
721
1289
|
store.set(plane.options);
|
|
722
1290
|
}
|
|
723
1291
|
});
|
|
724
|
-
return /* @__PURE__ */
|
|
725
|
-
/* @__PURE__ */
|
|
726
|
-
/* @__PURE__ */
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
1292
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1293
|
+
/* @__PURE__ */ jsxs4("group", { ref: previewRef, visible: false, children: [
|
|
1294
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
1295
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [previewSize, previewSize] }),
|
|
1296
|
+
/* @__PURE__ */ jsx8(
|
|
729
1297
|
"meshBasicMaterial",
|
|
730
1298
|
{
|
|
731
1299
|
color: theme.sectionOutline,
|
|
@@ -736,14 +1304,14 @@ var Picker = ({ box, theme }) => {
|
|
|
736
1304
|
}
|
|
737
1305
|
)
|
|
738
1306
|
] }),
|
|
739
|
-
/* @__PURE__ */
|
|
1307
|
+
/* @__PURE__ */ jsx8(
|
|
740
1308
|
"lineLoop",
|
|
741
1309
|
{
|
|
742
1310
|
geometry: square,
|
|
743
1311
|
scale: previewSize / 2,
|
|
744
1312
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
745
1313
|
raycast: () => null,
|
|
746
|
-
children: /* @__PURE__ */
|
|
1314
|
+
children: /* @__PURE__ */ jsx8(
|
|
747
1315
|
"lineBasicMaterial",
|
|
748
1316
|
{
|
|
749
1317
|
color: theme.sectionOutline,
|
|
@@ -756,15 +1324,15 @@ var Picker = ({ box, theme }) => {
|
|
|
756
1324
|
}
|
|
757
1325
|
)
|
|
758
1326
|
] }),
|
|
759
|
-
planes.map((plane) => /* @__PURE__ */
|
|
1327
|
+
planes.map((plane) => /* @__PURE__ */ jsxs4(
|
|
760
1328
|
"group",
|
|
761
1329
|
{
|
|
762
1330
|
position: [plane.position.x, plane.position.y, plane.position.z],
|
|
763
1331
|
rotation: AXIS_ROTATIONS[plane.axis],
|
|
764
1332
|
children: [
|
|
765
|
-
/* @__PURE__ */
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
/* @__PURE__ */
|
|
1333
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
|
|
1334
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [plane.size, plane.size] }),
|
|
1335
|
+
/* @__PURE__ */ jsx8(
|
|
768
1336
|
"meshBasicMaterial",
|
|
769
1337
|
{
|
|
770
1338
|
color: AXIS_COLORS[plane.axis],
|
|
@@ -775,14 +1343,14 @@ var Picker = ({ box, theme }) => {
|
|
|
775
1343
|
}
|
|
776
1344
|
)
|
|
777
1345
|
] }),
|
|
778
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsx8(
|
|
779
1347
|
"lineLoop",
|
|
780
1348
|
{
|
|
781
1349
|
geometry: square,
|
|
782
1350
|
scale: plane.size / 2,
|
|
783
1351
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
784
1352
|
raycast: () => null,
|
|
785
|
-
children: /* @__PURE__ */
|
|
1353
|
+
children: /* @__PURE__ */ jsx8("lineBasicMaterial", { color: AXIS_COLORS[plane.axis], transparent: true, opacity: 0.7 })
|
|
786
1354
|
}
|
|
787
1355
|
)
|
|
788
1356
|
]
|
|
@@ -793,22 +1361,22 @@ var Picker = ({ box, theme }) => {
|
|
|
793
1361
|
};
|
|
794
1362
|
var CutSheet = ({ box, section, theme }) => {
|
|
795
1363
|
const square = useSquare();
|
|
796
|
-
const cut =
|
|
797
|
-
const span =
|
|
798
|
-
const placement =
|
|
1364
|
+
const cut = useMemo5(() => resolveSectionPlane(section, box), [box, section]);
|
|
1365
|
+
const span = useMemo5(() => box.getSize(new Vector35()).length(), [box]);
|
|
1366
|
+
const placement = useMemo5(() => {
|
|
799
1367
|
if (!cut) return null;
|
|
800
1368
|
const position = sectionAnchor(box, cut.plane).addScaledVector(
|
|
801
1369
|
cut.plane.normal,
|
|
802
1370
|
-span * SHEET_SETBACK
|
|
803
1371
|
);
|
|
804
|
-
return { position, quaternion: new
|
|
1372
|
+
return { position, quaternion: new Quaternion3().setFromUnitVectors(Z, cut.plane.normal) };
|
|
805
1373
|
}, [box, cut, span]);
|
|
806
1374
|
if (!placement) return null;
|
|
807
1375
|
const size = span * OUTLINE_SCALE;
|
|
808
|
-
return /* @__PURE__ */
|
|
809
|
-
/* @__PURE__ */
|
|
810
|
-
/* @__PURE__ */
|
|
811
|
-
/* @__PURE__ */
|
|
1376
|
+
return /* @__PURE__ */ jsxs4("group", { position: placement.position, quaternion: placement.quaternion, children: [
|
|
1377
|
+
/* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
|
|
1378
|
+
/* @__PURE__ */ jsx8("planeGeometry", { args: [size, size] }),
|
|
1379
|
+
/* @__PURE__ */ jsx8(
|
|
812
1380
|
"meshBasicMaterial",
|
|
813
1381
|
{
|
|
814
1382
|
color: theme.sectionOutline,
|
|
@@ -819,14 +1387,14 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
819
1387
|
}
|
|
820
1388
|
)
|
|
821
1389
|
] }),
|
|
822
|
-
/* @__PURE__ */
|
|
1390
|
+
/* @__PURE__ */ jsx8(
|
|
823
1391
|
"lineLoop",
|
|
824
1392
|
{
|
|
825
1393
|
geometry: square,
|
|
826
1394
|
scale: size / 2,
|
|
827
1395
|
renderOrder: SECTION_RENDER_ORDER.outline,
|
|
828
1396
|
raycast: () => null,
|
|
829
|
-
children: /* @__PURE__ */
|
|
1397
|
+
children: /* @__PURE__ */ jsx8(
|
|
830
1398
|
"lineBasicMaterial",
|
|
831
1399
|
{
|
|
832
1400
|
color: theme.sectionOutline,
|
|
@@ -843,32 +1411,32 @@ var CutSheet = ({ box, section, theme }) => {
|
|
|
843
1411
|
|
|
844
1412
|
// src/measure-tool.tsx
|
|
845
1413
|
import { Html, Line } from "@react-three/drei";
|
|
846
|
-
import { useFrame as useFrame2, useThree as
|
|
1414
|
+
import { useFrame as useFrame2, useThree as useThree6 } from "@react-three/fiber";
|
|
847
1415
|
import {
|
|
848
|
-
useEffect as
|
|
849
|
-
useLayoutEffect,
|
|
850
|
-
useMemo as
|
|
851
|
-
useRef as
|
|
852
|
-
useState as
|
|
1416
|
+
useEffect as useEffect5,
|
|
1417
|
+
useLayoutEffect as useLayoutEffect3,
|
|
1418
|
+
useMemo as useMemo6,
|
|
1419
|
+
useRef as useRef4,
|
|
1420
|
+
useState as useState5,
|
|
853
1421
|
useSyncExternalStore as useSyncExternalStore2
|
|
854
1422
|
} from "react";
|
|
855
1423
|
import {
|
|
856
|
-
Matrix4,
|
|
1424
|
+
Matrix4 as Matrix42,
|
|
857
1425
|
Raycaster as Raycaster2,
|
|
858
1426
|
SphereGeometry,
|
|
859
|
-
Vector2 as
|
|
860
|
-
Vector3 as
|
|
1427
|
+
Vector2 as Vector22,
|
|
1428
|
+
Vector3 as Vector37
|
|
861
1429
|
} from "three";
|
|
862
1430
|
|
|
863
1431
|
// src/render/resample.ts
|
|
864
|
-
import { Vector3 as
|
|
1432
|
+
import { Vector3 as Vector36 } from "three";
|
|
865
1433
|
var ON_PLANE = 1e-7;
|
|
866
1434
|
var STITCH = 1e-5;
|
|
867
1435
|
var COLLINEAR = 1e-6;
|
|
868
1436
|
function clipSegments(positions, plane) {
|
|
869
1437
|
const out = [];
|
|
870
|
-
const a = new
|
|
871
|
-
const b = new
|
|
1438
|
+
const a = new Vector36();
|
|
1439
|
+
const b = new Vector36();
|
|
872
1440
|
const count = Math.floor(positions.length / 6);
|
|
873
1441
|
for (let i = 0; i < count; i += 1) {
|
|
874
1442
|
a.set(positions[i * 6], positions[i * 6 + 1], positions[i * 6 + 2]);
|
|
@@ -883,11 +1451,11 @@ function clipSegments(positions, plane) {
|
|
|
883
1451
|
return Float32Array.from(out);
|
|
884
1452
|
}
|
|
885
1453
|
function sectionContour(positions, regionOf, plane) {
|
|
886
|
-
const a = new
|
|
887
|
-
const b = new
|
|
888
|
-
const c = new
|
|
889
|
-
const p = new
|
|
890
|
-
const q = new
|
|
1454
|
+
const a = new Vector36();
|
|
1455
|
+
const b = new Vector36();
|
|
1456
|
+
const c = new Vector36();
|
|
1457
|
+
const p = new Vector36();
|
|
1458
|
+
const q = new Vector36();
|
|
891
1459
|
const byRegion = /* @__PURE__ */ new Map();
|
|
892
1460
|
const triangleCount = Math.floor(positions.length / 9);
|
|
893
1461
|
for (let t = 0; t < triangleCount; t += 1) {
|
|
@@ -945,17 +1513,17 @@ function stitch(segments, out) {
|
|
|
945
1513
|
return into.set(segments[o], segments[o + 1], segments[o + 2]);
|
|
946
1514
|
};
|
|
947
1515
|
const direction = (i, into) => {
|
|
948
|
-
const s = new
|
|
949
|
-
const e = new
|
|
1516
|
+
const s = new Vector36();
|
|
1517
|
+
const e = new Vector36();
|
|
950
1518
|
point(i, 0, s);
|
|
951
1519
|
point(i, 1, e);
|
|
952
1520
|
return into.subVectors(e, s).normalize();
|
|
953
1521
|
};
|
|
954
1522
|
const consumed = new Uint8Array(count);
|
|
955
|
-
const d = new
|
|
956
|
-
const n = new
|
|
1523
|
+
const d = new Vector36();
|
|
1524
|
+
const n = new Vector36();
|
|
957
1525
|
const next = (i, end) => {
|
|
958
|
-
const at = point(i, end, new
|
|
1526
|
+
const at = point(i, end, new Vector36());
|
|
959
1527
|
const list = ends.get(key(at.x, at.y, at.z)) ?? [];
|
|
960
1528
|
if (list.length !== 2) return null;
|
|
961
1529
|
const other = list.find((entry) => entry >> 1 !== i);
|
|
@@ -984,8 +1552,8 @@ function stitch(segments, out) {
|
|
|
984
1552
|
};
|
|
985
1553
|
const back = walk(0);
|
|
986
1554
|
const forward = walk(1);
|
|
987
|
-
const s = point(back.index, back.end, new
|
|
988
|
-
const e = point(forward.index, forward.end, new
|
|
1555
|
+
const s = point(back.index, back.end, new Vector36());
|
|
1556
|
+
const e = point(forward.index, forward.end, new Vector36());
|
|
989
1557
|
out.push(s.x, s.y, s.z, e.x, e.y, e.z);
|
|
990
1558
|
}
|
|
991
1559
|
}
|
|
@@ -993,8 +1561,8 @@ function pointInContour(point, plane, contour) {
|
|
|
993
1561
|
const [u, v] = planeAxes(plane);
|
|
994
1562
|
const px = point.dot(u);
|
|
995
1563
|
const py = point.dot(v);
|
|
996
|
-
const a = new
|
|
997
|
-
const b = new
|
|
1564
|
+
const a = new Vector36();
|
|
1565
|
+
const b = new Vector36();
|
|
998
1566
|
let inside = false;
|
|
999
1567
|
const count = Math.floor(contour.length / 6);
|
|
1000
1568
|
for (let i = 0; i < count; i += 1) {
|
|
@@ -1011,15 +1579,15 @@ function pointInContour(point, plane, contour) {
|
|
|
1011
1579
|
return inside;
|
|
1012
1580
|
}
|
|
1013
1581
|
function capHit(ray, plane, contour) {
|
|
1014
|
-
const point = ray.intersectPlane(plane, new
|
|
1582
|
+
const point = ray.intersectPlane(plane, new Vector36());
|
|
1015
1583
|
if (!point) return null;
|
|
1016
1584
|
return pointInContour(point, plane, contour) ? point : null;
|
|
1017
1585
|
}
|
|
1018
1586
|
function planeAxes(plane) {
|
|
1019
1587
|
const n = plane.normal;
|
|
1020
|
-
const seed = Math.abs(n.x) < 0.9 ? new
|
|
1021
|
-
const u = new
|
|
1022
|
-
const v = new
|
|
1588
|
+
const seed = Math.abs(n.x) < 0.9 ? new Vector36(1, 0, 0) : new Vector36(0, 1, 0);
|
|
1589
|
+
const u = new Vector36().crossVectors(n, seed).normalize();
|
|
1590
|
+
const v = new Vector36().crossVectors(n, u).normalize();
|
|
1023
1591
|
return [u, v];
|
|
1024
1592
|
}
|
|
1025
1593
|
function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
@@ -1032,8 +1600,8 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
|
|
|
1032
1600
|
}
|
|
1033
1601
|
|
|
1034
1602
|
// src/measure-tool.tsx
|
|
1035
|
-
import { Fragment as Fragment2, jsx as
|
|
1036
|
-
var
|
|
1603
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1604
|
+
var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
|
|
1037
1605
|
var MEASURE_LABEL_CLASS = "toolpath-measure-label";
|
|
1038
1606
|
var MeasureTool = ({
|
|
1039
1607
|
mode = "distance",
|
|
@@ -1046,26 +1614,26 @@ var MeasureTool = ({
|
|
|
1046
1614
|
}) => {
|
|
1047
1615
|
const store = useSectionStore();
|
|
1048
1616
|
const box = useContentBox();
|
|
1049
|
-
const resolved =
|
|
1617
|
+
const resolved = useMemo6(() => resolveTheme(theme), [theme]);
|
|
1050
1618
|
const picking = useSyncExternalStore2(store.subscribe, store.isPicking, store.isPicking);
|
|
1051
|
-
const [held, setHeld] =
|
|
1619
|
+
const [held, setHeld] = useState5([]);
|
|
1052
1620
|
const controlled = measurements !== void 0;
|
|
1053
1621
|
const list = controlled ? measurements : held;
|
|
1054
|
-
const [draft, setDraft] =
|
|
1055
|
-
|
|
1622
|
+
const [draft, setDraft] = useState5([]);
|
|
1623
|
+
useEffect5(() => {
|
|
1056
1624
|
store.setEngaged(true);
|
|
1057
1625
|
return () => store.setEngaged(false);
|
|
1058
1626
|
}, [store]);
|
|
1059
|
-
|
|
1627
|
+
useEffect5(() => {
|
|
1060
1628
|
setDraft([]);
|
|
1061
1629
|
}, [mode]);
|
|
1062
1630
|
const commit = (next) => {
|
|
1063
1631
|
if (!controlled) setHeld(next);
|
|
1064
1632
|
onChange?.(next);
|
|
1065
1633
|
};
|
|
1066
|
-
const latest =
|
|
1634
|
+
const latest = useRef4({ list, commit });
|
|
1067
1635
|
latest.current = { list, commit };
|
|
1068
|
-
|
|
1636
|
+
useEffect5(() => {
|
|
1069
1637
|
const onKey = (event) => {
|
|
1070
1638
|
if (event.key === "Escape") {
|
|
1071
1639
|
setDraft([]);
|
|
@@ -1093,8 +1661,8 @@ var MeasureTool = ({
|
|
|
1093
1661
|
};
|
|
1094
1662
|
if (box.isEmpty()) return null;
|
|
1095
1663
|
const label = className(labelClassName);
|
|
1096
|
-
return /* @__PURE__ */
|
|
1097
|
-
picking ? null : /* @__PURE__ */
|
|
1664
|
+
return /* @__PURE__ */ jsxs5("group", { userData: FURNITURE5, children: [
|
|
1665
|
+
picking ? null : /* @__PURE__ */ jsx9(
|
|
1098
1666
|
Snapper,
|
|
1099
1667
|
{
|
|
1100
1668
|
mode,
|
|
@@ -1105,7 +1673,7 @@ var MeasureTool = ({
|
|
|
1105
1673
|
label
|
|
1106
1674
|
}
|
|
1107
1675
|
),
|
|
1108
|
-
draft.map((point, index) => /* @__PURE__ */
|
|
1676
|
+
draft.map((point, index) => /* @__PURE__ */ jsx9(
|
|
1109
1677
|
ScreenDot,
|
|
1110
1678
|
{
|
|
1111
1679
|
point,
|
|
@@ -1114,8 +1682,8 @@ var MeasureTool = ({
|
|
|
1114
1682
|
},
|
|
1115
1683
|
index
|
|
1116
1684
|
)),
|
|
1117
|
-
draft.length === 2 ? /* @__PURE__ */
|
|
1118
|
-
list.map((measurement) => /* @__PURE__ */
|
|
1685
|
+
draft.length === 2 ? /* @__PURE__ */ jsx9(Segment, { points: [draft[0], draft[1]], color: resolved.measure }) : null,
|
|
1686
|
+
list.map((measurement) => /* @__PURE__ */ jsx9(
|
|
1119
1687
|
MeasurementView,
|
|
1120
1688
|
{
|
|
1121
1689
|
measurement,
|
|
@@ -1129,30 +1697,30 @@ var MeasureTool = ({
|
|
|
1129
1697
|
] });
|
|
1130
1698
|
};
|
|
1131
1699
|
var NO_POINTER = { pointerEvents: "none" };
|
|
1132
|
-
var NOWHERE = [new
|
|
1700
|
+
var NOWHERE = [new Vector37(), new Vector37()];
|
|
1133
1701
|
var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
1134
|
-
const camera =
|
|
1135
|
-
const scene =
|
|
1136
|
-
const size =
|
|
1137
|
-
const domElement =
|
|
1138
|
-
const invalidate =
|
|
1702
|
+
const camera = useThree6((state) => state.camera);
|
|
1703
|
+
const scene = useThree6((state) => state.scene);
|
|
1704
|
+
const size = useThree6((state) => state.size);
|
|
1705
|
+
const domElement = useThree6((state) => state.gl.domElement);
|
|
1706
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
1139
1707
|
const isTap = useTapGuard();
|
|
1140
|
-
const marker =
|
|
1141
|
-
const edgeLine =
|
|
1142
|
-
const band =
|
|
1143
|
-
const bandLabel =
|
|
1144
|
-
const bandText =
|
|
1145
|
-
const snap =
|
|
1708
|
+
const marker = useRef4(null);
|
|
1709
|
+
const edgeLine = useRef4(null);
|
|
1710
|
+
const band = useRef4(null);
|
|
1711
|
+
const bandLabel = useRef4(null);
|
|
1712
|
+
const bandText = useRef4(null);
|
|
1713
|
+
const snap = useRef4(null);
|
|
1146
1714
|
const dot = useDot();
|
|
1147
|
-
|
|
1715
|
+
useLayoutEffect3(() => {
|
|
1148
1716
|
if (edgeLine.current) edgeLine.current.visible = false;
|
|
1149
1717
|
if (band.current) band.current.visible = false;
|
|
1150
1718
|
}, []);
|
|
1151
|
-
const current =
|
|
1719
|
+
const current = useRef4({ mode, draft, onPlace, format, size });
|
|
1152
1720
|
current.current = { mode, draft, onPlace, format, size };
|
|
1153
|
-
|
|
1721
|
+
useEffect5(() => {
|
|
1154
1722
|
const raycaster = new Raycaster2();
|
|
1155
|
-
const pointer = new
|
|
1723
|
+
const pointer = new Vector22();
|
|
1156
1724
|
const samples = new SampleCache();
|
|
1157
1725
|
const hide = () => {
|
|
1158
1726
|
if (!snap.current) return;
|
|
@@ -1182,7 +1750,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1182
1750
|
const snapped = snapAt(target, target.edges, camera, viewport);
|
|
1183
1751
|
const from = placed[placed.length - 1];
|
|
1184
1752
|
const lock = shift && from ? lockToAxis(from, snapped.point) : null;
|
|
1185
|
-
const found = lock ? { ...snapped, point: new
|
|
1753
|
+
const found = lock ? { ...snapped, point: new Vector37(lock.point.x, lock.point.y, lock.point.z), edge: null } : snapped;
|
|
1186
1754
|
snap.current = found;
|
|
1187
1755
|
const node = marker.current;
|
|
1188
1756
|
if (node) {
|
|
@@ -1256,7 +1824,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1256
1824
|
window.removeEventListener("keyup", key);
|
|
1257
1825
|
};
|
|
1258
1826
|
}, [camera, domElement, invalidate, isTap, scene, theme.measure]);
|
|
1259
|
-
|
|
1827
|
+
useEffect5(() => {
|
|
1260
1828
|
if (draft.length === 0 && band.current) {
|
|
1261
1829
|
band.current.visible = false;
|
|
1262
1830
|
if (bandLabel.current) bandLabel.current.visible = false;
|
|
@@ -1267,9 +1835,9 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1267
1835
|
invalidate();
|
|
1268
1836
|
}
|
|
1269
1837
|
}, [draft, invalidate]);
|
|
1270
|
-
return /* @__PURE__ */
|
|
1271
|
-
/* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1838
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1839
|
+
/* @__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 }) }) }),
|
|
1840
|
+
/* @__PURE__ */ jsx9(
|
|
1273
1841
|
Line,
|
|
1274
1842
|
{
|
|
1275
1843
|
ref: edgeLine,
|
|
@@ -1283,7 +1851,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1283
1851
|
raycast: () => null
|
|
1284
1852
|
}
|
|
1285
1853
|
),
|
|
1286
|
-
/* @__PURE__ */
|
|
1854
|
+
/* @__PURE__ */ jsx9(
|
|
1287
1855
|
Line,
|
|
1288
1856
|
{
|
|
1289
1857
|
ref: band,
|
|
@@ -1297,7 +1865,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
|
|
|
1297
1865
|
raycast: () => null
|
|
1298
1866
|
}
|
|
1299
1867
|
),
|
|
1300
|
-
/* @__PURE__ */
|
|
1868
|
+
/* @__PURE__ */ jsx9("group", { ref: bandLabel, visible: false, children: /* @__PURE__ */ jsx9(Html, { center: true, zIndexRange: [0, 0], style: NO_POINTER, children: /* @__PURE__ */ jsx9(
|
|
1301
1869
|
"div",
|
|
1302
1870
|
{
|
|
1303
1871
|
ref: bandText,
|
|
@@ -1344,7 +1912,7 @@ function targetUnderRay(raycaster, scene, samples) {
|
|
|
1344
1912
|
edges: samples.edgesFor(hit.object)
|
|
1345
1913
|
};
|
|
1346
1914
|
}
|
|
1347
|
-
const local = new
|
|
1915
|
+
const local = new Matrix42();
|
|
1348
1916
|
for (const mesh of cutMeshes(scene)) {
|
|
1349
1917
|
const planes = clippingPlanesOf(mesh);
|
|
1350
1918
|
const sample = samples.for(mesh, planes);
|
|
@@ -1407,7 +1975,7 @@ var SampleCache = class {
|
|
|
1407
1975
|
const key = planes.map((plane) => `${plane.normal.x},${plane.normal.y},${plane.normal.z},${plane.constant}`).join("|");
|
|
1408
1976
|
const cached = this.held.get(mesh);
|
|
1409
1977
|
if (cached && cached.key === key) return cached;
|
|
1410
|
-
const local = new
|
|
1978
|
+
const local = new Matrix42().copy(mesh.matrixWorld).invert();
|
|
1411
1979
|
const localPlanes = planes.map((plane) => plane.clone().applyMatrix4(local));
|
|
1412
1980
|
const positions = mesh.geometry.getAttribute("position")?.array ?? [];
|
|
1413
1981
|
const regionOf = this.regionsOf(mesh.geometry);
|
|
@@ -1462,35 +2030,35 @@ var MeasurementView = ({
|
|
|
1462
2030
|
const anchor = measurementLabelAnchor(measurement);
|
|
1463
2031
|
if (measurement.kind === "distance") {
|
|
1464
2032
|
const [a2, b2] = measurement.points;
|
|
1465
|
-
return /* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
/* @__PURE__ */
|
|
1468
|
-
/* @__PURE__ */
|
|
1469
|
-
showDeltas ? /* @__PURE__ */
|
|
1470
|
-
/* @__PURE__ */
|
|
2033
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2034
|
+
/* @__PURE__ */ jsx9(Segment, { points: [a2, b2], color: theme.measure }),
|
|
2035
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
2036
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
2037
|
+
showDeltas ? /* @__PURE__ */ jsx9(DeltaLegs, { a: a2, b: b2, id: measurement.id, format, label }) : null,
|
|
2038
|
+
/* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "distance", id: measurement.id, className: label })
|
|
1471
2039
|
] });
|
|
1472
2040
|
}
|
|
1473
2041
|
const [a, vertex, b] = measurement.points;
|
|
1474
2042
|
const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
|
|
1475
|
-
return /* @__PURE__ */
|
|
1476
|
-
/* @__PURE__ */
|
|
1477
|
-
/* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
1479
|
-
/* @__PURE__ */
|
|
1480
|
-
/* @__PURE__ */
|
|
1481
|
-
/* @__PURE__ */
|
|
1482
|
-
/* @__PURE__ */
|
|
2043
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2044
|
+
/* @__PURE__ */ jsx9(Segment, { points: [vertex, a], color: theme.measure }),
|
|
2045
|
+
/* @__PURE__ */ jsx9(Segment, { points: [vertex, b], color: theme.measure }),
|
|
2046
|
+
/* @__PURE__ */ jsx9(Polyline, { points: arc, color: theme.measure }),
|
|
2047
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: a, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
2048
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: vertex, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
2049
|
+
/* @__PURE__ */ jsx9(ScreenDot, { point: b, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
|
|
2050
|
+
/* @__PURE__ */ jsx9(Label, { at: anchor, text, kind: "angle", id: measurement.id, className: label })
|
|
1483
2051
|
] });
|
|
1484
2052
|
};
|
|
1485
2053
|
var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
1486
|
-
const camera =
|
|
1487
|
-
const size =
|
|
1488
|
-
const invalidate =
|
|
1489
|
-
const legs =
|
|
1490
|
-
const lines =
|
|
1491
|
-
const centre =
|
|
2054
|
+
const camera = useThree6((state) => state.camera);
|
|
2055
|
+
const size = useThree6((state) => state.size);
|
|
2056
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
2057
|
+
const legs = useMemo6(() => deltaLegs(a, b), [a, b]);
|
|
2058
|
+
const lines = useRef4([]);
|
|
2059
|
+
const centre = useMemo6(() => {
|
|
1492
2060
|
const at = midpoint(a, b);
|
|
1493
|
-
return new
|
|
2061
|
+
return new Vector37(at.x, at.y, at.z);
|
|
1494
2062
|
}, [a, b]);
|
|
1495
2063
|
useFrame2(() => {
|
|
1496
2064
|
const dash = screenLength(camera, centre, size, DELTA_DASH_PIXELS);
|
|
@@ -1504,16 +2072,16 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1504
2072
|
}
|
|
1505
2073
|
if (changed) invalidate();
|
|
1506
2074
|
});
|
|
1507
|
-
return /* @__PURE__ */
|
|
1508
|
-
legs.map((leg, index) => /* @__PURE__ */
|
|
2075
|
+
return /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
2076
|
+
legs.map((leg, index) => /* @__PURE__ */ jsx9(
|
|
1509
2077
|
Line,
|
|
1510
2078
|
{
|
|
1511
2079
|
ref: (line) => {
|
|
1512
2080
|
lines.current[index] = line;
|
|
1513
2081
|
},
|
|
1514
2082
|
points: [
|
|
1515
|
-
new
|
|
1516
|
-
new
|
|
2083
|
+
new Vector37(leg.from.x, leg.from.y, leg.from.z),
|
|
2084
|
+
new Vector37(leg.to.x, leg.to.y, leg.to.z)
|
|
1517
2085
|
],
|
|
1518
2086
|
color: AXIS_COLORS[leg.axis],
|
|
1519
2087
|
lineWidth: MEASURE_LINE_PIXELS.leg,
|
|
@@ -1528,7 +2096,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1528
2096
|
},
|
|
1529
2097
|
leg.axis
|
|
1530
2098
|
)),
|
|
1531
|
-
legs.map((leg) => /* @__PURE__ */
|
|
2099
|
+
legs.map((leg) => /* @__PURE__ */ jsx9(
|
|
1532
2100
|
Label,
|
|
1533
2101
|
{
|
|
1534
2102
|
at: midpoint(leg.from, leg.to),
|
|
@@ -1542,14 +2110,14 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
|
|
|
1542
2110
|
))
|
|
1543
2111
|
] });
|
|
1544
2112
|
};
|
|
1545
|
-
var Label = ({ at, text, kind, id, axis, className: name }) => /* @__PURE__ */
|
|
2113
|
+
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 }) }) });
|
|
1546
2114
|
var ScreenDot = ({ point, pixels, color }) => {
|
|
1547
|
-
const camera =
|
|
1548
|
-
const size =
|
|
1549
|
-
const invalidate =
|
|
1550
|
-
const node =
|
|
2115
|
+
const camera = useThree6((state) => state.camera);
|
|
2116
|
+
const size = useThree6((state) => state.size);
|
|
2117
|
+
const invalidate = useThree6((state) => state.invalidate);
|
|
2118
|
+
const node = useRef4(null);
|
|
1551
2119
|
const dot = useDot();
|
|
1552
|
-
const position =
|
|
2120
|
+
const position = useMemo6(() => new Vector37(point.x, point.y, point.z), [point]);
|
|
1553
2121
|
useFrame2(() => {
|
|
1554
2122
|
const group = node.current;
|
|
1555
2123
|
if (!group) return;
|
|
@@ -1559,21 +2127,21 @@ var ScreenDot = ({ point, pixels, color }) => {
|
|
|
1559
2127
|
invalidate();
|
|
1560
2128
|
}
|
|
1561
2129
|
});
|
|
1562
|
-
return /* @__PURE__ */
|
|
2130
|
+
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 }) }) });
|
|
1563
2131
|
};
|
|
1564
2132
|
var Segment = ({ points, color }) => {
|
|
1565
2133
|
const [a, b] = points;
|
|
1566
|
-
const ends =
|
|
1567
|
-
() => [new
|
|
2134
|
+
const ends = useMemo6(
|
|
2135
|
+
() => [new Vector37(a.x, a.y, a.z), new Vector37(b.x, b.y, b.z)],
|
|
1568
2136
|
// Keyed on the numbers: a draft's tuple is a fresh array on every render,
|
|
1569
2137
|
// and a rebuilt array would rebuild the line's geometry with it.
|
|
1570
2138
|
[a.x, a.y, a.z, b.x, b.y, b.z]
|
|
1571
2139
|
);
|
|
1572
|
-
return /* @__PURE__ */
|
|
2140
|
+
return /* @__PURE__ */ jsx9(Polyline, { points: ends, color });
|
|
1573
2141
|
};
|
|
1574
2142
|
var Polyline = ({ points, color }) => {
|
|
1575
2143
|
if (points.length < 2) return null;
|
|
1576
|
-
return /* @__PURE__ */
|
|
2144
|
+
return /* @__PURE__ */ jsx9(
|
|
1577
2145
|
Line,
|
|
1578
2146
|
{
|
|
1579
2147
|
points,
|
|
@@ -1588,8 +2156,8 @@ var Polyline = ({ points, color }) => {
|
|
|
1588
2156
|
);
|
|
1589
2157
|
};
|
|
1590
2158
|
function useDot() {
|
|
1591
|
-
const geometry =
|
|
1592
|
-
|
|
2159
|
+
const geometry = useMemo6(() => new SphereGeometry(1, 16, 12), []);
|
|
2160
|
+
useEffect5(() => () => geometry.dispose(), [geometry]);
|
|
1593
2161
|
return geometry;
|
|
1594
2162
|
}
|
|
1595
2163
|
function isEditing(target) {
|
|
@@ -1640,10 +2208,14 @@ export {
|
|
|
1640
2208
|
AXES_PLANE_SCALE,
|
|
1641
2209
|
AXIS_COLORS,
|
|
1642
2210
|
Axes,
|
|
2211
|
+
BANANA_GAP,
|
|
2212
|
+
BANANA_MODEL_URL,
|
|
2213
|
+
Banana,
|
|
1643
2214
|
CAD_CAMERA_UP,
|
|
1644
2215
|
CANDIDATE_WEIGHT,
|
|
1645
2216
|
CAP_HATCH,
|
|
1646
2217
|
CHAMFER,
|
|
2218
|
+
CONTROL_SCHEME_OPTIONS,
|
|
1647
2219
|
CadCameraControls,
|
|
1648
2220
|
DEFAULT_FIT_MARGIN,
|
|
1649
2221
|
DEFAULT_SECTION_NORMAL,
|
|
@@ -1662,6 +2234,7 @@ export {
|
|
|
1662
2234
|
HIGHLIGHT_COLORS,
|
|
1663
2235
|
HIGHLIGHT_WEIGHT,
|
|
1664
2236
|
HOVER_WEIGHT,
|
|
2237
|
+
HoverCard,
|
|
1665
2238
|
MAX_FRAME_RATIO,
|
|
1666
2239
|
MEASURE_LABEL_CLASS,
|
|
1667
2240
|
MEASURE_LINE_PIXELS,
|
|
@@ -1686,17 +2259,21 @@ export {
|
|
|
1686
2259
|
PartMesh,
|
|
1687
2260
|
PartReportFormatError,
|
|
1688
2261
|
REGION_ATTRIBUTE,
|
|
2262
|
+
SECTION_GIZMO_FRAME_SCALE,
|
|
1689
2263
|
SECTION_RENDER_ORDER,
|
|
1690
2264
|
SNAP_MARKER_PIXELS,
|
|
1691
2265
|
SNAP_PIXELS,
|
|
1692
2266
|
SectionTool,
|
|
1693
2267
|
SectionView,
|
|
2268
|
+
Stock,
|
|
1694
2269
|
TAP_SLOP,
|
|
1695
2270
|
UnsupportedKernelVersionError,
|
|
1696
2271
|
VIEW_NAMES,
|
|
1697
2272
|
VIEW_SIGNS,
|
|
1698
2273
|
ViewCube,
|
|
1699
2274
|
Viewer,
|
|
2275
|
+
ViewerToolbar,
|
|
2276
|
+
ViewerToolbarProvider,
|
|
1700
2277
|
adaptedUp,
|
|
1701
2278
|
angleArc,
|
|
1702
2279
|
angleArcRadius,
|
|
@@ -1707,8 +2284,11 @@ export {
|
|
|
1707
2284
|
arrowPlacement,
|
|
1708
2285
|
aspectRatio,
|
|
1709
2286
|
axesPlanes,
|
|
2287
|
+
bananaFrameBounds,
|
|
2288
|
+
bananaPosition,
|
|
1710
2289
|
bestOwner,
|
|
1711
2290
|
boundsFromBox,
|
|
2291
|
+
boxStockBounds,
|
|
1712
2292
|
buildPick,
|
|
1713
2293
|
buildRegionAttribute,
|
|
1714
2294
|
buildRegionIndex,
|
|
@@ -1732,6 +2312,7 @@ export {
|
|
|
1732
2312
|
deltaBetween,
|
|
1733
2313
|
deltaLegs,
|
|
1734
2314
|
directionColor,
|
|
2315
|
+
directionHighlights,
|
|
1735
2316
|
directionIndexOf,
|
|
1736
2317
|
directionLabel,
|
|
1737
2318
|
distanceBetween,
|
|
@@ -1739,6 +2320,7 @@ export {
|
|
|
1739
2320
|
excludedFromFrame,
|
|
1740
2321
|
featureTypeRank,
|
|
1741
2322
|
fitDistance,
|
|
2323
|
+
fixedBoxStockBounds,
|
|
1742
2324
|
focusForPick,
|
|
1743
2325
|
formatDegrees,
|
|
1744
2326
|
formatMillimetres,
|
|
@@ -1781,6 +2363,7 @@ export {
|
|
|
1781
2363
|
sectionDepthConstant,
|
|
1782
2364
|
sectionDepthRange,
|
|
1783
2365
|
sectionFromPick,
|
|
2366
|
+
sectionMeasurement,
|
|
1784
2367
|
sectionOffset,
|
|
1785
2368
|
sectionOptionsFromState,
|
|
1786
2369
|
sectionPlane,
|
|
@@ -1798,6 +2381,7 @@ export {
|
|
|
1798
2381
|
useSectionStore,
|
|
1799
2382
|
useTapGuard,
|
|
1800
2383
|
useViewerControls,
|
|
2384
|
+
useViewerToolbar,
|
|
1801
2385
|
viewDirection,
|
|
1802
2386
|
viewKind,
|
|
1803
2387
|
viewUp,
|