@reekon-tools/boldr-utils 1.19.5 → 1.20.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/annotation/canvas/AnnotationCanvasInner.d.ts +2 -0
- package/dist/annotation/canvas/AnnotationCanvasInner.js +12 -1
- package/dist/annotation/canvas/AnnotationCanvasInner.native.d.ts +2 -0
- package/dist/annotation/canvas/AnnotationCanvasInner.native.js +329 -35
- package/dist/annotation/canvas/AnnotationCanvasSkia.d.ts +28 -1
- package/dist/annotation/canvas/AnnotationCanvasSkia.js +99 -14
- package/dist/annotation/canvas/Tool.d.ts +10 -1
- package/dist/annotation/canvas/backgroundLayers.d.ts +20 -1
- package/dist/annotation/canvas/backgroundLayers.js +94 -3
- package/dist/annotation/canvas/elements/BackgroundImageElement.d.ts +2 -1
- package/dist/annotation/canvas/elements/BackgroundImageElement.js +10 -2
- package/dist/annotation/canvas/fileTileSwap.d.ts +12 -0
- package/dist/annotation/canvas/fileTileSwap.js +87 -0
- package/dist/annotation/canvas/snapGeometry.d.ts +32 -0
- package/dist/annotation/canvas/snapGeometry.js +168 -0
- package/dist/annotation/canvas/stampLayout.d.ts +1 -1
- package/dist/annotation/canvas/stampLayout.js +10 -8
- package/dist/annotation/canvas/tools/measurementTool.js +38 -4
- package/dist/annotation/canvas/tools/selectTool.d.ts +18 -0
- package/dist/annotation/canvas/tools/selectTool.js +201 -43
- package/dist/annotation/canvas/tools/shapeTool.js +27 -5
- package/dist/annotation/canvas/useAnnotationCanvasState.d.ts +2 -0
- package/dist/annotation/canvas/useAnnotationCanvasState.js +19 -2
- package/dist/annotation/data/hooks/useAnnotationCanvasDoc.js +5 -1
- package/dist/exports.d.ts +3 -1
- package/dist/exports.js +2 -0
- package/dist/types/annotation.d.ts +10 -0
- package/package.json +1 -1
- package/dist/canvas/AnnotationCanvas.d.ts +0 -11
- package/dist/canvas/AnnotationCanvas.js +0 -10
- package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
- package/dist/canvas/AnnotationCanvas.native.js +0 -6
- package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
- package/dist/canvas/AnnotationCanvasInner.js +0 -219
- package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
- package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
- package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
- package/dist/canvas/AnnotationCanvasSkia.js +0 -20
- package/dist/canvas/Tool.d.ts +0 -38
- package/dist/canvas/Tool.js +0 -1
- package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
- package/dist/canvas/elements/BackgroundImageElement.js +0 -37
- package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
- package/dist/canvas/elements/MeasurementStampElement.js +0 -30
- package/dist/canvas/elements/ShapeElement.d.ts +0 -7
- package/dist/canvas/elements/ShapeElement.js +0 -62
- package/dist/canvas/elements/StrokeElement.d.ts +0 -7
- package/dist/canvas/elements/StrokeElement.js +0 -18
- package/dist/canvas/measurementPicker.d.ts +0 -10
- package/dist/canvas/measurementPicker.js +0 -1
- package/dist/canvas/measurementStampOverlay.d.ts +0 -11
- package/dist/canvas/measurementStampOverlay.js +0 -1
- package/dist/canvas/pointerAdapter.d.ts +0 -3
- package/dist/canvas/pointerAdapter.js +0 -19
- package/dist/canvas/stampLayout.d.ts +0 -5
- package/dist/canvas/stampLayout.js +0 -14
- package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
- package/dist/canvas/tools/measurementStampTool.js +0 -37
- package/dist/canvas/tools/panTool.d.ts +0 -5
- package/dist/canvas/tools/panTool.js +0 -25
- package/dist/canvas/tools/penTool.d.ts +0 -13
- package/dist/canvas/tools/penTool.js +0 -68
- package/dist/canvas/tools/selectTool.d.ts +0 -2
- package/dist/canvas/tools/selectTool.js +0 -182
- package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
- package/dist/canvas/useAnnotationCanvasState.js +0 -210
- package/dist/canvas/viewport.d.ts +0 -16
- package/dist/canvas/viewport.js +0 -54
- package/dist/data/AnnotationDataContext.d.ts +0 -8
- package/dist/data/AnnotationDataContext.js +0 -11
- package/dist/data/AnnotationDataProvider.d.ts +0 -65
- package/dist/data/AnnotationDataProvider.js +0 -4
- package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
- package/dist/data/InMemoryAnnotationProvider.js +0 -197
- package/dist/data/canvasPersistence.d.ts +0 -3
- package/dist/data/canvasPersistence.js +0 -26
- package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
- package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
- package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
- package/dist/data/hooks/useAnnotationDoc.js +0 -33
- package/dist/data/hooks/useAnnotationList.d.ts +0 -7
- package/dist/data/hooks/useAnnotationList.js +0 -26
- package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
- package/dist/data/hooks/useAnnotationMutations.js +0 -11
- package/dist/hooks/useParseMeasurement.d.ts +0 -4
- package/dist/hooks/useParseMeasurement.js +0 -14
- package/dist/utils/evaluateFormula.d.ts +0 -20
- package/dist/utils/evaluateFormula.js +0 -31
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { backgroundLayerDocRect, backgroundLayerIsLocked, backgroundLayersOf, isBackgroundLayerLocked, } from '../backgroundLayers.js';
|
|
1
|
+
import { MAX_BACKGROUND_DOC_EXTENT, MIN_BACKGROUND_DOC_EXTENT, backgroundCropBars, backgroundLayerDocRect, backgroundLayerFullDocRect, backgroundLayerIsLocked, backgroundLayersOf, isBackgroundLayerLocked, } from '../backgroundLayers.js';
|
|
2
2
|
import { stampTileDims } from '../stampLayout.js';
|
|
3
3
|
import { placementOf, linePosOf, snapLinePos, lerp, recomputeAnchor, rectCenter, rectCornerPoint, oppositeRectCorner, hitPlacedMeasurement, } from '../measurementGeometry.js';
|
|
4
4
|
import { hitShapeOutline, visualShapeBounds } from '../shapeGeometry.js';
|
|
5
|
+
import { collectSnapTargetsFlat, snapDelta } from '../snapGeometry.js';
|
|
5
6
|
import { DEFAULT_TEXT_FONT_SIZE, resizeScaleFromDrag, textResizeGeometry, textShapeBounds, } from '../textGeometry.js';
|
|
6
7
|
import { editTextShape } from './textEditing.js';
|
|
7
8
|
const HIT_PADDING = 6;
|
|
@@ -238,6 +239,32 @@ const slidePatch = (doc, id, delta, zoom) => {
|
|
|
238
239
|
ops: [{ op: 'updateMeasurement', id, patch: { linePos: t, anchor } }],
|
|
239
240
|
};
|
|
240
241
|
};
|
|
242
|
+
// Committed position of the grabbed endpoint — the base the drag delta (and
|
|
243
|
+
// the snap) is measured from. Measurement lines keep endpoints on `line.a/b`,
|
|
244
|
+
// shape lines on `geometry.points[0/1]`.
|
|
245
|
+
const endpointBase = (doc, id, elementKind, handle) => {
|
|
246
|
+
if (elementKind === 'shape') {
|
|
247
|
+
const sh = doc.shapes.find((x) => x.id === id);
|
|
248
|
+
const p = sh?.geometry.points[handle === 'a' ? 0 : 1];
|
|
249
|
+
return p ?? null;
|
|
250
|
+
}
|
|
251
|
+
const m = doc.placedMeasurements.find((x) => x.id === id);
|
|
252
|
+
if (!m?.line)
|
|
253
|
+
return null;
|
|
254
|
+
return handle === 'a' ? m.line.a : m.line.b;
|
|
255
|
+
};
|
|
256
|
+
// Snap bookkeeping for an endpoint grab: harvest every OTHER element's targets
|
|
257
|
+
// once (ctx.document is the committed doc, so in-flight previews never leak
|
|
258
|
+
// in) and freeze the capture radius in doc space at the grab zoom. Empty when
|
|
259
|
+
// snapping is off, so the drag stays a plain delta.
|
|
260
|
+
const snapStateForGrab = (ctx, excludeId, zoom) => ctx.snap?.enabled
|
|
261
|
+
? {
|
|
262
|
+
snap: {
|
|
263
|
+
targets: collectSnapTargetsFlat(ctx.document, excludeId),
|
|
264
|
+
radiusDoc: ctx.snap.radiusPx / zoom,
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
: {};
|
|
241
268
|
// Which endpoint handle of a (selected) line annotation is under `world`.
|
|
242
269
|
// Prefers the nearer endpoint when both are within range.
|
|
243
270
|
const findHandleHit = (doc, id, world, zoom) => {
|
|
@@ -468,12 +495,8 @@ const shapeCornerPatch = (doc, id, corner, delta) => {
|
|
|
468
495
|
// other, but never moves or scales:
|
|
469
496
|
// the drag/resize hit-tests below refuse it, and a gesture on it behaves like
|
|
470
497
|
// empty canvas (pan) while leaving the selection in place. ---
|
|
471
|
-
// Doc-space
|
|
472
|
-
//
|
|
473
|
-
// ceiling keeps the doc rect (which feeds fit math and the web SVG raster
|
|
474
|
-
// planner) from exploding to numerically silly sizes.
|
|
475
|
-
const MIN_BACKGROUND_DOC_EXTENT = 16;
|
|
476
|
-
const MAX_BACKGROUND_DOC_EXTENT = 65536;
|
|
498
|
+
// Doc-space size clamps live in backgroundLayers.ts (the native crop preview
|
|
499
|
+
// worklet shares the floor).
|
|
477
500
|
const backgroundLayerById = (doc, id) => backgroundLayersOf(doc.viewport).find((l) => l.id === id) ?? null;
|
|
478
501
|
const pointInBackgroundLayer = (layer, p) => {
|
|
479
502
|
const r = backgroundLayerDocRect(layer);
|
|
@@ -511,19 +534,30 @@ const findSelectedBackgroundHit = (doc, world, selectedIds) => {
|
|
|
511
534
|
}
|
|
512
535
|
return null;
|
|
513
536
|
};
|
|
514
|
-
// Corner-scale geometry for a background layer:
|
|
515
|
-
//
|
|
516
|
-
//
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
|
|
537
|
+
// Corner-scale geometry for a background layer: handle = the grabbed corner
|
|
538
|
+
// of its VISIBLE (cropped) doc rect, pivot = the opposite corner, which stays
|
|
539
|
+
// put — so the patch scales scaleX/scaleY AND repositions transform.x/y about
|
|
540
|
+
// the pivot (unlike text, whose top-left anchor is always the pivot). The
|
|
541
|
+
// chrome draws all four handles un-padded on the exact rect corners; the grab
|
|
542
|
+
// tolerance lives in the hit radius, not the geometry.
|
|
543
|
+
const BACKGROUND_RESIZE_CORNERS = [
|
|
544
|
+
'tl',
|
|
545
|
+
'tr',
|
|
546
|
+
'bl',
|
|
547
|
+
'br',
|
|
548
|
+
];
|
|
549
|
+
const backgroundResizeGeometry = (layer, corner) => {
|
|
520
550
|
const r = backgroundLayerDocRect(layer);
|
|
521
551
|
const maxDim = Math.max(r.width, r.height);
|
|
522
552
|
if (!(maxDim > 0))
|
|
523
553
|
return null;
|
|
554
|
+
const cornerPoint = (c) => ({
|
|
555
|
+
x: c === 'tl' || c === 'bl' ? r.x : r.x + r.width,
|
|
556
|
+
y: c === 'tl' || c === 'tr' ? r.y : r.y + r.height,
|
|
557
|
+
});
|
|
524
558
|
return {
|
|
525
|
-
pivot:
|
|
526
|
-
handle:
|
|
559
|
+
pivot: cornerPoint(oppositeRectCorner(corner)),
|
|
560
|
+
handle: cornerPoint(corner),
|
|
527
561
|
minScale: MIN_BACKGROUND_DOC_EXTENT / maxDim,
|
|
528
562
|
maxScale: MAX_BACKGROUND_DOC_EXTENT / maxDim,
|
|
529
563
|
};
|
|
@@ -532,41 +566,51 @@ const backgroundResizeGeometry = (layer) => {
|
|
|
532
566
|
// native UI-thread drag via DragSelectionConfig AND the web pointer handlers) ---
|
|
533
567
|
// Resize geometry when the (selected) text shape's or background layer's
|
|
534
568
|
// corner handle is under `world`, else null. A text handle sits on the padded
|
|
535
|
-
// selection box's bottom-right corner
|
|
536
|
-
// rect
|
|
537
|
-
// endpoint handles either way.
|
|
569
|
+
// selection box's bottom-right corner; a background offers all FOUR exact doc
|
|
570
|
+
// rect corners, each scaling about its opposite (see AnnotationCanvasSkia);
|
|
571
|
+
// grab radius matches the measurement endpoint handles either way.
|
|
538
572
|
const findResizeHandleHit = (doc, id, world, zoom) => {
|
|
573
|
+
const r2 = (HANDLE_GRAB_PX / zoom) ** 2;
|
|
574
|
+
const within = (geom) => {
|
|
575
|
+
const dx = world.x - geom.handle.x;
|
|
576
|
+
const dy = world.y - geom.handle.y;
|
|
577
|
+
return dx * dx + dy * dy <= r2;
|
|
578
|
+
};
|
|
539
579
|
const s = doc.shapes.find((x) => x.id === id);
|
|
540
|
-
|
|
580
|
+
if (s) {
|
|
581
|
+
const geom = textResizeGeometry(s);
|
|
582
|
+
return geom && within(geom) ? geom : null;
|
|
583
|
+
}
|
|
584
|
+
// A locked background cannot scale, so its corners offer no handles (the
|
|
541
585
|
// chrome draws none either — see AnnotationCanvasSkia).
|
|
542
|
-
|
|
543
|
-
? null
|
|
544
|
-
: backgroundLayerById(doc, id);
|
|
545
|
-
const geom = s
|
|
546
|
-
? textResizeGeometry(s)
|
|
547
|
-
: layer
|
|
548
|
-
? backgroundResizeGeometry(layer)
|
|
549
|
-
: null;
|
|
550
|
-
if (!geom)
|
|
586
|
+
if (isBackgroundLayerLocked(doc.viewport, id))
|
|
551
587
|
return null;
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
588
|
+
const layer = backgroundLayerById(doc, id);
|
|
589
|
+
if (!layer)
|
|
590
|
+
return null;
|
|
591
|
+
for (const corner of BACKGROUND_RESIZE_CORNERS) {
|
|
592
|
+
const geom = backgroundResizeGeometry(layer, corner);
|
|
593
|
+
if (geom && within(geom))
|
|
594
|
+
return geom;
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
556
597
|
};
|
|
557
598
|
// Scale the element by the drag (clamped to the geometry's scale range, so it
|
|
558
|
-
// matches the native live preview exactly).
|
|
559
|
-
//
|
|
560
|
-
//
|
|
561
|
-
// scaleX/scaleY
|
|
562
|
-
//
|
|
563
|
-
|
|
599
|
+
// matches the native live preview exactly). A text shape keeps its top-left
|
|
600
|
+
// anchor (the pivot) and bakes the scale into fontSize. A background scales
|
|
601
|
+
// uniformly about `geom.pivot` — the corner OPPOSITE the grabbed one — so
|
|
602
|
+
// scaleX/scaleY multiply AND transform.x/y move to keep the pivot fixed
|
|
603
|
+
// (both axes scale together, preserving the aspect ratio even on layers
|
|
604
|
+
// synthesized from a legacy 'stretch' fit). `geom` is the grab-start geometry
|
|
605
|
+
// the hit-test chose; without it (defensive) the bottom-right corner's
|
|
606
|
+
// geometry is assumed, which reproduces the pre-four-corner behavior.
|
|
607
|
+
const resizePatch = (doc, id, delta, grabGeom) => {
|
|
564
608
|
const s = doc.shapes.find((x) => x.id === id);
|
|
565
609
|
if (!s) {
|
|
566
610
|
const layer = backgroundLayerById(doc, id);
|
|
567
611
|
if (!layer || isBackgroundLayerLocked(doc.viewport, id))
|
|
568
612
|
return null;
|
|
569
|
-
const geom = backgroundResizeGeometry(layer);
|
|
613
|
+
const geom = grabGeom ?? backgroundResizeGeometry(layer, 'br');
|
|
570
614
|
if (!geom)
|
|
571
615
|
return null;
|
|
572
616
|
const scale = resizeScaleFromDrag(geom, delta);
|
|
@@ -577,7 +621,8 @@ const resizePatch = (doc, id, delta) => {
|
|
|
577
621
|
id,
|
|
578
622
|
patch: {
|
|
579
623
|
transform: {
|
|
580
|
-
|
|
624
|
+
x: geom.pivot.x + (layer.transform.x - geom.pivot.x) * scale,
|
|
625
|
+
y: geom.pivot.y + (layer.transform.y - geom.pivot.y) * scale,
|
|
581
626
|
scaleX: layer.transform.scaleX * scale,
|
|
582
627
|
scaleY: layer.transform.scaleY * scale,
|
|
583
628
|
},
|
|
@@ -597,6 +642,82 @@ const resizePatch = (doc, id, delta) => {
|
|
|
597
642
|
],
|
|
598
643
|
};
|
|
599
644
|
};
|
|
645
|
+
// --- Background crop (the four edge bars on a selected, unlocked layer) ----
|
|
646
|
+
// Which crop bar of the given (selected) background layer is under `world`,
|
|
647
|
+
// or null. Bars exist only on an unlocked RASTER layer (an SVG layer's
|
|
648
|
+
// doc-rect math — the web rasterizer — never sees a crop). The bar rect is
|
|
649
|
+
// the drawn gizmo (backgroundCropBars) padded by the standard screen-constant
|
|
650
|
+
// grab tolerance.
|
|
651
|
+
const findBackgroundCropBarHit = (doc, id, world, zoom) => {
|
|
652
|
+
if (isBackgroundLayerLocked(doc.viewport, id))
|
|
653
|
+
return null;
|
|
654
|
+
const layer = backgroundLayerById(doc, id);
|
|
655
|
+
if (!layer || layer.format === 'svg')
|
|
656
|
+
return null;
|
|
657
|
+
const pad = HANDLE_GRAB_PX / zoom;
|
|
658
|
+
for (const bar of backgroundCropBars(layer)) {
|
|
659
|
+
if (world.x >= bar.x - pad &&
|
|
660
|
+
world.x <= bar.x + bar.width + pad &&
|
|
661
|
+
world.y >= bar.y - pad &&
|
|
662
|
+
world.y <= bar.y + bar.height + pad) {
|
|
663
|
+
return bar.edge;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return null;
|
|
667
|
+
};
|
|
668
|
+
// New VISIBLE doc rect for a crop-bar drag: the grabbed edge follows the
|
|
669
|
+
// delta, clamped so the rect never leaves the full image's bounds (dragging
|
|
670
|
+
// back out un-crops — the bytes were never touched) and never collapses below
|
|
671
|
+
// the same doc-extent floor a corner resize enforces. The native live-preview
|
|
672
|
+
// worklet in AnnotationCanvasInner.native.tsx is a WORKLET TWIN of this math
|
|
673
|
+
// — keep them in sync.
|
|
674
|
+
export const cropDragVisibleRect = (start, full, edge, delta) => {
|
|
675
|
+
let { x, y, width, height } = start;
|
|
676
|
+
if (edge === 'top') {
|
|
677
|
+
const newY = Math.min(Math.max(start.y + delta.y, full.y), start.y + start.height - MIN_BACKGROUND_DOC_EXTENT);
|
|
678
|
+
height = start.y + start.height - newY;
|
|
679
|
+
y = newY;
|
|
680
|
+
}
|
|
681
|
+
else if (edge === 'bottom') {
|
|
682
|
+
height = Math.min(Math.max(start.height + delta.y, MIN_BACKGROUND_DOC_EXTENT), full.y + full.height - start.y);
|
|
683
|
+
}
|
|
684
|
+
else if (edge === 'left') {
|
|
685
|
+
const newX = Math.min(Math.max(start.x + delta.x, full.x), start.x + start.width - MIN_BACKGROUND_DOC_EXTENT);
|
|
686
|
+
width = start.x + start.width - newX;
|
|
687
|
+
x = newX;
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
width = Math.min(Math.max(start.width + delta.x, MIN_BACKGROUND_DOC_EXTENT), full.x + full.width - start.x);
|
|
691
|
+
}
|
|
692
|
+
return { x, y, width, height };
|
|
693
|
+
};
|
|
694
|
+
// Patch that commits a crop-bar drag: the new visible rect (clamped exactly
|
|
695
|
+
// like the live preview) converted back to an intrinsic-pixel crop window.
|
|
696
|
+
const backgroundCropPatch = (doc, id, edge, delta) => {
|
|
697
|
+
const layer = backgroundLayerById(doc, id);
|
|
698
|
+
if (!layer || isBackgroundLayerLocked(doc.viewport, id))
|
|
699
|
+
return null;
|
|
700
|
+
const { scaleX, scaleY } = layer.transform;
|
|
701
|
+
if (!(scaleX > 0) || !(scaleY > 0))
|
|
702
|
+
return null;
|
|
703
|
+
const visible = cropDragVisibleRect(backgroundLayerDocRect(layer), backgroundLayerFullDocRect(layer), edge, delta);
|
|
704
|
+
return {
|
|
705
|
+
ops: [
|
|
706
|
+
{
|
|
707
|
+
op: 'updateBackgroundImage',
|
|
708
|
+
id,
|
|
709
|
+
patch: {
|
|
710
|
+
crop: {
|
|
711
|
+
x: (visible.x - layer.transform.x) / scaleX,
|
|
712
|
+
y: (visible.y - layer.transform.y) / scaleY,
|
|
713
|
+
width: visible.width / scaleX,
|
|
714
|
+
height: visible.height / scaleY,
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
};
|
|
720
|
+
};
|
|
600
721
|
// Patch for the current drag mode (web pointer path), from a world-space delta.
|
|
601
722
|
const dragPatch = (s, doc, delta, zoom) => {
|
|
602
723
|
if (s.mode === 'endpoint' && s.handle) {
|
|
@@ -609,7 +730,10 @@ const dragPatch = (s, doc, delta, zoom) => {
|
|
|
609
730
|
if (s.mode === 'slide')
|
|
610
731
|
return slidePatch(doc, s.id, delta, zoom);
|
|
611
732
|
if (s.mode === 'resize')
|
|
612
|
-
return resizePatch(doc, s.id, delta);
|
|
733
|
+
return resizePatch(doc, s.id, delta, s.geom);
|
|
734
|
+
if (s.mode === 'bg-crop' && s.cropEdge) {
|
|
735
|
+
return backgroundCropPatch(doc, s.id, s.cropEdge, delta);
|
|
736
|
+
}
|
|
613
737
|
if (s.mode === 'rect-corner' && s.corner) {
|
|
614
738
|
return rectCornerPatch(doc, s.id, s.corner, delta);
|
|
615
739
|
}
|
|
@@ -643,6 +767,8 @@ export const createSelectTool = (options = {}) => ({
|
|
|
643
767
|
buildResizePatch: resizePatch,
|
|
644
768
|
hitTestBackground: findBackgroundHit,
|
|
645
769
|
hitTestSelectedBackground: findSelectedBackgroundHit,
|
|
770
|
+
hitTestBackgroundCropBar: findBackgroundCropBarHit,
|
|
771
|
+
buildBackgroundCropPatch: backgroundCropPatch,
|
|
646
772
|
hitTestRectCorner: findRectCornerHit,
|
|
647
773
|
buildRectCornerPatch: rectCornerPatch,
|
|
648
774
|
hitTestShapeCorner: findShapeCornerHit,
|
|
@@ -678,6 +804,7 @@ export const createSelectTool = (options = {}) => ({
|
|
|
678
804
|
handle,
|
|
679
805
|
start: world,
|
|
680
806
|
delta: { x: 0, y: 0 },
|
|
807
|
+
...snapStateForGrab(ctx, selId, zoom),
|
|
681
808
|
};
|
|
682
809
|
}
|
|
683
810
|
const shapeHandle = findShapeHandleHit(ctx.document, selId, world, zoom);
|
|
@@ -691,9 +818,11 @@ export const createSelectTool = (options = {}) => ({
|
|
|
691
818
|
handle: shapeHandle,
|
|
692
819
|
start: world,
|
|
693
820
|
delta: { x: 0, y: 0 },
|
|
821
|
+
...snapStateForGrab(ctx, selId, zoom),
|
|
694
822
|
};
|
|
695
823
|
}
|
|
696
|
-
|
|
824
|
+
const resizeGeom = findResizeHandleHit(ctx.document, selId, world, zoom);
|
|
825
|
+
if (resizeGeom) {
|
|
697
826
|
return {
|
|
698
827
|
kind: 'dragging',
|
|
699
828
|
id: selId,
|
|
@@ -704,6 +833,22 @@ export const createSelectTool = (options = {}) => ({
|
|
|
704
833
|
? 'background'
|
|
705
834
|
: 'shape',
|
|
706
835
|
mode: 'resize',
|
|
836
|
+
// The grabbed corner's geometry — a background scales about the
|
|
837
|
+
// OPPOSITE corner, so the patch must reuse what the grab chose.
|
|
838
|
+
geom: resizeGeom,
|
|
839
|
+
start: world,
|
|
840
|
+
delta: { x: 0, y: 0 },
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
// Crop bar on a selected, unlocked background layer.
|
|
844
|
+
const cropEdge = findBackgroundCropBarHit(ctx.document, selId, world, zoom);
|
|
845
|
+
if (cropEdge) {
|
|
846
|
+
return {
|
|
847
|
+
kind: 'dragging',
|
|
848
|
+
id: selId,
|
|
849
|
+
elementKind: 'background',
|
|
850
|
+
mode: 'bg-crop',
|
|
851
|
+
cropEdge,
|
|
707
852
|
start: world,
|
|
708
853
|
delta: { x: 0, y: 0 },
|
|
709
854
|
};
|
|
@@ -819,7 +964,7 @@ export const createSelectTool = (options = {}) => ({
|
|
|
819
964
|
}
|
|
820
965
|
if (s?.kind !== 'dragging')
|
|
821
966
|
return s;
|
|
822
|
-
|
|
967
|
+
let delta = {
|
|
823
968
|
x: event.world.x - s.start.x,
|
|
824
969
|
y: event.world.y - s.start.y,
|
|
825
970
|
};
|
|
@@ -829,12 +974,24 @@ export const createSelectTool = (options = {}) => ({
|
|
|
829
974
|
pendingTextEditId = null;
|
|
830
975
|
pendingBackgroundDeselectId = null;
|
|
831
976
|
}
|
|
977
|
+
// Magnetic endpoint snap: bend the delta so the grabbed end lands on the
|
|
978
|
+
// nearest target in range, and show the ring there. The snapped delta is
|
|
979
|
+
// what gets stored, so onPointerUp's commit reuses it unchanged.
|
|
980
|
+
if (s.mode === 'endpoint' && s.snap && s.handle) {
|
|
981
|
+
const base = endpointBase(ctx.document, s.id, s.elementKind, s.handle);
|
|
982
|
+
if (base) {
|
|
983
|
+
const r = snapDelta(base, delta, s.snap.targets, s.snap.radiusDoc);
|
|
984
|
+
delta = r.delta;
|
|
985
|
+
ctx.snap?.setIndicator(r.hit ? { x: r.hit.x, y: r.hit.y } : null);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
832
988
|
const patch = dragPatch(s, ctx.document, delta, ctx.viewport.state.zoom);
|
|
833
989
|
if (patch)
|
|
834
990
|
ctx.preview(patch);
|
|
835
991
|
return { ...s, delta };
|
|
836
992
|
},
|
|
837
993
|
onPointerUp(_event, ctx, state) {
|
|
994
|
+
ctx.snap?.setIndicator(null);
|
|
838
995
|
const editId = pendingTextEditId;
|
|
839
996
|
pendingTextEditId = null;
|
|
840
997
|
const deselectId = pendingBackgroundDeselectId;
|
|
@@ -868,6 +1025,7 @@ export const createSelectTool = (options = {}) => ({
|
|
|
868
1025
|
onCancel(_state, ctx) {
|
|
869
1026
|
pendingTextEditId = null;
|
|
870
1027
|
pendingBackgroundDeselectId = null;
|
|
1028
|
+
ctx.snap?.setIndicator(null);
|
|
871
1029
|
ctx.preview({ ops: [] });
|
|
872
1030
|
},
|
|
873
1031
|
hitTest(element, p) {
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { DEFAULT_LAYER_ID } from '../../../types/annotation.js';
|
|
2
2
|
import { annotationKindFor, shapePointsFromDrag, } from '../shapeGeometry.js';
|
|
3
|
+
import { collectSnapTargetsFlat, snapPoint } from '../snapGeometry.js';
|
|
4
|
+
const snapStateForPress = (ctx, kind) => kind === 'line' && ctx.snap?.enabled
|
|
5
|
+
? {
|
|
6
|
+
snap: {
|
|
7
|
+
targets: collectSnapTargetsFlat(ctx.document),
|
|
8
|
+
radiusDoc: ctx.snap.radiusPx / ctx.viewport.state.zoom,
|
|
9
|
+
},
|
|
10
|
+
}
|
|
11
|
+
: {};
|
|
12
|
+
const snapWorld = (s, world) => s.snap ? snapPoint(world, s.snap.targets, s.snap.radiusDoc) : null;
|
|
3
13
|
let counter = 0;
|
|
4
14
|
const makeId = (prefix) => `${prefix}-${Date.now().toString(36)}-${(counter++).toString(36)}`;
|
|
5
15
|
const DEFAULT_LABELS = {
|
|
@@ -61,12 +71,14 @@ export const createShapeTool = (options = {}) => {
|
|
|
61
71
|
dash,
|
|
62
72
|
},
|
|
63
73
|
onPointerDown(event, ctx) {
|
|
74
|
+
const snap = snapStateForPress(ctx, kind);
|
|
75
|
+
const startWorld = snapWorld(snap, event.world)?.point ?? event.world;
|
|
64
76
|
return {
|
|
65
77
|
kind: 'shape-drawing',
|
|
66
78
|
shape: buildShapeFromDrag({
|
|
67
79
|
kind,
|
|
68
|
-
a:
|
|
69
|
-
b:
|
|
80
|
+
a: startWorld,
|
|
81
|
+
b: startWorld,
|
|
70
82
|
color,
|
|
71
83
|
width,
|
|
72
84
|
cap,
|
|
@@ -74,22 +86,28 @@ export const createShapeTool = (options = {}) => {
|
|
|
74
86
|
dash,
|
|
75
87
|
layerId: firstLayerId(ctx.document),
|
|
76
88
|
}),
|
|
77
|
-
startWorld
|
|
89
|
+
startWorld,
|
|
78
90
|
startScreen: event.screen,
|
|
79
91
|
moved: false,
|
|
92
|
+
...snap,
|
|
80
93
|
};
|
|
81
94
|
},
|
|
82
95
|
onPointerMove(event, ctx, state) {
|
|
83
96
|
const s = state;
|
|
84
97
|
if (s?.kind !== 'shape-drawing')
|
|
85
98
|
return s;
|
|
99
|
+
const snapped = snapWorld(s, event.world);
|
|
100
|
+
if (s.snap) {
|
|
101
|
+
ctx.snap?.setIndicator(snapped?.hit ? { x: snapped.hit.x, y: snapped.hit.y } : null);
|
|
102
|
+
}
|
|
103
|
+
const end = snapped?.point ?? event.world;
|
|
86
104
|
const next = {
|
|
87
105
|
...s,
|
|
88
106
|
moved: true,
|
|
89
107
|
shape: {
|
|
90
108
|
...s.shape,
|
|
91
109
|
geometry: {
|
|
92
|
-
points: shapePointsFromDrag(kind, s.startWorld,
|
|
110
|
+
points: shapePointsFromDrag(kind, s.startWorld, end),
|
|
93
111
|
},
|
|
94
112
|
},
|
|
95
113
|
};
|
|
@@ -100,6 +118,7 @@ export const createShapeTool = (options = {}) => {
|
|
|
100
118
|
const s = state;
|
|
101
119
|
if (s?.kind !== 'shape-drawing')
|
|
102
120
|
return;
|
|
121
|
+
ctx.snap?.setIndicator(null);
|
|
103
122
|
const dx = event.screen.x - s.startScreen.x;
|
|
104
123
|
const dy = event.screen.y - s.startScreen.y;
|
|
105
124
|
if (!s.moved || dx * dx + dy * dy < minDragPx * minDragPx) {
|
|
@@ -107,15 +126,18 @@ export const createShapeTool = (options = {}) => {
|
|
|
107
126
|
ctx.preview({ ops: [] });
|
|
108
127
|
return;
|
|
109
128
|
}
|
|
129
|
+
// Same snap as the last move, so the commit equals the preview.
|
|
130
|
+
const end = snapWorld(s, event.world)?.point ?? event.world;
|
|
110
131
|
const shape = {
|
|
111
132
|
...s.shape,
|
|
112
133
|
geometry: {
|
|
113
|
-
points: shapePointsFromDrag(kind, s.startWorld,
|
|
134
|
+
points: shapePointsFromDrag(kind, s.startWorld, end),
|
|
114
135
|
},
|
|
115
136
|
};
|
|
116
137
|
ctx.commit({ ops: [{ op: 'addShape', shape }] });
|
|
117
138
|
},
|
|
118
139
|
onCancel(_state, ctx) {
|
|
140
|
+
ctx.snap?.setIndicator(null);
|
|
119
141
|
ctx.preview({ ops: [] });
|
|
120
142
|
},
|
|
121
143
|
};
|
|
@@ -64,6 +64,7 @@ export interface UseAnnotationCanvasStateProps {
|
|
|
64
64
|
measurements?: Measurement[];
|
|
65
65
|
pickMeasurement?: () => Promise<MeasurementRef | null>;
|
|
66
66
|
requestTextInput?: RequestTextInput;
|
|
67
|
+
snapToGeometry?: boolean;
|
|
67
68
|
width: number;
|
|
68
69
|
height: number;
|
|
69
70
|
initialViewport?: ViewportState;
|
|
@@ -94,6 +95,7 @@ export interface AnnotationCanvasStateApi {
|
|
|
94
95
|
fileTileScaleFactor: number;
|
|
95
96
|
penDrawingStroke: AnnotationStroke | null;
|
|
96
97
|
customPreviewState: ToolState;
|
|
98
|
+
snapIndicator: Vec2 | null;
|
|
97
99
|
dispatchPointerDown(event: CanvasPointerEvent): void;
|
|
98
100
|
dispatchPointerMove(event: CanvasPointerEvent): void;
|
|
99
101
|
dispatchPointerUp(event: CanvasPointerEvent): void;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { applyPatch, invertPatch, DEFAULT_LAYER_ID, } from '../../types/annotation.js';
|
|
3
3
|
import { resolveHeaderTileScaleFactor, resolveFileTileScaleFactor, resolveTileScaleFactor, stampTileDims, STAMP_TILE_SIZE, tileDocSizeForFactor, tileScaleFromDocSize, } from './stampLayout.js';
|
|
4
|
+
import { SNAP_RADIUS_PX } from './snapGeometry.js';
|
|
4
5
|
import { createViewportApi, fitRectToScreen, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, } from './viewport.js';
|
|
5
6
|
import { computeZoomFloor, pdfFirstPageDocRect } from './pdfDocument.js';
|
|
6
7
|
import { backgroundLayerDocRect, backgroundLayersOf, backgroundLayersUnionRect, } from './backgroundLayers.js';
|
|
@@ -113,7 +114,7 @@ const defaultLeaderLenDoc = (docWidth) => Math.min(800, Math.max(240, docWidth *
|
|
|
113
114
|
// inners share this hook; each wraps it with platform-specific event
|
|
114
115
|
// capture and JSX (div + DOM events vs. GestureDetector + RN Views).
|
|
115
116
|
export const useAnnotationCanvasState = (props) => {
|
|
116
|
-
const { canvas, onCommit, tools, activeToolId, selection, onSelectionChange, measurements, pickMeasurement, requestTextInput, width, height, initialViewport, tileScalePlatform, animateViewport, imperativeRef, } = props;
|
|
117
|
+
const { canvas, onCommit, tools, activeToolId, selection, onSelectionChange, measurements, pickMeasurement, requestTextInput, snapToGeometry, width, height, initialViewport, tileScalePlatform, animateViewport, imperativeRef, } = props;
|
|
117
118
|
const [viewport, setViewport] = useState(initialViewport ?? DEFAULT_VIEWPORT);
|
|
118
119
|
const [toolState, setToolState] = useState(undefined);
|
|
119
120
|
// Synchronous mirror for the DISPATCHERS. The native tap gesture
|
|
@@ -128,6 +129,8 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
128
129
|
toolStateRef.current = next;
|
|
129
130
|
setToolState(next);
|
|
130
131
|
};
|
|
132
|
+
// Snap ring position for JS-path drags (see AnnotationCanvasStateApi).
|
|
133
|
+
const [snapIndicator, setSnapIndicator] = useState(null);
|
|
131
134
|
const [previewPatch, setPreviewPatch] = useState(null);
|
|
132
135
|
const undoStackRef = useRef([]);
|
|
133
136
|
const redoStackRef = useRef([]);
|
|
@@ -194,7 +197,9 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
194
197
|
// equals the measurement factor (resolveHeaderTileScaleFactor's fallback) —
|
|
195
198
|
// the pre-split one-knob behavior.
|
|
196
199
|
let headerTileScaleFactor = resolveHeaderTileScaleFactor(effectiveCanvas, tileScalePlatformResolved);
|
|
197
|
-
// Object tiles
|
|
200
|
+
// Object tiles share the measurement factor — their own knob was retired
|
|
201
|
+
// (see resolveFileTileScaleFactor), so this always equals tileScaleFactor
|
|
202
|
+
// and rides the same calibration below.
|
|
198
203
|
let fileTileScaleFactor = resolveFileTileScaleFactor(effectiveCanvas, tileScalePlatformResolved);
|
|
199
204
|
if (tileScalePlatformResolved === 'mobile' &&
|
|
200
205
|
effectiveCanvas.tileScaleFactorMobile == null &&
|
|
@@ -226,6 +231,11 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
226
231
|
tileScaleFactor,
|
|
227
232
|
headerTileScaleFactor,
|
|
228
233
|
fileTileScaleFactor,
|
|
234
|
+
snap: {
|
|
235
|
+
enabled: snapToGeometry === true,
|
|
236
|
+
radiusPx: SNAP_RADIUS_PX,
|
|
237
|
+
setIndicator: setSnapIndicator,
|
|
238
|
+
},
|
|
229
239
|
preview(patch) {
|
|
230
240
|
setPreviewPatch(patch);
|
|
231
241
|
},
|
|
@@ -326,6 +336,7 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
326
336
|
onSelectionChange,
|
|
327
337
|
pickMeasurement,
|
|
328
338
|
requestTextInput,
|
|
339
|
+
snapToGeometry,
|
|
329
340
|
]);
|
|
330
341
|
// Live ctx for imperative handle methods (which are created in an effect and
|
|
331
342
|
// would otherwise capture a stale ctx/viewport).
|
|
@@ -351,6 +362,7 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
351
362
|
activePointerIdRef.current = null;
|
|
352
363
|
updateToolState(undefined);
|
|
353
364
|
setPreviewPatch(null);
|
|
365
|
+
setSnapIndicator(null);
|
|
354
366
|
// updateToolState is a stable-in-effect helper (ref + setState); the
|
|
355
367
|
// effect only re-runs on a tool identity change.
|
|
356
368
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -388,6 +400,9 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
388
400
|
activeTool.onPointerUp?.(event, ctx, toolStateRef.current);
|
|
389
401
|
activePointerIdRef.current = null;
|
|
390
402
|
updateToolState(undefined);
|
|
403
|
+
// Belt-and-braces: tools clear their own ring on release, but a tool
|
|
404
|
+
// that forgets must not leave a stale ring on the canvas.
|
|
405
|
+
setSnapIndicator(null);
|
|
391
406
|
},
|
|
392
407
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
393
408
|
[activeTool, ctx]);
|
|
@@ -408,6 +423,7 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
408
423
|
const prevToolState = toolStateRef.current;
|
|
409
424
|
updateToolState(undefined);
|
|
410
425
|
setPreviewPatch(null);
|
|
426
|
+
setSnapIndicator(null);
|
|
411
427
|
if (activeTool)
|
|
412
428
|
activeTool.onCancel?.(prevToolState, ctx);
|
|
413
429
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -901,6 +917,7 @@ export const useAnnotationCanvasState = (props) => {
|
|
|
901
917
|
fileTileScaleFactor,
|
|
902
918
|
penDrawingStroke,
|
|
903
919
|
customPreviewState: toolState,
|
|
920
|
+
snapIndicator,
|
|
904
921
|
dispatchPointerDown,
|
|
905
922
|
dispatchPointerMove,
|
|
906
923
|
dispatchPointerUp,
|
|
@@ -429,7 +429,11 @@ export const useAnnotationCanvasDoc = (options) => {
|
|
|
429
429
|
? backgroundLayersOf(viewport).find((l) => l.id === id)
|
|
430
430
|
: undefined;
|
|
431
431
|
const fileId = fileIdRef.current ?? createdIdRef.current;
|
|
432
|
-
|
|
432
|
+
// A LINKED layer (an expanded file tile — see fileTileSwap.ts) does not
|
|
433
|
+
// own its Storage object: its storagePath points into the SOURCE file's
|
|
434
|
+
// folder, and deleting it would destroy that file's photo. Remove the
|
|
435
|
+
// layer only.
|
|
436
|
+
if (layer && fileId && !layer.fileId) {
|
|
433
437
|
try {
|
|
434
438
|
await deleteImage(fileId, layer.storagePath);
|
|
435
439
|
}
|
package/dist/exports.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export { hydrateCanvasState } from './annotation/data/canvasPersistence.js';
|
|
|
28
28
|
export { InMemoryAnnotationProvider } from './annotation/data/InMemoryAnnotationProvider.js';
|
|
29
29
|
export type { AnnotationCanvasHandle } from './annotation/canvas/useAnnotationCanvasState.js';
|
|
30
30
|
export type { GestureConfig, PanTrigger, AnnotationCanvasInnerProps, } from './annotation/canvas/AnnotationCanvasInner.js';
|
|
31
|
-
export type { CanvasPointerEvent, RequestTextInput, ShapeDrawConfig, Tool, ToolContext, ToolState, } from './annotation/canvas/Tool.js';
|
|
31
|
+
export type { CanvasPointerEvent, RequestTextInput, ShapeDrawConfig, Tool, ToolContext, SnapContext, ToolState, } from './annotation/canvas/Tool.js';
|
|
32
32
|
export type { FileRef, MeasurementRef, PickMeasurement, } from './annotation/canvas/measurementPicker.js';
|
|
33
33
|
export type { MeasurementStampRenderArgs, RenderMeasurementStamp, } from './annotation/canvas/measurementStampOverlay.js';
|
|
34
34
|
export type { CanvasBackgroundSlotArgs, RenderCanvasBackground, } from './annotation/canvas/backgroundSlot.js';
|
|
@@ -37,6 +37,7 @@ export { GROUP_HEADER_STYLE, describeGroupHeader, groupHeaderSignature, type Gro
|
|
|
37
37
|
export { createViewportApi, fitToScreen, fitRectToScreen, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, type ViewportApi, type ViewportState, type ZoomLimits, } from './annotation/canvas/viewport.js';
|
|
38
38
|
export { DEFAULT_PDF_PAGE_GAP, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, makePdfDocumentId, createPdfDocument, pdfPageDocRects, pdfDocumentExtent, pdfFirstPageDocRect, computeZoomFloor, type PdfDocRect, } from './annotation/canvas/pdfDocument.js';
|
|
39
39
|
export { LEGACY_BACKGROUND_LAYER_ID, BACKGROUND_PLACEMENT_GAP_RATIO, backgroundLayersOf, backgroundLayerIsLocked, isBackgroundLayerLocked, defaultBackgroundLayerLocked, backgroundLayerDocRect, backgroundLayersUnionRect, placeNewBackgroundLayer, } from './annotation/canvas/backgroundLayers.js';
|
|
40
|
+
export { fileTileImageSource, expandFileTileToBackground, collapseBackgroundToFileTile, type FileTileImageSource, } from './annotation/canvas/fileTileSwap.js';
|
|
40
41
|
export { createPenTool, type PenToolOptions, } from './annotation/canvas/tools/penTool.js';
|
|
41
42
|
export { createSelectTool, type SelectToolOptions, } from './annotation/canvas/tools/selectTool.js';
|
|
42
43
|
export { createMeasurementStampTool, type MeasurementStampToolOptions, } from './annotation/canvas/tools/measurementStampTool.js';
|
|
@@ -51,3 +52,4 @@ export { annotationKindFor, hitShapeOutline, shapePointsFromDrag, type ShapeTool
|
|
|
51
52
|
export { DEFAULT_TEXT_FONT_SIZE, MIN_TEXT_FONT_SIZE, MAX_TEXT_FONT_SIZE, textShapeBounds, textResizeGeometry, type ResizeGeometry, type TextBounds, } from './annotation/canvas/textGeometry.js';
|
|
52
53
|
export { recomputeAnchor, projectToLinePos, snapLinePos, lerp, lineLength, placementOf, linePosOf, normalizeRect, rectCenter, rectCornerPoint, oppositeRectCorner, hitPlacedMeasurement, DEFAULT_LINE_POS, type NormalizedRect, type RectCorner, } from './annotation/canvas/measurementGeometry.js';
|
|
53
54
|
export { toSkiaStrokeCap, arrowheadTriangle, arrowheadLength, } from './annotation/canvas/strokeGeometry.js';
|
|
55
|
+
export { SNAP_RADIUS_PX, SNAP_RING_PX, SNAP_RING_WIDTH_PX, SNAP_KIND_POINT, SNAP_KIND_MIDPOINT, SNAP_STRIDE, collectSnapTargets, collectSnapTargetsFlat, encodeSnapTargets, snapNearest, snapDelta, snapPoint, type SnapHit, type SnapKind, type SnapTarget, } from './annotation/canvas/snapGeometry.js';
|
package/dist/exports.js
CHANGED
|
@@ -41,6 +41,7 @@ export { GROUP_HEADER_STYLE, describeGroupHeader, groupHeaderSignature, } from '
|
|
|
41
41
|
export { createViewportApi, fitToScreen, fitRectToScreen, imageDocRect, panBy, zoomAt, DEFAULT_VIEWPORT, DEFAULT_ZOOM_LIMITS, } from './annotation/canvas/viewport.js';
|
|
42
42
|
export { DEFAULT_PDF_PAGE_GAP, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, makePdfDocumentId, createPdfDocument, pdfPageDocRects, pdfDocumentExtent, pdfFirstPageDocRect, computeZoomFloor, } from './annotation/canvas/pdfDocument.js';
|
|
43
43
|
export { LEGACY_BACKGROUND_LAYER_ID, BACKGROUND_PLACEMENT_GAP_RATIO, backgroundLayersOf, backgroundLayerIsLocked, isBackgroundLayerLocked, defaultBackgroundLayerLocked, backgroundLayerDocRect, backgroundLayersUnionRect, placeNewBackgroundLayer, } from './annotation/canvas/backgroundLayers.js';
|
|
44
|
+
export { fileTileImageSource, expandFileTileToBackground, collapseBackgroundToFileTile, } from './annotation/canvas/fileTileSwap.js';
|
|
44
45
|
export { createPenTool, } from './annotation/canvas/tools/penTool.js';
|
|
45
46
|
export { createSelectTool, } from './annotation/canvas/tools/selectTool.js';
|
|
46
47
|
export { createMeasurementStampTool, } from './annotation/canvas/tools/measurementStampTool.js';
|
|
@@ -55,3 +56,4 @@ export { annotationKindFor, hitShapeOutline, shapePointsFromDrag, } from './anno
|
|
|
55
56
|
export { DEFAULT_TEXT_FONT_SIZE, MIN_TEXT_FONT_SIZE, MAX_TEXT_FONT_SIZE, textShapeBounds, textResizeGeometry, } from './annotation/canvas/textGeometry.js';
|
|
56
57
|
export { recomputeAnchor, projectToLinePos, snapLinePos, lerp, lineLength, placementOf, linePosOf, normalizeRect, rectCenter, rectCornerPoint, oppositeRectCorner, hitPlacedMeasurement, DEFAULT_LINE_POS, } from './annotation/canvas/measurementGeometry.js';
|
|
57
58
|
export { toSkiaStrokeCap, arrowheadTriangle, arrowheadLength, } from './annotation/canvas/strokeGeometry.js';
|
|
59
|
+
export { SNAP_RADIUS_PX, SNAP_RING_PX, SNAP_RING_WIDTH_PX, SNAP_KIND_POINT, SNAP_KIND_MIDPOINT, SNAP_STRIDE, collectSnapTargets, collectSnapTargetsFlat, encodeSnapTargets, snapNearest, snapDelta, snapPoint, } from './annotation/canvas/snapGeometry.js';
|
|
@@ -135,6 +135,12 @@ export interface BackgroundLayerTransform {
|
|
|
135
135
|
scaleX: number;
|
|
136
136
|
scaleY: number;
|
|
137
137
|
}
|
|
138
|
+
export interface BackgroundLayerCrop {
|
|
139
|
+
x: number;
|
|
140
|
+
y: number;
|
|
141
|
+
width: number;
|
|
142
|
+
height: number;
|
|
143
|
+
}
|
|
138
144
|
export interface AnnotationBackgroundLayer {
|
|
139
145
|
id: AnnotationElementId;
|
|
140
146
|
storagePath: string;
|
|
@@ -143,6 +149,10 @@ export interface AnnotationBackgroundLayer {
|
|
|
143
149
|
heightPx: number;
|
|
144
150
|
format?: 'raster' | 'svg';
|
|
145
151
|
transform: BackgroundLayerTransform;
|
|
152
|
+
crop?: BackgroundLayerCrop;
|
|
153
|
+
fileId?: string;
|
|
154
|
+
filePath?: string;
|
|
155
|
+
fileGroupId?: string;
|
|
146
156
|
locked?: boolean;
|
|
147
157
|
}
|
|
148
158
|
export interface AnnotationPdfPageSize {
|