@toolpath/viewer 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/dist/{chunk-ILZ2KQUK.js → chunk-4BU4QZCL.js} +498 -167
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/index.d.ts +405 -8
- package/dist/index.js +33 -1
- package/dist/{normalize-BXIGoZmc.d.ts → normalize-DsoIyM8B.d.ts} +19 -90
- package/package.json +5 -1
package/dist/engine/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PartModel, a as PartMeshRefs } from '../normalize-
|
|
2
|
-
export { E as EnginePart, b as EnginePartProps, M as MIN_KERNEL_VERSION, c as assertSupportedKernelVersion, n as normalizePartReport, s as smoothRegionNormals } from '../normalize-
|
|
1
|
+
import { P as PartModel, a as PartMeshRefs } from '../normalize-DsoIyM8B.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-DsoIyM8B.js';
|
|
3
3
|
import { BufferGeometry } from 'three';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
package/dist/engine/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as PartModel, V as Vec3, d as ViewerTheme, e as PartModelRegion,
|
|
2
|
-
export { B as BuildPickInput, C as
|
|
3
|
-
import { BufferGeometry, CanvasTexture, Vector3, Box3 } from 'three';
|
|
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-DsoIyM8B.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, N as NO_MODIFIERS, o as PICKED_SURFACE_LABEL, p as PartMesh, q as PartMeshProps, a as PartMeshRefs, r as PartObject, t as PartPick, u as PickModifiers, v as REGION_ATTRIBUTE, w as RegionHighlight, x as RegionPaint, S as SECTION_RENDER_ORDER, y as SectionAnchor, z as SectionBounds, A as SectionOptions, G as SectionPlacement, I as SectionState, J as SectionView, L as applyHighlightLayers, O as buildPick, Q as buildRegionAttribute, U as buildRegionTexels, W as createPart, X as directionColor, Y as dragPlane, Z as focusForPick, n as normalizePartReport, _ as pickedStartDepth, $ as resolveSectionPlane, a0 as resolveTheme, a1 as sectionBounds, a2 as sectionConstant, a3 as sectionDepth, a4 as sectionDepthConstant, a5 as sectionDepthRange, a6 as sectionFromPick, a7 as sectionOffset, a8 as sectionPlane, s as smoothRegionNormals, a9 as themesEqual, aa as viewDirection } from './normalize-DsoIyM8B.js';
|
|
3
|
+
import { BufferGeometry, CanvasTexture, Vector3, Box3, OrthographicCamera, PerspectiveCamera, Object3D } from 'three';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { RefObject, ReactNode, CSSProperties } from 'react';
|
|
6
6
|
import CameraControls from 'camera-controls';
|
|
@@ -269,6 +269,15 @@ interface DoubleTapPoint extends TapPoint {
|
|
|
269
269
|
interface DoubleTapTracker {
|
|
270
270
|
/** Whether this press completes a double tap begun by the last one. */
|
|
271
271
|
isDouble(event: DoubleTapPoint): boolean;
|
|
272
|
+
/**
|
|
273
|
+
* Forgets the press being waited on, so the next one starts a fresh pair.
|
|
274
|
+
*
|
|
275
|
+
* For the caller that knows the gesture was interrupted by something this
|
|
276
|
+
* has no way to see — the pointer leaving the thing being clicked, most of
|
|
277
|
+
* all. Two presses either side of a trip to a panel are two clicks that
|
|
278
|
+
* happen to be quick, however close together the clock puts them.
|
|
279
|
+
*/
|
|
280
|
+
reset(): void;
|
|
272
281
|
}
|
|
273
282
|
/**
|
|
274
283
|
* Pairing presses into a double tap by hand.
|
|
@@ -284,16 +293,29 @@ interface DoubleTapTracker {
|
|
|
284
293
|
*
|
|
285
294
|
* A completed double clears the state rather than leaving it, so three presses
|
|
286
295
|
* are one double and one single rather than two overlapping doubles.
|
|
296
|
+
*
|
|
297
|
+
* Time and position are all this can judge on, which is not enough on its own:
|
|
298
|
+
* clicking a face, pressing something in a panel and clicking the same face
|
|
299
|
+
* again is three gestures the clock cannot tell from two, and it is quick
|
|
300
|
+
* enough to land inside the window. {@link DoubleTapTracker.reset} is how the
|
|
301
|
+
* caller says the pair was broken by something it could see and this could not.
|
|
287
302
|
*/
|
|
288
303
|
declare function trackDoubleTaps(within?: number, slop?: number): DoubleTapTracker;
|
|
289
304
|
|
|
305
|
+
/** Whether the gesture ending at this event was a click rather than a drag. */
|
|
306
|
+
type TapGuard = (event: TapPoint) => boolean;
|
|
290
307
|
/**
|
|
291
308
|
* A click that is a click, for anything inside the canvas.
|
|
292
309
|
*
|
|
293
310
|
* Bound to the canvas element rather than to the object, because the gesture
|
|
294
311
|
* being judged started before any object knew about it.
|
|
312
|
+
*
|
|
313
|
+
* Under a `<Viewer>` this returns the tracker the scene already owns and
|
|
314
|
+
* attaches nothing. Called outside one — it is exported, so a consumer may use
|
|
315
|
+
* it in a scene of their own — it owns a tracker of its own instead. The effect
|
|
316
|
+
* is what differs, not the hooks: they run in the same order either way.
|
|
295
317
|
*/
|
|
296
|
-
declare function useTapGuard():
|
|
318
|
+
declare function useTapGuard(): TapGuard;
|
|
297
319
|
|
|
298
320
|
interface GridSpec {
|
|
299
321
|
/** Cell size in part units (millimetres). */
|
|
@@ -364,6 +386,200 @@ type SurfaceOf = ReadonlyMap<number, number>;
|
|
|
364
386
|
*/
|
|
365
387
|
declare function visualSurfaces(regions: readonly PartModelRegion[]): SurfaceOf;
|
|
366
388
|
|
|
389
|
+
type Projection = 'orthographic' | 'perspective';
|
|
390
|
+
type ViewerCamera = OrthographicCamera | PerspectiveCamera;
|
|
391
|
+
interface ViewportSize {
|
|
392
|
+
readonly width: number;
|
|
393
|
+
readonly height: number;
|
|
394
|
+
}
|
|
395
|
+
/** Vertical field of view, in degrees, for the perspective camera. */
|
|
396
|
+
declare const PERSPECTIVE_FOV = 30;
|
|
397
|
+
/** Padding around the framed bounds, as a multiple of its radius. */
|
|
398
|
+
declare const DEFAULT_FIT_MARGIN = 1.2;
|
|
399
|
+
/** Marks scene furniture — grid, axes — that the camera should not frame. */
|
|
400
|
+
declare const EXCLUDE_FROM_FRAME = "viewerExcludeFromFrame";
|
|
401
|
+
/**
|
|
402
|
+
* What the camera frames: a bounding *sphere*, not a box.
|
|
403
|
+
*
|
|
404
|
+
* A sphere makes framing rotation-invariant — the part stays fully visible from
|
|
405
|
+
* every angle, and a resize never needs to know the current pose. Framing
|
|
406
|
+
* per-axis box dimensions instead means recomputing the camera distance on
|
|
407
|
+
* every resize, and still clipping on some orientations.
|
|
408
|
+
*/
|
|
409
|
+
interface SceneBounds {
|
|
410
|
+
readonly center: Vector3;
|
|
411
|
+
readonly radius: number;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Bounds for a scene with nothing in it. A viewer is mounted before it has a
|
|
415
|
+
* part, and a camera with a zero-size frustum renders nothing at all.
|
|
416
|
+
*/
|
|
417
|
+
declare function defaultBounds(): SceneBounds;
|
|
418
|
+
/**
|
|
419
|
+
* A container can be laid out at zero size, and a degenerate aspect ratio
|
|
420
|
+
* produces a `NaN` projection matrix that never recovers. Fall back to square.
|
|
421
|
+
*/
|
|
422
|
+
declare function aspectRatio(size: ViewportSize): number;
|
|
423
|
+
/**
|
|
424
|
+
* A world length that covers `pixels` on screen at `point`.
|
|
425
|
+
*
|
|
426
|
+
* Anything sized in world units is a thumbnail on a plate and a wall on an
|
|
427
|
+
* insert; the whole reason a control is measured this way is that it is a
|
|
428
|
+
* control rather than part of the model. The section handle and the orbit
|
|
429
|
+
* target marker both size themselves with it, which is why it lives here with
|
|
430
|
+
* the camera rather than beside either of them.
|
|
431
|
+
*
|
|
432
|
+
* The two projections answer differently on purpose. An orthographic frustum
|
|
433
|
+
* covers the same world height wherever the point is, so distance does not
|
|
434
|
+
* enter; a perspective one covers more the further away it looks, so it does.
|
|
435
|
+
*/
|
|
436
|
+
declare function screenLength(camera: ViewerCamera, point: Vector3, viewport: ViewportSize, pixels: number): number;
|
|
437
|
+
declare function boundsFromBox(box: Box3): SceneBounds;
|
|
438
|
+
/**
|
|
439
|
+
* The bounds of everything worth framing under `root`.
|
|
440
|
+
*
|
|
441
|
+
* Skips objects flagged with {@link EXCLUDE_FROM_FRAME} and their descendants:
|
|
442
|
+
* a 100 mm grid around a 6 mm part would otherwise frame the grid, and the part
|
|
443
|
+
* would be a speck.
|
|
444
|
+
*/
|
|
445
|
+
declare function contentBounds(root: Object3D, into: Box3): SceneBounds;
|
|
446
|
+
/**
|
|
447
|
+
* The distance at which a sphere of `radius` fits inside a perspective frustum.
|
|
448
|
+
* Uses the narrower of the two field-of-view angles, so a portrait viewport
|
|
449
|
+
* frames on width and a landscape one on height.
|
|
450
|
+
*/
|
|
451
|
+
declare function perspectiveFitDistance(fovDegrees: number, aspect: number, radius: number): number;
|
|
452
|
+
/**
|
|
453
|
+
* Half-height of an orthographic frustum that fits a sphere of `radius`. A
|
|
454
|
+
* portrait viewport grows the height so the width still clears the sphere.
|
|
455
|
+
*/
|
|
456
|
+
declare function orthographicHalfHeight(aspect: number, radius: number): number;
|
|
457
|
+
declare function fitDistance(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): number;
|
|
458
|
+
declare function startPosition(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number): Vector3;
|
|
459
|
+
/**
|
|
460
|
+
* How far out the wheel may pull, as a fraction of the fitted framing: the part
|
|
461
|
+
* may shrink to a quarter of the view and no further. Past that it is a speck
|
|
462
|
+
* with no cue that Fit is the way back.
|
|
463
|
+
*/
|
|
464
|
+
declare const MIN_FRAME_RATIO = 0.25;
|
|
465
|
+
/**
|
|
466
|
+
* How far in the wheel may push, as a multiple of the fitted framing.
|
|
467
|
+
*
|
|
468
|
+
* Both ratios are the legacy viewer's `minZoom` 0.25 / `maxZoom` 10
|
|
469
|
+
* (`three-object.tsx:289-311`), which transfer almost exactly: legacy's
|
|
470
|
+
* orthographic frustum was `largestDimension * 2` tall and this one is the
|
|
471
|
+
* fitted bounding sphere, which for a cube agree within a few percent. So the
|
|
472
|
+
* numbers are the ones that shipped, and here they carry a meaning they did not
|
|
473
|
+
* have there — `zoom` 1 *is* the fitted framing, so 0.25 and 10 say exactly
|
|
474
|
+
* what a reader thinks they say.
|
|
475
|
+
*/
|
|
476
|
+
declare const MAX_FRAME_RATIO = 10;
|
|
477
|
+
/** How far the camera may travel, in the two units the two cameras travel in. */
|
|
478
|
+
interface CameraLimits {
|
|
479
|
+
readonly minZoom: number;
|
|
480
|
+
readonly maxZoom: number;
|
|
481
|
+
readonly minDistance: number;
|
|
482
|
+
readonly maxDistance: number;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* The clamps for a scene, so the wheel cannot leave the viewport empty.
|
|
486
|
+
*
|
|
487
|
+
* Without them it can, from either end, and both cameras can do it: an
|
|
488
|
+
* orthographic `camera.zoom` reaches 1e30 in sixty notches, and a perspective
|
|
489
|
+
* camera dives inside the part in eight, because `minDistance` defaults to
|
|
490
|
+
* `Number.EPSILON`. Fit recovers from both — but Fit is a double middle click
|
|
491
|
+
* and nothing on screen says so, which makes it a way out rather than an
|
|
492
|
+
* answer.
|
|
493
|
+
*
|
|
494
|
+
* One rule for both cameras: the wheel may take the part from a quarter of its
|
|
495
|
+
* fitted size to ten times it. Under an orthographic camera that scale *is* the
|
|
496
|
+
* frustum, so the ratios land on `zoom`; under a perspective camera apparent
|
|
497
|
+
* size is the inverse of distance, so they land on `distance` — which is why
|
|
498
|
+
* they swap sides between the two.
|
|
499
|
+
*
|
|
500
|
+
* Pure, and derived from `bounds`, so it re-derives wherever the scene is
|
|
501
|
+
* re-measured rather than being sampled once at mount.
|
|
502
|
+
*
|
|
503
|
+
* `framed` is what the view has been framed *on*, when that is not the whole
|
|
504
|
+
* scene — `frameBox` on a single feature. The band is then taken about that
|
|
505
|
+
* framing as well as about the scene's, because a band centred on the part is
|
|
506
|
+
* the wrong band for a view of a 3 mm hole in it: ten times the *part's* fitted
|
|
507
|
+
* size can be less than once the hole's, so the framing the caller asked for sat
|
|
508
|
+
* outside the clamps meant to keep it in view. Under an orthographic camera
|
|
509
|
+
* `zoom` is measured against a frustum still sized to the scene, so the framing
|
|
510
|
+
* enters as the zoom ratio it takes to reach; under a perspective one it enters
|
|
511
|
+
* as its own fitted distance.
|
|
512
|
+
*
|
|
513
|
+
* Widened to the union of the two rather than moved onto `framed`, and that is
|
|
514
|
+
* the whole of why this takes both. A band that simply followed the last framing
|
|
515
|
+
* would take the part away: framing a hole would put "far enough out to see what
|
|
516
|
+
* the hole is in" past `maxDistance`, so the wheel could no longer get back to
|
|
517
|
+
* the part it had just been looking at, and Fit — a gesture with nothing on
|
|
518
|
+
* screen to say so — would be the only way. Reaching further in must not cost
|
|
519
|
+
* the reach back out, so each end takes whichever bound is looser. With no
|
|
520
|
+
* `framed`, or one the size of the scene, both ends collapse to the scene's own
|
|
521
|
+
* and this is the function it was before.
|
|
522
|
+
*/
|
|
523
|
+
declare function cameraLimits(projection: Projection, size: ViewportSize, bounds: SceneBounds, margin?: number, framed?: SceneBounds): CameraLimits;
|
|
524
|
+
/**
|
|
525
|
+
* The box the orbit target is confined to — the fitted framing, several frames
|
|
526
|
+
* over. Written into `into` so the per-frame path allocates nothing.
|
|
527
|
+
*/
|
|
528
|
+
declare function targetBoundary(bounds: SceneBounds, into: Box3, margin?: number): Box3;
|
|
529
|
+
/**
|
|
530
|
+
* Points an existing camera's frustum at `bounds` for the current viewport.
|
|
531
|
+
* Pose is untouched — that belongs to the controls.
|
|
532
|
+
*/
|
|
533
|
+
declare function applyProjection(camera: ViewerCamera, size: ViewportSize, bounds: SceneBounds, margin?: number): void;
|
|
534
|
+
/**
|
|
535
|
+
* The world-up convention. The part data is Z-up (millimetres, no conversion) —
|
|
536
|
+
* not the glTF-conventional Y-up — so the camera has to say so explicitly.
|
|
537
|
+
*/
|
|
538
|
+
declare const CAD_CAMERA_UP: Vector3;
|
|
539
|
+
/**
|
|
540
|
+
* `up` re-squared against a view direction: the part of it perpendicular to
|
|
541
|
+
* `view`, written into `into`.
|
|
542
|
+
*
|
|
543
|
+
* This is what keeps the horizon level while a free orbit carries the camera
|
|
544
|
+
* over a pole — the up vector follows the view instead of flipping. The
|
|
545
|
+
* controls run it on every update, which is what makes the next sentence the
|
|
546
|
+
* important one.
|
|
547
|
+
*
|
|
548
|
+
* **It is path-dependent.** It is a projection, so it discards the component it
|
|
549
|
+
* removes and cannot put it back: running it at one view and then another does
|
|
550
|
+
* not give what running it once at the second view would. So a camera carries
|
|
551
|
+
* the roll of every pose it has passed through, and any pose that is meant to
|
|
552
|
+
* be canonical — the opening frame, a reset, a named view — has to say what
|
|
553
|
+
* `up` is rather than inherit it. `viewer.tsx` does, at `resetContent`. The
|
|
554
|
+
* test beside this pins both halves.
|
|
555
|
+
*
|
|
556
|
+
* `into` may be `view` or `up` — three.js `crossVectors` reads all six
|
|
557
|
+
* components before it writes any, and the intermediate is this module's own
|
|
558
|
+
* scratch, so writing the result over an input is safe. Said here because this
|
|
559
|
+
* is exported and an out-parameter invites the question; the test beside this
|
|
560
|
+
* pins it, so a three.js change cannot quietly turn the answer around.
|
|
561
|
+
*
|
|
562
|
+
* `view` and `up` being parallel is the one degenerate case: their cross is
|
|
563
|
+
* zero, `normalize` leaves it zero, and the result is a zero vector rather than
|
|
564
|
+
* an error. A caller with a free-orbiting camera reaches that at the pole,
|
|
565
|
+
* which is what `freeOrbit` and a squared `up` at every canonical pose exist to
|
|
566
|
+
* keep it away from.
|
|
567
|
+
*/
|
|
568
|
+
declare function adaptedUp(view: Vector3, up: Vector3, into: Vector3): Vector3;
|
|
569
|
+
/** Named viewing directions, as unit vectors from the part toward the camera. */
|
|
570
|
+
declare const cadViewDirections: {
|
|
571
|
+
readonly front: Vector3;
|
|
572
|
+
readonly back: Vector3;
|
|
573
|
+
readonly left: Vector3;
|
|
574
|
+
readonly right: Vector3;
|
|
575
|
+
readonly top: Vector3;
|
|
576
|
+
readonly bottom: Vector3;
|
|
577
|
+
readonly isometric: Vector3;
|
|
578
|
+
};
|
|
579
|
+
type ViewerView = keyof typeof cadViewDirections;
|
|
580
|
+
/** The current orbit direction, so Fit can retain the direction being looked from. */
|
|
581
|
+
declare function currentViewDirection(camera: ViewerCamera, target: Vector3, into: Vector3): Vector3;
|
|
582
|
+
|
|
367
583
|
/**
|
|
368
584
|
* Mouse and trackpad presets.
|
|
369
585
|
*
|
|
@@ -400,6 +616,20 @@ type ExtendedCameraControlsOptions = {
|
|
|
400
616
|
declare class ExtendedCameraControls extends CameraControls {
|
|
401
617
|
#private;
|
|
402
618
|
constructor(camera: ViewerCamera, domElement: HTMLElement, options?: ExtendedCameraControlsOptions);
|
|
619
|
+
/**
|
|
620
|
+
* Bounds how far the wheel may travel, and where the orbit target may go.
|
|
621
|
+
*
|
|
622
|
+
* Re-applied whenever the scene is re-measured rather than set once: the
|
|
623
|
+
* limits are derived from the part's bounds, and a viewer that loads a second
|
|
624
|
+
* part would otherwise keep the first part's idea of far.
|
|
625
|
+
*
|
|
626
|
+
* The boundary confines the *target*. Zoom-to-cursor moves it, and goes on
|
|
627
|
+
* moving it after the zoom clamp bites — which is a runaway no zoom clamp can
|
|
628
|
+
* catch. `boundaryEnclosesCamera` stays off: under an orthographic camera the
|
|
629
|
+
* camera sits well outside the framing it is looking at, so enclosing it would
|
|
630
|
+
* drag the target back in on every frame.
|
|
631
|
+
*/
|
|
632
|
+
applyLimits(limits: CameraLimits, boundary?: Box3): void;
|
|
403
633
|
get freeOrbit(): boolean;
|
|
404
634
|
get scheme(): ControlScheme;
|
|
405
635
|
/** Adds the listeners this subclass owns, on top of the base connection. */
|
|
@@ -478,16 +708,33 @@ interface ViewerControls {
|
|
|
478
708
|
}
|
|
479
709
|
interface ViewerHandle extends ViewerControls {
|
|
480
710
|
}
|
|
711
|
+
type VectorLike = Pick<Vector3, 'x' | 'y' | 'z'>;
|
|
481
712
|
|
|
482
713
|
declare const useViewerControls: () => ViewerControls;
|
|
714
|
+
/** Re-aims the orbit, or `null` when this viewer has the gesture turned off. */
|
|
715
|
+
type Retarget = ((point: VectorLike) => void) | null;
|
|
716
|
+
/**
|
|
717
|
+
* How the part reports "orbit about this from now on".
|
|
718
|
+
*
|
|
719
|
+
* Split this way because the two halves belong to different places: only the
|
|
720
|
+
* part knows *where* the pointer landed, and it knows it from the raycast the
|
|
721
|
+
* selection already ran, so nothing here casts a second ray. Only the viewer
|
|
722
|
+
* knows whether the gesture is wanted, because `retargetOnDoubleClick` is its
|
|
723
|
+
* prop. `null` is that answer, and it turns the pairing off at the source
|
|
724
|
+
* rather than making the part detect a gesture nobody will act on.
|
|
725
|
+
*/
|
|
726
|
+
declare const useRetarget: () => Retarget;
|
|
483
727
|
interface ViewerProps {
|
|
484
728
|
children?: ReactNode;
|
|
485
729
|
className?: string;
|
|
486
730
|
style?: CSSProperties;
|
|
487
731
|
/**
|
|
488
|
-
*
|
|
489
|
-
* parallel edges stay parallel, so
|
|
490
|
-
* same size wherever they sit.
|
|
732
|
+
* **Orthographic by default.** It is what a machinist reads a part in:
|
|
733
|
+
* parallel edges stay parallel, so a wall that looks square is square, and
|
|
734
|
+
* two features the same size measure the same size wherever they sit.
|
|
735
|
+
*
|
|
736
|
+
* Perspective is the better answer for reading a deep pocket *as* depth, and
|
|
737
|
+
* is one prop away.
|
|
491
738
|
*/
|
|
492
739
|
projection?: Projection;
|
|
493
740
|
/**
|
|
@@ -512,6 +759,43 @@ interface ViewerProps {
|
|
|
512
759
|
* makes easy to do.
|
|
513
760
|
*/
|
|
514
761
|
recentreOnDoubleClick?: boolean;
|
|
762
|
+
/**
|
|
763
|
+
* Whether a double **left** click on the part orbits about what was clicked
|
|
764
|
+
* from then on. On by default.
|
|
765
|
+
*
|
|
766
|
+
* The clicked point moves to the middle of the view at the same size and
|
|
767
|
+
* angle, and stays the pivot until something else moves it. It is how you get
|
|
768
|
+
* from "the whole part" to "this corner" without losing the ability to turn
|
|
769
|
+
* what you are looking at — and under an orthographic camera it is the only
|
|
770
|
+
* gesture that re-aims the pivot at all, because the wheel there scales a
|
|
771
|
+
* frustum rather than travelling toward anything.
|
|
772
|
+
*
|
|
773
|
+
* The move is immediate rather than eased: the damping these controls ship
|
|
774
|
+
* with settles a transition inside one frame. Turn on {@link showOrbitTarget}
|
|
775
|
+
* if the pivot moving needs to be visible.
|
|
776
|
+
*
|
|
777
|
+
* The click is still a click. Its pick arrives with `doubled: true` on it, so
|
|
778
|
+
* an app that wants a double click to mean something of its own can say so,
|
|
779
|
+
* and one that would rather a double click left the selection alone can
|
|
780
|
+
* ignore it. What the viewport claims is the *view*, not the pick.
|
|
781
|
+
*/
|
|
782
|
+
retargetOnDoubleClick?: boolean;
|
|
783
|
+
/**
|
|
784
|
+
* Whether to show two small circles at the orbit target — the point the view
|
|
785
|
+
* turns and zooms about. **Off by default.**
|
|
786
|
+
*
|
|
787
|
+
* They are up while a gesture is running and flash when the pivot moves on
|
|
788
|
+
* its own: a cursor zoom walking it, a double click re-aiming it, a Fit
|
|
789
|
+
* putting it back. Then they fade. It answers "why did the part swing that
|
|
790
|
+
* way", which nothing else on screen does, and it is what makes a wheel that
|
|
791
|
+
* has carried the pivot off the part legible while it is happening rather
|
|
792
|
+
* than after.
|
|
793
|
+
*
|
|
794
|
+
* Off by default because it is an aid rather than furniture: a viewer that
|
|
795
|
+
* grew a dot in the middle of every screenshot would be a surprise to anybody
|
|
796
|
+
* already rendering with this.
|
|
797
|
+
*/
|
|
798
|
+
showOrbitTarget?: boolean;
|
|
515
799
|
/** Lighting and background. The part's own colours are tuned against this rig. */
|
|
516
800
|
theme?: Partial<ViewerTheme>;
|
|
517
801
|
/**
|
|
@@ -684,4 +968,117 @@ declare function bestOwner(model: PartModel, owners: readonly FeatureTag[], cont
|
|
|
684
968
|
*/
|
|
685
969
|
declare function cycleOwner(owners: readonly FeatureTag[], current: FeatureTag | null): FeatureTag | null;
|
|
686
970
|
|
|
687
|
-
|
|
971
|
+
/** A camera and the point it orbits, together, because neither moves alone. */
|
|
972
|
+
interface RetargetPose {
|
|
973
|
+
readonly position: Vec3;
|
|
974
|
+
readonly target: Vec3;
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Where the camera and its orbit target go when somebody asks to orbit about a
|
|
978
|
+
* point on the part.
|
|
979
|
+
*
|
|
980
|
+
* The target moves to the point and the camera moves by the same delta, so the
|
|
981
|
+
* offset between them — the viewing direction and the distance along it — comes
|
|
982
|
+
* through untouched. Only the pivot changes: what was clicked ends up in the
|
|
983
|
+
* middle of the view, at the same size, seen from the same angle, and every
|
|
984
|
+
* orbit and zoom afterwards is about it. This says where the pose lands, not
|
|
985
|
+
* how it gets there — how fast is the caller's transition and the controls'
|
|
986
|
+
* damping, and in this viewer that is one frame.
|
|
987
|
+
*
|
|
988
|
+
* Moving the target alone would swing the camera to look at the new point from
|
|
989
|
+
* the old place, which reads as the part being knocked sideways. Moving the
|
|
990
|
+
* camera alone would not change what anything orbits about, which is the whole
|
|
991
|
+
* request.
|
|
992
|
+
*
|
|
993
|
+
* This is what makes an orthographic viewport navigable. Under a perspective
|
|
994
|
+
* camera the wheel travels toward the target and a mis-aimed pivot is something
|
|
995
|
+
* you can dolly out of; under an orthographic one the wheel scales a frustum,
|
|
996
|
+
* so nothing about zooming ever re-aims the pivot, and F9 measured a cursor
|
|
997
|
+
* zoom leaving it in space off the part's corner.
|
|
998
|
+
*
|
|
999
|
+
* Pure, so the arithmetic is testable without a scene: three vectors in, two
|
|
1000
|
+
* out, no camera and no controls.
|
|
1001
|
+
*/
|
|
1002
|
+
declare function retargetPose(cameraPosition: Vec3, currentTarget: Vec3, hitPoint: Vec3): RetargetPose;
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* The orbit target made briefly visible.
|
|
1006
|
+
*
|
|
1007
|
+
* The pivot is the one thing the viewport moves about and never shows. It is
|
|
1008
|
+
* also the one thing that moves without being dragged: zooming to the cursor
|
|
1009
|
+
* walks it, a double click re-aims it, Fit puts it back. So "why did the part
|
|
1010
|
+
* swing that way" has an answer the screen has never given.
|
|
1011
|
+
*
|
|
1012
|
+
* It is an aid rather than furniture, which is why it is measured in time as
|
|
1013
|
+
* well as in pixels — up while a gesture is running, a flash when the pivot
|
|
1014
|
+
* moves on its own, and gone otherwise. A marker that stayed would be a dot in
|
|
1015
|
+
* the middle of every screenshot.
|
|
1016
|
+
*/
|
|
1017
|
+
/** Radius of the solid centre dot, in CSS pixels. */
|
|
1018
|
+
declare const ORBIT_TARGET_PIXELS = 3;
|
|
1019
|
+
/** Outer radius of the ring around it, in CSS pixels. */
|
|
1020
|
+
declare const ORBIT_TARGET_RING_PIXELS = 14;
|
|
1021
|
+
/** The ring's thickness, as a fraction of its outer radius. */
|
|
1022
|
+
declare const ORBIT_TARGET_RING_WIDTH = 0.16;
|
|
1023
|
+
/**
|
|
1024
|
+
* A dot inside a ring, rather than the legacy viewer's two nested spheres.
|
|
1025
|
+
*
|
|
1026
|
+
* Legacy paired a depth-tested outer ball with a dot drawn over everything, so
|
|
1027
|
+
* the part burying the ball said the pivot was inside the material
|
|
1028
|
+
* (`use-target.ts:10-25`). Rendered against a real part that reads as a fault
|
|
1029
|
+
* rather than as a signal: a pivot sitting *on* a surface — which is exactly
|
|
1030
|
+
* what a double-click re-target produces, and the commonest case there is —
|
|
1031
|
+
* cuts the sphere in half, leaving a lens-shaped smudge with the dot off to one
|
|
1032
|
+
* side of it. The depth it was reporting is a question almost nobody asks; that
|
|
1033
|
+
* it looked broken is something everybody would see.
|
|
1034
|
+
*
|
|
1035
|
+
* A billboarded ring is the same marker with the ambiguity taken out. It is the
|
|
1036
|
+
* same shape at every pose, over any surface, and it is what a viewport
|
|
1037
|
+
* reticle looks like everywhere else.
|
|
1038
|
+
*/
|
|
1039
|
+
declare const ORBIT_TARGET_COLOR = 3948625;
|
|
1040
|
+
declare const ORBIT_TARGET_RING_COLOR = 7057587;
|
|
1041
|
+
/** A ring is a hint about where, not a second dot. */
|
|
1042
|
+
declare const ORBIT_TARGET_RING_OPACITY = 0.9;
|
|
1043
|
+
/**
|
|
1044
|
+
* Both colours are the package's own rather than {@link ViewerTheme} roles.
|
|
1045
|
+
*
|
|
1046
|
+
* Adding a required field to that interface is a breaking type change for
|
|
1047
|
+
* anybody who builds a whole theme by hand, which is a steep price for an aid
|
|
1048
|
+
* that is off by default and on screen for about a second at a time. They are
|
|
1049
|
+
* palette colours already in use — the slate the cube labels and the section
|
|
1050
|
+
* handle outline take, and the teal that outlines a section cut — so the marker
|
|
1051
|
+
* reads as part of the control family rather than as something imported. If a
|
|
1052
|
+
* consumer asks to re-colour it, the roles go in at the next major and nothing
|
|
1053
|
+
* about this module's shape changes.
|
|
1054
|
+
*/
|
|
1055
|
+
/**
|
|
1056
|
+
* How long the marker stays at full strength after the pivot moves on its own,
|
|
1057
|
+
* in milliseconds. Legacy's (`use-target.ts:96`).
|
|
1058
|
+
*/
|
|
1059
|
+
declare const ORBIT_TARGET_FLASH_MS = 100;
|
|
1060
|
+
/**
|
|
1061
|
+
* How long it takes to fade once nothing is holding it up, in milliseconds.
|
|
1062
|
+
*
|
|
1063
|
+
* Legacy faded in twenty steps of 0.05 on a 50 ms timer, which is this number
|
|
1064
|
+
* arrived at the long way round (`use-target.ts:79-87`). Long enough that
|
|
1065
|
+
* letting go of an orbit does not make the marker vanish before the eye finds
|
|
1066
|
+
* it, short enough that it is gone before anybody wonders what it is.
|
|
1067
|
+
*/
|
|
1068
|
+
declare const ORBIT_TARGET_FADE_MS = 1000;
|
|
1069
|
+
/**
|
|
1070
|
+
* How visible the marker is, `sinceHold` milliseconds after the last thing
|
|
1071
|
+
* holding it up let go.
|
|
1072
|
+
*
|
|
1073
|
+
* Linear, and negative input means something still is — a drag in progress, or
|
|
1074
|
+
* a flash inside its window — so the caller can pass one number rather than
|
|
1075
|
+
* branching on which of the two it is.
|
|
1076
|
+
*
|
|
1077
|
+
* Pure because the fade has to be driven from a frame rather than a timer:
|
|
1078
|
+
* this viewer renders on demand, so a `setInterval` writing an opacity would
|
|
1079
|
+
* change the material and never repaint it. The curve therefore has to be
|
|
1080
|
+
* something a frame can ask for the value of, at whatever moment it runs.
|
|
1081
|
+
*/
|
|
1082
|
+
declare function orbitTargetOpacity(sinceHold: number, fade?: number): number;
|
|
1083
|
+
|
|
1084
|
+
export { type ArrowPlacement, Axes, type AxesProps, CAD_CAMERA_UP, CHAMFER, CadCameraControls, type CadCameraControlsProps, type CameraLimits, type ControlScheme, type CubeZone, DEFAULT_FIT_MARGIN, DOUBLE_TAP_MS, DirectionArrows, type DirectionArrowsProps, type DirectionGroup, type DoubleTapPoint, type DoubleTapTracker, EXCLUDE_FROM_FRAME, ExtendedCameraControls, type ExtendedCameraControlsOptions, FEATURE_TYPE_RANKS, FeatureTag, FeatureType, Grid, type GridProps, type GridSpec, MAX_FRAME_RATIO, MIN_FRAME_RATIO, type NamedDirection, ORBIT_TARGET_COLOR, ORBIT_TARGET_FADE_MS, ORBIT_TARGET_FLASH_MS, ORBIT_TARGET_PIXELS, ORBIT_TARGET_RING_COLOR, ORBIT_TARGET_RING_OPACITY, ORBIT_TARGET_RING_PIXELS, ORBIT_TARGET_RING_WIDTH, PERSPECTIVE_FOV, PartModel, PartModelFeature, PartModelRegion, PartReportFormatError, type Projection, type RankingContext, RegionIndex, type Retarget, type RetargetPose, type SceneBounds, type SurfaceOf, TAP_SLOP, type TapPoint, type TapTracker, TriangleRange, UnsupportedKernelVersionError, VIEW_NAMES, VIEW_SIGNS, Vec3, ViewCube, type ViewCubeProps, type ViewKind, type ViewName, Viewer, type ViewerCamera, type ViewerControls, type ViewerHandle, type ViewerProps, ViewerTheme, type ViewerView, type ViewportSize, adaptedUp, applyProjection, arrowPlacement, aspectRatio, bestOwner, boundsFromBox, buildRegionIndex, cadViewDirections, cameraLimits, contentBounds, cubeOutlineGeometry, cubeZones, currentViewDirection, cycleOwner, defaultBounds, directionIndexOf, directionLabel, featureTypeRank, fitDistance, gridGeometry, gridSpec, groupByDirection, labelGeometry, labelTexture, movedFar, orbitTargetOpacity, orthographicHalfHeight, panelGeometry, perspectiveFitDistance, rankOwners, regionAdjacency, regionEdgesGeometry, regionNormals, retargetPose, sameDirection, screenLength, squaredUp, startPosition, targetBoundary, trackDoubleTaps, trackTaps, useContentBox, useRetarget, useTapGuard, useViewerControls, viewKind, viewUp, viewVector, visualSurfaces };
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,17 @@ import {
|
|
|
18
18
|
HIGHLIGHT_COLORS,
|
|
19
19
|
HIGHLIGHT_WEIGHT,
|
|
20
20
|
HOVER_WEIGHT,
|
|
21
|
+
MAX_FRAME_RATIO,
|
|
22
|
+
MIN_FRAME_RATIO,
|
|
21
23
|
NO_MODIFIERS,
|
|
24
|
+
ORBIT_TARGET_COLOR,
|
|
25
|
+
ORBIT_TARGET_FADE_MS,
|
|
26
|
+
ORBIT_TARGET_FLASH_MS,
|
|
27
|
+
ORBIT_TARGET_PIXELS,
|
|
28
|
+
ORBIT_TARGET_RING_COLOR,
|
|
29
|
+
ORBIT_TARGET_RING_OPACITY,
|
|
30
|
+
ORBIT_TARGET_RING_PIXELS,
|
|
31
|
+
ORBIT_TARGET_RING_WIDTH,
|
|
22
32
|
PERSPECTIVE_FOV,
|
|
23
33
|
PICKED_SURFACE_LABEL,
|
|
24
34
|
PartMesh,
|
|
@@ -32,6 +42,7 @@ import {
|
|
|
32
42
|
VIEW_NAMES,
|
|
33
43
|
VIEW_SIGNS,
|
|
34
44
|
Viewer,
|
|
45
|
+
adaptedUp,
|
|
35
46
|
applyHighlightLayers,
|
|
36
47
|
applyProjection,
|
|
37
48
|
arrowPlacement,
|
|
@@ -43,6 +54,7 @@ import {
|
|
|
43
54
|
buildRegionIndex,
|
|
44
55
|
buildRegionTexels,
|
|
45
56
|
cadViewDirections,
|
|
57
|
+
cameraLimits,
|
|
46
58
|
contentBounds,
|
|
47
59
|
createPart,
|
|
48
60
|
cubeOutlineGeometry,
|
|
@@ -62,6 +74,7 @@ import {
|
|
|
62
74
|
labelTexture,
|
|
63
75
|
movedFar,
|
|
64
76
|
normalizePartReport,
|
|
77
|
+
orbitTargetOpacity,
|
|
65
78
|
orthographicHalfHeight,
|
|
66
79
|
panelGeometry,
|
|
67
80
|
perspectiveFitDistance,
|
|
@@ -71,6 +84,7 @@ import {
|
|
|
71
84
|
regionEdgesGeometry,
|
|
72
85
|
resolveSectionPlane,
|
|
73
86
|
resolveTheme,
|
|
87
|
+
retargetPose,
|
|
74
88
|
sameDirection,
|
|
75
89
|
screenLength,
|
|
76
90
|
sectionBounds,
|
|
@@ -84,10 +98,12 @@ import {
|
|
|
84
98
|
smoothRegionNormals,
|
|
85
99
|
squaredUp,
|
|
86
100
|
startPosition,
|
|
101
|
+
targetBoundary,
|
|
87
102
|
themesEqual,
|
|
88
103
|
trackDoubleTaps,
|
|
89
104
|
trackTaps,
|
|
90
105
|
useContentBox,
|
|
106
|
+
useRetarget,
|
|
91
107
|
useTapGuard,
|
|
92
108
|
useViewerControls,
|
|
93
109
|
viewDirection,
|
|
@@ -95,7 +111,7 @@ import {
|
|
|
95
111
|
viewUp,
|
|
96
112
|
viewVector,
|
|
97
113
|
visualSurfaces
|
|
98
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-4BU4QZCL.js";
|
|
99
115
|
|
|
100
116
|
// src/primitives.tsx
|
|
101
117
|
import { GizmoHelper } from "@react-three/drei";
|
|
@@ -388,7 +404,17 @@ export {
|
|
|
388
404
|
HIGHLIGHT_COLORS,
|
|
389
405
|
HIGHLIGHT_WEIGHT,
|
|
390
406
|
HOVER_WEIGHT,
|
|
407
|
+
MAX_FRAME_RATIO,
|
|
408
|
+
MIN_FRAME_RATIO,
|
|
391
409
|
NO_MODIFIERS,
|
|
410
|
+
ORBIT_TARGET_COLOR,
|
|
411
|
+
ORBIT_TARGET_FADE_MS,
|
|
412
|
+
ORBIT_TARGET_FLASH_MS,
|
|
413
|
+
ORBIT_TARGET_PIXELS,
|
|
414
|
+
ORBIT_TARGET_RING_COLOR,
|
|
415
|
+
ORBIT_TARGET_RING_OPACITY,
|
|
416
|
+
ORBIT_TARGET_RING_PIXELS,
|
|
417
|
+
ORBIT_TARGET_RING_WIDTH,
|
|
392
418
|
PERSPECTIVE_FOV,
|
|
393
419
|
PICKED_SURFACE_LABEL,
|
|
394
420
|
PartMesh,
|
|
@@ -402,6 +428,7 @@ export {
|
|
|
402
428
|
VIEW_SIGNS,
|
|
403
429
|
ViewCube,
|
|
404
430
|
Viewer,
|
|
431
|
+
adaptedUp,
|
|
405
432
|
applyHighlightLayers,
|
|
406
433
|
applyProjection,
|
|
407
434
|
arrowPlacement,
|
|
@@ -413,6 +440,7 @@ export {
|
|
|
413
440
|
buildRegionIndex,
|
|
414
441
|
buildRegionTexels,
|
|
415
442
|
cadViewDirections,
|
|
443
|
+
cameraLimits,
|
|
416
444
|
contentBounds,
|
|
417
445
|
createPart,
|
|
418
446
|
cubeOutlineGeometry,
|
|
@@ -434,6 +462,7 @@ export {
|
|
|
434
462
|
labelTexture,
|
|
435
463
|
movedFar,
|
|
436
464
|
normalizePartReport,
|
|
465
|
+
orbitTargetOpacity,
|
|
437
466
|
orthographicHalfHeight,
|
|
438
467
|
panelGeometry,
|
|
439
468
|
perspectiveFitDistance,
|
|
@@ -444,6 +473,7 @@ export {
|
|
|
444
473
|
regionNormals,
|
|
445
474
|
resolveSectionPlane,
|
|
446
475
|
resolveTheme,
|
|
476
|
+
retargetPose,
|
|
447
477
|
sameDirection,
|
|
448
478
|
screenLength,
|
|
449
479
|
sectionBounds,
|
|
@@ -457,10 +487,12 @@ export {
|
|
|
457
487
|
smoothRegionNormals,
|
|
458
488
|
squaredUp,
|
|
459
489
|
startPosition,
|
|
490
|
+
targetBoundary,
|
|
460
491
|
themesEqual,
|
|
461
492
|
trackDoubleTaps,
|
|
462
493
|
trackTaps,
|
|
463
494
|
useContentBox,
|
|
495
|
+
useRetarget,
|
|
464
496
|
useTapGuard,
|
|
465
497
|
useViewerControls,
|
|
466
498
|
viewDirection,
|