modern-path2d 1.2.19 → 1.3.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
@@ -56,6 +56,11 @@ class Vector2 {
56
56
  get array() {
57
57
  return [this.x, this.y];
58
58
  }
59
+ finite() {
60
+ this.x = Number.isFinite(this.x) ? this.x : 0;
61
+ this.y = Number.isFinite(this.y) ? this.y : 0;
62
+ return this;
63
+ }
59
64
  set(x, y) {
60
65
  this.x = x;
61
66
  this.y = y;
@@ -1746,7 +1751,7 @@ class Curve {
1746
1751
  min.min(p);
1747
1752
  max.max(p);
1748
1753
  }
1749
- return { min, max };
1754
+ return { min: min.finite(), max: max.finite() };
1750
1755
  }
1751
1756
  getBoundingBox() {
1752
1757
  const { min, max } = this.getMinMax();
@@ -1782,7 +1787,7 @@ class Curve {
1782
1787
  const p1 = getPoint(indices[i]);
1783
1788
  const p2 = getPoint(indices[i + 1]);
1784
1789
  const p3 = getPoint(indices[i + 2]);
1785
- polygonStr += `<polygon points="${p1.join(",")} ${p2.join(",")} ${p3.join(",")}" fill="none" stroke="black" />`;
1790
+ polygonStr += `<polygon points="${p1.join(",")} ${p2.join(",")} ${p3.join(",")}" fill="none" stroke="black" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round" />`;
1786
1791
  }
1787
1792
  const viewBox = [min.x, min.y, max.x - min.x, max.y - min.y];
1788
1793
  return `<svg width="${viewBox[2]}" height="${viewBox[3]}" viewBox="${viewBox.join(" ")}" xmlns="http://www.w3.org/2000/svg">${polygonStr}</svg>`;
@@ -1965,76 +1970,24 @@ class RoundCurve extends Curve {
1965
1970
  return [this._center];
1966
1971
  }
1967
1972
  getAdaptivePointArray(output = []) {
1968
- const { cx, cy, rx, ry, dx, dy } = this;
1969
- if (!(rx >= 0 && ry >= 0 && dx >= 0 && dy >= 0)) {
1970
- return output;
1971
- }
1972
- const n = Math.ceil(2.3 * Math.sqrt(rx + ry));
1973
- const x = cx;
1974
- const y = cy;
1975
- const m = n * 8 + (dx ? 4 : 0) + (dy ? 4 : 0);
1976
- if (m === 0) {
1973
+ const { cx, cy, rx, ry, startAngle, endAngle, clockwise } = this;
1974
+ if (!(rx >= 0 && ry >= 0)) {
1977
1975
  return output;
1978
1976
  }
1979
- if (n === 0) {
1980
- output[0] = output[6] = x + dx;
1981
- output[1] = output[3] = y + dy;
1982
- output[2] = output[4] = x - dx;
1983
- output[5] = output[7] = y - dy;
1984
- return output;
1985
- }
1986
- let j1 = 0;
1987
- let j2 = n * 4 + (dx ? 2 : 0) + 2;
1988
- let j3 = j2;
1989
- let j4 = m;
1990
- let x0 = dx + rx;
1991
- let y0 = dy;
1992
- let x1 = x + x0;
1993
- let x2 = x - x0;
1994
- let y1 = y + y0;
1995
- output[j1++] = x1;
1996
- output[j1++] = y1;
1997
- output[--j2] = y1;
1998
- output[--j2] = x2;
1999
- if (dy) {
2000
- const y22 = y - y0;
2001
- output[j3++] = x2;
2002
- output[j3++] = y22;
2003
- output[--j4] = y22;
2004
- output[--j4] = x1;
2005
- }
2006
- for (let i = 1; i < n; i++) {
2007
- const a = Math.PI / 2 * (i / n);
2008
- const x02 = dx + Math.cos(a) * rx;
2009
- const y02 = dy + Math.sin(a) * ry;
2010
- const x12 = x + x02;
2011
- const x22 = x - x02;
2012
- const y12 = y + y02;
2013
- const y22 = y - y02;
2014
- output[j1++] = x12;
2015
- output[j1++] = y12;
2016
- output[--j2] = y12;
2017
- output[--j2] = x22;
2018
- output[j3++] = x22;
2019
- output[j3++] = y22;
2020
- output[--j4] = y22;
2021
- output[--j4] = x12;
2022
- }
2023
- x0 = dx;
2024
- y0 = dy + ry;
2025
- x1 = x + x0;
2026
- x2 = x - x0;
2027
- y1 = y + y0;
2028
- const y2 = y - y0;
2029
- output[j1++] = x1;
2030
- output[j1++] = y1;
2031
- output[--j4] = y2;
2032
- output[--j4] = x1;
2033
- if (dx) {
2034
- output[j1++] = x2;
2035
- output[j1++] = y1;
2036
- output[--j4] = y2;
2037
- output[--j4] = x2;
1977
+ let deltaAngle = endAngle - startAngle;
1978
+ if (!clockwise && deltaAngle > 0) {
1979
+ deltaAngle -= 2 * Math.PI;
1980
+ } else if (clockwise && deltaAngle < 0) {
1981
+ deltaAngle += 2 * Math.PI;
1982
+ }
1983
+ const arcLength = Math.abs(deltaAngle);
1984
+ const n = Math.max(1, Math.ceil(arcLength / (Math.PI / 16)));
1985
+ for (let i = 0; i <= n; i++) {
1986
+ const t = i / n;
1987
+ const angle = startAngle + deltaAngle * t;
1988
+ const x = cx + Math.cos(angle) * rx;
1989
+ const y = cy + Math.sin(angle) * ry;
1990
+ output.push(x, y);
2038
1991
  }
2039
1992
  return output;
2040
1993
  }
@@ -2094,7 +2047,7 @@ class RoundCurve extends Curve {
2094
2047
  min.y = Math.min(min.y, cy - halfHeight);
2095
2048
  max.x = Math.max(max.x, cx + halfWidth);
2096
2049
  max.y = Math.max(max.y, cy + halfHeight);
2097
- return { min, max };
2050
+ return { min: min.finite(), max: max.finite() };
2098
2051
  }
2099
2052
  copy(source) {
2100
2053
  super.copy(source);
@@ -2300,121 +2253,6 @@ class ArcCurve extends RoundCurve {
2300
2253
  }
2301
2254
  }
2302
2255
 
2303
- class LineCurve extends Curve {
2304
- constructor(p1 = new Vector2(), p2 = new Vector2()) {
2305
- super();
2306
- this.p1 = p1;
2307
- this.p2 = p2;
2308
- }
2309
- static from(p1x, p1y, p2x, p2y) {
2310
- return new LineCurve(
2311
- new Vector2(p1x, p1y),
2312
- new Vector2(p2x, p2y)
2313
- );
2314
- }
2315
- getPoint(t, output = new Vector2()) {
2316
- if (t === 1) {
2317
- output.copy(this.p2);
2318
- } else {
2319
- output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
2320
- }
2321
- return output;
2322
- }
2323
- getPointAt(u, output = new Vector2()) {
2324
- return this.getPoint(u, output);
2325
- }
2326
- getTangent(_t, output = new Vector2()) {
2327
- return output.subVectors(this.p2, this.p1).normalize();
2328
- }
2329
- getTangentAt(u, output = new Vector2()) {
2330
- return this.getTangent(u, output);
2331
- }
2332
- getControlPointRefs() {
2333
- return [this.p1, this.p2];
2334
- }
2335
- getAdaptivePointArray(output = []) {
2336
- output.push(
2337
- this.p1.x,
2338
- this.p1.y,
2339
- this.p2.x,
2340
- this.p2.y
2341
- );
2342
- return output;
2343
- }
2344
- getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2345
- const { p1, p2 } = this;
2346
- min.x = Math.min(min.x, p1.x, p2.x);
2347
- min.y = Math.min(min.y, p1.y, p2.y);
2348
- max.x = Math.max(max.x, p1.x, p2.x);
2349
- max.y = Math.max(max.y, p1.y, p2.y);
2350
- return { min, max };
2351
- }
2352
- toCommands() {
2353
- const { p1, p2 } = this;
2354
- return [
2355
- { type: "M", x: p1.x, y: p1.y },
2356
- { type: "L", x: p2.x, y: p2.y }
2357
- ];
2358
- }
2359
- fillTriangulate(options = {}) {
2360
- let {
2361
- vertices = [],
2362
- indices = [],
2363
- verticesStride = 2,
2364
- verticesOffset = vertices.length / verticesStride,
2365
- indicesOffset = indices.length
2366
- } = options;
2367
- const x = this.p1.x;
2368
- const y = this.p1.y;
2369
- const width = this.p2.x - this.p1.x || 1;
2370
- const height = this.p2.y - this.p2.y || 1;
2371
- const points = [
2372
- x,
2373
- y,
2374
- x + width,
2375
- y,
2376
- x + width,
2377
- y + height,
2378
- x,
2379
- y + height
2380
- ];
2381
- let count = 0;
2382
- verticesOffset *= verticesStride;
2383
- vertices[verticesOffset + count] = points[0];
2384
- vertices[verticesOffset + count + 1] = points[1];
2385
- count += verticesStride;
2386
- vertices[verticesOffset + count] = points[2];
2387
- vertices[verticesOffset + count + 1] = points[3];
2388
- count += verticesStride;
2389
- vertices[verticesOffset + count] = points[6];
2390
- vertices[verticesOffset + count + 1] = points[7];
2391
- count += verticesStride;
2392
- vertices[verticesOffset + count] = points[4];
2393
- vertices[verticesOffset + count + 1] = points[5];
2394
- count += verticesStride;
2395
- const verticesIndex = verticesOffset / verticesStride;
2396
- indices[indicesOffset++] = verticesIndex;
2397
- indices[indicesOffset++] = verticesIndex + 1;
2398
- indices[indicesOffset++] = verticesIndex + 2;
2399
- indices[indicesOffset++] = verticesIndex + 1;
2400
- indices[indicesOffset++] = verticesIndex + 3;
2401
- indices[indicesOffset++] = verticesIndex + 2;
2402
- return { vertices, indices };
2403
- }
2404
- drawTo(ctx) {
2405
- const { p1, p2 } = this;
2406
- ctx.lineTo(p1.x, p1.y);
2407
- ctx.lineTo(p2.x, p2.y);
2408
- return this;
2409
- }
2410
- copy(source) {
2411
- super.copy(source);
2412
- this.p1.copy(source.p1);
2413
- this.p2.copy(source.p2);
2414
- return this;
2415
- }
2416
- }
2417
-
2418
2256
  class CompositeCurve extends Curve {
2419
2257
  constructor(curves = []) {
2420
2258
  super();
@@ -2492,39 +2330,13 @@ class CompositeCurve extends Curve {
2492
2330
  fillTriangulate(options) {
2493
2331
  const indices = options?.indices ?? [];
2494
2332
  const vertices = options?.vertices ?? [];
2495
- const lines = [];
2496
- const fillLines = () => {
2497
- if (lines.length) {
2498
- const pointArray = [];
2499
- lines.forEach((line) => {
2500
- const x = line.p1.x;
2501
- const y = line.p1.y;
2502
- if (pointArray[pointArray.length - 2] !== x || pointArray[pointArray.length - 1] !== y) {
2503
- pointArray.push(x, y);
2504
- }
2505
- pointArray.push(line.p2.x, line.p2.y);
2506
- });
2507
- fillTriangulate(pointArray, {
2508
- ...options,
2509
- indices,
2510
- vertices
2511
- });
2512
- lines.length = 0;
2513
- }
2514
- };
2515
2333
  this.curves.forEach((curve) => {
2516
- if (curve instanceof LineCurve) {
2517
- lines.push(curve);
2518
- } else {
2519
- fillLines();
2520
- curve.fillTriangulate({
2521
- ...options,
2522
- indices,
2523
- vertices
2524
- });
2525
- }
2334
+ curve.fillTriangulate({
2335
+ ...options,
2336
+ indices,
2337
+ vertices
2338
+ });
2526
2339
  });
2527
- fillLines();
2528
2340
  return { indices, vertices };
2529
2341
  }
2530
2342
  applyTransform(transform) {
@@ -2533,7 +2345,7 @@ class CompositeCurve extends Curve {
2533
2345
  }
2534
2346
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2535
2347
  this.curves.forEach((curve) => curve.getMinMax(min, max));
2536
- return { min, max };
2348
+ return { min: min.finite(), max: max.finite() };
2537
2349
  }
2538
2350
  getBoundingBox() {
2539
2351
  const { min, max } = this.getMinMax();
@@ -2633,7 +2445,7 @@ class CubicBezierCurve extends Curve {
2633
2445
  }
2634
2446
  };
2635
2447
  samplePoints(tValues, 10);
2636
- return { min, max };
2448
+ return { min: min.finite(), max: max.finite() };
2637
2449
  }
2638
2450
  toCommands() {
2639
2451
  const { p1, cp1, cp2, p2 } = this;
@@ -2685,6 +2497,125 @@ class EllipseCurve extends RoundCurve {
2685
2497
  }
2686
2498
  }
2687
2499
 
2500
+ class LineCurve extends Curve {
2501
+ constructor(p1 = new Vector2(), p2 = new Vector2()) {
2502
+ super();
2503
+ this.p1 = p1;
2504
+ this.p2 = p2;
2505
+ }
2506
+ static from(p1x, p1y, p2x, p2y) {
2507
+ return new LineCurve(
2508
+ new Vector2(p1x, p1y),
2509
+ new Vector2(p2x, p2y)
2510
+ );
2511
+ }
2512
+ getPoint(t, output = new Vector2()) {
2513
+ if (t === 1) {
2514
+ output.copy(this.p2);
2515
+ } else {
2516
+ output.copy(this.p2).sub(this.p1).scale(t).add(this.p1);
2517
+ }
2518
+ return output;
2519
+ }
2520
+ getPointAt(u, output = new Vector2()) {
2521
+ return this.getPoint(u, output);
2522
+ }
2523
+ getTangent(_t, output = new Vector2()) {
2524
+ return output.subVectors(this.p2, this.p1).normalize();
2525
+ }
2526
+ getTangentAt(u, output = new Vector2()) {
2527
+ return this.getTangent(u, output);
2528
+ }
2529
+ getControlPointRefs() {
2530
+ return [this.p1, this.p2];
2531
+ }
2532
+ getAdaptivePointArray(output = []) {
2533
+ output.push(
2534
+ this.p1.x,
2535
+ this.p1.y,
2536
+ this.p2.x,
2537
+ this.p2.y
2538
+ );
2539
+ return output;
2540
+ }
2541
+ getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
2542
+ const { p1, p2 } = this;
2543
+ min.x = Math.min(min.x, p1.x, p2.x);
2544
+ min.y = Math.min(min.y, p1.y, p2.y);
2545
+ max.x = Math.max(max.x, p1.x, p2.x);
2546
+ max.y = Math.max(max.y, p1.y, p2.y);
2547
+ return { min: min.finite(), max: max.finite() };
2548
+ }
2549
+ toCommands() {
2550
+ const { p1, p2 } = this;
2551
+ return [
2552
+ { type: "M", x: p1.x, y: p1.y },
2553
+ { type: "L", x: p2.x, y: p2.y }
2554
+ ];
2555
+ }
2556
+ fillTriangulate(options = {}) {
2557
+ let {
2558
+ vertices = [],
2559
+ indices = [],
2560
+ verticesStride = 2,
2561
+ verticesOffset = vertices.length / verticesStride,
2562
+ indicesOffset = indices.length
2563
+ } = options;
2564
+ const minX = Math.min(this.p1.x, this.p2.x);
2565
+ const maxX = Math.max(this.p1.x, this.p2.x);
2566
+ const minY = Math.min(this.p1.y, this.p2.y);
2567
+ const maxY = Math.max(this.p1.y, this.p2.y);
2568
+ const x = minX;
2569
+ const y = minY;
2570
+ const width = maxX - minX;
2571
+ const height = maxY - minY;
2572
+ const points = [
2573
+ x,
2574
+ y,
2575
+ x + width,
2576
+ y,
2577
+ x + width,
2578
+ y + height,
2579
+ x,
2580
+ y + height
2581
+ ];
2582
+ let count = 0;
2583
+ verticesOffset *= verticesStride;
2584
+ vertices[verticesOffset + count] = points[0];
2585
+ vertices[verticesOffset + count + 1] = points[1];
2586
+ count += verticesStride;
2587
+ vertices[verticesOffset + count] = points[2];
2588
+ vertices[verticesOffset + count + 1] = points[3];
2589
+ count += verticesStride;
2590
+ vertices[verticesOffset + count] = points[6];
2591
+ vertices[verticesOffset + count + 1] = points[7];
2592
+ count += verticesStride;
2593
+ vertices[verticesOffset + count] = points[4];
2594
+ vertices[verticesOffset + count + 1] = points[5];
2595
+ count += verticesStride;
2596
+ const verticesIndex = verticesOffset / verticesStride;
2597
+ indices[indicesOffset++] = verticesIndex;
2598
+ indices[indicesOffset++] = verticesIndex + 1;
2599
+ indices[indicesOffset++] = verticesIndex + 2;
2600
+ indices[indicesOffset++] = verticesIndex + 1;
2601
+ indices[indicesOffset++] = verticesIndex + 3;
2602
+ indices[indicesOffset++] = verticesIndex + 2;
2603
+ return { vertices, indices };
2604
+ }
2605
+ drawTo(ctx) {
2606
+ const { p1, p2 } = this;
2607
+ ctx.lineTo(p1.x, p1.y);
2608
+ ctx.lineTo(p2.x, p2.y);
2609
+ return this;
2610
+ }
2611
+ copy(source) {
2612
+ super.copy(source);
2613
+ this.p1.copy(source.p1);
2614
+ this.p2.copy(source.p2);
2615
+ return this;
2616
+ }
2617
+ }
2618
+
2688
2619
  class PloygonCurve extends CompositeCurve {
2689
2620
  //
2690
2621
  }
@@ -2780,7 +2711,7 @@ class QuadraticBezierCurve extends Curve {
2780
2711
  min.y = Math.min(min.y, p1.y, p2.y, y1, y2);
2781
2712
  max.x = Math.max(max.x, p1.x, p2.x, x1, x2);
2782
2713
  max.y = Math.max(max.y, p1.y, p2.y, y1, y2);
2783
- return { min, max };
2714
+ return { min: min.finite(), max: max.finite() };
2784
2715
  }
2785
2716
  toCommands() {
2786
2717
  const { p1, cp, p2 } = this;
@@ -3422,7 +3353,7 @@ class Path2D extends CompositeCurve {
3422
3353
  }
3423
3354
  }
3424
3355
  });
3425
- return { min, max };
3356
+ return { min: min.finite(), max: max.finite() };
3426
3357
  }
3427
3358
  strokeTriangulate(options) {
3428
3359
  const indices = options?.indices ?? [];
@@ -3558,6 +3489,61 @@ class Path2DSet {
3558
3489
  }
3559
3490
  }
3560
3491
 
3492
+ class FFDControlGrid {
3493
+ constructor(rows, cols, width = 1, height = 1) {
3494
+ this.rows = rows;
3495
+ this.cols = cols;
3496
+ this.width = width;
3497
+ this.height = height;
3498
+ for (let i = 0; i < rows; i++) {
3499
+ this.controlPoints[i] = [];
3500
+ for (let j = 0; j < cols; j++) {
3501
+ this.controlPoints[i][j] = {
3502
+ x: j / (cols - 1) * width,
3503
+ y: i / (rows - 1) * height
3504
+ };
3505
+ }
3506
+ }
3507
+ }
3508
+ controlPoints = [];
3509
+ moveControlPoint(i, j, dx, dy) {
3510
+ this.controlPoints[i][j].x += dx;
3511
+ this.controlPoints[i][j].y += dy;
3512
+ return this;
3513
+ }
3514
+ }
3515
+ function bsplineBasis(t) {
3516
+ const B = [];
3517
+ B[0] = (1 - t) ** 3 / 6;
3518
+ B[1] = (3 * t ** 3 - 6 * t ** 2 + 4) / 6;
3519
+ B[2] = (-3 * t ** 3 + 3 * t ** 2 + 3 * t + 1) / 6;
3520
+ B[3] = t ** 3 / 6;
3521
+ return B;
3522
+ }
3523
+ function applyFFD(point, grid, width = grid.width, height = grid.height) {
3524
+ const s = point.x / width * (grid.cols - 1);
3525
+ const t = point.y / height * (grid.rows - 1);
3526
+ const i = Math.floor(s);
3527
+ const j = Math.floor(t);
3528
+ const u = s - i;
3529
+ const v = t - j;
3530
+ const bu = bsplineBasis(u);
3531
+ const bv = bsplineBasis(v);
3532
+ let x = 0;
3533
+ let y = 0;
3534
+ for (let m = 0; m < 4; m++) {
3535
+ for (let n = 0; n < 4; n++) {
3536
+ const row = Math.min(Math.max(j - 1 + m, 0), grid.rows - 1);
3537
+ const col = Math.min(Math.max(i - 1 + n, 0), grid.cols - 1);
3538
+ const cp = grid.controlPoints[row][col];
3539
+ const weight = bu[n] * bv[m];
3540
+ x += cp.x * weight;
3541
+ y += cp.y * weight;
3542
+ }
3543
+ }
3544
+ point.set(x, y);
3545
+ }
3546
+
3561
3547
  const dataUri = "data:image/svg+xml;";
3562
3548
  const base64DataUri = `${dataUri}base64,`;
3563
3549
  const utf8DataUri = `${dataUri}charset=utf8,`;
@@ -4087,4 +4073,4 @@ function svgToPath2DSet(svg) {
4087
4073
  );
4088
4074
  }
4089
4075
 
4090
- export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Vector2, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, parseArcCommand, parsePathDataArgs, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDOM, svgToPath2DSet };
4076
+ export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPloygonCurve, FFDControlGrid, LineCurve, Matrix3, Path2D, Path2DSet, PloygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, parseArcCommand, parsePathDataArgs, quadraticBezier, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDOM, svgToPath2DSet };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "modern-path2d",
3
3
  "type": "module",
4
- "version": "1.2.19",
4
+ "version": "1.3.1",
5
5
  "packageManager": "pnpm@9.15.1",
6
- "description": "A modern Path2D library, fully compatible with Web Path2D, with additional support for path animation, path deformation, path playback, etc.",
6
+ "description": "A Path2D library, fully compatible with Web Path2D, with additional support for triangulate、animationdeformation etc.",
7
7
  "author": "wxm",
8
8
  "license": "MIT",
9
9
  "homepage": "https://github.com/qq15725/modern-path2d",