@mce/bigesj 0.18.3 → 0.18.5

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.js CHANGED
@@ -1626,7 +1626,7 @@ function signedArea(data, start, end, dim) {
1626
1626
  return sum;
1627
1627
  }
1628
1628
  //#endregion
1629
- //#region ../../node_modules/.pnpm/modern-path2d@1.6.0/node_modules/modern-path2d/dist/index.mjs
1629
+ //#region ../../node_modules/.pnpm/modern-path2d@1.7.0/node_modules/modern-path2d/dist/index.mjs
1630
1630
  function drawPoint(ctx, x, y, options = {}) {
1631
1631
  const { radius = 1 } = options;
1632
1632
  ctx.moveTo(x, y);
@@ -1732,7 +1732,7 @@ var Vector2 = class Vector2 {
1732
1732
  return this.set(this._x * x, this._y * y);
1733
1733
  }
1734
1734
  divide(x = 0, y = x) {
1735
- return this.set(this._x / x, this._y / y);
1735
+ return this.set(x === 0 ? this._x : this._x / x, y === 0 ? this._y : this._y / y);
1736
1736
  }
1737
1737
  cross(p) {
1738
1738
  return this._x * p.y - this._y * p.x;
@@ -1910,9 +1910,9 @@ function getIntersectionPoint(p1, p2, q1, q2) {
1910
1910
  const s = q2.clone().sub(q1);
1911
1911
  const q1p1 = q1.clone().sub(p1);
1912
1912
  const crossRS = r.cross(s);
1913
- if (crossRS === 0) return new Vector2((p1.x + q1.x) / 2, (p1.y + q1.y) / 2);
1913
+ if (crossRS === 0) return null;
1914
1914
  const t = q1p1.cross(s) / crossRS;
1915
- if (Math.abs(t) > 1) return new Vector2((p1.x + q1.x) / 2, (p1.y + q1.y) / 2);
1915
+ if (Math.abs(t) > 1) return null;
1916
1916
  return new Vector2(p1.x + t * r.x, p1.y + t * r.y);
1917
1917
  }
1918
1918
  var FUNCTIONS_RE = /([\w-]+)\((.+?)\)/g;
@@ -2118,7 +2118,7 @@ function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
2118
2118
  function cross(ax, ay, bx, by, cx, cy) {
2119
2119
  return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
2120
2120
  }
2121
- function windingNumber(px, py, polygon) {
2121
+ function windingNumber$1(px, py, polygon) {
2122
2122
  const polygonLen = polygon.length;
2123
2123
  let wn = 0;
2124
2124
  for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
@@ -2137,11 +2137,18 @@ function distance(p1, p2) {
2137
2137
  const dy = p2[1] - p1[1];
2138
2138
  return Math.sqrt(dx * dx + dy * dy);
2139
2139
  }
2140
+ function aabbIntersects(a, b) {
2141
+ return a.minX <= b.maxX && a.maxX >= b.minX && a.minY <= b.maxY && a.maxY >= b.minY;
2142
+ }
2140
2143
  function nonzeroFillRule(paths) {
2141
2144
  const results = paths.map((_, i) => ({ index: i }));
2142
- const testPointsGroups = paths.map((path) => {
2145
+ const bboxes = [];
2146
+ const testPointsGroups = paths.map((path, pathIndex) => {
2143
2147
  const len = path.length;
2144
- if (!len) return [];
2148
+ if (!len) {
2149
+ bboxes[pathIndex] = null;
2150
+ return [];
2151
+ }
2145
2152
  let xMinYAuto = [Number.MAX_SAFE_INTEGER, 0];
2146
2153
  let xAutoYMin = [0, Number.MAX_SAFE_INTEGER];
2147
2154
  let xMaxYAuto = [Number.MIN_SAFE_INTEGER, 0];
@@ -2154,6 +2161,12 @@ function nonzeroFillRule(paths) {
2154
2161
  if (xMaxYAuto[0] < x) xMaxYAuto = [x, y];
2155
2162
  if (xAutoYMax[1] < y) xAutoYMax = [x, y];
2156
2163
  }
2164
+ bboxes[pathIndex] = {
2165
+ minX: xMinYAuto[0],
2166
+ minY: xAutoYMin[1],
2167
+ maxX: xMaxYAuto[0],
2168
+ maxY: xAutoYMax[1]
2169
+ };
2157
2170
  const mid = [(xMinYAuto[0] + xMaxYAuto[0]) / 2, (xAutoYMin[1] + xAutoYMax[1]) / 2];
2158
2171
  let xMidYMinDx;
2159
2172
  let xMidYMaxDx;
@@ -2199,13 +2212,16 @@ function nonzeroFillRule(paths) {
2199
2212
  for (let i = 0, len = paths.length; i < len; i++) {
2200
2213
  const _results = [];
2201
2214
  const testPoints = testPointsGroups[i];
2215
+ const boxI = bboxes[i];
2202
2216
  for (let j = 0; j < len; j++) {
2203
2217
  if (i === j) continue;
2218
+ const boxJ = bboxes[j];
2219
+ if (!boxI || !boxJ || !aabbIntersects(boxI, boxJ)) continue;
2204
2220
  const wnMap = {};
2205
2221
  const wnList = [];
2206
2222
  for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
2207
2223
  const [x, y] = testPoints[p];
2208
- const winding = windingNumber(x, y, paths[j]);
2224
+ const winding = windingNumber$1(x, y, paths[j]);
2209
2225
  wnMap[winding] = (wnMap[winding] ?? 0) + 1;
2210
2226
  wnList.push(winding);
2211
2227
  }
@@ -2223,6 +2239,88 @@ function nonzeroFillRule(paths) {
2223
2239
  }
2224
2240
  return results;
2225
2241
  }
2242
+ function isLeft(ax, ay, bx, by, px, py) {
2243
+ return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
2244
+ }
2245
+ function windingNumber(px, py, vertices) {
2246
+ const len = vertices.length;
2247
+ let wn = 0;
2248
+ for (let i = 0; i < len; i += 2) {
2249
+ const ax = vertices[i];
2250
+ const ay = vertices[i + 1];
2251
+ const k = (i + 2) % len;
2252
+ const bx = vertices[k];
2253
+ const by = vertices[k + 1];
2254
+ if (ay <= py) {
2255
+ if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) wn++;
2256
+ } else if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) wn--;
2257
+ }
2258
+ return wn;
2259
+ }
2260
+ function crossingNumber(px, py, vertices) {
2261
+ const len = vertices.length;
2262
+ let cn = 0;
2263
+ for (let i = 0; i < len; i += 2) {
2264
+ const ax = vertices[i];
2265
+ const ay = vertices[i + 1];
2266
+ const k = (i + 2) % len;
2267
+ const bx = vertices[k];
2268
+ const by = vertices[k + 1];
2269
+ if (ay <= py && by > py || ay > py && by <= py) {
2270
+ if (px < ax + (py - ay) / (by - ay) * (bx - ax)) cn++;
2271
+ }
2272
+ }
2273
+ return cn;
2274
+ }
2275
+ function segmentDistance(px, py, ax, ay, bx, by) {
2276
+ const dx = bx - ax;
2277
+ const dy = by - ay;
2278
+ const lenSq = dx * dx + dy * dy;
2279
+ let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
2280
+ if (t < 0) t = 0;
2281
+ else if (t > 1) t = 1;
2282
+ const cx = ax + t * dx;
2283
+ const cy = ay + t * dy;
2284
+ return Math.hypot(px - cx, py - cy);
2285
+ }
2286
+ function pointInPolygon(point, vertices, fillRule = "nonzero") {
2287
+ if (vertices.length < 6) return false;
2288
+ if (fillRule === "evenodd") return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
2289
+ return windingNumber(point.x, point.y, vertices) !== 0;
2290
+ }
2291
+ function pointInPolygons(point, polygons, fillRule = "nonzero") {
2292
+ const { x, y } = point;
2293
+ if (fillRule === "evenodd") {
2294
+ let cn = 0;
2295
+ for (let i = 0, len = polygons.length; i < len; i++) {
2296
+ const ring = polygons[i];
2297
+ if (ring.length >= 6) cn += crossingNumber(x, y, ring);
2298
+ }
2299
+ return (cn & 1) === 1;
2300
+ }
2301
+ let wn = 0;
2302
+ for (let i = 0, len = polygons.length; i < len; i++) {
2303
+ const ring = polygons[i];
2304
+ if (ring.length >= 6) wn += windingNumber(x, y, ring);
2305
+ }
2306
+ return wn !== 0;
2307
+ }
2308
+ function pointToPolylineDistance(point, vertices, closed = false) {
2309
+ const len = vertices.length;
2310
+ if (len < 2) return Infinity;
2311
+ const { x: px, y: py } = point;
2312
+ if (len === 2) return Math.hypot(px - vertices[0], py - vertices[1]);
2313
+ let min = Infinity;
2314
+ for (let i = 0; i < len - 2; i += 2) {
2315
+ const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
2316
+ if (d < min) min = d;
2317
+ }
2318
+ if (closed && len >= 6) {
2319
+ const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
2320
+ if (d < min) min = d;
2321
+ }
2322
+ return min;
2323
+ }
2226
2324
  function quadraticBezierP0(t, p) {
2227
2325
  const k = 1 - t;
2228
2326
  return k * k * p;
@@ -3728,6 +3826,39 @@ function svgToPath2DSet(svg) {
3728
3826
  var Curve = class {
3729
3827
  arcLengthDivision = 200;
3730
3828
  _lengths = [];
3829
+ _adaptiveCache;
3830
+ /**
3831
+ * Parent composite, set lazily when a composite caches its children. Lets
3832
+ * {@link invalidate} propagate up so an ancestor's caches refresh too.
3833
+ */
3834
+ _owner;
3835
+ _invalidating = false;
3836
+ /**
3837
+ * Drop cached arc lengths and the cached sampled outline used by hit testing, then
3838
+ * bubble up to {@link _owner}. Called automatically by {@link applyTransform} and the
3839
+ * `Path2D` mutators; call it manually after mutating control-point coordinates in place —
3840
+ * the caches cannot observe such mutations.
3841
+ */
3842
+ invalidate() {
3843
+ if (this._invalidating) return this;
3844
+ this._invalidating = true;
3845
+ this._invalidateSelf();
3846
+ this._owner?.invalidate();
3847
+ this._invalidating = false;
3848
+ return this;
3849
+ }
3850
+ /** Clears this curve's own caches. Composites also clear their children (see override). */
3851
+ _invalidateSelf() {
3852
+ this._lengths.length = 0;
3853
+ this._adaptiveCache = void 0;
3854
+ }
3855
+ /**
3856
+ * Sampled outline cached for repeated hit tests (read-only — do not mutate the result).
3857
+ * Invalidated by {@link invalidate}.
3858
+ */
3859
+ _getCachedAdaptiveVertices() {
3860
+ return this._adaptiveCache ??= this.getAdaptiveVertices();
3861
+ }
3731
3862
  getPointAt(u, output = new Vector2()) {
3732
3863
  return this.getPoint(this.getUToTMapping(u), output);
3733
3864
  }
@@ -3743,6 +3874,7 @@ var Curve = class {
3743
3874
  if (isFunction) transform(p);
3744
3875
  else transform.apply(p, p);
3745
3876
  });
3877
+ this.invalidate();
3746
3878
  return this;
3747
3879
  }
3748
3880
  getUnevenVertices(count = 5, output = []) {
@@ -3869,12 +4001,21 @@ var Curve = class {
3869
4001
  return mid;
3870
4002
  }
3871
4003
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
3872
- const potins = this.getPoints();
3873
- for (let i = 0, len = potins.length; i < len; i++) {
3874
- const p = potins[i];
3875
- min.clampMin(p);
3876
- max.clampMax(p);
4004
+ const vertices = this.getAdaptiveVertices();
4005
+ let minX = min.x;
4006
+ let minY = min.y;
4007
+ let maxX = max.x;
4008
+ let maxY = max.y;
4009
+ for (let i = 0, len = vertices.length; i < len; i += 2) {
4010
+ const x = vertices[i];
4011
+ const y = vertices[i + 1];
4012
+ if (x < minX) minX = x;
4013
+ if (y < minY) minY = y;
4014
+ if (x > maxX) maxX = x;
4015
+ if (y > maxY) maxY = y;
3877
4016
  }
4017
+ min.set(minX, minY);
4018
+ max.set(maxX, maxY);
3878
4019
  return {
3879
4020
  min: min.finite(),
3880
4021
  max: max.finite()
@@ -3884,6 +4025,42 @@ var Curve = class {
3884
4025
  const { min, max } = this.getMinMax();
3885
4026
  return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
3886
4027
  }
4028
+ /**
4029
+ * Test whether a point lies inside the area enclosed by this curve.
4030
+ *
4031
+ * The curve is sampled via {@link getAdaptiveVertices} into a single implicitly closed
4032
+ * ring. This is purely geometric (it ignores any `fill`/`stroke` style), mirroring
4033
+ * `CanvasRenderingContext2D.isPointInPath`.
4034
+ *
4035
+ * Composites that hold multiple sub-paths (e.g. {@link Path2D}) override this so holes
4036
+ * are honored — a single `Curve` is always one ring.
4037
+ */
4038
+ isPointInFill(point, options = {}) {
4039
+ return pointInPolygon(point, this._getCachedAdaptiveVertices(), options.fillRule);
4040
+ }
4041
+ /**
4042
+ * Test whether a point lies on this curve's stroke, i.e. within `strokeWidth / 2 + tolerance`
4043
+ * of the sampled outline. The point must be in the same coordinate space as the curve.
4044
+ *
4045
+ * Options: `strokeWidth` (path units, default `1`), `tolerance` (extra hit slack in path
4046
+ * units, default `0` — useful for thin strokes; no coordinate scaling is assumed, so convert
4047
+ * pixel tolerance to path units upstream if your path is normalized), and `closed` (whether
4048
+ * to include the closing edge from the last vertex back to the first).
4049
+ */
4050
+ isPointInStroke(point, options = {}) {
4051
+ const { strokeWidth = 1, tolerance = 0, closed = false } = options;
4052
+ return pointToPolylineDistance(point, this._getCachedAdaptiveVertices(), closed) <= strokeWidth / 2 + tolerance;
4053
+ }
4054
+ /**
4055
+ * Concise PathKit-style fill containment test: `contains(x, y)` is shorthand for
4056
+ * {@link isPointInFill} with a `{ x, y }` point.
4057
+ */
4058
+ contains(x, y, options = {}) {
4059
+ return this.isPointInFill({
4060
+ x,
4061
+ y
4062
+ }, options);
4063
+ }
3887
4064
  getFillVertices(_options) {
3888
4065
  return this.getAdaptiveVertices();
3889
4066
  }
@@ -4013,6 +4190,64 @@ var RoundCurve = class extends Curve {
4013
4190
  }
4014
4191
  return output.set(_x, _y);
4015
4192
  }
4193
+ /**
4194
+ * Point on the ellipse at an absolute angle (mirrors {@link getPoint}'s parameterization,
4195
+ * ignoring `_diff`).
4196
+ */
4197
+ _pointAtAngle(angle, output) {
4198
+ let x = this.cx + this.rx * Math.cos(angle);
4199
+ let y = this.cy + this.ry * Math.sin(angle);
4200
+ if (this.rotate !== 0) {
4201
+ const cos = Math.cos(this.rotate);
4202
+ const sin = Math.sin(this.rotate);
4203
+ const tx = x - this.cx;
4204
+ const ty = y - this.cy;
4205
+ x = tx * cos - ty * sin + this.cx;
4206
+ y = tx * sin + ty * cos + this.cy;
4207
+ }
4208
+ return output.set(x, y);
4209
+ }
4210
+ /**
4211
+ * Analytical bounds of the (elliptical) arc: the start/end points plus the per-axis
4212
+ * extrema angles that fall within the swept interval. Matches {@link getPoint}, so it is
4213
+ * exact for `ArcCurve`/`EllipseCurve`. The `_diff` offset (used only by the legacy
4214
+ * `_getAdaptiveVerticesByCircle` path) is intentionally ignored here.
4215
+ */
4216
+ getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
4217
+ const { startAngle, rotate } = this;
4218
+ const delta = this._getDeltaAngle();
4219
+ const cosT = Math.cos(rotate);
4220
+ const sinT = Math.sin(rotate);
4221
+ const p = tempV2;
4222
+ let minX = min.x;
4223
+ let minY = min.y;
4224
+ let maxX = max.x;
4225
+ let maxY = max.y;
4226
+ const consider = (angle) => {
4227
+ this._pointAtAngle(angle, p);
4228
+ if (p.x < minX) minX = p.x;
4229
+ if (p.y < minY) minY = p.y;
4230
+ if (p.x > maxX) maxX = p.x;
4231
+ if (p.y > maxY) maxY = p.y;
4232
+ };
4233
+ consider(startAngle);
4234
+ consider(startAngle + delta);
4235
+ const ax = Math.atan2(-this.ry * sinT, this.rx * cosT);
4236
+ const ay = Math.atan2(this.ry * cosT, this.rx * sinT);
4237
+ const bases = [
4238
+ ax,
4239
+ ax + Math.PI,
4240
+ ay,
4241
+ ay + Math.PI
4242
+ ];
4243
+ for (let i = 0; i < 4; i++) if (angleInSweep(bases[i], startAngle, delta)) consider(bases[i]);
4244
+ min.set(minX, minY);
4245
+ max.set(maxX, maxY);
4246
+ return {
4247
+ min: min.finite(),
4248
+ max: max.finite()
4249
+ };
4250
+ }
4016
4251
  toCommands() {
4017
4252
  const { cx, cy, rx, ry, startAngle, endAngle, clockwise, rotate } = this;
4018
4253
  const startX = cx + rx * Math.cos(startAngle) * Math.cos(rotate) - ry * Math.sin(startAngle) * Math.sin(rotate);
@@ -4083,6 +4318,7 @@ var RoundCurve = class extends Curve {
4083
4318
  this.cy = tempV2.y;
4084
4319
  if (isTransformSkewed(transform)) transfEllipseGeneric(this, transform);
4085
4320
  else transfEllipseNoSkew(this, transform);
4321
+ this.invalidate();
4086
4322
  return this;
4087
4323
  }
4088
4324
  getControlPointRefs() {
@@ -4212,6 +4448,18 @@ var RoundCurve = class extends Curve {
4212
4448
  return this;
4213
4449
  }
4214
4450
  };
4451
+ function angleInSweep(a, start, delta) {
4452
+ const PI_2 = Math.PI * 2;
4453
+ const eps = 1e-9;
4454
+ if (Math.abs(delta) >= PI_2 - eps) return true;
4455
+ let off = (a - start) % PI_2;
4456
+ if (delta >= 0) {
4457
+ if (off < -1e-9) off += PI_2;
4458
+ return off >= -1e-9 && off <= delta + eps;
4459
+ }
4460
+ if (off > eps) off -= PI_2;
4461
+ return off <= eps && off >= delta - eps;
4462
+ }
4215
4463
  function transfEllipseGeneric(curve, m) {
4216
4464
  const a = curve.rx;
4217
4465
  const b = curve.ry;
@@ -4412,6 +4660,22 @@ var CompositeCurve = class CompositeCurve extends Curve {
4412
4660
  super();
4413
4661
  this.curves = curves;
4414
4662
  }
4663
+ _adaptiveCacheLen = -1;
4664
+ _invalidateSelf() {
4665
+ super._invalidateSelf();
4666
+ this._adaptiveCacheLen = -1;
4667
+ this.curves.forEach((curve) => curve.invalidate());
4668
+ }
4669
+ _getCachedAdaptiveVertices() {
4670
+ if (!this._adaptiveCache || this._adaptiveCacheLen !== this.curves.length) {
4671
+ this.curves.forEach((curve) => {
4672
+ curve._owner = this;
4673
+ });
4674
+ this._adaptiveCache = this.getAdaptiveVertices();
4675
+ this._adaptiveCacheLen = this.curves.length;
4676
+ }
4677
+ return this._adaptiveCache;
4678
+ }
4415
4679
  getFlatCurves() {
4416
4680
  return this.curves.flatMap((curve) => {
4417
4681
  if (curve instanceof CompositeCurve) return curve.getFlatCurves();
@@ -4446,6 +4710,7 @@ var CompositeCurve = class CompositeCurve extends Curve {
4446
4710
  updateLengths() {
4447
4711
  const lengths = [];
4448
4712
  for (let i = 0, sum = 0, len = this.curves.length; i < len; i++) {
4713
+ this.curves[i]._owner = this;
4449
4714
  sum += this.curves[i].getLength();
4450
4715
  lengths.push(sum);
4451
4716
  }
@@ -4499,7 +4764,10 @@ var CompositeCurve = class CompositeCurve extends Curve {
4499
4764
  }
4500
4765
  }
4501
4766
  applyTransform(transform) {
4767
+ this._invalidating = true;
4502
4768
  this.curves.forEach((curve) => curve.applyTransform(transform));
4769
+ this._invalidating = false;
4770
+ this.invalidate();
4503
4771
  return this;
4504
4772
  }
4505
4773
  getMinMax(min = Vector2.MAX, max = Vector2.MIN) {
@@ -4756,7 +5024,7 @@ var RectangleCurve = class extends PolygonCurve {
4756
5024
  return this;
4757
5025
  }
4758
5026
  };
4759
- var RoundRectangleCurve = class extends RoundCurve {
5027
+ var RoundRectangleCurve = class extends CompositeCurve {
4760
5028
  constructor(x = 0, y = 0, width = 1, height = 1, radius = 1) {
4761
5029
  super();
4762
5030
  this.x = x;
@@ -4767,25 +5035,44 @@ var RoundRectangleCurve = class extends RoundCurve {
4767
5035
  this.update();
4768
5036
  }
4769
5037
  update() {
4770
- const { x, y, width, height, radius } = this;
4771
- const halfWidth = width / 2;
4772
- const halfHeight = height / 2;
4773
- const cx = x + halfWidth;
4774
- const cy = y + halfHeight;
4775
- const rx = Math.max(0, Math.min(radius, Math.min(halfWidth, halfHeight)));
4776
- const ry = rx;
4777
- this._center = new Vector2(cx, cy);
4778
- this._radius = new Vector2(rx, ry);
4779
- this._diff = new Vector2(halfWidth - rx, halfHeight - ry);
5038
+ const { x, y, width, height } = this;
5039
+ const r = Math.max(0, Math.min(this.radius, Math.abs(width) / 2, Math.abs(height) / 2));
5040
+ const x0 = x;
5041
+ const x1 = x + r;
5042
+ const x2 = x + width - r;
5043
+ const x3 = x + width;
5044
+ const y0 = y;
5045
+ const y1 = y + r;
5046
+ const y2 = y + height - r;
5047
+ const y3 = y + height;
5048
+ if (r <= 0) this.curves = [
5049
+ LineCurve.from(x0, y0, x3, y0),
5050
+ LineCurve.from(x3, y0, x3, y3),
5051
+ LineCurve.from(x3, y3, x0, y3),
5052
+ LineCurve.from(x0, y3, x0, y0)
5053
+ ];
5054
+ else {
5055
+ const HALF_PI = Math.PI / 2;
5056
+ this.curves = [
5057
+ LineCurve.from(x1, y0, x2, y0),
5058
+ new ArcCurve(x2, y1, r, -HALF_PI, 0, true),
5059
+ LineCurve.from(x3, y1, x3, y2),
5060
+ new ArcCurve(x2, y2, r, 0, HALF_PI, true),
5061
+ LineCurve.from(x2, y3, x1, y3),
5062
+ new ArcCurve(x1, y2, r, HALF_PI, Math.PI, true),
5063
+ LineCurve.from(x0, y2, x0, y1),
5064
+ new ArcCurve(x1, y1, r, Math.PI, Math.PI * 1.5, true)
5065
+ ];
5066
+ }
5067
+ this.invalidate();
4780
5068
  return this;
4781
5069
  }
4782
5070
  drawTo(ctx) {
4783
- const { x, y, width, height, radius } = this;
4784
- ctx.roundRect(x, y, width, height, radius);
5071
+ ctx.roundRect(this.x, this.y, this.width, this.height, this.radius);
4785
5072
  return this;
4786
5073
  }
4787
5074
  copyFrom(source) {
4788
- super.copyFrom(source);
5075
+ this.arcLengthDivision = source.arcLengthDivision;
4789
5076
  this.x = source.x;
4790
5077
  this.y = source.y;
4791
5078
  this.width = source.width;
@@ -4862,6 +5149,16 @@ var CurvePath = class extends CompositeCurve {
4862
5149
  getFillVertices(options) {
4863
5150
  return this._closeVertices(super.getFillVertices(options));
4864
5151
  }
5152
+ /**
5153
+ * Same as {@link Curve.isPointInStroke}, but `closed` defaults to this sub-path's actual
5154
+ * closed-ness: explicitly `autoClose`, or geometrically closed (first vertex === last).
5155
+ */
5156
+ isPointInStroke(point, options = {}) {
5157
+ const { strokeWidth = 1, tolerance = 0 } = options;
5158
+ const vertices = this._getCachedAdaptiveVertices();
5159
+ const len = vertices.length;
5160
+ return pointToPolylineDistance(point, vertices, options.closed ?? (this.autoClose || len >= 6 && vertices[0] === vertices[len - 2] && vertices[1] === vertices[len - 1])) <= strokeWidth / 2 + tolerance;
5161
+ }
4865
5162
  _setCurrentPoint(point) {
4866
5163
  this.currentPoint = new Vector2(point.x, point.y);
4867
5164
  if (!this.startPoint) this.startPoint = this.currentPoint.clone();
@@ -4999,6 +5296,8 @@ var CurvePath = class extends CompositeCurve {
4999
5296
  };
5000
5297
  var Path2D = class Path2D extends CompositeCurve {
5001
5298
  _meta;
5299
+ _ringsCache;
5300
+ _ringsCacheLen = -1;
5002
5301
  currentCurve = new CurvePath();
5003
5302
  style;
5004
5303
  get startPoint() {
@@ -5110,6 +5409,7 @@ var Path2D = class Path2D extends CompositeCurve {
5110
5409
  this.getControlPointRefs().forEach((point) => {
5111
5410
  point.scale(sx, sy, target);
5112
5411
  });
5412
+ this.invalidate();
5113
5413
  return this;
5114
5414
  }
5115
5415
  skew(ax, ay = 0, target = {
@@ -5119,6 +5419,7 @@ var Path2D = class Path2D extends CompositeCurve {
5119
5419
  this.getControlPointRefs().forEach((point) => {
5120
5420
  point.skew(ax, ay, target);
5121
5421
  });
5422
+ this.invalidate();
5122
5423
  return this;
5123
5424
  }
5124
5425
  rotate(rad, target = {
@@ -5128,6 +5429,7 @@ var Path2D = class Path2D extends CompositeCurve {
5128
5429
  this.getControlPointRefs().forEach((point) => {
5129
5430
  point.rotate(rad, target);
5130
5431
  });
5432
+ this.invalidate();
5131
5433
  return this;
5132
5434
  }
5133
5435
  bold(b) {
@@ -5173,47 +5475,67 @@ var Path2D = class Path2D extends CompositeCurve {
5173
5475
  }
5174
5476
  });
5175
5477
  });
5478
+ this.invalidate();
5176
5479
  return this;
5177
5480
  }
5481
+ /**
5482
+ * Test whether a point lies inside the filled area of this path.
5483
+ *
5484
+ * Each sub-path ({@link CurvePath}) is sampled into its own ring and all rings are
5485
+ * evaluated together via {@link pointInPolygons}, so holes (donut / hollow shapes) are
5486
+ * honored. This is purely geometric and ignores `style.fill` — for the `fill: 'none'`
5487
+ * fallback, gate the call upstream (see {@link Path2DSet.hitTest}).
5488
+ *
5489
+ * Defaults `fillRule` to `style.fillRule`, then `'nonzero'` (matching SVG/Canvas).
5490
+ */
5491
+ _invalidateSelf() {
5492
+ super._invalidateSelf();
5493
+ this._ringsCache = void 0;
5494
+ this._ringsCacheLen = -1;
5495
+ }
5496
+ /** Per-sub-path sampled rings, cached for repeated hit tests. */
5497
+ _getRings() {
5498
+ if (!this._ringsCache || this._ringsCacheLen !== this.curves.length) {
5499
+ this._ringsCache = this.curves.map((curve) => {
5500
+ curve._owner = this;
5501
+ return curve.getAdaptiveVertices();
5502
+ });
5503
+ this._ringsCacheLen = this.curves.length;
5504
+ }
5505
+ return this._ringsCache;
5506
+ }
5507
+ isPointInFill(point, options = {}) {
5508
+ const fillRule = options.fillRule ?? this.style.fillRule ?? "nonzero";
5509
+ return pointInPolygons(point, this._getRings(), fillRule);
5510
+ }
5511
+ /**
5512
+ * Test whether a point lies on this path's stroke. A hit on any sub-path counts.
5513
+ *
5514
+ * Defaults `strokeWidth` to this path's own {@link strokeWidth} (which is `0` when
5515
+ * `style.stroke` is `'none'`). Each sub-path infers its own closed-ness unless `closed`
5516
+ * is given explicitly.
5517
+ */
5518
+ isPointInStroke(point, options = {}) {
5519
+ const strokeWidth = options.strokeWidth ?? this.strokeWidth;
5520
+ const { tolerance = 0, closed } = options;
5521
+ return this.curves.some((curve) => curve.isPointInStroke(point, {
5522
+ strokeWidth,
5523
+ tolerance,
5524
+ closed
5525
+ }));
5526
+ }
5178
5527
  getMinMax(min = Vector2.MAX, max = Vector2.MIN, withStyle = true) {
5179
- const strokeWidth = this.strokeWidth;
5180
5528
  this.curves.forEach((curve) => {
5181
5529
  curve.getMinMax(min, max);
5182
- if (withStyle) {
5183
- if (strokeWidth > 1) {
5184
- const halfStrokeWidth = strokeWidth / 2;
5185
- const isClockwise = curve.isClockwise();
5186
- const points = [];
5187
- for (let t = 0; t <= 1; t += 1 / curve.arcLengthDivision) {
5188
- const point = curve.getPoint(t);
5189
- const normal = curve.getNormal(t);
5190
- const dist1 = normal.clone().scale(isClockwise ? halfStrokeWidth : -halfStrokeWidth);
5191
- const dist2 = normal.clone().scale(isClockwise ? -halfStrokeWidth : halfStrokeWidth);
5192
- points.push(point.clone().add(dist1), point.clone().add(dist2), point.clone().add({
5193
- x: halfStrokeWidth,
5194
- y: 0
5195
- }), point.clone().add({
5196
- x: -halfStrokeWidth,
5197
- y: 0
5198
- }), point.clone().add({
5199
- x: 0,
5200
- y: halfStrokeWidth
5201
- }), point.clone().add({
5202
- x: 0,
5203
- y: -halfStrokeWidth
5204
- }), point.clone().add({
5205
- x: halfStrokeWidth,
5206
- y: halfStrokeWidth
5207
- }), point.clone().add({
5208
- x: -halfStrokeWidth,
5209
- y: -halfStrokeWidth
5210
- }));
5211
- }
5212
- min.clampMin(...points);
5213
- max.clampMax(...points);
5214
- }
5215
- }
5216
5530
  });
5531
+ if (withStyle) {
5532
+ const strokeWidth = this.strokeWidth;
5533
+ if (strokeWidth > 1 && Number.isFinite(min.x)) {
5534
+ const half = strokeWidth / 2;
5535
+ min.set(min.x - half, min.y - half);
5536
+ max.set(max.x + half, max.y + half);
5537
+ }
5538
+ }
5217
5539
  return {
5218
5540
  min: min.finite(),
5219
5541
  max: max.finite()
@@ -5352,6 +5674,42 @@ var Path2DSet = class {
5352
5674
  this.paths = paths;
5353
5675
  this.viewBox = viewBox;
5354
5676
  }
5677
+ /**
5678
+ * Test whether a point lies inside the filled area of any path in this set.
5679
+ * Purely geometric (ignores `fill: 'none'`); use {@link hitTest} for style-aware hits.
5680
+ */
5681
+ isPointInFill(point, options = {}) {
5682
+ return this.paths.some((path) => path.isPointInFill(point, options));
5683
+ }
5684
+ /**
5685
+ * Concise PathKit-style fill containment test across the whole set; shorthand for
5686
+ * {@link isPointInFill} with a `{ x, y }` point.
5687
+ */
5688
+ contains(x, y, options = {}) {
5689
+ return this.isPointInFill({
5690
+ x,
5691
+ y
5692
+ }, options);
5693
+ }
5694
+ /**
5695
+ * Find the topmost path hit by a point, or `undefined` if none.
5696
+ *
5697
+ * Paths are tested top-to-bottom (last drawn first). For each path a fill hit is checked
5698
+ * first (skipped when `style.fill` is `'none'`), then — if `stroke` is enabled — a stroke
5699
+ * hit (skipped when `style.stroke` is `'none'`). This honors the "fill: none falls back to
5700
+ * stroke" rule; the coordinate space of `point` must match the paths (no scaling assumed).
5701
+ *
5702
+ * Options: `stroke` (also test strokes, default `true`), `tolerance` (extra stroke hit slack
5703
+ * in path units, default `0`), and `fillRule` (overrides each path's own fill rule).
5704
+ */
5705
+ hitTest(point, options = {}) {
5706
+ const { stroke = true, tolerance, fillRule } = options;
5707
+ for (let i = this.paths.length - 1; i >= 0; i--) {
5708
+ const path = this.paths[i];
5709
+ if ((path.style.fill ?? "#000") !== "none" && path.isPointInFill(point, { fillRule })) return path;
5710
+ if (stroke && (path.style.stroke ?? "none") !== "none" && path.isPointInStroke(point, { tolerance })) return path;
5711
+ }
5712
+ }
5355
5713
  getBoundingBox(withStyle = true) {
5356
5714
  if (!this.paths.length) return;
5357
5715
  const min = Vector2.MAX;
@@ -5655,7 +6013,7 @@ function _createPresetShape(node, options) {
5655
6013
  var root;
5656
6014
  async function getRoot() {
5657
6015
  if (!root) {
5658
- const presetShapeDefinitions = await import("./presetShapeDefinitions-C0rBrGs4.js").then((rep) => rep.default);
6016
+ const presetShapeDefinitions = await import("./presetShapeDefinitions-NAoQKZ6z.js").then((rep) => rep.default);
5659
6017
  root = OoxmlNode.fromXML(presetShapeDefinitions);
5660
6018
  }
5661
6019
  return root;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mce/bigesj",
3
3
  "type": "module",
4
- "version": "0.18.3",
4
+ "version": "0.18.5",
5
5
  "description": "Plugin for mce",
6
6
  "author": "wxm",
7
7
  "license": "MIT",
@@ -49,7 +49,7 @@
49
49
  "modern-openxml": "^1.10.1"
50
50
  },
51
51
  "devDependencies": {
52
- "mce": "0.18.3"
52
+ "mce": "0.18.5"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "mce": "^0"