@pixerate/editor 0.38.0 → 0.40.0

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.
@@ -675,14 +675,14 @@ function turnDeg(a, b, c) {
675
675
  if (!u || !v) return 0;
676
676
  return Math.acos(Math.min(1, Math.max(-1, dot(u, v)))) * 180 / Math.PI;
677
677
  }
678
- function findCorners(pts, closed, cornerAngle, span) {
678
+ function findCorners(pts, closed, cornerAngle, span2) {
679
679
  const n = pts.length;
680
680
  const at = (i) => pts[closed ? (i % n + n) % n : i];
681
681
  const reach = (i, step) => {
682
682
  for (let k = 1; k < (closed ? n : Infinity); k++) {
683
683
  const j = i + step * k;
684
684
  if (!closed && (j < 0 || j >= n)) return k > 1 ? j - step : null;
685
- if (dist(at(j), pts[i]) >= span) return j;
685
+ if (dist(at(j), pts[i]) >= span2) return j;
686
686
  }
687
687
  return null;
688
688
  };
@@ -704,7 +704,7 @@ function findCorners(pts, closed, cornerAngle, span) {
704
704
  let sharpest = true;
705
705
  for (let j = lo; j <= hi && sharpest; j++) {
706
706
  const k = closed ? (j % n + n) % n : j;
707
- if (k === i || dist(pts[k], pts[i]) >= span) continue;
707
+ if (k === i || dist(pts[k], pts[i]) >= span2) continue;
708
708
  if (turns[k] > turns[i] || turns[k] === turns[i] && k < i) sharpest = false;
709
709
  }
710
710
  if (sharpest) corners.push(i);
@@ -742,9 +742,9 @@ function fitCubics(points, tolerance, options = {}) {
742
742
  const tol = Math.max(tolerance, 1e-6);
743
743
  const cornerAngle = options.cornerAngleDeg ?? 30;
744
744
  const n = pts.length;
745
- const span = 4 * tol;
745
+ const span2 = 4 * tol;
746
746
  if (!closed || n < 3) {
747
- const corners2 = [0, ...n > 2 ? findCorners(pts, false, cornerAngle, span) : [], n - 1];
747
+ const corners2 = [0, ...n > 2 ? findCorners(pts, false, cornerAngle, span2) : [], n - 1];
748
748
  const out2 = [];
749
749
  for (let k = 1; k < corners2.length; k++) {
750
750
  const run = pts.slice(corners2[k - 1], corners2[k] + 1);
@@ -753,7 +753,7 @@ function fitCubics(points, tolerance, options = {}) {
753
753
  if (closed && n === 2) out2.push(...fitCubicRun([pts[1], pts[0]], tol));
754
754
  return out2;
755
755
  }
756
- const corners = findCorners(pts, true, cornerAngle, span);
756
+ const corners = findCorners(pts, true, cornerAngle, span2);
757
757
  if (corners.length === 0) {
758
758
  const ring = [...pts, pts[0]];
759
759
  const t = unit(sub(pts[1], pts[n - 1])) ?? [1, 0];
@@ -3651,8 +3651,8 @@ function deepPoint(rings) {
3651
3651
  }
3652
3652
  xs.sort((p, q) => p - q);
3653
3653
  for (let i = 0; i + 1 < xs.length; i += 2) {
3654
- const span = xs[i + 1] - xs[i];
3655
- if (!best || span > best.span) best = { span, p: [(xs[i] + xs[i + 1]) / 2, y] };
3654
+ const span2 = xs[i + 1] - xs[i];
3655
+ if (!best || span2 > best.span) best = { span: span2, p: [(xs[i] + xs[i + 1]) / 2, y] };
3656
3656
  }
3657
3657
  }
3658
3658
  return best?.p ?? null;
@@ -4274,6 +4274,152 @@ function closeGaps(vertices, edges, gap) {
4274
4274
  joined.add(best);
4275
4275
  }
4276
4276
  }
4277
+ function contourEnclosesArea(contour) {
4278
+ const segs = closeContour(contour, true);
4279
+ if (!segs.length) return false;
4280
+ const b = boundsOf(segs);
4281
+ const w = b.maxX - b.minX;
4282
+ const h = b.maxY - b.minY;
4283
+ return Math.abs(contourSignedArea(segs)) > 1e-9 * (w * w + h * h);
4284
+ }
4285
+ function nearestU(segs, p) {
4286
+ let best = null;
4287
+ segs.forEach((seg, k) => {
4288
+ const n = segmentNearest(seg, p);
4289
+ if (!best || n.distance < best.d) best = { u: k + n.t, d: n.distance };
4290
+ });
4291
+ return best;
4292
+ }
4293
+ function span(segs, u0, u1) {
4294
+ const n = segs.length;
4295
+ if (!n || !Number.isFinite(u0) || !Number.isFinite(u1)) return [];
4296
+ const first = Math.max(0, Math.floor(u0));
4297
+ const last = Math.min(Math.max(0, Math.ceil(u1)), first + 2 * n);
4298
+ const out = [];
4299
+ for (let k = first; k < last; k++) {
4300
+ const t0 = Math.min(1, Math.max(0, u0 - k));
4301
+ const t1 = Math.min(1, Math.max(0, u1 - k));
4302
+ if (t1 - t0 > 1e-12) out.push(subSegment(segs[k % n], t0, t1));
4303
+ }
4304
+ return out;
4305
+ }
4306
+ function snapEnds2(segs, to, tol) {
4307
+ if (!segs.length) return segs;
4308
+ const out = segs.map((s) => [...s]);
4309
+ const move = (seg, end, handle) => {
4310
+ const q = to.find((t) => dist2(t, seg[end]) <= tol);
4311
+ if (!q) return;
4312
+ const d = [q[0] - seg[end][0], q[1] - seg[end][1]];
4313
+ seg[end] = q;
4314
+ if (seg.length === 4) seg[handle] = [seg[handle][0] + d[0], seg[handle][1] + d[1]];
4315
+ };
4316
+ move(out[0], 0, 1);
4317
+ const last = out[out.length - 1];
4318
+ move(last, last.length - 1, last.length - 2);
4319
+ return out;
4320
+ }
4321
+ function cutOutPiece(path, piece, fillCloses, tol) {
4322
+ const segs = piece.segments;
4323
+ if (!segs.length) return null;
4324
+ let mid = null;
4325
+ let longest = -1;
4326
+ let perimeter = 0;
4327
+ for (const seg of segs) {
4328
+ const len2 = segmentLength(seg);
4329
+ perimeter += len2;
4330
+ if (len2 > longest) {
4331
+ longest = len2;
4332
+ mid = segmentPoint(seg, 0.5);
4333
+ }
4334
+ }
4335
+ if (!mid || perimeter <= tol) return null;
4336
+ const a = segs[0][0];
4337
+ const lastSeg = segs[segs.length - 1];
4338
+ const b = lastSeg[lastSeg.length - 1];
4339
+ const whole = !!piece.closed || dist2(a, b) <= tol;
4340
+ const contours = pathToContours(path);
4341
+ let found = null;
4342
+ contours.forEach((c, index2) => {
4343
+ if (!c.segments.length) return;
4344
+ const closed2 = c.closed || fillCloses && contourEnclosesArea(c);
4345
+ const ring2 = closeContour(c, closed2);
4346
+ const n = nearestU(ring2, mid);
4347
+ if (n && n.d <= tol && (!found || n.d < found.d)) found = { index: index2, ring: ring2, closed: closed2, um: n.u, d: n.d };
4348
+ });
4349
+ if (!found) return null;
4350
+ const { index, ring, closed, um } = found;
4351
+ const len = ring.length;
4352
+ const real = closeContour(contours[index]).length;
4353
+ const at = (p) => {
4354
+ const n = nearestU(ring, p);
4355
+ return n && n.d <= tol ? n.u : null;
4356
+ };
4357
+ let keep = [];
4358
+ if (!whole) {
4359
+ const ua = at(a);
4360
+ const ub = at(b);
4361
+ if (ua === null || ub === null) return null;
4362
+ if (closed) {
4363
+ const fwd = (from, to) => ((to - from) % len + len) % len;
4364
+ const [start, end] = fwd(ua, um) <= fwd(ua, ub) ? [ub, ua] : [ua, ub];
4365
+ const rest = fwd(start, end);
4366
+ if (rest > 1e-9 && len - rest > 1e-9) {
4367
+ const parts = real >= len ? [[0, 2 * len]] : [[0, real], [len, len + real]];
4368
+ keep = parts.map(([lo, hi]) => [Math.max(start, lo), Math.min(start + rest, hi)]).filter(([x, y]) => y - x > 1e-9);
4369
+ }
4370
+ } else {
4371
+ const lo = Math.min(ua, ub);
4372
+ const hi = Math.max(ua, ub);
4373
+ if (um < lo - 1e-9 || um > hi + 1e-9) return null;
4374
+ keep = [[0, lo], [hi, len]].filter(([x, y]) => y - x > 1e-9);
4375
+ }
4376
+ }
4377
+ const out = contours.slice(0, index);
4378
+ for (const [u0, u1] of keep) {
4379
+ const run = span(ring, u0, u1);
4380
+ if (run.length) out.push({ segments: snapEnds2(run, [a, b], tol), closed: false });
4381
+ }
4382
+ out.push(...contours.slice(index + 1));
4383
+ return contoursToPath(out);
4384
+ }
4385
+
4386
+ // src/canvas/pathops/hatch.ts
4387
+ function hatchPath(path, options = {}) {
4388
+ const out = [];
4389
+ const b = bezierPathBounds(path);
4390
+ if (!b || b.maxX - b.minX <= 0 || b.maxY - b.minY <= 0) return out;
4391
+ const want = Math.max(options.gap ?? 4, 1e-3);
4392
+ const s = Math.SQRT1_2;
4393
+ const rot = (p) => [(p[0] + p[1]) * s, (p[1] - p[0]) * s];
4394
+ const unrot = (x, y) => ({ x: (x - y) * s, y: (x + y) * s });
4395
+ const edges = [];
4396
+ for (const contour of pathToContours(path)) {
4397
+ const pts = flattenChain(closeContour(contour, true), want * 0.1).map(rot);
4398
+ for (let i = 0; i < pts.length; i++) {
4399
+ const p = pts[i];
4400
+ const q = pts[(i + 1) % pts.length];
4401
+ if (p[0] !== q[0] || p[1] !== q[1]) edges.push([p, q]);
4402
+ }
4403
+ }
4404
+ let y0 = Infinity;
4405
+ let y1 = -Infinity;
4406
+ for (const [p, q] of edges) {
4407
+ y0 = Math.min(y0, p[1], q[1]);
4408
+ y1 = Math.max(y1, p[1], q[1]);
4409
+ }
4410
+ if (!(y1 > y0)) return out;
4411
+ const gap = Math.max(want, (y1 - y0) / Math.max(1, options.maxLines ?? 400), 1e-6);
4412
+ const xs = [];
4413
+ for (let y = Math.floor(y0 / gap) * gap + gap / 2; y < y1; y += gap) {
4414
+ xs.length = 0;
4415
+ for (const [p, q] of edges) if (p[1] <= y !== q[1] <= y) xs.push(p[0] + (y - p[1]) / (q[1] - p[1]) * (q[0] - p[0]));
4416
+ xs.sort((m, n) => m - n);
4417
+ for (let i = 0; i + 1 < xs.length; i += 2) {
4418
+ out.push({ type: "M", ...unrot(xs[i], y) }, { type: "L", ...unrot(xs[i + 1], y) });
4419
+ }
4420
+ }
4421
+ return out;
4422
+ }
4277
4423
 
4278
4424
  // src/canvas/pathops/pathfinder.ts
4279
4425
  var PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
@@ -4440,7 +4586,10 @@ export {
4440
4586
  offsetPath,
4441
4587
  interiorPoint,
4442
4588
  PlanarMap,
4589
+ contourEnclosesArea,
4590
+ cutOutPiece,
4591
+ hatchPath,
4443
4592
  PATHFINDER_OPERATIONS,
4444
4593
  pathfinder
4445
4594
  };
4446
- //# sourceMappingURL=chunk-ZCCU3VNI.js.map
4595
+ //# sourceMappingURL=chunk-3LHWTGHW.js.map