@embedpdf/plugin-annotation 2.8.0 → 2.9.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +814 -139
- package/dist/index.js.map +1 -1
- package/dist/lib/geometry/cloudy-border.d.ts +90 -0
- package/dist/lib/geometry/index.d.ts +1 -0
- package/dist/lib/handlers/types.d.ts +2 -1
- package/dist/lib/tools/default-tools.d.ts +38 -87
- package/dist/lib/tools/types.d.ts +32 -1
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +352 -239
- 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 +352 -239
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/annotation-container.d.ts +4 -2
- package/dist/shared/components/annotations/circle.d.ts +6 -2
- package/dist/shared/components/annotations/polygon.d.ts +3 -1
- package/dist/shared/components/annotations/square.d.ts +6 -2
- package/dist/shared/components/types.d.ts +6 -2
- package/dist/shared-preact/components/annotation-container.d.ts +4 -2
- package/dist/shared-preact/components/annotations/circle.d.ts +6 -2
- package/dist/shared-preact/components/annotations/polygon.d.ts +3 -1
- package/dist/shared-preact/components/annotations/square.d.ts +6 -2
- package/dist/shared-preact/components/types.d.ts +6 -2
- package/dist/shared-react/components/annotation-container.d.ts +4 -2
- package/dist/shared-react/components/annotations/circle.d.ts +6 -2
- package/dist/shared-react/components/annotations/polygon.d.ts +3 -1
- package/dist/shared-react/components/annotations/square.d.ts +6 -2
- package/dist/shared-react/components/types.d.ts +6 -2
- package/dist/svelte/components/annotations/Circle.svelte.d.ts +3 -1
- package/dist/svelte/components/annotations/Polygon.svelte.d.ts +1 -0
- package/dist/svelte/components/annotations/Square.svelte.d.ts +3 -1
- package/dist/svelte/components/types.d.ts +2 -1
- package/dist/svelte/context/types.d.ts +6 -2
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +505 -293
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/components/annotation-container.vue.d.ts +7 -6
- package/dist/vue/components/annotations/circle.vue.d.ts +5 -1
- package/dist/vue/components/annotations/polygon.vue.d.ts +2 -0
- package/dist/vue/components/annotations/square.vue.d.ts +5 -1
- package/dist/vue/components/annotations.vue.d.ts +8 -9
- package/dist/vue/context/types.d.ts +6 -2
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +259 -121
- package/dist/vue/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -340,6 +340,62 @@ const getSelectionGroupingAction = (s) => {
|
|
|
340
340
|
}
|
|
341
341
|
return selected.length >= 2 ? "group" : "disabled";
|
|
342
342
|
};
|
|
343
|
+
const inkTools = [
|
|
344
|
+
{
|
|
345
|
+
id: "ink",
|
|
346
|
+
name: "Pen",
|
|
347
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
|
|
348
|
+
interaction: {
|
|
349
|
+
exclusive: false,
|
|
350
|
+
cursor: "crosshair",
|
|
351
|
+
isDraggable: true,
|
|
352
|
+
isResizable: true,
|
|
353
|
+
lockAspectRatio: false
|
|
354
|
+
},
|
|
355
|
+
defaults: {
|
|
356
|
+
type: PdfAnnotationSubtype.INK,
|
|
357
|
+
strokeColor: "#E44234",
|
|
358
|
+
color: "#E44234",
|
|
359
|
+
// deprecated alias
|
|
360
|
+
opacity: 1,
|
|
361
|
+
strokeWidth: 6
|
|
362
|
+
},
|
|
363
|
+
behavior: {
|
|
364
|
+
commitDelay: 800
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "inkHighlighter",
|
|
369
|
+
name: "Ink Highlighter",
|
|
370
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
|
|
371
|
+
interaction: {
|
|
372
|
+
exclusive: false,
|
|
373
|
+
cursor: "crosshair",
|
|
374
|
+
isDraggable: true,
|
|
375
|
+
isResizable: true,
|
|
376
|
+
lockAspectRatio: false,
|
|
377
|
+
lockGroupAspectRatio: (a) => {
|
|
378
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
379
|
+
return r2 >= 6 && r2 <= 84;
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
defaults: {
|
|
383
|
+
type: PdfAnnotationSubtype.INK,
|
|
384
|
+
intent: "InkHighlight",
|
|
385
|
+
strokeColor: "#FFCD45",
|
|
386
|
+
color: "#FFCD45",
|
|
387
|
+
// deprecated alias
|
|
388
|
+
opacity: 1,
|
|
389
|
+
strokeWidth: 14,
|
|
390
|
+
blendMode: PdfBlendMode.Multiply
|
|
391
|
+
},
|
|
392
|
+
behavior: {
|
|
393
|
+
commitDelay: 800,
|
|
394
|
+
smartLineRecognition: true,
|
|
395
|
+
smartLineThreshold: 0.15
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
];
|
|
343
399
|
const defaultTools = [
|
|
344
400
|
// Text Markup Tools
|
|
345
401
|
{
|
|
@@ -482,52 +538,7 @@ const defaultTools = [
|
|
|
482
538
|
}
|
|
483
539
|
},
|
|
484
540
|
// Drawing Tools
|
|
485
|
-
|
|
486
|
-
id: "ink",
|
|
487
|
-
name: "Pen",
|
|
488
|
-
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
|
|
489
|
-
interaction: {
|
|
490
|
-
exclusive: false,
|
|
491
|
-
cursor: "crosshair",
|
|
492
|
-
isDraggable: true,
|
|
493
|
-
isResizable: true,
|
|
494
|
-
lockAspectRatio: false
|
|
495
|
-
},
|
|
496
|
-
defaults: {
|
|
497
|
-
type: PdfAnnotationSubtype.INK,
|
|
498
|
-
strokeColor: "#E44234",
|
|
499
|
-
color: "#E44234",
|
|
500
|
-
// deprecated alias
|
|
501
|
-
opacity: 1,
|
|
502
|
-
strokeWidth: 6
|
|
503
|
-
}
|
|
504
|
-
},
|
|
505
|
-
{
|
|
506
|
-
id: "inkHighlighter",
|
|
507
|
-
name: "Ink Highlighter",
|
|
508
|
-
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
|
|
509
|
-
interaction: {
|
|
510
|
-
exclusive: false,
|
|
511
|
-
cursor: "crosshair",
|
|
512
|
-
isDraggable: true,
|
|
513
|
-
isResizable: true,
|
|
514
|
-
lockAspectRatio: false,
|
|
515
|
-
lockGroupAspectRatio: (a) => {
|
|
516
|
-
const r = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
517
|
-
return r >= 6 && r <= 84;
|
|
518
|
-
}
|
|
519
|
-
},
|
|
520
|
-
defaults: {
|
|
521
|
-
type: PdfAnnotationSubtype.INK,
|
|
522
|
-
intent: "InkHighlight",
|
|
523
|
-
strokeColor: "#FFCD45",
|
|
524
|
-
color: "#FFCD45",
|
|
525
|
-
// deprecated alias
|
|
526
|
-
opacity: 1,
|
|
527
|
-
strokeWidth: 14,
|
|
528
|
-
blendMode: PdfBlendMode.Multiply
|
|
529
|
-
}
|
|
530
|
-
},
|
|
541
|
+
...inkTools,
|
|
531
542
|
// Shape Tools
|
|
532
543
|
{
|
|
533
544
|
id: "circle",
|
|
@@ -540,8 +551,8 @@ const defaultTools = [
|
|
|
540
551
|
isResizable: true,
|
|
541
552
|
lockAspectRatio: false,
|
|
542
553
|
lockGroupAspectRatio: (a) => {
|
|
543
|
-
const
|
|
544
|
-
return
|
|
554
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
555
|
+
return r2 >= 6 && r2 <= 84;
|
|
545
556
|
}
|
|
546
557
|
},
|
|
547
558
|
defaults: {
|
|
@@ -568,8 +579,8 @@ const defaultTools = [
|
|
|
568
579
|
isResizable: true,
|
|
569
580
|
lockAspectRatio: false,
|
|
570
581
|
lockGroupAspectRatio: (a) => {
|
|
571
|
-
const
|
|
572
|
-
return
|
|
582
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
583
|
+
return r2 >= 6 && r2 <= 84;
|
|
573
584
|
}
|
|
574
585
|
},
|
|
575
586
|
defaults: {
|
|
@@ -599,8 +610,8 @@ const defaultTools = [
|
|
|
599
610
|
isGroupResizable: true,
|
|
600
611
|
// Scales proportionally in a group
|
|
601
612
|
lockGroupAspectRatio: (a) => {
|
|
602
|
-
const
|
|
603
|
-
return
|
|
613
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
614
|
+
return r2 >= 6 && r2 <= 84;
|
|
604
615
|
}
|
|
605
616
|
},
|
|
606
617
|
defaults: {
|
|
@@ -630,8 +641,8 @@ const defaultTools = [
|
|
|
630
641
|
isGroupResizable: true,
|
|
631
642
|
// Scales proportionally in a group
|
|
632
643
|
lockGroupAspectRatio: (a) => {
|
|
633
|
-
const
|
|
634
|
-
return
|
|
644
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
645
|
+
return r2 >= 6 && r2 <= 84;
|
|
635
646
|
}
|
|
636
647
|
},
|
|
637
648
|
defaults: {
|
|
@@ -666,8 +677,8 @@ const defaultTools = [
|
|
|
666
677
|
isGroupResizable: true,
|
|
667
678
|
// Scales proportionally in a group
|
|
668
679
|
lockGroupAspectRatio: (a) => {
|
|
669
|
-
const
|
|
670
|
-
return
|
|
680
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
681
|
+
return r2 >= 6 && r2 <= 84;
|
|
671
682
|
}
|
|
672
683
|
},
|
|
673
684
|
defaults: {
|
|
@@ -692,8 +703,8 @@ const defaultTools = [
|
|
|
692
703
|
isGroupResizable: true,
|
|
693
704
|
// Scales proportionally in a group
|
|
694
705
|
lockGroupAspectRatio: (a) => {
|
|
695
|
-
const
|
|
696
|
-
return
|
|
706
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
707
|
+
return r2 >= 6 && r2 <= 84;
|
|
697
708
|
}
|
|
698
709
|
},
|
|
699
710
|
defaults: {
|
|
@@ -736,8 +747,8 @@ const defaultTools = [
|
|
|
736
747
|
isResizable: true,
|
|
737
748
|
lockAspectRatio: false,
|
|
738
749
|
lockGroupAspectRatio: (a) => {
|
|
739
|
-
const
|
|
740
|
-
return
|
|
750
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
751
|
+
return r2 >= 6 && r2 <= 84;
|
|
741
752
|
}
|
|
742
753
|
},
|
|
743
754
|
defaults: {
|
|
@@ -1180,6 +1191,18 @@ function useState(initialValue) {
|
|
|
1180
1191
|
};
|
|
1181
1192
|
return [getValue, setValue];
|
|
1182
1193
|
}
|
|
1194
|
+
function isLineLike(points, threshold) {
|
|
1195
|
+
if (points.length < 3) return true;
|
|
1196
|
+
const A = points[0];
|
|
1197
|
+
const B = points[points.length - 1];
|
|
1198
|
+
const len = Math.hypot(B.x - A.x, B.y - A.y);
|
|
1199
|
+
if (len < 5) return false;
|
|
1200
|
+
const maxDev = points.reduce((max, P) => {
|
|
1201
|
+
const d = Math.abs((B.x - A.x) * (A.y - P.y) - (A.x - P.x) * (B.y - A.y)) / len;
|
|
1202
|
+
return Math.max(max, d);
|
|
1203
|
+
}, 0);
|
|
1204
|
+
return maxDev / len < threshold;
|
|
1205
|
+
}
|
|
1183
1206
|
const inkHandlerFactory = {
|
|
1184
1207
|
annotationType: PdfAnnotationSubtype.INK,
|
|
1185
1208
|
create(context) {
|
|
@@ -1215,7 +1238,8 @@ const inkHandlerFactory = {
|
|
|
1215
1238
|
data: {
|
|
1216
1239
|
...defaults,
|
|
1217
1240
|
rect: bounds,
|
|
1218
|
-
inkList: strokes
|
|
1241
|
+
inkList: strokes,
|
|
1242
|
+
blendMode: defaults.blendMode
|
|
1219
1243
|
}
|
|
1220
1244
|
};
|
|
1221
1245
|
};
|
|
@@ -1243,6 +1267,37 @@ const inkHandlerFactory = {
|
|
|
1243
1267
|
var _a;
|
|
1244
1268
|
setIsDrawing(false);
|
|
1245
1269
|
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
1270
|
+
const tool = getTool();
|
|
1271
|
+
const behavior = tool == null ? void 0 : tool.behavior;
|
|
1272
|
+
if (behavior == null ? void 0 : behavior.smartLineRecognition) {
|
|
1273
|
+
const threshold = behavior.smartLineThreshold ?? 0.15;
|
|
1274
|
+
const strokes = getStrokes();
|
|
1275
|
+
const last = strokes[strokes.length - 1];
|
|
1276
|
+
if (last && last.points.length > 1 && isLineLike(last.points, threshold)) {
|
|
1277
|
+
const first = last.points[0];
|
|
1278
|
+
const end = last.points[last.points.length - 1];
|
|
1279
|
+
const dx = end.x - first.x;
|
|
1280
|
+
const dy = end.y - first.y;
|
|
1281
|
+
const angleDeg = Math.atan2(Math.abs(dy), Math.abs(dx)) * (180 / Math.PI);
|
|
1282
|
+
const snapAngleDeg = behavior.snapAngleDeg ?? 15;
|
|
1283
|
+
if (angleDeg <= snapAngleDeg) {
|
|
1284
|
+
const avgY = last.points.reduce((sum, p) => sum + p.y, 0) / last.points.length;
|
|
1285
|
+
last.points = [
|
|
1286
|
+
{ x: first.x, y: avgY },
|
|
1287
|
+
{ x: end.x, y: avgY }
|
|
1288
|
+
];
|
|
1289
|
+
} else if (angleDeg >= 90 - snapAngleDeg) {
|
|
1290
|
+
const avgX = last.points.reduce((sum, p) => sum + p.x, 0) / last.points.length;
|
|
1291
|
+
last.points = [
|
|
1292
|
+
{ x: avgX, y: first.y },
|
|
1293
|
+
{ x: avgX, y: end.y }
|
|
1294
|
+
];
|
|
1295
|
+
}
|
|
1296
|
+
setStrokes([...strokes]);
|
|
1297
|
+
onPreview(getPreview());
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
const commitDelay = (behavior == null ? void 0 : behavior.commitDelay) ?? 800;
|
|
1246
1301
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1247
1302
|
timerRef.current = setTimeout(() => {
|
|
1248
1303
|
const strokes = getStrokes();
|
|
@@ -1263,7 +1318,7 @@ const inkHandlerFactory = {
|
|
|
1263
1318
|
}
|
|
1264
1319
|
setStrokes([]);
|
|
1265
1320
|
onPreview(null);
|
|
1266
|
-
},
|
|
1321
|
+
}, commitDelay);
|
|
1267
1322
|
},
|
|
1268
1323
|
onPointerCancel: (_, evt) => {
|
|
1269
1324
|
var _a;
|
|
@@ -1335,14 +1390,14 @@ const LINE_ENDING_HANDLERS = {
|
|
|
1335
1390
|
},
|
|
1336
1391
|
[PdfAnnotationLineEnding.Circle]: {
|
|
1337
1392
|
getSvgPath: (sw) => {
|
|
1338
|
-
const
|
|
1339
|
-
return `M ${
|
|
1393
|
+
const r2 = sw * 5 / 2;
|
|
1394
|
+
return `M ${r2} 0 A ${r2} ${r2} 0 1 1 ${-r2} 0 A ${r2} ${r2} 0 1 1 ${r2} 0`;
|
|
1340
1395
|
},
|
|
1341
1396
|
getLocalPoints: (sw) => {
|
|
1342
|
-
const
|
|
1397
|
+
const r2 = sw * 5 / 2;
|
|
1343
1398
|
return [
|
|
1344
|
-
{ x: -
|
|
1345
|
-
{ x:
|
|
1399
|
+
{ x: -r2, y: -r2 },
|
|
1400
|
+
{ x: r2, y: r2 }
|
|
1346
1401
|
];
|
|
1347
1402
|
},
|
|
1348
1403
|
getRotation: () => 0,
|
|
@@ -1484,7 +1539,7 @@ function createEnding(ending, strokeWidth, rad, px, py) {
|
|
|
1484
1539
|
if (!ending) return null;
|
|
1485
1540
|
const handler = LINE_ENDING_HANDLERS[ending];
|
|
1486
1541
|
if (!handler) return null;
|
|
1487
|
-
const toDeg = (
|
|
1542
|
+
const toDeg = (r2) => r2 * 180 / Math.PI;
|
|
1488
1543
|
const rotationAngle = handler.getRotation(rad);
|
|
1489
1544
|
return {
|
|
1490
1545
|
d: handler.getSvgPath(strokeWidth),
|
|
@@ -1701,8 +1756,8 @@ function useClickDetector({
|
|
|
1701
1756
|
onMove: (pos) => {
|
|
1702
1757
|
const start = getStartPos();
|
|
1703
1758
|
if (!start || getHasMoved()) return;
|
|
1704
|
-
const
|
|
1705
|
-
if (
|
|
1759
|
+
const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
|
|
1760
|
+
if (distance2 > threshold) {
|
|
1706
1761
|
setHasMoved(true);
|
|
1707
1762
|
}
|
|
1708
1763
|
},
|
|
@@ -2127,6 +2182,554 @@ const polylineHandlerFactory = {
|
|
|
2127
2182
|
};
|
|
2128
2183
|
}
|
|
2129
2184
|
};
|
|
2185
|
+
function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
|
|
2186
|
+
const theta = rotationDegrees * Math.PI / 180;
|
|
2187
|
+
const A = Math.abs(Math.cos(theta));
|
|
2188
|
+
const B = Math.abs(Math.sin(theta));
|
|
2189
|
+
const det = A * A - B * B;
|
|
2190
|
+
const newAABBw = newAABBRect.size.width;
|
|
2191
|
+
const newAABBh = newAABBRect.size.height;
|
|
2192
|
+
let newWidth;
|
|
2193
|
+
let newHeight;
|
|
2194
|
+
if (Math.abs(det) > 1e-6) {
|
|
2195
|
+
newWidth = (A * newAABBw - B * newAABBh) / det;
|
|
2196
|
+
newHeight = (A * newAABBh - B * newAABBw) / det;
|
|
2197
|
+
newWidth = Math.max(newWidth, 1);
|
|
2198
|
+
newHeight = Math.max(newHeight, 1);
|
|
2199
|
+
} else {
|
|
2200
|
+
const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
|
|
2201
|
+
const newArea = newAABBw * newAABBh;
|
|
2202
|
+
const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
|
|
2203
|
+
newWidth = originalUnrotatedRect.size.width * uniformScale;
|
|
2204
|
+
newHeight = originalUnrotatedRect.size.height * uniformScale;
|
|
2205
|
+
}
|
|
2206
|
+
const newCenterX = newAABBRect.origin.x + newAABBw / 2;
|
|
2207
|
+
const newCenterY = newAABBRect.origin.y + newAABBh / 2;
|
|
2208
|
+
return {
|
|
2209
|
+
origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
|
|
2210
|
+
size: { width: newWidth, height: newHeight }
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
const ANGLE_180 = Math.PI;
|
|
2214
|
+
const ANGLE_90 = Math.PI / 2;
|
|
2215
|
+
const ANGLE_34 = 34 * Math.PI / 180;
|
|
2216
|
+
const ANGLE_30 = 30 * Math.PI / 180;
|
|
2217
|
+
const ANGLE_12 = 12 * Math.PI / 180;
|
|
2218
|
+
class PathBuilder {
|
|
2219
|
+
constructor() {
|
|
2220
|
+
this.parts = [];
|
|
2221
|
+
this.bbox = {
|
|
2222
|
+
minX: Infinity,
|
|
2223
|
+
minY: Infinity,
|
|
2224
|
+
maxX: -Infinity,
|
|
2225
|
+
maxY: -Infinity
|
|
2226
|
+
};
|
|
2227
|
+
this.started = false;
|
|
2228
|
+
}
|
|
2229
|
+
moveTo(x, y) {
|
|
2230
|
+
const sy = -y;
|
|
2231
|
+
this.updateBBox(x, sy);
|
|
2232
|
+
this.parts.push(`M ${r(x)} ${r(sy)}`);
|
|
2233
|
+
this.started = true;
|
|
2234
|
+
}
|
|
2235
|
+
curveTo(x1, y1, x2, y2, x3, y3) {
|
|
2236
|
+
const sy1 = -y1;
|
|
2237
|
+
const sy2 = -y2;
|
|
2238
|
+
const sy3 = -y3;
|
|
2239
|
+
this.updateBBox(x1, sy1);
|
|
2240
|
+
this.updateBBox(x2, sy2);
|
|
2241
|
+
this.updateBBox(x3, sy3);
|
|
2242
|
+
this.parts.push(`C ${r(x1)} ${r(sy1)}, ${r(x2)} ${r(sy2)}, ${r(x3)} ${r(sy3)}`);
|
|
2243
|
+
}
|
|
2244
|
+
close() {
|
|
2245
|
+
if (this.started) {
|
|
2246
|
+
this.parts.push("Z");
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
build(lineWidth) {
|
|
2250
|
+
const d = lineWidth > 0 ? lineWidth / 2 : 0;
|
|
2251
|
+
return {
|
|
2252
|
+
path: this.parts.join(" "),
|
|
2253
|
+
bbox: {
|
|
2254
|
+
minX: this.bbox.minX - d,
|
|
2255
|
+
minY: this.bbox.minY - d,
|
|
2256
|
+
maxX: this.bbox.maxX + d,
|
|
2257
|
+
maxY: this.bbox.maxY + d
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
updateBBox(x, y) {
|
|
2262
|
+
if (x < this.bbox.minX) this.bbox.minX = x;
|
|
2263
|
+
if (y < this.bbox.minY) this.bbox.minY = y;
|
|
2264
|
+
if (x > this.bbox.maxX) this.bbox.maxX = x;
|
|
2265
|
+
if (y > this.bbox.maxY) this.bbox.maxY = y;
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
function r(n) {
|
|
2269
|
+
return Number(n.toFixed(4)).toString();
|
|
2270
|
+
}
|
|
2271
|
+
function distance(a, b) {
|
|
2272
|
+
const dx = b.x - a.x;
|
|
2273
|
+
const dy = b.y - a.y;
|
|
2274
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2275
|
+
}
|
|
2276
|
+
function cosine(dx, hypot) {
|
|
2277
|
+
return hypot === 0 ? 0 : dx / hypot;
|
|
2278
|
+
}
|
|
2279
|
+
function sine(dy, hypot) {
|
|
2280
|
+
return hypot === 0 ? 0 : dy / hypot;
|
|
2281
|
+
}
|
|
2282
|
+
function polygonDirection(pts) {
|
|
2283
|
+
let a = 0;
|
|
2284
|
+
const len = pts.length;
|
|
2285
|
+
for (let i = 0; i < len; i++) {
|
|
2286
|
+
const j = (i + 1) % len;
|
|
2287
|
+
a += pts[i].x * pts[j].y - pts[i].y * pts[j].x;
|
|
2288
|
+
}
|
|
2289
|
+
return a;
|
|
2290
|
+
}
|
|
2291
|
+
function ensurePositiveWinding(pts) {
|
|
2292
|
+
if (polygonDirection(pts) < 0) {
|
|
2293
|
+
pts.reverse();
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function removeZeroLengthSegments(polygon) {
|
|
2297
|
+
if (polygon.length <= 2) return polygon;
|
|
2298
|
+
const tolerance = 0.5;
|
|
2299
|
+
const result = [polygon[0]];
|
|
2300
|
+
for (let i = 1; i < polygon.length; i++) {
|
|
2301
|
+
const prev = result[result.length - 1];
|
|
2302
|
+
const cur = polygon[i];
|
|
2303
|
+
if (Math.abs(cur.x - prev.x) >= tolerance || Math.abs(cur.y - prev.y) >= tolerance) {
|
|
2304
|
+
result.push(cur);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return result;
|
|
2308
|
+
}
|
|
2309
|
+
function arcSegment(startAng, endAng, cx, cy, rx, ry, out, addMoveTo) {
|
|
2310
|
+
const cosA = Math.cos(startAng);
|
|
2311
|
+
const sinA = Math.sin(startAng);
|
|
2312
|
+
const cosB = Math.cos(endAng);
|
|
2313
|
+
const sinB = Math.sin(endAng);
|
|
2314
|
+
const denom = Math.sin((endAng - startAng) / 2);
|
|
2315
|
+
if (denom === 0) {
|
|
2316
|
+
if (addMoveTo) {
|
|
2317
|
+
out.moveTo(cx + rx * cosA, cy + ry * sinA);
|
|
2318
|
+
}
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
|
|
2322
|
+
const p1x = cx + rx * (cosA - bcp * sinA);
|
|
2323
|
+
const p1y = cy + ry * (sinA + bcp * cosA);
|
|
2324
|
+
const p2x = cx + rx * (cosB + bcp * sinB);
|
|
2325
|
+
const p2y = cy + ry * (sinB - bcp * cosB);
|
|
2326
|
+
const p3x = cx + rx * cosB;
|
|
2327
|
+
const p3y = cy + ry * sinB;
|
|
2328
|
+
if (addMoveTo) {
|
|
2329
|
+
out.moveTo(cx + rx * cosA, cy + ry * sinA);
|
|
2330
|
+
}
|
|
2331
|
+
out.curveTo(p1x, p1y, p2x, p2y, p3x, p3y);
|
|
2332
|
+
}
|
|
2333
|
+
function arcSegmentToArray(startAng, endAng, cx, cy, rx, ry) {
|
|
2334
|
+
const cosA = Math.cos(startAng);
|
|
2335
|
+
const sinA = Math.sin(startAng);
|
|
2336
|
+
const cosB = Math.cos(endAng);
|
|
2337
|
+
const sinB = Math.sin(endAng);
|
|
2338
|
+
const denom = Math.sin((endAng - startAng) / 2);
|
|
2339
|
+
if (denom === 0) return [];
|
|
2340
|
+
const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
|
|
2341
|
+
return [
|
|
2342
|
+
{ x: cx + rx * (cosA - bcp * sinA), y: cy + ry * (sinA + bcp * cosA) },
|
|
2343
|
+
{ x: cx + rx * (cosB + bcp * sinB), y: cy + ry * (sinB - bcp * cosB) },
|
|
2344
|
+
{ x: cx + rx * cosB, y: cy + ry * sinB }
|
|
2345
|
+
];
|
|
2346
|
+
}
|
|
2347
|
+
function getArc(startAng, endAng, rx, ry, cx, cy, out, addMoveTo) {
|
|
2348
|
+
const angleIncr = ANGLE_90;
|
|
2349
|
+
let angleTodo = endAng - startAng;
|
|
2350
|
+
while (angleTodo < 0) angleTodo += 2 * Math.PI;
|
|
2351
|
+
const sweep = angleTodo;
|
|
2352
|
+
let angleDone = 0;
|
|
2353
|
+
if (addMoveTo) {
|
|
2354
|
+
out.moveTo(cx + rx * Math.cos(startAng), cy + ry * Math.sin(startAng));
|
|
2355
|
+
}
|
|
2356
|
+
while (angleTodo > angleIncr) {
|
|
2357
|
+
arcSegment(startAng + angleDone, startAng + angleDone + angleIncr, cx, cy, rx, ry, out, false);
|
|
2358
|
+
angleDone += angleIncr;
|
|
2359
|
+
angleTodo -= angleIncr;
|
|
2360
|
+
}
|
|
2361
|
+
if (angleTodo > 0) {
|
|
2362
|
+
arcSegment(startAng + angleDone, startAng + sweep, cx, cy, rx, ry, out, false);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
function addCornerCurl(anglePrev, angleCur, radius, cx, cy, alpha, alphaPrev, out, addMoveTo) {
|
|
2366
|
+
let a = anglePrev + ANGLE_180 + alphaPrev;
|
|
2367
|
+
const b = anglePrev + ANGLE_180 + alphaPrev - 22 * Math.PI / 180;
|
|
2368
|
+
arcSegment(a, b, cx, cy, radius, radius, out, addMoveTo);
|
|
2369
|
+
a = b;
|
|
2370
|
+
const bEnd = angleCur - alpha;
|
|
2371
|
+
getArc(a, bEnd, radius, radius, cx, cy, out, false);
|
|
2372
|
+
}
|
|
2373
|
+
function addFirstIntermediateCurl(angleCur, r2, alpha, cx, cy, out) {
|
|
2374
|
+
const a = angleCur + ANGLE_180;
|
|
2375
|
+
arcSegment(a + alpha, a + alpha - ANGLE_30, cx, cy, r2, r2, out, false);
|
|
2376
|
+
arcSegment(a + alpha - ANGLE_30, a + ANGLE_90, cx, cy, r2, r2, out, false);
|
|
2377
|
+
arcSegment(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, cx, cy, r2, r2, out, false);
|
|
2378
|
+
}
|
|
2379
|
+
function getIntermediateCurlTemplate(angleCur, r2) {
|
|
2380
|
+
const pts = [];
|
|
2381
|
+
const a = angleCur + ANGLE_180;
|
|
2382
|
+
pts.push(...arcSegmentToArray(a + ANGLE_34, a + ANGLE_12, 0, 0, r2, r2));
|
|
2383
|
+
pts.push(...arcSegmentToArray(a + ANGLE_12, a + ANGLE_90, 0, 0, r2, r2));
|
|
2384
|
+
pts.push(...arcSegmentToArray(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, 0, 0, r2, r2));
|
|
2385
|
+
return pts;
|
|
2386
|
+
}
|
|
2387
|
+
function outputCurlTemplate(template, x, y, out) {
|
|
2388
|
+
for (let i = 0; i + 2 < template.length; i += 3) {
|
|
2389
|
+
const a = template[i];
|
|
2390
|
+
const b = template[i + 1];
|
|
2391
|
+
const c = template[i + 2];
|
|
2392
|
+
out.curveTo(a.x + x, a.y + y, b.x + x, b.y + y, c.x + x, c.y + y);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
function computeParamsPolygon(idealRadius, k, length) {
|
|
2396
|
+
if (length === 0) return { n: -1, adjustedRadius: idealRadius };
|
|
2397
|
+
const cornerSpace = 2 * k * idealRadius;
|
|
2398
|
+
const remaining = length - cornerSpace;
|
|
2399
|
+
if (remaining <= 0) {
|
|
2400
|
+
return { n: 0, adjustedRadius: idealRadius };
|
|
2401
|
+
}
|
|
2402
|
+
const idealAdvance = 2 * k * idealRadius;
|
|
2403
|
+
const n = Math.max(1, Math.ceil(remaining / idealAdvance));
|
|
2404
|
+
const adjustedRadius = remaining / (n * 2 * k);
|
|
2405
|
+
return { n, adjustedRadius };
|
|
2406
|
+
}
|
|
2407
|
+
function cloudyPolygonImpl(vertices, isEllipse, intensity, lineWidth, out) {
|
|
2408
|
+
let polygon = removeZeroLengthSegments(vertices);
|
|
2409
|
+
ensurePositiveWinding(polygon);
|
|
2410
|
+
const numPoints = polygon.length;
|
|
2411
|
+
if (numPoints < 2) return;
|
|
2412
|
+
if (intensity <= 0) {
|
|
2413
|
+
out.moveTo(polygon[0].x, polygon[0].y);
|
|
2414
|
+
for (let i = 1; i < numPoints; i++) {
|
|
2415
|
+
out.curveTo(
|
|
2416
|
+
polygon[i].x,
|
|
2417
|
+
polygon[i].y,
|
|
2418
|
+
polygon[i].x,
|
|
2419
|
+
polygon[i].y,
|
|
2420
|
+
polygon[i].x,
|
|
2421
|
+
polygon[i].y
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
let idealRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
|
|
2427
|
+
if (idealRadius < 0.5) idealRadius = 0.5;
|
|
2428
|
+
const k = Math.cos(ANGLE_34);
|
|
2429
|
+
const edgeAlphas = [];
|
|
2430
|
+
for (let j = 0; j + 1 < numPoints; j++) {
|
|
2431
|
+
const len = distance(polygon[j], polygon[j + 1]);
|
|
2432
|
+
if (len <= 0 || len >= 2 * k * idealRadius) {
|
|
2433
|
+
edgeAlphas.push(ANGLE_34);
|
|
2434
|
+
} else {
|
|
2435
|
+
edgeAlphas.push(Math.acos(Math.min(1, len / (2 * idealRadius))));
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
let anglePrev = 0;
|
|
2439
|
+
let outputStarted = false;
|
|
2440
|
+
for (let j = 0; j + 1 < numPoints; j++) {
|
|
2441
|
+
const pt = polygon[j];
|
|
2442
|
+
const ptNext = polygon[j + 1];
|
|
2443
|
+
const len = distance(pt, ptNext);
|
|
2444
|
+
if (len === 0) continue;
|
|
2445
|
+
const params = computeParamsPolygon(idealRadius, k, len);
|
|
2446
|
+
if (params.n < 0) {
|
|
2447
|
+
if (!outputStarted) {
|
|
2448
|
+
out.moveTo(pt.x, pt.y);
|
|
2449
|
+
outputStarted = true;
|
|
2450
|
+
}
|
|
2451
|
+
continue;
|
|
2452
|
+
}
|
|
2453
|
+
const edgeRadius = Math.max(0.5, params.adjustedRadius);
|
|
2454
|
+
const intermAdvance = 2 * k * edgeRadius;
|
|
2455
|
+
const firstAdvance = k * idealRadius + k * edgeRadius;
|
|
2456
|
+
let angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
|
|
2457
|
+
if (j === 0) {
|
|
2458
|
+
const ptPrev = polygon[numPoints - 2];
|
|
2459
|
+
anglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
|
|
2460
|
+
}
|
|
2461
|
+
const cos = cosine(ptNext.x - pt.x, len);
|
|
2462
|
+
const sin = sine(ptNext.y - pt.y, len);
|
|
2463
|
+
let x = pt.x;
|
|
2464
|
+
let y = pt.y;
|
|
2465
|
+
const alpha = edgeAlphas[j];
|
|
2466
|
+
const prevEdgeIdx = j === 0 ? numPoints - 2 : j - 1;
|
|
2467
|
+
const alphaPrevEdge = edgeAlphas[prevEdgeIdx] ?? ANGLE_34;
|
|
2468
|
+
addCornerCurl(
|
|
2469
|
+
anglePrev,
|
|
2470
|
+
angleCur,
|
|
2471
|
+
idealRadius,
|
|
2472
|
+
pt.x,
|
|
2473
|
+
pt.y,
|
|
2474
|
+
alpha,
|
|
2475
|
+
alphaPrevEdge,
|
|
2476
|
+
out,
|
|
2477
|
+
!outputStarted
|
|
2478
|
+
);
|
|
2479
|
+
outputStarted = true;
|
|
2480
|
+
if (params.n === 0) {
|
|
2481
|
+
x += len * cos;
|
|
2482
|
+
y += len * sin;
|
|
2483
|
+
} else {
|
|
2484
|
+
x += firstAdvance * cos;
|
|
2485
|
+
y += firstAdvance * sin;
|
|
2486
|
+
let numInterm = params.n;
|
|
2487
|
+
if (params.n >= 1) {
|
|
2488
|
+
addFirstIntermediateCurl(angleCur, edgeRadius, ANGLE_34, x, y, out);
|
|
2489
|
+
x += intermAdvance * cos;
|
|
2490
|
+
y += intermAdvance * sin;
|
|
2491
|
+
numInterm = params.n - 1;
|
|
2492
|
+
}
|
|
2493
|
+
const template = getIntermediateCurlTemplate(angleCur, edgeRadius);
|
|
2494
|
+
for (let i = 0; i < numInterm; i++) {
|
|
2495
|
+
outputCurlTemplate(template, x, y, out);
|
|
2496
|
+
x += intermAdvance * cos;
|
|
2497
|
+
y += intermAdvance * sin;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
anglePrev = angleCur;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function flattenEllipse(left, bottom, right, top) {
|
|
2504
|
+
const cx = (left + right) / 2;
|
|
2505
|
+
const cy = (bottom + top) / 2;
|
|
2506
|
+
const rx = (right - left) / 2;
|
|
2507
|
+
const ry = (top - bottom) / 2;
|
|
2508
|
+
if (rx <= 0 || ry <= 0) return [];
|
|
2509
|
+
const numSegments = Math.max(32, Math.ceil(Math.max(rx, ry) * 2));
|
|
2510
|
+
const points = [];
|
|
2511
|
+
for (let i = 0; i <= numSegments; i++) {
|
|
2512
|
+
const angle = 2 * Math.PI * i / numSegments;
|
|
2513
|
+
points.push({
|
|
2514
|
+
x: cx + rx * Math.cos(angle),
|
|
2515
|
+
y: cy + ry * Math.sin(angle)
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
return points;
|
|
2519
|
+
}
|
|
2520
|
+
function getEllipseCloudRadius(intensity, lineWidth) {
|
|
2521
|
+
return 4.75 * intensity + 0.5 * lineWidth;
|
|
2522
|
+
}
|
|
2523
|
+
function getPolygonCloudRadius(intensity, lineWidth) {
|
|
2524
|
+
return 4 * intensity + 0.5 * lineWidth;
|
|
2525
|
+
}
|
|
2526
|
+
function cloudyEllipseImpl(left, bottom, right, top, intensity, lineWidth, out) {
|
|
2527
|
+
if (intensity <= 0) {
|
|
2528
|
+
const rx = Math.abs(right - left) / 2;
|
|
2529
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2530
|
+
const cx = (left + right) / 2;
|
|
2531
|
+
const cy = (bottom + top) / 2;
|
|
2532
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const width = right - left;
|
|
2536
|
+
const height = top - bottom;
|
|
2537
|
+
let cloudRadius = getEllipseCloudRadius(intensity, lineWidth);
|
|
2538
|
+
const threshold1 = 0.5 * cloudRadius;
|
|
2539
|
+
if (width < threshold1 && height < threshold1) {
|
|
2540
|
+
const rx = Math.abs(right - left) / 2;
|
|
2541
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2542
|
+
const cx = (left + right) / 2;
|
|
2543
|
+
const cy = (bottom + top) / 2;
|
|
2544
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
const threshold2 = 5;
|
|
2548
|
+
if (width < threshold2 && height > 20 || width > 20 && height < threshold2) {
|
|
2549
|
+
cloudyPolygonImpl(
|
|
2550
|
+
[
|
|
2551
|
+
{ x: left, y: bottom },
|
|
2552
|
+
{ x: right, y: bottom },
|
|
2553
|
+
{ x: right, y: top },
|
|
2554
|
+
{ x: left, y: top },
|
|
2555
|
+
{ x: left, y: bottom }
|
|
2556
|
+
],
|
|
2557
|
+
true,
|
|
2558
|
+
intensity,
|
|
2559
|
+
lineWidth,
|
|
2560
|
+
out
|
|
2561
|
+
);
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
const radiusAdj = Math.sin(ANGLE_12) * cloudRadius - 1.5;
|
|
2565
|
+
let adjLeft = left;
|
|
2566
|
+
let adjRight = right;
|
|
2567
|
+
let adjBottom = bottom;
|
|
2568
|
+
let adjTop = top;
|
|
2569
|
+
if (width > 2 * radiusAdj) {
|
|
2570
|
+
adjLeft += radiusAdj;
|
|
2571
|
+
adjRight -= radiusAdj;
|
|
2572
|
+
} else {
|
|
2573
|
+
const mid = (left + right) / 2;
|
|
2574
|
+
adjLeft = mid - 0.1;
|
|
2575
|
+
adjRight = mid + 0.1;
|
|
2576
|
+
}
|
|
2577
|
+
if (height > 2 * radiusAdj) {
|
|
2578
|
+
adjBottom += radiusAdj;
|
|
2579
|
+
adjTop -= radiusAdj;
|
|
2580
|
+
} else {
|
|
2581
|
+
const mid = (top + bottom) / 2;
|
|
2582
|
+
adjTop = mid + 0.1;
|
|
2583
|
+
adjBottom = mid - 0.1;
|
|
2584
|
+
}
|
|
2585
|
+
const flatPolygon = flattenEllipse(adjLeft, adjBottom, adjRight, adjTop);
|
|
2586
|
+
const numFlatPts = flatPolygon.length;
|
|
2587
|
+
if (numFlatPts < 2) return;
|
|
2588
|
+
let totLen = 0;
|
|
2589
|
+
for (let i = 1; i < numFlatPts; i++) {
|
|
2590
|
+
totLen += distance(flatPolygon[i - 1], flatPolygon[i]);
|
|
2591
|
+
}
|
|
2592
|
+
const k = Math.cos(ANGLE_34);
|
|
2593
|
+
let curlAdvance = 2 * k * cloudRadius;
|
|
2594
|
+
let n = Math.ceil(totLen / curlAdvance);
|
|
2595
|
+
if (n < 2) {
|
|
2596
|
+
const rx = Math.abs(right - left) / 2;
|
|
2597
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2598
|
+
const cx = (left + right) / 2;
|
|
2599
|
+
const cy = (bottom + top) / 2;
|
|
2600
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
curlAdvance = totLen / n;
|
|
2604
|
+
cloudRadius = curlAdvance / (2 * k);
|
|
2605
|
+
if (cloudRadius < 0.5) {
|
|
2606
|
+
cloudRadius = 0.5;
|
|
2607
|
+
curlAdvance = 2 * k * cloudRadius;
|
|
2608
|
+
} else if (cloudRadius < 3) {
|
|
2609
|
+
const rx = Math.abs(right - left) / 2;
|
|
2610
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2611
|
+
const cx = (left + right) / 2;
|
|
2612
|
+
const cy = (bottom + top) / 2;
|
|
2613
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
const centerPoints = [];
|
|
2617
|
+
let lengthRemain = 0;
|
|
2618
|
+
const comparisonToler = lineWidth * 0.1;
|
|
2619
|
+
for (let i = 0; i + 1 < numFlatPts; i++) {
|
|
2620
|
+
const p1 = flatPolygon[i];
|
|
2621
|
+
const p2 = flatPolygon[i + 1];
|
|
2622
|
+
const segDx = p2.x - p1.x;
|
|
2623
|
+
const segDy = p2.y - p1.y;
|
|
2624
|
+
const segLen = distance(p1, p2);
|
|
2625
|
+
if (segLen === 0) continue;
|
|
2626
|
+
let lengthTodo = segLen + lengthRemain;
|
|
2627
|
+
if (lengthTodo >= curlAdvance - comparisonToler || i === numFlatPts - 2) {
|
|
2628
|
+
const cos = cosine(segDx, segLen);
|
|
2629
|
+
const sin = sine(segDy, segLen);
|
|
2630
|
+
let d = curlAdvance - lengthRemain;
|
|
2631
|
+
while (lengthTodo >= curlAdvance - comparisonToler) {
|
|
2632
|
+
centerPoints.push({ x: p1.x + d * cos, y: p1.y + d * sin });
|
|
2633
|
+
lengthTodo -= curlAdvance;
|
|
2634
|
+
d += curlAdvance;
|
|
2635
|
+
}
|
|
2636
|
+
lengthRemain = Math.max(0, lengthTodo);
|
|
2637
|
+
} else {
|
|
2638
|
+
lengthRemain += segLen;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
const cpLen = centerPoints.length;
|
|
2642
|
+
let epAnglePrev = 0;
|
|
2643
|
+
let epAlphaPrev = 0;
|
|
2644
|
+
for (let i = 0; i < cpLen; i++) {
|
|
2645
|
+
const idxNext = (i + 1) % cpLen;
|
|
2646
|
+
const pt = centerPoints[i];
|
|
2647
|
+
const ptNext = centerPoints[idxNext];
|
|
2648
|
+
if (i === 0) {
|
|
2649
|
+
const ptPrev = centerPoints[cpLen - 1];
|
|
2650
|
+
epAnglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
|
|
2651
|
+
epAlphaPrev = computeParamsEllipse(ptPrev, pt, cloudRadius, curlAdvance);
|
|
2652
|
+
}
|
|
2653
|
+
const angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
|
|
2654
|
+
const alpha = computeParamsEllipse(pt, ptNext, cloudRadius, curlAdvance);
|
|
2655
|
+
addCornerCurl(epAnglePrev, angleCur, cloudRadius, pt.x, pt.y, alpha, epAlphaPrev, out, i === 0);
|
|
2656
|
+
epAnglePrev = angleCur;
|
|
2657
|
+
epAlphaPrev = alpha;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
function computeParamsEllipse(pt, ptNext, r2, curlAdv) {
|
|
2661
|
+
const len = distance(pt, ptNext);
|
|
2662
|
+
if (len === 0) return ANGLE_34;
|
|
2663
|
+
const e = len - curlAdv;
|
|
2664
|
+
const arg = (curlAdv / 2 + e / 2) / r2;
|
|
2665
|
+
return arg < -1 || arg > 1 ? 0 : Math.acos(arg);
|
|
2666
|
+
}
|
|
2667
|
+
function getCloudyBorderExtent(intensity, lineWidth, isEllipse) {
|
|
2668
|
+
const cloudRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
|
|
2669
|
+
return cloudRadius + lineWidth / 2;
|
|
2670
|
+
}
|
|
2671
|
+
function generateCloudyRectanglePath(rect, rd, intensity, lineWidth) {
|
|
2672
|
+
const out = new PathBuilder();
|
|
2673
|
+
let left = 0;
|
|
2674
|
+
let top = 0;
|
|
2675
|
+
let right = rect.width;
|
|
2676
|
+
let bottom = rect.height;
|
|
2677
|
+
if (rd) {
|
|
2678
|
+
left += rd.left;
|
|
2679
|
+
top += rd.top;
|
|
2680
|
+
right -= rd.right;
|
|
2681
|
+
bottom -= rd.bottom;
|
|
2682
|
+
} else {
|
|
2683
|
+
left += lineWidth / 2;
|
|
2684
|
+
top += lineWidth / 2;
|
|
2685
|
+
right -= lineWidth / 2;
|
|
2686
|
+
bottom -= lineWidth / 2;
|
|
2687
|
+
}
|
|
2688
|
+
const polygon = [
|
|
2689
|
+
{ x: left, y: -top },
|
|
2690
|
+
{ x: right, y: -top },
|
|
2691
|
+
{ x: right, y: -bottom },
|
|
2692
|
+
{ x: left, y: -bottom },
|
|
2693
|
+
{ x: left, y: -top }
|
|
2694
|
+
];
|
|
2695
|
+
cloudyPolygonImpl(polygon, false, intensity, lineWidth, out);
|
|
2696
|
+
out.close();
|
|
2697
|
+
return out.build(lineWidth);
|
|
2698
|
+
}
|
|
2699
|
+
function generateCloudyEllipsePath(rect, rd, intensity, lineWidth) {
|
|
2700
|
+
const out = new PathBuilder();
|
|
2701
|
+
let left = 0;
|
|
2702
|
+
let top = 0;
|
|
2703
|
+
let right = rect.width;
|
|
2704
|
+
let bottom = rect.height;
|
|
2705
|
+
if (rd) {
|
|
2706
|
+
left += rd.left;
|
|
2707
|
+
top += rd.top;
|
|
2708
|
+
right -= rd.right;
|
|
2709
|
+
bottom -= rd.bottom;
|
|
2710
|
+
}
|
|
2711
|
+
cloudyEllipseImpl(left, -bottom, right, -top, intensity, lineWidth, out);
|
|
2712
|
+
out.close();
|
|
2713
|
+
return out.build(lineWidth);
|
|
2714
|
+
}
|
|
2715
|
+
function generateCloudyPolygonPath(vertices, rectOrigin, intensity, lineWidth) {
|
|
2716
|
+
const out = new PathBuilder();
|
|
2717
|
+
if (vertices.length < 3) {
|
|
2718
|
+
return out.build(lineWidth);
|
|
2719
|
+
}
|
|
2720
|
+
const localPts = vertices.map((v) => ({
|
|
2721
|
+
x: v.x - rectOrigin.x,
|
|
2722
|
+
y: -(v.y - rectOrigin.y)
|
|
2723
|
+
}));
|
|
2724
|
+
const first = localPts[0];
|
|
2725
|
+
const last = localPts[localPts.length - 1];
|
|
2726
|
+
if (first.x !== last.x || first.y !== last.y) {
|
|
2727
|
+
localPts.push({ x: first.x, y: first.y });
|
|
2728
|
+
}
|
|
2729
|
+
cloudyPolygonImpl(localPts, false, intensity, lineWidth, out);
|
|
2730
|
+
out.close();
|
|
2731
|
+
return out.build(lineWidth);
|
|
2732
|
+
}
|
|
2130
2733
|
const HANDLE_SIZE_PX = 14;
|
|
2131
2734
|
const polygonHandlerFactory = {
|
|
2132
2735
|
annotationType: PdfAnnotationSubtype.POLYGON,
|
|
@@ -2165,7 +2768,9 @@ const polygonHandlerFactory = {
|
|
|
2165
2768
|
if (vertices.length < 3) return;
|
|
2166
2769
|
const defaults = getDefaults();
|
|
2167
2770
|
if (!defaults) return;
|
|
2168
|
-
const
|
|
2771
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2772
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
|
|
2773
|
+
const rect = expandRect(rectFromPoints(vertices), pad);
|
|
2169
2774
|
const anno = {
|
|
2170
2775
|
...defaults,
|
|
2171
2776
|
vertices,
|
|
@@ -2173,7 +2778,10 @@ const polygonHandlerFactory = {
|
|
|
2173
2778
|
type: PdfAnnotationSubtype.POLYGON,
|
|
2174
2779
|
pageIndex: context.pageIndex,
|
|
2175
2780
|
id: uuidV4(),
|
|
2176
|
-
created: /* @__PURE__ */ new Date()
|
|
2781
|
+
created: /* @__PURE__ */ new Date(),
|
|
2782
|
+
...intensity > 0 && {
|
|
2783
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2784
|
+
}
|
|
2177
2785
|
};
|
|
2178
2786
|
onCommit(anno);
|
|
2179
2787
|
setVertices([]);
|
|
@@ -2186,8 +2794,10 @@ const polygonHandlerFactory = {
|
|
|
2186
2794
|
if (vertices.length === 0 || !currentPos) return null;
|
|
2187
2795
|
const defaults = getDefaults();
|
|
2188
2796
|
if (!defaults) return null;
|
|
2797
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2798
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
|
|
2189
2799
|
const allPoints = [...vertices, currentPos];
|
|
2190
|
-
const bounds = expandRect(rectFromPoints(allPoints),
|
|
2800
|
+
const bounds = expandRect(rectFromPoints(allPoints), pad);
|
|
2191
2801
|
return {
|
|
2192
2802
|
type: PdfAnnotationSubtype.POLYGON,
|
|
2193
2803
|
bounds,
|
|
@@ -2273,10 +2883,11 @@ const squareHandlerFactory = {
|
|
|
2273
2883
|
const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
|
|
2274
2884
|
const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
|
|
2275
2885
|
const strokeWidth = defaults.strokeWidth;
|
|
2276
|
-
const
|
|
2886
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2887
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
|
|
2277
2888
|
const rect = {
|
|
2278
|
-
origin: { x: x -
|
|
2279
|
-
size: { width: width +
|
|
2889
|
+
origin: { x: x - pad, y: y - pad },
|
|
2890
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2280
2891
|
};
|
|
2281
2892
|
const anno = {
|
|
2282
2893
|
...defaults,
|
|
@@ -2284,7 +2895,10 @@ const squareHandlerFactory = {
|
|
|
2284
2895
|
created: /* @__PURE__ */ new Date(),
|
|
2285
2896
|
id: uuidV4(),
|
|
2286
2897
|
pageIndex,
|
|
2287
|
-
rect
|
|
2898
|
+
rect,
|
|
2899
|
+
...intensity > 0 && {
|
|
2900
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2901
|
+
}
|
|
2288
2902
|
};
|
|
2289
2903
|
onCommit(anno);
|
|
2290
2904
|
}
|
|
@@ -2299,17 +2913,21 @@ const squareHandlerFactory = {
|
|
|
2299
2913
|
const defaults = getDefaults();
|
|
2300
2914
|
if (!defaults) return null;
|
|
2301
2915
|
const strokeWidth = defaults.strokeWidth;
|
|
2302
|
-
const
|
|
2916
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2917
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
|
|
2303
2918
|
const rect = {
|
|
2304
|
-
origin: { x: minX -
|
|
2305
|
-
size: { width: width +
|
|
2919
|
+
origin: { x: minX - pad, y: minY - pad },
|
|
2920
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2306
2921
|
};
|
|
2307
2922
|
return {
|
|
2308
2923
|
type: PdfAnnotationSubtype.SQUARE,
|
|
2309
2924
|
bounds: rect,
|
|
2310
2925
|
data: {
|
|
2311
2926
|
rect,
|
|
2312
|
-
...defaults
|
|
2927
|
+
...defaults,
|
|
2928
|
+
...intensity > 0 && {
|
|
2929
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2930
|
+
}
|
|
2313
2931
|
}
|
|
2314
2932
|
};
|
|
2315
2933
|
};
|
|
@@ -2343,13 +2961,18 @@ const squareHandlerFactory = {
|
|
|
2343
2961
|
if (!defaults2) return;
|
|
2344
2962
|
const preview = getPreview(clampedPos);
|
|
2345
2963
|
if (preview) {
|
|
2964
|
+
const intensity = defaults2.cloudyBorderIntensity ?? 0;
|
|
2965
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
|
|
2346
2966
|
const anno = {
|
|
2347
2967
|
...defaults2,
|
|
2348
2968
|
type: PdfAnnotationSubtype.SQUARE,
|
|
2349
2969
|
created: /* @__PURE__ */ new Date(),
|
|
2350
2970
|
id: uuidV4(),
|
|
2351
2971
|
pageIndex,
|
|
2352
|
-
rect: preview.data.rect
|
|
2972
|
+
rect: preview.data.rect,
|
|
2973
|
+
...pad !== void 0 && {
|
|
2974
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2975
|
+
}
|
|
2353
2976
|
};
|
|
2354
2977
|
onCommit(anno);
|
|
2355
2978
|
}
|
|
@@ -2473,10 +3096,11 @@ const circleHandlerFactory = {
|
|
|
2473
3096
|
const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
|
|
2474
3097
|
const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
|
|
2475
3098
|
const strokeWidth = defaults.strokeWidth;
|
|
2476
|
-
const
|
|
3099
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
3100
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
|
|
2477
3101
|
const rect = {
|
|
2478
|
-
origin: { x: x -
|
|
2479
|
-
size: { width: width +
|
|
3102
|
+
origin: { x: x - pad, y: y - pad },
|
|
3103
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2480
3104
|
};
|
|
2481
3105
|
const anno = {
|
|
2482
3106
|
...defaults,
|
|
@@ -2484,7 +3108,10 @@ const circleHandlerFactory = {
|
|
|
2484
3108
|
created: /* @__PURE__ */ new Date(),
|
|
2485
3109
|
id: uuidV4(),
|
|
2486
3110
|
pageIndex,
|
|
2487
|
-
rect
|
|
3111
|
+
rect,
|
|
3112
|
+
...intensity > 0 && {
|
|
3113
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3114
|
+
}
|
|
2488
3115
|
};
|
|
2489
3116
|
onCommit(anno);
|
|
2490
3117
|
}
|
|
@@ -2499,17 +3126,21 @@ const circleHandlerFactory = {
|
|
|
2499
3126
|
const defaults = getDefaults();
|
|
2500
3127
|
if (!defaults) return null;
|
|
2501
3128
|
const strokeWidth = defaults.strokeWidth;
|
|
2502
|
-
const
|
|
3129
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
3130
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
|
|
2503
3131
|
const rect = {
|
|
2504
|
-
origin: { x: minX -
|
|
2505
|
-
size: { width: width +
|
|
3132
|
+
origin: { x: minX - pad, y: minY - pad },
|
|
3133
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2506
3134
|
};
|
|
2507
3135
|
return {
|
|
2508
3136
|
type: PdfAnnotationSubtype.CIRCLE,
|
|
2509
3137
|
bounds: rect,
|
|
2510
3138
|
data: {
|
|
2511
3139
|
rect,
|
|
2512
|
-
...defaults
|
|
3140
|
+
...defaults,
|
|
3141
|
+
...intensity > 0 && {
|
|
3142
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3143
|
+
}
|
|
2513
3144
|
}
|
|
2514
3145
|
};
|
|
2515
3146
|
};
|
|
@@ -2543,6 +3174,8 @@ const circleHandlerFactory = {
|
|
|
2543
3174
|
if (!defaults2) return;
|
|
2544
3175
|
const preview = getPreview(clampedPos);
|
|
2545
3176
|
if (preview) {
|
|
3177
|
+
const intensity = defaults2.cloudyBorderIntensity ?? 0;
|
|
3178
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
|
|
2546
3179
|
const anno = {
|
|
2547
3180
|
...defaults2,
|
|
2548
3181
|
type: PdfAnnotationSubtype.CIRCLE,
|
|
@@ -2550,7 +3183,10 @@ const circleHandlerFactory = {
|
|
|
2550
3183
|
created: /* @__PURE__ */ new Date(),
|
|
2551
3184
|
id: uuidV4(),
|
|
2552
3185
|
pageIndex,
|
|
2553
|
-
rect: preview.data.rect
|
|
3186
|
+
rect: preview.data.rect,
|
|
3187
|
+
...pad !== void 0 && {
|
|
3188
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3189
|
+
}
|
|
2554
3190
|
};
|
|
2555
3191
|
onCommit(anno);
|
|
2556
3192
|
}
|
|
@@ -2722,23 +3358,29 @@ const patchInk = (original, ctx) => {
|
|
|
2722
3358
|
}
|
|
2723
3359
|
case "resize": {
|
|
2724
3360
|
if (!ctx.changes.rect) return ctx.changes;
|
|
2725
|
-
const { oldRect, resolvedRect, rects } = baseResizeScaling(
|
|
3361
|
+
const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
|
|
2726
3362
|
original,
|
|
2727
3363
|
ctx.changes.rect,
|
|
2728
3364
|
ctx.metadata
|
|
2729
3365
|
);
|
|
2730
|
-
const inset = (
|
|
2731
|
-
origin: { x:
|
|
3366
|
+
const inset = (r2, pad) => ({
|
|
3367
|
+
origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
|
|
2732
3368
|
size: {
|
|
2733
|
-
width: Math.max(1,
|
|
2734
|
-
height: Math.max(1,
|
|
3369
|
+
width: Math.max(1, r2.size.width - pad * 2),
|
|
3370
|
+
height: Math.max(1, r2.size.height - pad * 2)
|
|
2735
3371
|
}
|
|
2736
3372
|
});
|
|
2737
|
-
const
|
|
2738
|
-
|
|
2739
|
-
|
|
3373
|
+
const resizeEpsilon = 1e-3;
|
|
3374
|
+
const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
|
|
3375
|
+
const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
|
|
3376
|
+
const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
|
|
3377
|
+
const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
|
|
3378
|
+
const maxStrokeWidth = Math.max(
|
|
3379
|
+
1,
|
|
3380
|
+
Math.min(resolvedRect.size.width, resolvedRect.size.height)
|
|
2740
3381
|
);
|
|
2741
|
-
const
|
|
3382
|
+
const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
|
|
3383
|
+
const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
|
|
2742
3384
|
const innerOld = inset(oldRect, original.strokeWidth / 2);
|
|
2743
3385
|
const innerNew = inset(resolvedRect, newStrokeWidth / 2);
|
|
2744
3386
|
const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
|
|
@@ -2948,11 +3590,17 @@ const patchPolyline = (orig, ctx) => {
|
|
|
2948
3590
|
return ctx.changes;
|
|
2949
3591
|
}
|
|
2950
3592
|
};
|
|
3593
|
+
function getPolygonPad(intensity, strokeWidth) {
|
|
3594
|
+
if ((intensity ?? 0) > 0) {
|
|
3595
|
+
return getCloudyBorderExtent(intensity, strokeWidth, false);
|
|
3596
|
+
}
|
|
3597
|
+
return strokeWidth / 2;
|
|
3598
|
+
}
|
|
2951
3599
|
const patchPolygon = (orig, ctx) => {
|
|
2952
3600
|
switch (ctx.type) {
|
|
2953
3601
|
case "vertex-edit":
|
|
2954
3602
|
if (ctx.changes.vertices && ctx.changes.vertices.length) {
|
|
2955
|
-
const pad = orig.strokeWidth
|
|
3603
|
+
const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
|
|
2956
3604
|
const rawVertices = ctx.changes.vertices;
|
|
2957
3605
|
const rawRect = expandRect(rectFromPoints(rawVertices), pad);
|
|
2958
3606
|
const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
|
|
@@ -2996,15 +3644,30 @@ const patchPolygon = (orig, ctx) => {
|
|
|
2996
3644
|
};
|
|
2997
3645
|
}
|
|
2998
3646
|
case "property-update": {
|
|
2999
|
-
const
|
|
3647
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3648
|
+
const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
|
|
3000
3649
|
if (!needsRectUpdate) return ctx.changes;
|
|
3001
3650
|
const merged = { ...orig, ...ctx.changes };
|
|
3002
|
-
const pad = merged.strokeWidth
|
|
3651
|
+
const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
|
|
3003
3652
|
const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
|
|
3653
|
+
let patch = ctx.changes;
|
|
3654
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3655
|
+
if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
|
|
3656
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3657
|
+
if (intensity > 0) {
|
|
3658
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
|
|
3659
|
+
patch = {
|
|
3660
|
+
...patch,
|
|
3661
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3662
|
+
};
|
|
3663
|
+
} else {
|
|
3664
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3004
3667
|
const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
|
|
3005
3668
|
if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
|
|
3006
3669
|
return {
|
|
3007
|
-
...
|
|
3670
|
+
...patch,
|
|
3008
3671
|
unrotatedRect: tightRect,
|
|
3009
3672
|
rect: calculateRotatedRectAABBAroundPoint(
|
|
3010
3673
|
tightRect,
|
|
@@ -3013,7 +3676,7 @@ const patchPolygon = (orig, ctx) => {
|
|
|
3013
3676
|
)
|
|
3014
3677
|
};
|
|
3015
3678
|
}
|
|
3016
|
-
return { ...
|
|
3679
|
+
return { ...patch, rect: tightRect };
|
|
3017
3680
|
}
|
|
3018
3681
|
default:
|
|
3019
3682
|
return ctx.changes;
|
|
@@ -3029,11 +3692,29 @@ const patchCircle = (orig, ctx) => {
|
|
|
3029
3692
|
return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
|
|
3030
3693
|
case "rotate":
|
|
3031
3694
|
return baseRotateChanges(orig, ctx) ?? ctx.changes;
|
|
3032
|
-
case "property-update":
|
|
3695
|
+
case "property-update": {
|
|
3696
|
+
let patch = ctx.changes;
|
|
3697
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3698
|
+
const strokeChanged = ctx.changes.strokeWidth !== void 0;
|
|
3699
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3700
|
+
if (cloudyChanged || strokeChanged && hasCloudy) {
|
|
3701
|
+
const merged = { ...orig, ...ctx.changes };
|
|
3702
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3703
|
+
if (intensity > 0) {
|
|
3704
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
|
|
3705
|
+
patch = {
|
|
3706
|
+
...patch,
|
|
3707
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3708
|
+
};
|
|
3709
|
+
} else {
|
|
3710
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3033
3713
|
if (ctx.changes.rotation !== void 0) {
|
|
3034
|
-
|
|
3714
|
+
patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
|
|
3035
3715
|
}
|
|
3036
|
-
return
|
|
3716
|
+
return patch;
|
|
3717
|
+
}
|
|
3037
3718
|
default:
|
|
3038
3719
|
return ctx.changes;
|
|
3039
3720
|
}
|
|
@@ -3048,11 +3729,29 @@ const patchSquare = (orig, ctx) => {
|
|
|
3048
3729
|
return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
|
|
3049
3730
|
case "rotate":
|
|
3050
3731
|
return baseRotateChanges(orig, ctx) ?? ctx.changes;
|
|
3051
|
-
case "property-update":
|
|
3732
|
+
case "property-update": {
|
|
3733
|
+
let patch = ctx.changes;
|
|
3734
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3735
|
+
const strokeChanged = ctx.changes.strokeWidth !== void 0;
|
|
3736
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3737
|
+
if (cloudyChanged || strokeChanged && hasCloudy) {
|
|
3738
|
+
const merged = { ...orig, ...ctx.changes };
|
|
3739
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3740
|
+
if (intensity > 0) {
|
|
3741
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
|
|
3742
|
+
patch = {
|
|
3743
|
+
...patch,
|
|
3744
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3745
|
+
};
|
|
3746
|
+
} else {
|
|
3747
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3052
3750
|
if (ctx.changes.rotation !== void 0) {
|
|
3053
|
-
|
|
3751
|
+
patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
|
|
3054
3752
|
}
|
|
3055
|
-
return
|
|
3753
|
+
return patch;
|
|
3754
|
+
}
|
|
3056
3755
|
default:
|
|
3057
3756
|
return ctx.changes;
|
|
3058
3757
|
}
|
|
@@ -3095,34 +3794,6 @@ const patchStamp = (orig, ctx) => {
|
|
|
3095
3794
|
return ctx.changes;
|
|
3096
3795
|
}
|
|
3097
3796
|
};
|
|
3098
|
-
function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
|
|
3099
|
-
const theta = rotationDegrees * Math.PI / 180;
|
|
3100
|
-
const A = Math.abs(Math.cos(theta));
|
|
3101
|
-
const B = Math.abs(Math.sin(theta));
|
|
3102
|
-
const det = A * A - B * B;
|
|
3103
|
-
const newAABBw = newAABBRect.size.width;
|
|
3104
|
-
const newAABBh = newAABBRect.size.height;
|
|
3105
|
-
let newWidth;
|
|
3106
|
-
let newHeight;
|
|
3107
|
-
if (Math.abs(det) > 1e-6) {
|
|
3108
|
-
newWidth = (A * newAABBw - B * newAABBh) / det;
|
|
3109
|
-
newHeight = (A * newAABBh - B * newAABBw) / det;
|
|
3110
|
-
newWidth = Math.max(newWidth, 1);
|
|
3111
|
-
newHeight = Math.max(newHeight, 1);
|
|
3112
|
-
} else {
|
|
3113
|
-
const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
|
|
3114
|
-
const newArea = newAABBw * newAABBh;
|
|
3115
|
-
const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
|
|
3116
|
-
newWidth = originalUnrotatedRect.size.width * uniformScale;
|
|
3117
|
-
newHeight = originalUnrotatedRect.size.height * uniformScale;
|
|
3118
|
-
}
|
|
3119
|
-
const newCenterX = newAABBRect.origin.x + newAABBw / 2;
|
|
3120
|
-
const newCenterY = newAABBRect.origin.y + newAABBh / 2;
|
|
3121
|
-
return {
|
|
3122
|
-
origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
|
|
3123
|
-
size: { width: newWidth, height: newHeight }
|
|
3124
|
-
};
|
|
3125
|
-
}
|
|
3126
3797
|
const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
3127
3798
|
constructor(id, registry, config) {
|
|
3128
3799
|
var _a, _b, _c;
|
|
@@ -5131,10 +5802,14 @@ export {
|
|
|
5131
5802
|
calculateRotatedRectAABBAroundPoint2 as calculateRotatedRectAABBAroundPoint,
|
|
5132
5803
|
convertAABBRectToUnrotatedSpace,
|
|
5133
5804
|
createToolPredicate,
|
|
5805
|
+
generateCloudyEllipsePath,
|
|
5806
|
+
generateCloudyPolygonPath,
|
|
5807
|
+
generateCloudyRectanglePath,
|
|
5134
5808
|
getAnnotationByUid,
|
|
5135
5809
|
getAnnotations,
|
|
5136
5810
|
getAnnotationsByPageIndex,
|
|
5137
5811
|
getAttachedLinks,
|
|
5812
|
+
getCloudyBorderExtent,
|
|
5138
5813
|
getGroupLeaderId,
|
|
5139
5814
|
getGroupMembers,
|
|
5140
5815
|
getIRTChildIds,
|