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

  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const clamp = (value, minimum, maximum) => Math.min(maximum, Math.max(minimum, finite(value, minimum)));
  const round = (value, precision = 3) => {
    const factor = 10 ** precision;
    return Math.round(finite(value) * factor) / factor;
  };
  const clone = (value) => value == null ? value : JSON.parse(JSON.stringify(value));
  const pointDistance = (a, b) => Math.hypot(finite(a?.[0]) - finite(b?.[0]), finite(a?.[1]) - finite(b?.[1]));
  const normalizeAngle = (value) => {
    const angle = finite(value) % 360;
    return angle < -180 ? angle + 360 : angle > 180 ? angle - 360 : angle;
  };
  const angleAt = (cx, cy, x, y) => Math.atan2(finite(y) - cy, finite(x) - cx) * 180 / Math.PI;
  const polar = (cx, cy, rx, ry, angle) => {
    const radians = finite(angle) * Math.PI / 180;
    return [round(cx + Math.cos(radians) * rx), round(cy + Math.sin(radians) * ry)];
  };
  const pathPoint = (point) => `${round(point[0])} ${round(point[1])}`;
  const percent = (value, fallback = 0, minimum = 0, maximum = 100) => clamp(value ?? fallback, minimum, maximum);
  const pxMaximum = (object) => Math.max(1, Math.min(Math.max(1, finite(object?.width, 1)), Math.max(1, finite(object?.height, 1))) / 2);
  const normalizedPiePositions = (value, sections) => {
    const count = Math.max(1, Math.round(finite(sections, 1)));
    const positions = Array.isArray(value)
      ? value.map((entry) => finite(entry, NaN)).filter(Number.isFinite).sort((a, b) => a - b)
      : [];
    const valid = positions.length === count - 1
      && positions.every((entry, index) => entry > .001 && entry < .999 && (!index || entry - positions[index - 1] > .001));
    return valid ? positions.map((entry) => round(entry, 6)) : Array.from({ length: count - 1 }, (_, index) => round((index + 1) / count, 6));
  };

  const types = Object.freeze([
    'block-arrow', 'arrow', 'beveled-rectangle', 'chamfer-rectangle', 'connector-line', 'doughnut',
    'l-shape', 'measure', 'pie', 'rounded-rectangle', 'smart-polygon', 'spiral', 'star'
  ]);
  const typeSet = new Set(types);
  const arrowHeadStyles = Object.freeze(['none', 'open', 'triangle', 'bar', 'circle', 'diamond']);
  const cornerStyles = Object.freeze(['round', 'bevel', 'chamfer']);
  const cornerLabels = Object.freeze(['OL', 'OR', 'UR', 'UL']);

  const numberField = (key, label, minimum, maximum, step = 1, unit = '') => Object.freeze({ key, label, type: 'number', min: minimum, max: maximum, step, unit });
  const checkboxField = (key, label) => Object.freeze({ key, label, type: 'checkbox' });
  const selectField = (key, label, options) => Object.freeze({ key, label, type: 'select', options: Object.freeze(options) });

  const cornerFields = Object.freeze([
    numberField('radius', 'Eckenmaß alle', -10000, 10000, 1, 'px'),
    checkboxField('cornersLocked', 'Ecken koppeln'),
    selectField('cornerStyle', 'Eckentyp', [
      ['round', 'Rund'], ['bevel', 'Abgeschrägt'], ['chamfer', 'Chamfer']
    ]),
    ...cornerLabels.map((label, index) => Object.freeze({ ...numberField(`radius${index}`, `Eckenmaß ${label}`, -10000, 10000, 1, 'px'), showWhen: 'cornersUnlocked' })),
    ...cornerLabels.map((label, index) => Object.freeze({ ...selectField(`cornerStyle${index}`, `Typ ${label}`, [
      ['round', 'Rund'], ['bevel', 'Abgeschrägt'], ['chamfer', 'Chamfer']
    ]), showWhen: 'cornersUnlocked' }))
  ]);

  const definitions = Object.freeze({
    'block-arrow': Object.freeze({ label: 'Blockpfeil', fields: Object.freeze([
      numberField('headLength', 'Kopflänge', 5, 90, 1, '%'),
      numberField('headHeight', 'Kopfhöhe', 10, 100, 1, '%'),
      numberField('shaftHeight', 'Schaftbreite', 2, 95, 1, '%'),
      numberField('tipInset', 'Spitze', 0, 45, 1, '%')
    ]) }),
    arrow: Object.freeze({ label: 'Pfeillinie', fields: Object.freeze([
      selectField('startHead', 'Anfang', arrowHeadStyles.map((value) => [value, ({ none: 'Keine', open: 'Offen', triangle: 'Dreieck', bar: 'Strich', circle: 'Kreis', diamond: 'Raute' })[value]])),
      selectField('endHead', 'Ende', arrowHeadStyles.map((value) => [value, ({ none: 'Keine', open: 'Offen', triangle: 'Dreieck', bar: 'Strich', circle: 'Kreis', diamond: 'Raute' })[value]])),
      numberField('headSize', 'Pfeilgröße', 3, 200, 1, 'px')
    ]) }),
    'beveled-rectangle': Object.freeze({ label: 'Abgeschrägtes Rechteck', fields: cornerFields }),
    'chamfer-rectangle': Object.freeze({ label: 'Chamfer-Rechteck', fields: cornerFields }),
    'rounded-rectangle': Object.freeze({ label: 'Rundes Rechteck', fields: cornerFields }),
    'connector-line': Object.freeze({ label: 'Verbinderlinie', fields: Object.freeze([
      numberField('startY', 'Anfang Y', 0, 100, 1, '%'), numberField('endY', 'Ende Y', 0, 100, 1, '%'),
      numberField('cross', 'Quersegment', 0, 100, 1, '%'), numberField('radiusA', 'Rundung 1', 0, 10000, 1, 'px'),
      numberField('radiusB', 'Rundung 2', 0, 10000, 1, 'px')
    ]) }),
    doughnut: Object.freeze({ label: 'Ring', fields: Object.freeze([
      numberField('inner', 'Innenradius', 0, 95, 1, '%'), numberField('start', 'Start', -180, 180, 1, '°'),
      numberField('sweep', 'Winkel', 1, 360, 1, '°'), numberField('sections', 'Segmente', 1, 32, 1, '')
    ]) }),
    'l-shape': Object.freeze({ label: 'L-Form', fields: Object.freeze([
      numberField('thickness', 'Armstärke', 1, 10000, 1, 'px'), numberField('roundness', 'Knickrundung', -10000, 10000, 1, 'px')
    ]) }),
    measure: Object.freeze({ label: 'Maßband', fields: Object.freeze([
      selectField('unit', 'Maßeinheit', [['px', 'Pixel'], ['in', 'Zoll']])
    ]) }),
    pie: Object.freeze({ label: 'Torte', fields: Object.freeze([
      numberField('start', 'Start', -180, 180, 1, '°'), numberField('sweep', 'Winkel', 0, 360, 1, '°'),
      numberField('coverage', 'Anteil', 0, 100, 1, '%'), numberField('sections', 'Segmente', 1, 32, 1, ''),
      numberField('inner', 'Innenradius', 0, 100, 1, '%')
    ]) }),
    'smart-polygon': Object.freeze({ label: 'Intelligentes Polygon', fields: Object.freeze([
      numberField('sides', 'Seiten', 3, 25, 1, ''), numberField('rotation', 'Drehung', -180, 180, 1, '°'),
      numberField('inner', 'Innenpolygon', 0, 90, 1, '%'), numberField('sections', 'Segmente', 1, 25, 1, '')
    ]) }),
    spiral: Object.freeze({ label: 'Spirale', fields: Object.freeze([
      numberField('turns', 'Windungen', 0.5, 12, .1, ''), checkboxField('closed', 'Spirale schließen')
    ]) }),
    star: Object.freeze({ label: 'Stern', fields: Object.freeze([
      numberField('points', 'Zacken', 3, 25, 1, ''), numberField('inner', 'Tiefe', 5, 95, 1, '%'),
      numberField('rotation', 'Drehung', -180, 180, 1, '°'), numberField('peakRoundness', 'Spitzen rund', 0, 45, 1, '%'),
      numberField('valleyRoundness', 'Täler rund', 0, 45, 1, '%')
    ]) })
  });

  function isAutoShape(value) {
    return typeSet.has(typeof value === 'string' ? value : value?.type);
  }

  function defaultCornerStyle(type) {
    if (type === 'rounded-rectangle') return 'round';
    if (type === 'chamfer-rectangle') return 'chamfer';
    return 'bevel';
  }

  function normalizedCornerRadius(value, style, maximum, fallback = 0) {
    const radius = clamp(value ?? fallback, -maximum, maximum);
    return style === 'round' ? radius : Math.abs(radius);
  }

  function cornerHandlePoint(index, width, height, radius, style) {
    const amount = Math.abs(finite(radius));
    if (style === 'round' && radius < 0) {
      return [index === 1 || index === 2 ? width : 0, index >= 2 ? height - amount : amount];
    }
    return [index === 1 || index === 2 ? width - amount : amount, index >= 2 ? height : 0];
  }

  function normalizedParams(type, raw = {}, object = {}) {
    const source = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1));
    const maximumRadius = Math.max(0, Math.min(width, height) / 2);
    if (['rounded-rectangle', 'beveled-rectangle', 'chamfer-rectangle'].includes(type)) {
      const fallback = Math.min(24, width / 5, height / 5);
      const radii = Array.isArray(source.cornerRadii)
        ? source.cornerRadii
        : [source.radius ?? fallback, source.radius ?? fallback, source.radius ?? fallback, source.radius ?? fallback];
      const styles = Array.isArray(source.cornerStyles) ? source.cornerStyles : [];
      const normalizedStyles = Array.from({ length: 4 }, (_, index) => cornerStyles.includes(styles[index]) ? styles[index] : defaultCornerStyle(type));
      return {
        cornerRadii: Array.from({ length: 4 }, (_, index) => normalizedCornerRadius(radii[index], normalizedStyles[index], maximumRadius || fallback, fallback)),
        cornerStyles: normalizedStyles,
        cornersLocked: source.cornersLocked !== false
      };
    }
    if (type === 'block-arrow') return {
      headLength: percent(source.headLength, 30, 5, 90), headHeight: percent(source.headHeight, 100, 10, 100),
      shaftHeight: percent(source.shaftHeight, 52, 2, 95), tipInset: percent(source.tipInset, 0, 0, 45)
    };
    if (type === 'arrow') return {
      startHead: arrowHeadStyles.includes(source.startHead) ? source.startHead : 'none',
      endHead: arrowHeadStyles.includes(source.endHead) ? source.endHead : 'open',
      headSize: clamp(source.headSize ?? Math.max(9, finite(object.strokeWidth, 3) * 3.6), 3, 200)
    };
    if (type === 'connector-line') return {
      startY: percent(source.startY, 0), endY: percent(source.endY, 100), cross: percent(source.cross, 50),
      radiusA: clamp(source.radiusA ?? 0, 0, Math.max(width, height)), radiusB: clamp(source.radiusB ?? 0, 0, Math.max(width, height))
    };
    if (type === 'doughnut') return {
      inner: percent(source.inner, 46, 0, 95), start: normalizeAngle(source.start ?? -90),
      sweep: clamp(source.sweep ?? 360, 1, 360), sections: clamp(Math.round(finite(source.sections, 1)), 1, 32)
    };
    if (type === 'l-shape') {
      const maximum = Math.max(1, Math.min(width, height) - 1); const fallback = clamp(40, 1, maximum);
      const thickness = clamp(source.thickness ?? object.lThickness ?? fallback, 1, maximum);
      return { thickness, roundness: clamp(source.roundness ?? object.lRoundness ?? 0, -thickness, thickness) };
    }
    if (type === 'measure') return { unit: source.unit === 'in' ? 'in' : 'px' };
    if (type === 'pie') {
      const sections = clamp(Math.round(finite(source.sections, 1)), 1, 32);
      return {
        start: normalizeAngle(source.start ?? -90), sweep: clamp(source.sweep ?? 90, 0, 360), sections,
        segmentPositions: normalizedPiePositions(source.segmentPositions, sections), inner: percent(source.inner, 0, 0, 100)
      };
    }
    if (type === 'smart-polygon') {
      const sides = clamp(Math.round(finite(source.sides, 5)), 3, 25);
      return { sides, rotation: normalizeAngle(source.rotation ?? -90), inner: percent(source.inner, 0, 0, 90), sections: clamp(Math.round(finite(source.sections, 1)), 1, sides) };
    }
    if (type === 'spiral') return { turns: clamp(source.turns ?? 2.3, .5, 12), closed: Boolean(source.closed) };
    if (type === 'star') return {
      points: clamp(Math.round(finite(source.points, 5)), 3, 25), inner: percent(source.inner, 44, 5, 95),
      rotation: normalizeAngle(source.rotation ?? -90), peakRoundness: percent(source.peakRoundness, 0, 0, 45),
      valleyRoundness: percent(source.valleyRoundness, 0, 0, 45)
    };
    return {};
  }

  function ensure(object) {
    if (!isAutoShape(object)) return null;
    object.autoShape = normalizedParams(object.type, object.autoShape, object);
    if (object.type === 'l-shape') {
      object.lThickness = object.autoShape.thickness;
      object.lRoundness = object.autoShape.roundness;
    }
    return object.autoShape;
  }

  function sanitize(type, value, object = {}) {
    return isAutoShape(type) ? normalizedParams(type, value, { ...object, type }) : undefined;
  }

  function definitionFor(value) {
    return definitions[typeof value === 'string' ? value : value?.type] || null;
  }

  function propertyFields(object) {
    const params = ensure(object); const definition = definitionFor(object);
    if (!params || !definition) return [];
    return definition.fields.filter((field) => field.showWhen !== 'cornersUnlocked' || params.cornersLocked === false);
  }

  function getProperty(object, key) {
    const params = ensure(object); if (!params) return undefined;
    if (object.type === 'pie' && key === 'coverage') return round(params.sweep / 3.6, 2);
    if (key === 'radius') return round(params.cornerRadii?.[0] || 0);
    if (/^radius[0-3]$/.test(key)) return round(params.cornerRadii?.[Number(key.at(-1))] || 0);
    if (key === 'cornerStyle') return params.cornerStyles?.every((style) => style === params.cornerStyles[0]) ? params.cornerStyles[0] : params.cornerStyles?.[0];
    if (/^cornerStyle[0-3]$/.test(key)) return params.cornerStyles?.[Number(key.at(-1))];
    return params[key];
  }

  function setProperty(object, key, value) {
    const params = ensure(object); if (!params) return false;
    const before = JSON.stringify(params);
    if (object.type === 'pie' && key === 'coverage') params.sweep = clamp(value, 0, 100) * 3.6;
    else if (key === 'radius') params.cornerRadii = params.cornerRadii.map((radius, index) => normalizedCornerRadius(value, params.cornerStyles[index], pxMaximum(object), radius));
    else if (/^radius[0-3]$/.test(key)) {
      const index = Number(key.at(-1)); params.cornerRadii[index] = normalizedCornerRadius(value, params.cornerStyles[index], pxMaximum(object), params.cornerRadii[index]);
    }
    else if (key === 'cornerStyle' && cornerStyles.includes(value)) {
      params.cornerStyles = params.cornerStyles.map(() => value);
      if (value !== 'round') params.cornerRadii = params.cornerRadii.map(Math.abs);
    }
    else if (/^cornerStyle[0-3]$/.test(key) && cornerStyles.includes(value)) {
      const index = Number(key.at(-1)); params.cornerStyles[index] = value;
      if (value !== 'round') params.cornerRadii[index] = Math.abs(params.cornerRadii[index]);
    }
    else if (key === 'cornersLocked') {
      params.cornersLocked = Boolean(value);
      if (params.cornersLocked) {
        params.cornerRadii = params.cornerRadii.map(() => params.cornerRadii[0]);
        params.cornerStyles = params.cornerStyles.map(() => params.cornerStyles[0]);
      }
    } else if (key === 'closed') params.closed = Boolean(value);
    else params[key] = value;
    object.autoShape = normalizedParams(object.type, params, object);
    if (object.type === 'l-shape') { object.lThickness = object.autoShape.thickness; object.lRoundness = object.autoShape.roundness; }
    return JSON.stringify(object.autoShape) !== before;
  }

  function control(id, label, point, cursor = 'move', behavior = 'drag') {
    return Object.freeze({ id, label, x: round(point[0]), y: round(point[1]), cursor, behavior });
  }

  function controls(object) {
    const params = ensure(object); if (!params) return [];
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1));
    const cx = width / 2; const cy = height / 2;
    if (['rounded-rectangle', 'beveled-rectangle', 'chamfer-rectangle'].includes(object.type)) {
      return params.cornerRadii.map((radius, index) => control(
        `corner-${index}`,
        `Ecke ${cornerLabels[index]} · Rund: horizontal konvex, vertikal konkav · Alt-Ziehen: einzeln · Klick: Typ`,
        cornerHandlePoint(index, width, height, radius, params.cornerStyles[index]), 'move'
      ));
    }
    if (object.type === 'block-arrow') {
      const baseX = width * (1 - params.headLength / 100); const headTop = height * (1 - params.headHeight / 100) / 2;
      const shaftTop = height * (1 - params.shaftHeight / 100) / 2; const tipX = width * (1 - params.tipInset / 100);
      return [
        control('head-length', 'Länge der Pfeilspitze', [baseX, cy], 'ew-resize'),
        control('head-height', 'Aufweitung des Pfeilkopfs', [baseX, headTop], 'ns-resize'),
        control('shaft-height', 'Breite des Pfeilschafts', [0, shaftTop], 'ns-resize'),
        control('tip', 'Schärfe der Pfeilspitze', [tipX, cy], 'ew-resize')
      ];
    }
    if (object.type === 'arrow') {
      const points = Array.isArray(object.points) && object.points.length >= 2 ? object.points : [[0, 0], [width, height]];
      return [control('start-head', 'Pfeilspitze am Anfang wechseln', points[0], 'pointer', 'click'), control('end-head', 'Pfeilspitze am Ende wechseln', points.at(-1), 'pointer', 'click')];
    }
    if (object.type === 'connector-line') {
      const startY = height * params.startY / 100; const endY = height * params.endY / 100; const crossX = width * params.cross / 100;
      const direction = Math.sign(endY - startY) || 1; const maximum = Math.min(Math.abs(endY - startY) / 2, Math.max(width, height));
      return [
        control('start', 'Anfangspunkt', [0, startY], 'ns-resize'), control('end', 'Endpunkt', [width, endY], 'ns-resize'),
        control('cross', 'Position des Quersegments', [crossX, (startY + endY) / 2], 'ew-resize'),
        control('radius-a', 'Rundung der ersten Ecke', [crossX - Math.min(params.radiusA, maximum), startY + direction * Math.min(params.radiusA, maximum)], 'move'),
        control('radius-b', 'Rundung der zweiten Ecke', [crossX + Math.min(params.radiusB, maximum), endY - direction * Math.min(params.radiusB, maximum)], 'move')
      ];
    }
    if (object.type === 'doughnut') {
      const inner = polar(cx, cy, width * params.inner / 200, height * params.inner / 200, 0);
      const end = polar(cx, cy, width / 2, height / 2, params.start + params.sweep);
      return [control('inner', 'Innenradius', inner, 'ew-resize'), control('reset-inner', 'Innenradius auf null setzen', [cx, cy], 'pointer', 'click'), control('sector', 'Ringsegment · Alt-Klick fügt ein Segment hinzu', end, 'crosshair')];
    }
    if (object.type === 'l-shape') return [
      control('roundness', 'Knick-Rundung: rechts innen, links außen', [params.roundness, 0], 'ew-resize'),
      control('horizontal', 'Horizontale Armlänge', [width, params.thickness / 2], 'ew-resize'),
      control('thickness', 'Gemeinsame Armstärke', [params.thickness, height], 'ew-resize'),
      control('vertical', 'Vertikale Armlänge', [params.thickness / 2, height], 'ns-resize')
    ];
    if (object.type === 'measure') {
      const points = Array.isArray(object.points) && object.points.length >= 2 ? object.points : [[0, 0], [width, height]];
      return [control('endpoint-0', 'Messpunkt 1', points[0], 'move'), control('endpoint-1', 'Messpunkt 2', points.at(-1), 'move')];
    }
    if (object.type === 'pie') {
      const resetDistance = Math.min(width, height) * .12;
      return [
        control('start', 'Beginn des ersten Tortenstücks', polar(cx, cy, width / 2, height / 2, params.start), 'crosshair'),
        ...params.segmentPositions.map((position, index) => control(
          `segment-${index + 1}`, `Grenze zwischen Tortenstück ${index + 1} und ${index + 2}`,
          polar(cx, cy, width / 2, height / 2, params.start + params.sweep * position), 'crosshair'
        )),
        control('end', 'Ende des letzten Tortenstücks · Alt-Klick fügt ein Segment hinzu', polar(cx, cy, width / 2, height / 2, params.start + params.sweep), 'crosshair'),
        control('inner', 'Innenradius 0–100 %', polar(cx, cy, width * params.inner / 200, height * params.inner / 200, params.start + params.sweep / 2), 'move'),
        control('reset', 'Auf ein volles Segment zurücksetzen', polar(cx, cy, resetDistance, resetDistance, params.start + params.sweep / 2 + 90), 'pointer', 'click')
      ];
    }
    if (object.type === 'smart-polygon') {
      const countX = width * (params.sides - 3) / 22;
      const inner = polar(cx, cy, width * params.inner / 200, height * params.inner / 200, params.rotation);
      const rotate = polar(cx, cy, width / 2, height / 2, params.rotation);
      const sectionY = height * (params.sections - 1) / Math.max(1, params.sides - 1);
      return [
        control('rotate', 'Polygon drehen', rotate, 'crosshair'), control('sides', 'Seitenzahl', [countX, 0], 'ew-resize'),
        control('inner', 'Inneres Polygon · Klick setzt es zurück', inner, 'ew-resize'), control('sections', 'Segmente', [width, sectionY], 'ns-resize')
      ];
    }
    if (object.type === 'spiral') {
      const turnsY = height * (12 - params.turns) / 11.5;
      return [control('turns', 'Anzahl der Windungen', [width, turnsY], 'ns-resize'), control('toggle', params.closed ? 'Spirale öffnen' : 'Spirale schließen', [cx, cy], 'pointer', 'click')];
    }
    if (object.type === 'star') {
      const outer = polar(cx, cy, width / 2, height / 2, params.rotation);
      const inner = polar(cx, cy, width * params.inner / 200, height * params.inner / 200, params.rotation + 180 / params.points);
      const peakRound = polar(cx, cy, width * (1 - params.peakRoundness / 50) / 2, height * (1 - params.peakRoundness / 50) / 2, params.rotation + 28 / params.points);
      const valleyRound = polar(cx, cy, width * params.inner / 200 * (1 - params.valleyRoundness / 50), height * params.inner / 200 * (1 - params.valleyRoundness / 50), params.rotation + 208 / params.points);
      return [
        control('points', 'Anzahl der Zacken', [width * (params.points - 3) / 22, 0], 'ew-resize'),
        control('valley', 'Tiefe der Täler', inner, 'move'), control('peak', 'Winkel der Spitzen', outer, 'crosshair'),
        control('peak-round', 'Rundung der Spitzen', peakRound, 'move'),
        control('valley-round', 'Rundung der Täler', valleyRound, 'move')
      ];
    }
    return [];
  }

  function restoreParams(object, origin) {
    object.autoShape = normalizedParams(origin.type, clone(origin.autoShape), origin);
    if (object.type === 'l-shape') { object.lThickness = object.autoShape.thickness; object.lRoundness = object.autoShape.roundness; }
    return object.autoShape;
  }

  function dragControl(object, id, local, options = {}, origin = object) {
    if (!isAutoShape(object) || !isAutoShape(origin)) return false;
    const params = restoreParams(object, origin); const width = Math.max(1, finite(origin.width, 1)); const height = Math.max(1, finite(origin.height, 1));
    const x = finite(local?.x); const y = finite(local?.y); const cx = width / 2; const cy = height / 2;
    if (/^corner-[0-3]$/.test(id)) {
      const index = Number(id.at(-1)); const maximum = Math.min(width, height) / 2;
      const horizontal = clamp(index === 1 || index === 2 ? width - x : x, 0, maximum);
      const vertical = clamp(index >= 2 ? height - y : y, 0, maximum);
      let radius = horizontal;
      if (params.cornerStyles[index] === 'round') {
        const start = cornerHandlePoint(index, width, height, params.cornerRadii[index], params.cornerStyles[index]);
        const deltaX = Math.abs(x - start[0]); const deltaY = Math.abs(y - start[1]);
        radius = deltaY > deltaX ? -vertical : deltaX > deltaY ? horizontal : params.cornerRadii[index];
      }
      if (options.altKey || params.cornersLocked === false) {
        params.cornerRadii[index] = radius;
        if (options.altKey) params.cornersLocked = false;
      }
      else params.cornerRadii = params.cornerRadii.map(() => radius);
    } else if (object.type === 'block-arrow') {
      if (id === 'head-length') params.headLength = percent((width - x) / width * 100, params.headLength, 5, 90);
      if (id === 'head-height') params.headHeight = percent((1 - Math.abs(y - cy) * 2 / height) * 100, params.headHeight, 10, 100);
      if (id === 'shaft-height') params.shaftHeight = percent((1 - Math.abs(y - cy) * 2 / height) * 100, params.shaftHeight, 2, 95);
      if (id === 'tip') params.tipInset = percent((width - x) / width * 100, params.tipInset, 0, 45);
    } else if (object.type === 'connector-line') {
      if (id === 'start') params.startY = percent(y / height * 100, params.startY);
      if (id === 'end') params.endY = percent(y / height * 100, params.endY);
      if (id === 'cross') params.cross = percent(x / width * 100, params.cross);
      if (id === 'radius-a') params.radiusA = clamp(Math.hypot(x - width * params.cross / 100, y - height * params.startY / 100) / Math.SQRT2, 0, Math.max(width, height));
      if (id === 'radius-b') params.radiusB = clamp(Math.hypot(x - width * params.cross / 100, y - height * params.endY / 100) / Math.SQRT2, 0, Math.max(width, height));
    } else if (object.type === 'doughnut') {
      if (id === 'inner') params.inner = percent(Math.hypot((x - cx) / (width / 2), (y - cy) / (height / 2)) * 100, params.inner, 0, 95);
      if (id === 'sector') {
        const end = angleAt(cx, cy, x, y); let sweep = end - params.start; while (sweep <= 0) sweep += 360;
        params.sweep = clamp(sweep, 1, 360);
      }
    } else if (object.type === 'l-shape') {
      if (id === 'roundness') params.roundness = clamp(x, -params.thickness, params.thickness);
      if (id === 'horizontal') object.width = Math.max(params.thickness + 1, x);
      if (id === 'vertical') object.height = Math.max(params.thickness + 1, y);
      if (id === 'thickness') params.thickness = clamp(x, 1, Math.max(1, Math.min(width, height) - 1));
      params.roundness = clamp(params.roundness, -params.thickness, params.thickness);
    } else if (object.type === 'measure' && /^endpoint-[01]$/.test(id)) {
      const index = Number(id.at(-1)); const points = clone(Array.isArray(origin.points) ? origin.points : [[0, 0], [width, height]]);
      points[index] = [round(clamp(x, 0, width)), round(clamp(y, 0, height))]; object.points = points;
    } else if (object.type === 'pie') {
      if (id === 'start') params.start = normalizeAngle(angleAt(cx, cy, x, y));
      if (id === 'end') {
        let sweep = angleAt(cx, cy, x, y) - params.start; while (sweep <= 0) sweep += 360;
        params.sweep = clamp(sweep, 0, 360);
      }
      if (/^segment-\d+$/.test(id)) {
        const index = Number(id.split('-')[1]) - 1;
        if (index >= 0 && index < params.segmentPositions.length) {
          let offset = angleAt(cx, cy, x, y) - params.start; while (offset < 0) offset += 360;
          const minimumGap = Math.min(.08, 1 / Math.max(360, params.sweep));
          const lower = index ? params.segmentPositions[index - 1] + minimumGap : minimumGap;
          const upper = index + 1 < params.segmentPositions.length ? params.segmentPositions[index + 1] - minimumGap : 1 - minimumGap;
          params.segmentPositions[index] = clamp(offset / Math.max(1, params.sweep), lower, upper);
        }
      }
      if (id === 'inner') params.inner = percent(Math.hypot((x - cx) / (width / 2), (y - cy) / (height / 2)) * 100, params.inner, 0, 100);
    } else if (object.type === 'smart-polygon') {
      if (id === 'rotate') params.rotation = normalizeAngle(angleAt(cx, cy, x, y));
      if (id === 'sides') params.sides = clamp(Math.round(3 + clamp(x / width, 0, 1) * 22), 3, 25);
      if (id === 'inner') params.inner = percent(Math.hypot((x - cx) / (width / 2), (y - cy) / (height / 2)) * 100, params.inner, 0, 90);
      if (id === 'sections') params.sections = clamp(Math.round(1 + clamp(y / height, 0, 1) * (params.sides - 1)), 1, params.sides);
    } else if (object.type === 'spiral' && id === 'turns') {
      params.turns = clamp(12 - clamp(y / height, 0, 1) * 11.5, .5, 12);
    } else if (object.type === 'star') {
      if (id === 'points') params.points = clamp(Math.round(3 + clamp(x / width, 0, 1) * 22), 3, 25);
      if (id === 'valley') params.inner = percent(Math.hypot((x - cx) / (width / 2), (y - cy) / (height / 2)) * 100, params.inner, 5, 95);
      if (id === 'peak') params.rotation = normalizeAngle(angleAt(cx, cy, x, y));
      if (id === 'peak-round') params.peakRoundness = percent((1 - Math.hypot(x - cx, y - cy) / Math.max(.001, pointDistance([cx, cy], polar(cx, cy, width / 2, height / 2, params.rotation)))) * 50, params.peakRoundness, 0, 45);
      if (id === 'valley-round') params.valleyRoundness = percent((1 - Math.hypot(x - cx, y - cy) / Math.max(.001, pointDistance([cx, cy], polar(cx, cy, width * params.inner / 200, height * params.inner / 200, params.rotation + 180 / params.points)))) * 50, params.valleyRoundness, 0, 45);
    }
    object.autoShape = normalizedParams(object.type, params, object);
    if (object.type === 'l-shape') { object.lThickness = object.autoShape.thickness; object.lRoundness = object.autoShape.roundness; }
    return JSON.stringify(object.autoShape) !== JSON.stringify(normalizedParams(origin.type, origin.autoShape, origin)) || object.width !== origin.width || object.height !== origin.height || JSON.stringify(object.points) !== JSON.stringify(origin.points);
  }

  function cycle(values, current) {
    const index = values.indexOf(current);
    return values[(index + 1 + values.length) % values.length];
  }

  function activateControl(object, id, options = {}) {
    const params = ensure(object); if (!params) return false;
    const before = JSON.stringify(params);
    if (object.type === 'arrow' && id === 'start-head') params.startHead = cycle(arrowHeadStyles, params.startHead);
    else if (object.type === 'arrow' && id === 'end-head') params.endHead = cycle(arrowHeadStyles, params.endHead);
    else if (/^corner-[0-3]$/.test(id)) {
      const index = Number(id.at(-1)); const next = cycle(cornerStyles, params.cornerStyles[index]);
      if (options.altKey || options.shiftKey) {
        params.cornerStyles[index] = next; if (next !== 'round') params.cornerRadii[index] = Math.abs(params.cornerRadii[index]); params.cornersLocked = false;
      }
      else {
        params.cornerStyles = params.cornerStyles.map(() => next);
        if (next !== 'round') params.cornerRadii = params.cornerRadii.map(Math.abs);
        params.cornersLocked = true;
      }
    } else if (object.type === 'doughnut' && id === 'reset-inner') params.inner = 0;
    else if (object.type === 'doughnut' && id === 'sector' && options.altKey) params.sections = clamp(params.sections + (options.shiftKey ? -1 : 1), 1, 32);
    else if (object.type === 'pie' && id === 'reset') { params.start = -90; params.sweep = 360; params.sections = 1; params.inner = 0; }
    else if (object.type === 'pie' && id === 'end' && options.altKey) params.sections = clamp(params.sections + (options.shiftKey ? -1 : 1), 1, 32);
    else if (object.type === 'smart-polygon' && id === 'inner') params.inner = 0;
    else if (object.type === 'spiral' && id === 'toggle') params.closed = !params.closed;
    object.autoShape = normalizedParams(object.type, params, object);
    return JSON.stringify(object.autoShape) !== before;
  }

  function rectanglePath(object, params) {
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1));
    const signedRadii = params.cornerRadii.map((value) => clamp(value, -Math.min(width, height) / 2, Math.min(width, height) / 2));
    const radii = signedRadii.map(Math.abs); const styles = params.cornerStyles;
    const corner = (index, end, convexControl, concaveControl) => {
      const radius = radii[index]; if (radius <= .001) return ` L ${pathPoint(end)}`;
      if (styles[index] === 'round') return ` Q ${pathPoint(signedRadii[index] < 0 ? concaveControl : convexControl)} ${pathPoint(end)}`;
      if (styles[index] === 'chamfer') return ` L ${pathPoint(concaveControl)} L ${pathPoint(end)}`;
      return ` L ${pathPoint(end)}`;
    };
    const r = radii;
    let d = `M ${r[0]} 0 H ${width - r[1]}`;
    d += corner(1, [width, r[1]], [width, 0], [width - r[1], r[1]]);
    d += ` V ${height - r[2]}` + corner(2, [width - r[2], height], [width, height], [width - r[2], height - r[2]]);
    d += ` H ${r[3]}` + corner(3, [0, height - r[3]], [0, height], [r[3], height - r[3]]);
    d += ` V ${r[0]}` + corner(0, [r[0], 0], [0, 0], [r[0], r[0]]) + ' Z';
    return d;
  }

  function ringSectorPath(width, height, start, sweep, innerPercent, sections = 1, segmentPositions = null) {
    const cx = width / 2; const cy = height / 2; const rx = width / 2; const ry = height / 2;
    const innerRx = rx * innerPercent / 100; const innerRy = ry * innerPercent / 100;
    const count = Math.max(1, Math.round(sections));
    const positions = Array.isArray(segmentPositions) && segmentPositions.length === count - 1
      ? [0, ...segmentPositions, 1]
      : Array.from({ length: count + 1 }, (_, index) => index / count);
    const paths = [];
    if (sweep >= 359.999 && count === 1) {
      const outerStart = polar(cx, cy, rx, ry, start); const outerOpposite = polar(cx, cy, rx, ry, start + 180);
      let d = `M ${pathPoint(outerStart)} A ${rx} ${ry} 0 0 1 ${pathPoint(outerOpposite)} A ${rx} ${ry} 0 0 1 ${pathPoint(outerStart)} Z`;
      if (innerPercent > .001) {
        const innerStart = polar(cx, cy, innerRx, innerRy, start); const innerOpposite = polar(cx, cy, innerRx, innerRy, start + 180);
        d += ` M ${pathPoint(innerStart)} A ${innerRx} ${innerRy} 0 0 0 ${pathPoint(innerOpposite)} A ${innerRx} ${innerRy} 0 0 0 ${pathPoint(innerStart)} Z`;
      }
      return d;
    }
    for (let index = 0; index < count; index += 1) {
      const from = start + sweep * positions[index]; const to = start + sweep * positions[index + 1]; const part = to - from; const large = part > 180 ? 1 : 0;
      const outerA = polar(cx, cy, rx, ry, from); const outerB = polar(cx, cy, rx, ry, to);
      if (innerPercent <= .001) paths.push(`M ${cx} ${cy} L ${pathPoint(outerA)} A ${rx} ${ry} 0 ${large} 1 ${pathPoint(outerB)} Z`);
      else {
        const innerA = polar(cx, cy, innerRx, innerRy, from); const innerB = polar(cx, cy, innerRx, innerRy, to);
        paths.push(`M ${pathPoint(outerA)} A ${rx} ${ry} 0 ${large} 1 ${pathPoint(outerB)} L ${pathPoint(innerB)} A ${innerRx} ${innerRy} 0 ${large} 0 ${pathPoint(innerA)} Z`);
      }
    }
    return paths.join(' ');
  }

  function regularPoints(count, width, height, rotation = -90, radius = 1) {
    const cx = width / 2; const cy = height / 2;
    return Array.from({ length: count }, (_, index) => polar(cx, cy, width * radius / 2, height * radius / 2, rotation + index * 360 / count));
  }

  function roundedClosedPath(points, roundnessForIndex) {
    if (!points.length) return '';
    const entries = points.map((point, index) => {
      const previous = points[(index - 1 + points.length) % points.length]; const next = points[(index + 1) % points.length];
      const amount = clamp(roundnessForIndex(index), 0, 45) / 100;
      return {
        point,
        before: [round(point[0] + (previous[0] - point[0]) * amount), round(point[1] + (previous[1] - point[1]) * amount)],
        after: [round(point[0] + (next[0] - point[0]) * amount), round(point[1] + (next[1] - point[1]) * amount)]
      };
    });
    let d = `M ${pathPoint(entries[0].before)}`;
    entries.forEach((entry, index) => {
      d += ` Q ${pathPoint(entry.point)} ${pathPoint(entry.after)}`;
      const next = entries[(index + 1) % entries.length]; d += ` L ${pathPoint(next.before)}`;
    });
    return `${d} Z`;
  }

  function connectorPath(object, params) {
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1));
    const sy = height * params.startY / 100; const ey = height * params.endY / 100; const x = width * params.cross / 100;
    const direction = Math.sign(ey - sy) || 1; const span = Math.abs(ey - sy);
    const r1 = Math.min(params.radiusA, Math.abs(x), span / 2); const r2 = Math.min(params.radiusB, Math.abs(width - x), span / 2);
    return `M 0 ${round(sy)} H ${round(x - r1)} Q ${round(x)} ${round(sy)} ${round(x)} ${round(sy + direction * r1)} V ${round(ey - direction * r2)} Q ${round(x)} ${round(ey)} ${round(x + r2)} ${round(ey)} H ${round(width)}`;
  }

  function geometry(object) {
    const params = ensure(object); if (!params) return null;
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1)); const cx = width / 2; const cy = height / 2;
    if (['rounded-rectangle', 'beveled-rectangle', 'chamfer-rectangle'].includes(object.type)) return { tag: 'path', attrs: { d: rectanglePath(object, params) } };
    if (object.type === 'block-arrow') {
      const baseX = width * (1 - params.headLength / 100); const headTop = height * (1 - params.headHeight / 100) / 2;
      const headBottom = height - headTop; const shaftTop = height * (1 - params.shaftHeight / 100) / 2; const shaftBottom = height - shaftTop; const tipX = width * (1 - params.tipInset / 100);
      const points = [[0, shaftTop], [baseX, shaftTop], [baseX, headTop], [tipX, cy], [baseX, headBottom], [baseX, shaftBottom], [0, shaftBottom]];
      return { tag: 'path', attrs: { d: `M ${points.map(pathPoint).join(' L ')} Z` } };
    }
    if (object.type === 'connector-line') return { tag: 'path', attrs: { d: connectorPath(object, params), fill: 'none' } };
    if (object.type === 'doughnut') return { tag: 'path', attrs: { d: ringSectorPath(width, height, params.start, params.sweep, params.inner, params.sections), 'fill-rule': 'evenodd' } };
    if (object.type === 'l-shape') {
      const thickness = params.thickness; const innerRoundness = Math.max(0, params.roundness); const outerRoundness = Math.max(0, -params.roundness);
      const inner = innerRoundness > .01
        ? `H ${round(thickness + innerRoundness)} Q ${round(thickness)} ${round(thickness)} ${round(thickness)} ${round(thickness + innerRoundness)}`
        : `H ${round(thickness)} V ${round(thickness)}`;
      const d = outerRoundness > .01
        ? `M ${round(outerRoundness)} 0 H ${round(width)} V ${round(thickness)} ${inner} V ${round(height)} H 0 V ${round(outerRoundness)} Q 0 0 ${round(outerRoundness)} 0 Z`
        : `M 0 0 H ${round(width)} V ${round(thickness)} ${inner} V ${round(height)} H 0 Z`;
      return { tag: 'path', attrs: { d } };
    }
    if (object.type === 'pie') return { tag: 'path', attrs: {
      d: ringSectorPath(width, height, params.start, params.sweep, params.inner, params.sections, params.segmentPositions),
      'fill-rule': params.inner > .001 ? 'evenodd' : undefined,
      'data-pie-sections': params.sections, 'data-pie-inner': params.inner, 'data-pie-coverage': round(params.sweep / 3.6, 2)
    } };
    if (object.type === 'smart-polygon') {
      const outer = regularPoints(params.sides, width, height, params.rotation); let d = `M ${outer.map(pathPoint).join(' L ')} Z`;
      if (params.inner > 0) {
        const inner = regularPoints(params.sides, width, height, params.rotation, params.inner / 100).reverse();
        d += ` M ${inner.map(pathPoint).join(' L ')} Z`;
      }
      if (params.sections > 1) {
        for (let index = 0; index < params.sections; index += 1) {
          const vertex = outer[Math.round(index * params.sides / params.sections) % params.sides];
          const from = params.inner > 0 ? polar(cx, cy, width * params.inner / 200, height * params.inner / 200, angleAt(cx, cy, vertex[0], vertex[1])) : [cx, cy];
          d += ` M ${pathPoint(from)} L ${pathPoint(vertex)}`;
        }
      }
      return { tag: 'path', attrs: { d, 'fill-rule': 'evenodd' } };
    }
    if (object.type === 'spiral') {
      const count = Math.max(32, Math.ceil(params.turns * 24)); const points = Array.from({ length: count }, (_, index) => {
        const progress = index / (count - 1); const angle = progress * params.turns * Math.PI * 2 - Math.PI / 2; const radius = progress * .48;
        return [round(cx + Math.cos(angle) * width * radius), round(cy + Math.sin(angle) * height * radius)];
      });
      const attrs = { d: `${points.map((point, index) => `${index ? 'L' : 'M'} ${pathPoint(point)}`).join(' ')}${params.closed ? ' Z' : ''}` };
      if (!params.closed) attrs.fill = 'none';
      return { tag: 'path', attrs };
    }
    if (object.type === 'star') {
      const points = Array.from({ length: params.points * 2 }, (_, index) => polar(cx, cy, width * (index % 2 ? params.inner / 100 : 1) / 2, height * (index % 2 ? params.inner / 100 : 1) / 2, params.rotation + index * 180 / params.points));
      return { tag: 'path', attrs: { d: roundedClosedPath(points, (index) => index % 2 ? params.valleyRoundness : params.peakRoundness) } };
    }
    return null;
  }

  function ellipseContour(width, height, factor = 1, start = -90, sweep = 360, steps = 64) {
    const cx = width / 2; const cy = height / 2;
    return Array.from({ length: steps }, (_, index) => polar(cx, cy, width * factor / 2, height * factor / 2, start + sweep * index / Math.max(1, steps - (sweep >= 359.999 ? 0 : 1))));
  }

  function contours(object) {
    const params = ensure(object); if (!params) return [];
    const width = Math.max(1, finite(object.width, 1)); const height = Math.max(1, finite(object.height, 1)); const cx = width / 2; const cy = height / 2;
    const appendArc = (target, centerX, centerY, radius, from, to, steps = 7) => {
      for (let index = 1; index <= steps; index += 1) target.push(polar(centerX, centerY, radius, radius, from + (to - from) * index / steps));
    };
    const appendQuadratic = (target, start, controlPoint, end, steps = 8) => {
      for (let index = 1; index <= steps; index += 1) {
        const t = index / steps; const inverse = 1 - t;
        target.push([round(inverse * inverse * start[0] + 2 * inverse * t * controlPoint[0] + t * t * end[0]), round(inverse * inverse * start[1] + 2 * inverse * t * controlPoint[1] + t * t * end[1])]);
      }
    };
    if (['rounded-rectangle', 'beveled-rectangle', 'chamfer-rectangle'].includes(object.type)) {
      const signed = params.cornerRadii; const r = signed.map(Math.abs); const styles = params.cornerStyles; const points = [[r[0], 0], [width - r[1], 0]];
      if (r[1] > .001 && styles[1] === 'round') {
        if (signed[1] < 0) appendQuadratic(points, points.at(-1), [width - r[1], r[1]], [width, r[1]]);
        else appendArc(points, width - r[1], r[1], r[1], -90, 0);
      } else { if (r[1] > .001 && styles[1] === 'chamfer') points.push([width - r[1], r[1]]); points.push([width, r[1]]); }
      points.push([width, height - r[2]]);
      if (r[2] > .001 && styles[2] === 'round') {
        if (signed[2] < 0) appendQuadratic(points, points.at(-1), [width - r[2], height - r[2]], [width - r[2], height]);
        else appendArc(points, width - r[2], height - r[2], r[2], 0, 90);
      } else { if (r[2] > .001 && styles[2] === 'chamfer') points.push([width - r[2], height - r[2]]); points.push([width - r[2], height]); }
      points.push([r[3], height]);
      if (r[3] > .001 && styles[3] === 'round') {
        if (signed[3] < 0) appendQuadratic(points, points.at(-1), [r[3], height - r[3]], [0, height - r[3]]);
        else appendArc(points, r[3], height - r[3], r[3], 90, 180);
      } else { if (r[3] > .001 && styles[3] === 'chamfer') points.push([r[3], height - r[3]]); points.push([0, height - r[3]]); }
      points.push([0, r[0]]);
      if (r[0] > .001 && styles[0] === 'round') {
        if (signed[0] < 0) appendQuadratic(points, points.at(-1), [r[0], r[0]], [r[0], 0]);
        else appendArc(points, r[0], r[0], r[0], 180, 270);
      } else { if (r[0] > .001 && styles[0] === 'chamfer') points.push([r[0], r[0]]); points.push([r[0], 0]); }
      return [points];
    }
    if (object.type === 'block-arrow') {
      const baseX = width * (1 - params.headLength / 100); const headTop = height * (1 - params.headHeight / 100) / 2; const shaftTop = height * (1 - params.shaftHeight / 100) / 2; const tipX = width * (1 - params.tipInset / 100);
      return [[[0, shaftTop], [baseX, shaftTop], [baseX, headTop], [tipX, cy], [baseX, height - headTop], [baseX, height - shaftTop], [0, height - shaftTop]]];
    }
    if (object.type === 'doughnut') {
      if (params.sweep >= 359.999) return [ellipseContour(width, height), ...(params.inner > .001 ? [ellipseContour(width, height, params.inner / 100).reverse()] : [])];
      const outer = ellipseContour(width, height, 1, params.start, params.sweep, 48); const inner = ellipseContour(width, height, params.inner / 100, params.start, params.sweep, 48).reverse();
      return [[...outer, ...(params.inner > .001 ? inner : [[cx, cy]])]];
    }
    if (object.type === 'l-shape') {
      const thickness = params.thickness; const inner = Math.max(0, params.roundness); const outer = Math.max(0, -params.roundness);
      const points = [[outer, 0], [width, 0], [width, thickness]];
      if (inner > .001) {
        const start = [thickness + inner, thickness]; points.push(start); appendQuadratic(points, start, [thickness, thickness], [thickness, thickness + inner]);
      } else points.push([thickness, thickness]);
      points.push([thickness, height], [0, height], [0, outer]);
      if (outer > .001) appendQuadratic(points, [0, outer], [0, 0], [outer, 0]);
      return [points];
    }
    if (object.type === 'pie') {
      const outer = ellipseContour(width, height, 1, params.start, params.sweep, 48);
      if (params.inner <= .001) return [[[cx, cy], ...outer]];
      const inner = ellipseContour(width, height, params.inner / 100, params.start, params.sweep, 48).reverse();
      return params.sweep >= 359.999 ? [outer, inner] : [[...outer, ...inner]];
    }
    if (object.type === 'smart-polygon') {
      const result = [regularPoints(params.sides, width, height, params.rotation)];
      if (params.inner > 0) result.push(regularPoints(params.sides, width, height, params.rotation, params.inner / 100).reverse());
      return result;
    }
    if (object.type === 'star') return [Array.from({ length: params.points * 2 }, (_, index) => polar(cx, cy, width * (index % 2 ? params.inner / 100 : 1) / 2, height * (index % 2 ? params.inner / 100 : 1) / 2, params.rotation + index * 180 / params.points))];
    if (object.type === 'spiral' && params.closed) {
      const shape = geometry(object); const numbers = (shape?.attrs?.d.match(/-?(?:\d+\.?\d*|\.\d+)/g) || []).map(Number); const points = [];
      for (let index = 0; index + 1 < numbers.length; index += 2) points.push([numbers[index], numbers[index + 1]]);
      return points.length >= 3 ? [points] : [];
    }
    return [];
  }

  function outlinePoints(object) {
    return contours(object)[0] || [];
  }

  function scaleParams(object, origin, scaleX, scaleY) {
    if (!isAutoShape(object) || !isAutoShape(origin)) return false;
    const params = clone(ensure(origin)); const scale = Math.sqrt(Math.abs(finite(scaleX, 1) * finite(scaleY, 1)));
    if (Array.isArray(params.cornerRadii)) params.cornerRadii = params.cornerRadii.map((value) => value * scale);
    if (object.type === 'connector-line') { params.radiusA *= scale; params.radiusB *= scale; }
    if (object.type === 'l-shape') { params.thickness *= scale; params.roundness *= scale; }
    if (object.type === 'arrow') params.headSize *= scale;
    object.autoShape = normalizedParams(object.type, params, object);
    if (object.type === 'l-shape') { object.lThickness = object.autoShape.thickness; object.lRoundness = object.autoShape.roundness; }
    return true;
  }

  const api = Object.freeze({
    types, definitions, arrowHeadStyles, cornerStyles, isAutoShape, definitionFor, ensure, sanitize,
    propertyFields, getProperty, setProperty, controls, dragControl, activateControl, geometry, contours, outlinePoints, scaleParams
  });
  root.PyroAutoShapes = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : self);
