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.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$1(px, py, polygon) {
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$1(x, y, paths[j]);
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.forEach((curve) => {
4939
- curve.fillTriangulate({
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.0",
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",