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.mjs
CHANGED
|
@@ -485,6 +485,195 @@ function cubicBezier(t, p0, p1, p2, p3) {
|
|
|
485
485
|
return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
function isLeft(ax, ay, bx, by, px, py) {
|
|
489
|
+
return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
|
|
490
|
+
}
|
|
491
|
+
function windingNumber$1(px, py, vertices) {
|
|
492
|
+
const len = vertices.length;
|
|
493
|
+
let wn = 0;
|
|
494
|
+
for (let i = 0; i < len; i += 2) {
|
|
495
|
+
const ax = vertices[i];
|
|
496
|
+
const ay = vertices[i + 1];
|
|
497
|
+
const k = (i + 2) % len;
|
|
498
|
+
const bx = vertices[k];
|
|
499
|
+
const by = vertices[k + 1];
|
|
500
|
+
if (ay <= py) {
|
|
501
|
+
if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) {
|
|
502
|
+
wn++;
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) {
|
|
506
|
+
wn--;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return wn;
|
|
511
|
+
}
|
|
512
|
+
function crossingNumber(px, py, vertices) {
|
|
513
|
+
const len = vertices.length;
|
|
514
|
+
let cn = 0;
|
|
515
|
+
for (let i = 0; i < len; i += 2) {
|
|
516
|
+
const ax = vertices[i];
|
|
517
|
+
const ay = vertices[i + 1];
|
|
518
|
+
const k = (i + 2) % len;
|
|
519
|
+
const bx = vertices[k];
|
|
520
|
+
const by = vertices[k + 1];
|
|
521
|
+
if (ay <= py && by > py || ay > py && by <= py) {
|
|
522
|
+
const t = (py - ay) / (by - ay);
|
|
523
|
+
if (px < ax + t * (bx - ax)) {
|
|
524
|
+
cn++;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return cn;
|
|
529
|
+
}
|
|
530
|
+
function segmentDistance(px, py, ax, ay, bx, by) {
|
|
531
|
+
const dx = bx - ax;
|
|
532
|
+
const dy = by - ay;
|
|
533
|
+
const lenSq = dx * dx + dy * dy;
|
|
534
|
+
let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
535
|
+
if (t < 0) {
|
|
536
|
+
t = 0;
|
|
537
|
+
} else if (t > 1) {
|
|
538
|
+
t = 1;
|
|
539
|
+
}
|
|
540
|
+
const cx = ax + t * dx;
|
|
541
|
+
const cy = ay + t * dy;
|
|
542
|
+
return Math.hypot(px - cx, py - cy);
|
|
543
|
+
}
|
|
544
|
+
function pointInPolygon(point, vertices, fillRule = "nonzero") {
|
|
545
|
+
if (vertices.length < 6) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
if (fillRule === "evenodd") {
|
|
549
|
+
return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
|
|
550
|
+
}
|
|
551
|
+
return windingNumber$1(point.x, point.y, vertices) !== 0;
|
|
552
|
+
}
|
|
553
|
+
function pointInPolygons(point, polygons, fillRule = "nonzero") {
|
|
554
|
+
const { x, y } = point;
|
|
555
|
+
if (fillRule === "evenodd") {
|
|
556
|
+
let cn = 0;
|
|
557
|
+
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
558
|
+
const ring = polygons[i];
|
|
559
|
+
if (ring.length >= 6) {
|
|
560
|
+
cn += crossingNumber(x, y, ring);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return (cn & 1) === 1;
|
|
564
|
+
}
|
|
565
|
+
let wn = 0;
|
|
566
|
+
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
567
|
+
const ring = polygons[i];
|
|
568
|
+
if (ring.length >= 6) {
|
|
569
|
+
wn += windingNumber$1(x, y, ring);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return wn !== 0;
|
|
573
|
+
}
|
|
574
|
+
function pointToSegmentDistance(point, a, b) {
|
|
575
|
+
return segmentDistance(point.x, point.y, a.x, a.y, b.x, b.y);
|
|
576
|
+
}
|
|
577
|
+
function pointToPolylineDistance(point, vertices, closed = false) {
|
|
578
|
+
const len = vertices.length;
|
|
579
|
+
if (len < 2) {
|
|
580
|
+
return Infinity;
|
|
581
|
+
}
|
|
582
|
+
const { x: px, y: py } = point;
|
|
583
|
+
if (len === 2) {
|
|
584
|
+
return Math.hypot(px - vertices[0], py - vertices[1]);
|
|
585
|
+
}
|
|
586
|
+
let min = Infinity;
|
|
587
|
+
for (let i = 0; i < len - 2; i += 2) {
|
|
588
|
+
const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
|
|
589
|
+
if (d < min) {
|
|
590
|
+
min = d;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (closed && len >= 6) {
|
|
594
|
+
const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
|
|
595
|
+
if (d < min) {
|
|
596
|
+
min = d;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return min;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function boundsOf(ring) {
|
|
603
|
+
if (ring.length < 6) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
let minX = Infinity;
|
|
607
|
+
let minY = Infinity;
|
|
608
|
+
let maxX = -Infinity;
|
|
609
|
+
let maxY = -Infinity;
|
|
610
|
+
for (let i = 0; i < ring.length; i += 2) {
|
|
611
|
+
const x = ring[i];
|
|
612
|
+
const y = ring[i + 1];
|
|
613
|
+
if (x < minX)
|
|
614
|
+
minX = x;
|
|
615
|
+
if (y < minY)
|
|
616
|
+
minY = y;
|
|
617
|
+
if (x > maxX)
|
|
618
|
+
maxX = x;
|
|
619
|
+
if (y > maxY)
|
|
620
|
+
maxY = y;
|
|
621
|
+
}
|
|
622
|
+
return { minX, minY, maxX, maxY };
|
|
623
|
+
}
|
|
624
|
+
function bboxInside(inner, outer) {
|
|
625
|
+
return inner.minX >= outer.minX && inner.maxX <= outer.maxX && inner.minY >= outer.minY && inner.maxY <= outer.maxY;
|
|
626
|
+
}
|
|
627
|
+
function ringInsideRing(inner, outer) {
|
|
628
|
+
const n = inner.length / 2;
|
|
629
|
+
const step = Math.max(1, Math.floor(n / 9));
|
|
630
|
+
let tested = 0;
|
|
631
|
+
let inside = 0;
|
|
632
|
+
for (let i = 0; i < n; i += step) {
|
|
633
|
+
tested++;
|
|
634
|
+
if (pointInPolygon({ x: inner[i * 2], y: inner[i * 2 + 1] }, outer, "evenodd")) {
|
|
635
|
+
inside++;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return tested > 0 && inside * 2 > tested;
|
|
639
|
+
}
|
|
640
|
+
function evenoddFillRule(paths) {
|
|
641
|
+
const len = paths.length;
|
|
642
|
+
const bboxes = paths.map(boundsOf);
|
|
643
|
+
const depth = Array.from({ length: len }).fill(0);
|
|
644
|
+
const containers = paths.map(() => []);
|
|
645
|
+
for (let i = 0; i < len; i++) {
|
|
646
|
+
const bi = bboxes[i];
|
|
647
|
+
if (!bi) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
for (let j = 0; j < len; j++) {
|
|
651
|
+
if (i === j) {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const bj = bboxes[j];
|
|
655
|
+
if (!bj || !bboxInside(bi, bj)) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (ringInsideRing(paths[i], paths[j])) {
|
|
659
|
+
depth[i]++;
|
|
660
|
+
containers[i].push(j);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return paths.map((_, i) => {
|
|
665
|
+
let parentIndex = -1;
|
|
666
|
+
let bestDepth = -1;
|
|
667
|
+
for (const j of containers[i]) {
|
|
668
|
+
if (depth[j] > bestDepth) {
|
|
669
|
+
bestDepth = depth[j];
|
|
670
|
+
parentIndex = j;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return { index: i, depth: depth[i], parentIndex };
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
488
677
|
function fillTriangulate(pointArray, options = {}) {
|
|
489
678
|
let {
|
|
490
679
|
vertices = [],
|
|
@@ -647,7 +836,7 @@ function getDirectedArea(vertices) {
|
|
|
647
836
|
function cross(ax, ay, bx, by, cx, cy) {
|
|
648
837
|
return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
649
838
|
}
|
|
650
|
-
function windingNumber
|
|
839
|
+
function windingNumber(px, py, polygon) {
|
|
651
840
|
const polygonLen = polygon.length;
|
|
652
841
|
let wn = 0;
|
|
653
842
|
for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
|
|
@@ -768,7 +957,7 @@ function nonzeroFillRule(paths) {
|
|
|
768
957
|
const wnList = [];
|
|
769
958
|
for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
|
|
770
959
|
const [x, y] = testPoints[p];
|
|
771
|
-
const winding = windingNumber
|
|
960
|
+
const winding = windingNumber(x, y, paths[j]);
|
|
772
961
|
wnMap[winding] = (wnMap[winding] ?? 0) + 1;
|
|
773
962
|
wnList.push(winding);
|
|
774
963
|
}
|
|
@@ -791,120 +980,6 @@ function nonzeroFillRule(paths) {
|
|
|
791
980
|
return results;
|
|
792
981
|
}
|
|
793
982
|
|
|
794
|
-
function isLeft(ax, ay, bx, by, px, py) {
|
|
795
|
-
return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
|
|
796
|
-
}
|
|
797
|
-
function windingNumber(px, py, vertices) {
|
|
798
|
-
const len = vertices.length;
|
|
799
|
-
let wn = 0;
|
|
800
|
-
for (let i = 0; i < len; i += 2) {
|
|
801
|
-
const ax = vertices[i];
|
|
802
|
-
const ay = vertices[i + 1];
|
|
803
|
-
const k = (i + 2) % len;
|
|
804
|
-
const bx = vertices[k];
|
|
805
|
-
const by = vertices[k + 1];
|
|
806
|
-
if (ay <= py) {
|
|
807
|
-
if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) {
|
|
808
|
-
wn++;
|
|
809
|
-
}
|
|
810
|
-
} else {
|
|
811
|
-
if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) {
|
|
812
|
-
wn--;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return wn;
|
|
817
|
-
}
|
|
818
|
-
function crossingNumber(px, py, vertices) {
|
|
819
|
-
const len = vertices.length;
|
|
820
|
-
let cn = 0;
|
|
821
|
-
for (let i = 0; i < len; i += 2) {
|
|
822
|
-
const ax = vertices[i];
|
|
823
|
-
const ay = vertices[i + 1];
|
|
824
|
-
const k = (i + 2) % len;
|
|
825
|
-
const bx = vertices[k];
|
|
826
|
-
const by = vertices[k + 1];
|
|
827
|
-
if (ay <= py && by > py || ay > py && by <= py) {
|
|
828
|
-
const t = (py - ay) / (by - ay);
|
|
829
|
-
if (px < ax + t * (bx - ax)) {
|
|
830
|
-
cn++;
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return cn;
|
|
835
|
-
}
|
|
836
|
-
function segmentDistance(px, py, ax, ay, bx, by) {
|
|
837
|
-
const dx = bx - ax;
|
|
838
|
-
const dy = by - ay;
|
|
839
|
-
const lenSq = dx * dx + dy * dy;
|
|
840
|
-
let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
|
|
841
|
-
if (t < 0) {
|
|
842
|
-
t = 0;
|
|
843
|
-
} else if (t > 1) {
|
|
844
|
-
t = 1;
|
|
845
|
-
}
|
|
846
|
-
const cx = ax + t * dx;
|
|
847
|
-
const cy = ay + t * dy;
|
|
848
|
-
return Math.hypot(px - cx, py - cy);
|
|
849
|
-
}
|
|
850
|
-
function pointInPolygon(point, vertices, fillRule = "nonzero") {
|
|
851
|
-
if (vertices.length < 6) {
|
|
852
|
-
return false;
|
|
853
|
-
}
|
|
854
|
-
if (fillRule === "evenodd") {
|
|
855
|
-
return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
|
|
856
|
-
}
|
|
857
|
-
return windingNumber(point.x, point.y, vertices) !== 0;
|
|
858
|
-
}
|
|
859
|
-
function pointInPolygons(point, polygons, fillRule = "nonzero") {
|
|
860
|
-
const { x, y } = point;
|
|
861
|
-
if (fillRule === "evenodd") {
|
|
862
|
-
let cn = 0;
|
|
863
|
-
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
864
|
-
const ring = polygons[i];
|
|
865
|
-
if (ring.length >= 6) {
|
|
866
|
-
cn += crossingNumber(x, y, ring);
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
return (cn & 1) === 1;
|
|
870
|
-
}
|
|
871
|
-
let wn = 0;
|
|
872
|
-
for (let i = 0, len = polygons.length; i < len; i++) {
|
|
873
|
-
const ring = polygons[i];
|
|
874
|
-
if (ring.length >= 6) {
|
|
875
|
-
wn += windingNumber(x, y, ring);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return wn !== 0;
|
|
879
|
-
}
|
|
880
|
-
function pointToSegmentDistance(point, a, b) {
|
|
881
|
-
return segmentDistance(point.x, point.y, a.x, a.y, b.x, b.y);
|
|
882
|
-
}
|
|
883
|
-
function pointToPolylineDistance(point, vertices, closed = false) {
|
|
884
|
-
const len = vertices.length;
|
|
885
|
-
if (len < 2) {
|
|
886
|
-
return Infinity;
|
|
887
|
-
}
|
|
888
|
-
const { x: px, y: py } = point;
|
|
889
|
-
if (len === 2) {
|
|
890
|
-
return Math.hypot(px - vertices[0], py - vertices[1]);
|
|
891
|
-
}
|
|
892
|
-
let min = Infinity;
|
|
893
|
-
for (let i = 0; i < len - 2; i += 2) {
|
|
894
|
-
const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
|
|
895
|
-
if (d < min) {
|
|
896
|
-
min = d;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
if (closed && len >= 6) {
|
|
900
|
-
const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
|
|
901
|
-
if (d < min) {
|
|
902
|
-
min = d;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return min;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
983
|
function quadraticBezierP0(t, p) {
|
|
909
984
|
const k = 1 - t;
|
|
910
985
|
return k * k * p;
|
|
@@ -4935,14 +5010,30 @@ class Path2D extends CompositeCurve {
|
|
|
4935
5010
|
});
|
|
4936
5011
|
}
|
|
4937
5012
|
} else {
|
|
4938
|
-
this.curves.
|
|
4939
|
-
|
|
5013
|
+
const paths = this.curves.map((curve) => curve.getFillVertices(_options));
|
|
5014
|
+
const groups = evenoddFillRule(paths);
|
|
5015
|
+
const groupsLen = groups.length;
|
|
5016
|
+
for (let i = 0; i < groupsLen; i++) {
|
|
5017
|
+
const pointArray = paths[i];
|
|
5018
|
+
if ((groups[i].depth & 1) === 1 || !pointArray.length) {
|
|
5019
|
+
continue;
|
|
5020
|
+
}
|
|
5021
|
+
const _pointArray = pointArray.slice();
|
|
5022
|
+
const holes = [];
|
|
5023
|
+
for (let j = 0; j < groupsLen; j++) {
|
|
5024
|
+
if (groups[j].parentIndex === i && (groups[j].depth & 1) === 1) {
|
|
5025
|
+
holes.push(_pointArray.length / 2);
|
|
5026
|
+
_pointArray.push(...paths[j]);
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
fillTriangulate(_pointArray, {
|
|
4940
5030
|
...options,
|
|
4941
5031
|
indices,
|
|
4942
5032
|
vertices,
|
|
5033
|
+
holes,
|
|
4943
5034
|
style: { ...this.style }
|
|
4944
5035
|
});
|
|
4945
|
-
}
|
|
5036
|
+
}
|
|
4946
5037
|
}
|
|
4947
5038
|
return { indices, vertices };
|
|
4948
5039
|
}
|
|
@@ -5259,4 +5350,4 @@ function applyFFD(point, grid, width = grid.width, height = grid.height) {
|
|
|
5259
5350
|
point.set(x, y);
|
|
5260
5351
|
}
|
|
5261
5352
|
|
|
5262
|
-
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 };
|
|
5353
|
+
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 };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modern-path2d",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.1",
|
|
5
5
|
"packageManager": "pnpm@9.15.1",
|
|
6
6
|
"description": "A Path2D library, fully compatible with Web Path2D, with additional support for triangulate、animation、deformation etc.",
|
|
7
7
|
"author": "wxm",
|