@embedpdf/utils 2.4.1 → 2.6.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/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +834 -317
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +834 -317
- package/dist/react/index.js.map +1 -1
- package/dist/shared/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-preact/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared-preact/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared-preact/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-preact/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-preact/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-preact/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-react/hooks/use-drag-resize.d.ts +4 -0
- package/dist/shared-react/hooks/use-interaction-handles.d.ts +18 -2
- package/dist/shared-react/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-react/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-react/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-react/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-svelte/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-svelte/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-svelte/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-svelte/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/shared-vue/plugin-interaction-primitives/drag-resize-controller.d.ts +49 -23
- package/dist/shared-vue/plugin-interaction-primitives/index.d.ts +1 -0
- package/dist/shared-vue/plugin-interaction-primitives/resize-geometry.d.ts +72 -0
- package/dist/shared-vue/plugin-interaction-primitives/utils.d.ts +33 -0
- package/dist/svelte/hooks/use-drag-resize.svelte.d.ts +1 -0
- package/dist/svelte/hooks/use-interaction-handles.svelte.d.ts +16 -2
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +680 -288
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/hooks/use-drag-resize.d.ts +9 -0
- package/dist/vue/hooks/use-interaction-handles.d.ts +17 -2
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +716 -292
- package/dist/vue/index.js.map +1 -1
- package/package.json +2 -2
package/dist/vue/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { defineComponent, computed, renderSlot, toRaw,
|
|
1
|
+
import { defineComponent, computed, renderSlot, toRaw, isRef, unref, ref, markRaw, watch, onUnmounted, isReactive, isProxy } from "vue";
|
|
2
2
|
import { getCounterRotation } from "@embedpdf/utils";
|
|
3
|
+
import { rotatePointAround, calculateRotatedRectAABB, normalizeAngle } from "@embedpdf/models";
|
|
3
4
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
4
5
|
__name: "counter-rotate-container",
|
|
5
6
|
props: {
|
|
@@ -43,12 +44,405 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
});
|
|
47
|
+
const ROTATION_HANDLE_MARGIN = 35;
|
|
48
|
+
const HANDLE_BASE_ANGLE = {
|
|
49
|
+
n: 0,
|
|
50
|
+
ne: 45,
|
|
51
|
+
e: 90,
|
|
52
|
+
se: 135,
|
|
53
|
+
s: 180,
|
|
54
|
+
sw: 225,
|
|
55
|
+
w: 270,
|
|
56
|
+
nw: 315
|
|
57
|
+
};
|
|
58
|
+
const SECTOR_CURSORS = [
|
|
59
|
+
"ns-resize",
|
|
60
|
+
// 0: north
|
|
61
|
+
"nesw-resize",
|
|
62
|
+
// 1: NE
|
|
63
|
+
"ew-resize",
|
|
64
|
+
// 2: east
|
|
65
|
+
"nwse-resize",
|
|
66
|
+
// 3: SE
|
|
67
|
+
"ns-resize",
|
|
68
|
+
// 4: south
|
|
69
|
+
"nesw-resize",
|
|
70
|
+
// 5: SW
|
|
71
|
+
"ew-resize",
|
|
72
|
+
// 6: west
|
|
73
|
+
"nwse-resize"
|
|
74
|
+
// 7: NW
|
|
75
|
+
];
|
|
76
|
+
function diagonalCursor(handle, pageQuarterTurns, annotationRotation = 0) {
|
|
77
|
+
const pageAngle = pageQuarterTurns * 90;
|
|
78
|
+
const totalAngle = HANDLE_BASE_ANGLE[handle] + pageAngle + annotationRotation;
|
|
79
|
+
const normalized = (totalAngle % 360 + 360) % 360;
|
|
80
|
+
const sector = Math.round(normalized / 45) % 8;
|
|
81
|
+
return SECTOR_CURSORS[sector];
|
|
82
|
+
}
|
|
83
|
+
function edgeOffset(k, spacing, mode) {
|
|
84
|
+
const base = -k / 2;
|
|
85
|
+
if (mode === "center") return base;
|
|
86
|
+
return mode === "outside" ? base - spacing : base + spacing;
|
|
87
|
+
}
|
|
88
|
+
function describeResizeFromConfig(cfg, ui = {}) {
|
|
89
|
+
const {
|
|
90
|
+
handleSize = 8,
|
|
91
|
+
spacing = 1,
|
|
92
|
+
offsetMode = "outside",
|
|
93
|
+
includeSides = false,
|
|
94
|
+
zIndex = 3,
|
|
95
|
+
rotationAwareCursor = true
|
|
96
|
+
} = ui;
|
|
97
|
+
const pageQuarterTurns = (cfg.pageRotation ?? 0) % 4;
|
|
98
|
+
const annotationRot = cfg.annotationRotation ?? 0;
|
|
99
|
+
const off = (edge) => ({
|
|
100
|
+
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
101
|
+
});
|
|
102
|
+
const corners = [
|
|
103
|
+
["nw", { ...off("top"), ...off("left") }],
|
|
104
|
+
["ne", { ...off("top"), ...off("right") }],
|
|
105
|
+
["sw", { ...off("bottom"), ...off("left") }],
|
|
106
|
+
["se", { ...off("bottom"), ...off("right") }]
|
|
107
|
+
];
|
|
108
|
+
const sides = includeSides ? [
|
|
109
|
+
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
110
|
+
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
111
|
+
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
112
|
+
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
113
|
+
] : [];
|
|
114
|
+
const all = [...corners, ...sides];
|
|
115
|
+
return all.map(([handle, pos]) => ({
|
|
116
|
+
handle,
|
|
117
|
+
style: {
|
|
118
|
+
position: "absolute",
|
|
119
|
+
width: handleSize + "px",
|
|
120
|
+
height: handleSize + "px",
|
|
121
|
+
borderRadius: "50%",
|
|
122
|
+
zIndex,
|
|
123
|
+
cursor: rotationAwareCursor ? diagonalCursor(handle, pageQuarterTurns, annotationRot) : "default",
|
|
124
|
+
pointerEvents: "auto",
|
|
125
|
+
touchAction: "none",
|
|
126
|
+
...pos
|
|
127
|
+
},
|
|
128
|
+
attrs: { "data-epdf-handle": handle }
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
132
|
+
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
133
|
+
const rect = cfg.element;
|
|
134
|
+
const scale = cfg.scale ?? 1;
|
|
135
|
+
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
136
|
+
return verts.map((v, i) => {
|
|
137
|
+
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
138
|
+
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
139
|
+
return {
|
|
140
|
+
handle: "nw",
|
|
141
|
+
// not used; kept for type
|
|
142
|
+
style: {
|
|
143
|
+
position: "absolute",
|
|
144
|
+
left: left + "px",
|
|
145
|
+
top: top + "px",
|
|
146
|
+
width: vertexSize + "px",
|
|
147
|
+
height: vertexSize + "px",
|
|
148
|
+
borderRadius: "50%",
|
|
149
|
+
cursor: "pointer",
|
|
150
|
+
zIndex,
|
|
151
|
+
pointerEvents: "auto",
|
|
152
|
+
touchAction: "none"
|
|
153
|
+
},
|
|
154
|
+
attrs: { "data-epdf-vertex": i }
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function describeRotationFromConfig(cfg, ui = {}, currentAngle = 0) {
|
|
159
|
+
const { handleSize = 16, zIndex = 5, showConnector = true, connectorWidth = 1 } = ui;
|
|
160
|
+
const scale = cfg.scale ?? 1;
|
|
161
|
+
const rect = cfg.element;
|
|
162
|
+
const orbitRect = cfg.rotationElement ?? rect;
|
|
163
|
+
const orbitCenter = cfg.rotationCenter ?? {
|
|
164
|
+
x: rect.origin.x + rect.size.width / 2,
|
|
165
|
+
y: rect.origin.y + rect.size.height / 2
|
|
166
|
+
};
|
|
167
|
+
orbitRect.size.width * scale;
|
|
168
|
+
orbitRect.size.height * scale;
|
|
169
|
+
const centerX = (orbitCenter.x - orbitRect.origin.x) * scale;
|
|
170
|
+
const centerY = (orbitCenter.y - orbitRect.origin.y) * scale;
|
|
171
|
+
const angleRad = currentAngle * Math.PI / 180;
|
|
172
|
+
const margin = ui.margin ?? ROTATION_HANDLE_MARGIN;
|
|
173
|
+
const radius = rect.size.height * scale / 2 + margin;
|
|
174
|
+
const handleCenterX = centerX + radius * Math.sin(angleRad);
|
|
175
|
+
const handleCenterY = centerY - radius * Math.cos(angleRad);
|
|
176
|
+
const handleLeft = handleCenterX - handleSize / 2;
|
|
177
|
+
const handleTop = handleCenterY - handleSize / 2;
|
|
178
|
+
return {
|
|
179
|
+
handleStyle: {
|
|
180
|
+
position: "absolute",
|
|
181
|
+
left: handleLeft + "px",
|
|
182
|
+
top: handleTop + "px",
|
|
183
|
+
width: handleSize + "px",
|
|
184
|
+
height: handleSize + "px",
|
|
185
|
+
borderRadius: "50%",
|
|
186
|
+
cursor: "grab",
|
|
187
|
+
zIndex,
|
|
188
|
+
pointerEvents: "auto",
|
|
189
|
+
touchAction: "none"
|
|
190
|
+
},
|
|
191
|
+
connectorStyle: showConnector ? {
|
|
192
|
+
position: "absolute",
|
|
193
|
+
left: centerX - connectorWidth / 2 + "px",
|
|
194
|
+
top: centerY - radius + "px",
|
|
195
|
+
width: connectorWidth + "px",
|
|
196
|
+
height: radius + "px",
|
|
197
|
+
transformOrigin: "center bottom",
|
|
198
|
+
transform: `rotate(${currentAngle}deg)`,
|
|
199
|
+
zIndex: zIndex - 1,
|
|
200
|
+
pointerEvents: "none"
|
|
201
|
+
} : {},
|
|
202
|
+
radius,
|
|
203
|
+
attrs: { "data-epdf-rotation-handle": true }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
46
206
|
function getAnchor(handle) {
|
|
47
207
|
return {
|
|
48
208
|
x: handle.includes("e") ? "left" : handle.includes("w") ? "right" : "center",
|
|
49
209
|
y: handle.includes("s") ? "top" : handle.includes("n") ? "bottom" : "center"
|
|
50
210
|
};
|
|
51
211
|
}
|
|
212
|
+
function getAnchorPoint(rect, anchor) {
|
|
213
|
+
const x = anchor.x === "left" ? rect.origin.x : anchor.x === "right" ? rect.origin.x + rect.size.width : rect.origin.x + rect.size.width / 2;
|
|
214
|
+
const y = anchor.y === "top" ? rect.origin.y : anchor.y === "bottom" ? rect.origin.y + rect.size.height : rect.origin.y + rect.size.height / 2;
|
|
215
|
+
return { x, y };
|
|
216
|
+
}
|
|
217
|
+
function applyResizeDelta(startRect, delta, anchor) {
|
|
218
|
+
let x = startRect.origin.x;
|
|
219
|
+
let y = startRect.origin.y;
|
|
220
|
+
let width = startRect.size.width;
|
|
221
|
+
let height = startRect.size.height;
|
|
222
|
+
if (anchor.x === "left") {
|
|
223
|
+
width += delta.x;
|
|
224
|
+
} else if (anchor.x === "right") {
|
|
225
|
+
x += delta.x;
|
|
226
|
+
width -= delta.x;
|
|
227
|
+
}
|
|
228
|
+
if (anchor.y === "top") {
|
|
229
|
+
height += delta.y;
|
|
230
|
+
} else if (anchor.y === "bottom") {
|
|
231
|
+
y += delta.y;
|
|
232
|
+
height -= delta.y;
|
|
233
|
+
}
|
|
234
|
+
return { origin: { x, y }, size: { width, height } };
|
|
235
|
+
}
|
|
236
|
+
function enforceAspectRatio(rect, startRect, anchor, aspectRatio) {
|
|
237
|
+
let { x, y } = rect.origin;
|
|
238
|
+
let { width, height } = rect.size;
|
|
239
|
+
const isEdgeHandle = anchor.x === "center" || anchor.y === "center";
|
|
240
|
+
if (isEdgeHandle) {
|
|
241
|
+
if (anchor.y === "center") {
|
|
242
|
+
height = width / aspectRatio;
|
|
243
|
+
y = startRect.origin.y + (startRect.size.height - height) / 2;
|
|
244
|
+
} else {
|
|
245
|
+
width = height * aspectRatio;
|
|
246
|
+
x = startRect.origin.x + (startRect.size.width - width) / 2;
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
const dw = Math.abs(width - startRect.size.width);
|
|
250
|
+
const dh = Math.abs(height - startRect.size.height);
|
|
251
|
+
const total = dw + dh;
|
|
252
|
+
if (total === 0) {
|
|
253
|
+
width = startRect.size.width;
|
|
254
|
+
height = startRect.size.height;
|
|
255
|
+
} else {
|
|
256
|
+
const wWeight = dw / total;
|
|
257
|
+
const hWeight = dh / total;
|
|
258
|
+
const wFromW = width;
|
|
259
|
+
const hFromW = width / aspectRatio;
|
|
260
|
+
const wFromH = height * aspectRatio;
|
|
261
|
+
const hFromH = height;
|
|
262
|
+
width = wWeight * wFromW + hWeight * wFromH;
|
|
263
|
+
height = wWeight * hFromW + hWeight * hFromH;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (anchor.x === "right") {
|
|
267
|
+
x = startRect.origin.x + startRect.size.width - width;
|
|
268
|
+
}
|
|
269
|
+
if (anchor.y === "bottom") {
|
|
270
|
+
y = startRect.origin.y + startRect.size.height - height;
|
|
271
|
+
}
|
|
272
|
+
return { origin: { x, y }, size: { width, height } };
|
|
273
|
+
}
|
|
274
|
+
function clampToBounds(rect, startRect, anchor, bbox, maintainAspectRatio) {
|
|
275
|
+
if (!bbox) return rect;
|
|
276
|
+
let { x, y } = rect.origin;
|
|
277
|
+
let { width, height } = rect.size;
|
|
278
|
+
width = Math.max(1, width);
|
|
279
|
+
height = Math.max(1, height);
|
|
280
|
+
const anchorX = anchor.x === "left" ? startRect.origin.x : startRect.origin.x + startRect.size.width;
|
|
281
|
+
const anchorY = anchor.y === "top" ? startRect.origin.y : startRect.origin.y + startRect.size.height;
|
|
282
|
+
const maxW = anchor.x === "left" ? bbox.width - anchorX : anchor.x === "right" ? anchorX : Math.min(startRect.origin.x, bbox.width - startRect.origin.x - startRect.size.width) * 2 + startRect.size.width;
|
|
283
|
+
const maxH = anchor.y === "top" ? bbox.height - anchorY : anchor.y === "bottom" ? anchorY : Math.min(startRect.origin.y, bbox.height - startRect.origin.y - startRect.size.height) * 2 + startRect.size.height;
|
|
284
|
+
if (maintainAspectRatio) {
|
|
285
|
+
const scaleW = width > maxW ? maxW / width : 1;
|
|
286
|
+
const scaleH = height > maxH ? maxH / height : 1;
|
|
287
|
+
const scale = Math.min(scaleW, scaleH);
|
|
288
|
+
if (scale < 1) {
|
|
289
|
+
width *= scale;
|
|
290
|
+
height *= scale;
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
width = Math.min(width, maxW);
|
|
294
|
+
height = Math.min(height, maxH);
|
|
295
|
+
}
|
|
296
|
+
if (anchor.x === "left") {
|
|
297
|
+
x = anchorX;
|
|
298
|
+
} else if (anchor.x === "right") {
|
|
299
|
+
x = anchorX - width;
|
|
300
|
+
} else {
|
|
301
|
+
x = startRect.origin.x + (startRect.size.width - width) / 2;
|
|
302
|
+
}
|
|
303
|
+
if (anchor.y === "top") {
|
|
304
|
+
y = anchorY;
|
|
305
|
+
} else if (anchor.y === "bottom") {
|
|
306
|
+
y = anchorY - height;
|
|
307
|
+
} else {
|
|
308
|
+
y = startRect.origin.y + (startRect.size.height - height) / 2;
|
|
309
|
+
}
|
|
310
|
+
x = Math.max(0, Math.min(x, bbox.width - width));
|
|
311
|
+
y = Math.max(0, Math.min(y, bbox.height - height));
|
|
312
|
+
return { origin: { x, y }, size: { width, height } };
|
|
313
|
+
}
|
|
314
|
+
function reanchorRect(rect, startRect, anchor) {
|
|
315
|
+
let x;
|
|
316
|
+
let y;
|
|
317
|
+
if (anchor.x === "left") {
|
|
318
|
+
x = startRect.origin.x;
|
|
319
|
+
} else if (anchor.x === "right") {
|
|
320
|
+
x = startRect.origin.x + startRect.size.width - rect.size.width;
|
|
321
|
+
} else {
|
|
322
|
+
x = startRect.origin.x + (startRect.size.width - rect.size.width) / 2;
|
|
323
|
+
}
|
|
324
|
+
if (anchor.y === "top") {
|
|
325
|
+
y = startRect.origin.y;
|
|
326
|
+
} else if (anchor.y === "bottom") {
|
|
327
|
+
y = startRect.origin.y + startRect.size.height - rect.size.height;
|
|
328
|
+
} else {
|
|
329
|
+
y = startRect.origin.y + (startRect.size.height - rect.size.height) / 2;
|
|
330
|
+
}
|
|
331
|
+
return { origin: { x, y }, size: rect.size };
|
|
332
|
+
}
|
|
333
|
+
function applyConstraints(position, constraints, maintainAspectRatio, skipBoundingClamp = false) {
|
|
334
|
+
if (!constraints) return position;
|
|
335
|
+
let {
|
|
336
|
+
origin: { x, y },
|
|
337
|
+
size: { width, height }
|
|
338
|
+
} = position;
|
|
339
|
+
const minW = constraints.minWidth ?? 1;
|
|
340
|
+
const minH = constraints.minHeight ?? 1;
|
|
341
|
+
const maxW = constraints.maxWidth;
|
|
342
|
+
const maxH = constraints.maxHeight;
|
|
343
|
+
if (maintainAspectRatio && width > 0 && height > 0) {
|
|
344
|
+
const ratio = width / height;
|
|
345
|
+
if (width < minW) {
|
|
346
|
+
width = minW;
|
|
347
|
+
height = width / ratio;
|
|
348
|
+
}
|
|
349
|
+
if (height < minH) {
|
|
350
|
+
height = minH;
|
|
351
|
+
width = height * ratio;
|
|
352
|
+
}
|
|
353
|
+
if (maxW !== void 0 && width > maxW) {
|
|
354
|
+
width = maxW;
|
|
355
|
+
height = width / ratio;
|
|
356
|
+
}
|
|
357
|
+
if (maxH !== void 0 && height > maxH) {
|
|
358
|
+
height = maxH;
|
|
359
|
+
width = height * ratio;
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
width = Math.max(minW, width);
|
|
363
|
+
height = Math.max(minH, height);
|
|
364
|
+
if (maxW !== void 0) width = Math.min(maxW, width);
|
|
365
|
+
if (maxH !== void 0) height = Math.min(maxH, height);
|
|
366
|
+
}
|
|
367
|
+
if (constraints.boundingBox && !skipBoundingClamp) {
|
|
368
|
+
x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));
|
|
369
|
+
y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));
|
|
370
|
+
}
|
|
371
|
+
return { origin: { x, y }, size: { width, height } };
|
|
372
|
+
}
|
|
373
|
+
function isRectWithinRotatedBounds(rect, angleDegrees, bbox) {
|
|
374
|
+
const eps = 1e-6;
|
|
375
|
+
const aabb = calculateRotatedRectAABB(rect, angleDegrees);
|
|
376
|
+
return aabb.origin.x >= -eps && aabb.origin.y >= -eps && aabb.origin.x + aabb.size.width <= bbox.width + eps && aabb.origin.y + aabb.size.height <= bbox.height + eps;
|
|
377
|
+
}
|
|
378
|
+
function computeResizeStep(delta, handle, config, clampLocalBounds, skipConstraintBoundingClamp) {
|
|
379
|
+
const { startRect, maintainAspectRatio = false, annotationRotation = 0, constraints } = config;
|
|
380
|
+
const anchor = getAnchor(handle);
|
|
381
|
+
const aspectRatio = startRect.size.width / startRect.size.height || 1;
|
|
382
|
+
let rect = applyResizeDelta(startRect, delta, anchor);
|
|
383
|
+
if (maintainAspectRatio) {
|
|
384
|
+
rect = enforceAspectRatio(rect, startRect, anchor, aspectRatio);
|
|
385
|
+
}
|
|
386
|
+
if (clampLocalBounds) {
|
|
387
|
+
rect = clampToBounds(rect, startRect, anchor, constraints == null ? void 0 : constraints.boundingBox, maintainAspectRatio);
|
|
388
|
+
}
|
|
389
|
+
rect = applyConstraints(rect, constraints, maintainAspectRatio, skipConstraintBoundingClamp);
|
|
390
|
+
if (skipConstraintBoundingClamp) {
|
|
391
|
+
rect = reanchorRect(rect, startRect, anchor);
|
|
392
|
+
}
|
|
393
|
+
if (annotationRotation !== 0) {
|
|
394
|
+
const anchorPt = getAnchorPoint(startRect, anchor);
|
|
395
|
+
const oldCenter = {
|
|
396
|
+
x: startRect.origin.x + startRect.size.width / 2,
|
|
397
|
+
y: startRect.origin.y + startRect.size.height / 2
|
|
398
|
+
};
|
|
399
|
+
const newCenter = {
|
|
400
|
+
x: rect.origin.x + rect.size.width / 2,
|
|
401
|
+
y: rect.origin.y + rect.size.height / 2
|
|
402
|
+
};
|
|
403
|
+
const oldVisual = rotatePointAround(anchorPt, oldCenter, annotationRotation);
|
|
404
|
+
const newVisual = rotatePointAround(anchorPt, newCenter, annotationRotation);
|
|
405
|
+
rect = {
|
|
406
|
+
origin: {
|
|
407
|
+
x: rect.origin.x + (oldVisual.x - newVisual.x),
|
|
408
|
+
y: rect.origin.y + (oldVisual.y - newVisual.y)
|
|
409
|
+
},
|
|
410
|
+
size: rect.size
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
return rect;
|
|
414
|
+
}
|
|
415
|
+
function computeResizedRect(delta, handle, config) {
|
|
416
|
+
const { annotationRotation = 0, constraints } = config;
|
|
417
|
+
const bbox = constraints == null ? void 0 : constraints.boundingBox;
|
|
418
|
+
if (annotationRotation !== 0 && bbox) {
|
|
419
|
+
const target = computeResizeStep(delta, handle, config, false, true);
|
|
420
|
+
if (isRectWithinRotatedBounds(target, annotationRotation, bbox)) {
|
|
421
|
+
return target;
|
|
422
|
+
}
|
|
423
|
+
let best = computeResizeStep({ x: 0, y: 0 }, handle, config, false, true);
|
|
424
|
+
let low = 0;
|
|
425
|
+
let high = 1;
|
|
426
|
+
for (let i = 0; i < 20; i += 1) {
|
|
427
|
+
const mid = (low + high) / 2;
|
|
428
|
+
const trial = computeResizeStep(
|
|
429
|
+
{ x: delta.x * mid, y: delta.y * mid },
|
|
430
|
+
handle,
|
|
431
|
+
config,
|
|
432
|
+
false,
|
|
433
|
+
true
|
|
434
|
+
);
|
|
435
|
+
if (isRectWithinRotatedBounds(trial, annotationRotation, bbox)) {
|
|
436
|
+
best = trial;
|
|
437
|
+
low = mid;
|
|
438
|
+
} else {
|
|
439
|
+
high = mid;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return best;
|
|
443
|
+
}
|
|
444
|
+
return computeResizeStep(delta, handle, config, true, false);
|
|
445
|
+
}
|
|
52
446
|
class DragResizeController {
|
|
53
447
|
constructor(config, onUpdate) {
|
|
54
448
|
this.config = config;
|
|
@@ -56,29 +450,41 @@ class DragResizeController {
|
|
|
56
450
|
this.state = "idle";
|
|
57
451
|
this.startPoint = null;
|
|
58
452
|
this.startElement = null;
|
|
453
|
+
this.startRotationElement = null;
|
|
454
|
+
this.gestureRotationCenter = null;
|
|
59
455
|
this.activeHandle = null;
|
|
60
456
|
this.currentPosition = null;
|
|
61
457
|
this.activeVertexIndex = null;
|
|
62
458
|
this.startVertices = [];
|
|
63
459
|
this.currentVertices = [];
|
|
460
|
+
this.rotationCenter = null;
|
|
461
|
+
this.centerScreen = null;
|
|
462
|
+
this.initialRotation = 0;
|
|
463
|
+
this.lastComputedRotation = 0;
|
|
464
|
+
this.rotationDelta = 0;
|
|
465
|
+
this.rotationSnappedAngle = null;
|
|
64
466
|
this.currentVertices = config.vertices || [];
|
|
65
467
|
}
|
|
66
468
|
updateConfig(config) {
|
|
67
469
|
this.config = { ...this.config, ...config };
|
|
68
|
-
this.
|
|
470
|
+
if (this.state !== "vertex-editing") {
|
|
471
|
+
this.currentVertices = config.vertices || [];
|
|
472
|
+
}
|
|
69
473
|
}
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
// Gesture start
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
70
477
|
startDrag(clientX, clientY) {
|
|
71
478
|
this.state = "dragging";
|
|
72
479
|
this.startPoint = { x: clientX, y: clientY };
|
|
73
480
|
this.startElement = { ...this.config.element };
|
|
481
|
+
this.startRotationElement = this.config.rotationElement ? { ...this.config.rotationElement } : null;
|
|
74
482
|
this.currentPosition = { ...this.config.element };
|
|
75
483
|
this.onUpdate({
|
|
76
484
|
state: "start",
|
|
77
485
|
transformData: {
|
|
78
486
|
type: "move",
|
|
79
|
-
changes: {
|
|
80
|
-
rect: this.startElement
|
|
81
|
-
}
|
|
487
|
+
changes: { rect: this.startElement }
|
|
82
488
|
}
|
|
83
489
|
});
|
|
84
490
|
}
|
|
@@ -92,9 +498,7 @@ class DragResizeController {
|
|
|
92
498
|
state: "start",
|
|
93
499
|
transformData: {
|
|
94
500
|
type: "resize",
|
|
95
|
-
changes: {
|
|
96
|
-
rect: this.startElement
|
|
97
|
-
},
|
|
501
|
+
changes: { rect: this.startElement },
|
|
98
502
|
metadata: {
|
|
99
503
|
handle: this.activeHandle,
|
|
100
504
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -109,45 +513,87 @@ class DragResizeController {
|
|
|
109
513
|
this.activeVertexIndex = vertexIndex;
|
|
110
514
|
this.startPoint = { x: clientX, y: clientY };
|
|
111
515
|
this.startVertices = [...this.currentVertices];
|
|
516
|
+
this.gestureRotationCenter = this.config.rotationCenter ?? {
|
|
517
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
518
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
519
|
+
};
|
|
112
520
|
this.onUpdate({
|
|
113
521
|
state: "start",
|
|
114
522
|
transformData: {
|
|
115
523
|
type: "vertex-edit",
|
|
116
|
-
changes: {
|
|
117
|
-
|
|
118
|
-
|
|
524
|
+
changes: { vertices: this.startVertices },
|
|
525
|
+
metadata: { vertexIndex }
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
startRotation(clientX, clientY, initialRotation = 0, orbitRadiusPx) {
|
|
530
|
+
this.state = "rotating";
|
|
531
|
+
this.startPoint = { x: clientX, y: clientY };
|
|
532
|
+
this.startElement = { ...this.config.element };
|
|
533
|
+
this.rotationCenter = this.config.rotationCenter ?? {
|
|
534
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
535
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
536
|
+
};
|
|
537
|
+
const { scale = 1 } = this.config;
|
|
538
|
+
const orbitRect = this.config.rotationElement ?? this.config.element;
|
|
539
|
+
const sw = orbitRect.size.width * scale;
|
|
540
|
+
const sh = orbitRect.size.height * scale;
|
|
541
|
+
const radius = orbitRadiusPx ?? Math.max(sw, sh) / 2 + ROTATION_HANDLE_MARGIN;
|
|
542
|
+
const pageRotOffset = (this.config.pageRotation ?? 0) * 90;
|
|
543
|
+
const screenAngleRad = (initialRotation + pageRotOffset) * Math.PI / 180;
|
|
544
|
+
this.centerScreen = {
|
|
545
|
+
x: clientX - radius * Math.sin(screenAngleRad),
|
|
546
|
+
y: clientY + radius * Math.cos(screenAngleRad)
|
|
547
|
+
};
|
|
548
|
+
this.initialRotation = initialRotation;
|
|
549
|
+
this.lastComputedRotation = initialRotation;
|
|
550
|
+
this.rotationDelta = 0;
|
|
551
|
+
this.rotationSnappedAngle = null;
|
|
552
|
+
this.onUpdate({
|
|
553
|
+
state: "start",
|
|
554
|
+
transformData: {
|
|
555
|
+
type: "rotate",
|
|
556
|
+
changes: { rotation: initialRotation },
|
|
119
557
|
metadata: {
|
|
120
|
-
|
|
558
|
+
rotationAngle: initialRotation,
|
|
559
|
+
rotationDelta: 0,
|
|
560
|
+
rotationCenter: this.rotationCenter,
|
|
561
|
+
isSnapped: false
|
|
121
562
|
}
|
|
122
563
|
}
|
|
123
564
|
});
|
|
124
565
|
}
|
|
125
|
-
|
|
566
|
+
// ---------------------------------------------------------------------------
|
|
567
|
+
// Gesture move
|
|
568
|
+
// ---------------------------------------------------------------------------
|
|
569
|
+
move(clientX, clientY, buttons) {
|
|
126
570
|
if (this.state === "idle" || !this.startPoint) return;
|
|
571
|
+
if (buttons !== void 0 && buttons === 0) {
|
|
572
|
+
this.end();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
127
575
|
if (this.state === "dragging" && this.startElement) {
|
|
128
576
|
const delta = this.calculateDelta(clientX, clientY);
|
|
129
577
|
const position = this.calculateDragPosition(delta);
|
|
130
578
|
this.currentPosition = position;
|
|
131
579
|
this.onUpdate({
|
|
132
580
|
state: "move",
|
|
133
|
-
transformData: {
|
|
134
|
-
type: "move",
|
|
135
|
-
changes: {
|
|
136
|
-
rect: position
|
|
137
|
-
}
|
|
138
|
-
}
|
|
581
|
+
transformData: { type: "move", changes: { rect: position } }
|
|
139
582
|
});
|
|
140
583
|
} else if (this.state === "resizing" && this.activeHandle && this.startElement) {
|
|
141
|
-
const delta = this.
|
|
142
|
-
const position =
|
|
584
|
+
const delta = this.calculateLocalDelta(clientX, clientY);
|
|
585
|
+
const position = computeResizedRect(delta, this.activeHandle, {
|
|
586
|
+
startRect: this.startElement,
|
|
587
|
+
maintainAspectRatio: this.config.maintainAspectRatio,
|
|
588
|
+
annotationRotation: this.config.annotationRotation,
|
|
589
|
+
constraints: this.config.constraints
|
|
590
|
+
});
|
|
143
591
|
this.currentPosition = position;
|
|
144
592
|
this.onUpdate({
|
|
145
593
|
state: "move",
|
|
146
594
|
transformData: {
|
|
147
595
|
type: "resize",
|
|
148
|
-
changes: {
|
|
149
|
-
rect: position
|
|
150
|
-
},
|
|
596
|
+
changes: { rect: position },
|
|
151
597
|
metadata: {
|
|
152
598
|
handle: this.activeHandle,
|
|
153
599
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -161,16 +607,40 @@ class DragResizeController {
|
|
|
161
607
|
state: "move",
|
|
162
608
|
transformData: {
|
|
163
609
|
type: "vertex-edit",
|
|
164
|
-
changes: {
|
|
165
|
-
|
|
166
|
-
|
|
610
|
+
changes: { vertices },
|
|
611
|
+
metadata: { vertexIndex: this.activeVertexIndex }
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
} else if (this.state === "rotating" && this.rotationCenter) {
|
|
615
|
+
const absoluteAngle = this.calculateAngleFromMouse(clientX, clientY);
|
|
616
|
+
const snapResult = this.applyRotationSnapping(absoluteAngle);
|
|
617
|
+
const snappedAngle = normalizeAngle(snapResult.angle);
|
|
618
|
+
const previousAngle = this.lastComputedRotation;
|
|
619
|
+
const rawDelta = snappedAngle - previousAngle;
|
|
620
|
+
const adjustedDelta = rawDelta > 180 ? rawDelta - 360 : rawDelta < -180 ? rawDelta + 360 : rawDelta;
|
|
621
|
+
this.rotationDelta += adjustedDelta;
|
|
622
|
+
this.lastComputedRotation = snappedAngle;
|
|
623
|
+
this.rotationSnappedAngle = snapResult.isSnapped ? snappedAngle : null;
|
|
624
|
+
this.onUpdate({
|
|
625
|
+
state: "move",
|
|
626
|
+
transformData: {
|
|
627
|
+
type: "rotate",
|
|
628
|
+
changes: { rotation: snappedAngle },
|
|
167
629
|
metadata: {
|
|
168
|
-
|
|
630
|
+
rotationAngle: snappedAngle,
|
|
631
|
+
rotationDelta: this.rotationDelta,
|
|
632
|
+
rotationCenter: this.rotationCenter,
|
|
633
|
+
isSnapped: snapResult.isSnapped,
|
|
634
|
+
snappedAngle: this.rotationSnappedAngle ?? void 0,
|
|
635
|
+
cursorPosition: { clientX, clientY }
|
|
169
636
|
}
|
|
170
637
|
}
|
|
171
638
|
});
|
|
172
639
|
}
|
|
173
640
|
}
|
|
641
|
+
// ---------------------------------------------------------------------------
|
|
642
|
+
// Gesture end / cancel
|
|
643
|
+
// ---------------------------------------------------------------------------
|
|
174
644
|
end() {
|
|
175
645
|
if (this.state === "idle") return;
|
|
176
646
|
const wasState = this.state;
|
|
@@ -181,23 +651,32 @@ class DragResizeController {
|
|
|
181
651
|
state: "end",
|
|
182
652
|
transformData: {
|
|
183
653
|
type: "vertex-edit",
|
|
184
|
-
changes: {
|
|
185
|
-
|
|
186
|
-
|
|
654
|
+
changes: { vertices: this.currentVertices },
|
|
655
|
+
metadata: { vertexIndex: vertexIndex || void 0 }
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
} else if (wasState === "rotating") {
|
|
659
|
+
this.onUpdate({
|
|
660
|
+
state: "end",
|
|
661
|
+
transformData: {
|
|
662
|
+
type: "rotate",
|
|
663
|
+
changes: { rotation: this.lastComputedRotation },
|
|
187
664
|
metadata: {
|
|
188
|
-
|
|
665
|
+
rotationAngle: this.lastComputedRotation,
|
|
666
|
+
rotationDelta: this.rotationDelta,
|
|
667
|
+
rotationCenter: this.rotationCenter || void 0,
|
|
668
|
+
isSnapped: this.rotationSnappedAngle !== null,
|
|
669
|
+
snappedAngle: this.rotationSnappedAngle ?? void 0
|
|
189
670
|
}
|
|
190
671
|
}
|
|
191
672
|
});
|
|
192
673
|
} else {
|
|
193
|
-
const finalPosition = this.
|
|
674
|
+
const finalPosition = this.currentPosition || this.config.element;
|
|
194
675
|
this.onUpdate({
|
|
195
676
|
state: "end",
|
|
196
677
|
transformData: {
|
|
197
678
|
type: wasState === "dragging" ? "move" : "resize",
|
|
198
|
-
changes: {
|
|
199
|
-
rect: finalPosition
|
|
200
|
-
},
|
|
679
|
+
changes: { rect: finalPosition },
|
|
201
680
|
metadata: wasState === "dragging" ? void 0 : {
|
|
202
681
|
handle: handle || void 0,
|
|
203
682
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -214,11 +693,21 @@ class DragResizeController {
|
|
|
214
693
|
state: "end",
|
|
215
694
|
transformData: {
|
|
216
695
|
type: "vertex-edit",
|
|
217
|
-
changes: {
|
|
218
|
-
|
|
219
|
-
|
|
696
|
+
changes: { vertices: this.startVertices },
|
|
697
|
+
metadata: { vertexIndex: this.activeVertexIndex || void 0 }
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
} else if (this.state === "rotating") {
|
|
701
|
+
this.onUpdate({
|
|
702
|
+
state: "end",
|
|
703
|
+
transformData: {
|
|
704
|
+
type: "rotate",
|
|
705
|
+
changes: { rotation: this.initialRotation },
|
|
220
706
|
metadata: {
|
|
221
|
-
|
|
707
|
+
rotationAngle: this.initialRotation,
|
|
708
|
+
rotationDelta: 0,
|
|
709
|
+
rotationCenter: this.rotationCenter || void 0,
|
|
710
|
+
isSnapped: false
|
|
222
711
|
}
|
|
223
712
|
}
|
|
224
713
|
});
|
|
@@ -227,9 +716,7 @@ class DragResizeController {
|
|
|
227
716
|
state: "end",
|
|
228
717
|
transformData: {
|
|
229
718
|
type: this.state === "dragging" ? "move" : "resize",
|
|
230
|
-
changes: {
|
|
231
|
-
rect: this.startElement
|
|
232
|
-
},
|
|
719
|
+
changes: { rect: this.startElement },
|
|
233
720
|
metadata: this.state === "dragging" ? void 0 : {
|
|
234
721
|
handle: this.activeHandle || void 0,
|
|
235
722
|
maintainAspectRatio: this.config.maintainAspectRatio
|
|
@@ -239,18 +726,29 @@ class DragResizeController {
|
|
|
239
726
|
}
|
|
240
727
|
this.reset();
|
|
241
728
|
}
|
|
729
|
+
// ---------------------------------------------------------------------------
|
|
730
|
+
// Private: state management
|
|
731
|
+
// ---------------------------------------------------------------------------
|
|
242
732
|
reset() {
|
|
243
733
|
this.state = "idle";
|
|
244
734
|
this.startPoint = null;
|
|
245
735
|
this.startElement = null;
|
|
736
|
+
this.startRotationElement = null;
|
|
737
|
+
this.gestureRotationCenter = null;
|
|
246
738
|
this.activeHandle = null;
|
|
247
739
|
this.currentPosition = null;
|
|
248
740
|
this.activeVertexIndex = null;
|
|
249
741
|
this.startVertices = [];
|
|
742
|
+
this.rotationCenter = null;
|
|
743
|
+
this.centerScreen = null;
|
|
744
|
+
this.initialRotation = 0;
|
|
745
|
+
this.lastComputedRotation = 0;
|
|
746
|
+
this.rotationDelta = 0;
|
|
747
|
+
this.rotationSnappedAngle = null;
|
|
250
748
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
749
|
+
// ---------------------------------------------------------------------------
|
|
750
|
+
// Private: coordinate transformation (screen → page → local)
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
254
752
|
calculateDelta(clientX, clientY) {
|
|
255
753
|
if (!this.startPoint) return { x: 0, y: 0 };
|
|
256
754
|
const rawDelta = {
|
|
@@ -271,18 +769,50 @@ class DragResizeController {
|
|
|
271
769
|
y: -sin * scaledX + cos * scaledY
|
|
272
770
|
};
|
|
273
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Calculate delta projected into the annotation's local (unrotated) coordinate space.
|
|
774
|
+
* Used for resize and vertex-edit where mouse movement must be mapped to the
|
|
775
|
+
* annotation's own axes, accounting for its rotation.
|
|
776
|
+
*/
|
|
777
|
+
calculateLocalDelta(clientX, clientY) {
|
|
778
|
+
const pageDelta = this.calculateDelta(clientX, clientY);
|
|
779
|
+
const { annotationRotation = 0 } = this.config;
|
|
780
|
+
if (annotationRotation === 0) return pageDelta;
|
|
781
|
+
const rad = annotationRotation * Math.PI / 180;
|
|
782
|
+
const cos = Math.cos(rad);
|
|
783
|
+
const sin = Math.sin(rad);
|
|
784
|
+
return {
|
|
785
|
+
x: cos * pageDelta.x + sin * pageDelta.y,
|
|
786
|
+
y: -sin * pageDelta.x + cos * pageDelta.y
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
// ---------------------------------------------------------------------------
|
|
790
|
+
// Private: vertex clamping
|
|
791
|
+
// ---------------------------------------------------------------------------
|
|
274
792
|
clampPoint(p) {
|
|
275
793
|
var _a;
|
|
276
794
|
const bbox = (_a = this.config.constraints) == null ? void 0 : _a.boundingBox;
|
|
277
795
|
if (!bbox) return p;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
796
|
+
const { annotationRotation = 0 } = this.config;
|
|
797
|
+
if (annotationRotation === 0) {
|
|
798
|
+
return {
|
|
799
|
+
x: Math.max(0, Math.min(p.x, bbox.width)),
|
|
800
|
+
y: Math.max(0, Math.min(p.y, bbox.height))
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
const center = this.gestureRotationCenter ?? this.config.rotationCenter ?? {
|
|
804
|
+
x: this.config.element.origin.x + this.config.element.size.width / 2,
|
|
805
|
+
y: this.config.element.origin.y + this.config.element.size.height / 2
|
|
281
806
|
};
|
|
807
|
+
const visual = rotatePointAround(p, center, annotationRotation);
|
|
808
|
+
const clampedX = Math.max(0, Math.min(visual.x, bbox.width));
|
|
809
|
+
const clampedY = Math.max(0, Math.min(visual.y, bbox.height));
|
|
810
|
+
if (clampedX === visual.x && clampedY === visual.y) return p;
|
|
811
|
+
return rotatePointAround({ x: clampedX, y: clampedY }, center, -annotationRotation);
|
|
282
812
|
}
|
|
283
813
|
calculateVertexPosition(clientX, clientY) {
|
|
284
814
|
if (this.activeVertexIndex === null) return this.startVertices;
|
|
285
|
-
const delta = this.
|
|
815
|
+
const delta = this.calculateLocalDelta(clientX, clientY);
|
|
286
816
|
const newVertices = [...this.startVertices];
|
|
287
817
|
const currentVertex = newVertices[this.activeVertexIndex];
|
|
288
818
|
const moved = {
|
|
@@ -292,6 +822,9 @@ class DragResizeController {
|
|
|
292
822
|
newVertices[this.activeVertexIndex] = this.clampPoint(moved);
|
|
293
823
|
return newVertices;
|
|
294
824
|
}
|
|
825
|
+
// ---------------------------------------------------------------------------
|
|
826
|
+
// Private: drag position
|
|
827
|
+
// ---------------------------------------------------------------------------
|
|
295
828
|
calculateDragPosition(delta) {
|
|
296
829
|
if (!this.startElement) return this.config.element;
|
|
297
830
|
const position = {
|
|
@@ -304,253 +837,71 @@ class DragResizeController {
|
|
|
304
837
|
height: this.startElement.size.height
|
|
305
838
|
}
|
|
306
839
|
};
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
rect = this.enforceAspectRatio(rect, anchor, aspectRatio);
|
|
320
|
-
}
|
|
321
|
-
rect = this.clampToBounds(rect, anchor, aspectRatio);
|
|
322
|
-
return this.applyConstraints(rect);
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Apply the mouse delta to produce a raw (unconstrained) resized rect.
|
|
326
|
-
*/
|
|
327
|
-
applyResizeDelta(delta, anchor) {
|
|
328
|
-
const start = this.startElement;
|
|
329
|
-
let x = start.origin.x;
|
|
330
|
-
let y = start.origin.y;
|
|
331
|
-
let width = start.size.width;
|
|
332
|
-
let height = start.size.height;
|
|
333
|
-
if (anchor.x === "left") {
|
|
334
|
-
width += delta.x;
|
|
335
|
-
} else if (anchor.x === "right") {
|
|
336
|
-
x += delta.x;
|
|
337
|
-
width -= delta.x;
|
|
338
|
-
}
|
|
339
|
-
if (anchor.y === "top") {
|
|
340
|
-
height += delta.y;
|
|
341
|
-
} else if (anchor.y === "bottom") {
|
|
342
|
-
y += delta.y;
|
|
343
|
-
height -= delta.y;
|
|
344
|
-
}
|
|
345
|
-
return { origin: { x, y }, size: { width, height } };
|
|
346
|
-
}
|
|
347
|
-
/**
|
|
348
|
-
* Enforce aspect ratio while respecting the anchor.
|
|
349
|
-
* For edge handles (center anchor on one axis), the rect expands symmetrically on that axis.
|
|
350
|
-
* For corner handles, the anchor corner stays fixed.
|
|
351
|
-
*/
|
|
352
|
-
enforceAspectRatio(rect, anchor, aspectRatio) {
|
|
353
|
-
const start = this.startElement;
|
|
354
|
-
let { x, y } = rect.origin;
|
|
355
|
-
let { width, height } = rect.size;
|
|
356
|
-
const isEdgeHandle = anchor.x === "center" || anchor.y === "center";
|
|
357
|
-
if (isEdgeHandle) {
|
|
358
|
-
if (anchor.y === "center") {
|
|
359
|
-
height = width / aspectRatio;
|
|
360
|
-
y = start.origin.y + (start.size.height - height) / 2;
|
|
361
|
-
} else {
|
|
362
|
-
width = height * aspectRatio;
|
|
363
|
-
x = start.origin.x + (start.size.width - width) / 2;
|
|
364
|
-
}
|
|
365
|
-
} else {
|
|
366
|
-
const dw = Math.abs(width - start.size.width);
|
|
367
|
-
const dh = Math.abs(height - start.size.height);
|
|
368
|
-
if (dw >= dh) {
|
|
369
|
-
height = width / aspectRatio;
|
|
840
|
+
const { annotationRotation = 0, constraints } = this.config;
|
|
841
|
+
const bbox = constraints == null ? void 0 : constraints.boundingBox;
|
|
842
|
+
if (annotationRotation !== 0 && bbox) {
|
|
843
|
+
let aabbW;
|
|
844
|
+
let aabbH;
|
|
845
|
+
let offsetX;
|
|
846
|
+
let offsetY;
|
|
847
|
+
if (this.startRotationElement) {
|
|
848
|
+
aabbW = this.startRotationElement.size.width;
|
|
849
|
+
aabbH = this.startRotationElement.size.height;
|
|
850
|
+
offsetX = this.startRotationElement.origin.x - this.startElement.origin.x;
|
|
851
|
+
offsetY = this.startRotationElement.origin.y - this.startElement.origin.y;
|
|
370
852
|
} else {
|
|
371
|
-
|
|
853
|
+
const rad = Math.abs(annotationRotation * Math.PI / 180);
|
|
854
|
+
const cos = Math.abs(Math.cos(rad));
|
|
855
|
+
const sin = Math.abs(Math.sin(rad));
|
|
856
|
+
const w = position.size.width;
|
|
857
|
+
const h = position.size.height;
|
|
858
|
+
aabbW = w * cos + h * sin;
|
|
859
|
+
aabbH = w * sin + h * cos;
|
|
860
|
+
offsetX = (w - aabbW) / 2;
|
|
861
|
+
offsetY = (h - aabbH) / 2;
|
|
372
862
|
}
|
|
863
|
+
let { x, y } = position.origin;
|
|
864
|
+
x = Math.max(-offsetX, Math.min(x, bbox.width - aabbW - offsetX));
|
|
865
|
+
y = Math.max(-offsetY, Math.min(y, bbox.height - aabbH - offsetY));
|
|
866
|
+
return { origin: { x, y }, size: position.size };
|
|
373
867
|
}
|
|
374
|
-
|
|
375
|
-
x = start.origin.x + start.size.width - width;
|
|
376
|
-
}
|
|
377
|
-
if (anchor.y === "bottom") {
|
|
378
|
-
y = start.origin.y + start.size.height - height;
|
|
379
|
-
}
|
|
380
|
-
return { origin: { x, y }, size: { width, height } };
|
|
868
|
+
return applyConstraints(position, constraints, this.config.maintainAspectRatio ?? false);
|
|
381
869
|
}
|
|
870
|
+
// ---------------------------------------------------------------------------
|
|
871
|
+
// Private: rotation
|
|
872
|
+
// ---------------------------------------------------------------------------
|
|
382
873
|
/**
|
|
383
|
-
*
|
|
874
|
+
* Calculate the angle from the center to a point in screen coordinates.
|
|
384
875
|
*/
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
if (anchor.x === "left") {
|
|
411
|
-
x = anchorX;
|
|
412
|
-
} else if (anchor.x === "right") {
|
|
413
|
-
x = anchorX - width;
|
|
414
|
-
} else {
|
|
415
|
-
x = start.origin.x + (start.size.width - width) / 2;
|
|
416
|
-
}
|
|
417
|
-
if (anchor.y === "top") {
|
|
418
|
-
y = anchorY;
|
|
419
|
-
} else if (anchor.y === "bottom") {
|
|
420
|
-
y = anchorY - height;
|
|
421
|
-
} else {
|
|
422
|
-
y = start.origin.y + (start.size.height - height) / 2;
|
|
423
|
-
}
|
|
424
|
-
x = Math.max(0, Math.min(x, bbox.width - width));
|
|
425
|
-
y = Math.max(0, Math.min(y, bbox.height - height));
|
|
426
|
-
return { origin: { x, y }, size: { width, height } };
|
|
427
|
-
}
|
|
428
|
-
applyConstraints(position) {
|
|
429
|
-
const { constraints } = this.config;
|
|
430
|
-
if (!constraints) return position;
|
|
431
|
-
let {
|
|
432
|
-
origin: { x, y },
|
|
433
|
-
size: { width, height }
|
|
434
|
-
} = position;
|
|
435
|
-
const minW = constraints.minWidth ?? 1;
|
|
436
|
-
const minH = constraints.minHeight ?? 1;
|
|
437
|
-
const maxW = constraints.maxWidth;
|
|
438
|
-
const maxH = constraints.maxHeight;
|
|
439
|
-
if (this.config.maintainAspectRatio && width > 0 && height > 0) {
|
|
440
|
-
const ratio = width / height;
|
|
441
|
-
if (width < minW) {
|
|
442
|
-
width = minW;
|
|
443
|
-
height = width / ratio;
|
|
444
|
-
}
|
|
445
|
-
if (height < minH) {
|
|
446
|
-
height = minH;
|
|
447
|
-
width = height * ratio;
|
|
448
|
-
}
|
|
449
|
-
if (maxW !== void 0 && width > maxW) {
|
|
450
|
-
width = maxW;
|
|
451
|
-
height = width / ratio;
|
|
452
|
-
}
|
|
453
|
-
if (maxH !== void 0 && height > maxH) {
|
|
454
|
-
height = maxH;
|
|
455
|
-
width = height * ratio;
|
|
876
|
+
calculateAngleFromMouse(clientX, clientY) {
|
|
877
|
+
if (!this.centerScreen) return this.initialRotation;
|
|
878
|
+
const dx = clientX - this.centerScreen.x;
|
|
879
|
+
const dy = clientY - this.centerScreen.y;
|
|
880
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
881
|
+
if (dist < 10) return this.lastComputedRotation;
|
|
882
|
+
const pageRotOffset = (this.config.pageRotation ?? 0) * 90;
|
|
883
|
+
const angleDeg = Math.atan2(dy, dx) * (180 / Math.PI) + 90 - pageRotOffset;
|
|
884
|
+
return normalizeAngle(Math.round(angleDeg));
|
|
885
|
+
}
|
|
886
|
+
applyRotationSnapping(angle) {
|
|
887
|
+
const snapAngles = this.config.rotationSnapAngles ?? [0, 90, 180, 270];
|
|
888
|
+
const threshold = this.config.rotationSnapThreshold ?? 4;
|
|
889
|
+
const normalizedAngle = normalizeAngle(angle);
|
|
890
|
+
for (const candidate of snapAngles) {
|
|
891
|
+
const normalizedCandidate = normalizeAngle(candidate);
|
|
892
|
+
const diff = Math.abs(normalizedAngle - normalizedCandidate);
|
|
893
|
+
const minimalDiff = Math.min(diff, 360 - diff);
|
|
894
|
+
if (minimalDiff <= threshold) {
|
|
895
|
+
return {
|
|
896
|
+
angle: normalizedCandidate,
|
|
897
|
+
isSnapped: true,
|
|
898
|
+
snapTarget: normalizedCandidate
|
|
899
|
+
};
|
|
456
900
|
}
|
|
457
|
-
} else {
|
|
458
|
-
width = Math.max(minW, width);
|
|
459
|
-
height = Math.max(minH, height);
|
|
460
|
-
if (maxW !== void 0) width = Math.min(maxW, width);
|
|
461
|
-
if (maxH !== void 0) height = Math.min(maxH, height);
|
|
462
901
|
}
|
|
463
|
-
|
|
464
|
-
x = Math.max(0, Math.min(x, constraints.boundingBox.width - width));
|
|
465
|
-
y = Math.max(0, Math.min(y, constraints.boundingBox.height - height));
|
|
466
|
-
}
|
|
467
|
-
return { origin: { x, y }, size: { width, height } };
|
|
902
|
+
return { angle: normalizedAngle, isSnapped: false };
|
|
468
903
|
}
|
|
469
904
|
}
|
|
470
|
-
function diagonalCursor(handle, rot) {
|
|
471
|
-
const diag0 = {
|
|
472
|
-
nw: "nwse-resize",
|
|
473
|
-
ne: "nesw-resize",
|
|
474
|
-
sw: "nesw-resize",
|
|
475
|
-
se: "nwse-resize"
|
|
476
|
-
};
|
|
477
|
-
if (handle === "n" || handle === "s") return "ns-resize";
|
|
478
|
-
if (handle === "e" || handle === "w") return "ew-resize";
|
|
479
|
-
if (rot % 2 === 0) return diag0[handle];
|
|
480
|
-
return { nw: "nesw-resize", ne: "nwse-resize", sw: "nwse-resize", se: "nesw-resize" }[handle];
|
|
481
|
-
}
|
|
482
|
-
function edgeOffset(k, spacing, mode) {
|
|
483
|
-
const base = -k / 2;
|
|
484
|
-
if (mode === "center") return base;
|
|
485
|
-
return mode === "outside" ? base - spacing : base + spacing;
|
|
486
|
-
}
|
|
487
|
-
function describeResizeFromConfig(cfg, ui = {}) {
|
|
488
|
-
const {
|
|
489
|
-
handleSize = 8,
|
|
490
|
-
spacing = 1,
|
|
491
|
-
offsetMode = "outside",
|
|
492
|
-
includeSides = false,
|
|
493
|
-
zIndex = 3,
|
|
494
|
-
rotationAwareCursor = true
|
|
495
|
-
} = ui;
|
|
496
|
-
const rotation = (cfg.pageRotation ?? 0) % 4;
|
|
497
|
-
const off = (edge) => ({
|
|
498
|
-
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
499
|
-
});
|
|
500
|
-
const corners = [
|
|
501
|
-
["nw", { ...off("top"), ...off("left") }],
|
|
502
|
-
["ne", { ...off("top"), ...off("right") }],
|
|
503
|
-
["sw", { ...off("bottom"), ...off("left") }],
|
|
504
|
-
["se", { ...off("bottom"), ...off("right") }]
|
|
505
|
-
];
|
|
506
|
-
const sides = includeSides ? [
|
|
507
|
-
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
508
|
-
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
509
|
-
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
510
|
-
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
511
|
-
] : [];
|
|
512
|
-
const all = [...corners, ...sides];
|
|
513
|
-
return all.map(([handle, pos]) => ({
|
|
514
|
-
handle,
|
|
515
|
-
style: {
|
|
516
|
-
position: "absolute",
|
|
517
|
-
width: handleSize + "px",
|
|
518
|
-
height: handleSize + "px",
|
|
519
|
-
borderRadius: "50%",
|
|
520
|
-
zIndex,
|
|
521
|
-
cursor: rotationAwareCursor ? diagonalCursor(handle, rotation) : "default",
|
|
522
|
-
touchAction: "none",
|
|
523
|
-
...pos
|
|
524
|
-
},
|
|
525
|
-
attrs: { "data-epdf-handle": handle }
|
|
526
|
-
}));
|
|
527
|
-
}
|
|
528
|
-
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
529
|
-
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
530
|
-
const rect = cfg.element;
|
|
531
|
-
const scale = cfg.scale ?? 1;
|
|
532
|
-
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
533
|
-
return verts.map((v, i) => {
|
|
534
|
-
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
535
|
-
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
536
|
-
return {
|
|
537
|
-
handle: "nw",
|
|
538
|
-
// not used; kept for type
|
|
539
|
-
style: {
|
|
540
|
-
position: "absolute",
|
|
541
|
-
left: left + "px",
|
|
542
|
-
top: top + "px",
|
|
543
|
-
width: vertexSize + "px",
|
|
544
|
-
height: vertexSize + "px",
|
|
545
|
-
borderRadius: "50%",
|
|
546
|
-
cursor: "pointer",
|
|
547
|
-
zIndex,
|
|
548
|
-
touchAction: "none"
|
|
549
|
-
},
|
|
550
|
-
attrs: { "data-epdf-vertex": i }
|
|
551
|
-
};
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
905
|
const norm = (v) => toRaw(isRef(v) ? unref(v) : v);
|
|
555
906
|
const toNum = (n, fallback = 0) => {
|
|
556
907
|
const v = Number(n);
|
|
@@ -572,19 +923,27 @@ function useDragResize(options) {
|
|
|
572
923
|
const {
|
|
573
924
|
onUpdate,
|
|
574
925
|
element,
|
|
926
|
+
rotationCenter,
|
|
927
|
+
rotationElement,
|
|
575
928
|
vertices,
|
|
576
929
|
constraints,
|
|
577
930
|
maintainAspectRatio,
|
|
578
931
|
pageRotation,
|
|
932
|
+
annotationRotation,
|
|
579
933
|
scale,
|
|
580
934
|
enabled
|
|
581
935
|
} = options;
|
|
582
936
|
const initialCfg = {
|
|
583
937
|
element: rectDTO(norm(element)),
|
|
938
|
+
rotationCenter: rotationCenter ? norm(rotationCenter) : void 0,
|
|
939
|
+
rotationElement: rotationElement ? rectDTO(norm(rotationElement)) : void 0,
|
|
584
940
|
vertices: vertices ? vertsDTO(norm(vertices)) : void 0,
|
|
585
941
|
constraints: constraintsDTO(constraints),
|
|
586
942
|
maintainAspectRatio: boolDTO(enabled === void 0 ? void 0 : norm(maintainAspectRatio)),
|
|
587
943
|
pageRotation: numDTO(pageRotation === void 0 ? void 0 : norm(pageRotation)),
|
|
944
|
+
annotationRotation: numDTO(
|
|
945
|
+
annotationRotation === void 0 ? void 0 : norm(annotationRotation)
|
|
946
|
+
),
|
|
588
947
|
scale: numDTO(scale === void 0 ? void 0 : norm(scale))
|
|
589
948
|
};
|
|
590
949
|
if (!controller.value) {
|
|
@@ -593,22 +952,30 @@ function useDragResize(options) {
|
|
|
593
952
|
watch(
|
|
594
953
|
() => ({
|
|
595
954
|
element,
|
|
955
|
+
rotationCenter,
|
|
956
|
+
rotationElement,
|
|
596
957
|
vertices,
|
|
597
958
|
constraints,
|
|
598
959
|
maintainAspectRatio,
|
|
599
960
|
pageRotation,
|
|
961
|
+
annotationRotation,
|
|
600
962
|
scale
|
|
601
963
|
}),
|
|
602
964
|
(nc) => {
|
|
603
965
|
var _a;
|
|
604
966
|
(_a = controller.value) == null ? void 0 : _a.updateConfig({
|
|
605
967
|
element: rectDTO(norm(nc.element)),
|
|
968
|
+
rotationCenter: nc.rotationCenter ? norm(nc.rotationCenter) : void 0,
|
|
969
|
+
rotationElement: nc.rotationElement ? rectDTO(norm(nc.rotationElement)) : void 0,
|
|
606
970
|
vertices: nc.vertices ? vertsDTO(norm(nc.vertices)) : void 0,
|
|
607
971
|
constraints: constraintsDTO(nc.constraints),
|
|
608
972
|
maintainAspectRatio: boolDTO(
|
|
609
973
|
nc.maintainAspectRatio === void 0 ? void 0 : norm(nc.maintainAspectRatio)
|
|
610
974
|
),
|
|
611
975
|
pageRotation: numDTO(nc.pageRotation === void 0 ? void 0 : norm(nc.pageRotation)),
|
|
976
|
+
annotationRotation: numDTO(
|
|
977
|
+
nc.annotationRotation === void 0 ? void 0 : norm(nc.annotationRotation)
|
|
978
|
+
),
|
|
612
979
|
scale: numDTO(nc.scale === void 0 ? void 0 : norm(nc.scale))
|
|
613
980
|
});
|
|
614
981
|
},
|
|
@@ -628,7 +995,7 @@ function useDragResize(options) {
|
|
|
628
995
|
};
|
|
629
996
|
const handleMove = (e) => {
|
|
630
997
|
var _a;
|
|
631
|
-
return (_a = controller.value) == null ? void 0 : _a.move(e.clientX, e.clientY);
|
|
998
|
+
return (_a = controller.value) == null ? void 0 : _a.move(e.clientX, e.clientY, e.buttons);
|
|
632
999
|
};
|
|
633
1000
|
const handleEnd = (e) => {
|
|
634
1001
|
var _a, _b, _c;
|
|
@@ -666,6 +1033,22 @@ function useDragResize(options) {
|
|
|
666
1033
|
onPointerup: handleEnd,
|
|
667
1034
|
onPointercancel: handleCancel
|
|
668
1035
|
});
|
|
1036
|
+
const createRotationProps = (initialRotation = 0, orbitRadiusPx) => ({
|
|
1037
|
+
onPointerdown: (e) => {
|
|
1038
|
+
var _a, _b, _c;
|
|
1039
|
+
if (!isEnabled()) return;
|
|
1040
|
+
e.preventDefault();
|
|
1041
|
+
e.stopPropagation();
|
|
1042
|
+
const handleRect = e.currentTarget.getBoundingClientRect();
|
|
1043
|
+
const handleCenterX = handleRect.left + handleRect.width / 2;
|
|
1044
|
+
const handleCenterY = handleRect.top + handleRect.height / 2;
|
|
1045
|
+
(_a = controller.value) == null ? void 0 : _a.startRotation(handleCenterX, handleCenterY, initialRotation, orbitRadiusPx);
|
|
1046
|
+
(_c = (_b = e.currentTarget).setPointerCapture) == null ? void 0 : _c.call(_b, e.pointerId);
|
|
1047
|
+
},
|
|
1048
|
+
onPointermove: handleMove,
|
|
1049
|
+
onPointerup: handleEnd,
|
|
1050
|
+
onPointercancel: handleCancel
|
|
1051
|
+
});
|
|
669
1052
|
const dragProps = computed(
|
|
670
1053
|
() => isEnabled() ? {
|
|
671
1054
|
onPointerdown: handleDragStart,
|
|
@@ -674,7 +1057,7 @@ function useDragResize(options) {
|
|
|
674
1057
|
onPointercancel: handleCancel
|
|
675
1058
|
} : {}
|
|
676
1059
|
);
|
|
677
|
-
return { dragProps, createResizeProps, createVertexProps };
|
|
1060
|
+
return { dragProps, createResizeProps, createVertexProps, createRotationProps };
|
|
678
1061
|
}
|
|
679
1062
|
function useDoublePressProps(onDouble, { delay = 300, tolerancePx = 18 } = {}) {
|
|
680
1063
|
const last = ref({ t: 0, x: 0, y: 0 });
|
|
@@ -707,27 +1090,41 @@ function useInteractionHandles(opts) {
|
|
|
707
1090
|
controller,
|
|
708
1091
|
resizeUI,
|
|
709
1092
|
vertexUI,
|
|
1093
|
+
rotationUI,
|
|
710
1094
|
includeVertices = false,
|
|
1095
|
+
includeRotation = false,
|
|
1096
|
+
currentRotation = 0,
|
|
711
1097
|
handleAttrs,
|
|
712
|
-
vertexAttrs
|
|
1098
|
+
vertexAttrs,
|
|
1099
|
+
rotationAttrs
|
|
713
1100
|
} = opts;
|
|
714
|
-
const { dragProps, createResizeProps, createVertexProps } = useDragResize(controller);
|
|
1101
|
+
const { dragProps, createResizeProps, createVertexProps, createRotationProps } = useDragResize(controller);
|
|
715
1102
|
const elementPlain = computed(() => rectDTO(norm(controller.element)));
|
|
716
1103
|
const verticesPlain = computed(
|
|
717
1104
|
() => controller.vertices ? vertsDTO(norm(controller.vertices)) : void 0
|
|
718
1105
|
);
|
|
719
1106
|
const scalePlain = computed(() => Number(norm(controller.scale ?? 1)));
|
|
720
1107
|
const rotationPlain = computed(() => Number(norm(controller.pageRotation ?? 0)));
|
|
1108
|
+
const annotationRotationPlain = computed(
|
|
1109
|
+
() => controller.annotationRotation !== void 0 ? Number(norm(controller.annotationRotation)) : void 0
|
|
1110
|
+
);
|
|
721
1111
|
const maintainPlain = computed(
|
|
722
1112
|
() => controller.maintainAspectRatio === void 0 ? void 0 : Boolean(norm(controller.maintainAspectRatio))
|
|
723
1113
|
);
|
|
724
1114
|
const constraintsPlain = computed(() => norm(controller.constraints ?? void 0));
|
|
1115
|
+
const rotationCenterPlain = computed(
|
|
1116
|
+
() => controller.rotationCenter ? norm(controller.rotationCenter) : void 0
|
|
1117
|
+
);
|
|
1118
|
+
const rotationElementPlain = computed(
|
|
1119
|
+
() => controller.rotationElement ? rectDTO(norm(controller.rotationElement)) : void 0
|
|
1120
|
+
);
|
|
725
1121
|
const resize = computed(() => {
|
|
726
1122
|
const desc = describeResizeFromConfig(
|
|
727
1123
|
{
|
|
728
1124
|
element: elementPlain.value,
|
|
729
1125
|
scale: scalePlain.value,
|
|
730
1126
|
pageRotation: rotationPlain.value,
|
|
1127
|
+
annotationRotation: annotationRotationPlain.value,
|
|
731
1128
|
maintainAspectRatio: maintainPlain.value,
|
|
732
1129
|
constraints: constraintsPlain.value
|
|
733
1130
|
},
|
|
@@ -760,7 +1157,34 @@ function useInteractionHandles(opts) {
|
|
|
760
1157
|
...(vertexAttrs == null ? void 0 : vertexAttrs(i)) ?? {}
|
|
761
1158
|
}));
|
|
762
1159
|
});
|
|
763
|
-
|
|
1160
|
+
const rotation = computed(() => {
|
|
1161
|
+
const showRotation = Boolean(norm(includeRotation ?? false));
|
|
1162
|
+
if (!showRotation) return null;
|
|
1163
|
+
const rot = Number(norm(currentRotation ?? 0));
|
|
1164
|
+
const desc = describeRotationFromConfig(
|
|
1165
|
+
{
|
|
1166
|
+
element: elementPlain.value,
|
|
1167
|
+
rotationCenter: rotationCenterPlain.value,
|
|
1168
|
+
rotationElement: rotationElementPlain.value,
|
|
1169
|
+
scale: scalePlain.value
|
|
1170
|
+
},
|
|
1171
|
+
rotationUI,
|
|
1172
|
+
rot
|
|
1173
|
+
);
|
|
1174
|
+
return {
|
|
1175
|
+
handle: {
|
|
1176
|
+
style: desc.handleStyle,
|
|
1177
|
+
...createRotationProps(rot, desc.radius),
|
|
1178
|
+
...desc.attrs ?? {},
|
|
1179
|
+
...(rotationAttrs == null ? void 0 : rotationAttrs()) ?? {}
|
|
1180
|
+
},
|
|
1181
|
+
connector: {
|
|
1182
|
+
style: desc.connectorStyle,
|
|
1183
|
+
"data-epdf-rotation-connector": true
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
});
|
|
1187
|
+
return { dragProps, resize, vertices, rotation };
|
|
764
1188
|
}
|
|
765
1189
|
function deepToRaw(sourceObj) {
|
|
766
1190
|
const objectIterator = (input) => {
|