@toolpath/viewer 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { P as PartModel, V as Vec3, d as ViewerTheme, e as PartModelRegion, T as TriangleRange, F as FeatureTag, R as RegionIndex, f as PartModelFeature, g as FeatureType } from './normalize-OULo7e0F.js';
2
- export { B as BuildPickInput, C as CANDIDATE_WEIGHT, D as DEFAULT_THEME, h as DIRECTION_COLORS, E as EnginePart, i as FeatureHighlight, H as HANDLE_PIXELS, j as HIGHLIGHT_COLORS, k as HIGHLIGHT_WEIGHT, l as HOVER_WEIGHT, m as HighlightLayers, K as KnownFeatureType, o as KnownShapeKind, N as NO_MODIFIERS, p as PICKED_SURFACE_LABEL, q as PartMesh, r as PartMeshProps, a as PartMeshRefs, t as PartObject, u as PartPick, v as PickModifiers, w as REGION_ATTRIBUTE, x as RegionHighlight, y as RegionPaint, S as SECTION_RENDER_ORDER, z as SectionAnchor, A as SectionBounds, G as SectionOptions, I as SectionPlacement, J as SectionState, L as SectionView, O as ShapeKind, Q as applyHighlightLayers, U as buildPick, W as buildRegionAttribute, X as buildRegionTexels, Y as createPart, Z as directionColor, _ as dragPlane, $ as focusForPick, n as normalizePartReport, a0 as pickedStartDepth, a1 as resolveSectionPlane, a2 as resolveTheme, a3 as sectionBounds, a4 as sectionConstant, a5 as sectionDepth, a6 as sectionDepthConstant, a7 as sectionDepthRange, a8 as sectionFromPick, a9 as sectionOffset, aa as sectionPlane, s as smoothRegionNormals, ab as themesEqual, ac as viewDirection } from './normalize-OULo7e0F.js';
3
- import { BufferGeometry, CanvasTexture, Vector3, Box3, OrthographicCamera, PerspectiveCamera, Object3D } from 'three';
1
+ import { P as PartModel, V as Vec3, d as ViewerTheme, S as SectionOptions, e as SectionState, f as PartModelRegion, T as TriangleRange, F as FeatureTag, R as RegionIndex, g as PartModelFeature, h as FeatureType } from './normalize-DHOSNiuO.js';
2
+ export { A as AXES_PLANE_OFFSET, i as AXES_PLANE_SCALE, j as AxesPlane, B as BuildPickInput, C as CANDIDATE_WEIGHT, k as CAP_HATCH, D as DEFAULT_SECTION_NORMAL, l as DEFAULT_THEME, m as DIRECTION_COLORS, o as DISABLED_SECTION, E as EnginePart, p as FeatureHighlight, H as HANDLE_PIXELS, q as HIGHLIGHT_COLORS, r as HIGHLIGHT_WEIGHT, t as HOVER_WEIGHT, u as HighlightLayers, K as KnownFeatureType, v as KnownShapeKind, N as NO_MODIFIERS, O as OUTLINE_SCALE, w as PICKED_SURFACE_LABEL, x as PREVIEW_SCALE, y as PartMesh, z as PartMeshProps, a as PartMeshRefs, G as PartObject, I as PartPick, J as PickModifiers, L as REGION_ATTRIBUTE, Q as RegionHighlight, U as RegionPaint, W as SECTION_RENDER_ORDER, X as SectionAnchor, Y as SectionBounds, Z as SectionPlacement, _ as ShapeKind, $ as SurfaceHit, a0 as applyHighlightLayers, a1 as axesPlanes, a2 as buildPick, a3 as buildRegionAttribute, a4 as buildRegionTexels, a5 as createPart, a6 as directionColor, a7 as dragPlane, a8 as focusForPick, a9 as hitUnderRay, n as normalizePartReport, aa as pickedStartDepth, ab as resolveTheme, ac as sectionAnchor, ad as sectionBounds, ae as sectionConstant, af as sectionDepth, ag as sectionDepthConstant, ah as sectionDepthRange, ai as sectionFromPick, aj as sectionOffset, ak as sectionOptionsFromState, al as sectionPlane, s as smoothRegionNormals, am as surfaceUnderRay, an as themesEqual, ao as viewDirection } from './normalize-DHOSNiuO.js';
3
+ import { BufferGeometry, CanvasTexture, Box3, Plane, Vector3, OrthographicCamera, PerspectiveCamera, Object3D, Matrix4, Vector2, ShaderMaterial, Texture, Color, MeshBasicMaterial, WebGLRenderTarget } from 'three';
4
4
  import * as react from 'react';
5
5
  import { RefObject, ReactNode, CSSProperties } from 'react';
6
6
  import CameraControls from 'camera-controls';
