@toolpath/viewer 1.1.1 → 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';
@@ -192,6 +192,603 @@ 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
+ /** 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;
207
+ }
208
+ /**
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.
227
+ */
228
+ declare const SectionView: ({ geometry, box, plane, theme, showHandle, onDrag, }: SectionViewProps) => react.JSX.Element;
229
+
230
+ interface SectionToolProps {
231
+ theme?: Partial<ViewerTheme>;
232
+ }
233
+ /**
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.
249
+ */
250
+ declare const SectionTool: ({ theme }: SectionToolProps) => react.JSX.Element | null;
251
+
252
+ type Projection = 'orthographic' | 'perspective';
253
+ type ViewerCamera = OrthographicCamera | PerspectiveCamera;
254
+ interface ViewportSize {
255
+ readonly width: number;
256
+ readonly height: number;
257
+ }
258
+ /** Vertical field of view, in degrees, for the perspective camera. */
259
+ declare const PERSPECTIVE_FOV = 30;
260
+ /** Padding around the framed bounds, as a multiple of its radius. */
261
+ declare const DEFAULT_FIT_MARGIN = 1.2;
262
+ /** Marks scene furniture — grid, axes — that the camera should not frame. */
263
+ declare const EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
264
+ /**
265
+ * What the camera frames: a bounding *sphere*, not a box.
266
+ *
267
+ * A sphere makes framing rotation-invariant — the part stays fully visible from
268
+ * every angle, and a resize never needs to know the current pose. Framing
269
+ * per-axis box dimensions instead means recomputing the camera distance on
270
+ * every resize, and still clipping on some orientations.
271
+ */
272
+ interface SceneBounds {
273
+ readonly center: Vector3;
274
+ readonly radius: number;
275
+ }
276
+ /**
277
+ * Bounds for a scene with nothing in it. A viewer is mounted before it has a
278
+ * part, and a camera with a zero-size frustum renders nothing at all.
279
+ */
280
+ declare function defaultBounds(): SceneBounds;
281
+ /**
282
+ * A container can be laid out at zero size, and a degenerate aspect ratio
283
+ * produces a `NaN` projection matrix that never recovers. Fall back to square.
284
+ */
285
+ declare function aspectRatio(size: ViewportSize): number;
286
+ /**
287
+ * A world length that covers `pixels` on screen at `point`.
288
+ *
289
+ * Anything sized in world units is a thumbnail on a plate and a wall on an
290
+ * insert; the whole reason a control is measured this way is that it is a
291
+ * control rather than part of the model. The section handle and the orbit
292
+ * target marker both size themselves with it, which is why it lives here with
293
+ * the camera rather than beside either of them.
294
+ *
295
+ * The two projections answer differently on purpose. An orthographic frustum
296
+ * covers the same world height wherever the point is, so distance does not
297
+ * enter; a perspective one covers more the further away it looks, so it does.
298
+ */
299
+ declare function screenLength(camera: ViewerCamera, point: Vector3, viewport: ViewportSize, pixels: number): number;
300
+ declare function boundsFromBox(box: Box3): SceneBounds;
301
+ /**
302
+ * The bounds of everything worth framing under `root`.
303
+ *
304
+ * Skips objects flagged with {@link EXCLUDE_FROM_FRAME} and their descendants:
305
+ * a 100 mm grid around a 6 mm part would otherwise frame the grid, and the part
306
+ * would be a speck.
307
+ */
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;
316
+ /**
317
+ * The distance at which a sphere of `radius` fits inside a perspective frustum.
318
+ * Uses the narrower of the two field-of-view angles, so a portrait viewport
319
+ * frames on width and a landscape one on height.
320
+ */
321
+ declare function perspectiveFitDistance(fovDegrees: number, aspect: number, radius: number): number;
322
+ /**
323
+ * Half-height of an orthographic frustum that fits a sphere of `radius`. A
324
+ * portrait viewport grows the height so the width still clears the sphere.
325
+ */
326
+ declare function orthographicHalfHeight(aspect: number, radius: number): number;
327
+ declare function fitDistance(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): number;
328
+ declare function startPosition(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): Vector3;
329
+ /**
330
+ * How far out the wheel may pull, as a fraction of the fitted framing: the part
331
+ * may shrink to a quarter of the view and no further. Past that it is a speck
332
+ * with no cue that Fit is the way back.
333
+ */
334
+ declare const MIN_FRAME_RATIO = 0.25;
335
+ /**
336
+ * How far in the wheel may push, as a multiple of the fitted framing.
337
+ *
338
+ * Both ratios are the legacy viewer's `minZoom` 0.25 / `maxZoom` 10
339
+ * (`three-object.tsx:289-311`), which transfer almost exactly: legacy's
340
+ * orthographic frustum was `largestDimension * 2` tall and this one is the
341
+ * fitted bounding sphere, which for a cube agree within a few percent. So the
342
+ * numbers are the ones that shipped, and here they carry a meaning they did not
343
+ * have there — `zoom` 1 *is* the fitted framing, so 0.25 and 10 say exactly
344
+ * what a reader thinks they say.
345
+ */
346
+ declare const MAX_FRAME_RATIO = 10;
347
+ /** How far the camera may travel, in the two units the two cameras travel in. */
348
+ interface CameraLimits {
349
+ readonly minZoom: number;
350
+ readonly maxZoom: number;
351
+ readonly minDistance: number;
352
+ readonly maxDistance: number;
353
+ }
354
+ /**
355
+ * The clamps for a scene, so the wheel cannot leave the viewport empty.
356
+ *
357
+ * Without them it can, from either end, and both cameras can do it: an
358
+ * orthographic `camera.zoom` reaches 1e30 in sixty notches, and a perspective
359
+ * camera dives inside the part in eight, because `minDistance` defaults to
360
+ * `Number.EPSILON`. Fit recovers from both — but Fit is a double middle click
361
+ * and nothing on screen says so, which makes it a way out rather than an
362
+ * answer.
363
+ *
364
+ * One rule for both cameras: the wheel may take the part from a quarter of its
365
+ * fitted size to ten times it. Under an orthographic camera that scale *is* the
366
+ * frustum, so the ratios land on `zoom`; under a perspective camera apparent
367
+ * size is the inverse of distance, so they land on `distance` — which is why
368
+ * they swap sides between the two.
369
+ *
370
+ * Pure, and derived from `bounds`, so it re-derives wherever the scene is
371
+ * re-measured rather than being sampled once at mount.
372
+ *
373
+ * `framed` is what the view has been framed *on*, when that is not the whole
374
+ * scene — `frameBox` on a single feature. The band is then taken about that
375
+ * framing as well as about the scene's, because a band centred on the part is
376
+ * the wrong band for a view of a 3 mm hole in it: ten times the *part's* fitted
377
+ * size can be less than once the hole's, so the framing the caller asked for sat
378
+ * outside the clamps meant to keep it in view. Under an orthographic camera
379
+ * `zoom` is measured against a frustum still sized to the scene, so the framing
380
+ * enters as the zoom ratio it takes to reach; under a perspective one it enters
381
+ * as its own fitted distance.
382
+ *
383
+ * Widened to the union of the two rather than moved onto `framed`, and that is
384
+ * the whole of why this takes both. A band that simply followed the last framing
385
+ * would take the part away: framing a hole would put "far enough out to see what
386
+ * the hole is in" past `maxDistance`, so the wheel could no longer get back to
387
+ * the part it had just been looking at, and Fit — a gesture with nothing on
388
+ * screen to say so — would be the only way. Reaching further in must not cost
389
+ * the reach back out, so each end takes whichever bound is looser. With no
390
+ * `framed`, or one the size of the scene, both ends collapse to the scene's own
391
+ * and this is the function it was before.
392
+ */
393
+ declare function cameraLimits(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number, framed?: SceneBounds): CameraLimits;
394
+ /**
395
+ * The box the orbit target is confined to — the fitted framing, several frames
396
+ * over. Written into `into` so the per-frame path allocates nothing.
397
+ */
398
+ declare function targetBoundary(bounds: SceneBounds, into: Box3, margin?: number): Box3;
399
+ /**
400
+ * Points an existing camera's frustum at `bounds` for the current viewport.
401
+ * Pose is untouched — that belongs to the controls.
402
+ */
403
+ declare function applyProjection(camera: ViewerCamera, size: ViewportSize, bounds: SceneBounds, margin?: number): void;
404
+ /**
405
+ * The world-up convention. The part data is Z-up (millimetres, no conversion) —
406
+ * not the glTF-conventional Y-up — so the camera has to say so explicitly.
407
+ */
408
+ declare const CAD_CAMERA_UP: Vector3;
409
+ /**
410
+ * `up` re-squared against a view direction: the part of it perpendicular to
411
+ * `view`, written into `into`.
412
+ *
413
+ * This is what keeps the horizon level while a free orbit carries the camera
414
+ * over a pole — the up vector follows the view instead of flipping. The
415
+ * controls run it on every update, which is what makes the next sentence the
416
+ * important one.
417
+ *
418
+ * **It is path-dependent.** It is a projection, so it discards the component it
419
+ * removes and cannot put it back: running it at one view and then another does
420
+ * not give what running it once at the second view would. So a camera carries
421
+ * the roll of every pose it has passed through, and any pose that is meant to
422
+ * be canonical — the opening frame, a reset, a named view — has to say what
423
+ * `up` is rather than inherit it. `viewer.tsx` does, at `resetContent`. The
424
+ * test beside this pins both halves.
425
+ *
426
+ * `into` may be `view` or `up` — three.js `crossVectors` reads all six
427
+ * components before it writes any, and the intermediate is this module's own
428
+ * scratch, so writing the result over an input is safe. Said here because this
429
+ * is exported and an out-parameter invites the question; the test beside this
430
+ * pins it, so a three.js change cannot quietly turn the answer around.
431
+ *
432
+ * `view` and `up` being parallel is the one degenerate case: their cross is
433
+ * zero, `normalize` leaves it zero, and the result is a zero vector rather than
434
+ * an error. A caller with a free-orbiting camera reaches that at the pole,
435
+ * which is what `freeOrbit` and a squared `up` at every canonical pose exist to
436
+ * keep it away from.
437
+ */
438
+ declare function adaptedUp(view: Vector3, up: Vector3, into: Vector3): Vector3;
439
+ /** Named viewing directions, as unit vectors from the part toward the camera. */
440
+ declare const cadViewDirections: {
441
+ readonly front: Vector3;
442
+ readonly back: Vector3;
443
+ readonly left: Vector3;
444
+ readonly right: Vector3;
445
+ readonly top: Vector3;
446
+ readonly bottom: Vector3;
447
+ readonly isometric: Vector3;
448
+ };
449
+ type ViewerView = keyof typeof cadViewDirections;
450
+ /** The current orbit direction, so Fit can retain the direction being looked from. */
451
+ declare function currentViewDirection(camera: ViewerCamera, target: Vector3, into: Vector3): Vector3;
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
+
195
792
  interface ArrowPlacement {
196
793
  /** Where the arrow's tip sits: outside the box, on the direction's ray. */
197
794
  readonly tip: Vector3;
@@ -398,200 +995,6 @@ type SurfaceOf = ReadonlyMap<number, number>;
398
995
  */
399
996
  declare function visualSurfaces(regions: readonly PartModelRegion[]): SurfaceOf;
400
997
 
401
- type Projection = 'orthographic' | 'perspective';
402
- type ViewerCamera = OrthographicCamera | PerspectiveCamera;
403
- interface ViewportSize {
404
- readonly width: number;
405
- readonly height: number;
406
- }
407
- /** Vertical field of view, in degrees, for the perspective camera. */
408
- declare const PERSPECTIVE_FOV = 30;
409
- /** Padding around the framed bounds, as a multiple of its radius. */
410
- declare const DEFAULT_FIT_MARGIN = 1.2;
411
- /** Marks scene furniture — grid, axes — that the camera should not frame. */
412
- declare const EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
413
- /**
414
- * What the camera frames: a bounding *sphere*, not a box.
415
- *
416
- * A sphere makes framing rotation-invariant — the part stays fully visible from
417
- * every angle, and a resize never needs to know the current pose. Framing
418
- * per-axis box dimensions instead means recomputing the camera distance on
419
- * every resize, and still clipping on some orientations.
420
- */
421
- interface SceneBounds {
422
- readonly center: Vector3;
423
- readonly radius: number;
424
- }
425
- /**
426
- * Bounds for a scene with nothing in it. A viewer is mounted before it has a
427
- * part, and a camera with a zero-size frustum renders nothing at all.
428
- */
429
- declare function defaultBounds(): SceneBounds;
430
- /**
431
- * A container can be laid out at zero size, and a degenerate aspect ratio
432
- * produces a `NaN` projection matrix that never recovers. Fall back to square.
433
- */
434
- declare function aspectRatio(size: ViewportSize): number;
435
- /**
436
- * A world length that covers `pixels` on screen at `point`.
437
- *
438
- * Anything sized in world units is a thumbnail on a plate and a wall on an
439
- * insert; the whole reason a control is measured this way is that it is a
440
- * control rather than part of the model. The section handle and the orbit
441
- * target marker both size themselves with it, which is why it lives here with
442
- * the camera rather than beside either of them.
443
- *
444
- * The two projections answer differently on purpose. An orthographic frustum
445
- * covers the same world height wherever the point is, so distance does not
446
- * enter; a perspective one covers more the further away it looks, so it does.
447
- */
448
- declare function screenLength(camera: ViewerCamera, point: Vector3, viewport: ViewportSize, pixels: number): number;
449
- declare function boundsFromBox(box: Box3): SceneBounds;
450
- /**
451
- * The bounds of everything worth framing under `root`.
452
- *
453
- * Skips objects flagged with {@link EXCLUDE_FROM_FRAME} and their descendants:
454
- * a 100 mm grid around a 6 mm part would otherwise frame the grid, and the part
455
- * would be a speck.
456
- */
457
- declare function contentBounds(root: Object3D, into: Box3): SceneBounds;
458
- /**
459
- * The distance at which a sphere of `radius` fits inside a perspective frustum.
460
- * Uses the narrower of the two field-of-view angles, so a portrait viewport
461
- * frames on width and a landscape one on height.
462
- */
463
- declare function perspectiveFitDistance(fovDegrees: number, aspect: number, radius: number): number;
464
- /**
465
- * Half-height of an orthographic frustum that fits a sphere of `radius`. A
466
- * portrait viewport grows the height so the width still clears the sphere.
467
- */
468
- declare function orthographicHalfHeight(aspect: number, radius: number): number;
469
- declare function fitDistance(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): number;
470
- declare function startPosition(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): Vector3;
471
- /**
472
- * How far out the wheel may pull, as a fraction of the fitted framing: the part
473
- * may shrink to a quarter of the view and no further. Past that it is a speck
474
- * with no cue that Fit is the way back.
475
- */
476
- declare const MIN_FRAME_RATIO = 0.25;
477
- /**
478
- * How far in the wheel may push, as a multiple of the fitted framing.
479
- *
480
- * Both ratios are the legacy viewer's `minZoom` 0.25 / `maxZoom` 10
481
- * (`three-object.tsx:289-311`), which transfer almost exactly: legacy's
482
- * orthographic frustum was `largestDimension * 2` tall and this one is the
483
- * fitted bounding sphere, which for a cube agree within a few percent. So the
484
- * numbers are the ones that shipped, and here they carry a meaning they did not
485
- * have there — `zoom` 1 *is* the fitted framing, so 0.25 and 10 say exactly
486
- * what a reader thinks they say.
487
- */
488
- declare const MAX_FRAME_RATIO = 10;
489
- /** How far the camera may travel, in the two units the two cameras travel in. */
490
- interface CameraLimits {
491
- readonly minZoom: number;
492
- readonly maxZoom: number;
493
- readonly minDistance: number;
494
- readonly maxDistance: number;
495
- }
496
- /**
497
- * The clamps for a scene, so the wheel cannot leave the viewport empty.
498
- *
499
- * Without them it can, from either end, and both cameras can do it: an
500
- * orthographic `camera.zoom` reaches 1e30 in sixty notches, and a perspective
501
- * camera dives inside the part in eight, because `minDistance` defaults to
502
- * `Number.EPSILON`. Fit recovers from both — but Fit is a double middle click
503
- * and nothing on screen says so, which makes it a way out rather than an
504
- * answer.
505
- *
506
- * One rule for both cameras: the wheel may take the part from a quarter of its
507
- * fitted size to ten times it. Under an orthographic camera that scale *is* the
508
- * frustum, so the ratios land on `zoom`; under a perspective camera apparent
509
- * size is the inverse of distance, so they land on `distance` — which is why
510
- * they swap sides between the two.
511
- *
512
- * Pure, and derived from `bounds`, so it re-derives wherever the scene is
513
- * re-measured rather than being sampled once at mount.
514
- *
515
- * `framed` is what the view has been framed *on*, when that is not the whole
516
- * scene — `frameBox` on a single feature. The band is then taken about that
517
- * framing as well as about the scene's, because a band centred on the part is
518
- * the wrong band for a view of a 3 mm hole in it: ten times the *part's* fitted
519
- * size can be less than once the hole's, so the framing the caller asked for sat
520
- * outside the clamps meant to keep it in view. Under an orthographic camera
521
- * `zoom` is measured against a frustum still sized to the scene, so the framing
522
- * enters as the zoom ratio it takes to reach; under a perspective one it enters
523
- * as its own fitted distance.
524
- *
525
- * Widened to the union of the two rather than moved onto `framed`, and that is
526
- * the whole of why this takes both. A band that simply followed the last framing
527
- * would take the part away: framing a hole would put "far enough out to see what
528
- * the hole is in" past `maxDistance`, so the wheel could no longer get back to
529
- * the part it had just been looking at, and Fit — a gesture with nothing on
530
- * screen to say so — would be the only way. Reaching further in must not cost
531
- * the reach back out, so each end takes whichever bound is looser. With no
532
- * `framed`, or one the size of the scene, both ends collapse to the scene's own
533
- * and this is the function it was before.
534
- */
535
- declare function cameraLimits(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number, framed?: SceneBounds): CameraLimits;
536
- /**
537
- * The box the orbit target is confined to — the fitted framing, several frames
538
- * over. Written into `into` so the per-frame path allocates nothing.
539
- */
540
- declare function targetBoundary(bounds: SceneBounds, into: Box3, margin?: number): Box3;
541
- /**
542
- * Points an existing camera's frustum at `bounds` for the current viewport.
543
- * Pose is untouched — that belongs to the controls.
544
- */
545
- declare function applyProjection(camera: ViewerCamera, size: ViewportSize, bounds: SceneBounds, margin?: number): void;
546
- /**
547
- * The world-up convention. The part data is Z-up (millimetres, no conversion) —
548
- * not the glTF-conventional Y-up — so the camera has to say so explicitly.
549
- */
550
- declare const CAD_CAMERA_UP: Vector3;
551
- /**
552
- * `up` re-squared against a view direction: the part of it perpendicular to
553
- * `view`, written into `into`.
554
- *
555
- * This is what keeps the horizon level while a free orbit carries the camera
556
- * over a pole — the up vector follows the view instead of flipping. The
557
- * controls run it on every update, which is what makes the next sentence the
558
- * important one.
559
- *
560
- * **It is path-dependent.** It is a projection, so it discards the component it
561
- * removes and cannot put it back: running it at one view and then another does
562
- * not give what running it once at the second view would. So a camera carries
563
- * the roll of every pose it has passed through, and any pose that is meant to
564
- * be canonical — the opening frame, a reset, a named view — has to say what
565
- * `up` is rather than inherit it. `viewer.tsx` does, at `resetContent`. The
566
- * test beside this pins both halves.
567
- *
568
- * `into` may be `view` or `up` — three.js `crossVectors` reads all six
569
- * components before it writes any, and the intermediate is this module's own
570
- * scratch, so writing the result over an input is safe. Said here because this
571
- * is exported and an out-parameter invites the question; the test beside this
572
- * pins it, so a three.js change cannot quietly turn the answer around.
573
- *
574
- * `view` and `up` being parallel is the one degenerate case: their cross is
575
- * zero, `normalize` leaves it zero, and the result is a zero vector rather than
576
- * an error. A caller with a free-orbiting camera reaches that at the pole,
577
- * which is what `freeOrbit` and a squared `up` at every canonical pose exist to
578
- * keep it away from.
579
- */
580
- declare function adaptedUp(view: Vector3, up: Vector3, into: Vector3): Vector3;
581
- /** Named viewing directions, as unit vectors from the part toward the camera. */
582
- declare const cadViewDirections: {
583
- readonly front: Vector3;
584
- readonly back: Vector3;
585
- readonly left: Vector3;
586
- readonly right: Vector3;
587
- readonly top: Vector3;
588
- readonly bottom: Vector3;
589
- readonly isometric: Vector3;
590
- };
591
- type ViewerView = keyof typeof cadViewDirections;
592
- /** The current orbit direction, so Fit can retain the direction being looked from. */
593
- declare function currentViewDirection(camera: ViewerCamera, target: Vector3, into: Vector3): Vector3;
594
-
595
998
  /**
596
999
  * Mouse and trackpad presets.
597
1000
  *
@@ -717,12 +1120,26 @@ interface ViewerControls {
717
1120
  * see a wall and then asks to zoom to it wants it closer, not re-oriented.
718
1121
  */
719
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;
720
1131
  }
721
1132
  interface ViewerHandle extends ViewerControls {
722
1133
  }
723
1134
  type VectorLike = Pick<Vector3, 'x' | 'y' | 'z'>;
724
1135
 
725
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;
726
1143
  /** Re-aims the orbit, or `null` when this viewer has the gesture turned off. */
727
1144
  type Retarget = ((point: VectorLike) => void) | null;
728
1145
  /**
@@ -1093,4 +1510,4 @@ declare const ORBIT_TARGET_FADE_MS = 1000;
1093
1510
  */
1094
1511
  declare function orbitTargetOpacity(sinceHold: number, fade?: number): number;
1095
1512
 
1096
- 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 };