modern-path2d 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +226 -119
- package/dist/index.d.cts +21 -2
- package/dist/index.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +2 -2
- package/dist/index.mjs +226 -120
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -492,6 +492,195 @@ function cubicBezier(t, p0, p1, p2, p3) {
|
|
|
492
492
|
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
+
function isLeft(ax, ay, bx, by, px, py) {
|
|
496
|
+
return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
|
|
497
|
+
}
|
|
498
|
+
function windingNumber$1(px, py, vertices) {
|
|
499
|
+
const len = vertices.length;
|
|
500
|
+
let wn = 0;
|
|
501
|
+
for (let i = 0; i < len; i += 2) {
|
|
502
|
+
const ax = vertices[i];
|
|
503
|
+
const ay = vertices[i + 1];
|
|
504
|
+
const k = (i + 2) % len;
|
|
505
|
+
const bx = vertices[k];
|
|
506
|
+
const by = vertices[k + 1];
|
|
507
|
+
if (ay <= py) {
|
|
508
|
+
if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) {
|
|
509
|
+
wn++;
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) {
|
|
513
|
+
wn--;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return wn;
|
|
518
|
+
}
|
|
519
|
+
function crossingNumber(px, py, vertices) {
|
|
520
|
+
const len = vertices.length;
|
|
521
|
+
let cn = 0;
|
|
522
|
+
for (let i = 0; i < len; i += 2) {
|
|
523
|
+
const ax = vertices[i];
|
|
524
|
+
const ay = vertices[i + 1];
|
|
525
|
+
const k = (i + 2) % len;
|
|
526
|
+
const bx = vertices[k];
|
|
527
|
+
const by = vertices[k + 1];
|
|
528
|
+
if (ay <= py && by > py || ay > py && by <= py) {
|
|
529
|
+
const t = (py - ay) / (by - ay);
|
|
530
|
+
if (px < ax + t * (bx - ax)) {
|
|
531
|
+
cn++;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return cn;
|
|
536
|
+
}
|
|
537
|
+
function segmentDistance(px, py, ax, ay, bx, by) {
|
|
538
|
+
const dx = bx - ax;
|
|
539
|
+
const dy = by - ay;
|
|
540
|
+
const lenSq = dx * dx + dy * dy;
|
|
541
|
+
let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
542
|
+
if (t < 0) {
|
|
543
|
+
t = 0;
|
|
544
|
+
} else if (t > 1) {
|
|
545
|
+
t = 1;
|
|
546
|
+
}
|
|
547
|
+
const cx = ax + t * dx;
|
|
548
|
+
const cy = ay + t * dy;
|
|
549
|
+
return Math.hypot(px - cx, py - cy);
|
|
550
|
+
}
|
|
551
|
+
function pointInPolygon(point, vertices, fillRule = "nonzero") {
|
|
552
|
+
if (vertices.length < 6) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
if (fillRule === "evenodd") {
|
|
556
|
+
return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
|
|
557
|
+
}
|
|
558
|
+
return windingNumber$1(point.x, point.y, vertices) !== 0;
|
|
559
|
+
}
|
|
560
|
+
function pointInPolygons(point, polygons, fillRule = "nonzero") {
|
|
561
|
+
const { x, y } = point;
|
|
562
|
+
if (fillRule === "evenodd") {
|
|
563
|
+
let cn = 0;
|
|
564
|
+
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
565
|
+
const ring = polygons[i];
|
|
566
|
+
if (ring.length >= 6) {
|
|
567
|
+
cn += crossingNumber(x, y, ring);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return (cn & 1) === 1;
|
|
571
|
+
}
|
|
572
|
+
let wn = 0;
|
|
573
|
+
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
574
|
+
const ring = polygons[i];
|
|
575
|
+
if (ring.length >= 6) {
|
|
576
|
+
wn += windingNumber$1(x, y, ring);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return wn !== 0;
|
|
580
|
+
}
|
|
581
|
+
function pointToSegmentDistance(point, a, b) {
|
|
582
|
+
return segmentDistance(point.x, point.y, a.x, a.y, b.x, b.y);
|
|
583
|
+
}
|
|
584
|
+
function pointToPolylineDistance(point, vertices, closed = false) {
|
|
585
|
+
const len = vertices.length;
|
|
586
|
+
if (len < 2) {
|
|
587
|
+
return Infinity;
|
|
588
|
+
}
|
|
589
|
+
const { x: px, y: py } = point;
|
|
590
|
+
if (len === 2) {
|
|
591
|
+
return Math.hypot(px - vertices[0], py - vertices[1]);
|
|
592
|
+
}
|
|
593
|
+
let min = Infinity;
|
|
594
|
+
for (let i = 0; i < len - 2; i += 2) {
|
|
595
|
+
const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
|
|
596
|
+
if (d < min) {
|
|
597
|
+
min = d;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (closed && len >= 6) {
|
|
601
|
+
const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
|
|
602
|
+
if (d < min) {
|
|
603
|
+
min = d;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return min;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function boundsOf(ring) {
|
|
610
|
+
if (ring.length < 6) {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
let minX = Infinity;
|
|
614
|
+
let minY = Infinity;
|
|
615
|
+
let maxX = -Infinity;
|
|
616
|
+
let maxY = -Infinity;
|
|
617
|
+
for (let i = 0; i < ring.length; i += 2) {
|
|
618
|
+
const x = ring[i];
|
|
619
|
+
const y = ring[i + 1];
|
|
620
|
+
if (x < minX)
|
|
621
|
+
minX = x;
|
|
622
|
+
if (y < minY)
|
|
623
|
+
minY = y;
|
|
624
|
+
if (x > maxX)
|
|
625
|
+
maxX = x;
|
|
626
|
+
if (y > maxY)
|
|
627
|
+
maxY = y;
|
|
628
|
+
}
|
|
629
|
+
return { minX, minY, maxX, maxY };
|
|
630
|
+
}
|
|
631
|
+
function bboxInside(inner, outer) {
|
|
632
|
+
return inner.minX >= outer.minX && inner.maxX <= outer.maxX && inner.minY >= outer.minY && inner.maxY <= outer.maxY;
|
|
633
|
+
}
|
|
634
|
+
function ringInsideRing(inner, outer) {
|
|
635
|
+
const n = inner.length / 2;
|
|
636
|
+
const step = Math.max(1, Math.floor(n / 9));
|
|
637
|
+
let tested = 0;
|
|
638
|
+
let inside = 0;
|
|
639
|
+
for (let i = 0; i < n; i += step) {
|
|
640
|
+
tested++;
|
|
641
|
+
if (pointInPolygon({ x: inner[i * 2], y: inner[i * 2 + 1] }, outer, "evenodd")) {
|
|
642
|
+
inside++;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return tested > 0 && inside * 2 > tested;
|
|
646
|
+
}
|
|
647
|
+
function evenoddFillRule(paths) {
|
|
648
|
+
const len = paths.length;
|
|
649
|
+
const bboxes = paths.map(boundsOf);
|
|
650
|
+
const depth = Array.from({ length: len }).fill(0);
|
|
651
|
+
const containers = paths.map(() => []);
|
|
652
|
+
for (let i = 0; i < len; i++) {
|
|
653
|
+
const bi = bboxes[i];
|
|
654
|
+
if (!bi) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
for (let j = 0; j < len; j++) {
|
|
658
|
+
if (i === j) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const bj = bboxes[j];
|
|
662
|
+
if (!bj || !bboxInside(bi, bj)) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
if (ringInsideRing(paths[i], paths[j])) {
|
|
666
|
+
depth[i]++;
|
|
667
|
+
containers[i].push(j);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return paths.map((_, i) => {
|
|
672
|
+
let parentIndex = -1;
|
|
673
|
+
let bestDepth = -1;
|
|
674
|
+
for (const j of containers[i]) {
|
|
675
|
+
if (depth[j] > bestDepth) {
|
|
676
|
+
bestDepth = depth[j];
|
|
677
|
+
parentIndex = j;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return { index: i, depth: depth[i], parentIndex };
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
495
684
|
function fillTriangulate(pointArray, options = {}) {
|
|
496
685
|
let {
|
|
497
686
|
vertices = [],
|
|
@@ -654,7 +843,7 @@ function getDirectedArea(vertices) {
|
|
|
654
843
|
function cross(ax, ay, bx, by, cx, cy) {
|
|
655
844
|
return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
656
845
|
}
|
|
657
|
-
function windingNumber
|
|
846
|
+
function windingNumber(px, py, polygon) {
|
|
658
847
|
const polygonLen = polygon.length;
|
|
659
848
|
let wn = 0;
|
|
660
849
|
for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
|
|
@@ -775,7 +964,7 @@ function nonzeroFillRule(paths) {
|
|
|
775
964
|
const wnList = [];
|
|
776
965
|
for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
|
|
777
966
|
const [x, y] = testPoints[p];
|
|
778
|
-
const winding = windingNumber
|
|
967
|
+
const winding = windingNumber(x, y, paths[j]);
|
|
779
968
|
wnMap[winding] = (wnMap[winding] ?? 0) + 1;
|
|
780
969
|
wnList.push(winding);
|
|
781
970
|
}
|
|
@@ -798,120 +987,6 @@ function nonzeroFillRule(paths) {
|
|
|
798
987
|
return results;
|
|
799
988
|
}
|
|
800
989
|
|
|
801
|
-
function isLeft(ax, ay, bx, by, px, py) {
|
|
802
|
-
return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
|
|
803
|
-
}
|
|
804
|
-
function windingNumber(px, py, vertices) {
|
|
805
|
-
const len = vertices.length;
|
|
806
|
-
let wn = 0;
|
|
807
|
-
for (let i = 0; i < len; i += 2) {
|
|
808
|
-
const ax = vertices[i];
|
|
809
|
-
const ay = vertices[i + 1];
|
|
810
|
-
const k = (i + 2) % len;
|
|
811
|
-
const bx = vertices[k];
|
|
812
|
-
const by = vertices[k + 1];
|
|
813
|
-
if (ay <= py) {
|
|
814
|
-
if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) {
|
|
815
|
-
wn++;
|
|
816
|
-
}
|
|
817
|
-
} else {
|
|
818
|
-
if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) {
|
|
819
|
-
wn--;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
return wn;
|
|
824
|
-
}
|
|
825
|
-
function crossingNumber(px, py, vertices) {
|
|
826
|
-
const len = vertices.length;
|
|
827
|
-
let cn = 0;
|
|
828
|
-
for (let i = 0; i < len; i += 2) {
|
|
829
|
-
const ax = vertices[i];
|
|
830
|
-
const ay = vertices[i + 1];
|
|
831
|
-
const k = (i + 2) % len;
|
|
832
|
-
const bx = vertices[k];
|
|
833
|
-
const by = vertices[k + 1];
|
|
834
|
-
if (ay <= py && by > py || ay > py && by <= py) {
|
|
835
|
-
const t = (py - ay) / (by - ay);
|
|
836
|
-
if (px < ax + t * (bx - ax)) {
|
|
837
|
-
cn++;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
return cn;
|
|
842
|
-
}
|
|
843
|
-
function segmentDistance(px, py, ax, ay, bx, by) {
|
|
844
|
-
const dx = bx - ax;
|
|
845
|
-
const dy = by - ay;
|
|
846
|
-
const lenSq = dx * dx + dy * dy;
|
|
847
|
-
let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
848
|
-
if (t < 0) {
|
|
849
|
-
t = 0;
|
|
850
|
-
} else if (t > 1) {
|
|
851
|
-
t = 1;
|
|
852
|
-
}
|
|
853
|
-
const cx = ax + t * dx;
|
|
854
|
-
const cy = ay + t * dy;
|
|
855
|
-
return Math.hypot(px - cx, py - cy);
|
|
856
|
-
}
|
|
857
|
-
function pointInPolygon(point, vertices, fillRule = "nonzero") {
|
|
858
|
-
if (vertices.length < 6) {
|
|
859
|
-
return false;
|
|
860
|
-
}
|
|
861
|
-
if (fillRule === "evenodd") {
|
|
862
|
-
return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
|
|
863
|
-
}
|
|
864
|
-
return windingNumber(point.x, point.y, vertices) !== 0;
|
|
865
|
-
}
|
|
866
|
-
function pointInPolygons(point, polygons, fillRule = "nonzero") {
|
|
867
|
-
const { x, y } = point;
|
|
868
|
-
if (fillRule === "evenodd") {
|
|
869
|
-
let cn = 0;
|
|
870
|
-
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
871
|
-
const ring = polygons[i];
|
|
872
|
-
if (ring.length >= 6) {
|
|
873
|
-
cn += crossingNumber(x, y, ring);
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return (cn & 1) === 1;
|
|
877
|
-
}
|
|
878
|
-
let wn = 0;
|
|
879
|
-
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
880
|
-
const ring = polygons[i];
|
|
881
|
-
if (ring.length >= 6) {
|
|
882
|
-
wn += windingNumber(x, y, ring);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
return wn !== 0;
|
|
886
|
-
}
|
|
887
|
-
function pointToSegmentDistance(point, a, b) {
|
|
888
|
-
return segmentDistance(point.x, point.y, a.x, a.y, b.x, b.y);
|
|
889
|
-
}
|
|
890
|
-
function pointToPolylineDistance(point, vertices, closed = false) {
|
|
891
|
-
const len = vertices.length;
|
|
892
|
-
if (len < 2) {
|
|
893
|
-
return Infinity;
|
|
894
|
-
}
|
|
895
|
-
const { x: px, y: py } = point;
|
|
896
|
-
if (len === 2) {
|
|
897
|
-
return Math.hypot(px - vertices[0], py - vertices[1]);
|
|
898
|
-
}
|
|
899
|
-
let min = Infinity;
|
|
900
|
-
for (let i = 0; i < len - 2; i += 2) {
|
|
901
|
-
const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
|
|
902
|
-
if (d < min) {
|
|
903
|
-
min = d;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (closed && len >= 6) {
|
|
907
|
-
const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
|
|
908
|
-
if (d < min) {
|
|
909
|
-
min = d;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
return min;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
990
|
function quadraticBezierP0(t, p) {
|
|
916
991
|
const k = 1 - t;
|
|
917
992
|
return k * k * p;
|
|
@@ -959,6 +1034,10 @@ function strokeTriangulate(points, options = {}) {
|
|
|
959
1034
|
if (points.length === 0) {
|
|
960
1035
|
return { vertices, indices };
|
|
961
1036
|
}
|
|
1037
|
+
points = dedupeConsecutivePoints(points, eps);
|
|
1038
|
+
if (points.length < 4) {
|
|
1039
|
+
return { vertices, indices };
|
|
1040
|
+
}
|
|
962
1041
|
const style = lineStyle;
|
|
963
1042
|
let alignment = style.alignment;
|
|
964
1043
|
if (lineStyle.alignment !== 0.5) {
|
|
@@ -1248,6 +1327,17 @@ function strokeTriangulate(points, options = {}) {
|
|
|
1248
1327
|
indices
|
|
1249
1328
|
};
|
|
1250
1329
|
}
|
|
1330
|
+
function dedupeConsecutivePoints(points, eps) {
|
|
1331
|
+
const out = [points[0], points[1]];
|
|
1332
|
+
for (let i = 2; i < points.length; i += 2) {
|
|
1333
|
+
const x = points[i];
|
|
1334
|
+
const y = points[i + 1];
|
|
1335
|
+
if (Math.abs(x - out[out.length - 2]) >= eps || Math.abs(y - out[out.length - 1]) >= eps) {
|
|
1336
|
+
out.push(x, y);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return out;
|
|
1340
|
+
}
|
|
1251
1341
|
function getOrientationOfPoints(points) {
|
|
1252
1342
|
const m = points.length;
|
|
1253
1343
|
if (m < 6) {
|
|
@@ -4942,14 +5032,30 @@ class Path2D extends CompositeCurve {
|
|
|
4942
5032
|
});
|
|
4943
5033
|
}
|
|
4944
5034
|
} else {
|
|
4945
|
-
this.curves.
|
|
4946
|
-
|
|
5035
|
+
const paths = this.curves.map((curve) => curve.getFillVertices(_options));
|
|
5036
|
+
const groups = evenoddFillRule(paths);
|
|
5037
|
+
const groupsLen = groups.length;
|
|
5038
|
+
for (let i = 0; i < groupsLen; i++) {
|
|
5039
|
+
const pointArray = paths[i];
|
|
5040
|
+
if ((groups[i].depth & 1) === 1 || !pointArray.length) {
|
|
5041
|
+
continue;
|
|
5042
|
+
}
|
|
5043
|
+
const _pointArray = pointArray.slice();
|
|
5044
|
+
const holes = [];
|
|
5045
|
+
for (let j = 0; j < groupsLen; j++) {
|
|
5046
|
+
if (groups[j].parentIndex === i && (groups[j].depth & 1) === 1) {
|
|
5047
|
+
holes.push(_pointArray.length / 2);
|
|
5048
|
+
_pointArray.push(...paths[j]);
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
fillTriangulate(_pointArray, {
|
|
4947
5052
|
...options,
|
|
4948
5053
|
indices,
|
|
4949
5054
|
vertices,
|
|
5055
|
+
holes,
|
|
4950
5056
|
style: { ...this.style }
|
|
4951
5057
|
});
|
|
4952
|
-
}
|
|
5058
|
+
}
|
|
4953
5059
|
}
|
|
4954
5060
|
return { indices, vertices };
|
|
4955
5061
|
}
|
|
@@ -5292,6 +5398,7 @@ exports.applyFFD = applyFFD;
|
|
|
5292
5398
|
exports.catmullRom = catmullRom;
|
|
5293
5399
|
exports.cubicBezier = cubicBezier;
|
|
5294
5400
|
exports.drawPoint = drawPoint;
|
|
5401
|
+
exports.evenoddFillRule = evenoddFillRule;
|
|
5295
5402
|
exports.fillTriangulate = fillTriangulate;
|
|
5296
5403
|
exports.getAdaptiveCubicBezierCurvePoints = getAdaptiveCubicBezierCurvePoints;
|
|
5297
5404
|
exports.getAdaptiveQuadraticBezierCurvePoints = getAdaptiveQuadraticBezierCurvePoints;
|
package/dist/index.d.cts
CHANGED
|
@@ -288,6 +288,25 @@ declare function parseCssArg(name: string, value: string, context?: ParseCssFunc
|
|
|
288
288
|
|
|
289
289
|
declare function cubicBezier(t: number, p0: number, p1: number, p2: number, p3: number): number;
|
|
290
290
|
|
|
291
|
+
interface EvenoddFillRuleResult {
|
|
292
|
+
index: number;
|
|
293
|
+
/** Containment depth: 0/2/4… are filled shells, 1/3/5… are holes. */
|
|
294
|
+
depth: number;
|
|
295
|
+
/** Immediate enclosing ring (deepest container), or -1 for a top-level ring. */
|
|
296
|
+
parentIndex: number;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Even-odd nesting of a ring soup: classify each ring by how many other rings contain it.
|
|
300
|
+
* Even depth → a filled shell; odd depth → a hole. Each ring also gets its immediate parent
|
|
301
|
+
* (the deepest container), so a shell can collect exactly the holes one level inside it — and
|
|
302
|
+
* an island inside a hole becomes its own shell again (nested donuts work).
|
|
303
|
+
*
|
|
304
|
+
* Mirrors {@link nonzeroFillRule}'s output shape, but uses containment parity (even-odd) instead
|
|
305
|
+
* of winding. Used by `Path2D.fillTriangulate` for `fillRule: 'evenodd'` so WebGL/triangulated
|
|
306
|
+
* fills get real holes instead of solid overlapping rings.
|
|
307
|
+
*/
|
|
308
|
+
declare function evenoddFillRule(paths: number[][]): EvenoddFillRuleResult[];
|
|
309
|
+
|
|
291
310
|
interface FillTriangulateOptions {
|
|
292
311
|
holes?: number[];
|
|
293
312
|
vertices?: number[];
|
|
@@ -995,5 +1014,5 @@ declare function svgToDom(svg: string | SVGElement): SVGElement;
|
|
|
995
1014
|
|
|
996
1015
|
declare function svgToPath2DSet(svg: string | SVGElement): Path2DSet;
|
|
997
1016
|
|
|
998
|
-
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
999
|
-
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|
|
1017
|
+
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, evenoddFillRule, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
1018
|
+
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, EvenoddFillRuleResult, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|
package/dist/index.d.mts
CHANGED
|
@@ -288,6 +288,25 @@ declare function parseCssArg(name: string, value: string, context?: ParseCssFunc
|
|
|
288
288
|
|
|
289
289
|
declare function cubicBezier(t: number, p0: number, p1: number, p2: number, p3: number): number;
|
|
290
290
|
|
|
291
|
+
interface EvenoddFillRuleResult {
|
|
292
|
+
index: number;
|
|
293
|
+
/** Containment depth: 0/2/4… are filled shells, 1/3/5… are holes. */
|
|
294
|
+
depth: number;
|
|
295
|
+
/** Immediate enclosing ring (deepest container), or -1 for a top-level ring. */
|
|
296
|
+
parentIndex: number;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Even-odd nesting of a ring soup: classify each ring by how many other rings contain it.
|
|
300
|
+
* Even depth → a filled shell; odd depth → a hole. Each ring also gets its immediate parent
|
|
301
|
+
* (the deepest container), so a shell can collect exactly the holes one level inside it — and
|
|
302
|
+
* an island inside a hole becomes its own shell again (nested donuts work).
|
|
303
|
+
*
|
|
304
|
+
* Mirrors {@link nonzeroFillRule}'s output shape, but uses containment parity (even-odd) instead
|
|
305
|
+
* of winding. Used by `Path2D.fillTriangulate` for `fillRule: 'evenodd'` so WebGL/triangulated
|
|
306
|
+
* fills get real holes instead of solid overlapping rings.
|
|
307
|
+
*/
|
|
308
|
+
declare function evenoddFillRule(paths: number[][]): EvenoddFillRuleResult[];
|
|
309
|
+
|
|
291
310
|
interface FillTriangulateOptions {
|
|
292
311
|
holes?: number[];
|
|
293
312
|
vertices?: number[];
|
|
@@ -995,5 +1014,5 @@ declare function svgToDom(svg: string | SVGElement): SVGElement;
|
|
|
995
1014
|
|
|
996
1015
|
declare function svgToPath2DSet(svg: string | SVGElement): Path2DSet;
|
|
997
1016
|
|
|
998
|
-
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
999
|
-
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|
|
1017
|
+
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, evenoddFillRule, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
1018
|
+
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, EvenoddFillRuleResult, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|
package/dist/index.d.ts
CHANGED
|
@@ -288,6 +288,25 @@ declare function parseCssArg(name: string, value: string, context?: ParseCssFunc
|
|
|
288
288
|
|
|
289
289
|
declare function cubicBezier(t: number, p0: number, p1: number, p2: number, p3: number): number;
|
|
290
290
|
|
|
291
|
+
interface EvenoddFillRuleResult {
|
|
292
|
+
index: number;
|
|
293
|
+
/** Containment depth: 0/2/4… are filled shells, 1/3/5… are holes. */
|
|
294
|
+
depth: number;
|
|
295
|
+
/** Immediate enclosing ring (deepest container), or -1 for a top-level ring. */
|
|
296
|
+
parentIndex: number;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Even-odd nesting of a ring soup: classify each ring by how many other rings contain it.
|
|
300
|
+
* Even depth → a filled shell; odd depth → a hole. Each ring also gets its immediate parent
|
|
301
|
+
* (the deepest container), so a shell can collect exactly the holes one level inside it — and
|
|
302
|
+
* an island inside a hole becomes its own shell again (nested donuts work).
|
|
303
|
+
*
|
|
304
|
+
* Mirrors {@link nonzeroFillRule}'s output shape, but uses containment parity (even-odd) instead
|
|
305
|
+
* of winding. Used by `Path2D.fillTriangulate` for `fillRule: 'evenodd'` so WebGL/triangulated
|
|
306
|
+
* fills get real holes instead of solid overlapping rings.
|
|
307
|
+
*/
|
|
308
|
+
declare function evenoddFillRule(paths: number[][]): EvenoddFillRuleResult[];
|
|
309
|
+
|
|
291
310
|
interface FillTriangulateOptions {
|
|
292
311
|
holes?: number[];
|
|
293
312
|
vertices?: number[];
|
|
@@ -995,5 +1014,5 @@ declare function svgToDom(svg: string | SVGElement): SVGElement;
|
|
|
995
1014
|
|
|
996
1015
|
declare function svgToPath2DSet(svg: string | SVGElement): Path2DSet;
|
|
997
1016
|
|
|
998
|
-
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
999
|
-
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|
|
1017
|
+
export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, evenoddFillRule, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase };
|
|
1018
|
+
export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, EvenoddFillRuleResult, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };
|