@@ -114,7 +114,7 @@ interface GridProps {
114
114
  * fixed cells is either invisible on a 900 mm plate or a solid wash under a
115
115
  * 12 mm insert.
116
116
  */
117
- declare const Grid: ({ step, extent, color, opacity }: GridProps) => react.JSX.Element;
117
+ declare const Grid: ({ step, extent, color, opacity }: GridProps) => react.JSX.Element | null;
118
118
  interface AxesProps {
119
119
  size?: number;
120
120
  }
@@ -192,199 +192,62 @@ interface DirectionArrowsProps {
192
192
  */
193
193
  declare const DirectionArrows: ({ directions, activeDirection, shownDirection, previewDirection, namedDirections, onPickDirection, theme, visible, }: DirectionArrowsProps) => react.JSX.Element | null;
194
194
 
195
- interface ArrowPlacement {
196
- /** Where the arrow's tip sits: outside the box, on the direction's ray. */
197
- readonly tip: Vector3;
198
- readonly length: number;
195
+ /** The cut's plane for a set of options, or `null` when there is no cut. */
196
+ declare function resolveSectionPlane(options: SectionOptions | undefined, box: Box3): {
197
+ plane: Plane;
198
+ state: SectionState;
199
+ } | null;
200
+ interface SectionViewProps {
201
+ geometry: BufferGeometry;
202
+ box: Box3;
203
+ plane: Plane;
204
+ theme: ViewerTheme;
205
+ showHandle: boolean;
206
+ onDrag?: (constant: number) => void;
199
207
  }
200
208
  /**
201
- * Where an arrow for `direction` sits relative to the part.
202
- *
203
- * The exit distance is the smallest `halfExtent / |component|` over the axes the
204
- * direction actually moves along — the first face of the box the ray leaves
205
- * through. That formulation is what makes this work for **arbitrary unit
206
- * vectors** rather than only for axes: real reports carry tilted directions,
207
- * one of them a 36° five-axis setup, and an axis-aligned simplification would
208
- * bury those arrows inside the part.
209
+ * A clipping plane with a hatched cap over the cut, and an arrow that drags it.
210
+ *
211
+ * The cap is the standard two-pass stencil trick: draw the clipped geometry's
212
+ * back faces incrementing the stencil and its front faces decrementing it, so a
213
+ * non-zero stencil marks exactly where the plane passes through solid material,
214
+ * then fill that region with a quad. Without it a section shows the inside of
215
+ * the far wall and the part reads as hollow.
216
+ *
217
+ * The fill is hatched and outlined — see `render/section-cap.ts` — which takes
218
+ * a pre-pass: the same stencil trick is run once more into a render target on
219
+ * its own, so the cap's shader can find its edge by sampling that mask. That
220
+ * pass draws a scene of its own holding nothing but the stencil meshes and the
221
+ * mask quad, at a priority ahead of R3F's own render.
222
+ *
223
+ * **The renderer must be created with `stencil: true`.** three defaults it to
224
+ * false, and without it every one of those stencil operations is a silent
225
+ * no-op — the cut still happens and the part still looks hollow, which is a
226
+ * confusing way to find out.
209
227
  */
210
- declare function arrowPlacement(direction: Vec3, box: Box3): ArrowPlacement;
228
+ declare const SectionView: ({ geometry, box, plane, theme, showHandle, onDrag, }: SectionViewProps) => react.JSX.Element;
211
229
 
212
- /**
213
- * The bounds of the part, for the overlays that have to be sized against it.
214
- *
215
- * Measured on a frame rather than in an effect, because a Suspense-loaded mesh
216
- * does not exist yet when the effects around it run — the same reason the
217
- * viewer's opening frame waits. Measured once: an overlay that re-fitted itself
218
- * while the part was being orbited would be a grid that breathes.
219
- *
220
- * Scene furniture is excluded, so the grid and the axes do not size each other.
221
- */
222
- declare function useContentBox(): Box3;
223
-
224
- /**
225
- * Telling a click apart from the end of a drag.
226
- *
227
- * The browser calls both a click: press, move, release over the same element
228
- * fires `click` however far the pointer travelled in between. In a viewport
229
- * that is the difference between "select this face" and "I have finished
230
- * orbiting" — and the part is one mesh, so releasing over a different face is
231
- * still the same element and still a click.
232
- *
233
- * Left unguarded, every orbit that happens to end over the part selects
234
- * whatever it ended on, throwing away the selection the orbit was made to look
235
- * at.
236
- */
237
- /** How far a pointer may travel and still be a click, in CSS pixels. */
238
- declare const TAP_SLOP = 4;
239
- interface TapPoint {
240
- readonly clientX: number;
241
- readonly clientY: number;
242
- }
243
- /** Whether the pointer travelled far enough for this to be a drag. */
244
- declare function movedFar(from: TapPoint, to: TapPoint, slop?: number): boolean;
245
- interface TapTracker {
246
- /** Whether the gesture ending at this event was a click rather than a drag. */
247
- isTap(event: TapPoint): boolean;
248
- dispose(): void;
249
- }
250
- /**
251
- * Watches an element for the start of every gesture, so its end can be judged.
252
- *
253
- * Listens in the capture phase: whatever else handles the press — the camera
254
- * controls, a drag handle — the start of the gesture still has to be recorded,
255
- * and a listener that runs after `stopPropagation` runs never.
256
- */
257
- declare function trackTaps(element: HTMLElement): TapTracker;
258
- /**
259
- * How long between two presses still reads as one gesture, in milliseconds.
260
- *
261
- * The browser's own `dblclick` threshold is a platform setting and is not
262
- * readable from script, so a hand-paired gesture has to pick a number. 400ms is
263
- * inside every default and outside a deliberate pair of separate clicks.
264
- */
265
- declare const DOUBLE_TAP_MS = 400;
266
- interface DoubleTapPoint extends TapPoint {
267
- readonly timeStamp: number;
268
- }
269
- interface DoubleTapTracker {
270
- /** Whether this press completes a double tap begun by the last one. */
271
- isDouble(event: DoubleTapPoint): boolean;
272
- /**
273
- * Forgets the press being waited on, so the next one starts a fresh pair.
274
- *
275
- * For the caller that knows the gesture was interrupted by something this
276
- * has no way to see — the pointer leaving the thing being clicked, most of
277
- * all. Two presses either side of a trip to a panel are two clicks that
278
- * happen to be quick, however close together the clock puts them.
279
- */
280
- reset(): void;
281
- }
282
- /**
283
- * Pairing presses into a double tap by hand.
284
- *
285
- * Needed because `dblclick` fires for the **primary button only** — there is no
286
- * such event for the middle button, however many times it is pressed. So a
287
- * middle-button gesture has to be assembled from single presses.
288
- *
289
- * Position matters as well as time: two presses at opposite corners inside the
290
- * window are two clicks that happened to be quick, not one gesture, and
291
- * treating them as a double would re-frame the view out from under somebody
292
- * who was doing something else. The same {@link TAP_SLOP} the click guard uses.
293
- *
294
- * A completed double clears the state rather than leaving it, so three presses
295
- * are one double and one single rather than two overlapping doubles.
296
- *
297
- * Time and position are all this can judge on, which is not enough on its own:
298
- * clicking a face, pressing something in a panel and clicking the same face
299
- * again is three gestures the clock cannot tell from two, and it is quick
300
- * enough to land inside the window. {@link DoubleTapTracker.reset} is how the
301
- * caller says the pair was broken by something it could see and this could not.
302
- */
303
- declare function trackDoubleTaps(within?: number, slop?: number): DoubleTapTracker;
304
-
305
- /** Whether the gesture ending at this event was a click rather than a drag. */
306
- type TapGuard = (event: TapPoint) => boolean;
307
- /**
308
- * A click that is a click, for anything inside the canvas.
309
- *
310
- * Bound to the canvas element rather than to the object, because the gesture
311
- * being judged started before any object knew about it.
312
- *
313
- * Under a `<Viewer>` this returns the tracker the scene already owns and
314
- * attaches nothing. Called outside one — it is exported, so a consumer may use
315
- * it in a scene of their own — it owns a tracker of its own instead. The effect
316
- * is what differs, not the hooks: they run in the same order either way.
317
- */
318
- declare function useTapGuard(): TapGuard;
319
-
320
- interface GridSpec {
321
- /** Cell size in part units (millimetres). */
322
- readonly step: number;
323
- /** Half-width of the grid, so it spans `2 × extent`. */
324
- readonly extent: number;
325
- /** The plane the grid sits on: the bottom of the part, snapped to a step. */
326
- readonly z: number;
327
- readonly center: Vector3;
230
+ interface SectionToolProps {
231
+ theme?: Partial<ViewerTheme>;
328
232
  }
329
233
  /**
330
- * Sizes a ground grid for a part.
331
- *
332
- * The step comes from a 1-2-5 progression rather than a fixed size, so the same
333
- * code reads sensibly for a 12 mm insert and a 900 mm plate — the Engine emits
334
- * millimetres but says nothing about scale.
335
- *
336
- * The plane is the *bottom* of the part, not `z = 0`: parts usually sit on
337
- * `z = 0` and then the two agree, but one modelled about its centre would
338
- * otherwise be sliced in half by its own grid.
339
- */
340
- declare function gridSpec(box: Box3): GridSpec;
341
- /**
342
- * A ground grid on the part's Z-up base plane.
343
- *
344
- * Built directly rather than with `GridHelper`, which lays out on XZ for a Y-up
345
- * world and has to be rotated into place — the Engine is Z-up, and a rotated
346
- * helper is a thing to remember rather than a thing that is true.
347
- */
348
- declare function gridGeometry(spec: GridSpec): BufferGeometry;
349
-
350
- /**
351
- * The lines between regions, and nothing else.
352
- *
353
- * `EdgesGeometry` draws an edge wherever two facets meet at more than some
354
- * angle, which is a guess standing in for "is this a real edge". On a machined
355
- * part the guess is wrong in both directions: a small bore tessellated into
356
- * twelve facets has 30° between them and gets drawn as a nut, while a shallow
357
- * chamfer meeting a wall at 12° gets no line at all.
358
- *
359
- * The report already knows. A region is one analytic surface, so an edge inside
360
- * one is tessellation and an edge between two is a real boundary — the same
361
- * fact that makes region-aware shading possible, used for the other half of
362
- * what makes a part read as a part.
363
- *
364
- * With one qualification. The Engine splits a surface where that makes a better
365
- * machining plan, and those splits are boundaries between regions without being
366
- * edges of the part: a floor cut in two to be reached from two directions is
367
- * still one face to look at. So the walk is over *visual surfaces* — regions
368
- * grouped by the kernel's exact `splitOrigin` lineage — and a split leaves no
369
- * line. Nothing about picking or features goes through that grouping.
370
- *
371
- * The mesh must be non-indexed, which `parsePartGeometry` guarantees.
372
- */
373
- declare function regionEdgesGeometry(geometry: BufferGeometry, model: Pick<PartModel, 'regions' | 'regionIndex'>): BufferGeometry;
374
-
375
- /** Region index → the visual surface it belongs to. */
376
- type SurfaceOf = ReadonlyMap<number, number>;
377
- /**
378
- * The visual surface of each post-split region.
379
- *
380
- * The kernel divides B-rep faces to make machining relationships unambiguous.
381
- * Those divisions remain meaningful to feature extraction and picking, but
382
- * they are not physical edges. `splitOrigin` is the kernel's exact lineage
383
- * relation: equal values mean the regions were one face before that analysis
384
- * split. It is therefore the single grouping used for rendering, rather than
385
- * attempting to infer continuations from mesh normals or surface kinds.
234
+ * The interactive way to cut a part open. Mount it beside `<PartMesh>`.
235
+ *
236
+ * With no cut in place it offers two: hover a face and a small square previews
237
+ * a cut through it — click to make it — or click one of the three global
238
+ * planes standing behind the part to sweep along that axis. Once there is a
239
+ * cut it draws the cutting plane as a translucent sheet with an outline, and
240
+ * `Escape` clears it. The handle that drags the cut, and the hatched cap, are
241
+ * the part's own and appear whether or not this is mounted.
242
+ *
243
+ * Nothing needs wiring. The cut lives in the viewer, `<PartMesh>` follows it
244
+ * when it is given no `section` of its own, and `ViewerHandle.setSection` is
245
+ * the way to set or clear it from outside the canvas. While this is mounted
246
+ * the part reports no hovers or picks: a click on a face cuts through it
247
+ * without also selecting it, and a cut part is looked at rather than picked
248
+ * at. Unmounting it hands the pointer back.
386
249
  */
387
- declare function visualSurfaces(regions: readonly PartModelRegion[]): SurfaceOf;
250
+ declare const SectionTool: ({ theme }: SectionToolProps) => react.JSX.Element | null;
388
251
 
389
252
  type Projection = 'orthographic' | 'perspective';
390
253
  type ViewerCamera = OrthographicCamera | PerspectiveCamera;
@@ -443,6 +306,13 @@ declare function boundsFromBox(box: Box3): SceneBounds;
443
306
  * would be a speck.
444
307
  */
445
308
  declare function contentBounds(root: Object3D, into: Box3): SceneBounds;
309
+ /**
310
+ * Whether `object`, or anything between it and `root`, carries
311
+ * {@link EXCLUDE_FROM_FRAME}. The flag is set on an overlay's outermost group
312
+ * and means the whole subtree, so the walk goes up rather than reading the
313
+ * object alone.
314
+ */
315
+ declare function excludedFromFrame(object: Object3D, root: Object3D): boolean;
446
316
  /**
447
317
  * The distance at which a sphere of `radius` fits inside a perspective frustum.
448
318
  * Uses the narrower of the two field-of-view angles, so a portrait viewport
@@ -580,6 +450,551 @@ type ViewerView = keyof typeof cadViewDirections;
580
450
  /** The current orbit direction, so Fit can retain the direction being looked from. */
581
451
  declare function currentViewDirection(camera: ViewerCamera, target: Vector3, into: Vector3): Vector3;
582
452
 
453
+ /**
454
+ * Measuring, without a canvas: where a pointer snaps to, what two or three
455
+ * points measure, and how that is written down. `measure-tool.tsx` is the
456
+ * React surface over this.
457
+ */
458
+ /** Draws over the part and the section cap, under nothing. */
459
+ declare const MEASURE_RENDER_ORDER = 7;
460
+ /**
461
+ * One colour per world axis, the hue `<Axes>` draws that axis in — lightened,
462
+ * because a pure primary through 30% on a dark page is mud, and the blue one
463
+ * was not there at all. The section tool's three global planes and a
464
+ * distance's X, Y, Z legs both wear them, so an axis is one colour everywhere.
465
+ */
466
+ declare const AXIS_COLORS: Record<Axis, number>;
467
+ /**
468
+ * How close, in CSS pixels, the pointer has to be for a snap to take.
469
+ *
470
+ * A vertex or a midpoint is a point, so it gets the wider reach; an edge is a
471
+ * line the pointer can sit on, so it needs less. The pointer snaps to the
472
+ * strongest kind it is within reach of — a corner over the edge it ends, the
473
+ * edge over the face it bounds — which is what makes clicking the corner of a
474
+ * part produce the corner rather than a point a pixel off it.
475
+ */
476
+ declare const SNAP_PIXELS: {
477
+ readonly vertex: 14;
478
+ readonly midpoint: 12;
479
+ readonly edge: 9;
480
+ };
481
+ /** The snap indicator's radius on screen, by what it is on, in CSS pixels. */
482
+ declare const SNAP_MARKER_PIXELS: {
483
+ readonly vertex: 6;
484
+ readonly midpoint: 5;
485
+ readonly edge: 4;
486
+ readonly face: 3.5;
487
+ };
488
+ /** A measurement's end marker, in CSS pixels. */
489
+ declare const MEASURE_MARKER_PIXELS = 3.5;
490
+ /**
491
+ * Line widths in CSS pixels: a measurement's own line, a delta leg, and the
492
+ * edge the pointer is snapped to.
493
+ */
494
+ declare const MEASURE_LINE_PIXELS: {
495
+ readonly line: 2;
496
+ readonly leg: 1.5;
497
+ readonly edge: 3;
498
+ };
499
+ /** Dash and gap of a delta leg, in CSS pixels. */
500
+ declare const DELTA_DASH_PIXELS = 6;
501
+ /** How much of the shorter arm an angle's arc is drawn at. */
502
+ declare const ANGLE_ARC_FRACTION = 0.3;
503
+ /** Segments in an angle's arc. */
504
+ declare const ANGLE_ARC_SEGMENTS = 24;
505
+ type SnapKind = 'vertex' | 'midpoint' | 'edge' | 'face';
506
+ /** Where the pointer landed after snapping, and what it landed on. */
507
+ interface Snap {
508
+ readonly kind: SnapKind;
509
+ readonly point: Vector3;
510
+ /** The outward normal of the surface under the pointer, in world space. */
511
+ readonly normal: Vector3;
512
+ /** For an edge or midpoint snap, the edge in world space. */
513
+ readonly edge: readonly [Vector3, Vector3] | null;
514
+ }
515
+ /**
516
+ * The edges a pointer can snap to: segment endpoints, six floats per segment,
517
+ * in the space `matrixWorld` maps to the world.
518
+ *
519
+ * In practice this is the part's own edge overlay — `regionEdgesGeometry` —
520
+ * which draws a line only where two analytic surfaces meet. Those are the edges
521
+ * a machinist would put a caliper on; the diagonal a tessellator drew across a
522
+ * flat face is not, and snapping to it would measure the mesh rather than the
523
+ * part.
524
+ */
525
+ interface SnapEdges {
526
+ readonly positions: ArrayLike<number>;
527
+ readonly matrixWorld: Matrix4;
528
+ }
529
+ /** What the ray hit, before snapping. */
530
+ interface SnapHit {
531
+ readonly point: Vector3;
532
+ readonly normal: Vector3;
533
+ }
534
+ /**
535
+ * Where a hit on the part snaps to.
536
+ *
537
+ * Nearness is judged on screen, in pixels, because that is where the pointer
538
+ * is: a corner three pixels away is a corner however far the camera is. The
539
+ * search is bounded in world space first — a segment further from the hit than
540
+ * the widest reach could ever cover is skipped without being projected — and
541
+ * held to the surface the hit is on: an edge that is within reach of the point
542
+ * but well off its tangent plane is on the far side of a wall, and snapping to
543
+ * it would put a measurement on something the eye cannot see.
544
+ */
545
+ declare function snapAt(hit: SnapHit, edges: SnapEdges | null, camera: ViewerCamera, viewport: ViewportSize): Snap;
546
+ /** Where a world point lands on the canvas, in CSS pixels from the top left. */
547
+ declare function screenPoint(camera: ViewerCamera, point: Vector3, viewport: ViewportSize, into?: Vector2): Vector2;
548
+ type MeasureMode = 'distance' | 'angle';
549
+ /** How many clicks each kind of measurement takes. */
550
+ declare const POINTS_PER_MEASUREMENT: Record<MeasureMode, number>;
551
+ interface DistanceMeasurement {
552
+ readonly id: number;
553
+ readonly kind: 'distance';
554
+ readonly points: readonly [Vec3, Vec3];
555
+ }
556
+ /** Three points: the two arms' ends, with the vertex between them. */
557
+ interface AngleMeasurement {
558
+ readonly id: number;
559
+ readonly kind: 'angle';
560
+ readonly points: readonly [Vec3, Vec3, Vec3];
561
+ }
562
+ type Measurement = DistanceMeasurement | AngleMeasurement;
563
+ /**
564
+ * A measurement from the points clicked so far, or `null` while there are not
565
+ * enough of them yet.
566
+ */
567
+ declare function measurementFromPoints(kind: MeasureMode, points: readonly Vec3[], id: number): Measurement | null;
568
+ /** An id no measurement in `held` has. */
569
+ declare function nextMeasurementId(held: readonly Measurement[]): number;
570
+ declare function distanceBetween(a: Vec3, b: Vec3): number;
571
+ /** `b − a`, per axis. */
572
+ declare function deltaBetween(a: Vec3, b: Vec3): Vec3;
573
+ declare function midpoint(a: Vec3, b: Vec3): Vec3;
574
+ /**
575
+ * The angle at `vertex` between the arms to `a` and `b`, in degrees, from 0 to
576
+ * 180. An arm with no length has no direction, and the angle is 0.
577
+ */
578
+ declare function angleAt(vertex: Vec3, a: Vec3, b: Vec3): number;
579
+ type Axis = 'x' | 'y' | 'z';
580
+ /** One leg of a distance's X, Y, Z breakdown. */
581
+ interface DeltaLeg {
582
+ readonly axis: Axis;
583
+ readonly from: Vec3;
584
+ readonly to: Vec3;
585
+ readonly length: number;
586
+ }
587
+ /** Where a point lands when it is held to one axis from `from`. */
588
+ interface AxisLock {
589
+ readonly axis: Axis;
590
+ readonly point: Vec3;
591
+ }
592
+ /**
593
+ * `to`, held to the one axis it has moved furthest along from `from`.
594
+ *
595
+ * The ortho lock a CAD user reaches for with Shift: the next point is dropped
596
+ * onto the X, Y or Z line through the last one, so a distance meant to be "how
597
+ * long is this" is not "how long is this, and a little sideways". Which axis is
598
+ * whichever the pointer is furthest along, and a pointer that has not moved
599
+ * stays put on X.
600
+ */
601
+ declare function lockToAxis(from: Vec3, to: Vec3): AxisLock;
602
+ /**
603
+ * A distance broken into its X, Y and Z parts, as a path from `a` to `b` that
604
+ * runs along one axis at a time.
605
+ *
606
+ * Only the legs with length are returned, and none at all when the distance
607
+ * already runs along an axis: a single leg would lie on top of the line it
608
+ * explains and say nothing the line does not.
609
+ */
610
+ declare function deltaLegs(a: Vec3, b: Vec3): readonly DeltaLeg[];
611
+ /**
612
+ * Points along the arc an angle is drawn with, from the arm to `a` round to
613
+ * the arm to `b`, at `radius` from the vertex.
614
+ *
615
+ * Interpolated on the sphere rather than along the chord, so the arc is an
616
+ * arc. Two arms that point the same way, or opposite ways, have no plane to
617
+ * draw in, and get their two ends and nothing between.
618
+ */
619
+ declare function angleArc(vertex: Vec3, a: Vec3, b: Vec3, radius: number, segments?: number): Vector3[];
620
+ /** The radius an angle's arc is drawn at: a fraction of the shorter arm. */
621
+ declare function angleArcRadius(vertex: Vec3, a: Vec3, b: Vec3): number;
622
+ /** A length in millimetres, as the tool writes it when told nothing else. */
623
+ declare function formatMillimetres(mm: number, digits?: number): string;
624
+ declare function formatDegrees(degrees: number, digits?: number): string;
625
+ /** What a measurement's label says. `format` writes a length; angles write themselves. */
626
+ declare function measurementLabel(measurement: Measurement, format?: (mm: number) => string): string;
627
+ /** Where a measurement's label sits: the middle of a distance, the vertex of an angle. */
628
+ declare function measurementLabelAnchor(measurement: Measurement): Vec3;
629
+
630
+ /**
631
+ * The class every label carries, for a stylesheet to find.
632
+ *
633
+ * The labels ship with no styling at all. Each one is a `<div>` with this
634
+ * class, `data-measure-label` naming what it is — `distance`, `angle`, `delta`
635
+ * for one leg of a distance's breakdown, or `live` for the readout that
636
+ * follows the pointer — `data-axis` on a leg, and `data-measurement-id` on
637
+ * anything belonging to a finished measurement. The README shows a stylesheet
638
+ * to start from.
639
+ */
640
+ declare const MEASURE_LABEL_CLASS = "toolpath-measure-label";
641
+ /** What a label is, for a stylesheet to tell them apart. */
642
+ type MeasureLabelKind = 'distance' | 'angle' | 'delta' | 'live';
643
+ interface MeasureToolProps {
644
+ /**
645
+ * What a click sequence measures. `distance` takes two points; `angle` takes
646
+ * three — one arm's end, the vertex, the other arm's end. Changing it drops
647
+ * any points already placed.
648
+ */
649
+ mode?: MeasureMode;
650
+ /**
651
+ * The measurements on screen. Pass it to own them; omit it and the tool keeps
652
+ * its own list, which `onChange` still reports.
653
+ */
654
+ measurements?: readonly Measurement[];
655
+ /** The list changed: a measurement finished, or Delete removed the last one. */
656
+ onChange?: (measurements: readonly Measurement[]) => void;
657
+ /**
658
+ * Whether a distance also shows its X, Y and Z parts, as dashed legs in the
659
+ * axis colours with their own labels. On by default; a distance that already
660
+ * runs along an axis shows none either way.
661
+ */
662
+ showDeltas?: boolean;
663
+ /**
664
+ * Writes a length. Millimetres to two places unless told otherwise — the part
665
+ * is in millimetres, and converting is the consumer's call.
666
+ */
667
+ format?: (mm: number) => string;
668
+ /**
669
+ * Added to every label's `className`, beside {@link MEASURE_LABEL_CLASS} —
670
+ * a Tailwind utility list, or a CSS module's class.
671
+ */
672
+ labelClassName?: string;
673
+ theme?: Partial<ViewerTheme>;
674
+ }
675
+ /**
676
+ * Measure the part from inside the viewport. Mount it beside `<PartMesh>`.
677
+ *
678
+ * The pointer snaps to what is under it — a corner first, then an edge's
679
+ * midpoint, then the edge, then the face — and a marker shows where the click
680
+ * will land. A distance is two clicks and an angle three; the line follows the
681
+ * pointer between them with a live readout. Finished measurements stay up,
682
+ * drawn over the part with a label each, until Delete removes the last one or
683
+ * the tool is unmounted. Escape drops the points of one in progress. Holding
684
+ * Shift holds the next point to the X, Y or Z line through the last one — the
685
+ * axis the pointer is furthest along — so a length along an edge is measured
686
+ * along the edge and not a little across it.
687
+ *
688
+ * Nothing needs wiring. While it is mounted the part reports no hovers or
689
+ * picks — a click that places a point is not a click that selects a face — and
690
+ * unmounting it hands the pointer back. It snaps to the edges the part draws,
691
+ * which are the boundaries between analytic surfaces rather than the mesh's
692
+ * triangles, so a corner is a corner of the part and not of a facet.
693
+ *
694
+ * Lines are drawn at a width in pixels, through drei's `Line`, because a
695
+ * WebGL line is one pixel wide whatever is asked of it and one pixel of blue
696
+ * over a white face is not a measurement anybody can see.
697
+ *
698
+ * Labels are DOM elements laid over the canvas, so they are text a test can
699
+ * read — and they are **unstyled**. This is a headless component: what a
700
+ * label looks like is the consumer's stylesheet's, through
701
+ * {@link MEASURE_LABEL_CLASS} and the data attributes on each one. The one
702
+ * thing set here is that the element each label is wrapped in takes no
703
+ * pointer events, because a label over the part that took the click meant
704
+ * for the face under it would be a bug rather than a look.
705
+ */
706
+ declare const MeasureTool: ({ mode, measurements, onChange, showDeltas, format, labelClassName, theme, }: MeasureToolProps) => react.JSX.Element | null;
707
+
708
+ /**
709
+ * The cut a viewer holds for itself, when nobody else is holding it.
710
+ *
711
+ * `<PartMesh section>` is the controlled path and is untouched by this. The
712
+ * store exists for the other one: `<SectionTool>` mounted beside the part with
713
+ * no wiring at all. The two are siblings and cannot see each other, so the
714
+ * viewer keeps one of these and each reads or writes it — the tool sets the
715
+ * cut, the part clips to it and moves it when the handle is dragged.
716
+ *
717
+ * It also carries whether a tool is **engaged** — mounted at all. While one
718
+ * is, the pointer over the part belongs to it: the part reports no hovers or
719
+ * picks, so clicking a face to cut through it does not also select it, the
720
+ * hover paint does not sit under the tool's own preview, and a cut part is
721
+ * read as a cut rather than picked at. Unmounting the tool hands the pointer
722
+ * back. `<MeasureTool>` engages it too — a click that puts a measurement point
723
+ * on a face is not a request to select the face — which is why the flag is a
724
+ * count rather than a boolean: two tools up at once let go one at a time.
725
+ *
726
+ * A subscription rather than React state, so a drag re-renders the two
727
+ * subscribers and not everything under the canvas.
728
+ */
729
+ interface SectionStore {
730
+ get(): SectionOptions | null;
731
+ set(next: SectionOptions | null): void;
732
+ /** Whether a tool is mounted, and the part should stay out of the way. */
733
+ isEngaged(): boolean;
734
+ /** Counted: a tool engages on mount and disengages on unmount, and the part waits for the last. */
735
+ setEngaged(engaged: boolean): void;
736
+ /** Notified on every change to either the cut or the engaged flag. */
737
+ subscribe(listener: () => void): () => void;
738
+ }
739
+ declare function createSectionStore(): SectionStore;
740
+
741
+ type CapUniforms = {
742
+ uMask: {
743
+ value: Texture | null;
744
+ };
745
+ uResolution: {
746
+ value: Vector2;
747
+ };
748
+ uFill: {
749
+ value: Color;
750
+ };
751
+ uHatch: {
752
+ value: Color;
753
+ };
754
+ uOutline: {
755
+ value: Color;
756
+ };
757
+ uPitch: {
758
+ value: number;
759
+ };
760
+ uLine: {
761
+ value: number;
762
+ };
763
+ uThickness: {
764
+ value: number;
765
+ };
766
+ };
767
+ type CapMaterial = ShaderMaterial & {
768
+ uniforms: CapUniforms;
769
+ };
770
+ /**
771
+ * The render target the mask is drawn into. Sized later, to the drawing
772
+ * buffer, because that is what `gl_FragCoord` is measured in and a mask at
773
+ * any other size would put the outline beside the edge rather than on it.
774
+ */
775
+ declare function createMaskTarget(): WebGLRenderTarget;
776
+ /**
777
+ * The cap's own material, filled only where the main pass's stencil says the
778
+ * plane is inside material. Unthemed until {@link applyCapTheme} is called.
779
+ */
780
+ declare function createCapMaterial(mask: Texture): CapMaterial;
781
+ /** Re-themes a cap in place, and re-scales its hatch for the device pixel ratio. */
782
+ declare function applyCapTheme(material: CapMaterial, theme: ViewerTheme, dpr: number): void;
783
+ /**
784
+ * The two materials of the stencil pass: back faces count up, front faces
785
+ * count down, and what is left non-zero is where the plane is inside solid
786
+ * material. Nothing is written but the stencil.
787
+ */
788
+ declare function createStencilMaterials(clip: Plane[]): [MeshBasicMaterial, MeshBasicMaterial];
789
+ /** The quad the mask pass draws where the stencil is set — white on black. */
790
+ declare function createMaskMaterial(): MeshBasicMaterial;
791
+
792
+ interface ArrowPlacement {
793
+ /** Where the arrow's tip sits: outside the box, on the direction's ray. */
794
+ readonly tip: Vector3;
795
+ readonly length: number;
796
+ }
797
+ /**
798
+ * Where an arrow for `direction` sits relative to the part.
799
+ *
800
+ * The exit distance is the smallest `halfExtent / |component|` over the axes the
801
+ * direction actually moves along — the first face of the box the ray leaves
802
+ * through. That formulation is what makes this work for **arbitrary unit
803
+ * vectors** rather than only for axes: real reports carry tilted directions,
804
+ * one of them a 36° five-axis setup, and an axis-aligned simplification would
805
+ * bury those arrows inside the part.
806
+ */
807
+ declare function arrowPlacement(direction: Vec3, box: Box3): ArrowPlacement;
808
+
809
+ /**
810
+ * The bounds of the part, for the overlays that have to be sized against it.
811
+ *
812
+ * Measured on a frame rather than in an effect, because a Suspense-loaded mesh
813
+ * does not exist yet when the effects around it run — the same reason the
814
+ * viewer's opening frame waits. Measured once: an overlay that re-fitted itself
815
+ * while the part was being orbited would be a grid that breathes.
816
+ *
817
+ * Scene furniture is excluded, so the grid and the axes do not size each other.
818
+ */
819
+ declare function useContentBox(): Box3;
820
+
821
+ /**
822
+ * Telling a click apart from the end of a drag.
823
+ *
824
+ * The browser calls both a click: press, move, release over the same element
825
+ * fires `click` however far the pointer travelled in between. In a viewport
826
+ * that is the difference between "select this face" and "I have finished
827
+ * orbiting" — and the part is one mesh, so releasing over a different face is
828
+ * still the same element and still a click.
829
+ *
830
+ * Left unguarded, every orbit that happens to end over the part selects
831
+ * whatever it ended on, throwing away the selection the orbit was made to look
832
+ * at.
833
+ */
834
+ /** How far a pointer may travel and still be a click, in CSS pixels. */
835
+ declare const TAP_SLOP = 4;
836
+ interface TapPoint {
837
+ readonly clientX: number;
838
+ readonly clientY: number;
839
+ }
840
+ /** Whether the pointer travelled far enough for this to be a drag. */
841
+ declare function movedFar(from: TapPoint, to: TapPoint, slop?: number): boolean;
842
+ interface TapTracker {
843
+ /** Whether the gesture ending at this event was a click rather than a drag. */
844
+ isTap(event: TapPoint): boolean;
845
+ dispose(): void;
846
+ }
847
+ /**
848
+ * Watches an element for the start of every gesture, so its end can be judged.
849
+ *
850
+ * Listens in the capture phase: whatever else handles the press — the camera
851
+ * controls, a drag handle — the start of the gesture still has to be recorded,
852
+ * and a listener that runs after `stopPropagation` runs never.
853
+ */
854
+ declare function trackTaps(element: HTMLElement): TapTracker;
855
+ /**
856
+ * How long between two presses still reads as one gesture, in milliseconds.
857
+ *
858
+ * The browser's own `dblclick` threshold is a platform setting and is not
859
+ * readable from script, so a hand-paired gesture has to pick a number. 400ms is
860
+ * inside every default and outside a deliberate pair of separate clicks.
861
+ */
862
+ declare const DOUBLE_TAP_MS = 400;
863
+ interface DoubleTapPoint extends TapPoint {
864
+ readonly timeStamp: number;
865
+ }
866
+ interface DoubleTapTracker {
867
+ /** Whether this press completes a double tap begun by the last one. */
868
+ isDouble(event: DoubleTapPoint): boolean;
869
+ /**
870
+ * Forgets the press being waited on, so the next one starts a fresh pair.
871
+ *
872
+ * For the caller that knows the gesture was interrupted by something this
873
+ * has no way to see — the pointer leaving the thing being clicked, most of
874
+ * all. Two presses either side of a trip to a panel are two clicks that
875
+ * happen to be quick, however close together the clock puts them.
876
+ */
877
+ reset(): void;
878
+ }
879
+ /**
880
+ * Pairing presses into a double tap by hand.
881
+ *
882
+ * Needed because `dblclick` fires for the **primary button only** — there is no
883
+ * such event for the middle button, however many times it is pressed. So a
884
+ * middle-button gesture has to be assembled from single presses.
885
+ *
886
+ * Position matters as well as time: two presses at opposite corners inside the
887
+ * window are two clicks that happened to be quick, not one gesture, and
888
+ * treating them as a double would re-frame the view out from under somebody
889
+ * who was doing something else. The same {@link TAP_SLOP} the click guard uses.
890
+ *
891
+ * A completed double clears the state rather than leaving it, so three presses
892
+ * are one double and one single rather than two overlapping doubles.
893
+ *
894
+ * Time and position are all this can judge on, which is not enough on its own:
895
+ * clicking a face, pressing something in a panel and clicking the same face
896
+ * again is three gestures the clock cannot tell from two, and it is quick
897
+ * enough to land inside the window. {@link DoubleTapTracker.reset} is how the
898
+ * caller says the pair was broken by something it could see and this could not.
899
+ */
900
+ declare function trackDoubleTaps(within?: number, slop?: number): DoubleTapTracker;
901
+
902
+ /** Whether the gesture ending at this event was a click rather than a drag. */
903
+ type TapGuard = (event: TapPoint) => boolean;
904
+ /**
905
+ * A click that is a click, for anything inside the canvas.
906
+ *
907
+ * Bound to the canvas element rather than to the object, because the gesture
908
+ * being judged started before any object knew about it.
909
+ *
910
+ * Under a `<Viewer>` this returns the tracker the scene already owns and
911
+ * attaches nothing. Called outside one — it is exported, so a consumer may use
912
+ * it in a scene of their own — it owns a tracker of its own instead. The effect
913
+ * is what differs, not the hooks: they run in the same order either way.
914
+ */
915
+ declare function useTapGuard(): TapGuard;
916
+
917
+ interface GridSpec {
918
+ /** Cell size in part units (millimetres). */
919
+ readonly step: number;
920
+ /** Half-width of the grid, so it spans `2 × extent`. */
921
+ readonly extent: number;
922
+ /** The plane the grid sits on: the bottom of the part, snapped to a step. */
923
+ readonly z: number;
924
+ readonly center: Vector3;
925
+ }
926
+ /**
927
+ * Sizes a ground grid for a part.
928
+ *
929
+ * The step comes from a 1-2-5 progression rather than a fixed size, so the same
930
+ * code reads sensibly for a 12 mm insert and a 900 mm plate — the Engine emits
931
+ * millimetres but says nothing about scale.
932
+ *
933
+ * The plane is the *bottom* of the part, not `z = 0`: parts usually sit on
934
+ * `z = 0` and then the two agree, but one modelled about its centre would
935
+ * otherwise be sliced in half by its own grid.
936
+ *
937
+ * **Give this a measured box.** The plane is `box.min.z`, which is `+Infinity`
938
+ * for an empty one. Nothing else in the spec shows it — `getSize` and
939
+ * `getCenter` both short-circuit to zero for an empty `Box3` — so the step and
940
+ * the extent come back looking ordinary and {@link gridGeometry} then builds
941
+ * every vertex on a plane at infinity, which three.js reports as "Computed
942
+ * radius is NaN" once per mount.
943
+ *
944
+ * `useContentBox` hands back an empty box until the scene has been
945
+ * measured, so a caller pairing the two wants `if (box.isEmpty()) return null`
946
+ * ahead of this. That is what the package's own `Grid` does, and it is why it
947
+ * draws nothing on the opening frame rather than a grid nobody asked for.
948
+ */
949
+ declare function gridSpec(box: Box3): GridSpec;
950
+ /**
951
+ * A ground grid on the part's Z-up base plane.
952
+ *
953
+ * Built directly rather than with `GridHelper`, which lays out on XZ for a Y-up
954
+ * world and has to be rotated into place — the Engine is Z-up, and a rotated
955
+ * helper is a thing to remember rather than a thing that is true.
956
+ */
957
+ declare function gridGeometry(spec: GridSpec): BufferGeometry;
958
+
959
+ /**
960
+ * The lines between regions, and nothing else.
961
+ *
962
+ * `EdgesGeometry` draws an edge wherever two facets meet at more than some
963
+ * angle, which is a guess standing in for "is this a real edge". On a machined
964
+ * part the guess is wrong in both directions: a small bore tessellated into
965
+ * twelve facets has 30° between them and gets drawn as a nut, while a shallow
966
+ * chamfer meeting a wall at 12° gets no line at all.
967
+ *
968
+ * The report already knows. A region is one analytic surface, so an edge inside
969
+ * one is tessellation and an edge between two is a real boundary — the same
970
+ * fact that makes region-aware shading possible, used for the other half of
971
+ * what makes a part read as a part.
972
+ *
973
+ * With one qualification. The Engine splits a surface where that makes a better
974
+ * machining plan, and those splits are boundaries between regions without being
975
+ * edges of the part: a floor cut in two to be reached from two directions is
976
+ * still one face to look at. So the walk is over *visual surfaces* — regions
977
+ * grouped by the kernel's exact `splitOrigin` lineage — and a split leaves no
978
+ * line. Nothing about picking or features goes through that grouping.
979
+ *
980
+ * The mesh must be non-indexed, which `parsePartGeometry` guarantees.
981
+ */
982
+ declare function regionEdgesGeometry(geometry: BufferGeometry, model: Pick<PartModel, 'regions' | 'regionIndex'>): BufferGeometry;
983
+
984
+ /** Region index → the visual surface it belongs to. */
985
+ type SurfaceOf = ReadonlyMap<number, number>;
986
+ /**
987
+ * The visual surface of each post-split region.
988
+ *
989
+ * The kernel divides B-rep faces to make machining relationships unambiguous.
990
+ * Those divisions remain meaningful to feature extraction and picking, but
991
+ * they are not physical edges. `splitOrigin` is the kernel's exact lineage
992
+ * relation: equal values mean the regions were one face before that analysis
993
+ * split. It is therefore the single grouping used for rendering, rather than
994
+ * attempting to infer continuations from mesh normals or surface kinds.
995
+ */
996
+ declare function visualSurfaces(regions: readonly PartModelRegion[]): SurfaceOf;
997
+
583
998
  /**
584
999
  * Mouse and trackpad presets.
585
1000
  *
@@ -705,12 +1120,26 @@ interface ViewerControls {
705
1120
  * see a wall and then asks to zoom to it wants it closer, not re-oriented.
706
1121
  */
707
1122
  frameBox(box: Box3): void;
1123
+ /**
1124
+ * Sets or clears the cut the viewer holds for itself — the one `<SectionTool>`
1125
+ * places and `<PartMesh>` follows when it is given no `section` of its own.
1126
+ *
1127
+ * `null` clears it. A `<PartMesh section>` is not affected: that cut is the
1128
+ * consumer's, and this one is the viewer's.
1129
+ */
1130
+ setSection(options: SectionOptions | null): void;
708
1131
  }
709
1132
  interface ViewerHandle extends ViewerControls {
710
1133
  }
711
1134
  type VectorLike = Pick<Vector3, 'x' | 'y' | 'z'>;
712
1135
 
713
1136
  declare const useViewerControls: () => ViewerControls;
1137
+ /**
1138
+ * The cut this viewer holds for itself. See `render/section-store.ts` for why
1139
+ * it is a store: `<SectionTool>` writes it and `<PartMesh>` reads it, and the
1140
+ * two are siblings.
1141
+ */
1142
+ declare const useSectionStore: () => SectionStore;
714
1143
  /** Re-aims the orbit, or `null` when this viewer has the gesture turned off. */
715
1144
  type Retarget = ((point: VectorLike) => void) | null;
716
1145
  /**
@@ -1081,4 +1510,4 @@ declare const ORBIT_TARGET_FADE_MS = 1000;
1081
1510
  */
1082
1511
  declare function orbitTargetOpacity(sinceHold: number, fade?: number): number;
1083
1512
 
1084
- export { type ArrowPlacement, Axes, type AxesProps, CAD_CAMERA_UP, CHAMFER, CadCameraControls, type CadCameraControlsProps, type CameraLimits, type ControlScheme, type CubeZone, DEFAULT_FIT_MARGIN, DOUBLE_TAP_MS, DirectionArrows, type DirectionArrowsProps, type DirectionGroup, type DoubleTapPoint, type DoubleTapTracker, EXCLUDE_FROM_FRAME, ExtendedCameraControls, type ExtendedCameraControlsOptions, FEATURE_TYPE_RANKS, FeatureTag, FeatureType, Grid, type GridProps, type GridSpec, MAX_FRAME_RATIO, MIN_FRAME_RATIO, type NamedDirection, ORBIT_TARGET_COLOR, ORBIT_TARGET_FADE_MS, ORBIT_TARGET_FLASH_MS, ORBIT_TARGET_PIXELS, ORBIT_TARGET_RING_COLOR, ORBIT_TARGET_RING_OPACITY, ORBIT_TARGET_RING_PIXELS, ORBIT_TARGET_RING_WIDTH, PERSPECTIVE_FOV, PartModel, PartModelFeature, PartModelRegion, PartReportFormatError, type Projection, type RankingContext, RegionIndex, type Retarget, type RetargetPose, type SceneBounds, type SurfaceOf, TAP_SLOP, type TapPoint, type TapTracker, TriangleRange, UnsupportedKernelVersionError, VIEW_NAMES, VIEW_SIGNS, Vec3, ViewCube, type ViewCubeProps, type ViewKind, type ViewName, Viewer, type ViewerCamera, type ViewerControls, type ViewerHandle, type ViewerProps, ViewerTheme, type ViewerView, type ViewportSize, adaptedUp, applyProjection, arrowPlacement, aspectRatio, bestOwner, boundsFromBox, buildRegionIndex, cadViewDirections, cameraLimits, contentBounds, cubeOutlineGeometry, cubeZones, currentViewDirection, cycleOwner, defaultBounds, directionIndexOf, directionLabel, featureTypeRank, fitDistance, gridGeometry, gridSpec, groupByDirection, labelGeometry, labelTexture, movedFar, orbitTargetOpacity, orthographicHalfHeight, panelGeometry, perspectiveFitDistance, rankOwners, regionAdjacency, regionEdgesGeometry, regionNormals, retargetPose, sameDirection, screenLength, squaredUp, startPosition, targetBoundary, trackDoubleTaps, trackTaps, useContentBox, useRetarget, useTapGuard, useViewerControls, viewKind, viewUp, viewVector, visualSurfaces };
1513
+ export { ANGLE_ARC_FRACTION, ANGLE_ARC_SEGMENTS, AXIS_COLORS, type AngleMeasurement, type ArrowPlacement, Axes, type AxesProps, type Axis, type AxisLock, CAD_CAMERA_UP, CHAMFER, CadCameraControls, type CadCameraControlsProps, type CameraLimits, type CapMaterial, type CapUniforms, type ControlScheme, type CubeZone, DEFAULT_FIT_MARGIN, DELTA_DASH_PIXELS, DOUBLE_TAP_MS, type DeltaLeg, DirectionArrows, type DirectionArrowsProps, type DirectionGroup, type DistanceMeasurement, type DoubleTapPoint, type DoubleTapTracker, EXCLUDE_FROM_FRAME, ExtendedCameraControls, type ExtendedCameraControlsOptions, FEATURE_TYPE_RANKS, FeatureTag, FeatureType, Grid, type GridProps, type GridSpec, MAX_FRAME_RATIO, MEASURE_LABEL_CLASS, MEASURE_LINE_PIXELS, MEASURE_MARKER_PIXELS, MEASURE_RENDER_ORDER, MIN_FRAME_RATIO, type MeasureLabelKind, type MeasureMode, MeasureTool, type MeasureToolProps, type Measurement, type NamedDirection, ORBIT_TARGET_COLOR, ORBIT_TARGET_FADE_MS, ORBIT_TARGET_FLASH_MS, ORBIT_TARGET_PIXELS, ORBIT_TARGET_RING_COLOR, ORBIT_TARGET_RING_OPACITY, ORBIT_TARGET_RING_PIXELS, ORBIT_TARGET_RING_WIDTH, PERSPECTIVE_FOV, POINTS_PER_MEASUREMENT, PartModel, PartModelFeature, PartModelRegion, PartReportFormatError, type Projection, type RankingContext, RegionIndex, type Retarget, type RetargetPose, SNAP_MARKER_PIXELS, SNAP_PIXELS, type SceneBounds, SectionOptions, SectionState, type SectionStore, SectionTool, type SectionToolProps, SectionView, type Snap, type SnapEdges, type SnapHit, type SnapKind, type SurfaceOf, TAP_SLOP, type TapPoint, type TapTracker, TriangleRange, UnsupportedKernelVersionError, VIEW_NAMES, VIEW_SIGNS, Vec3, ViewCube, type ViewCubeProps, type ViewKind, type ViewName, Viewer, type ViewerCamera, type ViewerControls, type ViewerHandle, type ViewerProps, ViewerTheme, type ViewerView, type ViewportSize, adaptedUp, angleArc, angleArcRadius, angleAt, applyCapTheme, applyProjection, arrowPlacement, aspectRatio, bestOwner, boundsFromBox, buildRegionIndex, cadViewDirections, cameraLimits, contentBounds, createCapMaterial, createMaskMaterial, createMaskTarget, createSectionStore, createStencilMaterials, cubeOutlineGeometry, cubeZones, currentViewDirection, cycleOwner, defaultBounds, deltaBetween, deltaLegs, directionIndexOf, directionLabel, distanceBetween, excludedFromFrame, featureTypeRank, fitDistance, formatDegrees, formatMillimetres, gridGeometry, gridSpec, groupByDirection, labelGeometry, labelTexture, lockToAxis, measurementFromPoints, measurementLabel, measurementLabelAnchor, midpoint, movedFar, nextMeasurementId, orbitTargetOpacity, orthographicHalfHeight, panelGeometry, perspectiveFitDistance, rankOwners, regionAdjacency, regionEdgesGeometry, regionNormals, resolveSectionPlane, retargetPose, sameDirection, screenLength, screenPoint, snapAt, squaredUp, startPosition, targetBoundary, trackDoubleTaps, trackTaps, useContentBox, useRetarget, useSectionStore, useTapGuard, useViewerControls, viewKind, viewUp, viewVector, visualSurfaces };