@toolpath/viewer 2.2.1 → 2.3.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.
@@ -1,5 +1,5 @@
1
- import { P as PartModel, a as PartMeshRefs } from '../normalize--FiXUhO4.js';
2
- export { E as EnginePart, b as EnginePartProps, M as MIN_KERNEL_VERSION, c as assertSupportedKernelVersion, n as normalizePartReport, s as smoothRegionNormals } from '../normalize--FiXUhO4.js';
1
+ import { P as PartModel, a as PartMeshRefs } from '../normalize-C4Wj4IGK.js';
2
+ export { E as EnginePart, b as EnginePartProps, M as MIN_KERNEL_VERSION, c as assertSupportedKernelVersion, n as normalizePartReport, s as smoothRegionNormals } from '../normalize-C4Wj4IGK.js';
3
3
  import { BufferGeometry } from 'three';
4
4
  import 'react';
5
5
 
@@ -12,7 +12,7 @@ import {
12
12
  parsePartGeometry,
13
13
  partMeshAssets,
14
14
  smoothRegionNormals
15
- } from "../chunk-7S6SIYHL.js";
15
+ } from "../chunk-N54FHMTG.js";
16
16
  export {
17
17
  EnginePart,
18
18
  MIN_KERNEL_VERSION,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { P as PartModel, F as FeatureTag, d as PartPick, V as Vec3, R as RegionHighlight, e as ViewerTheme, S as SectionOptions, f as SectionState, g as PartModelRegion, T as TriangleRange, h as RegionIndex, i as PartModelFeature, j as FeatureType } from './normalize--FiXUhO4.js';
2
- export { A as AXES_PLANE_OFFSET, k as AXES_PLANE_SCALE, l as AxesPlane, B as BuildPickInput, C as CANDIDATE_WEIGHT, m as CAP_HATCH, D as DEFAULT_SECTION_NORMAL, o as DEFAULT_THEME, p as DIRECTION_COLORS, q as DISABLED_SECTION, E as EnginePart, r as FeatureHighlight, t as FocusOptions, H as HANDLE_PIXELS, u as HIGHLIGHT_COLORS, v as HIGHLIGHT_WEIGHT, w as HOVER_WEIGHT, x as HighlightLayers, K as KnownFeatureType, y as KnownShapeKind, N as NO_MODIFIERS, O as OUTLINE_SCALE, z as PICKED_SURFACE_LABEL, G as PREVIEW_SCALE, I as PartDisplay, J as PartMesh, L as PartMeshProps, a as PartMeshRefs, Q as PartObject, U as PickModifiers, W as PointerLocation, X as REGION_ATTRIBUTE, Y as RegionPaint, Z as SECTION_GIZMO_FRAME_SCALE, _ as SECTION_RENDER_ORDER, $ as SectionAnchor, a0 as SectionBounds, a1 as SectionPlacement, a2 as ShapeKind, a3 as SurfaceHit, a4 as applyHighlightLayers, a5 as axesPlanes, a6 as buildPick, a7 as buildRegionAttribute, a8 as buildRegionTexels, a9 as createPart, aa as directionColor, ab as dragPlane, ac as focusForPick, ad as hitUnderRay, n as normalizePartReport, ae as pickedStartDepth, af as resolveTheme, ag as sectionAnchor, ah as sectionBounds, ai as sectionConstant, aj as sectionDepth, ak as sectionDepthConstant, al as sectionDepthRange, am as sectionFromPick, an as sectionMeasurement, ao as sectionOffset, ap as sectionOptionsFromState, aq as sectionPlane, s as smoothRegionNormals, ar as surfaceUnderRay, as as themesEqual, at as viewDirection } from './normalize--FiXUhO4.js';
3
- import { BufferGeometry, Box3, CanvasTexture, Plane, Vector3, OrthographicCamera, PerspectiveCamera, Object3D, Matrix4, Vector2, Ray, ShaderMaterial, Texture, Color, MeshBasicMaterial, WebGLRenderTarget } from 'three';
1
+ import { P as PartModel, F as FeatureTag, d as PartPick, V as Vec3, R as RegionHighlight, e as ViewerTheme, S as SectionOptions, f as SectionState, g as ViewerCamera, h as ViewportSize, i as PartModelRegion, C as CameraLimits, j as ViewerView, k as Projection, T as TriangleRange, l as RegionIndex, m as PartModelFeature, o as FeatureType } from './normalize-C4Wj4IGK.js';
2
+ export { A as AXES_PLANE_OFFSET, p as AXES_PLANE_SCALE, q as AxesPlane, B as BuildPickInput, r as CAD_CAMERA_UP, t as CANDIDATE_WEIGHT, u as CAP_HATCH, D as DEFAULT_FIT_MARGIN, v as DEFAULT_SECTION_NORMAL, w as DEFAULT_THEME, x as DIRECTION_COLORS, y as DISABLED_SECTION, z as EXCLUDE_FROM_FRAME, E as EnginePart, G as FeatureHighlight, H as FocusOptions, I as HANDLE_PIXELS, J as HIGHLIGHT_COLORS, K as HIGHLIGHT_WEIGHT, L as HOVER_WEIGHT, N as HighlightLayers, O as KnownFeatureType, Q as KnownShapeKind, U as MAX_FRAME_RATIO, W as MIN_FRAME_RATIO, X as NO_MODIFIERS, Y as OUTLINE_SCALE, Z as PERSPECTIVE_FOV, _ as PICKED_SURFACE_LABEL, $ as PREVIEW_SCALE, a0 as PartDisplay, a1 as PartMesh, a2 as PartMeshProps, a as PartMeshRefs, a3 as PartObject, a4 as PickModifiers, a5 as PointerLocation, a6 as REGION_ATTRIBUTE, a7 as RegionPaint, a8 as SECTION_GIZMO_FRAME_SCALE, a9 as SECTION_RENDER_ORDER, aa as SceneBounds, ab as SectionAnchor, ac as SectionBounds, ad as SectionPlacement, ae as ShapeKind, af as SharpCorner, ag as SharpCornerOptions, ah as SurfaceCreases, ai as SurfaceHit, aj as adaptedUp, ak as applyHighlightLayers, al as applyProjection, am as aspectRatio, an as axesPlanes, ao as boundsFromBox, ap as buildPick, aq as buildRegionAttribute, ar as buildRegionTexels, as as cadViewDirections, at as cameraLimits, au as contentBounds, av as createPart, aw as currentViewDirection, ax as defaultBounds, ay as directionColor, az as dragPlane, aA as excludedFromFrame, aB as findSharpCorners, aC as fitDistance, aD as focusForPick, aE as hitUnderRay, n as normalizePartReport, aF as orthographicHalfHeight, aG as perspectiveFitDistance, aH as pickedStartDepth, aI as resolveTheme, aJ as screenLength, aK as sectionAnchor, aL as sectionBounds, aM as sectionConstant, aN as sectionDepth, aO as sectionDepthConstant, aP as sectionDepthRange, aQ as sectionFromPick, aR as sectionMeasurement, aS as sectionOffset, aT as sectionOptionsFromState, aU as sectionPlane, aV as sharpCornerPositions, s as smoothRegionNormals, aW as startPosition, aX as surfaceCreases, aY as surfaceUnderRay, aZ as targetBoundary, a_ as themesEqual, a$ as viewDirection } from './normalize-C4Wj4IGK.js';
3
+ import { BufferGeometry, Box3, CanvasTexture, Plane, Vector3, Matrix4, Vector2, Ray, ShaderMaterial, Texture, Color, MeshBasicMaterial, WebGLRenderTarget } from 'three';
4
4
  import * as react from 'react';
5
5
  import { ReactNode, PropsWithChildren, RefObject, CSSProperties } from 'react';
6
6
  import CameraControls from 'camera-controls';
@@ -91,6 +91,8 @@ interface ViewerToolbarControls {
91
91
  tools?: ViewerToolbarControl;
92
92
  directions?: ViewerToolbarControl;
93
93
  dfm?: ViewerToolbarControl;
94
+ /** The DFM rules' sharp corners, drawn on the part: see `sharpCorners` on `<PartMesh>`. */
95
+ sharpCorners?: ViewerToolbarControl;
94
96
  hover?: ViewerToolbarControl;
95
97
  focus?: ViewerToolbarControl;
96
98
  wireframe?: ViewerToolbarControl;
@@ -133,6 +135,7 @@ declare const ViewerToolbar: (({ className, children }: ViewerToolbarProps) => r
133
135
  ToolsButton: () => react.JSX.Element;
134
136
  DirectionsButton: () => react.JSX.Element;
135
137
  DfmButton: () => react.JSX.Element;
138
+ SharpCornersButton: () => react.JSX.Element;
136
139
  HoverButton: () => react.JSX.Element;
137
140
  FocusButton: () => react.JSX.Element;
138
141
  WireframeButton: () => react.JSX.Element;
@@ -534,207 +537,6 @@ interface SectionToolProps {
534
537
  */
535
538
  declare const SectionTool: ({ theme }: SectionToolProps) => react.JSX.Element | null;
536
539
 
537
- type Projection = 'orthographic' | 'perspective';
538
- type ViewerCamera = OrthographicCamera | PerspectiveCamera;
539
- interface ViewportSize {
540
- readonly width: number;
541
- readonly height: number;
542
- }
543
- /** Vertical field of view, in degrees, for the perspective camera. */
544
- declare const PERSPECTIVE_FOV = 30;
545
- /** Padding around the framed bounds, as a multiple of its radius. */
546
- declare const DEFAULT_FIT_MARGIN = 1.2;
547
- /** Marks scene furniture — grid, axes — that the camera should not frame. */
548
- declare const EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
549
- /**
550
- * What the camera frames: a bounding *sphere*, not a box.
551
- *
552
- * A sphere makes framing rotation-invariant — the part stays fully visible from
553
- * every angle, and a resize never needs to know the current pose. Framing
554
- * per-axis box dimensions instead means recomputing the camera distance on
555
- * every resize, and still clipping on some orientations.
556
- */
557
- interface SceneBounds {
558
- readonly center: Vector3;
559
- readonly radius: number;
560
- }
561
- /**
562
- * Bounds for a scene with nothing in it. A viewer is mounted before it has a
563
- * part, and a camera with a zero-size frustum renders nothing at all.
564
- */
565
- declare function defaultBounds(): SceneBounds;
566
- /**
567
- * A container can be laid out at zero size, and a degenerate aspect ratio
568
- * produces a `NaN` projection matrix that never recovers. Fall back to square.
569
- */
570
- declare function aspectRatio(size: ViewportSize): number;
571
- /**
572
- * A world length that covers `pixels` on screen at `point`.
573
- *
574
- * Anything sized in world units is a thumbnail on a plate and a wall on an
575
- * insert; the whole reason a control is measured this way is that it is a
576
- * control rather than part of the model. The section handle and the orbit
577
- * target marker both size themselves with it, which is why it lives here with
578
- * the camera rather than beside either of them.
579
- *
580
- * The two projections answer differently on purpose. An orthographic frustum
581
- * covers the same world height wherever the point is, so distance does not
582
- * enter; a perspective one covers more the further away it looks, so it does.
583
- */
584
- declare function screenLength(camera: ViewerCamera, point: Vector3, viewport: ViewportSize, pixels: number): number;
585
- declare function boundsFromBox(box: Box3): SceneBounds;
586
- /**
587
- * The bounds of everything worth framing under `root`.
588
- *
589
- * Skips objects flagged with {@link EXCLUDE_FROM_FRAME} and their descendants:
590
- * a 100 mm grid around a 6 mm part would otherwise frame the grid, and the part
591
- * would be a speck.
592
- */
593
- declare function contentBounds(root: Object3D, into: Box3): SceneBounds;
594
- /**
595
- * Whether `object`, or anything between it and `root`, carries
596
- * {@link EXCLUDE_FROM_FRAME}. The flag is set on an overlay's outermost group
597
- * and means the whole subtree, so the walk goes up rather than reading the
598
- * object alone.
599
- */
600
- declare function excludedFromFrame(object: Object3D, root: Object3D): boolean;
601
- /**
602
- * The distance at which a sphere of `radius` fits inside a perspective frustum.
603
- * Uses the narrower of the two field-of-view angles, so a portrait viewport
604
- * frames on width and a landscape one on height.
605
- */
606
- declare function perspectiveFitDistance(fovDegrees: number, aspect: number, radius: number): number;
607
- /**
608
- * Half-height of an orthographic frustum that fits a sphere of `radius`. A
609
- * portrait viewport grows the height so the width still clears the sphere.
610
- */
611
- declare function orthographicHalfHeight(aspect: number, radius: number): number;
612
- declare function fitDistance(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): number;
613
- declare function startPosition(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): Vector3;
614
- /**
615
- * How far out the wheel may pull, as a fraction of the fitted framing: the part
616
- * may shrink to a quarter of the view and no further. Past that it is a speck
617
- * with no cue that Fit is the way back.
618
- */
619
- declare const MIN_FRAME_RATIO = 0.25;
620
- /**
621
- * How far in the wheel may push, as a multiple of the fitted framing.
622
- *
623
- * Both ratios are the legacy viewer's `minZoom` 0.25 / `maxZoom` 10
624
- * (`three-object.tsx:289-311`), which transfer almost exactly: legacy's
625
- * orthographic frustum was `largestDimension * 2` tall and this one is the
626
- * fitted bounding sphere, which for a cube agree within a few percent. So the
627
- * numbers are the ones that shipped, and here they carry a meaning they did not
628
- * have there — `zoom` 1 *is* the fitted framing, so 0.25 and 10 say exactly
629
- * what a reader thinks they say.
630
- */
631
- declare const MAX_FRAME_RATIO = 10;
632
- /** How far the camera may travel, in the two units the two cameras travel in. */
633
- interface CameraLimits {
634
- readonly minZoom: number;
635
- readonly maxZoom: number;
636
- readonly minDistance: number;
637
- readonly maxDistance: number;
638
- }
639
- /**
640
- * The clamps for a scene, so the wheel cannot leave the viewport empty.
641
- *
642
- * Without them it can, from either end, and both cameras can do it: an
643
- * orthographic `camera.zoom` reaches 1e30 in sixty notches, and a perspective
644
- * camera dives inside the part in eight, because `minDistance` defaults to
645
- * `Number.EPSILON`. Fit recovers from both — but Fit is a double middle click
646
- * and nothing on screen says so, which makes it a way out rather than an
647
- * answer.
648
- *
649
- * One rule for both cameras: the wheel may take the part from a quarter of its
650
- * fitted size to ten times it. Under an orthographic camera that scale *is* the
651
- * frustum, so the ratios land on `zoom`; under a perspective camera apparent
652
- * size is the inverse of distance, so they land on `distance` — which is why
653
- * they swap sides between the two.
654
- *
655
- * Pure, and derived from `bounds`, so it re-derives wherever the scene is
656
- * re-measured rather than being sampled once at mount.
657
- *
658
- * `framed` is what the view has been framed *on*, when that is not the whole
659
- * scene — `frameBox` on a single feature. The band is then taken about that
660
- * framing as well as about the scene's, because a band centred on the part is
661
- * the wrong band for a view of a 3 mm hole in it: ten times the *part's* fitted
662
- * size can be less than once the hole's, so the framing the caller asked for sat
663
- * outside the clamps meant to keep it in view. Under an orthographic camera
664
- * `zoom` is measured against a frustum still sized to the scene, so the framing
665
- * enters as the zoom ratio it takes to reach; under a perspective one it enters
666
- * as its own fitted distance.
667
- *
668
- * Widened to the union of the two rather than moved onto `framed`, and that is
669
- * the whole of why this takes both. A band that simply followed the last framing
670
- * would take the part away: framing a hole would put "far enough out to see what
671
- * the hole is in" past `maxDistance`, so the wheel could no longer get back to
672
- * the part it had just been looking at, and Fit — a gesture with nothing on
673
- * screen to say so — would be the only way. Reaching further in must not cost
674
- * the reach back out, so each end takes whichever bound is looser. With no
675
- * `framed`, or one the size of the scene, both ends collapse to the scene's own
676
- * and this is the function it was before.
677
- */
678
- declare function cameraLimits(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number, framed?: SceneBounds): CameraLimits;
679
- /**
680
- * The box the orbit target is confined to — the fitted framing, several frames
681
- * over. Written into `into` so the per-frame path allocates nothing.
682
- */
683
- declare function targetBoundary(bounds: SceneBounds, into: Box3, margin?: number): Box3;
684
- /**
685
- * Points an existing camera's frustum at `bounds` for the current viewport.
686
- * Pose is untouched — that belongs to the controls.
687
- */
688
- declare function applyProjection(camera: ViewerCamera, size: ViewportSize, bounds: SceneBounds, margin?: number): void;
689
- /**
690
- * The world-up convention. The part data is Z-up (millimetres, no conversion) —
691
- * not the glTF-conventional Y-up — so the camera has to say so explicitly.
692
- */
693
- declare const CAD_CAMERA_UP: Vector3;
694
- /**
695
- * `up` re-squared against a view direction: the part of it perpendicular to
696
- * `view`, written into `into`.
697
- *
698
- * This is what keeps the horizon level while a free orbit carries the camera
699
- * over a pole — the up vector follows the view instead of flipping. The
700
- * controls run it on every update, which is what makes the next sentence the
701
- * important one.
702
- *
703
- * **It is path-dependent.** It is a projection, so it discards the component it
704
- * removes and cannot put it back: running it at one view and then another does
705
- * not give what running it once at the second view would. So a camera carries
706
- * the roll of every pose it has passed through, and any pose that is meant to
707
- * be canonical — the opening frame, a reset, a named view — has to say what
708
- * `up` is rather than inherit it. `viewer.tsx` does, at `resetContent`. The
709
- * test beside this pins both halves.
710
- *
711
- * `into` may be `view` or `up` — three.js `crossVectors` reads all six
712
- * components before it writes any, and the intermediate is this module's own
713
- * scratch, so writing the result over an input is safe. Said here because this
714
- * is exported and an out-parameter invites the question; the test beside this
715
- * pins it, so a three.js change cannot quietly turn the answer around.
716
- *
717
- * `view` and `up` being parallel is the one degenerate case: their cross is
718
- * zero, `normalize` leaves it zero, and the result is a zero vector rather than
719
- * an error. A caller with a free-orbiting camera reaches that at the pole,
720
- * which is what `freeOrbit` and a squared `up` at every canonical pose exist to
721
- * keep it away from.
722
- */
723
- declare function adaptedUp(view: Vector3, up: Vector3, into: Vector3): Vector3;
724
- /** Named viewing directions, as unit vectors from the part toward the camera. */
725
- declare const cadViewDirections: {
726
- readonly front: Vector3;
727
- readonly back: Vector3;
728
- readonly left: Vector3;
729
- readonly right: Vector3;
730
- readonly top: Vector3;
731
- readonly bottom: Vector3;
732
- readonly isometric: Vector3;
733
- };
734
- type ViewerView = keyof typeof cadViewDirections;
735
- /** The current orbit direction, so Fit can retain the direction being looked from. */
736
- declare function currentViewDirection(camera: ViewerCamera, target: Vector3, into: Vector3): Vector3;
737
-
738
540
  /**
739
541
  * Measuring, without a canvas: where a pointer snaps to, what two or three
740
542
  * points measure, and how that is written down. `measure-tool.tsx` is the
@@ -1977,4 +1779,4 @@ declare const ORBIT_TARGET_FADE_MS = 1000;
1977
1779
  */
1978
1780
  declare function orbitTargetOpacity(sinceHold: number, fade?: number): number;
1979
1781
 
1980
- export { ANGLE_ARC_FRACTION, ANGLE_ARC_SEGMENTS, AXIS_COLORS, type AngleMeasurement, type ArrowPlacement, Axes, type AxesProps, type Axis, type AxisLock, BANANA_GAP, BANANA_MODEL_URL, Banana, type BananaProps, type BoxStockFigure, type BoxStockInput, type BoxStockPreview, CAD_CAMERA_UP, CHAMFER, CONTROL_SCHEME_OPTIONS, CadCameraControls, type CadCameraControlsProps, type CameraLimits, type CapMaterial, type CapUniforms, type ControlScheme, type ControlSchemeOption, type CubeZone, type CylinderStockFigure, type CylinderStockInput, type CylinderStockPreview, 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, type FeatureTriangles, FeatureType, Grid, type GridProps, type GridSpec, HoverCard, type HoverCardProps, 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, type PartContextValue, PartModel, PartModelFeature, PartModelRegion, PartPick, PartReportFormatError, type Projection, type RankingContext, RegionHighlight, RegionIndex, type Retarget, type RetargetPose, SNAP_MARKER_PIXELS, SNAP_PIXELS, type SceneBounds, SectionOptions, type SectionSample, SectionState, type SectionStore, SectionTool, type SectionToolProps, SectionView, type Snap, type SnapEdges, type SnapHit, type SnapKind, Stock, type StockAllowance, type StockAppearanceProps, type StockPosition, type StockProps, type StockSource, type SurfaceOf, TAP_SLOP, type TapPoint, type TapTracker, type ToolMark, ToolMarks, type ToolMarksProps, type ToolProfilePoint, TriangleRange, UnsupportedKernelVersionError, VIEW_NAMES, VIEW_SIGNS, Vec3, ViewCube, type ViewCubeProps, type ViewKind, type ViewName, Viewer, type ViewerCamera, type ViewerControls, type ViewerHandle, type ViewerProps, ViewerTheme, ViewerToolbar, type ViewerToolbarControl, type ViewerToolbarControls, type ViewerToolbarControlsProps, type ViewerToolbarProps, ViewerToolbarProvider, type ViewerView, type ViewportSize, adaptedUp, angleArc, angleArcRadius, angleAt, applyCapTheme, applyProjection, arrowPlacement, aspectRatio, bananaFrameBounds, bananaPosition, bestOwner, boundsFromBox, boxStockBounds, buildRegionIndex, cadViewDirections, cameraLimits, capHit, clipSegments, contentBounds, createCapMaterial, createMaskMaterial, createMaskTarget, createSectionStore, createStencilMaterials, cubeOutlineGeometry, cubeZones, currentViewDirection, cycleOwner, defaultBounds, deltaBetween, deltaLegs, directionHighlights, directionIndexOf, directionLabel, distanceBetween, excludedFromFrame, featureTriangles, featureTypeRank, fitDistance, fixedBoxStockBounds, formatDegrees, formatMillimetres, gridGeometry, gridSpec, groupByDirection, labelGeometry, labelTexture, lockToAxis, measurementFromPoints, measurementLabel, measurementLabelAnchor, midpoint, movedFar, nextMeasurementId, orbitTargetOpacity, orthographicHalfHeight, panelGeometry, perspectiveFitDistance, pointInContour, rankOwners, regionAdjacency, regionEdgesGeometry, regionNormals, resolveSectionPlane, retargetPose, sameDirection, sampleSection, screenLength, screenPoint, sectionContour, snapAt, squaredUp, startPosition, targetBoundary, trackDoubleTaps, trackTaps, useContentBox, usePartContext, useRetarget, useSectionStore, useTapGuard, useViewerControls, useViewerToolbar, viewKind, viewUp, viewVector, visualSurfaces };
1782
+ export { ANGLE_ARC_FRACTION, ANGLE_ARC_SEGMENTS, AXIS_COLORS, type AngleMeasurement, type ArrowPlacement, Axes, type AxesProps, type Axis, type AxisLock, BANANA_GAP, BANANA_MODEL_URL, Banana, type BananaProps, type BoxStockFigure, type BoxStockInput, type BoxStockPreview, CHAMFER, CONTROL_SCHEME_OPTIONS, CadCameraControls, type CadCameraControlsProps, CameraLimits, type CapMaterial, type CapUniforms, type ControlScheme, type ControlSchemeOption, type CubeZone, type CylinderStockFigure, type CylinderStockInput, type CylinderStockPreview, DELTA_DASH_PIXELS, DOUBLE_TAP_MS, type DeltaLeg, DirectionArrows, type DirectionArrowsProps, type DirectionGroup, type DistanceMeasurement, type DoubleTapPoint, type DoubleTapTracker, ExtendedCameraControls, type ExtendedCameraControlsOptions, FEATURE_TYPE_RANKS, FeatureTag, type FeatureTriangles, FeatureType, Grid, type GridProps, type GridSpec, HoverCard, type HoverCardProps, MEASURE_LABEL_CLASS, MEASURE_LINE_PIXELS, MEASURE_MARKER_PIXELS, MEASURE_RENDER_ORDER, 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, POINTS_PER_MEASUREMENT, type PartContextValue, PartModel, PartModelFeature, PartModelRegion, PartPick, PartReportFormatError, Projection, type RankingContext, RegionHighlight, RegionIndex, type Retarget, type RetargetPose, SNAP_MARKER_PIXELS, SNAP_PIXELS, SectionOptions, type SectionSample, SectionState, type SectionStore, SectionTool, type SectionToolProps, SectionView, type Snap, type SnapEdges, type SnapHit, type SnapKind, Stock, type StockAllowance, type StockAppearanceProps, type StockPosition, type StockProps, type StockSource, type SurfaceOf, TAP_SLOP, type TapPoint, type TapTracker, type ToolMark, ToolMarks, type ToolMarksProps, type ToolProfilePoint, TriangleRange, UnsupportedKernelVersionError, VIEW_NAMES, VIEW_SIGNS, Vec3, ViewCube, type ViewCubeProps, type ViewKind, type ViewName, Viewer, ViewerCamera, type ViewerControls, type ViewerHandle, type ViewerProps, ViewerTheme, ViewerToolbar, type ViewerToolbarControl, type ViewerToolbarControls, type ViewerToolbarControlsProps, type ViewerToolbarProps, ViewerToolbarProvider, ViewerView, ViewportSize, angleArc, angleArcRadius, angleAt, applyCapTheme, arrowPlacement, bananaFrameBounds, bananaPosition, bestOwner, boxStockBounds, buildRegionIndex, capHit, clipSegments, createCapMaterial, createMaskMaterial, createMaskTarget, createSectionStore, createStencilMaterials, cubeOutlineGeometry, cubeZones, cycleOwner, deltaBetween, deltaLegs, directionHighlights, directionIndexOf, directionLabel, distanceBetween, featureTriangles, featureTypeRank, fixedBoxStockBounds, formatDegrees, formatMillimetres, gridGeometry, gridSpec, groupByDirection, labelGeometry, labelTexture, lockToAxis, measurementFromPoints, measurementLabel, measurementLabelAnchor, midpoint, movedFar, nextMeasurementId, orbitTargetOpacity, panelGeometry, pointInContour, rankOwners, regionAdjacency, regionEdgesGeometry, regionNormals, resolveSectionPlane, retargetPose, sameDirection, sampleSection, screenPoint, sectionContour, snapAt, squaredUp, trackDoubleTaps, trackTaps, useContentBox, usePartContext, useRetarget, useSectionStore, useTapGuard, useViewerControls, useViewerToolbar, viewKind, viewUp, viewVector, visualSurfaces };
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ import {
99
99
  dragPlane,
100
100
  excludedFromFrame,
101
101
  featureTypeRank,
102
+ findSharpCorners,
102
103
  fitDistance,
103
104
  focusForPick,
104
105
  formatDegrees,
@@ -140,10 +141,12 @@ import {
140
141
  sectionOffset,
141
142
  sectionOptionsFromState,
142
143
  sectionPlane,
144
+ sharpCornerPositions,
143
145
  smoothRegionNormals,
144
146
  snapAt,
145
147
  squaredUp,
146
148
  startPosition,
149
+ surfaceCreases,
147
150
  surfaceUnderRay,
148
151
  targetBoundary,
149
152
  themesEqual,
@@ -160,7 +163,7 @@ import {
160
163
  viewUp,
161
164
  viewVector,
162
165
  visualSurfaces
163
- } from "./chunk-7S6SIYHL.js";
166
+ } from "./chunk-N54FHMTG.js";
164
167
 
165
168
  // src/render/feature-triangles.ts
166
169
  var EMPTY = { positions: new Float32Array(), normals: new Float32Array() };
@@ -241,7 +244,7 @@ var HoverCard = ({ pick, children, className: className2, offset = 16 }) => {
241
244
  import { createContext, useContext } from "react";
242
245
 
243
246
  // src/toolbar-icons.tsx
244
- import { jsx as jsx2 } from "react/jsx-runtime";
247
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
245
248
  var SvgIcon = ({ viewBox = "0 0 24 24", fill = "none", children }) => /* @__PURE__ */ jsx2(
246
249
  "svg",
247
250
  {
@@ -267,6 +270,10 @@ var BananaIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { viewBox: "0 0 576 512", f
267
270
  var ToolsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M9 2h6v7H9zM9.5 9v9.5l2.5 3.5 2.5-3.5V9M9.5 12l5 2.5M9.5 15.5l5 2.5" }) });
268
271
  var DirectionsIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M7 3v18m-4-4 4 4 4-4M17 21V3m-4 4 4-4 4 4" }) });
269
272
  var DfmIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M8.3 3h7.4L21 8.3v7.4L15.7 21H8.3L3 15.7V8.3zM12 7.5v5.5m0 3.5v.01" }) });
273
+ var SharpCornersIcon = () => /* @__PURE__ */ jsxs(SvgIcon, { children: [
274
+ /* @__PURE__ */ jsx2("path", { d: "M4 3v17h17" }),
275
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "15", r: "4" })
276
+ ] });
270
277
  var HoverIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v12H9l-5 4zm4 5h8m-8 3h5" }) });
271
278
  var FocusIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 4h16v16H4zM8 8h8v8H8zM2 12h4m12 0h4M12 2v4m0 12v4" }) });
272
279
  var WireframeIcon = () => /* @__PURE__ */ jsx2(SvgIcon, { children: /* @__PURE__ */ jsx2("path", { d: "M4 7l8-4 8 4v10l-8 4-8-4zM4 7l8 4 8-4M12 11v10M4 17l8-4 8 4M12 3v10" }) });
@@ -283,6 +290,7 @@ var ICONS = {
283
290
  tools: ToolsIcon,
284
291
  directions: DirectionsIcon,
285
292
  dfm: DfmIcon,
293
+ sharpCorners: SharpCornersIcon,
286
294
  hover: HoverIcon,
287
295
  focus: FocusIcon,
288
296
  wireframe: WireframeIcon,
@@ -295,7 +303,7 @@ var ToolbarIcon = ({ name }) => {
295
303
  };
296
304
 
297
305
  // src/viewer-toolbar.tsx
298
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
306
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
299
307
  var ViewerToolbarContext = createContext(null);
300
308
  var ViewerToolbarProvider = ({
301
309
  controls,
@@ -313,7 +321,7 @@ var ToolbarButton = ({ action, icon, label, toggle = true }) => {
313
321
  throw new Error(`<ViewerToolbar.${buttonName(action)}> requires a '${action}' toolbar control`);
314
322
  }
315
323
  const pressed = control.pressed ?? false;
316
- return /* @__PURE__ */ jsxs(
324
+ return /* @__PURE__ */ jsxs2(
317
325
  "button",
318
326
  {
319
327
  className: "viewer-toolbar-button",
@@ -385,6 +393,14 @@ var DfmButton = () => /* @__PURE__ */ jsx3(
385
393
  label: (pressed) => pressed ? "Hide DFM flags" : "Show DFM flags"
386
394
  }
387
395
  );
396
+ var SharpCornersButton = () => /* @__PURE__ */ jsx3(
397
+ ToolbarButton,
398
+ {
399
+ action: "sharpCorners",
400
+ icon: "sharpCorners",
401
+ label: (pressed) => pressed ? "Hide sharp corners" : "Show sharp corners"
402
+ }
403
+ );
388
404
  var HoverButton = () => /* @__PURE__ */ jsx3(
389
405
  ToolbarButton,
390
406
  {
@@ -444,6 +460,7 @@ var DefaultControls = () => {
444
460
  const analysis = [
445
461
  controls.directions ? /* @__PURE__ */ jsx3(DirectionsButton, {}, "directions") : null,
446
462
  controls.dfm ? /* @__PURE__ */ jsx3(DfmButton, {}, "dfm") : null,
463
+ controls.sharpCorners ? /* @__PURE__ */ jsx3(SharpCornersButton, {}, "sharpCorners") : null,
447
464
  controls.hover ? /* @__PURE__ */ jsx3(HoverButton, {}, "hover") : null,
448
465
  controls.focus ? /* @__PURE__ */ jsx3(FocusButton, {}, "focus") : null,
449
466
  controls.wireframe ? /* @__PURE__ */ jsx3(WireframeButton, {}, "wireframe") : null,
@@ -455,7 +472,7 @@ var DefaultControls = () => {
455
472
  controls.reset ? /* @__PURE__ */ jsx3(ResetButton, {}, "reset") : null,
456
473
  controls.top ? /* @__PURE__ */ jsx3(TopButton, {}, "top") : null
457
474
  ].filter(Boolean);
458
- return /* @__PURE__ */ jsxs(Controls, { children: [
475
+ return /* @__PURE__ */ jsxs2(Controls, { children: [
459
476
  display,
460
477
  display.length && analysis.length ? /* @__PURE__ */ jsx3(Divider, {}) : null,
461
478
  analysis,
@@ -484,6 +501,7 @@ var ViewerToolbar = Object.assign(ViewerToolbarRoot, {
484
501
  ToolsButton,
485
502
  DirectionsButton,
486
503
  DfmButton,
504
+ SharpCornersButton,
487
505
  HoverButton,
488
506
  FocusButton,
489
507
  WireframeButton,
@@ -1024,7 +1042,7 @@ function gridFor(box, options = {}) {
1024
1042
  }
1025
1043
 
1026
1044
  // src/primitives.tsx
1027
- import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
1045
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1028
1046
  var FURNITURE2 = { [EXCLUDE_FROM_FRAME]: true };
1029
1047
  var Grid = ({ step, extent, color, opacity = 0.35 }) => {
1030
1048
  const theme = resolveTheme();
@@ -1086,7 +1104,7 @@ var CubePanels = ({
1086
1104
  setHovered(name);
1087
1105
  invalidate();
1088
1106
  };
1089
- return /* @__PURE__ */ jsxs2("group", { ref: groupRef, scale: 38, children: [
1107
+ return /* @__PURE__ */ jsxs3("group", { ref: groupRef, scale: 38, children: [
1090
1108
  panels.map(({ zone, geometry }) => /* @__PURE__ */ jsx6(
1091
1109
  "mesh",
1092
1110
  {
@@ -1148,7 +1166,7 @@ function arrowPlacement(direction, box) {
1148
1166
  }
1149
1167
 
1150
1168
  // src/direction-arrows.tsx
1151
- import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1169
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1152
1170
  var FURNITURE3 = { [EXCLUDE_FROM_FRAME]: true };
1153
1171
  var DirectionArrows = ({
1154
1172
  directions,
@@ -1175,7 +1193,7 @@ var DirectionArrows = ({
1175
1193
  // Excluded from framing: the arrows are deliberately placed outside the
1176
1194
  // part, so a Fit that measured them would frame the arrows and leave the
1177
1195
  // part small in the middle of them.
1178
- /* @__PURE__ */ jsxs3("group", { userData: FURNITURE3, children: [
1196
+ /* @__PURE__ */ jsxs4("group", { userData: FURNITURE3, children: [
1179
1197
  directions.map((direction, index) => {
1180
1198
  if (!drawn(index)) return null;
1181
1199
  return /* @__PURE__ */ jsx7(
@@ -1248,7 +1266,7 @@ import {
1248
1266
  Vector2,
1249
1267
  Vector3 as Vector36
1250
1268
  } from "three";
1251
- import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
1269
+ import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1252
1270
  var FURNITURE4 = { [EXCLUDE_FROM_FRAME]: true };
1253
1271
  var Z = new Vector36(0, 0, 1);
1254
1272
  var AXIS_ROTATIONS = {
@@ -1383,9 +1401,9 @@ var Picker = ({ box, theme }) => {
1383
1401
  store.set(plane.options);
1384
1402
  }
1385
1403
  });
1386
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
1387
- /* @__PURE__ */ jsxs4("group", { ref: previewRef, visible: false, children: [
1388
- /* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1404
+ return /* @__PURE__ */ jsxs5(Fragment, { children: [
1405
+ /* @__PURE__ */ jsxs5("group", { ref: previewRef, visible: false, children: [
1406
+ /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1389
1407
  /* @__PURE__ */ jsx8("planeGeometry", { args: [previewSize, previewSize] }),
1390
1408
  /* @__PURE__ */ jsx8(
1391
1409
  "meshBasicMaterial",
@@ -1418,13 +1436,13 @@ var Picker = ({ box, theme }) => {
1418
1436
  }
1419
1437
  )
1420
1438
  ] }),
1421
- planes.map((plane) => /* @__PURE__ */ jsxs4(
1439
+ planes.map((plane) => /* @__PURE__ */ jsxs5(
1422
1440
  "group",
1423
1441
  {
1424
1442
  position: [plane.position.x, plane.position.y, plane.position.z],
1425
1443
  rotation: AXIS_ROTATIONS[plane.axis],
1426
1444
  children: [
1427
- /* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
1445
+ /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, ...planeHandlers(plane), children: [
1428
1446
  /* @__PURE__ */ jsx8("planeGeometry", { args: [plane.size, plane.size] }),
1429
1447
  /* @__PURE__ */ jsx8(
1430
1448
  "meshBasicMaterial",
@@ -1467,8 +1485,8 @@ var CutSheet = ({ box, section, theme }) => {
1467
1485
  }, [box, cut, span]);
1468
1486
  if (!placement) return null;
1469
1487
  const size = span * OUTLINE_SCALE;
1470
- return /* @__PURE__ */ jsxs4("group", { position: placement.position, quaternion: placement.quaternion, children: [
1471
- /* @__PURE__ */ jsxs4("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1488
+ return /* @__PURE__ */ jsxs5("group", { position: placement.position, quaternion: placement.quaternion, children: [
1489
+ /* @__PURE__ */ jsxs5("mesh", { renderOrder: SECTION_RENDER_ORDER.outline, raycast: () => null, children: [
1472
1490
  /* @__PURE__ */ jsx8("planeGeometry", { args: [size, size] }),
1473
1491
  /* @__PURE__ */ jsx8(
1474
1492
  "meshBasicMaterial",
@@ -1694,7 +1712,7 @@ function sampleSection(meshPositions, regionOf, edgePositions, plane) {
1694
1712
  }
1695
1713
 
1696
1714
  // src/measure-tool.tsx
1697
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
1715
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1698
1716
  var FURNITURE5 = { [EXCLUDE_FROM_FRAME]: true };
1699
1717
  var MEASURE_LABEL_CLASS = "toolpath-measure-label";
1700
1718
  var MeasureTool = ({
@@ -1755,7 +1773,7 @@ var MeasureTool = ({
1755
1773
  };
1756
1774
  if (box.isEmpty()) return null;
1757
1775
  const label = className(labelClassName);
1758
- return /* @__PURE__ */ jsxs5("group", { userData: FURNITURE5, children: [
1776
+ return /* @__PURE__ */ jsxs6("group", { userData: FURNITURE5, children: [
1759
1777
  picking ? null : /* @__PURE__ */ jsx9(
1760
1778
  Snapper,
1761
1779
  {
@@ -1929,7 +1947,7 @@ var Snapper = ({ mode, draft, onPlace, theme, format, label }) => {
1929
1947
  invalidate();
1930
1948
  }
1931
1949
  }, [draft, invalidate]);
1932
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1950
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1933
1951
  /* @__PURE__ */ jsx9("group", { ref: marker, visible: false, children: /* @__PURE__ */ jsx9("mesh", { geometry: dot, renderOrder: MEASURE_RENDER_ORDER, raycast: () => null, children: /* @__PURE__ */ jsx9("meshBasicMaterial", { color: theme.measureSnap, depthTest: false, depthWrite: false }) }) }),
1934
1952
  /* @__PURE__ */ jsx9(
1935
1953
  Line,
@@ -2124,7 +2142,7 @@ var MeasurementView = ({
2124
2142
  const anchor = measurementLabelAnchor(measurement);
2125
2143
  if (measurement.kind === "distance") {
2126
2144
  const [a2, b2] = measurement.points;
2127
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2145
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2128
2146
  /* @__PURE__ */ jsx9(Segment, { points: [a2, b2], color: theme.measure }),
2129
2147
  /* @__PURE__ */ jsx9(ScreenDot, { point: a2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
2130
2148
  /* @__PURE__ */ jsx9(ScreenDot, { point: b2, pixels: MEASURE_MARKER_PIXELS, color: theme.measure }),
@@ -2134,7 +2152,7 @@ var MeasurementView = ({
2134
2152
  }
2135
2153
  const [a, vertex, b] = measurement.points;
2136
2154
  const arc = angleArc(vertex, a, b, angleArcRadius(vertex, a, b));
2137
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2155
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2138
2156
  /* @__PURE__ */ jsx9(Segment, { points: [vertex, a], color: theme.measure }),
2139
2157
  /* @__PURE__ */ jsx9(Segment, { points: [vertex, b], color: theme.measure }),
2140
2158
  /* @__PURE__ */ jsx9(Polyline, { points: arc, color: theme.measure }),
@@ -2166,7 +2184,7 @@ var DeltaLegs = ({ a, b, id, format, label }) => {
2166
2184
  }
2167
2185
  if (changed) invalidate();
2168
2186
  });
2169
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
2187
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2170
2188
  legs.map((leg, index) => /* @__PURE__ */ jsx9(
2171
2189
  Line,
2172
2190
  {
@@ -2340,7 +2358,7 @@ function toolMarkShape(mark) {
2340
2358
  }
2341
2359
 
2342
2360
  // src/tool-marks.tsx
2343
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
2361
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2344
2362
  var BODY = { color: 16317180, opacity: 0.32 };
2345
2363
  var NO_POINTER2 = { pointerEvents: "none" };
2346
2364
  var ignoreRay = () => void 0;
@@ -2354,7 +2372,7 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
2354
2372
  [marks]
2355
2373
  );
2356
2374
  const label = labelClassName ? `${MEASURE_LABEL_CLASS} ${labelClassName}` : MEASURE_LABEL_CLASS;
2357
- return /* @__PURE__ */ jsx10("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs6("group", { children: [
2375
+ return /* @__PURE__ */ jsx10("group", { visible, userData: { [EXCLUDE_FROM_FRAME]: true }, children: shapes.map(({ mark, shape }, index) => /* @__PURE__ */ jsxs7("group", { children: [
2358
2376
  /* @__PURE__ */ jsx10(ToolBody, { shape }),
2359
2377
  [...shape.rims, shape.dimension, ...shape.ticks].map((points, line) => /* @__PURE__ */ jsx10(
2360
2378
  Line2,
@@ -2369,16 +2387,16 @@ var ToolMarks = ({ marks, visible = true, labelClassName, theme }) => {
2369
2387
  },
2370
2388
  line
2371
2389
  )),
2372
- mark.label && visible ? /* @__PURE__ */ jsx10(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs6("div", { className: label, "data-measure-label": "tool", children: [
2390
+ mark.label && visible ? /* @__PURE__ */ jsx10(Html2, { position: shape.labelAt, center: true, zIndexRange: [0, 0], style: NO_POINTER2, children: /* @__PURE__ */ jsxs7("div", { className: label, "data-measure-label": "tool", children: [
2373
2391
  /* @__PURE__ */ jsx10("span", { "data-measure-value": true, children: mark.label }),
2374
- mark.note ? /* @__PURE__ */ jsxs6("span", { "data-measure-note": true, children: [
2392
+ mark.note ? /* @__PURE__ */ jsxs7("span", { "data-measure-note": true, children: [
2375
2393
  " ",
2376
2394
  mark.note
2377
2395
  ] }) : null
2378
2396
  ] }) }) : null
2379
2397
  ] }, index)) });
2380
2398
  };
2381
- var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs6(
2399
+ var ToolBody = ({ shape }) => /* @__PURE__ */ jsxs7(
2382
2400
  "mesh",
2383
2401
  {
2384
2402
  position: shape.base,
@@ -2554,6 +2572,7 @@ export {
2554
2572
  excludedFromFrame,
2555
2573
  featureTriangles,
2556
2574
  featureTypeRank,
2575
+ findSharpCorners,
2557
2576
  fitDistance,
2558
2577
  fixedBoxStockBounds,
2559
2578
  focusForPick,
@@ -2602,10 +2621,12 @@ export {
2602
2621
  sectionOffset,
2603
2622
  sectionOptionsFromState,
2604
2623
  sectionPlane,
2624
+ sharpCornerPositions,
2605
2625
  smoothRegionNormals,
2606
2626
  snapAt,
2607
2627
  squaredUp,
2608
2628
  startPosition,
2629
+ surfaceCreases,
2609
2630
  surfaceUnderRay,
2610
2631
  targetBoundary,
2611
2632
  themesEqual,