@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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Magnetic snapping for line endpoints (Asana 1217966180270874). While an
|
|
2
|
+
// endpoint of a measurement line (`placement: 'line'`) or a line/arrow shape is
|
|
3
|
+
// being placed or dragged, it snaps to nearby geometry: other lines' endpoints
|
|
4
|
+
// and midpoints, polygon/rect vertices, and measurement-rectangle corners.
|
|
5
|
+
// Skia-free, safe in tools, hit-tests and unit tests.
|
|
6
|
+
//
|
|
7
|
+
// `snapNearest`/`snapDelta` carry 'worklet' directives: the native Inner calls
|
|
8
|
+
// them per frame on the UI thread (the live endpoint derived values and the
|
|
9
|
+
// shape rubber-band gesture) — the same mechanism planeGeometry's grid
|
|
10
|
+
// generators rely on. Keep them free of closures over non-worklet module
|
|
11
|
+
// functions (numeric constants are fine) so the workletizer can lift them.
|
|
12
|
+
//
|
|
13
|
+
// Targets travel to the UI thread as a FLAT number array (`[x, y, kind, …]`,
|
|
14
|
+
// stride SNAP_STRIDE) written once per drag — see `encodeSnapTargets`.
|
|
15
|
+
import { placementOf, normalizeRect } from './measurementGeometry.js';
|
|
16
|
+
// Screen-px capture radius: the grabbed endpoint jumps to a target once it is
|
|
17
|
+
// within this many pixels of it. Converted to doc space via `/ zoom` at drag
|
|
18
|
+
// begin (frozen for the drag, like the slide detent's SLIDE_SNAP_PX).
|
|
19
|
+
export const SNAP_RADIUS_PX = 16;
|
|
20
|
+
// Screen-px radius / ring width of the snap indicator drawn around the target
|
|
21
|
+
// while a snap is active. Larger than the endpoint handle (HANDLE_RADIUS_PX 7)
|
|
22
|
+
// so the ring frames the handle instead of hiding under it.
|
|
23
|
+
export const SNAP_RING_PX = 11;
|
|
24
|
+
export const SNAP_RING_WIDTH_PX = 2;
|
|
25
|
+
// Target kinds, ordered by priority: on an exact distance tie the LOWER kind
|
|
26
|
+
// wins, so a true endpoint/vertex beats a midpoint sitting on the same pixel.
|
|
27
|
+
export const SNAP_KIND_POINT = 0;
|
|
28
|
+
export const SNAP_KIND_MIDPOINT = 1;
|
|
29
|
+
// Flat encoding stride: [x, y, kind].
|
|
30
|
+
export const SNAP_STRIDE = 3;
|
|
31
|
+
// Every snap target in the document, in doc space. `excludeId` drops the
|
|
32
|
+
// element being edited ENTIRELY (its endpoints and midpoint) — snapping a
|
|
33
|
+
// line's endpoint onto its own other end would collapse it, and onto its own
|
|
34
|
+
// midpoint would halve it. Targets:
|
|
35
|
+
// - measurement `placement:'line'`: a, b (point) + midpoint. Plane-edge
|
|
36
|
+
// tiles included — their endpoints are real geometry even though the
|
|
37
|
+
// select tool refuses to drag them.
|
|
38
|
+
// - measurement `placement:'rectangle'`: the four normalized corners.
|
|
39
|
+
// - shape line/arrow: both endpoints + midpoint.
|
|
40
|
+
// - shape rect: the four normalized corners (stored as two opposite corners).
|
|
41
|
+
// - shape polygon (triangles included): every vertex.
|
|
42
|
+
// - ellipse, text, strokes, backgrounds: nothing.
|
|
43
|
+
// Degenerate (zero-length) lines contribute their coincident endpoints but no
|
|
44
|
+
// midpoint.
|
|
45
|
+
export const collectSnapTargets = (doc, excludeId) => {
|
|
46
|
+
const out = [];
|
|
47
|
+
const point = (p, sourceId) => out.push({ x: p.x, y: p.y, kind: SNAP_KIND_POINT, sourceId });
|
|
48
|
+
const segment = (a, b, sourceId) => {
|
|
49
|
+
point(a, sourceId);
|
|
50
|
+
point(b, sourceId);
|
|
51
|
+
if (a.x !== b.x || a.y !== b.y) {
|
|
52
|
+
out.push({
|
|
53
|
+
x: (a.x + b.x) / 2,
|
|
54
|
+
y: (a.y + b.y) / 2,
|
|
55
|
+
kind: SNAP_KIND_MIDPOINT,
|
|
56
|
+
sourceId,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const corners = (r, sourceId) => {
|
|
61
|
+
const n = normalizeRect(r);
|
|
62
|
+
point({ x: n.minX, y: n.minY }, sourceId);
|
|
63
|
+
point({ x: n.maxX, y: n.minY }, sourceId);
|
|
64
|
+
point({ x: n.maxX, y: n.maxY }, sourceId);
|
|
65
|
+
point({ x: n.minX, y: n.maxY }, sourceId);
|
|
66
|
+
};
|
|
67
|
+
for (const m of doc.placedMeasurements) {
|
|
68
|
+
if (excludeId != null && m.id === excludeId)
|
|
69
|
+
continue;
|
|
70
|
+
const placement = placementOf(m);
|
|
71
|
+
if (placement === 'line' && m.line)
|
|
72
|
+
segment(m.line.a, m.line.b, m.id);
|
|
73
|
+
else if (placement === 'rectangle' && m.rect)
|
|
74
|
+
corners(m.rect, m.id);
|
|
75
|
+
}
|
|
76
|
+
for (const s of doc.shapes) {
|
|
77
|
+
if (excludeId != null && s.id === excludeId)
|
|
78
|
+
continue;
|
|
79
|
+
const pts = s.geometry.points;
|
|
80
|
+
switch (s.kind) {
|
|
81
|
+
case 'line':
|
|
82
|
+
case 'arrow': {
|
|
83
|
+
const [a, b] = pts;
|
|
84
|
+
if (a && b)
|
|
85
|
+
segment(a, b, s.id);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case 'rect': {
|
|
89
|
+
const [a, b] = pts;
|
|
90
|
+
if (a && b)
|
|
91
|
+
corners({ a, b }, s.id);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'polygon':
|
|
95
|
+
for (const p of pts)
|
|
96
|
+
point(p, s.id);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
};
|
|
104
|
+
// Flat `[x, y, kind, …]` buffer for UI-thread transfer (a plain number[],
|
|
105
|
+
// like the freehand `livePoints` — TypedArrays aren't proven shareable here).
|
|
106
|
+
export const encodeSnapTargets = (targets) => {
|
|
107
|
+
const flat = new Array(targets.length * SNAP_STRIDE);
|
|
108
|
+
for (let i = 0; i < targets.length; i++) {
|
|
109
|
+
const t = targets[i];
|
|
110
|
+
flat[i * SNAP_STRIDE] = t.x;
|
|
111
|
+
flat[i * SNAP_STRIDE + 1] = t.y;
|
|
112
|
+
flat[i * SNAP_STRIDE + 2] = t.kind;
|
|
113
|
+
}
|
|
114
|
+
return flat;
|
|
115
|
+
};
|
|
116
|
+
export const collectSnapTargetsFlat = (doc, excludeId) => encodeSnapTargets(collectSnapTargets(doc, excludeId));
|
|
117
|
+
// Nearest target to (x, y) within `radiusDoc` (inclusive), or null. Ties on
|
|
118
|
+
// distance go to the lower kind (point beats midpoint); remaining ties to the
|
|
119
|
+
// earlier target. `radiusDoc <= 0` disables snapping. Worklet-callable.
|
|
120
|
+
export const snapNearest = (x, y, flat, radiusDoc) => {
|
|
121
|
+
'worklet';
|
|
122
|
+
if (radiusDoc <= 0)
|
|
123
|
+
return null;
|
|
124
|
+
const r2 = radiusDoc * radiusDoc;
|
|
125
|
+
let bestD2 = Infinity;
|
|
126
|
+
let bestKind = Infinity;
|
|
127
|
+
let bestIdx = -1;
|
|
128
|
+
for (let i = 0; i + 2 < flat.length; i += 3) {
|
|
129
|
+
const dx = flat[i] - x;
|
|
130
|
+
const dy = flat[i + 1] - y;
|
|
131
|
+
const d2 = dx * dx + dy * dy;
|
|
132
|
+
if (d2 > r2)
|
|
133
|
+
continue;
|
|
134
|
+
const kind = flat[i + 2];
|
|
135
|
+
if (d2 < bestD2 || (d2 === bestD2 && kind < bestKind)) {
|
|
136
|
+
bestD2 = d2;
|
|
137
|
+
bestKind = kind;
|
|
138
|
+
bestIdx = i;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (bestIdx < 0)
|
|
142
|
+
return null;
|
|
143
|
+
return {
|
|
144
|
+
x: flat[bestIdx],
|
|
145
|
+
y: flat[bestIdx + 1],
|
|
146
|
+
kind: flat[bestIdx + 2] === 1 ? 1 : 0,
|
|
147
|
+
index: bestIdx / 3,
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
// Snap a DRAG: `base` is the grabbed endpoint's committed position and `delta`
|
|
151
|
+
// the raw world-space drag offset. Returns the effective delta that lands the
|
|
152
|
+
// endpoint exactly on the nearest target (or the raw delta when nothing is in
|
|
153
|
+
// range), so the existing delta-based patch builders stay untouched. Worklet-
|
|
154
|
+
// callable — the native select gesture runs it in onEnd so the commit matches
|
|
155
|
+
// the live preview bit-for-bit.
|
|
156
|
+
export const snapDelta = (base, delta, flat, radiusDoc) => {
|
|
157
|
+
'worklet';
|
|
158
|
+
const hit = snapNearest(base.x + delta.x, base.y + delta.y, flat, radiusDoc);
|
|
159
|
+
if (!hit)
|
|
160
|
+
return { delta, hit: null };
|
|
161
|
+
return { delta: { x: hit.x - base.x, y: hit.y - base.y }, hit };
|
|
162
|
+
};
|
|
163
|
+
// Snap an ABSOLUTE point (drag-to-create tools place raw world points).
|
|
164
|
+
export const snapPoint = (p, flat, radiusDoc) => {
|
|
165
|
+
'worklet';
|
|
166
|
+
const hit = snapNearest(p.x, p.y, flat, radiusDoc);
|
|
167
|
+
return hit ? { point: { x: hit.x, y: hit.y }, hit } : { point: p, hit: null };
|
|
168
|
+
};
|
|
@@ -20,7 +20,7 @@ export declare const TILE_SCALE_MIN = 0.05;
|
|
|
20
20
|
export declare const TILE_SCALE_MAX = 2;
|
|
21
21
|
export declare const resolveTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile">, platform: TileScalePlatform) => number;
|
|
22
22
|
export declare const resolveHeaderTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile" | "headerTileScaleFactor" | "headerTileScaleFactorMobile">, platform: TileScalePlatform) => number;
|
|
23
|
-
export declare const resolveFileTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile"
|
|
23
|
+
export declare const resolveFileTileScaleFactor: (canvas: Pick<AnnotationCanvasState, "tileScaleFactor" | "tileScaleFactorMobile">, platform: TileScalePlatform) => number;
|
|
24
24
|
export declare const clampTileScale: (v: number) => number;
|
|
25
25
|
export declare const tileDocSizeForFactor: (factor: number, zoom: number) => number;
|
|
26
26
|
export declare const tileScaleFromDocSize: (docSize: number, zoom: number) => number;
|
|
@@ -97,14 +97,16 @@ export const resolveTileScaleFactor = (canvas, platform) => (platform === 'mobil
|
|
|
97
97
|
export const resolveHeaderTileScaleFactor = (canvas, platform) => (platform === 'mobile'
|
|
98
98
|
? (canvas.headerTileScaleFactorMobile ?? canvas.headerTileScaleFactor)
|
|
99
99
|
: canvas.headerTileScaleFactor) ?? resolveTileScaleFactor(canvas, platform);
|
|
100
|
-
// Resolve the
|
|
101
|
-
// knob (Asana 1217570500895545),
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
// Resolve the OBJECT-tile scale for a platform: the MEASUREMENT factor.
|
|
101
|
+
// Object tiles briefly had their own third knob (Asana 1217570500895545), but
|
|
102
|
+
// it was retired — an object thumbnail must size consistently with the
|
|
103
|
+
// measurement tiles beside it, so its slider now drives (and follows) the one
|
|
104
|
+
// shared factor. The `fileTileScaleFactor(Mobile)` fields it wrote persist on
|
|
105
|
+
// old documents and old undo payloads still write them; they are deliberately
|
|
106
|
+
// IGNORED here — resolving them would let a stale divergent value shadow the
|
|
107
|
+
// shared knob forever. Kept as a named resolver (not inlined at call sites)
|
|
108
|
+
// so the read path stays greppable and the retirement is documented once.
|
|
109
|
+
export const resolveFileTileScaleFactor = (canvas, platform) => resolveTileScaleFactor(canvas, platform);
|
|
108
110
|
// Clamp a tile-scale-factor candidate to the supported range. The single guard
|
|
109
111
|
// for the value before it lands in the document (slider input, restored docs).
|
|
110
112
|
export const clampTileScale = (v) => v < TILE_SCALE_MIN ? TILE_SCALE_MIN : v > TILE_SCALE_MAX ? TILE_SCALE_MAX : v;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_LAYER_ID } from '../../../types/annotation.js';
|
|
2
2
|
import { DEFAULT_LINE_POS, recomputeAnchor, rectCenter, } from '../measurementGeometry.js';
|
|
3
|
+
import { collectSnapTargetsFlat, snapPoint } from '../snapGeometry.js';
|
|
3
4
|
// The `line*` style fields for a placed measurement, built from the tool's
|
|
4
5
|
// style options. Undefined fields are dropped (never emitted) so the render
|
|
5
6
|
// defaults apply and Firestore never sees an `undefined` value.
|
|
@@ -22,6 +23,25 @@ const lineStyleFields = (placement, style) => {
|
|
|
22
23
|
}
|
|
23
24
|
return out;
|
|
24
25
|
};
|
|
26
|
+
// Snap state for a fresh line press. The new annotation isn't in the doc yet,
|
|
27
|
+
// so nothing needs excluding. Empty when snapping is off.
|
|
28
|
+
const snapStateForPress = (ctx, placement) => placement === 'line' && ctx.snap?.enabled
|
|
29
|
+
? {
|
|
30
|
+
snap: {
|
|
31
|
+
targets: collectSnapTargetsFlat(ctx.document),
|
|
32
|
+
radiusDoc: ctx.snap.radiusPx / ctx.viewport.state.zoom,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
: {};
|
|
36
|
+
// The world point a drag end should use: snapped when a target is in range.
|
|
37
|
+
// Reports the target (or null) to the indicator.
|
|
38
|
+
const snapEnd = (s, ctx, world) => {
|
|
39
|
+
if (!s.snap)
|
|
40
|
+
return world;
|
|
41
|
+
const r = snapPoint(world, s.snap.targets, s.snap.radiusDoc);
|
|
42
|
+
ctx.snap?.setIndicator(r.hit ? { x: r.hit.x, y: r.hit.y } : null);
|
|
43
|
+
return r.point;
|
|
44
|
+
};
|
|
25
45
|
let counter = 0;
|
|
26
46
|
const makeId = () => `annotation-${Date.now().toString(36)}-${(counter++).toString(36)}`;
|
|
27
47
|
const firstLayerId = (doc) => doc.layers[0]?.id ?? DEFAULT_LAYER_ID;
|
|
@@ -130,13 +150,21 @@ export const createMeasurementTool = (options = {}) => {
|
|
|
130
150
|
id: options.id ?? defaultId,
|
|
131
151
|
label: options.label ?? defaultLabel,
|
|
132
152
|
cursor: 'crosshair',
|
|
133
|
-
onPointerDown(event) {
|
|
153
|
+
onPointerDown(event, ctx) {
|
|
154
|
+
const snap = snapStateForPress(ctx, placement);
|
|
155
|
+
// The START of a new line snaps too (a leader that begins exactly on a
|
|
156
|
+
// corner is the point of the feature); the ring flashes there until the
|
|
157
|
+
// first move re-evaluates it against the moving end.
|
|
158
|
+
const startWorld = snap.snap
|
|
159
|
+
? snapPoint(event.world, snap.snap.targets, snap.snap.radiusDoc).point
|
|
160
|
+
: event.world;
|
|
134
161
|
return {
|
|
135
162
|
kind: 'measurement-drawing',
|
|
136
163
|
id: makeId(),
|
|
137
|
-
startWorld
|
|
164
|
+
startWorld,
|
|
138
165
|
startScreen: event.screen,
|
|
139
166
|
moved: false,
|
|
167
|
+
...snap,
|
|
140
168
|
};
|
|
141
169
|
},
|
|
142
170
|
onPointerMove(event, ctx, state) {
|
|
@@ -149,7 +177,7 @@ export const createMeasurementTool = (options = {}) => {
|
|
|
149
177
|
placement,
|
|
150
178
|
linePos,
|
|
151
179
|
a: s.startWorld,
|
|
152
|
-
b: event.world,
|
|
180
|
+
b: snapEnd(s, ctx, event.world),
|
|
153
181
|
style,
|
|
154
182
|
valueSource: valueSourceFor(ctx),
|
|
155
183
|
});
|
|
@@ -160,6 +188,7 @@ export const createMeasurementTool = (options = {}) => {
|
|
|
160
188
|
const s = state;
|
|
161
189
|
if (s?.kind !== 'measurement-drawing')
|
|
162
190
|
return;
|
|
191
|
+
ctx.snap?.setIndicator(null);
|
|
163
192
|
const dx = event.screen.x - s.startScreen.x;
|
|
164
193
|
const dy = event.screen.y - s.startScreen.y;
|
|
165
194
|
if (!s.moved || dx * dx + dy * dy < minDragPx * minDragPx) {
|
|
@@ -167,18 +196,23 @@ export const createMeasurementTool = (options = {}) => {
|
|
|
167
196
|
ctx.preview({ ops: [] });
|
|
168
197
|
return;
|
|
169
198
|
}
|
|
199
|
+
// Same snap as the last move, so the commit equals the preview.
|
|
200
|
+
const b = s.snap
|
|
201
|
+
? snapPoint(event.world, s.snap.targets, s.snap.radiusDoc).point
|
|
202
|
+
: event.world;
|
|
170
203
|
place(ctx, buildMeasurement({
|
|
171
204
|
id: s.id,
|
|
172
205
|
layerId: firstLayerId(ctx.document),
|
|
173
206
|
placement,
|
|
174
207
|
linePos,
|
|
175
208
|
a: s.startWorld,
|
|
176
|
-
b
|
|
209
|
+
b,
|
|
177
210
|
style,
|
|
178
211
|
valueSource: valueSourceFor(ctx),
|
|
179
212
|
}));
|
|
180
213
|
},
|
|
181
214
|
onCancel(_state, ctx) {
|
|
215
|
+
ctx.snap?.setIndicator(null);
|
|
182
216
|
ctx.preview({ ops: [] });
|
|
183
217
|
},
|
|
184
218
|
};
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
+
import type { Vec2 } from '../../../types/annotation.js';
|
|
2
|
+
import { type BackgroundCropEdge } from '../backgroundLayers.js';
|
|
1
3
|
import type { Tool } from '../Tool.js';
|
|
2
4
|
export interface SelectToolOptions {
|
|
3
5
|
panOnEmptyDrag?: boolean;
|
|
4
6
|
}
|
|
7
|
+
export declare const cropDragVisibleRect: (start: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}, full: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}, edge: BackgroundCropEdge, delta: Vec2) => {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
5
23
|
export declare const createSelectTool: (options?: SelectToolOptions) => Tool;
|