@embedpdf/utils 2.5.0 → 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 -322
- 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 -322
- 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 -293
- 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 -297
- package/dist/vue/index.js.map +1 -1
- package/package.json +3 -3
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,258 +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 isOddRotation = rot % 2 === 1;
|
|
472
|
-
if (handle === "n" || handle === "s") {
|
|
473
|
-
return isOddRotation ? "ew-resize" : "ns-resize";
|
|
474
|
-
}
|
|
475
|
-
if (handle === "e" || handle === "w") {
|
|
476
|
-
return isOddRotation ? "ns-resize" : "ew-resize";
|
|
477
|
-
}
|
|
478
|
-
const diag0 = {
|
|
479
|
-
nw: "nwse-resize",
|
|
480
|
-
ne: "nesw-resize",
|
|
481
|
-
sw: "nesw-resize",
|
|
482
|
-
se: "nwse-resize"
|
|
483
|
-
};
|
|
484
|
-
if (!isOddRotation) return diag0[handle];
|
|
485
|
-
return { nw: "nesw-resize", ne: "nwse-resize", sw: "nwse-resize", se: "nesw-resize" }[handle];
|
|
486
|
-
}
|
|
487
|
-
function edgeOffset(k, spacing, mode) {
|
|
488
|
-
const base = -k / 2;
|
|
489
|
-
if (mode === "center") return base;
|
|
490
|
-
return mode === "outside" ? base - spacing : base + spacing;
|
|
491
|
-
}
|
|
492
|
-
function describeResizeFromConfig(cfg, ui = {}) {
|
|
493
|
-
const {
|
|
494
|
-
handleSize = 8,
|
|
495
|
-
spacing = 1,
|
|
496
|
-
offsetMode = "outside",
|
|
497
|
-
includeSides = false,
|
|
498
|
-
zIndex = 3,
|
|
499
|
-
rotationAwareCursor = true
|
|
500
|
-
} = ui;
|
|
501
|
-
const rotation = (cfg.pageRotation ?? 0) % 4;
|
|
502
|
-
const off = (edge) => ({
|
|
503
|
-
[edge]: edgeOffset(handleSize, spacing, offsetMode) + "px"
|
|
504
|
-
});
|
|
505
|
-
const corners = [
|
|
506
|
-
["nw", { ...off("top"), ...off("left") }],
|
|
507
|
-
["ne", { ...off("top"), ...off("right") }],
|
|
508
|
-
["sw", { ...off("bottom"), ...off("left") }],
|
|
509
|
-
["se", { ...off("bottom"), ...off("right") }]
|
|
510
|
-
];
|
|
511
|
-
const sides = includeSides ? [
|
|
512
|
-
["n", { ...off("top"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
513
|
-
["s", { ...off("bottom"), left: `calc(50% - ${handleSize / 2}px)` }],
|
|
514
|
-
["w", { ...off("left"), top: `calc(50% - ${handleSize / 2}px)` }],
|
|
515
|
-
["e", { ...off("right"), top: `calc(50% - ${handleSize / 2}px)` }]
|
|
516
|
-
] : [];
|
|
517
|
-
const all = [...corners, ...sides];
|
|
518
|
-
return all.map(([handle, pos]) => ({
|
|
519
|
-
handle,
|
|
520
|
-
style: {
|
|
521
|
-
position: "absolute",
|
|
522
|
-
width: handleSize + "px",
|
|
523
|
-
height: handleSize + "px",
|
|
524
|
-
borderRadius: "50%",
|
|
525
|
-
zIndex,
|
|
526
|
-
cursor: rotationAwareCursor ? diagonalCursor(handle, rotation) : "default",
|
|
527
|
-
touchAction: "none",
|
|
528
|
-
...pos
|
|
529
|
-
},
|
|
530
|
-
attrs: { "data-epdf-handle": handle }
|
|
531
|
-
}));
|
|
532
|
-
}
|
|
533
|
-
function describeVerticesFromConfig(cfg, ui = {}, liveVertices) {
|
|
534
|
-
const { vertexSize = 12, zIndex = 4 } = ui;
|
|
535
|
-
const rect = cfg.element;
|
|
536
|
-
const scale = cfg.scale ?? 1;
|
|
537
|
-
const verts = liveVertices ?? cfg.vertices ?? [];
|
|
538
|
-
return verts.map((v, i) => {
|
|
539
|
-
const left = (v.x - rect.origin.x) * scale - vertexSize / 2;
|
|
540
|
-
const top = (v.y - rect.origin.y) * scale - vertexSize / 2;
|
|
541
|
-
return {
|
|
542
|
-
handle: "nw",
|
|
543
|
-
// not used; kept for type
|
|
544
|
-
style: {
|
|
545
|
-
position: "absolute",
|
|
546
|
-
left: left + "px",
|
|
547
|
-
top: top + "px",
|
|
548
|
-
width: vertexSize + "px",
|
|
549
|
-
height: vertexSize + "px",
|
|
550
|
-
borderRadius: "50%",
|
|
551
|
-
cursor: "pointer",
|
|
552
|
-
zIndex,
|
|
553
|
-
touchAction: "none"
|
|
554
|
-
},
|
|
555
|
-
attrs: { "data-epdf-vertex": i }
|
|
556
|
-
};
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
905
|
const norm = (v) => toRaw(isRef(v) ? unref(v) : v);
|
|
560
906
|
const toNum = (n, fallback = 0) => {
|
|
561
907
|
const v = Number(n);
|
|
@@ -577,19 +923,27 @@ function useDragResize(options) {
|
|
|
577
923
|
const {
|
|
578
924
|
onUpdate,
|
|
579
925
|
element,
|
|
926
|
+
rotationCenter,
|
|
927
|
+
rotationElement,
|
|
580
928
|
vertices,
|
|
581
929
|
constraints,
|
|
582
930
|
maintainAspectRatio,
|
|
583
931
|
pageRotation,
|
|
932
|
+
annotationRotation,
|
|
584
933
|
scale,
|
|
585
934
|
enabled
|
|
586
935
|
} = options;
|
|
587
936
|
const initialCfg = {
|
|
588
937
|
element: rectDTO(norm(element)),
|
|
938
|
+
rotationCenter: rotationCenter ? norm(rotationCenter) : void 0,
|
|
939
|
+
rotationElement: rotationElement ? rectDTO(norm(rotationElement)) : void 0,
|
|
589
940
|
vertices: vertices ? vertsDTO(norm(vertices)) : void 0,
|
|
590
941
|
constraints: constraintsDTO(constraints),
|
|
591
942
|
maintainAspectRatio: boolDTO(enabled === void 0 ? void 0 : norm(maintainAspectRatio)),
|
|
592
943
|
pageRotation: numDTO(pageRotation === void 0 ? void 0 : norm(pageRotation)),
|
|
944
|
+
annotationRotation: numDTO(
|
|
945
|
+
annotationRotation === void 0 ? void 0 : norm(annotationRotation)
|
|
946
|
+
),
|
|
593
947
|
scale: numDTO(scale === void 0 ? void 0 : norm(scale))
|
|
594
948
|
};
|
|
595
949
|
if (!controller.value) {
|
|
@@ -598,22 +952,30 @@ function useDragResize(options) {
|
|
|
598
952
|
watch(
|
|
599
953
|
() => ({
|
|
600
954
|
element,
|
|
955
|
+
rotationCenter,
|
|
956
|
+
rotationElement,
|
|
601
957
|
vertices,
|
|
602
958
|
constraints,
|
|
603
959
|
maintainAspectRatio,
|
|
604
960
|
pageRotation,
|
|
961
|
+
annotationRotation,
|
|
605
962
|
scale
|
|
606
963
|
}),
|
|
607
964
|
(nc) => {
|
|
608
965
|
var _a;
|
|
609
966
|
(_a = controller.value) == null ? void 0 : _a.updateConfig({
|
|
610
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,
|
|
611
970
|
vertices: nc.vertices ? vertsDTO(norm(nc.vertices)) : void 0,
|
|
612
971
|
constraints: constraintsDTO(nc.constraints),
|
|
613
972
|
maintainAspectRatio: boolDTO(
|
|
614
973
|
nc.maintainAspectRatio === void 0 ? void 0 : norm(nc.maintainAspectRatio)
|
|
615
974
|
),
|
|
616
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
|
+
),
|
|
617
979
|
scale: numDTO(nc.scale === void 0 ? void 0 : norm(nc.scale))
|
|
618
980
|
});
|
|
619
981
|
},
|
|
@@ -633,7 +995,7 @@ function useDragResize(options) {
|
|
|
633
995
|
};
|
|
634
996
|
const handleMove = (e) => {
|
|
635
997
|
var _a;
|
|
636
|
-
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);
|
|
637
999
|
};
|
|
638
1000
|
const handleEnd = (e) => {
|
|
639
1001
|
var _a, _b, _c;
|
|
@@ -671,6 +1033,22 @@ function useDragResize(options) {
|
|
|
671
1033
|
onPointerup: handleEnd,
|
|
672
1034
|
onPointercancel: handleCancel
|
|
673
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
|
+
});
|
|
674
1052
|
const dragProps = computed(
|
|
675
1053
|
() => isEnabled() ? {
|
|
676
1054
|
onPointerdown: handleDragStart,
|
|
@@ -679,7 +1057,7 @@ function useDragResize(options) {
|
|
|
679
1057
|
onPointercancel: handleCancel
|
|
680
1058
|
} : {}
|
|
681
1059
|
);
|
|
682
|
-
return { dragProps, createResizeProps, createVertexProps };
|
|
1060
|
+
return { dragProps, createResizeProps, createVertexProps, createRotationProps };
|
|
683
1061
|
}
|
|
684
1062
|
function useDoublePressProps(onDouble, { delay = 300, tolerancePx = 18 } = {}) {
|
|
685
1063
|
const last = ref({ t: 0, x: 0, y: 0 });
|
|
@@ -712,27 +1090,41 @@ function useInteractionHandles(opts) {
|
|
|
712
1090
|
controller,
|
|
713
1091
|
resizeUI,
|
|
714
1092
|
vertexUI,
|
|
1093
|
+
rotationUI,
|
|
715
1094
|
includeVertices = false,
|
|
1095
|
+
includeRotation = false,
|
|
1096
|
+
currentRotation = 0,
|
|
716
1097
|
handleAttrs,
|
|
717
|
-
vertexAttrs
|
|
1098
|
+
vertexAttrs,
|
|
1099
|
+
rotationAttrs
|
|
718
1100
|
} = opts;
|
|
719
|
-
const { dragProps, createResizeProps, createVertexProps } = useDragResize(controller);
|
|
1101
|
+
const { dragProps, createResizeProps, createVertexProps, createRotationProps } = useDragResize(controller);
|
|
720
1102
|
const elementPlain = computed(() => rectDTO(norm(controller.element)));
|
|
721
1103
|
const verticesPlain = computed(
|
|
722
1104
|
() => controller.vertices ? vertsDTO(norm(controller.vertices)) : void 0
|
|
723
1105
|
);
|
|
724
1106
|
const scalePlain = computed(() => Number(norm(controller.scale ?? 1)));
|
|
725
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
|
+
);
|
|
726
1111
|
const maintainPlain = computed(
|
|
727
1112
|
() => controller.maintainAspectRatio === void 0 ? void 0 : Boolean(norm(controller.maintainAspectRatio))
|
|
728
1113
|
);
|
|
729
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
|
+
);
|
|
730
1121
|
const resize = computed(() => {
|
|
731
1122
|
const desc = describeResizeFromConfig(
|
|
732
1123
|
{
|
|
733
1124
|
element: elementPlain.value,
|
|
734
1125
|
scale: scalePlain.value,
|
|
735
1126
|
pageRotation: rotationPlain.value,
|
|
1127
|
+
annotationRotation: annotationRotationPlain.value,
|
|
736
1128
|
maintainAspectRatio: maintainPlain.value,
|
|
737
1129
|
constraints: constraintsPlain.value
|
|
738
1130
|
},
|
|
@@ -765,7 +1157,34 @@ function useInteractionHandles(opts) {
|
|
|
765
1157
|
...(vertexAttrs == null ? void 0 : vertexAttrs(i)) ?? {}
|
|
766
1158
|
}));
|
|
767
1159
|
});
|
|
768
|
-
|
|
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 };
|
|
769
1188
|
}
|
|
770
1189
|
function deepToRaw(sourceObj) {
|
|
771
1190
|
const objectIterator = (input) => {
|