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

  const TAU = Math.PI * 2;
  const EPSILON = 1e-9;
  const COMMAND_PARAMETERS = Object.freeze({
    M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7, Z: 0
  });
  const commandNames = new Set(Object.keys(COMMAND_PARAMETERS));
  const numberPattern = '[-+]?(?:\\d*\\.\\d+|\\d+\\.?)(?:[eE][-+]?\\d+)?';
  const numberAtStartPattern = new RegExp(`^${numberPattern}`);

  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const round = (value, digits = 6) => Number(finite(value).toFixed(digits));
  const point = (x = 0, y = 0) => ({ x: finite(x), y: finite(y) });
  const pointArray = (value) => [round(value.x), round(value.y)];
  const identityMatrix = () => [1, 0, 0, 1, 0, 0];

  function multiplyMatrices(left, right) {
    const [a1, b1, c1, d1, e1, f1] = left; const [a2, b2, c2, d2, e2, f2] = right;
    return [
      a1 * a2 + c1 * b2,
      b1 * a2 + d1 * b2,
      a1 * c2 + c1 * d2,
      b1 * c2 + d1 * d2,
      a1 * e2 + c1 * f2 + e1,
      b1 * e2 + d1 * f2 + f1
    ].map((value) => round(value, 12));
  }

  function applyMatrix(matrix, value) {
    const [a, b, c, d, e, f] = matrix; const x = finite(value?.x ?? value?.[0]); const y = finite(value?.y ?? value?.[1]);
    return { x: a * x + c * y + e, y: b * x + d * y + f };
  }

  function parseNumberList(value) {
    return String(value || '').match(new RegExp(numberPattern, 'g'))?.map(Number).filter(Number.isFinite) || [];
  }

  function parseTransform(value) {
    const source = String(value || '').trim();
    if (!source) return identityMatrix();
    let matrix = identityMatrix(); const expression = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
    for (const match of source.matchAll(expression)) {
      const name = match[1].toLowerCase(); const values = parseNumberList(match[2]); let next;
      if (name === 'matrix' && values.length >= 6) next = values.slice(0, 6);
      else if (name === 'translate' && values.length) next = [1, 0, 0, 1, values[0], values[1] || 0];
      else if (name === 'scale' && values.length) next = [values[0], 0, 0, values.length > 1 ? values[1] : values[0], 0, 0];
      else if (name === 'rotate' && values.length) {
        const angle = values[0] * Math.PI / 180; const cosine = Math.cos(angle); const sine = Math.sin(angle);
        const rotation = [cosine, sine, -sine, cosine, 0, 0];
        next = values.length >= 3
          ? multiplyMatrices([1, 0, 0, 1, values[1], values[2]], multiplyMatrices(rotation, [1, 0, 0, 1, -values[1], -values[2]]))
          : rotation;
      } else if (name === 'skewx' && values.length) next = [1, 0, Math.tan(values[0] * Math.PI / 180), 1, 0, 0];
      else if (name === 'skewy' && values.length) next = [1, Math.tan(values[0] * Math.PI / 180), 0, 1, 0, 0];
      else throw new Error(`Nicht unterstützte SVG-Transformation: ${match[1]}`);
      matrix = multiplyMatrices(matrix, next);
    }
    const remainder = source.replace(expression, '').replace(/[\s,]/g, '');
    if (remainder) throw new Error(`Ungültige SVG-Transformation: ${source}`);
    return matrix;
  }

  function vectorAngle(ux, uy, vx, vy) {
    const dot = ux * vx + uy * vy; const lengths = Math.hypot(ux, uy) * Math.hypot(vx, vy);
    if (lengths < EPSILON) return 0;
    const angle = Math.acos(Math.max(-1, Math.min(1, dot / lengths)));
    return ux * vy - uy * vx < 0 ? -angle : angle;
  }

  function arcToCubics(startValue, rxValue, ryValue, rotationValue, largeArcValue, sweepValue, endValue) {
    const start = point(startValue?.x ?? startValue?.[0], startValue?.y ?? startValue?.[1]);
    const end = point(endValue?.x ?? endValue?.[0], endValue?.y ?? endValue?.[1]);
    let rx = Math.abs(finite(rxValue)); let ry = Math.abs(finite(ryValue));
    if (rx < EPSILON || ry < EPSILON || Math.hypot(end.x - start.x, end.y - start.y) < EPSILON) return [];
    const rotation = finite(rotationValue) * Math.PI / 180; const cosine = Math.cos(rotation); const sine = Math.sin(rotation);
    const halfX = (start.x - end.x) / 2; const halfY = (start.y - end.y) / 2;
    const transformedX = cosine * halfX + sine * halfY; const transformedY = -sine * halfX + cosine * halfY;
    const scale = transformedX ** 2 / rx ** 2 + transformedY ** 2 / ry ** 2;
    if (scale > 1) { const factor = Math.sqrt(scale); rx *= factor; ry *= factor; }
    const rxSquared = rx ** 2; const rySquared = ry ** 2;
    const numerator = Math.max(0, rxSquared * rySquared - rxSquared * transformedY ** 2 - rySquared * transformedX ** 2);
    const denominator = Math.max(EPSILON, rxSquared * transformedY ** 2 + rySquared * transformedX ** 2);
    const direction = Boolean(largeArcValue) === Boolean(sweepValue) ? -1 : 1;
    const coefficient = direction * Math.sqrt(numerator / denominator);
    const centerPrimeX = coefficient * rx * transformedY / ry;
    const centerPrimeY = coefficient * -ry * transformedX / rx;
    const centerX = cosine * centerPrimeX - sine * centerPrimeY + (start.x + end.x) / 2;
    const centerY = sine * centerPrimeX + cosine * centerPrimeY + (start.y + end.y) / 2;
    const unitStartX = (transformedX - centerPrimeX) / rx; const unitStartY = (transformedY - centerPrimeY) / ry;
    const unitEndX = (-transformedX - centerPrimeX) / rx; const unitEndY = (-transformedY - centerPrimeY) / ry;
    let startAngle = vectorAngle(1, 0, unitStartX, unitStartY);
    let delta = vectorAngle(unitStartX, unitStartY, unitEndX, unitEndY);
    if (!sweepValue && delta > 0) delta -= TAU;
    if (sweepValue && delta < 0) delta += TAU;
    const count = Math.max(1, Math.ceil(Math.abs(delta) / (Math.PI / 2))); const step = delta / count;
    const position = (angle) => ({
      x: centerX + cosine * rx * Math.cos(angle) - sine * ry * Math.sin(angle),
      y: centerY + sine * rx * Math.cos(angle) + cosine * ry * Math.sin(angle)
    });
    const derivative = (angle) => ({
      x: -cosine * rx * Math.sin(angle) - sine * ry * Math.cos(angle),
      y: -sine * rx * Math.sin(angle) + cosine * ry * Math.cos(angle)
    });
    const result = [];
    for (let index = 0; index < count; index += 1) {
      const endAngle = startAngle + step; const from = position(startAngle); const to = position(endAngle);
      const fromDerivative = derivative(startAngle); const toDerivative = derivative(endAngle);
      const alpha = 4 / 3 * Math.tan(step / 4);
      result.push({
        c1: pointArray({ x: from.x + fromDerivative.x * alpha, y: from.y + fromDerivative.y * alpha }),
        c2: pointArray({ x: to.x - toDerivative.x * alpha, y: to.y - toDerivative.y * alpha }),
        end: index === count - 1 ? pointArray(end) : pointArray(to)
      });
      startAngle = endAngle;
    }
    return result;
  }

  function tokenizePath(value, { stopAtError = false } = {}) {
    const source = String(value || ''); const tokens = []; let cursor = 0;
    while (cursor < source.length) {
      const whitespace = source.slice(cursor).match(/^[\s,]+/);
      if (whitespace) { cursor += whitespace[0].length; continue; }
      const character = source[cursor];
      if (/[a-z]/i.test(character)) { tokens.push(character); cursor += 1; continue; }
      const number = source.slice(cursor).match(numberAtStartPattern);
      if (number) { tokens.push(number[0]); cursor += number[0].length; continue; }
      if (stopAtError) break;
      throw new Error(`Ungültige Zeichen im SVG-Pfad: ${source.slice(cursor, cursor + 24)}`);
    }
    return tokens;
  }

  function parsePath(value, { maximumCommands = 100000, stopAtError = true } = {}) {
    const tokens = tokenizePath(value, { stopAtError }); const paths = []; let index = 0; let command = null; let commandCount = 0; let invalid = false;
    let current = point(); let subpath = null; let previousCommand = ''; let previousCubic = null; let previousQuadratic = null;
    const isCommand = (token) => /^[a-z]$/i.test(token || '');
    const hasNumbers = () => index < tokens.length && !isCommand(tokens[index]);
    const take = (count) => {
      if (index + count > tokens.length || tokens.slice(index, index + count).some(isCommand)) {
        if (stopAtError) { invalid = true; return null; }
        throw new Error(`SVG-Pfadkommando ${command} ist unvollständig`);
      }
      const values = tokens.slice(index, index + count).map(Number); index += count;
      if (values.some((entry) => !Number.isFinite(entry))) throw new Error(`SVG-Pfadkommando ${command} enthält ungültige Zahlen`);
      return values;
    };
    const finish = () => {
      if (subpath?.points.length) paths.push(subpath);
      subpath = null;
    };
    const startAt = (target) => {
      finish(); current = target; subpath = { points: [pointArray(target)], segments: [null], markerVertices: [true], closed: false };
    };
    const ensureSubpath = () => {
      if (!subpath) startAt(current);
    };
    const addSegment = (target, segment = null, markerVertex = true) => {
      ensureSubpath(); subpath.segments[subpath.points.length - 1] = segment;
      subpath.points.push(pointArray(target)); subpath.segments.push(null); subpath.markerVertices.push(markerVertex); current = point(target.x, target.y);
    };
    const relativePoint = (x, y, relative) => point(relative ? current.x + x : x, relative ? current.y + y : y);
    const resetControls = () => { previousCubic = null; previousQuadratic = null; };

    while (index < tokens.length) {
      if (isCommand(tokens[index])) command = tokens[index++];
      else if (!command) {
        if (stopAtError) break;
        throw new Error('SVG-Pfad beginnt nicht mit einem Kommando');
      }
      const upper = command.toUpperCase(); const relative = command !== upper;
      if (!commandNames.has(upper)) {
        if (stopAtError) break;
        throw new Error(`Nicht unterstütztes SVG-Pfadkommando: ${command}`);
      }
      commandCount += 1;
      if (commandCount > maximumCommands) throw new Error('SVG-Pfad enthält zu viele Kommandos');
      if (upper === 'Z') {
        if (subpath?.points.length) { subpath.closed = true; current = point(subpath.points[0][0], subpath.points[0][1]); }
        resetControls(); previousCommand = 'Z'; command = null; continue;
      }
      if (!hasNumbers()) {
        if (stopAtError) break;
        throw new Error(`SVG-Pfadkommando ${command} besitzt keine Parameter`);
      }
      let firstSet = true;
      while (hasNumbers()) {
        const parameterCount = COMMAND_PARAMETERS[upper]; const values = take(parameterCount); if (!values) break;
        if (upper === 'M') {
          const target = relativePoint(values[0], values[1], relative);
          if (firstSet) startAt(target); else addSegment(target);
          resetControls(); previousCommand = firstSet ? 'M' : 'L';
        } else if (upper === 'L') {
          addSegment(relativePoint(values[0], values[1], relative)); resetControls(); previousCommand = 'L';
        } else if (upper === 'H') {
          addSegment(point(relative ? current.x + values[0] : values[0], current.y)); resetControls(); previousCommand = 'H';
        } else if (upper === 'V') {
          addSegment(point(current.x, relative ? current.y + values[0] : values[0])); resetControls(); previousCommand = 'V';
        } else if (upper === 'C') {
          const c1 = relativePoint(values[0], values[1], relative); const c2 = relativePoint(values[2], values[3], relative); const target = relativePoint(values[4], values[5], relative);
          addSegment(target, { c1: pointArray(c1), c2: pointArray(c2) }); previousCubic = c2; previousQuadratic = null; previousCommand = 'C';
        } else if (upper === 'S') {
          const c1 = ['C', 'S'].includes(previousCommand) && previousCubic ? point(current.x * 2 - previousCubic.x, current.y * 2 - previousCubic.y) : point(current.x, current.y);
          const c2 = relativePoint(values[0], values[1], relative); const target = relativePoint(values[2], values[3], relative);
          addSegment(target, { c1: pointArray(c1), c2: pointArray(c2) }); previousCubic = c2; previousQuadratic = null; previousCommand = 'S';
        } else if (upper === 'Q') {
          const control = relativePoint(values[0], values[1], relative); const target = relativePoint(values[2], values[3], relative);
          const c1 = point(current.x + (control.x - current.x) * 2 / 3, current.y + (control.y - current.y) * 2 / 3);
          const c2 = point(target.x + (control.x - target.x) * 2 / 3, target.y + (control.y - target.y) * 2 / 3);
          addSegment(target, { c1: pointArray(c1), c2: pointArray(c2) }); previousQuadratic = control; previousCubic = null; previousCommand = 'Q';
        } else if (upper === 'T') {
          const control = ['Q', 'T'].includes(previousCommand) && previousQuadratic ? point(current.x * 2 - previousQuadratic.x, current.y * 2 - previousQuadratic.y) : point(current.x, current.y);
          const target = relativePoint(values[0], values[1], relative);
          const c1 = point(current.x + (control.x - current.x) * 2 / 3, current.y + (control.y - current.y) * 2 / 3);
          const c2 = point(target.x + (control.x - target.x) * 2 / 3, target.y + (control.y - target.y) * 2 / 3);
          addSegment(target, { c1: pointArray(c1), c2: pointArray(c2) }); previousQuadratic = control; previousCubic = null; previousCommand = 'T';
        } else if (upper === 'A') {
          const target = relativePoint(values[5], values[6], relative);
          const curves = arcToCubics(current, values[0], values[1], values[2], values[3] !== 0, values[4] !== 0, target);
          if (!curves.length && Math.hypot(target.x - current.x, target.y - current.y) >= EPSILON) addSegment(target);
          else curves.forEach((curve, curveIndex) => addSegment(
            point(curve.end[0], curve.end[1]), { c1: curve.c1, c2: curve.c2 }, curveIndex === curves.length - 1
          ));
          previousCubic = curves.length ? point(curves.at(-1).c2[0], curves.at(-1).c2[1]) : null; previousQuadratic = null; previousCommand = 'A';
        }
        firstSet = false;
        if (upper === 'M') command = relative ? 'l' : 'L';
      }
      if (invalid) break;
    }
    finish();
    return paths.filter((path) => path.points.length > 0);
  }

  function transformSubpaths(paths, matrix) {
    return (paths || []).map((path) => ({
      points: (path.points || []).map((entry) => pointArray(applyMatrix(matrix, entry))),
      segments: (path.segments || []).map((segment) => segment ? {
        c1: pointArray(applyMatrix(matrix, segment.c1)), c2: pointArray(applyMatrix(matrix, segment.c2))
      } : null),
      markerVertices: Array.isArray(path.markerVertices) ? path.markerVertices.map((value) => value !== false) : undefined,
      closed: Boolean(path.closed)
    }));
  }

  function cubicAt(p0, p1, p2, p3, t) {
    const inverse = 1 - t;
    return inverse ** 3 * p0 + 3 * inverse ** 2 * t * p1 + 3 * inverse * t ** 2 * p2 + t ** 3 * p3;
  }

  function cubicExtrema(p0, p1, p2, p3) {
    const a = -p0 + 3 * p1 - 3 * p2 + p3; const b = 2 * (p0 - 2 * p1 + p2); const c = p1 - p0;
    if (Math.abs(a) < EPSILON) return Math.abs(b) < EPSILON ? [] : [-c / b].filter((value) => value > 0 && value < 1);
    const discriminant = b ** 2 - 4 * a * c;
    if (discriminant < 0) return [];
    const root = Math.sqrt(discriminant);
    return [(-b + root) / (2 * a), (-b - root) / (2 * a)].filter((value) => value > 0 && value < 1);
  }

  function subpathBounds(paths) {
    const xs = []; const ys = [];
    (paths || []).forEach((path) => {
      const points = path.points || [];
      points.forEach(([x, y]) => { xs.push(finite(x)); ys.push(finite(y)); });
      const count = path.closed ? points.length : Math.max(0, points.length - 1);
      for (let index = 0; index < count; index += 1) {
        const segment = path.segments?.[index]; if (!segment) continue;
        const start = points[index]; const end = points[(index + 1) % points.length];
        cubicExtrema(start[0], segment.c1[0], segment.c2[0], end[0]).forEach((t) => xs.push(cubicAt(start[0], segment.c1[0], segment.c2[0], end[0], t)));
        cubicExtrema(start[1], segment.c1[1], segment.c2[1], end[1]).forEach((t) => ys.push(cubicAt(start[1], segment.c1[1], segment.c2[1], end[1], t)));
      }
    });
    if (!xs.length) return { x: 0, y: 0, width: 0, height: 0, right: 0, bottom: 0 };
    const x = Math.min(...xs); const y = Math.min(...ys); const right = Math.max(...xs); const bottom = Math.max(...ys);
    return { x: round(x), y: round(y), width: round(right - x), height: round(bottom - y), right: round(right), bottom: round(bottom) };
  }

  function normalizeSubpaths(paths) {
    const bounds = subpathBounds(paths);
    const matrix = [1, 0, 0, 1, -bounds.x, -bounds.y];
    return { paths: transformSubpaths(paths, matrix), bounds };
  }

  const api = Object.freeze({
    identityMatrix, multiplyMatrices, applyMatrix, parseTransform,
    tokenizePath, parsePath, arcToCubics, transformSubpaths, subpathBounds, normalizeSubpaths
  });
  root.PyroSvgPath = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : window);
