modern-path2d 1.8.0 → 1.8.1
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 +211 -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 +211 -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;
|
|
@@ -4942,14 +5017,30 @@ class Path2D extends CompositeCurve {
|
|
|
4942
5017
|
});
|
|
4943
5018
|
}
|
|
4944
5019
|
} else {
|
|
4945
|
-
this.curves.
|
|
4946
|
-
|
|
5020
|
+
const paths = this.curves.map((curve) => curve.getFillVertices(_options));
|
|
5021
|
+
const groups = evenoddFillRule(paths);
|
|
5022
|
+
const groupsLen = groups.length;
|
|
5023
|
+
for (let i = 0; i < groupsLen; i++) {
|
|
5024
|
+
const pointArray = paths[i];
|
|
5025
|
+
if ((groups[i].depth & 1) === 1 || !pointArray.length) {
|
|
5026
|
+
continue;
|
|
5027
|
+
}
|
|
5028
|
+
const _pointArray = pointArray.slice();
|
|
5029
|
+
const holes = [];
|
|
5030
|
+
for (let j = 0; j < groupsLen; j++) {
|
|
5031
|
+
if (groups[j].parentIndex === i && (groups[j].depth & 1) === 1) {
|
|
5032
|
+
holes.push(_pointArray.length / 2);
|
|
5033
|
+
_pointArray.push(...paths[j]);
|
|
5034
|
+
}
|
|
5035
|
+
}
|
|
5036
|
+
fillTriangulate(_pointArray, {
|
|
4947
5037
|
...options,
|
|
4948
5038
|
indices,
|
|
4949
5039
|
vertices,
|
|
5040
|
+
holes,
|
|
4950
5041
|
style: { ...this.style }
|
|
4951
5042
|
});
|
|
4952
|
-
}
|
|
5043
|
+
}
|
|
4953
5044
|
}
|
|
4954
5045
|
return { indices, vertices };
|
|
4955
5046
|
}
|
|
@@ -5292,6 +5383,7 @@ exports.applyFFD = applyFFD;
|
|
|
5292
5383
|
exports.catmullRom = catmullRom;
|
|
5293
5384
|
exports.cubicBezier = cubicBezier;
|
|
5294
5385
|
exports.drawPoint = drawPoint;
|
|
5386
|
+
exports.evenoddFillRule = evenoddFillRule;
|
|
5295
5387
|
exports.fillTriangulate = fillTriangulate;
|
|
5296
5388
|
exports.getAdaptiveCubicBezierCurvePoints = getAdaptiveCubicBezierCurvePoints;
|
|
5297
5389
|
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 };
|