@reekon-tools/boldr-utils 1.15.0 → 1.15.2
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.js +5 -2
- package/dist/annotation/canvas/AnnotationCanvasInner.native.js +10 -3
- package/dist/annotation/canvas/AnnotationCanvasSkia.d.ts +2 -1
- package/dist/annotation/canvas/AnnotationCanvasSkia.js +17 -3
- package/dist/annotation/canvas/Tool.d.ts +1 -0
- package/dist/annotation/canvas/elements/PlaneElement.d.ts +18 -0
- package/dist/annotation/canvas/elements/PlaneElement.js +115 -0
- package/dist/annotation/canvas/planeGeometry.d.ts +48 -0
- package/dist/annotation/canvas/planeGeometry.js +507 -0
- package/dist/annotation/canvas/tools/measurementTool.d.ts +1 -0
- package/dist/annotation/canvas/tools/measurementTool.js +13 -0
- package/dist/annotation/canvas/tools/planeTool.d.ts +12 -0
- package/dist/annotation/canvas/tools/planeTool.js +357 -0
- package/dist/annotation/canvas/tools/selectTool.js +9 -0
- package/dist/annotation/canvas/useAnnotationCanvasState.d.ts +3 -1
- package/dist/annotation/canvas/useAnnotationCanvasState.js +17 -0
- package/dist/canvas/AnnotationCanvas.d.ts +11 -0
- package/dist/canvas/AnnotationCanvas.js +10 -0
- package/dist/canvas/AnnotationCanvas.native.d.ts +8 -0
- package/dist/canvas/AnnotationCanvas.native.js +6 -0
- package/dist/canvas/AnnotationCanvasInner.d.ts +39 -0
- package/dist/canvas/AnnotationCanvasInner.js +219 -0
- package/dist/canvas/AnnotationCanvasInner.native.d.ts +35 -0
- package/dist/canvas/AnnotationCanvasInner.native.js +138 -0
- package/dist/canvas/AnnotationCanvasSkia.d.ts +27 -0
- package/dist/canvas/AnnotationCanvasSkia.js +20 -0
- package/dist/canvas/Tool.d.ts +38 -0
- package/dist/canvas/Tool.js +1 -0
- package/dist/canvas/elements/BackgroundImageElement.d.ts +9 -0
- package/dist/canvas/elements/BackgroundImageElement.js +37 -0
- package/dist/canvas/elements/MeasurementStampElement.d.ts +13 -0
- package/dist/canvas/elements/MeasurementStampElement.js +30 -0
- package/dist/canvas/elements/ShapeElement.d.ts +7 -0
- package/dist/canvas/elements/ShapeElement.js +62 -0
- package/dist/canvas/elements/StrokeElement.d.ts +7 -0
- package/dist/canvas/elements/StrokeElement.js +18 -0
- package/dist/canvas/measurementPicker.d.ts +10 -0
- package/dist/canvas/measurementPicker.js +1 -0
- package/dist/canvas/measurementStampOverlay.d.ts +11 -0
- package/dist/canvas/measurementStampOverlay.js +1 -0
- package/dist/canvas/pointerAdapter.d.ts +3 -0
- package/dist/canvas/pointerAdapter.js +19 -0
- package/dist/canvas/stampLayout.d.ts +5 -0
- package/dist/canvas/stampLayout.js +14 -0
- package/dist/canvas/tools/measurementStampTool.d.ts +9 -0
- package/dist/canvas/tools/measurementStampTool.js +37 -0
- package/dist/canvas/tools/panTool.d.ts +5 -0
- package/dist/canvas/tools/panTool.js +25 -0
- package/dist/canvas/tools/penTool.d.ts +13 -0
- package/dist/canvas/tools/penTool.js +68 -0
- package/dist/canvas/tools/selectTool.d.ts +2 -0
- package/dist/canvas/tools/selectTool.js +182 -0
- package/dist/canvas/useAnnotationCanvasState.d.ts +54 -0
- package/dist/canvas/useAnnotationCanvasState.js +210 -0
- package/dist/canvas/viewport.d.ts +16 -0
- package/dist/canvas/viewport.js +54 -0
- package/dist/data/AnnotationDataContext.d.ts +8 -0
- package/dist/data/AnnotationDataContext.js +11 -0
- package/dist/data/AnnotationDataProvider.d.ts +65 -0
- package/dist/data/AnnotationDataProvider.js +4 -0
- package/dist/data/InMemoryAnnotationProvider.d.ts +30 -0
- package/dist/data/InMemoryAnnotationProvider.js +197 -0
- package/dist/data/canvasPersistence.d.ts +3 -0
- package/dist/data/canvasPersistence.js +26 -0
- package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +33 -0
- package/dist/data/hooks/useAnnotationCanvasDoc.js +314 -0
- package/dist/data/hooks/useAnnotationDoc.d.ts +7 -0
- package/dist/data/hooks/useAnnotationDoc.js +33 -0
- package/dist/data/hooks/useAnnotationList.d.ts +7 -0
- package/dist/data/hooks/useAnnotationList.js +26 -0
- package/dist/data/hooks/useAnnotationMutations.d.ts +9 -0
- package/dist/data/hooks/useAnnotationMutations.js +11 -0
- package/dist/exports.d.ts +2 -0
- package/dist/exports.js +2 -0
- package/dist/hooks/useParseMeasurement.d.ts +4 -0
- package/dist/hooks/useParseMeasurement.js +14 -0
- package/dist/types/annotation.d.ts +37 -0
- package/dist/types/annotation.js +30 -0
- package/dist/types/firestore.d.ts +5 -0
- package/dist/utils/evaluateFormula.d.ts +20 -0
- package/dist/utils/evaluateFormula.js +31 -0
- package/package.json +1 -1
- package/dist/theme/index.d.ts +0 -3
- package/dist/theme/index.js +0 -3
- package/dist/theme/preset.d.ts +0 -120
- package/dist/theme/preset.js +0 -25
- package/dist/theme/semantic.d.ts +0 -136
- package/dist/theme/semantic.js +0 -116
|
@@ -306,12 +306,15 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
306
306
|
// act on them (view mode's pan tool selects measurements but must not
|
|
307
307
|
// advertise draggability).
|
|
308
308
|
selectedId: selection?.ids[0] ?? null,
|
|
309
|
-
|
|
309
|
+
// Plane-calibration handles reuse the same screen-constant sizing —
|
|
310
|
+
// the plane tool (planeEditing) is the only tool that can drag them.
|
|
311
|
+
handleRadius: activeTool?.dragSelection || activeTool?.planeEditing
|
|
310
312
|
? HANDLE_PX / state.viewport.zoom
|
|
311
313
|
: undefined,
|
|
312
|
-
handleRingWidth: activeTool?.dragSelection
|
|
314
|
+
handleRingWidth: activeTool?.dragSelection || activeTool?.planeEditing
|
|
313
315
|
? HANDLE_RING_PX / state.viewport.zoom
|
|
314
316
|
: undefined,
|
|
317
|
+
showPlaneHandles: activeTool?.planeEditing === true,
|
|
315
318
|
customPreview,
|
|
316
319
|
}), renderMeasurementStamp && (_jsx("div", { style: {
|
|
317
320
|
position: 'absolute',
|
|
@@ -1603,9 +1603,16 @@ export const AnnotationCanvasInner = (props) => {
|
|
|
1603
1603
|
// Endpoint/corner handles are drag affordances — only the select tool can
|
|
1604
1604
|
// act on them, so suppress them when the active tool has no drag support
|
|
1605
1605
|
// (e.g. view mode's pan tool, where a selected measurement still shows its
|
|
1606
|
-
// tile chrome but must not advertise draggability).
|
|
1607
|
-
|
|
1608
|
-
|
|
1606
|
+
// tile chrome but must not advertise draggability). Plane-calibration
|
|
1607
|
+
// handles reuse the same screen-constant sizing; the plane tool
|
|
1608
|
+
// (planeEditing) is the only tool that can drag those.
|
|
1609
|
+
handleRadius: activeTool?.dragSelection || activeTool?.planeEditing
|
|
1610
|
+
? handleRadius
|
|
1611
|
+
: undefined,
|
|
1612
|
+
handleRingWidth: activeTool?.dragSelection || activeTool?.planeEditing
|
|
1613
|
+
? handleRingWidth
|
|
1614
|
+
: undefined,
|
|
1615
|
+
showPlaneHandles: activeTool?.planeEditing === true,
|
|
1609
1616
|
customPreview,
|
|
1610
1617
|
};
|
|
1611
1618
|
// Lens window for the loupe: an opaque white base + the magnified scene
|
|
@@ -88,9 +88,10 @@ export interface AnnotationCanvasSkiaProps {
|
|
|
88
88
|
handleRingWidth?: number | {
|
|
89
89
|
value: number;
|
|
90
90
|
};
|
|
91
|
+
showPlaneHandles?: boolean;
|
|
91
92
|
customPreview?: ReactNode;
|
|
92
93
|
wrapContent?: (content: ReactNode) => ReactNode;
|
|
93
94
|
canvasRef?: RefObject<CanvasRef | null>;
|
|
94
95
|
}
|
|
95
|
-
export declare const AnnotationCanvasSkia: ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, customPreview, wrapContent, canvasRef, }: AnnotationCanvasSkiaProps) => import("react/jsx-runtime").JSX.Element;
|
|
96
|
+
export declare const AnnotationCanvasSkia: ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, customPreview, wrapContent, canvasRef, }: AnnotationCanvasSkiaProps) => import("react/jsx-runtime").JSX.Element;
|
|
96
97
|
export {};
|
|
@@ -6,6 +6,7 @@ import { SELECTION_PAD, textResizeGeometry, textShapeBounds, } from './textGeome
|
|
|
6
6
|
import { visualShapeBounds } from './shapeGeometry.js';
|
|
7
7
|
import { backgroundLayerDocRect, backgroundLayersOf, } from './backgroundLayers.js';
|
|
8
8
|
import { BackgroundLayerElement } from './elements/BackgroundImageElement.js';
|
|
9
|
+
import { PlaneElement } from './elements/PlaneElement.js';
|
|
9
10
|
import { ShapeElement } from './elements/ShapeElement.js';
|
|
10
11
|
import { StrokeElement } from './elements/StrokeElement.js';
|
|
11
12
|
// Default visual constants for the measurement-annotation line (the tile itself
|
|
@@ -79,8 +80,21 @@ const SelectionBox = ({ bounds, isDragging, transform, }) => (_jsx(DraggableElem
|
|
|
79
80
|
// since the function-call pattern works identically on native we use it
|
|
80
81
|
// in both Inners for consistency. Don't add hooks here; this is a plain
|
|
81
82
|
// JSX-returning helper, not a component.
|
|
82
|
-
export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, customPreview, wrapContent, canvasRef, }) => {
|
|
83
|
-
|
|
83
|
+
export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTransform, viewport, resolveImageUrl, backgroundSkImages, valueFont, textFontMgr, penDrawingStroke, livePreview, shapePreview, draggingId, dragTransform, resizingId, resizeTransform, selectedId, endpointDragId, shapeEndpointDragId, liveLineP1, liveLineP2, rectDragId, liveRect, shapeResizeId, shapeResizePath, shapeResizeBox, handleRadius, handleRingWidth, showPlaneHandles, customPreview, wrapContent, canvasRef, }) => {
|
|
84
|
+
// Doc rect a SCALE-mode plane grid covers: the first background layer (the
|
|
85
|
+
// photo being calibrated), else the document rect. Pinned to layer 0 like
|
|
86
|
+
// the tile-sizing reference — adding a second image must not re-span the
|
|
87
|
+
// grid.
|
|
88
|
+
const firstLayer = backgroundLayersOf(effectiveCanvas.viewport)[0];
|
|
89
|
+
const planeScaleBounds = firstLayer
|
|
90
|
+
? backgroundLayerDocRect(firstLayer)
|
|
91
|
+
: {
|
|
92
|
+
x: 0,
|
|
93
|
+
y: 0,
|
|
94
|
+
width: effectiveCanvas.viewport.width,
|
|
95
|
+
height: effectiveCanvas.viewport.height,
|
|
96
|
+
};
|
|
97
|
+
const world = (_jsxs(Group, { transform: worldTransform, children: [backgroundLayersOf(effectiveCanvas.viewport).map((layer) => (_jsx(DraggableElement, { isDragging: layer.id === draggingId || layer.id === resizingId, transform: layer.id === resizingId ? resizeTransform : dragTransform, children: _jsx(BackgroundLayerElement, { layer: layer, resolveUrl: resolveImageUrl, skImage: backgroundSkImages?.[layer.id], viewport: viewport, screenWidth: width, screenHeight: height }) }, layer.id))), (effectiveCanvas.planes ?? []).map((plane) => (_jsx(PlaneElement, { plane: plane, scaleBounds: planeScaleBounds, showHandles: showPlaneHandles === true, handleRadius: showPlaneHandles ? handleRadius : undefined, handleRingWidth: showPlaneHandles ? handleRingWidth : undefined }, plane.id))), effectiveCanvas.strokes.map((stroke) => (_jsx(DraggableElement, { isDragging: stroke.id === draggingId, transform: dragTransform, children: _jsx(StrokeElement, { stroke: stroke }) }, stroke.id))), effectiveCanvas.shapes.map((shape) => {
|
|
84
98
|
// Line/arrow shapes support endpoint editing. When selected they show
|
|
85
99
|
// grab handles at both ends; during an endpoint drag the line renders
|
|
86
100
|
// from the live endpoints (one following the finger) — the shape twin
|
|
@@ -173,7 +187,7 @@ export const AnnotationCanvasSkia = ({ width, height, effectiveCanvas, worldTran
|
|
|
173
187
|
const startArrow = placed.line && !isEndpointDrag && placed.lineStartCap === 'arrow'
|
|
174
188
|
? headPath(placed.line.a, placed.line.b)
|
|
175
189
|
: null;
|
|
176
|
-
const content = (_jsxs(_Fragment, { children: [_jsx(Line, { p1: p1, p2: p2, color: lineColor, style: "stroke", strokeWidth: lineWidth, strokeCap: toSkiaStrokeCap(placed.lineCap), children: placed.lineDash && (_jsx(DashPathEffect, { intervals: dashIntervals(lineWidth) })) }), startArrow && (_jsx(Path, { path: startArrow, color: lineColor, style: "fill" })), endArrow && (_jsx(Path, { path: endArrow, color: lineColor, style: "fill" })), isSelected && handleRadius != null && (_jsxs(_Fragment, { children: [_jsx(Handle, { c: p1, r: handleRadius, ringWidth: handleRingWidth }), _jsx(Handle, { c: p2, r: handleRadius, ringWidth: handleRingWidth })] }))] }));
|
|
190
|
+
const content = (_jsxs(_Fragment, { children: [_jsx(Line, { p1: p1, p2: p2, color: lineColor, style: "stroke", strokeWidth: lineWidth, strokeCap: toSkiaStrokeCap(placed.lineCap), children: placed.lineDash && (_jsx(DashPathEffect, { intervals: dashIntervals(lineWidth) })) }), startArrow && (_jsx(Path, { path: startArrow, color: lineColor, style: "fill" })), endArrow && (_jsx(Path, { path: endArrow, color: lineColor, style: "fill" })), isSelected && handleRadius != null && !placed.planeEdge && (_jsxs(_Fragment, { children: [_jsx(Handle, { c: p1, r: handleRadius, ringWidth: handleRingWidth }), _jsx(Handle, { c: p2, r: handleRadius, ringWidth: handleRingWidth })] }))] }));
|
|
177
191
|
return isEndpointDrag ? (_jsx(Group, { children: content }, placed.id)) : (_jsx(DraggableElement, { isDragging: placed.id === draggingId, transform: dragTransform, children: content }, placed.id));
|
|
178
192
|
}), (() => {
|
|
179
193
|
if (!selectedId)
|
|
@@ -99,6 +99,7 @@ export interface Tool {
|
|
|
99
99
|
shapeDraw?: ShapeDrawConfig;
|
|
100
100
|
panViewport?: boolean;
|
|
101
101
|
dragSelection?: DragSelectionConfig;
|
|
102
|
+
planeEditing?: boolean;
|
|
102
103
|
onPointerDown?(event: CanvasPointerEvent, ctx: ToolContext, state: ToolState): ToolState | void;
|
|
103
104
|
onPointerMove?(event: CanvasPointerEvent, ctx: ToolContext, state: ToolState): ToolState | void;
|
|
104
105
|
onPointerUp?(event: CanvasPointerEvent, ctx: ToolContext, state: ToolState): ToolState | void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PlaneCalibration } from '../../../types/annotation.js';
|
|
2
|
+
type AnimatedNumber = number | {
|
|
3
|
+
value: number;
|
|
4
|
+
};
|
|
5
|
+
export interface PlaneElementProps {
|
|
6
|
+
plane: PlaneCalibration;
|
|
7
|
+
scaleBounds: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
showHandles: boolean;
|
|
14
|
+
handleRadius?: AnimatedNumber;
|
|
15
|
+
handleRingWidth?: AnimatedNumber;
|
|
16
|
+
}
|
|
17
|
+
export declare const PlaneElement: import("react").MemoExoticComponent<({ plane, scaleBounds, showHandles, handleRadius, handleRingWidth, }: PlaneElementProps) => import("react/jsx-runtime").JSX.Element>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Circle, Group, Path, Skia } from '@shopify/react-native-skia';
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { planeGridSegments, planeGridSegmentsForScale, resolvePlaneGridSpacingUm, } from '../planeGeometry.js';
|
|
5
|
+
import { FormulaColors } from '../../../theme/colors.js';
|
|
6
|
+
// Plane-calibration chrome colors. Deliberately NOT the selection blue
|
|
7
|
+
// (#0066FF — measurement chrome) so the calibration reads as its own layer of
|
|
8
|
+
// information: the formula purple outlines the calibrated quad / reference
|
|
9
|
+
// line (and inks its handles and dimension-tile lines — see planeTool), while
|
|
10
|
+
// the grid it casts gets its own pale pink so the two never blur.
|
|
11
|
+
const PLANE_COLOR = FormulaColors.purple;
|
|
12
|
+
const PLANE_GRID_COLOR = '#FFD1F8';
|
|
13
|
+
const PLANE_GRID_OPACITY = 0.9;
|
|
14
|
+
// Doc-space stroke widths (scale with zoom, like the drawn shapes).
|
|
15
|
+
const PLANE_GRID_WIDTH = 2.5;
|
|
16
|
+
const PLANE_OUTLINE_WIDTH = 4;
|
|
17
|
+
// Cross-tick half-length (doc units) marking the ends of a scale-mode
|
|
18
|
+
// reference line, so it reads as "a measured span", not just a line.
|
|
19
|
+
const REF_TICK_LEN = 18;
|
|
20
|
+
const segmentsPath = (segments) => {
|
|
21
|
+
const path = Skia.Path.Make();
|
|
22
|
+
for (const { a, b } of segments) {
|
|
23
|
+
path.moveTo(a.x, a.y);
|
|
24
|
+
path.lineTo(b.x, b.y);
|
|
25
|
+
}
|
|
26
|
+
return path;
|
|
27
|
+
};
|
|
28
|
+
// The calibration overlay: grid + outline (+ editing handles). Memoized like
|
|
29
|
+
// ShapeElement — applyOp replaces the plane object only when a setPlane op
|
|
30
|
+
// lands (previews included), so unrelated re-renders reuse the built paths.
|
|
31
|
+
// The grid respects the plane's stored gridSpacingUm (hosts stamp it at
|
|
32
|
+
// dimension-entry time from the unit the user typed); absent, it falls back
|
|
33
|
+
// to an auto "nice metric" spacing for the calibrated extent.
|
|
34
|
+
export const PlaneElement = memo(({ plane, scaleBounds, showHandles, handleRadius, handleRingWidth, }) => {
|
|
35
|
+
const gridVisible = plane.gridVisible !== false;
|
|
36
|
+
const gridPath = useMemo(() => {
|
|
37
|
+
if (!gridVisible)
|
|
38
|
+
return null;
|
|
39
|
+
const bounds = {
|
|
40
|
+
x: scaleBounds.x,
|
|
41
|
+
y: scaleBounds.y,
|
|
42
|
+
width: scaleBounds.width,
|
|
43
|
+
height: scaleBounds.height,
|
|
44
|
+
};
|
|
45
|
+
const spacing = resolvePlaneGridSpacingUm(plane, 'metric', {
|
|
46
|
+
width: bounds.width,
|
|
47
|
+
height: bounds.height,
|
|
48
|
+
});
|
|
49
|
+
if (spacing == null)
|
|
50
|
+
return null;
|
|
51
|
+
const segments = plane.mode === 'perspective'
|
|
52
|
+
? planeGridSegments(plane, spacing, bounds)
|
|
53
|
+
: planeGridSegmentsForScale(plane, spacing, bounds);
|
|
54
|
+
return segments.length > 0 ? segmentsPath(segments) : null;
|
|
55
|
+
// Keyed on the bounds' VALUES: the caller derives the rect object fresh
|
|
56
|
+
// each render (backgroundLayerDocRect), so identity would thrash.
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
+
}, [
|
|
59
|
+
plane,
|
|
60
|
+
gridVisible,
|
|
61
|
+
scaleBounds.x,
|
|
62
|
+
scaleBounds.y,
|
|
63
|
+
scaleBounds.width,
|
|
64
|
+
scaleBounds.height,
|
|
65
|
+
]);
|
|
66
|
+
const outlinePath = useMemo(() => {
|
|
67
|
+
const path = Skia.Path.Make();
|
|
68
|
+
if (plane.mode === 'perspective') {
|
|
69
|
+
const [tl, tr, br, bl] = plane.corners;
|
|
70
|
+
path.moveTo(tl.x, tl.y);
|
|
71
|
+
path.lineTo(tr.x, tr.y);
|
|
72
|
+
path.lineTo(br.x, br.y);
|
|
73
|
+
path.lineTo(bl.x, bl.y);
|
|
74
|
+
path.close();
|
|
75
|
+
return path;
|
|
76
|
+
}
|
|
77
|
+
// Scale mode: the reference line with a perpendicular tick at each end.
|
|
78
|
+
const { a, b } = plane.refLine;
|
|
79
|
+
const dx = b.x - a.x;
|
|
80
|
+
const dy = b.y - a.y;
|
|
81
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
82
|
+
path.moveTo(a.x, a.y);
|
|
83
|
+
path.lineTo(b.x, b.y);
|
|
84
|
+
if (len > 0) {
|
|
85
|
+
const nx = (-dy / len) * REF_TICK_LEN;
|
|
86
|
+
const ny = (dx / len) * REF_TICK_LEN;
|
|
87
|
+
path.moveTo(a.x - nx, a.y - ny);
|
|
88
|
+
path.lineTo(a.x + nx, a.y + ny);
|
|
89
|
+
path.moveTo(b.x - nx, b.y - ny);
|
|
90
|
+
path.lineTo(b.x + nx, b.y + ny);
|
|
91
|
+
}
|
|
92
|
+
return path;
|
|
93
|
+
}, [plane]);
|
|
94
|
+
const handlePoints = showHandles
|
|
95
|
+
? plane.mode === 'perspective'
|
|
96
|
+
? plane.corners
|
|
97
|
+
: [plane.refLine.a, plane.refLine.b]
|
|
98
|
+
: [];
|
|
99
|
+
return (_jsxs(Group, { children: [gridPath && (_jsx(Path, { path: gridPath, color: PLANE_GRID_COLOR, style: "stroke", strokeWidth: PLANE_GRID_WIDTH, opacity: PLANE_GRID_OPACITY })), _jsx(Path, { path: outlinePath, color: PLANE_COLOR, style: "stroke", strokeWidth: PLANE_OUTLINE_WIDTH, strokeJoin: "round" }), handleRadius != null &&
|
|
100
|
+
handlePoints.map((p, i) => (_jsxs(Group, { children: [_jsx(Circle, { c: p, r: handleRadius, color: PLANE_COLOR }), _jsx(Circle, { c: p, r: handleRadius, color: "#FFFFFF", style: "stroke", strokeWidth: handleRingWidth })] }, i)))] }));
|
|
101
|
+
},
|
|
102
|
+
// Value-compare the bounds rect: AnnotationCanvasSkia derives it fresh each
|
|
103
|
+
// render (backgroundLayerDocRect returns a new object), so the default
|
|
104
|
+
// shallow compare would re-render this element on every canvas render and
|
|
105
|
+
// defeat the grid-path memoization. Everything else compares by identity —
|
|
106
|
+
// applyOp replaces the plane object only when a setPlane op lands.
|
|
107
|
+
(prev, next) => prev.plane === next.plane &&
|
|
108
|
+
prev.showHandles === next.showHandles &&
|
|
109
|
+
prev.handleRadius === next.handleRadius &&
|
|
110
|
+
prev.handleRingWidth === next.handleRingWidth &&
|
|
111
|
+
prev.scaleBounds.x === next.scaleBounds.x &&
|
|
112
|
+
prev.scaleBounds.y === next.scaleBounds.y &&
|
|
113
|
+
prev.scaleBounds.width === next.scaleBounds.width &&
|
|
114
|
+
prev.scaleBounds.height === next.scaleBounds.height);
|
|
115
|
+
PlaneElement.displayName = 'PlaneElement';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { PlacedMeasurementRef, PlaneCalibration, PlaneEdge, PlanePerspectiveCalibration, PlaneScaleCalibration, Vec2 } from '../../types/annotation.js';
|
|
2
|
+
export interface Homography {
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
c: number;
|
|
6
|
+
d: number;
|
|
7
|
+
e: number;
|
|
8
|
+
f: number;
|
|
9
|
+
g: number;
|
|
10
|
+
h: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const homographyFromQuad: (corners: readonly [Vec2, Vec2, Vec2, Vec2]) => Homography | null;
|
|
13
|
+
export declare const invertHomography: (m: Homography) => Homography | null;
|
|
14
|
+
export declare const applyHomography: (m: Homography, p: Vec2) => Vec2 | null;
|
|
15
|
+
export declare const isQuadConvexNonDegenerate: (corners: readonly [Vec2, Vec2, Vec2, Vec2]) => boolean;
|
|
16
|
+
export declare const normalizeQuadOrder: (pts: readonly Vec2[]) => [Vec2, Vec2, Vec2, Vec2] | null;
|
|
17
|
+
export declare const docToPlaneUm: (cal: PlanePerspectiveCalibration, p: Vec2) => Vec2 | null;
|
|
18
|
+
export declare const planeDistanceUm: (cal: PlaneCalibration, a: Vec2, b: Vec2) => number | null;
|
|
19
|
+
export declare const planeEdgeLine: (cal: PlaneCalibration, edge: PlaneEdge) => {
|
|
20
|
+
a: Vec2;
|
|
21
|
+
b: Vec2;
|
|
22
|
+
} | null;
|
|
23
|
+
export declare const planeDimensionValueUm: (cal: PlaneCalibration, edge: PlaneEdge) => number | null;
|
|
24
|
+
export declare const withPlaneDimensionUm: (cal: PlaneCalibration, edge: PlaneEdge, um: number) => PlaneCalibration;
|
|
25
|
+
export declare const planeLengthForPlaced: (planes: PlaneCalibration[] | undefined, placed: PlacedMeasurementRef) => number | null;
|
|
26
|
+
export declare const nicePlaneGridSpacingUm: (extentUm: number, system: "metric" | "imperial") => number;
|
|
27
|
+
export declare const resolvePlaneGridSpacingUm: (cal: PlaneCalibration, system: "metric" | "imperial", scaleBounds?: {
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}) => number | null;
|
|
31
|
+
export declare const planeGridSegments: (cal: PlanePerspectiveCalibration, spacingUm: number, bounds: {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}) => {
|
|
37
|
+
a: Vec2;
|
|
38
|
+
b: Vec2;
|
|
39
|
+
}[];
|
|
40
|
+
export declare const planeGridSegmentsForScale: (cal: PlaneScaleCalibration, spacingUm: number, bounds: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}) => {
|
|
46
|
+
a: Vec2;
|
|
47
|
+
b: Vec2;
|
|
48
|
+
}[];
|