(function attachPyroPathOperations(root) {
  'use strict';

  const epsilon = 1e-8;
  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const clonePoint = (point) => [finite(point?.[0]), finite(point?.[1])];
  const distance = (a, b) => Math.hypot(finite(a?.[0]) - finite(b?.[0]), finite(a?.[1]) - finite(b?.[1]));
  const cross = (a, b) => finite(a?.[0]) * finite(b?.[1]) - finite(a?.[1]) * finite(b?.[0]);
  const subtract = (a, b) => [finite(a?.[0]) - finite(b?.[0]), finite(a?.[1]) - finite(b?.[1])];
  const interpolate = (a, b, t) => [finite(a?.[0]) + (finite(b?.[0]) - finite(a?.[0])) * t, finite(a?.[1]) + (finite(b?.[1]) - finite(a?.[1])) * t];
  const pointKey = (point) => `${Math.round(finite(point?.[0]) * 1e6)},${Math.round(finite(point?.[1]) * 1e6)}`;
  const isBezier = (segment) => Array.isArray(segment?.c1) && Array.isArray(segment?.c2)
    && segment.c1.length >= 2 && segment.c2.length >= 2
    && segment.c1.every((value) => Number.isFinite(Number(value)))
    && segment.c2.every((value) => Number.isFinite(Number(value)));
  const cloneSegment = (segment) => isBezier(segment) ? { c1: clonePoint(segment.c1), c2: clonePoint(segment.c2) } : null;

  function normalizeContour(contour) {
    const points = (Array.isArray(contour) ? contour : []).filter((point) => Array.isArray(point) && point.length >= 2).map(clonePoint);
    const compact = [];
    points.forEach((point) => { if (!compact.length || distance(point, compact.at(-1)) > epsilon) compact.push(point); });
    if (compact.length > 1 && distance(compact[0], compact.at(-1)) <= epsilon) compact.pop();
    return compact.length >= 3 ? compact : [];
  }

  function normalizeContours(contours) {
    return (Array.isArray(contours) ? contours : []).map(normalizeContour).filter((contour) => contour.length >= 3);
  }

  function signedArea(contour) {
    return normalizeContour(contour).reduce((sum, point, index, points) => {
      const next = points[(index + 1) % points.length];
      return sum + point[0] * next[1] - next[0] * point[1];
    }, 0) / 2;
  }

  function pointInPolygon(point, contour) {
    let inside = false;
    for (let index = 0, previous = contour.length - 1; index < contour.length; previous = index, index += 1) {
      const a = contour[index]; const b = contour[previous];
      const intersects = (a[1] > point[1]) !== (b[1] > point[1])
        && point[0] < (b[0] - a[0]) * (point[1] - a[1]) / ((b[1] - a[1]) || Number.EPSILON) + a[0];
      if (intersects) inside = !inside;
    }
    return inside;
  }

  function pointInNormalizedContours(point, contours) {
    return contours.reduce((inside, contour) => pointInPolygon(point, contour) ? !inside : inside, false);
  }

  function pointInContours(point, contours) {
    return pointInNormalizedContours(point, normalizeContours(contours));
  }

  function contourBounds(contours) {
    const points = normalizeContours(contours).flat();
    if (!points.length) return { x: 0, y: 0, width: 0, height: 0, right: 0, bottom: 0 };
    let x = Infinity; let y = Infinity; let right = -Infinity; let bottom = -Infinity;
    points.forEach((point) => {
      x = Math.min(x, point[0]); y = Math.min(y, point[1]);
      right = Math.max(right, point[0]); bottom = Math.max(bottom, point[1]);
    });
    return { x, y, width: right - x, height: bottom - y, right, bottom };
  }

  function edgesFor(contours) {
    return normalizeContours(contours).flatMap((contour, contourIndex) => contour.map((a, index) => ({
      a, b: contour[(index + 1) % contour.length], contourIndex, index, splits: [0, 1]
    })));
  }

  function parameterOnSegment(point, edge) {
    const vector = subtract(edge.b, edge.a); const lengthSquared = vector[0] ** 2 + vector[1] ** 2;
    if (lengthSquared <= epsilon) return 0;
    return ((point[0] - edge.a[0]) * vector[0] + (point[1] - edge.a[1]) * vector[1]) / lengthSquared;
  }

  function addSplit(edge, value) {
    const next = Math.max(0, Math.min(1, value));
    if (!edge.splits.some((existing) => Math.abs(existing - next) <= epsilon)) edge.splits.push(next);
  }

  function splitAtIntersections(leftEdges, rightEdges) {
    leftEdges.forEach((left) => rightEdges.forEach((right) => {
      const r = subtract(left.b, left.a); const s = subtract(right.b, right.a); const offset = subtract(right.a, left.a);
      const denominator = cross(r, s); const scale = Math.max(1, Math.hypot(...r) * Math.hypot(...s));
      if (Math.abs(denominator) > epsilon * scale) {
        const leftT = cross(offset, s) / denominator; const rightT = cross(offset, r) / denominator;
        if (leftT >= -epsilon && leftT <= 1 + epsilon && rightT >= -epsilon && rightT <= 1 + epsilon) {
          addSplit(left, leftT); addSplit(right, rightT);
        }
        return;
      }
      if (Math.abs(cross(offset, r)) > epsilon * Math.max(1, Math.hypot(...r))) return;
      [[right.a, right, left], [right.b, right, left], [left.a, left, right], [left.b, left, right]].forEach(([point, source, target]) => {
        const sourceT = parameterOnSegment(point, source); const targetT = parameterOnSegment(point, target);
        if (sourceT >= -epsilon && sourceT <= 1 + epsilon && targetT >= -epsilon && targetT <= 1 + epsilon) {
          addSplit(source, sourceT); addSplit(target, targetT);
        }
      });
    }));
  }

  function splitSelfIntersections(edges) {
    const contourEdgeCounts = new Map();
    edges.forEach((edge) => contourEdgeCounts.set(edge.contourIndex, (contourEdgeCounts.get(edge.contourIndex) || 0) + 1));
    for (let leftIndex = 0; leftIndex < edges.length; leftIndex += 1) {
      const left = edges[leftIndex];
      for (let rightIndex = leftIndex + 1; rightIndex < edges.length; rightIndex += 1) {
        const right = edges[rightIndex];
        if (left.contourIndex === right.contourIndex) {
          const count = contourEdgeCounts.get(left.contourIndex) || 0;
          const adjacent = left.index === right.index || Math.abs(left.index - right.index) === 1
            || Math.abs(left.index - right.index) === count - 1;
          if (adjacent) continue;
        }
        splitAtIntersections([left], [right]);
      }
    }
  }

  function splitEdges(edges) {
    return edges.flatMap((edge) => {
      const values = [...edge.splits].sort((a, b) => a - b); const segments = [];
      for (let index = 0; index < values.length - 1; index += 1) {
        const a = interpolate(edge.a, edge.b, values[index]); const b = interpolate(edge.a, edge.b, values[index + 1]);
        if (distance(a, b) > epsilon) segments.push({ a, b });
      }
      return segments;
    });
  }

  function operationValue(operation, left, right) {
    if (operation === 'intersection' || operation === 'intersect') return left && right;
    if (operation === 'difference' || operation === 'subtract') return left && !right;
    if (operation === 'xor' || operation === 'exclude') return left !== right;
    return left || right;
  }

  function simplifyContour(contour) {
    let points = normalizeContour(contour);
    if (points.length < 3) return [];
    let changed = true;
    while (changed && points.length >= 3) {
      changed = false;
      const next = points.filter((point, index) => {
        const previous = points[(index - 1 + points.length) % points.length]; const following = points[(index + 1) % points.length];
        const a = subtract(point, previous); const b = subtract(following, point);
        const collinear = Math.abs(cross(a, b)) <= 1e-7 * Math.max(1, Math.hypot(...a) * Math.hypot(...b));
        if (collinear && a[0] * b[0] + a[1] * b[1] >= 0) { changed = true; return false; }
        return true;
      });
      points = next;
    }
    return points.length >= 3 ? points : [];
  }

  function chooseNext(previous, candidates) {
    if (candidates.length <= 1) return candidates[0];
    const incoming = subtract(previous.b, previous.a);
    return candidates.map((candidate) => {
      const outgoing = subtract(candidate.b, candidate.a);
      let turn = Math.atan2(cross(incoming, outgoing), incoming[0] * outgoing[0] + incoming[1] * outgoing[1]);
      if (turn < -epsilon) turn += Math.PI * 2;
      return { candidate, turn };
    }).sort((a, b) => a.turn - b.turn)[0].candidate;
  }

  function chainSegments(segments) {
    const starts = new Map();
    segments.forEach((segment) => {
      const key = pointKey(segment.a); const list = starts.get(key) || [];
      list.push(segment); starts.set(key, list);
    });
    const contours = [];
    for (const first of segments) {
      if (first.used) continue;
      const startKey = pointKey(first.a); const contour = [clonePoint(first.a)];
      let current = first; let closed = false; let guard = 0;
      while (current && guard++ <= segments.length + 2) {
        current.used = true; contour.push(clonePoint(current.b));
        const endKey = pointKey(current.b);
        if (endKey === startKey) { closed = true; break; }
        const candidates = (starts.get(endKey) || []).filter((segment) => !segment.used);
        current = chooseNext(current, candidates);
      }
      if (!closed) continue;
      contour.pop();
      const simplified = simplifyContour(contour);
      if (simplified.length >= 3) contours.push(simplified);
    }
    return contours;
  }

  function contourAreaLimit(contours, requested = null) {
    if (Number.isFinite(Number(requested))) return Math.max(0, Number(requested));
    const bounds = contourBounds(contours); const span = Math.max(bounds.width, bounds.height, 1);
    return Math.max(1e-8, span * span * 1e-10);
  }

  // Zerlegt Selbstüberschneidungen nach der Even-Odd-Füllregel und entfernt
  // doppelte Innenkanten. Dadurch erhalten Offset und Boolean auch bei
  // schleifenförmigen Eingaben wieder echte, geschlossene Teilkonturen.
  function resolveContours(rawContours, options = {}) {
    const contours = normalizeContours(rawContours); if (!contours.length) return [];
    const edges = edgesFor(contours); splitSelfIntersections(edges);
    const bounds = contourBounds(contours); const probe = Math.max(1e-6, Math.hypot(bounds.width, bounds.height) * 1e-7);
    const boundary = [];
    splitEdges(edges).forEach((segment) => {
      const dx = segment.b[0] - segment.a[0]; const dy = segment.b[1] - segment.a[1]; const length = Math.hypot(dx, dy);
      if (length <= epsilon) return;
      const middle = [(segment.a[0] + segment.b[0]) / 2, (segment.a[1] + segment.b[1]) / 2];
      const rightPoint = [middle[0] - dy / length * probe, middle[1] + dx / length * probe];
      const leftPoint = [middle[0] + dy / length * probe, middle[1] - dx / length * probe];
      const rightInside = pointInNormalizedContours(rightPoint, contours); const leftInside = pointInNormalizedContours(leftPoint, contours);
      if (rightInside === leftInside) return;
      boundary.push(rightInside ? { a: segment.a, b: segment.b } : { a: segment.b, b: segment.a });
    });
    const unique = new Map();
    boundary.forEach((segment) => {
      const a = pointKey(segment.a); const b = pointKey(segment.b); const key = a < b ? `${a}|${b}` : `${b}|${a}`;
      if (!unique.has(key)) unique.set(key, { a: clonePoint(segment.a), b: clonePoint(segment.b), used: false });
    });
    const limit = contourAreaLimit(contours, options.minArea);
    return chainSegments([...unique.values()]).filter((contour) => Math.abs(signedArea(contour)) > limit);
  }

  function booleanContours(leftContours, rightContours, operation = 'union') {
    const left = resolveContours(leftContours); const right = resolveContours(rightContours);
    if (!left.length || !right.length) {
      if (operation === 'intersection' || operation === 'intersect') return [];
      if (operation === 'difference' || operation === 'subtract') return left;
      return [...left, ...right];
    }
    const leftEdges = edgesFor(left); const rightEdges = edgesFor(right);
    splitAtIntersections(leftEdges, rightEdges);
    const bounds = contourBounds([...left, ...right]); const probe = Math.max(1e-5, Math.hypot(bounds.width, bounds.height) * 1e-6);
    const selected = [];
    [...splitEdges(leftEdges), ...splitEdges(rightEdges)].forEach((segment) => {
      const dx = segment.b[0] - segment.a[0]; const dy = segment.b[1] - segment.a[1]; const length = Math.hypot(dx, dy);
      if (length <= epsilon) return;
      const middle = [(segment.a[0] + segment.b[0]) / 2, (segment.a[1] + segment.b[1]) / 2];
      const rightPoint = [middle[0] - dy / length * probe, middle[1] + dx / length * probe];
      const leftPoint = [middle[0] + dy / length * probe, middle[1] - dx / length * probe];
      const rightInside = operationValue(operation, pointInNormalizedContours(rightPoint, left), pointInNormalizedContours(rightPoint, right));
      const leftInside = operationValue(operation, pointInNormalizedContours(leftPoint, left), pointInNormalizedContours(leftPoint, right));
      if (rightInside === leftInside) return;
      selected.push(rightInside ? segment : { a: segment.b, b: segment.a });
    });
    const unique = new Map();
    selected.forEach((segment) => {
      const a = pointKey(segment.a); const b = pointKey(segment.b); const key = a < b ? `${a}|${b}` : `${b}|${a}`;
      if (!unique.has(key)) unique.set(key, { a: clonePoint(segment.a), b: clonePoint(segment.b), used: false });
    });
    return resolveContours(chainSegments([...unique.values()]));
  }

  function combineMany(contourSets, operation = 'union') {
    const sets = (Array.isArray(contourSets) ? contourSets : []).map((contours) => resolveContours(contours)).filter((contours) => contours.length);
    if (!sets.length) return [];
    return sets.slice(1).reduce((result, contours) => booleanContours(result, contours, operation), sets[0]);
  }

  function lineIntersection(a, directionA, b, directionB) {
    const denominator = cross(directionA, directionB); if (Math.abs(denominator) < 1e-9) return null;
    const offset = subtract(b, a); const t = cross(offset, directionB) / denominator;
    return [a[0] + directionA[0] * t, a[1] + directionA[1] * t];
  }

  function arcPoints(center, from, to, direction, maximumStep = Math.PI / 12) {
    const radius = distance(center, from); if (radius <= epsilon) return [clonePoint(to)];
    const start = Math.atan2(from[1] - center[1], from[0] - center[0]); let delta = Math.atan2(to[1] - center[1], to[0] - center[0]) - start;
    if (direction > 0) while (delta <= 0) delta += Math.PI * 2;
    else while (delta >= 0) delta -= Math.PI * 2;
    const steps = Math.max(1, Math.ceil(Math.abs(delta) / maximumStep));
    return Array.from({ length: steps }, (_, index) => {
      const angle = start + delta * (index + 1) / steps;
      return [center[0] + Math.cos(angle) * radius, center[1] + Math.sin(angle) * radius];
    });
  }

  function contourInteriorPoint(contour) {
    const areaDirection = signedArea(contour) >= 0 ? 1 : -1;
    const bounds = contourBounds([contour]); const probe = Math.max(1e-6, Math.hypot(bounds.width, bounds.height) * 1e-6);
    for (let index = 0; index < contour.length; index += 1) {
      const a = contour[index]; const b = contour[(index + 1) % contour.length]; const dx = b[0] - a[0]; const dy = b[1] - a[1]; const length = Math.hypot(dx, dy);
      if (length <= epsilon) continue;
      const middle = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
      const candidate = [middle[0] - dy / length * probe * areaDirection, middle[1] + dx / length * probe * areaDirection];
      if (pointInPolygon(candidate, contour)) return candidate;
    }
    return clonePoint(contour[0]);
  }

  function offsetSingleContour(contour, distanceValue, hole, options = {}) {
    const amount = Math.abs(finite(distanceValue)); if (amount <= epsilon) return normalizeContour(contour);
    const areaDirection = signedArea(contour) >= 0 ? 1 : -1;
    const expansionDirection = (hole ? 1 : -1) * (distanceValue >= 0 ? 1 : -1);
    const side = areaDirection * expansionDirection; const lineJoin = ['round', 'bevel', 'miter'].includes(options.lineJoin) ? options.lineJoin : 'round';
    const miterLimit = Math.max(1, finite(options.miterLimit, 8)); const result = [];
    for (let index = 0; index < contour.length; index += 1) {
      const previous = contour[(index - 1 + contour.length) % contour.length]; const point = contour[index]; const next = contour[(index + 1) % contour.length];
      const incomingRaw = subtract(point, previous); const outgoingRaw = subtract(next, point);
      const incomingLength = Math.hypot(...incomingRaw); const outgoingLength = Math.hypot(...outgoingRaw);
      if (incomingLength <= epsilon || outgoingLength <= epsilon) continue;
      const incoming = [incomingRaw[0] / incomingLength, incomingRaw[1] / incomingLength]; const outgoing = [outgoingRaw[0] / outgoingLength, outgoingRaw[1] / outgoingLength];
      const from = [point[0] - incoming[1] * side * amount, point[1] + incoming[0] * side * amount];
      const to = [point[0] - outgoing[1] * side * amount, point[1] + outgoing[0] * side * amount];
      const intersection = lineIntersection(from, incoming, to, outgoing); const turn = cross(incoming, outgoing); const outside = turn * side < 0;
      if (!outside && intersection) result.push(intersection);
      else if (lineJoin === 'round') result.push(from, ...arcPoints(point, from, to, turn >= 0 ? 1 : -1));
      else if (lineJoin === 'miter' && intersection && distance(intersection, point) <= amount * miterLimit) result.push(intersection);
      else result.push(from, to);
    }
    return normalizeContour(result);
  }

  function minimumDistanceToContours(point, contours) {
    let best = Infinity;
    contours.forEach((contour) => contour.forEach((a, index) => {
      const b = contour[(index + 1) % contour.length]; const vector = subtract(b, a); const lengthSquared = vector[0] ** 2 + vector[1] ** 2;
      const t = lengthSquared <= epsilon ? 0 : Math.max(0, Math.min(1, ((point[0] - a[0]) * vector[0] + (point[1] - a[1]) * vector[1]) / lengthSquared));
      best = Math.min(best, distance(point, interpolate(a, b, t)));
    }));
    return best;
  }

  function offsetContours(rawContours, rawDistance, options = {}) {
    const contours = resolveContours(rawContours, options); const distanceValue = finite(rawDistance);
    if (!contours.length || Math.abs(distanceValue) <= epsilon) return contours.map((contour) => contour.map(clonePoint));
    const offset = contours.map((contour, index) => {
      const sample = contourInteriorPoint(contour);
      const depth = contours.reduce((count, other, otherIndex) => count + (otherIndex !== index && pointInPolygon(sample, other) ? 1 : 0), 0);
      return offsetSingleContour(contour, distanceValue, depth % 2 === 1, options);
    }).filter((contour) => contour.length >= 3);
    const cleaned = resolveContours(offset, options); const requested = Math.abs(distanceValue);
    return cleaned.filter((contour) => {
      if (requested <= 1e-6) return true;
      const averageDistance = contour.reduce((sum, point) => sum + minimumDistanceToContours(point, contours), 0) / contour.length;
      return averageDistance >= requested * .72;
    });
  }

  function normalizePathContour(value = {}) {
    const points = normalizeContour(value.points || value.contour || value);
    return {
      points,
      segments: Array.from({ length: points.length }, (_, index) => cloneSegment(value.segments?.[index] ?? value.bezierSegments?.[index])),
      closed: points.length >= 3
    };
  }

  function cubicPoint(curve, t) {
    const mt = 1 - t; const mt2 = mt * mt; const t2 = t * t;
    return [
      curve.p0[0] * mt2 * mt + 3 * curve.c1[0] * mt2 * t + 3 * curve.c2[0] * mt * t2 + curve.p1[0] * t2 * t,
      curve.p0[1] * mt2 * mt + 3 * curve.c1[1] * mt2 * t + 3 * curve.c2[1] * mt * t2 + curve.p1[1] * t2 * t
    ];
  }

  function splitCubic(curve, t) {
    const p01 = interpolate(curve.p0, curve.c1, t); const p12 = interpolate(curve.c1, curve.c2, t); const p23 = interpolate(curve.c2, curve.p1, t);
    const p012 = interpolate(p01, p12, t); const p123 = interpolate(p12, p23, t); const middle = interpolate(p012, p123, t);
    return [{ p0: curve.p0, c1: p01, c2: p012, p1: middle }, { p0: middle, c1: p123, c2: p23, p1: curve.p1 }];
  }

  function cubicRange(curve, from, to) {
    let start = Math.max(0, Math.min(1, from)); let end = Math.max(0, Math.min(1, to)); let reversed = false;
    if (end < start) { [start, end] = [end, start]; reversed = true; }
    let selected = curve;
    if (end < 1 - epsilon) selected = splitCubic(selected, end)[0];
    if (start > epsilon) selected = splitCubic(selected, start / Math.max(end, epsilon))[1];
    if (reversed) selected = { p0: selected.p1, c1: selected.c2, c2: selected.c1, p1: selected.p0 };
    return selected;
  }

  function flattenPathContour(value, options = {}, sourceId = '') {
    const path = normalizePathContour(value); if (!path.points.length) return { contour: [], edges: [] };
    const tolerance = Math.max(.05, finite(options.tolerance, .35)); const contour = [clonePoint(path.points[0])]; const edges = [];
    path.points.forEach((start, index) => {
      const end = path.points[(index + 1) % path.points.length]; const segment = path.segments[index];
      if (!isBezier(segment)) {
        contour.push(clonePoint(end)); edges.push({ a: clonePoint(start), b: clonePoint(end), sourceId, segmentIndex: index, curve: null, t0: 0, t1: 1 }); return;
      }
      const curve = { p0: clonePoint(start), c1: clonePoint(segment.c1), c2: clonePoint(segment.c2), p1: clonePoint(end) };
      const estimated = distance(curve.p0, curve.c1) + distance(curve.c1, curve.c2) + distance(curve.c2, curve.p1);
      const steps = Math.max(4, Math.min(128, Math.ceil(estimated / Math.max(2, tolerance * 4))));
      for (let step = 1; step <= steps; step += 1) {
        const t0 = (step - 1) / steps; const t1 = step / steps; const a = cubicPoint(curve, t0); const b = cubicPoint(curve, t1);
        contour.push(b); edges.push({ a, b, sourceId, segmentIndex: index, curve, t0, t1 });
      }
    });
    if (distance(contour[0], contour.at(-1)) <= 1e-6) contour.pop();
    return { contour: normalizeContour(contour), edges };
  }

  function edgeProvenance(a, b, sources) {
    const middle = interpolate(a, b, .5); let best = null;
    sources.forEach((source) => {
      if (!source.curve) return;
      const vector = subtract(source.b, source.a); const lengthSquared = vector[0] ** 2 + vector[1] ** 2; if (lengthSquared <= epsilon) return;
      const t = ((middle[0] - source.a[0]) * vector[0] + (middle[1] - source.a[1]) * vector[1]) / lengthSquared;
      if (t < -1e-5 || t > 1 + 1e-5) return;
      const projected = interpolate(source.a, source.b, t); const error = distance(projected, middle);
      const allowed = Math.max(2e-4, Math.sqrt(lengthSquared) * 2e-4); if (error > allowed || (best && error >= best.error)) return;
      const project = (point) => Math.max(0, Math.min(1, ((point[0] - source.a[0]) * vector[0] + (point[1] - source.a[1]) * vector[1]) / lengthSquared));
      const localA = project(a); const localB = project(b);
      best = { ...source, t0: source.t0 + (source.t1 - source.t0) * localA, t1: source.t0 + (source.t1 - source.t0) * localB, error };
    });
    return best;
  }

  function curvePathFromContour(contour, sources) {
    const edgeData = contour.map((point, index) => {
      const next = contour[(index + 1) % contour.length]; return { a: point, b: next, source: edgeProvenance(point, next, sources) };
    });
    const mergeable = (left, right) => Boolean(left?.source && right?.source
      && left.source.sourceId === right.source.sourceId && left.source.segmentIndex === right.source.segmentIndex
      && Math.abs(left.source.t1 - right.source.t0) <= 2e-3
      && Math.sign(left.source.t1 - left.source.t0) === Math.sign(right.source.t1 - right.source.t0));
    let start = edgeData.findIndex((edge, index) => !mergeable(edgeData[(index - 1 + edgeData.length) % edgeData.length], edge));
    if (start < 0) start = 0;
    const ordered = [...edgeData.slice(start), ...edgeData.slice(0, start)]; const runs = [];
    ordered.forEach((edge) => {
      const current = runs.at(-1);
      if (current && mergeable(current.at(-1), edge)) current.push(edge); else runs.push([edge]);
    });
    const points = []; const segments = [];
    runs.forEach((run) => {
      const first = run[0]; const last = run.at(-1); points.push(clonePoint(first.a));
      if (first.source) {
        const curve = cubicRange(first.source.curve, first.source.t0, last.source.t1);
        segments.push({ c1: clonePoint(curve.c1), c2: clonePoint(curve.c2) });
      } else segments.push(null);
    });
    return { points, segments, closed: points.length >= 3 };
  }

  // Führt Boolean auf adaptiv abgeflachten Kurven aus, ordnet die erhaltenen
  // Kanten danach aber wieder ihren ursprünglichen kubischen Segmenten zu.
  // Unberührte und nur geschnittene Bézierstücke bleiben so echte C-Segmente.
  function combinePathSets(pathSets, operation = 'union', options = {}) {
    const sources = []; const contourSets = (Array.isArray(pathSets) ? pathSets : []).map((set, setIndex) => (Array.isArray(set) ? set : []).map((path, contourIndex) => {
      const flattened = flattenPathContour(path, options, `${setIndex}:${contourIndex}`); sources.push(...flattened.edges); return flattened.contour;
    }).filter((contour) => contour.length >= 3));
    const contours = combineMany(contourSets, operation); const limit = contourAreaLimit(contours, options.minArea);
    return contours.filter((contour) => Math.abs(signedArea(contour)) > limit).map((contour) => curvePathFromContour(contour, sources));
  }

  const api = Object.freeze({
    normalizeContour, normalizeContours, signedArea, pointInContours, contourBounds, resolveContours,
    booleanContours, combineMany, offsetContours, normalizePathContour, flattenPathContour, combinePathSets
  });
  root.PyroPathOperations = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : self);
