gradiente 2.3.0 → 2.5.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.
package/dist/index.js CHANGED
@@ -1430,6 +1430,31 @@ var RadialGradient = class RadialGradient extends GradientBase {
1430
1430
  }
1431
1431
  };
1432
1432
  //#endregion
1433
+ //#region src/gradients/DiamondGradient.ts
1434
+ var DiamondGradient = class DiamondGradient extends RadialGradient {
1435
+ type = "diamond-gradient";
1436
+ constructor(input) {
1437
+ super(input);
1438
+ }
1439
+ static fromString(input) {
1440
+ return DiamondGradient.fromAbi(parseStringToAbi(input));
1441
+ }
1442
+ static fromAbi(abi) {
1443
+ if (abi.functionName !== "diamond-gradient") throw new Error("Invalid function name for DiamondGradient");
1444
+ const config = this._parseConfig(abi.inputs);
1445
+ const inputsWithoutConfig = abi.inputs[0]?.type === "config" ? abi.inputs.slice(1) : abi.inputs;
1446
+ const stops = this._normalizeAbiInputsToStops(inputsWithoutConfig);
1447
+ return new DiamondGradient({
1448
+ isRepeating: abi.isRepeating,
1449
+ config,
1450
+ stops
1451
+ });
1452
+ }
1453
+ clone() {
1454
+ return new DiamondGradient(this.toJSON());
1455
+ }
1456
+ };
1457
+ //#endregion
1433
1458
  //#region src/gradients/ConicGradient.ts
1434
1459
  var ConicGradient = class ConicGradient extends GradientBase {
1435
1460
  type = "conic-gradient";
@@ -1612,6 +1637,421 @@ var ConicGradient = class ConicGradient extends GradientBase {
1612
1637
  }
1613
1638
  };
1614
1639
  //#endregion
1640
+ //#region src/gradients/MeshGradient.ts
1641
+ const MESH_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1642
+ const MESH_LENGTH_UNITS = [
1643
+ "px",
1644
+ "em",
1645
+ "rem",
1646
+ "vw",
1647
+ "vh",
1648
+ "vmin",
1649
+ "vmax",
1650
+ "cm",
1651
+ "mm",
1652
+ "in",
1653
+ "pt",
1654
+ "pc"
1655
+ ];
1656
+ var MeshGradient = class MeshGradient {
1657
+ type = "mesh-gradient";
1658
+ _config;
1659
+ _vertices;
1660
+ _patches;
1661
+ constructor(input) {
1662
+ const config = structuredClone(input.config);
1663
+ if (config.interpolation) config.interpolation = MeshGradient._normalizeInterpolation(config.interpolation);
1664
+ this._config = config;
1665
+ this._vertices = structuredClone(input.vertices);
1666
+ this._patches = structuredClone(input.patches);
1667
+ this._validate();
1668
+ }
1669
+ get config() {
1670
+ return structuredClone(this._config);
1671
+ }
1672
+ get vertices() {
1673
+ return structuredClone(this._vertices);
1674
+ }
1675
+ get patches() {
1676
+ return structuredClone(this._patches);
1677
+ }
1678
+ static fromString(input) {
1679
+ const { functionName, isRepeating, inputs } = MeshGradient._parseFunction(input);
1680
+ if (functionName !== "mesh-gradient") throw new Error("Invalid function name for MeshGradient");
1681
+ if (isRepeating) throw new Error("MeshGradient does not support repeating gradients");
1682
+ let config = null;
1683
+ const vertices = [];
1684
+ const patches = [];
1685
+ const handles = [];
1686
+ for (const rawInput of inputs) {
1687
+ const tokens = splitTopLevelByWhitespace(rawInput);
1688
+ const kind = tokens[0];
1689
+ if (kind === "grid") {
1690
+ if (config !== null) throw new Error("mesh-gradient can only contain one grid config");
1691
+ config = this._parseConfig(tokens);
1692
+ continue;
1693
+ }
1694
+ if (kind === "vertex") {
1695
+ vertices.push(this._parseVertex(tokens));
1696
+ continue;
1697
+ }
1698
+ if (kind === "patch") {
1699
+ patches.push(this._parsePatch(tokens));
1700
+ continue;
1701
+ }
1702
+ if (kind === "handle") {
1703
+ handles.push(this._parseHandle(tokens));
1704
+ continue;
1705
+ }
1706
+ throw new Error(`Unsupported mesh-gradient input: ${rawInput}`);
1707
+ }
1708
+ if (!config) throw new Error("mesh-gradient requires a grid config");
1709
+ const patchesWithHandles = this._attachHandles(patches, handles);
1710
+ return new MeshGradient({
1711
+ config,
1712
+ vertices,
1713
+ patches: patchesWithHandles
1714
+ });
1715
+ }
1716
+ static fromAbi(abi) {
1717
+ if (abi.functionName !== "mesh-gradient") throw new Error("Invalid function name for MeshGradient");
1718
+ if (abi.isRepeating) throw new Error("MeshGradient does not support repeating gradients");
1719
+ return MeshGradient.fromString(`${abi.functionName}(${abi.inputs.map((input) => input.value).join(", ")})`);
1720
+ }
1721
+ clone() {
1722
+ return new MeshGradient(this.toJSON());
1723
+ }
1724
+ toJSON() {
1725
+ return {
1726
+ type: this.type,
1727
+ config: this.config,
1728
+ vertices: this.vertices,
1729
+ patches: this.patches
1730
+ };
1731
+ }
1732
+ toString() {
1733
+ const parts = [
1734
+ this._serializeConfig(),
1735
+ ...this._vertices.map((vertex) => this._serializeVertex(vertex)),
1736
+ ...this._patches.map((patch) => this._serializePatch(patch)),
1737
+ ...this._patches.flatMap((patch) => this._serializeHandles(patch))
1738
+ ];
1739
+ return `${this.type}(${parts.join(", ")})`;
1740
+ }
1741
+ getVertex(id) {
1742
+ const vertex = this._vertices.find((item) => item.id === id);
1743
+ return vertex ? structuredClone(vertex) : null;
1744
+ }
1745
+ _serializeConfig() {
1746
+ const parts = [
1747
+ "grid",
1748
+ String(this._config.rows),
1749
+ String(this._config.columns),
1750
+ "method",
1751
+ this._config.method
1752
+ ];
1753
+ if (this._config.interpolation) {
1754
+ parts.push("in", this._config.interpolation.colorSpace);
1755
+ if (this._config.interpolation.hue) parts.push(this._config.interpolation.hue, "hue");
1756
+ }
1757
+ return parts.join(" ");
1758
+ }
1759
+ _serializeVertex(vertex) {
1760
+ return [
1761
+ "vertex",
1762
+ vertex.id,
1763
+ this._formatLengthPercentage(vertex.x),
1764
+ this._formatLengthPercentage(vertex.y),
1765
+ vertex.color
1766
+ ].join(" ");
1767
+ }
1768
+ _serializePatch(patch) {
1769
+ return [
1770
+ "patch",
1771
+ patch.id,
1772
+ patch.topLeft,
1773
+ patch.topRight,
1774
+ patch.bottomRight,
1775
+ patch.bottomLeft
1776
+ ].join(" ");
1777
+ }
1778
+ _serializeHandles(patch) {
1779
+ if (!patch.handles) return [];
1780
+ const result = [];
1781
+ for (const side of [
1782
+ "top",
1783
+ "right",
1784
+ "bottom",
1785
+ "left"
1786
+ ]) {
1787
+ const handle = patch.handles[side];
1788
+ if (!handle) continue;
1789
+ result.push([
1790
+ "handle",
1791
+ patch.id,
1792
+ side,
1793
+ this._formatLengthPercentage(handle.from.x),
1794
+ this._formatLengthPercentage(handle.from.y),
1795
+ this._formatLengthPercentage(handle.to.x),
1796
+ this._formatLengthPercentage(handle.to.y)
1797
+ ].join(" "));
1798
+ }
1799
+ return result;
1800
+ }
1801
+ _formatLengthPercentage(value) {
1802
+ if (value.kind === "percent") return `${value.value}%`;
1803
+ return `${value.value}${value.unit}`;
1804
+ }
1805
+ _validate() {
1806
+ if (!Number.isInteger(this._config.rows) || this._config.rows < 2) throw new Error("Mesh gradient rows must be an integer >= 2");
1807
+ if (!Number.isInteger(this._config.columns) || this._config.columns < 2) throw new Error("Mesh gradient columns must be an integer >= 2");
1808
+ if (this._config.method !== "bilinear" && this._config.method !== "bicubic") throw new Error("Invalid mesh gradient interpolation method");
1809
+ if (this._config.interpolation && !isGradientColorSpace(this._config.interpolation.colorSpace)) throw new Error(`Invalid mesh gradient color space: ${this._config.interpolation.colorSpace}`);
1810
+ const expectedVertexCount = this._config.rows * this._config.columns;
1811
+ const expectedPatchCount = (this._config.rows - 1) * (this._config.columns - 1);
1812
+ if (this._vertices.length !== expectedVertexCount) throw new Error(`Mesh gradient expected ${expectedVertexCount} vertices for ${this._config.rows}x${this._config.columns} grid, received ${this._vertices.length}`);
1813
+ if (this._patches.length !== expectedPatchCount) throw new Error(`Mesh gradient expected ${expectedPatchCount} patches for ${this._config.rows}x${this._config.columns} grid, received ${this._patches.length}`);
1814
+ const vertexIds = /* @__PURE__ */ new Set();
1815
+ for (const vertex of this._vertices) {
1816
+ this._validateId(vertex.id, "vertex");
1817
+ if (vertexIds.has(vertex.id)) throw new Error(`Duplicate mesh vertex id: ${vertex.id}`);
1818
+ vertexIds.add(vertex.id);
1819
+ this._validateLengthPercentage(vertex.x, `vertex ${vertex.id} x`);
1820
+ this._validateLengthPercentage(vertex.y, `vertex ${vertex.id} y`);
1821
+ if (!parse$1(vertex.color)) throw new Error(`Invalid mesh vertex color: ${vertex.color}`);
1822
+ }
1823
+ const patchIds = /* @__PURE__ */ new Set();
1824
+ for (const patch of this._patches) {
1825
+ this._validateId(patch.id, "patch");
1826
+ if (patchIds.has(patch.id)) throw new Error(`Duplicate mesh patch id: ${patch.id}`);
1827
+ patchIds.add(patch.id);
1828
+ const ids = [
1829
+ patch.topLeft,
1830
+ patch.topRight,
1831
+ patch.bottomRight,
1832
+ patch.bottomLeft
1833
+ ];
1834
+ if (new Set(ids).size !== 4) throw new Error(`Mesh patch must use 4 unique vertices: ${patch.id}`);
1835
+ for (const id of ids) if (!vertexIds.has(id)) throw new Error(`Mesh patch references missing vertex: ${id}`);
1836
+ if (patch.handles) this._validateHandles(patch);
1837
+ }
1838
+ if (this._patches.length === 0) throw new Error("Mesh gradient requires at least one patch");
1839
+ }
1840
+ _validateId(id, label) {
1841
+ if (!MESH_ID_PATTERN.test(id)) throw new Error(`Invalid mesh ${label} id: ${id}`);
1842
+ }
1843
+ _validateHandles(patch) {
1844
+ if (!patch.handles) return;
1845
+ for (const side of [
1846
+ "top",
1847
+ "right",
1848
+ "bottom",
1849
+ "left"
1850
+ ]) {
1851
+ const handle = patch.handles[side];
1852
+ if (!handle) continue;
1853
+ this._validateLengthPercentage(handle.from.x, `patch ${patch.id} ${side} handle from x`);
1854
+ this._validateLengthPercentage(handle.from.y, `patch ${patch.id} ${side} handle from y`);
1855
+ this._validateLengthPercentage(handle.to.x, `patch ${patch.id} ${side} handle to x`);
1856
+ this._validateLengthPercentage(handle.to.y, `patch ${patch.id} ${side} handle to y`);
1857
+ }
1858
+ }
1859
+ _validateLengthPercentage(value, label) {
1860
+ if (value.kind === "percent") {
1861
+ if (!Number.isFinite(value.value)) throw new Error(`Invalid mesh ${label}`);
1862
+ return;
1863
+ }
1864
+ if (!Number.isFinite(value.value)) throw new Error(`Invalid mesh ${label}`);
1865
+ }
1866
+ static _parseConfig(tokens) {
1867
+ if (tokens.length < 3) throw new Error("Invalid mesh grid config");
1868
+ const rows = Number(tokens[1]);
1869
+ const columns = Number(tokens[2]);
1870
+ let method = "bilinear";
1871
+ let interpolation;
1872
+ for (let index = 3; index < tokens.length; index += 1) {
1873
+ const token = tokens[index];
1874
+ if (token === "method") {
1875
+ const value = tokens[index + 1];
1876
+ if (value !== "bilinear" && value !== "bicubic") throw new Error(`Invalid mesh gradient method: ${value}`);
1877
+ method = value;
1878
+ index += 1;
1879
+ continue;
1880
+ }
1881
+ if (token === "in") {
1882
+ const colorSpace = tokens[index + 1];
1883
+ const maybeHue = tokens[index + 2];
1884
+ const maybeHueKeyword = tokens[index + 3];
1885
+ if (!isGradientColorSpace(colorSpace)) throw new Error(`Invalid mesh gradient color space: ${colorSpace}`);
1886
+ if (maybeHue !== void 0 && maybeHueKeyword === "hue" && isGradientHueInterpolation(maybeHue)) {
1887
+ interpolation = this._normalizeInterpolation({
1888
+ colorSpace,
1889
+ hue: maybeHue
1890
+ });
1891
+ index += 3;
1892
+ continue;
1893
+ }
1894
+ interpolation = { colorSpace };
1895
+ index += 1;
1896
+ continue;
1897
+ }
1898
+ throw new Error(`Unsupported mesh grid config token: ${token}`);
1899
+ }
1900
+ return {
1901
+ rows,
1902
+ columns,
1903
+ method,
1904
+ interpolation
1905
+ };
1906
+ }
1907
+ static _parseVertex(tokens) {
1908
+ if (tokens.length < 5) throw new Error("Invalid mesh vertex input");
1909
+ return {
1910
+ id: tokens[1],
1911
+ x: this._parseLengthPercentage(tokens[2]),
1912
+ y: this._parseLengthPercentage(tokens[3]),
1913
+ color: tokens.slice(4).join(" ")
1914
+ };
1915
+ }
1916
+ static _parsePatch(tokens) {
1917
+ if (tokens.length !== 6) throw new Error("Invalid mesh patch input");
1918
+ return {
1919
+ id: tokens[1],
1920
+ topLeft: tokens[2],
1921
+ topRight: tokens[3],
1922
+ bottomRight: tokens[4],
1923
+ bottomLeft: tokens[5]
1924
+ };
1925
+ }
1926
+ static _parseHandle(tokens) {
1927
+ if (tokens.length !== 7) throw new Error("Invalid mesh handle input");
1928
+ const side = tokens[2];
1929
+ if (side !== "top" && side !== "right" && side !== "bottom" && side !== "left") throw new Error(`Invalid mesh handle side: ${side}`);
1930
+ return {
1931
+ patchId: tokens[1],
1932
+ side,
1933
+ from: {
1934
+ x: this._parseLengthPercentage(tokens[3]),
1935
+ y: this._parseLengthPercentage(tokens[4])
1936
+ },
1937
+ to: {
1938
+ x: this._parseLengthPercentage(tokens[5]),
1939
+ y: this._parseLengthPercentage(tokens[6])
1940
+ }
1941
+ };
1942
+ }
1943
+ static _attachHandles(patches, handles) {
1944
+ if (handles.length === 0) return patches;
1945
+ const patchMap = new Map(patches.map((patch) => [patch.id, patch]));
1946
+ const nextPatches = patches.map((patch) => structuredClone(patch));
1947
+ const nextPatchMap = new Map(nextPatches.map((patch) => [patch.id, patch]));
1948
+ for (const handle of handles) {
1949
+ if (!patchMap.has(handle.patchId)) throw new Error(`Mesh handle references missing patch: ${handle.patchId}`);
1950
+ const patch = nextPatchMap.get(handle.patchId);
1951
+ patch.handles ??= {};
1952
+ if (patch.handles[handle.side]) throw new Error(`Duplicate mesh handle for patch ${handle.patchId} side ${handle.side}`);
1953
+ patch.handles[handle.side] = {
1954
+ from: handle.from,
1955
+ to: handle.to
1956
+ };
1957
+ }
1958
+ return nextPatches;
1959
+ }
1960
+ static _parseLengthPercentage(input) {
1961
+ if (input.endsWith("%")) {
1962
+ const value = parseFloat(input);
1963
+ if (!Number.isFinite(value)) throw new Error(`Invalid mesh length-percentage: ${input}`);
1964
+ return {
1965
+ kind: "percent",
1966
+ value
1967
+ };
1968
+ }
1969
+ const match = input.match(/^(-?\d*\.?\d+)([a-zA-Z]+)$/);
1970
+ if (!match) throw new Error(`Invalid mesh length-percentage: ${input}`);
1971
+ const unit = match[2];
1972
+ if (!MESH_LENGTH_UNITS.includes(unit)) throw new Error(`Unsupported mesh length unit: ${match[2]}`);
1973
+ return {
1974
+ kind: "length",
1975
+ value: parseFloat(match[1]),
1976
+ unit
1977
+ };
1978
+ }
1979
+ static _normalizeInterpolation(value) {
1980
+ const { colorSpace, hue } = value;
1981
+ if (hue === void 0 || !isGradientPolarColorSpace(colorSpace)) return { colorSpace };
1982
+ return {
1983
+ colorSpace,
1984
+ hue
1985
+ };
1986
+ }
1987
+ static _parseFunction(input) {
1988
+ const source = input.trim();
1989
+ const openIndex = source.indexOf("(");
1990
+ if (openIndex <= 0) throw new Error("Expected mesh-gradient function call");
1991
+ let functionName = source.slice(0, openIndex).trim();
1992
+ const isRepeating = functionName.startsWith("repeating-");
1993
+ if (isRepeating) functionName = functionName.slice(10);
1994
+ const closeIndex = this._findOuterClosingParenIndex(source, openIndex);
1995
+ if (closeIndex === -1) throw new Error("Unclosed mesh-gradient function parenthesis");
1996
+ const trailing = source.slice(closeIndex + 1).trim();
1997
+ if (trailing.length > 0 && !trailing.startsWith(`${functionName}(`) && !trailing.startsWith(`repeating-${functionName}(`)) throw new Error(`Unexpected mesh-gradient trailing input: ${trailing}`);
1998
+ const body = source.slice(openIndex + 1, closeIndex);
1999
+ return {
2000
+ functionName,
2001
+ isRepeating,
2002
+ inputs: this._splitTopLevelInputs(body)
2003
+ };
2004
+ }
2005
+ static _findOuterClosingParenIndex(value, openIndex) {
2006
+ let depth = 0;
2007
+ for (let index = openIndex; index < value.length; index += 1) {
2008
+ const char = value[index];
2009
+ if (char === "(") {
2010
+ depth += 1;
2011
+ continue;
2012
+ }
2013
+ if (char === ")") {
2014
+ depth -= 1;
2015
+ if (depth === 0) return index;
2016
+ if (depth < 0) return -1;
2017
+ }
2018
+ }
2019
+ return -1;
2020
+ }
2021
+ static _splitTopLevelInputs(value) {
2022
+ const result = [];
2023
+ let current = "";
2024
+ let parenDepth = 0;
2025
+ for (let index = 0; index < value.length; index += 1) {
2026
+ const char = value[index];
2027
+ if (char === "(") {
2028
+ parenDepth += 1;
2029
+ current += char;
2030
+ continue;
2031
+ }
2032
+ if (char === ")") {
2033
+ parenDepth -= 1;
2034
+ if (parenDepth < 0) throw new Error("Unbalanced mesh-gradient input parentheses");
2035
+ current += char;
2036
+ continue;
2037
+ }
2038
+ if (char === "," && parenDepth === 0) {
2039
+ this._pushTrimmed(result, current);
2040
+ current = "";
2041
+ continue;
2042
+ }
2043
+ current += char;
2044
+ }
2045
+ this._pushTrimmed(result, current);
2046
+ if (parenDepth !== 0) throw new Error("Unbalanced mesh-gradient input parentheses");
2047
+ return result;
2048
+ }
2049
+ static _pushTrimmed(target, value) {
2050
+ const trimmed = value.trim();
2051
+ if (trimmed.length > 0) target.push(trimmed);
2052
+ }
2053
+ };
2054
+ //#endregion
1615
2055
  //#region src/gradient-transformer/modules/css/ModuleTransformerLinearGradientToCss.ts
1616
2056
  var ModuleTransformerLinearGradientToCss = class {
1617
2057
  target = "css";
@@ -1632,21 +2072,11 @@ var ModuleTransformerRadialGradientToCss = class {
1632
2072
  }
1633
2073
  };
1634
2074
  //#endregion
1635
- //#region src/gradient-transformer/modules/css/ModuleTransformerConicGradientToCss.ts
1636
- var ModuleTransformerConicGradientToCss = class {
1637
- target = "css";
1638
- gradientType = "conic-gradient";
1639
- to(input) {
1640
- if (!(input instanceof ConicGradient)) throw new Error("Expected ConicGradient");
1641
- return input.toString();
1642
- }
1643
- };
1644
- //#endregion
1645
2075
  //#region src/gradient-transformer/modules/helpers/expand-repeating-stops.ts
1646
2076
  function positiveModulo(value, modulo) {
1647
2077
  return (value % modulo + modulo) % modulo;
1648
2078
  }
1649
- function sampleColorAtPosition(stops, position) {
2079
+ function sampleColorAtPosition$2(stops, position) {
1650
2080
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
1651
2081
  if (colorStops.length === 0) throw new Error("Cannot sample color from empty stops.");
1652
2082
  if (position <= colorStops[0].position) return colorStops[0].value;
@@ -1664,7 +2094,7 @@ function sampleColorAtPosition(stops, position) {
1664
2094
  return lastStop.value;
1665
2095
  }
1666
2096
  function sampleRepeatingColorAtPosition(stops, position, firstPosition, period) {
1667
- return sampleColorAtPosition(stops, firstPosition + positiveModulo(position - firstPosition, period));
2097
+ return sampleColorAtPosition$2(stops, firstPosition + positiveModulo(position - firstPosition, period));
1668
2098
  }
1669
2099
  function expandRepeatingStopsTo(stops, from, to) {
1670
2100
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
@@ -1783,12 +2213,12 @@ function getColorStopsWithPositions(stops) {
1783
2213
  });
1784
2214
  }
1785
2215
  function formatColorForCanvas(input) {
1786
- const color = toRgb$6(input);
2216
+ const color = toRgb$10(input);
1787
2217
  if (!color) throw new Error("Failed to convert interpolated color to rgb.");
1788
2218
  return formatRgb(color);
1789
2219
  }
1790
2220
  const DEFAULT_SAMPLE_COUNT = 64;
1791
- const toRgb$6 = converter("rgb");
2221
+ const toRgb$10 = converter("rgb");
1792
2222
  function resolveRenderableGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_COUNT) {
1793
2223
  const colorStops = getColorStopsWithPositions(gradient.stops);
1794
2224
  const interpolation = gradient.config.interpolation;
@@ -1818,413 +2248,1781 @@ function resolveRenderableGradientStops(gradient, sampleCount = DEFAULT_SAMPLE_C
1818
2248
  return gradient.isRepeating ? expandRepeatingStops(sampledStops) : sampledStops;
1819
2249
  }
1820
2250
  //#endregion
1821
- //#region src/gradient-transformer/modules/canvas/ModuleTransformerLinearGradientToCanvas.ts
1822
- const toRgb$5 = converter("rgb");
1823
- function toCanvasColor$1(input) {
1824
- const color = toRgb$5(input);
2251
+ //#region src/gradient-transformer/modules/helpers/mesh-rendering.ts
2252
+ const toRgb$9 = converter("rgb");
2253
+ function toColor(input) {
2254
+ const color = toRgb$9(input);
1825
2255
  if (!color) throw new Error(`Failed to convert color: ${input}`);
1826
- return formatRgb(color);
2256
+ return [
2257
+ color.r ?? 0,
2258
+ color.g ?? 0,
2259
+ color.b ?? 0,
2260
+ color.alpha ?? 1
2261
+ ];
1827
2262
  }
1828
- function getStopRange$2(stops) {
1829
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
1830
- if (!colorStops.length) return {
1831
- min: 0,
1832
- max: 1,
1833
- stops: []
1834
- };
2263
+ function resolveLengthPercentage$5(value, reference) {
2264
+ if (value.kind === "percent") return value.value / 100 * reference;
2265
+ if (value.unit === "px") return value.value;
2266
+ throw new Error(`Unsupported mesh-gradient length unit: ${value.unit}`);
2267
+ }
2268
+ function resolveMeshVertex(vertex, width, height) {
1835
2269
  return {
1836
- min: Math.min(...colorStops.map((stop) => stop.position)),
1837
- max: Math.max(...colorStops.map((stop) => stop.position)),
1838
- stops: colorStops
2270
+ id: vertex.id,
2271
+ x: resolveLengthPercentage$5(vertex.x, width),
2272
+ y: resolveLengthPercentage$5(vertex.y, height),
2273
+ color: toColor(vertex.color)
1839
2274
  };
1840
2275
  }
1841
- function normalizeStops$3(stops, min, max) {
1842
- const range = max - min || 1;
1843
- return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
1844
- ...stop,
1845
- position: (stop.position - min) / range
1846
- }));
2276
+ function buildMeshVertexMap(gradient, width, height) {
2277
+ return new Map(gradient.vertices.map((vertex) => [vertex.id, resolveMeshVertex(vertex, width, height)]));
1847
2278
  }
1848
- var ModuleTransformerLinearGradientToCanvas = class {
1849
- target = "canvas-2d";
1850
- gradientType = "linear-gradient";
1851
- to(input) {
1852
- const gradient = input;
1853
- return { draw: (ctx, width, height) => {
1854
- const angle = gradient.config.angle;
1855
- const dirX = Math.sin(angle);
1856
- const dirY = -Math.cos(angle);
1857
- const centerX = width / 2;
1858
- const centerY = height / 2;
1859
- const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
1860
- let startX = centerX - dirX * lineLength / 2;
1861
- let startY = centerY - dirY * lineLength / 2;
1862
- let endX = centerX + dirX * lineLength / 2;
1863
- let endY = centerY + dirY * lineLength / 2;
1864
- const { min, max, stops } = getStopRange$2(resolveRenderableGradientStops(gradient));
1865
- let normalizedStops = stops;
1866
- if (min < 0 || max > 1) {
1867
- const vx = endX - startX;
1868
- const vy = endY - startY;
1869
- const baseStartX = startX;
1870
- const baseStartY = startY;
1871
- startX = baseStartX + vx * min;
1872
- startY = baseStartY + vy * min;
1873
- endX = baseStartX + vx * max;
1874
- endY = baseStartY + vy * max;
1875
- normalizedStops = normalizeStops$3(stops, min, max);
1876
- }
1877
- const canvasGradient = ctx.createLinearGradient(startX, startY, endX, endY);
1878
- for (const stop of normalizedStops) canvasGradient.addColorStop(stop.position, toCanvasColor$1(stop.value));
1879
- ctx.clearRect(0, 0, width, height);
1880
- ctx.fillStyle = canvasGradient;
1881
- ctx.fillRect(0, 0, width, height);
1882
- } };
2279
+ function buildRegularMeshGrid(gradient, vertexMap) {
2280
+ const idGrid = buildRegularMeshGridFromVertexIds(gradient, vertexMap);
2281
+ if (idGrid) return idGrid;
2282
+ const vertices = gradient.vertices.map((vertex) => {
2283
+ const resolved = vertexMap.get(vertex.id);
2284
+ if (!resolved) throw new Error(`Missing mesh vertex: ${vertex.id}`);
2285
+ return resolved;
2286
+ }).sort((a, b) => {
2287
+ if (Math.abs(a.y - b.y) > 1e-4) return a.y - b.y;
2288
+ return a.x - b.x;
2289
+ });
2290
+ const result = [];
2291
+ for (let row = 0; row < gradient.config.rows; row += 1) {
2292
+ const start = row * gradient.config.columns;
2293
+ const end = start + gradient.config.columns;
2294
+ result.push(vertices.slice(start, end));
1883
2295
  }
1884
- };
1885
- //#endregion
1886
- //#region src/gradient-transformer/modules/canvas/ModuleTransformerRadialGradientToCanvas.ts
1887
- const toRgb$4 = converter("rgb");
1888
- const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
1889
- function toCanvasColor(input) {
1890
- const color = toRgb$4(input);
1891
- if (!color) throw new Error(`Failed to convert color: ${input}`);
1892
- return formatRgb(color);
2296
+ return result;
1893
2297
  }
1894
- function getStopRange$1(stops) {
1895
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
1896
- if (!colorStops.length) return {
1897
- min: 0,
1898
- max: 1,
1899
- stops: []
1900
- };
1901
- return {
1902
- min: Math.min(...colorStops.map((stop) => stop.position)),
1903
- max: Math.max(...colorStops.map((stop) => stop.position)),
1904
- stops: colorStops
2298
+ function buildRegularMeshGridFromVertexIds(gradient, vertexMap) {
2299
+ const result = Array.from({ length: gradient.config.rows }, () => []);
2300
+ for (const vertex of gradient.vertices) {
2301
+ const match = vertex.id.match(/^v(\d+)(\d+)$/);
2302
+ if (!match) return null;
2303
+ const column = Number(match[1]);
2304
+ const row = Number(match[2]);
2305
+ if (!Number.isInteger(row) || !Number.isInteger(column) || row < 0 || column < 0 || row >= gradient.config.rows || column >= gradient.config.columns) return null;
2306
+ const resolved = vertexMap.get(vertex.id);
2307
+ if (!resolved) throw new Error(`Missing mesh vertex: ${vertex.id}`);
2308
+ if (result[row][column]) return null;
2309
+ result[row][column] = resolved;
2310
+ }
2311
+ if (result.some((row) => row.length !== gradient.config.columns || row.some((vertex) => vertex === void 0))) return null;
2312
+ return result;
2313
+ }
2314
+ function findPatchCell(grid, patch) {
2315
+ for (let row = 0; row < grid.length - 1; row += 1) for (let column = 0; column < grid[row].length - 1; column += 1) if (grid[row][column].id === patch.topLeft && grid[row][column + 1].id === patch.topRight && grid[row + 1][column + 1].id === patch.bottomRight && grid[row + 1][column].id === patch.bottomLeft) return {
2316
+ row,
2317
+ column
1905
2318
  };
2319
+ throw new Error(`Mesh patch does not match adjacent regular grid vertices: ${patch.id}`);
1906
2320
  }
1907
- function normalizeStops$2(stops, min, max) {
1908
- const range = max - min || 1;
1909
- return stops.map((stop) => ({
1910
- ...stop,
1911
- position: (stop.position - min) / range
1912
- }));
2321
+ function clampIndex(index, length) {
2322
+ return Math.min(length - 1, Math.max(0, index));
1913
2323
  }
1914
- function getDistanceToSide$1(center, width, height, side) {
1915
- if (side === "left") return center.x;
1916
- if (side === "right") return width - center.x;
1917
- if (side === "top") return center.y;
1918
- return height - center.y;
2324
+ function catmullRom(p0, p1, p2, p3, t) {
2325
+ const t2 = t * t;
2326
+ const t3 = t2 * t;
2327
+ return .5 * (2 * p1 + (-p0 + p2) * t + (2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 + (-p0 + 3 * p1 - 3 * p2 + p3) * t3);
1919
2328
  }
1920
- function getDistanceToCorner$1(center, corner) {
1921
- const dx = corner.x - center.x;
1922
- const dy = corner.y - center.y;
1923
- return Math.sqrt(dx * dx + dy * dy);
2329
+ function clampColor(value) {
2330
+ return Math.min(1, Math.max(0, value));
1924
2331
  }
1925
- function getCornerDeltas$1(center, width, height) {
2332
+ function mix(a, b, t) {
2333
+ return a + (b - a) * t;
2334
+ }
2335
+ function mixColor(a, b, t) {
1926
2336
  return [
1927
- {
2337
+ mix(a[0], b[0], t),
2338
+ mix(a[1], b[1], t),
2339
+ mix(a[2], b[2], t),
2340
+ mix(a[3], b[3], t)
2341
+ ];
2342
+ }
2343
+ function sampleBilinearColor(topLeft, topRight, bottomRight, bottomLeft, u, v) {
2344
+ return mixColor(mixColor(topLeft.color, topRight.color, u), mixColor(bottomLeft.color, bottomRight.color, u), v);
2345
+ }
2346
+ function sampleBicubicColor(grid, row, column, u, v) {
2347
+ const rows = grid.length;
2348
+ const columns = grid[0]?.length ?? 0;
2349
+ const color = [
2350
+ 0,
2351
+ 0,
2352
+ 0,
2353
+ 0
2354
+ ];
2355
+ for (let channel = 0; channel < 4; channel += 1) {
2356
+ const values = [];
2357
+ for (let y = -1; y <= 2; y += 1) {
2358
+ const sampleRow = grid[clampIndex(row + y, rows)];
2359
+ const p0 = sampleRow[clampIndex(column - 1, columns)].color[channel];
2360
+ const p1 = sampleRow[clampIndex(column, columns)].color[channel];
2361
+ const p2 = sampleRow[clampIndex(column + 1, columns)].color[channel];
2362
+ const p3 = sampleRow[clampIndex(column + 2, columns)].color[channel];
2363
+ values.push(catmullRom(p0, p1, p2, p3, u));
2364
+ }
2365
+ color[channel] = clampColor(catmullRom(values[0], values[1], values[2], values[3], v));
2366
+ }
2367
+ return color;
2368
+ }
2369
+ function samplePosition(topLeft, topRight, bottomRight, bottomLeft, u, v) {
2370
+ const topX = mix(topLeft.x, topRight.x, u);
2371
+ const topY = mix(topLeft.y, topRight.y, u);
2372
+ const bottomX = mix(bottomLeft.x, bottomRight.x, u);
2373
+ const bottomY = mix(bottomLeft.y, bottomRight.y, u);
2374
+ return {
2375
+ x: mix(topX, bottomX, v),
2376
+ y: mix(topY, bottomY, v)
2377
+ };
2378
+ }
2379
+ function samplePatchColor(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u, v) {
2380
+ if (gradient.config.method === "bicubic") return sampleBicubicColor(grid, row, column, u, v);
2381
+ return sampleBilinearColor(topLeft, topRight, bottomRight, bottomLeft, u, v).map(clampColor);
2382
+ }
2383
+ function buildPatchTriangles(gradient, grid, patch, vertexMap, subdivisions) {
2384
+ const topLeft = vertexMap.get(patch.topLeft);
2385
+ const topRight = vertexMap.get(patch.topRight);
2386
+ const bottomRight = vertexMap.get(patch.bottomRight);
2387
+ const bottomLeft = vertexMap.get(patch.bottomLeft);
2388
+ if (!topLeft || !topRight || !bottomRight || !bottomLeft) throw new Error(`Mesh patch references missing vertex: ${patch.id}`);
2389
+ const cell = findPatchCell(grid, patch);
2390
+ const samples = [];
2391
+ for (let y = 0; y <= subdivisions; y += 1) {
2392
+ const row = [];
2393
+ const v = y / subdivisions;
2394
+ for (let x = 0; x <= subdivisions; x += 1) {
2395
+ const u = x / subdivisions;
2396
+ const position = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u, v);
2397
+ const color = samplePatchColor(gradient, grid, cell.row, cell.column, topLeft, topRight, bottomRight, bottomLeft, u, v);
2398
+ row.push({
2399
+ id: `${patch.id}:${x}:${y}`,
2400
+ x: position.x,
2401
+ y: position.y,
2402
+ color
2403
+ });
2404
+ }
2405
+ samples.push(row);
2406
+ }
2407
+ const triangles = [];
2408
+ for (let y = 0; y < subdivisions; y += 1) for (let x = 0; x < subdivisions; x += 1) {
2409
+ const topLeftSample = samples[y][x];
2410
+ const topRightSample = samples[y][x + 1];
2411
+ const bottomRightSample = samples[y + 1][x + 1];
2412
+ const bottomLeftSample = samples[y + 1][x];
2413
+ triangles.push([
2414
+ topLeftSample,
2415
+ topRightSample,
2416
+ bottomRightSample
2417
+ ], [
2418
+ topLeftSample,
2419
+ bottomRightSample,
2420
+ bottomLeftSample
2421
+ ]);
2422
+ }
2423
+ return triangles;
2424
+ }
2425
+ function samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u, v, x, y, id) {
2426
+ return {
2427
+ id,
2428
+ x,
2429
+ y,
2430
+ color: samplePatchColor(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u, v)
2431
+ };
2432
+ }
2433
+ function buildMeshEdgeSkirtTriangles(gradient, grid, width, height, subdivisions) {
2434
+ const triangles = [];
2435
+ const rows = grid.length;
2436
+ const columns = grid[0]?.length ?? 0;
2437
+ if (rows < 2 || columns < 2) return triangles;
2438
+ for (let column = 0; column < columns - 1; column += 1) {
2439
+ const topLeft = grid[0][column];
2440
+ const topRight = grid[0][column + 1];
2441
+ const bottomRight = grid[1][column + 1];
2442
+ const bottomLeft = grid[1][column];
2443
+ for (let index = 0; index < subdivisions; index += 1) {
2444
+ const u0 = index / subdivisions;
2445
+ const u1 = (index + 1) / subdivisions;
2446
+ const top0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u0, 0);
2447
+ const top1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u1, 0);
2448
+ const bottom0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u0, 1);
2449
+ const bottom1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u1, 1);
2450
+ const v0 = Math.min(0, (0 - top0.y) / Math.max(bottom0.y - top0.y, 1e-4));
2451
+ const v1 = Math.min(0, (0 - top1.y) / Math.max(bottom1.y - top1.y, 1e-4));
2452
+ const boundary0 = samplePatchVertexAt(gradient, grid, 0, column, topLeft, topRight, bottomRight, bottomLeft, u0, 0, top0.x, top0.y, `top:${column}:${index}:b0`);
2453
+ const boundary1 = samplePatchVertexAt(gradient, grid, 0, column, topLeft, topRight, bottomRight, bottomLeft, u1, 0, top1.x, top1.y, `top:${column}:${index}:b1`);
2454
+ const projected0 = samplePatchVertexAt(gradient, grid, 0, column, topLeft, topRight, bottomRight, bottomLeft, u0, v0, top0.x, 0, `top:${column}:${index}:p0`);
2455
+ const projected1 = samplePatchVertexAt(gradient, grid, 0, column, topLeft, topRight, bottomRight, bottomLeft, u1, v1, top1.x, 0, `top:${column}:${index}:p1`);
2456
+ triangles.push([
2457
+ projected0,
2458
+ projected1,
2459
+ boundary1
2460
+ ], [
2461
+ projected0,
2462
+ boundary1,
2463
+ boundary0
2464
+ ]);
2465
+ }
2466
+ }
2467
+ for (let column = 0; column < columns - 1; column += 1) {
2468
+ const row = rows - 2;
2469
+ const topLeft = grid[row][column];
2470
+ const topRight = grid[row][column + 1];
2471
+ const bottomRight = grid[row + 1][column + 1];
2472
+ const bottomLeft = grid[row + 1][column];
2473
+ for (let index = 0; index < subdivisions; index += 1) {
2474
+ const u0 = index / subdivisions;
2475
+ const u1 = (index + 1) / subdivisions;
2476
+ const top0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u0, 0);
2477
+ const top1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u1, 0);
2478
+ const bottom0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u0, 1);
2479
+ const bottom1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, u1, 1);
2480
+ const v0 = Math.max(1, (height - top0.y) / Math.max(bottom0.y - top0.y, 1e-4));
2481
+ const v1 = Math.max(1, (height - top1.y) / Math.max(bottom1.y - top1.y, 1e-4));
2482
+ const boundary0 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u0, 1, bottom0.x, bottom0.y, `bottom:${column}:${index}:b0`);
2483
+ const boundary1 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u1, 1, bottom1.x, bottom1.y, `bottom:${column}:${index}:b1`);
2484
+ const projected0 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u0, v0, bottom0.x, height, `bottom:${column}:${index}:p0`);
2485
+ const projected1 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u1, v1, bottom1.x, height, `bottom:${column}:${index}:p1`);
2486
+ triangles.push([
2487
+ boundary0,
2488
+ boundary1,
2489
+ projected1
2490
+ ], [
2491
+ boundary0,
2492
+ projected1,
2493
+ projected0
2494
+ ]);
2495
+ }
2496
+ }
2497
+ for (let row = 0; row < rows - 1; row += 1) {
2498
+ const topLeft = grid[row][0];
2499
+ const topRight = grid[row][1];
2500
+ const bottomRight = grid[row + 1][1];
2501
+ const bottomLeft = grid[row + 1][0];
2502
+ for (let index = 0; index < subdivisions; index += 1) {
2503
+ const v0 = index / subdivisions;
2504
+ const v1 = (index + 1) / subdivisions;
2505
+ const left0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 0, v0);
2506
+ const left1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 0, v1);
2507
+ const right0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 1, v0);
2508
+ const right1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 1, v1);
2509
+ const u0 = Math.min(0, (0 - left0.x) / Math.max(right0.x - left0.x, 1e-4));
2510
+ const u1 = Math.min(0, (0 - left1.x) / Math.max(right1.x - left1.x, 1e-4));
2511
+ const boundary0 = samplePatchVertexAt(gradient, grid, row, 0, topLeft, topRight, bottomRight, bottomLeft, 0, v0, left0.x, left0.y, `left:${row}:${index}:b0`);
2512
+ const boundary1 = samplePatchVertexAt(gradient, grid, row, 0, topLeft, topRight, bottomRight, bottomLeft, 0, v1, left1.x, left1.y, `left:${row}:${index}:b1`);
2513
+ const projected0 = samplePatchVertexAt(gradient, grid, row, 0, topLeft, topRight, bottomRight, bottomLeft, u0, v0, 0, left0.y, `left:${row}:${index}:p0`);
2514
+ const projected1 = samplePatchVertexAt(gradient, grid, row, 0, topLeft, topRight, bottomRight, bottomLeft, u1, v1, 0, left1.y, `left:${row}:${index}:p1`);
2515
+ triangles.push([
2516
+ projected0,
2517
+ boundary0,
2518
+ boundary1
2519
+ ], [
2520
+ projected0,
2521
+ boundary1,
2522
+ projected1
2523
+ ]);
2524
+ }
2525
+ }
2526
+ for (let row = 0; row < rows - 1; row += 1) {
2527
+ const column = columns - 2;
2528
+ const topLeft = grid[row][column];
2529
+ const topRight = grid[row][column + 1];
2530
+ const bottomRight = grid[row + 1][column + 1];
2531
+ const bottomLeft = grid[row + 1][column];
2532
+ for (let index = 0; index < subdivisions; index += 1) {
2533
+ const v0 = index / subdivisions;
2534
+ const v1 = (index + 1) / subdivisions;
2535
+ const left0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 0, v0);
2536
+ const left1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 0, v1);
2537
+ const right0 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 1, v0);
2538
+ const right1 = samplePosition(topLeft, topRight, bottomRight, bottomLeft, 1, v1);
2539
+ const u0 = Math.max(1, (width - left0.x) / Math.max(right0.x - left0.x, 1e-4));
2540
+ const u1 = Math.max(1, (width - left1.x) / Math.max(right1.x - left1.x, 1e-4));
2541
+ const boundary0 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, 1, v0, right0.x, right0.y, `right:${row}:${index}:b0`);
2542
+ const boundary1 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, 1, v1, right1.x, right1.y, `right:${row}:${index}:b1`);
2543
+ const projected0 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u0, v0, width, right0.y, `right:${row}:${index}:p0`);
2544
+ const projected1 = samplePatchVertexAt(gradient, grid, row, column, topLeft, topRight, bottomRight, bottomLeft, u1, v1, width, right1.y, `right:${row}:${index}:p1`);
2545
+ triangles.push([
2546
+ boundary0,
2547
+ projected0,
2548
+ projected1
2549
+ ], [
2550
+ boundary0,
2551
+ projected1,
2552
+ boundary1
2553
+ ]);
2554
+ }
2555
+ }
2556
+ return triangles;
2557
+ }
2558
+ //#endregion
2559
+ //#region src/gradient-transformer/modules/css/ModuleTransformerDiamondGradientToCss.ts
2560
+ const DIAMOND_SAMPLE_COUNT = 96;
2561
+ function getColorStops$2(stops) {
2562
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2563
+ }
2564
+ function sampleColorAtPosition$1(stops, position) {
2565
+ if (stops.length === 0) throw new Error("Cannot sample color from empty diamond gradient stops.");
2566
+ if (stops.length === 1 || position <= stops[0].position) return stops[0].value;
2567
+ const lastStop = stops[stops.length - 1];
2568
+ if (position >= lastStop.position) return lastStop.value;
2569
+ for (let index = 0; index < stops.length - 1; index += 1) {
2570
+ const current = stops[index];
2571
+ const next = stops[index + 1];
2572
+ if (position >= current.position && position <= next.position) {
2573
+ const range = next.position - current.position || 1;
2574
+ const localT = (position - current.position) / range;
2575
+ return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
2576
+ }
2577
+ }
2578
+ return lastStop.value;
2579
+ }
2580
+ function resolvePosition(position) {
2581
+ if (position.kind === "keywords") return {
2582
+ x: resolveKeywordX$3(position.x),
2583
+ y: resolveKeywordY$3(position.y)
2584
+ };
2585
+ return {
2586
+ x: resolveLengthPercentage$4(position.x, 100),
2587
+ y: resolveLengthPercentage$4(position.y, 100)
2588
+ };
2589
+ }
2590
+ function resolveKeywordX$3(value) {
2591
+ if (value === "left") return 0;
2592
+ if (value === "right") return 100;
2593
+ return 50;
2594
+ }
2595
+ function resolveKeywordY$3(value) {
2596
+ if (value === "top") return 0;
2597
+ if (value === "bottom") return 100;
2598
+ return 50;
2599
+ }
2600
+ function resolveLengthPercentage$4(value, reference) {
2601
+ if (value.kind === "percent") return value.value / 100 * reference;
2602
+ if (value.unit === "px") return value.value;
2603
+ throw new Error(`Unsupported diamond-gradient length unit for CSS transformer: ${value.unit}`);
2604
+ }
2605
+ function getDistanceToCorner$6(center, corner) {
2606
+ return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
2607
+ }
2608
+ function getCornerDeltas$5(center) {
2609
+ return [
2610
+ {
1928
2611
  dx: -center.x,
1929
2612
  dy: -center.y
1930
2613
  },
1931
2614
  {
1932
- dx: width - center.x,
2615
+ dx: 100 - center.x,
1933
2616
  dy: -center.y
1934
2617
  },
1935
2618
  {
1936
2619
  dx: -center.x,
1937
- dy: height - center.y
2620
+ dy: 100 - center.y
1938
2621
  },
1939
2622
  {
1940
- dx: width - center.x,
1941
- dy: height - center.y
2623
+ dx: 100 - center.x,
2624
+ dy: 100 - center.y
1942
2625
  }
1943
2626
  ];
1944
2627
  }
1945
- function scaleEllipseRadiiToCorner$1(radiusX, radiusY, dx, dy) {
2628
+ function scaleDiamondRadiiToCorner$3(radiusX, radiusY, dx, dy) {
1946
2629
  const safeRadiusX = Math.max(radiusX, 1e-4);
1947
2630
  const safeRadiusY = Math.max(radiusY, 1e-4);
1948
- const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
2631
+ const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
1949
2632
  return {
1950
2633
  x: safeRadiusX * scale,
1951
2634
  y: safeRadiusY * scale
1952
2635
  };
1953
2636
  }
1954
- var ModuleTransformerRadialGradientToCanvas = class {
1955
- target = "canvas-2d";
1956
- gradientType = "radial-gradient";
1957
- to(input) {
1958
- const gradient = input;
1959
- return { draw: (ctx, width, height) => {
1960
- const center = this._resolveCenter(gradient.config.position, width, height);
1961
- const radii = this._resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
1962
- const maxVisibleT = getMaxVisibleRadialT(center, radii, width, height);
1963
- const baseStops = resolveRenderableGradientStops(gradient, RADIAL_GRADIENT_SAMPLE_COUNT);
1964
- const { min, max, stops } = getStopRange$1(gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
1965
- let normalizedStops = stops;
1966
- let innerFactor = 0;
1967
- let outerFactor = gradient.isRepeating ? maxVisibleT : 1;
1968
- if (gradient.isRepeating) normalizedStops = normalizeStops$2(stops, 0, maxVisibleT);
1969
- else if (min < 0 || max > 1) {
1970
- normalizedStops = normalizeStops$2(stops, min, max);
1971
- innerFactor = min;
1972
- outerFactor = max;
1973
- }
1974
- if (gradient.config.shape === "circle") {
1975
- const baseRadius = radii.x;
1976
- const innerRadius = Math.max(0, baseRadius * innerFactor);
1977
- const outerRadius = Math.max(innerRadius + 1e-4, baseRadius * outerFactor);
1978
- const g = ctx.createRadialGradient(center.x, center.y, innerRadius, center.x, center.y, outerRadius);
1979
- for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
1980
- ctx.fillStyle = g;
1981
- ctx.fillRect(0, 0, width, height);
1982
- return;
1983
- }
1984
- const outerRadius = Math.max(radii.x, radii.y);
1985
- const scaleX = radii.x / outerRadius;
1986
- const scaleY = radii.y / outerRadius;
1987
- const innerRadius = Math.max(0, outerRadius * innerFactor);
1988
- const scaledOuterRadius = Math.max(innerRadius + 1e-4, outerRadius * outerFactor);
1989
- ctx.save();
1990
- ctx.translate(center.x, center.y);
1991
- ctx.scale(scaleX, scaleY);
1992
- const g = ctx.createRadialGradient(0, 0, innerRadius, 0, 0, scaledOuterRadius);
1993
- for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor(stop.value));
1994
- ctx.fillStyle = g;
1995
- const drawRadius = scaledOuterRadius + 2;
1996
- ctx.fillRect(-drawRadius / scaleX * 2, -drawRadius / scaleY * 2, drawRadius / scaleX * 4, drawRadius / scaleY * 4);
1997
- ctx.restore();
1998
- } };
1999
- }
2000
- _resolveCenter(position, width, height) {
2001
- if (position.kind === "keywords") return {
2002
- x: this._resolveKeywordX(position.x, width),
2003
- y: this._resolveKeywordY(position.y, height)
2004
- };
2637
+ function getMaxVisibleDiamondT$3(center, radii) {
2638
+ return Math.max(...[
2639
+ {
2640
+ x: 0,
2641
+ y: 0
2642
+ },
2643
+ {
2644
+ x: 100,
2645
+ y: 0
2646
+ },
2647
+ {
2648
+ x: 0,
2649
+ y: 100
2650
+ },
2651
+ {
2652
+ x: 100,
2653
+ y: 100
2654
+ }
2655
+ ].map((corner) => Math.abs(corner.x - center.x) / Math.max(radii.x, 1e-4) + Math.abs(corner.y - center.y) / Math.max(radii.y, 1e-4)));
2656
+ }
2657
+ function resolveDiamondRadii$2(size, shape, center) {
2658
+ if (size.kind === "explicit") {
2659
+ const radiusX = resolveLengthPercentage$4(size.x, 100);
2660
+ const radiusY = size.y ? resolveLengthPercentage$4(size.y, 100) : radiusX;
2005
2661
  return {
2006
- x: this._resolve(position.x, width),
2007
- y: this._resolve(position.y, height)
2662
+ x: Math.max(radiusX, 1e-4),
2663
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
2008
2664
  };
2009
2665
  }
2010
- _resolveKeywordX(value, width) {
2011
- if (value === "left") return 0;
2012
- if (value === "center") return width / 2;
2013
- return width;
2014
- }
2015
- _resolveKeywordY(value, height) {
2016
- if (value === "top") return 0;
2017
- if (value === "center") return height / 2;
2018
- return height;
2019
- }
2020
- _resolveRadialRadii(size, shape, center, width, height) {
2021
- if (size.kind === "explicit") {
2022
- const radiusX = this._resolve(size.x, width);
2023
- const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
2666
+ const left = center.x;
2667
+ const right = 100 - center.x;
2668
+ const top = center.y;
2669
+ const bottom = 100 - center.y;
2670
+ if (shape === "circle") {
2671
+ const cornerDistances = [
2672
+ {
2673
+ x: 0,
2674
+ y: 0
2675
+ },
2676
+ {
2677
+ x: 100,
2678
+ y: 0
2679
+ },
2680
+ {
2681
+ x: 0,
2682
+ y: 100
2683
+ },
2684
+ {
2685
+ x: 100,
2686
+ y: 100
2687
+ }
2688
+ ].map((corner) => getDistanceToCorner$6(center, corner));
2689
+ if (size.value === "closest-side") {
2690
+ const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
2024
2691
  return {
2025
- x: Math.max(radiusX, 1e-4),
2026
- y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
2692
+ x: radius,
2693
+ y: radius
2027
2694
  };
2028
2695
  }
2029
- const left = getDistanceToSide$1(center, width, height, "left");
2030
- const right = getDistanceToSide$1(center, width, height, "right");
2031
- const top = getDistanceToSide$1(center, width, height, "top");
2032
- const bottom = getDistanceToSide$1(center, width, height, "bottom");
2033
- if (shape === "circle") {
2034
- const cornerDistances = [
2035
- {
2036
- x: 0,
2037
- y: 0
2038
- },
2039
- {
2040
- x: width,
2041
- y: 0
2042
- },
2043
- {
2044
- x: 0,
2045
- y: height
2046
- },
2047
- {
2048
- x: width,
2049
- y: height
2050
- }
2051
- ].map((corner) => getDistanceToCorner$1(center, corner));
2052
- if (size.value === "closest-side") {
2053
- const radius = Math.min(left, right, top, bottom);
2054
- return {
2055
- x: radius,
2056
- y: radius
2057
- };
2058
- }
2059
- if (size.value === "farthest-side") {
2060
- const radius = Math.max(left, right, top, bottom);
2061
- return {
2062
- x: radius,
2063
- y: radius
2064
- };
2065
- }
2066
- if (size.value === "closest-corner") {
2067
- const radius = Math.min(...cornerDistances);
2068
- return {
2069
- x: radius,
2070
- y: radius
2071
- };
2072
- }
2073
- const radius = Math.max(...cornerDistances);
2696
+ if (size.value === "farthest-side") {
2697
+ const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
2074
2698
  return {
2075
2699
  x: radius,
2076
2700
  y: radius
2077
2701
  };
2078
2702
  }
2079
- const closestSideRadiusX = Math.min(left, right);
2080
- const closestSideRadiusY = Math.min(top, bottom);
2081
- const farthestSideRadiusX = Math.max(left, right);
2082
- const farthestSideRadiusY = Math.max(top, bottom);
2083
- if (size.value === "closest-side") return {
2084
- x: Math.max(closestSideRadiusX, 1e-4),
2085
- y: Math.max(closestSideRadiusY, 1e-4)
2086
- };
2087
- if (size.value === "farthest-side") return {
2088
- x: Math.max(farthestSideRadiusX, 1e-4),
2089
- y: Math.max(farthestSideRadiusY, 1e-4)
2703
+ if (size.value === "closest-corner") {
2704
+ const radius = Math.max(Math.min(...cornerDistances), 1e-4);
2705
+ return {
2706
+ x: radius,
2707
+ y: radius
2708
+ };
2709
+ }
2710
+ const radius = Math.max(Math.max(...cornerDistances), 1e-4);
2711
+ return {
2712
+ x: radius,
2713
+ y: radius
2090
2714
  };
2091
- const corners = getCornerDeltas$1(center, width, height);
2092
- if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner$1(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
2093
- const closestArea = closest.x * closest.y;
2094
- return current.x * current.y < closestArea ? current : closest;
2095
- });
2096
- return corners.map((corner) => scaleEllipseRadiiToCorner$1(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
2097
- const farthestArea = farthest.x * farthest.y;
2098
- return current.x * current.y > farthestArea ? current : farthest;
2099
- });
2100
2715
  }
2101
- _resolve(value, size) {
2102
- if (value.kind === "percent") return value.value / 100 * size;
2103
- if (value.unit === "px") return value.value;
2104
- return value.value;
2716
+ const closestSideRadiusX = Math.min(left, right);
2717
+ const closestSideRadiusY = Math.min(top, bottom);
2718
+ const farthestSideRadiusX = Math.max(left, right);
2719
+ const farthestSideRadiusY = Math.max(top, bottom);
2720
+ if (size.value === "closest-side") return {
2721
+ x: Math.max(closestSideRadiusX, 1e-4),
2722
+ y: Math.max(closestSideRadiusY, 1e-4)
2723
+ };
2724
+ if (size.value === "farthest-side") return {
2725
+ x: Math.max(farthestSideRadiusX, 1e-4),
2726
+ y: Math.max(farthestSideRadiusY, 1e-4)
2727
+ };
2728
+ const corners = getCornerDeltas$5(center);
2729
+ if (size.value === "closest-corner") return corners.map((corner) => scaleDiamondRadiiToCorner$3(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => current.x * current.y < closest.x * closest.y ? current : closest);
2730
+ return corners.map((corner) => scaleDiamondRadiiToCorner$3(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => current.x * current.y > farthest.x * farthest.y ? current : farthest);
2731
+ }
2732
+ function formatPoint$1(value) {
2733
+ return Number(value.toFixed(3)).toString();
2734
+ }
2735
+ function buildDiamondPolygon$1(center, radii, position) {
2736
+ const x = radii.x * position;
2737
+ const y = radii.y * position;
2738
+ return [
2739
+ `${formatPoint$1(center.x)} ${formatPoint$1(center.y - y)}`,
2740
+ `${formatPoint$1(center.x + x)} ${formatPoint$1(center.y)}`,
2741
+ `${formatPoint$1(center.x)} ${formatPoint$1(center.y + y)}`,
2742
+ `${formatPoint$1(center.x - x)} ${formatPoint$1(center.y)}`
2743
+ ].join(" ");
2744
+ }
2745
+ function encodeSvgDataUrl$2(svg) {
2746
+ return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
2747
+ }
2748
+ var ModuleTransformerDiamondGradientToCss = class {
2749
+ target = "css";
2750
+ gradientType = "diamond-gradient";
2751
+ to(input) {
2752
+ if (!(input instanceof DiamondGradient)) throw new Error("Expected DiamondGradient");
2753
+ const center = resolvePosition(input.config.position);
2754
+ const radii = resolveDiamondRadii$2(input.config.size, input.config.shape, center);
2755
+ const maxVisibleT = getMaxVisibleDiamondT$3(center, radii);
2756
+ const maxT = input.isRepeating ? maxVisibleT : 1;
2757
+ const baseStops = resolveRenderableGradientStops(input, DIAMOND_SAMPLE_COUNT);
2758
+ const stops = getColorStops$2(input.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
2759
+ const outerColor = sampleColorAtPosition$1(stops, maxT);
2760
+ const polygons = [];
2761
+ const sampleCount = Math.max(DIAMOND_SAMPLE_COUNT, Math.ceil(DIAMOND_SAMPLE_COUNT * maxT));
2762
+ for (let index = sampleCount; index >= 0; index -= 1) {
2763
+ const position = index / sampleCount * maxT;
2764
+ const color = sampleColorAtPosition$1(stops, position);
2765
+ const points = buildDiamondPolygon$1(center, radii, position);
2766
+ polygons.push(`<polygon points="${points}" fill="${color}"/>`);
2767
+ }
2768
+ return encodeSvgDataUrl$2([
2769
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">",
2770
+ `<rect width="100" height="100" fill="${outerColor}"/>`,
2771
+ ...polygons,
2772
+ "</svg>"
2773
+ ].join(""));
2105
2774
  }
2106
2775
  };
2107
2776
  //#endregion
2108
- //#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
2109
- const CONIC_GRADIENT_SAMPLE_COUNT = 128;
2110
- const toRgb$3 = converter("rgb");
2111
- var ModuleTransformerConicGradientToCanvas = class {
2112
- target = "canvas-2d";
2777
+ //#region src/gradient-transformer/modules/css/ModuleTransformerConicGradientToCss.ts
2778
+ var ModuleTransformerConicGradientToCss = class {
2779
+ target = "css";
2113
2780
  gradientType = "conic-gradient";
2781
+ to(input) {
2782
+ if (!(input instanceof ConicGradient)) throw new Error("Expected ConicGradient");
2783
+ return input.toString();
2784
+ }
2785
+ };
2786
+ //#endregion
2787
+ //#region src/gradient-transformer/modules/css/ModuleTransformerMeshGradientToCss.ts
2788
+ const CSS_SAMPLE_SIZE = 96;
2789
+ const BICUBIC_SUBDIVISIONS$3 = 24;
2790
+ function formatRgba$1(color) {
2791
+ return `rgba(${color[0]},${color[1]},${color[2]},${Number(color[3].toFixed(3))})`;
2792
+ }
2793
+ function getBarycentric$1(x, y, a, b, c) {
2794
+ const denominator = (b.y - c.y) * (a.x - c.x) + (c.x - b.x) * (a.y - c.y);
2795
+ if (Math.abs(denominator) < 1e-6) return null;
2796
+ const wA = ((b.y - c.y) * (x - c.x) + (c.x - b.x) * (y - c.y)) / denominator;
2797
+ const wB = ((c.y - a.y) * (x - c.x) + (a.x - c.x) * (y - c.y)) / denominator;
2798
+ const wC = 1 - wA - wB;
2799
+ if (wA < -1e-4 || wB < -1e-4 || wC < -1e-4) return null;
2800
+ return [
2801
+ wA,
2802
+ wB,
2803
+ wC
2804
+ ];
2805
+ }
2806
+ function mixTriangleColor$1(weights, a, b, c) {
2807
+ const [wA, wB, wC] = weights;
2808
+ return [
2809
+ Math.round((a.color[0] * wA + b.color[0] * wB + c.color[0] * wC) * 255),
2810
+ Math.round((a.color[1] * wA + b.color[1] * wB + c.color[1] * wC) * 255),
2811
+ Math.round((a.color[2] * wA + b.color[2] * wB + c.color[2] * wC) * 255),
2812
+ a.color[3] * wA + b.color[3] * wB + c.color[3] * wC
2813
+ ];
2814
+ }
2815
+ function encodeSvgDataUrl$1(svg) {
2816
+ return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
2817
+ }
2818
+ var ModuleTransformerMeshGradientToCss = class {
2819
+ target = "css";
2820
+ gradientType = "mesh-gradient";
2821
+ to(input) {
2822
+ if (!(input instanceof MeshGradient)) throw new Error("Expected MeshGradient");
2823
+ const vertexMap = buildMeshVertexMap(input, CSS_SAMPLE_SIZE, CSS_SAMPLE_SIZE);
2824
+ const grid = buildRegularMeshGrid(input, vertexMap);
2825
+ const subdivisions = input.config.method === "bicubic" ? BICUBIC_SUBDIVISIONS$3 : 1;
2826
+ const triangles = input.patches.flatMap((patch) => buildPatchTriangles(input, grid, patch, vertexMap, subdivisions));
2827
+ const edgeTriangles = buildMeshEdgeSkirtTriangles(input, grid, CSS_SAMPLE_SIZE, CSS_SAMPLE_SIZE, subdivisions);
2828
+ const renderTriangles = [...triangles, ...edgeTriangles];
2829
+ const rects = [];
2830
+ for (let y = 0; y < CSS_SAMPLE_SIZE; y += 1) for (let x = 0; x < CSS_SAMPLE_SIZE; x += 1) {
2831
+ const sampleX = x + .5;
2832
+ const sampleY = y + .5;
2833
+ let color = null;
2834
+ for (const [a, b, c] of renderTriangles) {
2835
+ const weights = getBarycentric$1(sampleX, sampleY, a, b, c);
2836
+ if (!weights) continue;
2837
+ color = mixTriangleColor$1(weights, a, b, c);
2838
+ break;
2839
+ }
2840
+ if (!color) continue;
2841
+ rects.push(`<rect x="${x}" y="${y}" width="1" height="1" fill="${formatRgba$1(color)}"/>`);
2842
+ }
2843
+ return encodeSvgDataUrl$1([
2844
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${CSS_SAMPLE_SIZE} ${CSS_SAMPLE_SIZE}" preserveAspectRatio="none" shape-rendering="crispEdges">`,
2845
+ ...rects,
2846
+ "</svg>"
2847
+ ].join(""));
2848
+ }
2849
+ };
2850
+ //#endregion
2851
+ //#region src/gradient-transformer/modules/canvas/ModuleTransformerLinearGradientToCanvas.ts
2852
+ const toRgb$8 = converter("rgb");
2853
+ function toCanvasColor$2(input) {
2854
+ const color = toRgb$8(input);
2855
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
2856
+ return formatRgb(color);
2857
+ }
2858
+ function getStopRange$2(stops) {
2859
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
2860
+ if (!colorStops.length) return {
2861
+ min: 0,
2862
+ max: 1,
2863
+ stops: []
2864
+ };
2865
+ return {
2866
+ min: Math.min(...colorStops.map((stop) => stop.position)),
2867
+ max: Math.max(...colorStops.map((stop) => stop.position)),
2868
+ stops: colorStops
2869
+ };
2870
+ }
2871
+ function normalizeStops$4(stops, min, max) {
2872
+ const range = max - min || 1;
2873
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
2874
+ ...stop,
2875
+ position: (stop.position - min) / range
2876
+ }));
2877
+ }
2878
+ var ModuleTransformerLinearGradientToCanvas = class {
2879
+ target = "canvas-2d";
2880
+ gradientType = "linear-gradient";
2114
2881
  to(input) {
2115
2882
  const gradient = input;
2116
2883
  return { draw: (ctx, width, height) => {
2117
- const imageData = ctx.createImageData(width, height);
2118
- const data = imageData.data;
2119
- const { x: cx, y: cy } = this._resolvePosition(gradient.config.position, width, height);
2120
- const from = this._toRad(gradient.config.from);
2121
- const renderStops = resolveRenderableGradientStops(gradient, CONIC_GRADIENT_SAMPLE_COUNT);
2122
- const stops = this._normalizeStops(renderStops);
2123
- if (stops.length === 0) {
2124
- ctx.putImageData(imageData, 0, 0);
2125
- return;
2884
+ const angle = gradient.config.angle;
2885
+ const dirX = Math.sin(angle);
2886
+ const dirY = -Math.cos(angle);
2887
+ const centerX = width / 2;
2888
+ const centerY = height / 2;
2889
+ const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
2890
+ let startX = centerX - dirX * lineLength / 2;
2891
+ let startY = centerY - dirY * lineLength / 2;
2892
+ let endX = centerX + dirX * lineLength / 2;
2893
+ let endY = centerY + dirY * lineLength / 2;
2894
+ const { min, max, stops } = getStopRange$2(resolveRenderableGradientStops(gradient));
2895
+ let normalizedStops = stops;
2896
+ if (min < 0 || max > 1) {
2897
+ const vx = endX - startX;
2898
+ const vy = endY - startY;
2899
+ const baseStartX = startX;
2900
+ const baseStartY = startY;
2901
+ startX = baseStartX + vx * min;
2902
+ startY = baseStartY + vy * min;
2903
+ endX = baseStartX + vx * max;
2904
+ endY = baseStartY + vy * max;
2905
+ normalizedStops = normalizeStops$4(stops, min, max);
2126
2906
  }
2127
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
2128
- const dx = x - cx;
2129
- const dy = y - cy;
2130
- let angle = Math.atan2(dy, dx) + Math.PI / 2 - from;
2131
- while (angle < 0) angle += Math.PI * 2;
2132
- while (angle >= Math.PI * 2) angle -= Math.PI * 2;
2133
- const t = angle / (Math.PI * 2);
2134
- const color = this._sampleColor(stops, t);
2135
- const index = (y * width + x) * 4;
2136
- data[index] = color.r;
2137
- data[index + 1] = color.g;
2138
- data[index + 2] = color.b;
2139
- data[index + 3] = color.a;
2907
+ const canvasGradient = ctx.createLinearGradient(startX, startY, endX, endY);
2908
+ for (const stop of normalizedStops) canvasGradient.addColorStop(stop.position, toCanvasColor$2(stop.value));
2909
+ ctx.clearRect(0, 0, width, height);
2910
+ ctx.fillStyle = canvasGradient;
2911
+ ctx.fillRect(0, 0, width, height);
2912
+ } };
2913
+ }
2914
+ };
2915
+ //#endregion
2916
+ //#region src/gradient-transformer/modules/canvas/ModuleTransformerRadialGradientToCanvas.ts
2917
+ const toRgb$7 = converter("rgb");
2918
+ const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
2919
+ function toCanvasColor$1(input) {
2920
+ const color = toRgb$7(input);
2921
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
2922
+ return formatRgb(color);
2923
+ }
2924
+ function getStopRange$1(stops) {
2925
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
2926
+ if (!colorStops.length) return {
2927
+ min: 0,
2928
+ max: 1,
2929
+ stops: []
2930
+ };
2931
+ return {
2932
+ min: Math.min(...colorStops.map((stop) => stop.position)),
2933
+ max: Math.max(...colorStops.map((stop) => stop.position)),
2934
+ stops: colorStops
2935
+ };
2936
+ }
2937
+ function normalizeStops$3(stops, min, max) {
2938
+ const range = max - min || 1;
2939
+ return stops.map((stop) => ({
2940
+ ...stop,
2941
+ position: (stop.position - min) / range
2942
+ }));
2943
+ }
2944
+ function getDistanceToSide$1(center, width, height, side) {
2945
+ if (side === "left") return center.x;
2946
+ if (side === "right") return width - center.x;
2947
+ if (side === "top") return center.y;
2948
+ return height - center.y;
2949
+ }
2950
+ function getDistanceToCorner$5(center, corner) {
2951
+ const dx = corner.x - center.x;
2952
+ const dy = corner.y - center.y;
2953
+ return Math.sqrt(dx * dx + dy * dy);
2954
+ }
2955
+ function getCornerDeltas$4(center, width, height) {
2956
+ return [
2957
+ {
2958
+ dx: -center.x,
2959
+ dy: -center.y
2960
+ },
2961
+ {
2962
+ dx: width - center.x,
2963
+ dy: -center.y
2964
+ },
2965
+ {
2966
+ dx: -center.x,
2967
+ dy: height - center.y
2968
+ },
2969
+ {
2970
+ dx: width - center.x,
2971
+ dy: height - center.y
2972
+ }
2973
+ ];
2974
+ }
2975
+ function scaleEllipseRadiiToCorner$1(radiusX, radiusY, dx, dy) {
2976
+ const safeRadiusX = Math.max(radiusX, 1e-4);
2977
+ const safeRadiusY = Math.max(radiusY, 1e-4);
2978
+ const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
2979
+ return {
2980
+ x: safeRadiusX * scale,
2981
+ y: safeRadiusY * scale
2982
+ };
2983
+ }
2984
+ var ModuleTransformerRadialGradientToCanvas = class {
2985
+ target = "canvas-2d";
2986
+ gradientType = "radial-gradient";
2987
+ to(input) {
2988
+ const gradient = input;
2989
+ return { draw: (ctx, width, height) => {
2990
+ const center = this._resolveCenter(gradient.config.position, width, height);
2991
+ const radii = this._resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
2992
+ const maxVisibleT = getMaxVisibleRadialT(center, radii, width, height);
2993
+ const baseStops = resolveRenderableGradientStops(gradient, RADIAL_GRADIENT_SAMPLE_COUNT);
2994
+ const { min, max, stops } = getStopRange$1(gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
2995
+ let normalizedStops = stops;
2996
+ let innerFactor = 0;
2997
+ let outerFactor = gradient.isRepeating ? maxVisibleT : 1;
2998
+ if (gradient.isRepeating) normalizedStops = normalizeStops$3(stops, 0, maxVisibleT);
2999
+ else if (min < 0 || max > 1) {
3000
+ normalizedStops = normalizeStops$3(stops, min, max);
3001
+ innerFactor = min;
3002
+ outerFactor = max;
2140
3003
  }
2141
- ctx.putImageData(imageData, 0, 0);
3004
+ if (gradient.config.shape === "circle") {
3005
+ const baseRadius = radii.x;
3006
+ const innerRadius = Math.max(0, baseRadius * innerFactor);
3007
+ const outerRadius = Math.max(innerRadius + 1e-4, baseRadius * outerFactor);
3008
+ const g = ctx.createRadialGradient(center.x, center.y, innerRadius, center.x, center.y, outerRadius);
3009
+ for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor$1(stop.value));
3010
+ ctx.fillStyle = g;
3011
+ ctx.fillRect(0, 0, width, height);
3012
+ return;
3013
+ }
3014
+ const outerRadius = Math.max(radii.x, radii.y);
3015
+ const scaleX = radii.x / outerRadius;
3016
+ const scaleY = radii.y / outerRadius;
3017
+ const innerRadius = Math.max(0, outerRadius * innerFactor);
3018
+ const scaledOuterRadius = Math.max(innerRadius + 1e-4, outerRadius * outerFactor);
3019
+ ctx.save();
3020
+ ctx.translate(center.x, center.y);
3021
+ ctx.scale(scaleX, scaleY);
3022
+ const g = ctx.createRadialGradient(0, 0, innerRadius, 0, 0, scaledOuterRadius);
3023
+ for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor$1(stop.value));
3024
+ ctx.fillStyle = g;
3025
+ const drawRadius = scaledOuterRadius + 2;
3026
+ ctx.fillRect(-drawRadius / scaleX * 2, -drawRadius / scaleY * 2, drawRadius / scaleX * 4, drawRadius / scaleY * 4);
3027
+ ctx.restore();
3028
+ } };
3029
+ }
3030
+ _resolveCenter(position, width, height) {
3031
+ if (position.kind === "keywords") return {
3032
+ x: this._resolveKeywordX(position.x, width),
3033
+ y: this._resolveKeywordY(position.y, height)
3034
+ };
3035
+ return {
3036
+ x: this._resolve(position.x, width),
3037
+ y: this._resolve(position.y, height)
3038
+ };
3039
+ }
3040
+ _resolveKeywordX(value, width) {
3041
+ if (value === "left") return 0;
3042
+ if (value === "center") return width / 2;
3043
+ return width;
3044
+ }
3045
+ _resolveKeywordY(value, height) {
3046
+ if (value === "top") return 0;
3047
+ if (value === "center") return height / 2;
3048
+ return height;
3049
+ }
3050
+ _resolveRadialRadii(size, shape, center, width, height) {
3051
+ if (size.kind === "explicit") {
3052
+ const radiusX = this._resolve(size.x, width);
3053
+ const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
3054
+ return {
3055
+ x: Math.max(radiusX, 1e-4),
3056
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
3057
+ };
3058
+ }
3059
+ const left = getDistanceToSide$1(center, width, height, "left");
3060
+ const right = getDistanceToSide$1(center, width, height, "right");
3061
+ const top = getDistanceToSide$1(center, width, height, "top");
3062
+ const bottom = getDistanceToSide$1(center, width, height, "bottom");
3063
+ if (shape === "circle") {
3064
+ const cornerDistances = [
3065
+ {
3066
+ x: 0,
3067
+ y: 0
3068
+ },
3069
+ {
3070
+ x: width,
3071
+ y: 0
3072
+ },
3073
+ {
3074
+ x: 0,
3075
+ y: height
3076
+ },
3077
+ {
3078
+ x: width,
3079
+ y: height
3080
+ }
3081
+ ].map((corner) => getDistanceToCorner$5(center, corner));
3082
+ if (size.value === "closest-side") {
3083
+ const radius = Math.min(left, right, top, bottom);
3084
+ return {
3085
+ x: radius,
3086
+ y: radius
3087
+ };
3088
+ }
3089
+ if (size.value === "farthest-side") {
3090
+ const radius = Math.max(left, right, top, bottom);
3091
+ return {
3092
+ x: radius,
3093
+ y: radius
3094
+ };
3095
+ }
3096
+ if (size.value === "closest-corner") {
3097
+ const radius = Math.min(...cornerDistances);
3098
+ return {
3099
+ x: radius,
3100
+ y: radius
3101
+ };
3102
+ }
3103
+ const radius = Math.max(...cornerDistances);
3104
+ return {
3105
+ x: radius,
3106
+ y: radius
3107
+ };
3108
+ }
3109
+ const closestSideRadiusX = Math.min(left, right);
3110
+ const closestSideRadiusY = Math.min(top, bottom);
3111
+ const farthestSideRadiusX = Math.max(left, right);
3112
+ const farthestSideRadiusY = Math.max(top, bottom);
3113
+ if (size.value === "closest-side") return {
3114
+ x: Math.max(closestSideRadiusX, 1e-4),
3115
+ y: Math.max(closestSideRadiusY, 1e-4)
3116
+ };
3117
+ if (size.value === "farthest-side") return {
3118
+ x: Math.max(farthestSideRadiusX, 1e-4),
3119
+ y: Math.max(farthestSideRadiusY, 1e-4)
3120
+ };
3121
+ const corners = getCornerDeltas$4(center, width, height);
3122
+ if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner$1(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
3123
+ const closestArea = closest.x * closest.y;
3124
+ return current.x * current.y < closestArea ? current : closest;
3125
+ });
3126
+ return corners.map((corner) => scaleEllipseRadiiToCorner$1(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
3127
+ const farthestArea = farthest.x * farthest.y;
3128
+ return current.x * current.y > farthestArea ? current : farthest;
3129
+ });
3130
+ }
3131
+ _resolve(value, size) {
3132
+ if (value.kind === "percent") return value.value / 100 * size;
3133
+ if (value.unit === "px") return value.value;
3134
+ return value.value;
3135
+ }
3136
+ };
3137
+ //#endregion
3138
+ //#region src/gradient-transformer/modules/canvas/ModuleTransformerDiamondGradientToCanvas.ts
3139
+ const toRgb$6 = converter("rgb");
3140
+ const DIAMOND_GRADIENT_SAMPLE_COUNT = 128;
3141
+ const DIAMOND_COLOR_LOOKUP_SIZE = 1024;
3142
+ function toCanvasColor(input) {
3143
+ const color = toRgb$6(input);
3144
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
3145
+ return [
3146
+ Math.round((color.r ?? 0) * 255),
3147
+ Math.round((color.g ?? 0) * 255),
3148
+ Math.round((color.b ?? 0) * 255),
3149
+ Math.round((color.alpha ?? 1) * 255)
3150
+ ];
3151
+ }
3152
+ function getColorStops$1(stops) {
3153
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
3154
+ }
3155
+ function sampleColorAtPosition(stops, position) {
3156
+ if (stops.length === 0) throw new Error("Cannot sample color from empty diamond gradient stops.");
3157
+ if (stops.length === 1 || position <= stops[0].position) return toCanvasColor(stops[0].value);
3158
+ const lastStop = stops[stops.length - 1];
3159
+ if (position >= lastStop.position) return toCanvasColor(lastStop.value);
3160
+ for (let index = 0; index < stops.length - 1; index += 1) {
3161
+ const current = stops[index];
3162
+ const next = stops[index + 1];
3163
+ if (position >= current.position && position <= next.position) {
3164
+ const range = next.position - current.position || 1;
3165
+ const localT = (position - current.position) / range;
3166
+ const currentColor = toCanvasColor(current.value);
3167
+ const nextColor = toCanvasColor(next.value);
3168
+ return [
3169
+ Math.round(currentColor[0] + (nextColor[0] - currentColor[0]) * localT),
3170
+ Math.round(currentColor[1] + (nextColor[1] - currentColor[1]) * localT),
3171
+ Math.round(currentColor[2] + (nextColor[2] - currentColor[2]) * localT),
3172
+ Math.round(currentColor[3] + (nextColor[3] - currentColor[3]) * localT)
3173
+ ];
3174
+ }
3175
+ }
3176
+ return toCanvasColor(lastStop.value);
3177
+ }
3178
+ function buildColorLookup(stops, maxT) {
3179
+ const lookup = new Uint8ClampedArray(DIAMOND_COLOR_LOOKUP_SIZE * 4);
3180
+ for (let index = 0; index < DIAMOND_COLOR_LOOKUP_SIZE; index += 1) {
3181
+ const color = sampleColorAtPosition(stops, index / (DIAMOND_COLOR_LOOKUP_SIZE - 1) * maxT);
3182
+ const offset = index * 4;
3183
+ lookup[offset] = color[0];
3184
+ lookup[offset + 1] = color[1];
3185
+ lookup[offset + 2] = color[2];
3186
+ lookup[offset + 3] = color[3];
3187
+ }
3188
+ return lookup;
3189
+ }
3190
+ function getMaxVisibleDiamondT$2(center, radii, width, height) {
3191
+ return Math.max(...[
3192
+ {
3193
+ x: 0,
3194
+ y: 0
3195
+ },
3196
+ {
3197
+ x: width,
3198
+ y: 0
3199
+ },
3200
+ {
3201
+ x: 0,
3202
+ y: height
3203
+ },
3204
+ {
3205
+ x: width,
3206
+ y: height
3207
+ }
3208
+ ].map((corner) => Math.abs(corner.x - center.x) / Math.max(radii.x, 1e-4) + Math.abs(corner.y - center.y) / Math.max(radii.y, 1e-4)));
3209
+ }
3210
+ function getDistanceToCorner$4(center, corner) {
3211
+ return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
3212
+ }
3213
+ function getCornerDeltas$3(center, width, height) {
3214
+ return [
3215
+ {
3216
+ dx: -center.x,
3217
+ dy: -center.y
3218
+ },
3219
+ {
3220
+ dx: width - center.x,
3221
+ dy: -center.y
3222
+ },
3223
+ {
3224
+ dx: -center.x,
3225
+ dy: height - center.y
3226
+ },
3227
+ {
3228
+ dx: width - center.x,
3229
+ dy: height - center.y
3230
+ }
3231
+ ];
3232
+ }
3233
+ function scaleDiamondRadiiToCorner$2(radiusX, radiusY, dx, dy) {
3234
+ const safeRadiusX = Math.max(radiusX, 1e-4);
3235
+ const safeRadiusY = Math.max(radiusY, 1e-4);
3236
+ const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
3237
+ return {
3238
+ x: safeRadiusX * scale,
3239
+ y: safeRadiusY * scale
3240
+ };
3241
+ }
3242
+ var ModuleTransformerDiamondGradientToCanvas = class {
3243
+ target = "canvas-2d";
3244
+ gradientType = "diamond-gradient";
3245
+ to(input) {
3246
+ if (!(input instanceof DiamondGradient)) throw new Error("Expected DiamondGradient");
3247
+ const gradient = input;
3248
+ return { draw: (ctx, width, height) => {
3249
+ const center = this._resolveCenter(gradient.config.position, width, height);
3250
+ const radii = this._resolveDiamondRadii(gradient.config.size, gradient.config.shape, center, width, height);
3251
+ const maxVisibleT = getMaxVisibleDiamondT$2(center, radii, width, height);
3252
+ const baseStops = resolveRenderableGradientStops(gradient, DIAMOND_GRADIENT_SAMPLE_COUNT);
3253
+ const colorLookup = buildColorLookup(getColorStops$1(gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops), maxVisibleT);
3254
+ const imageData = ctx.createImageData(width, height);
3255
+ const data = imageData.data;
3256
+ for (let y = 0; y < height; y += 1) for (let x = 0; x < width; x += 1) {
3257
+ const t = Math.abs(x - center.x) / Math.max(radii.x, 1e-4) + Math.abs(y - center.y) / Math.max(radii.y, 1e-4);
3258
+ const lookupOffset = Math.min(DIAMOND_COLOR_LOOKUP_SIZE - 1, Math.max(0, Math.round(t / Math.max(maxVisibleT, 1e-4) * (DIAMOND_COLOR_LOOKUP_SIZE - 1)))) * 4;
3259
+ const offset = (y * width + x) * 4;
3260
+ data[offset] = colorLookup[lookupOffset];
3261
+ data[offset + 1] = colorLookup[lookupOffset + 1];
3262
+ data[offset + 2] = colorLookup[lookupOffset + 2];
3263
+ data[offset + 3] = colorLookup[lookupOffset + 3];
3264
+ }
3265
+ ctx.putImageData(imageData, 0, 0);
3266
+ } };
3267
+ }
3268
+ _resolveCenter(position, width, height) {
3269
+ if (position.kind === "keywords") return {
3270
+ x: this._resolveKeywordX(position.x, width),
3271
+ y: this._resolveKeywordY(position.y, height)
3272
+ };
3273
+ return {
3274
+ x: this._resolve(position.x, width),
3275
+ y: this._resolve(position.y, height)
3276
+ };
3277
+ }
3278
+ _resolveKeywordX(value, width) {
3279
+ if (value === "left") return 0;
3280
+ if (value === "center") return width / 2;
3281
+ return width;
3282
+ }
3283
+ _resolveKeywordY(value, height) {
3284
+ if (value === "top") return 0;
3285
+ if (value === "center") return height / 2;
3286
+ return height;
3287
+ }
3288
+ _resolveDiamondRadii(size, shape, center, width, height) {
3289
+ if (size.kind === "explicit") {
3290
+ const radiusX = this._resolve(size.x, width);
3291
+ const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
3292
+ return {
3293
+ x: Math.max(radiusX, 1e-4),
3294
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
3295
+ };
3296
+ }
3297
+ const left = center.x;
3298
+ const right = width - center.x;
3299
+ const top = center.y;
3300
+ const bottom = height - center.y;
3301
+ if (shape === "circle") {
3302
+ const cornerDistances = [
3303
+ {
3304
+ x: 0,
3305
+ y: 0
3306
+ },
3307
+ {
3308
+ x: width,
3309
+ y: 0
3310
+ },
3311
+ {
3312
+ x: 0,
3313
+ y: height
3314
+ },
3315
+ {
3316
+ x: width,
3317
+ y: height
3318
+ }
3319
+ ].map((corner) => getDistanceToCorner$4(center, corner));
3320
+ if (size.value === "closest-side") {
3321
+ const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
3322
+ return {
3323
+ x: radius,
3324
+ y: radius
3325
+ };
3326
+ }
3327
+ if (size.value === "farthest-side") {
3328
+ const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
3329
+ return {
3330
+ x: radius,
3331
+ y: radius
3332
+ };
3333
+ }
3334
+ if (size.value === "closest-corner") {
3335
+ const radius = Math.max(Math.min(...cornerDistances), 1e-4);
3336
+ return {
3337
+ x: radius,
3338
+ y: radius
3339
+ };
3340
+ }
3341
+ const radius = Math.max(Math.max(...cornerDistances), 1e-4);
3342
+ return {
3343
+ x: radius,
3344
+ y: radius
3345
+ };
3346
+ }
3347
+ const closestSideRadiusX = Math.min(left, right);
3348
+ const closestSideRadiusY = Math.min(top, bottom);
3349
+ const farthestSideRadiusX = Math.max(left, right);
3350
+ const farthestSideRadiusY = Math.max(top, bottom);
3351
+ if (size.value === "closest-side") return {
3352
+ x: Math.max(closestSideRadiusX, 1e-4),
3353
+ y: Math.max(closestSideRadiusY, 1e-4)
3354
+ };
3355
+ if (size.value === "farthest-side") return {
3356
+ x: Math.max(farthestSideRadiusX, 1e-4),
3357
+ y: Math.max(farthestSideRadiusY, 1e-4)
3358
+ };
3359
+ const corners = getCornerDeltas$3(center, width, height);
3360
+ if (size.value === "closest-corner") return corners.map((corner) => scaleDiamondRadiiToCorner$2(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => current.x * current.y < closest.x * closest.y ? current : closest);
3361
+ return corners.map((corner) => scaleDiamondRadiiToCorner$2(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => current.x * current.y > farthest.x * farthest.y ? current : farthest);
3362
+ }
3363
+ _resolve(value, size) {
3364
+ if (value.kind === "percent") return value.value / 100 * size;
3365
+ if (value.unit === "px") return value.value;
3366
+ throw new Error(`Unsupported diamond-gradient length unit for Canvas transformer: ${value.unit}`);
3367
+ }
3368
+ };
3369
+ //#endregion
3370
+ //#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
3371
+ const CONIC_GRADIENT_SAMPLE_COUNT = 128;
3372
+ const toRgb$5 = converter("rgb");
3373
+ var ModuleTransformerConicGradientToCanvas = class {
3374
+ target = "canvas-2d";
3375
+ gradientType = "conic-gradient";
3376
+ to(input) {
3377
+ const gradient = input;
3378
+ return { draw: (ctx, width, height) => {
3379
+ const imageData = ctx.createImageData(width, height);
3380
+ const data = imageData.data;
3381
+ const { x: cx, y: cy } = this._resolvePosition(gradient.config.position, width, height);
3382
+ const from = this._toRad(gradient.config.from);
3383
+ const renderStops = resolveRenderableGradientStops(gradient, CONIC_GRADIENT_SAMPLE_COUNT);
3384
+ const stops = this._normalizeStops(renderStops);
3385
+ if (stops.length === 0) {
3386
+ ctx.putImageData(imageData, 0, 0);
3387
+ return;
3388
+ }
3389
+ for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
3390
+ const dx = x - cx;
3391
+ const dy = y - cy;
3392
+ let angle = Math.atan2(dy, dx) + Math.PI / 2 - from;
3393
+ while (angle < 0) angle += Math.PI * 2;
3394
+ while (angle >= Math.PI * 2) angle -= Math.PI * 2;
3395
+ const t = angle / (Math.PI * 2);
3396
+ const color = this._sampleColor(stops, t);
3397
+ const index = (y * width + x) * 4;
3398
+ data[index] = color.r;
3399
+ data[index + 1] = color.g;
3400
+ data[index + 2] = color.b;
3401
+ data[index + 3] = color.a;
3402
+ }
3403
+ ctx.putImageData(imageData, 0, 0);
3404
+ } };
3405
+ }
3406
+ _resolvePosition(position, width, height) {
3407
+ if (position.kind === "keywords") return {
3408
+ x: this._resolveKeywordX(position.x, width),
3409
+ y: this._resolveKeywordY(position.y, height)
3410
+ };
3411
+ return {
3412
+ x: this._resolve(position.x, width),
3413
+ y: this._resolve(position.y, height)
3414
+ };
3415
+ }
3416
+ _resolve(value, size) {
3417
+ if (value.kind === "percent") return value.value / 100 * size;
3418
+ if (value.unit === "px") return value.value;
3419
+ return value.value;
3420
+ }
3421
+ _resolveKeywordX(value, width) {
3422
+ if (value === "left") return 0;
3423
+ if (value === "right") return width;
3424
+ return width / 2;
3425
+ }
3426
+ _resolveKeywordY(value, height) {
3427
+ if (value === "top") return 0;
3428
+ if (value === "bottom") return height;
3429
+ return height / 2;
3430
+ }
3431
+ _toRad(angle) {
3432
+ if (angle.unit === "deg") return degToRad(angle.value);
3433
+ if (angle.unit === "turn") return turnToRad(angle.value);
3434
+ if (angle.unit === "grad") return gradToRad(angle.value);
3435
+ return angle.value;
3436
+ }
3437
+ _normalizeStops(stops) {
3438
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
3439
+ position: this._clamp01(stop.position),
3440
+ color: this._parseColor(stop.value)
3441
+ })).sort((a, b) => a.position - b.position);
3442
+ }
3443
+ _sampleColor(stops, t) {
3444
+ if (stops.length === 1) return stops[0].color;
3445
+ const first = stops[0];
3446
+ const extended = [...stops, {
3447
+ ...first,
3448
+ position: first.position + 1
3449
+ }];
3450
+ let sampleT = t;
3451
+ if (sampleT < first.position) sampleT += 1;
3452
+ for (let i = 0; i < extended.length - 1; i++) {
3453
+ const left = extended[i];
3454
+ const right = extended[i + 1];
3455
+ if (sampleT >= left.position && sampleT <= right.position) {
3456
+ const span = right.position - left.position || 1;
3457
+ const localT = (sampleT - left.position) / span;
3458
+ return this._mixColor(left.color, right.color, localT);
3459
+ }
3460
+ }
3461
+ return stops[stops.length - 1].color;
3462
+ }
3463
+ _mixColor(from, to, t) {
3464
+ return {
3465
+ r: Math.round(from.r + (to.r - from.r) * t),
3466
+ g: Math.round(from.g + (to.g - from.g) * t),
3467
+ b: Math.round(from.b + (to.b - from.b) * t),
3468
+ a: Math.round(from.a + (to.a - from.a) * t)
3469
+ };
3470
+ }
3471
+ _parseColor(input) {
3472
+ const color = toRgb$5(input);
3473
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
3474
+ return {
3475
+ r: Math.round((color.r ?? 0) * 255),
3476
+ g: Math.round((color.g ?? 0) * 255),
3477
+ b: Math.round((color.b ?? 0) * 255),
3478
+ a: Math.round((color.alpha ?? 1) * 255)
3479
+ };
3480
+ }
3481
+ _clamp01(value) {
3482
+ return Math.max(0, Math.min(1, value));
3483
+ }
3484
+ };
3485
+ //#endregion
3486
+ //#region src/gradient-transformer/modules/canvas/ModuleTransformerMeshGradientToCanvas.ts
3487
+ const BICUBIC_SUBDIVISIONS$2 = 24;
3488
+ function getBarycentric(x, y, a, b, c) {
3489
+ const denominator = (b.y - c.y) * (a.x - c.x) + (c.x - b.x) * (a.y - c.y);
3490
+ if (Math.abs(denominator) < 1e-6) return null;
3491
+ const wA = ((b.y - c.y) * (x - c.x) + (c.x - b.x) * (y - c.y)) / denominator;
3492
+ const wB = ((c.y - a.y) * (x - c.x) + (a.x - c.x) * (y - c.y)) / denominator;
3493
+ const wC = 1 - wA - wB;
3494
+ const epsilon = -1e-4;
3495
+ if (wA < epsilon || wB < epsilon || wC < epsilon) return null;
3496
+ return [
3497
+ wA,
3498
+ wB,
3499
+ wC
3500
+ ];
3501
+ }
3502
+ function mixTriangleColor(weights, a, b, c) {
3503
+ const [wA, wB, wC] = weights;
3504
+ return [
3505
+ Math.round((a.color[0] * wA + b.color[0] * wB + c.color[0] * wC) * 255),
3506
+ Math.round((a.color[1] * wA + b.color[1] * wB + c.color[1] * wC) * 255),
3507
+ Math.round((a.color[2] * wA + b.color[2] * wB + c.color[2] * wC) * 255),
3508
+ Math.round((a.color[3] * wA + b.color[3] * wB + c.color[3] * wC) * 255)
3509
+ ];
3510
+ }
3511
+ function fillTriangle(data, width, height, a, b, c) {
3512
+ const minX = Math.max(0, Math.floor(Math.min(a.x, b.x, c.x)));
3513
+ const maxX = Math.min(width - 1, Math.ceil(Math.max(a.x, b.x, c.x)));
3514
+ const minY = Math.max(0, Math.floor(Math.min(a.y, b.y, c.y)));
3515
+ const maxY = Math.min(height - 1, Math.ceil(Math.max(a.y, b.y, c.y)));
3516
+ for (let y = minY; y <= maxY; y += 1) for (let x = minX; x <= maxX; x += 1) {
3517
+ const weights = getBarycentric(x + .5, y + .5, a, b, c);
3518
+ if (!weights) continue;
3519
+ const color = mixTriangleColor(weights, a, b, c);
3520
+ const offset = (y * width + x) * 4;
3521
+ data[offset] = color[0];
3522
+ data[offset + 1] = color[1];
3523
+ data[offset + 2] = color[2];
3524
+ data[offset + 3] = color[3];
3525
+ }
3526
+ }
3527
+ var ModuleTransformerMeshGradientToCanvas = class {
3528
+ target = "canvas-2d";
3529
+ gradientType = "mesh-gradient";
3530
+ to(input) {
3531
+ if (!(input instanceof MeshGradient)) throw new Error("Expected MeshGradient");
3532
+ const gradient = input;
3533
+ return { draw: (ctx, width, height) => {
3534
+ const imageData = ctx.createImageData(width, height);
3535
+ const vertexMap = buildMeshVertexMap(gradient, width, height);
3536
+ const grid = buildRegularMeshGrid(gradient, vertexMap);
3537
+ const subdivisions = gradient.config.method === "bicubic" ? BICUBIC_SUBDIVISIONS$2 : 1;
3538
+ const edgeTriangles = buildMeshEdgeSkirtTriangles(gradient, grid, width, height, subdivisions);
3539
+ for (const patch of gradient.patches) {
3540
+ const triangles = buildPatchTriangles(gradient, grid, patch, vertexMap, subdivisions);
3541
+ for (const [a, b, c] of triangles) fillTriangle(imageData.data, width, height, a, b, c);
3542
+ }
3543
+ for (const [a, b, c] of edgeTriangles) fillTriangle(imageData.data, width, height, a, b, c);
3544
+ ctx.putImageData(imageData, 0, 0);
3545
+ } };
3546
+ }
3547
+ };
3548
+ //#endregion
3549
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerLinearGradientToWebgl.ts
3550
+ const toRgb$4 = converter("rgb");
3551
+ const MAX_STOPS$3 = 128;
3552
+ function toWebGLColor$3(input) {
3553
+ const color = toRgb$4(input);
3554
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
3555
+ return [
3556
+ color.r ?? 0,
3557
+ color.g ?? 0,
3558
+ color.b ?? 0,
3559
+ color.alpha ?? 1
3560
+ ];
3561
+ }
3562
+ function getStopRange(stops) {
3563
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
3564
+ if (!colorStops.length) return {
3565
+ min: 0,
3566
+ max: 1,
3567
+ stops: []
3568
+ };
3569
+ return {
3570
+ min: Math.min(...colorStops.map((stop) => stop.position)),
3571
+ max: Math.max(...colorStops.map((stop) => stop.position)),
3572
+ stops: colorStops
3573
+ };
3574
+ }
3575
+ function normalizeStops$2(stops, min, max) {
3576
+ const range = max - min || 1;
3577
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
3578
+ ...stop,
3579
+ position: (stop.position - min) / range
3580
+ }));
3581
+ }
3582
+ function createShader$4(gl, type, source) {
3583
+ const shader = gl.createShader(type);
3584
+ if (!shader) throw new Error("Failed to create WebGL shader.");
3585
+ gl.shaderSource(shader, source);
3586
+ gl.compileShader(shader);
3587
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
3588
+ const error = gl.getShaderInfoLog(shader);
3589
+ gl.deleteShader(shader);
3590
+ throw new Error(`WebGL shader compile error: ${error}`);
3591
+ }
3592
+ return shader;
3593
+ }
3594
+ function createProgram$4(gl, vertexSource, fragmentSource) {
3595
+ const vertexShader = createShader$4(gl, gl.VERTEX_SHADER, vertexSource);
3596
+ const fragmentShader = createShader$4(gl, gl.FRAGMENT_SHADER, fragmentSource);
3597
+ const program = gl.createProgram();
3598
+ if (!program) throw new Error("Failed to create WebGL program.");
3599
+ gl.attachShader(program, vertexShader);
3600
+ gl.attachShader(program, fragmentShader);
3601
+ gl.linkProgram(program);
3602
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
3603
+ const error = gl.getProgramInfoLog(program);
3604
+ gl.deleteProgram(program);
3605
+ throw new Error(`WebGL program link error: ${error}`);
3606
+ }
3607
+ return program;
3608
+ }
3609
+ function getColorStopCount$3(stops) {
3610
+ return stops.filter((stop) => stop.type === "color-stop").length;
3611
+ }
3612
+ function getWebGLSampleCount$3(gradient, maxStops) {
3613
+ const colorStopCount = getColorStopCount$3(gradient.stops);
3614
+ const segmentCount = Math.max(1, colorStopCount - 1);
3615
+ return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
3616
+ }
3617
+ function getColorAtPosition$3(stops, position) {
3618
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
3619
+ if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
3620
+ if (position <= colorStops[0].position) return colorStops[0].value;
3621
+ const lastStop = colorStops[colorStops.length - 1];
3622
+ if (position >= lastStop.position) return lastStop.value;
3623
+ for (let index = 0; index < colorStops.length - 1; index += 1) {
3624
+ const current = colorStops[index];
3625
+ const next = colorStops[index + 1];
3626
+ if (position >= current.position && position <= next.position) {
3627
+ const range = next.position - current.position || 1;
3628
+ const localT = (position - current.position) / range;
3629
+ return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
3630
+ }
3631
+ }
3632
+ return lastStop.value;
3633
+ }
3634
+ function fitStopsToWebGLLimit$3(stops, maxStops) {
3635
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
3636
+ if (colorStops.length <= maxStops) return colorStops;
3637
+ const sampledStops = [];
3638
+ for (let index = 0; index < maxStops; index += 1) {
3639
+ const position = index / (maxStops - 1);
3640
+ sampledStops.push({
3641
+ type: "color-stop",
3642
+ value: getColorAtPosition$3(colorStops, position),
3643
+ position
3644
+ });
3645
+ }
3646
+ return sampledStops;
3647
+ }
3648
+ var ModuleTransformerLinearGradientToCanvasWebGL = class {
3649
+ target = "canvas-webgl";
3650
+ gradientType = "linear-gradient";
3651
+ to(input) {
3652
+ const gradient = input;
3653
+ return { draw: (canvas, width, height) => {
3654
+ const gl = canvas.getContext("webgl");
3655
+ if (!gl) throw new Error("WebGL is not supported.");
3656
+ canvas.width = width;
3657
+ canvas.height = height;
3658
+ gl.viewport(0, 0, width, height);
3659
+ const program = createProgram$4(gl, `
3660
+ attribute vec2 a_position;
3661
+ varying vec2 v_uv;
3662
+
3663
+ void main() {
3664
+ v_uv = a_position * 0.5 + 0.5;
3665
+ gl_Position = vec4(a_position, 0.0, 1.0);
3666
+ }
3667
+ `, `
3668
+ precision mediump float;
3669
+
3670
+ varying vec2 v_uv;
3671
+
3672
+ uniform vec2 u_start;
3673
+ uniform vec2 u_end;
3674
+ uniform int u_stopCount;
3675
+ uniform float u_positions[${MAX_STOPS$3}];
3676
+ uniform vec4 u_colors[${MAX_STOPS$3}];
3677
+
3678
+ vec4 getGradientColor(float t) {
3679
+ vec4 result = u_colors[0];
3680
+
3681
+ for (int i = 0; i < ${MAX_STOPS$3 - 1}; i++) {
3682
+ if (i >= u_stopCount - 1) {
3683
+ break;
3684
+ }
3685
+
3686
+ float currentPosition = u_positions[i];
3687
+ float nextPosition = u_positions[i + 1];
3688
+
3689
+ if (t <= currentPosition) {
3690
+ return u_colors[i];
3691
+ }
3692
+
3693
+ if (t >= currentPosition && t <= nextPosition) {
3694
+ float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
3695
+ return mix(u_colors[i], u_colors[i + 1], localT);
3696
+ }
3697
+
3698
+ result = u_colors[i + 1];
3699
+ }
3700
+
3701
+ return result;
3702
+ }
3703
+
3704
+ void main() {
3705
+ vec2 axis = u_end - u_start;
3706
+ vec2 point = v_uv;
3707
+
3708
+ float axisLengthSquared = dot(axis, axis);
3709
+ float t = dot(point - u_start, axis) / axisLengthSquared;
3710
+
3711
+ t = clamp(t, 0.0, 1.0);
3712
+
3713
+ gl_FragColor = getGradientColor(t);
3714
+ }
3715
+ `);
3716
+ gl.useProgram(program);
3717
+ const positionBuffer = gl.createBuffer();
3718
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
3719
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
3720
+ -1,
3721
+ -1,
3722
+ 1,
3723
+ -1,
3724
+ -1,
3725
+ 1,
3726
+ -1,
3727
+ 1,
3728
+ 1,
3729
+ -1,
3730
+ 1,
3731
+ 1
3732
+ ]), gl.STATIC_DRAW);
3733
+ const positionLocation = gl.getAttribLocation(program, "a_position");
3734
+ gl.enableVertexAttribArray(positionLocation);
3735
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
3736
+ const angle = gradient.config.angle;
3737
+ const dirX = Math.sin(angle);
3738
+ const dirY = -Math.cos(angle);
3739
+ const centerX = width / 2;
3740
+ const centerY = height / 2;
3741
+ const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
3742
+ let startX = centerX - dirX * lineLength / 2;
3743
+ let startY = centerY - dirY * lineLength / 2;
3744
+ let endX = centerX + dirX * lineLength / 2;
3745
+ let endY = centerY + dirY * lineLength / 2;
3746
+ const { min, max, stops } = getStopRange(resolveRenderableGradientStops(gradient, getWebGLSampleCount$3(gradient, MAX_STOPS$3)));
3747
+ let normalizedStops = stops;
3748
+ if (min < 0 || max > 1) {
3749
+ const vx = endX - startX;
3750
+ const vy = endY - startY;
3751
+ const baseStartX = startX;
3752
+ const baseStartY = startY;
3753
+ startX = baseStartX + vx * min;
3754
+ startY = baseStartY + vy * min;
3755
+ endX = baseStartX + vx * max;
3756
+ endY = baseStartY + vy * max;
3757
+ normalizedStops = normalizeStops$2(stops, min, max);
3758
+ }
3759
+ const startU = startX / width;
3760
+ const startV = 1 - startY / height;
3761
+ const endU = endX / width;
3762
+ const endV = 1 - endY / height;
3763
+ const limitedStops = fitStopsToWebGLLimit$3(normalizedStops, MAX_STOPS$3);
3764
+ const positions = new Float32Array(MAX_STOPS$3);
3765
+ const colors = new Float32Array(MAX_STOPS$3 * 4);
3766
+ limitedStops.forEach((stop, index) => {
3767
+ const color = toWebGLColor$3(stop.value);
3768
+ positions[index] = stop.position;
3769
+ colors[index * 4 + 0] = color[0];
3770
+ colors[index * 4 + 1] = color[1];
3771
+ colors[index * 4 + 2] = color[2];
3772
+ colors[index * 4 + 3] = color[3];
3773
+ });
3774
+ gl.uniform2f(gl.getUniformLocation(program, "u_start"), startU, startV);
3775
+ gl.uniform2f(gl.getUniformLocation(program, "u_end"), endU, endV);
3776
+ gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
3777
+ gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
3778
+ gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
3779
+ gl.clearColor(0, 0, 0, 0);
3780
+ gl.clear(gl.COLOR_BUFFER_BIT);
3781
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
3782
+ } };
3783
+ }
3784
+ };
3785
+ //#endregion
3786
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerConicGradientToWebgl.ts
3787
+ const toRgb$3 = converter("rgb");
3788
+ const MAX_STOPS$2 = 128;
3789
+ const TWO_PI$1 = Math.PI * 2;
3790
+ function toWebGLColor$2(input) {
3791
+ const color = toRgb$3(input);
3792
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
3793
+ return [
3794
+ color.r ?? 0,
3795
+ color.g ?? 0,
3796
+ color.b ?? 0,
3797
+ color.alpha ?? 1
3798
+ ];
3799
+ }
3800
+ function createShader$3(gl, type, source) {
3801
+ const shader = gl.createShader(type);
3802
+ if (!shader) throw new Error("Failed to create WebGL shader.");
3803
+ gl.shaderSource(shader, source);
3804
+ gl.compileShader(shader);
3805
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
3806
+ const error = gl.getShaderInfoLog(shader);
3807
+ gl.deleteShader(shader);
3808
+ throw new Error(`WebGL shader compile error: ${error}`);
3809
+ }
3810
+ return shader;
3811
+ }
3812
+ function createProgram$3(gl, vertexSource, fragmentSource) {
3813
+ const vertexShader = createShader$3(gl, gl.VERTEX_SHADER, vertexSource);
3814
+ const fragmentShader = createShader$3(gl, gl.FRAGMENT_SHADER, fragmentSource);
3815
+ const program = gl.createProgram();
3816
+ if (!program) throw new Error("Failed to create WebGL program.");
3817
+ gl.attachShader(program, vertexShader);
3818
+ gl.attachShader(program, fragmentShader);
3819
+ gl.linkProgram(program);
3820
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
3821
+ const error = gl.getProgramInfoLog(program);
3822
+ gl.deleteProgram(program);
3823
+ throw new Error(`WebGL program link error: ${error}`);
3824
+ }
3825
+ return program;
3826
+ }
3827
+ function getColorStopCount$2(stops) {
3828
+ return stops.filter((stop) => stop.type === "color-stop").length;
3829
+ }
3830
+ function getWebGLSampleCount$2(gradient, maxStops) {
3831
+ const colorStopCount = getColorStopCount$2(gradient.stops);
3832
+ const segmentCount = Math.max(1, colorStopCount - 1);
3833
+ return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
3834
+ }
3835
+ function getColorAtPosition$2(stops, position) {
3836
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
3837
+ if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
3838
+ if (position <= colorStops[0].position) return colorStops[0].value;
3839
+ const lastStop = colorStops[colorStops.length - 1];
3840
+ if (position >= lastStop.position) return lastStop.value;
3841
+ for (let index = 0; index < colorStops.length - 1; index += 1) {
3842
+ const current = colorStops[index];
3843
+ const next = colorStops[index + 1];
3844
+ if (position >= current.position && position <= next.position) {
3845
+ const range = next.position - current.position || 1;
3846
+ const localT = (position - current.position) / range;
3847
+ return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
3848
+ }
3849
+ }
3850
+ return lastStop.value;
3851
+ }
3852
+ function fitStopsToWebGLLimit$2(stops, maxStops) {
3853
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
3854
+ if (colorStops.length <= maxStops) return colorStops;
3855
+ const sampledStops = [];
3856
+ for (let index = 0; index < maxStops; index += 1) {
3857
+ const position = index / (maxStops - 1);
3858
+ sampledStops.push({
3859
+ type: "color-stop",
3860
+ value: getColorAtPosition$2(colorStops, position),
3861
+ position
3862
+ });
3863
+ }
3864
+ return sampledStops;
3865
+ }
3866
+ function resolveLengthPercentage$3(value, reference) {
3867
+ if (value.kind === "percent") return value.value / 100 * reference;
3868
+ if (value.kind === "length") {
3869
+ if (value.unit === "px") return value.value;
3870
+ throw new Error(`Unsupported gradient length unit for WebGL conic gradient: ${value.unit}`);
3871
+ }
3872
+ return value;
3873
+ }
3874
+ function resolveAngleToRadians$1(angle) {
3875
+ if (angle.unit === "rad") return angle.value;
3876
+ if (angle.unit === "deg") return angle.value / 360 * TWO_PI$1;
3877
+ if (angle.unit === "turn") return angle.value * TWO_PI$1;
3878
+ if (angle.unit === "grad") return angle.value / 400 * TWO_PI$1;
3879
+ return angle.unit;
3880
+ }
3881
+ function resolveKeywordPositionX$2(x, width) {
3882
+ if (x === "left") return 0;
3883
+ if (x === "center") return width / 2;
3884
+ if (x === "right") return width;
3885
+ return width / 2;
3886
+ }
3887
+ function resolveKeywordPositionY$2(y, height) {
3888
+ if (y === "top") return 0;
3889
+ if (y === "center") return height / 2;
3890
+ if (y === "bottom") return height;
3891
+ return height / 2;
3892
+ }
3893
+ function resolveConicCenter(position, width, height) {
3894
+ if (position.kind === "keywords") return {
3895
+ x: resolveKeywordPositionX$2(position.x, width),
3896
+ y: resolveKeywordPositionY$2(position.y, height)
3897
+ };
3898
+ if (position.kind === "values") return {
3899
+ x: resolveLengthPercentage$3(position.x, width),
3900
+ y: resolveLengthPercentage$3(position.y, height)
3901
+ };
3902
+ return {
3903
+ x: width / 2,
3904
+ y: height / 2
3905
+ };
3906
+ }
3907
+ var ModuleTransformerConicGradientToCanvasWebGL = class {
3908
+ target = "canvas-webgl";
3909
+ gradientType = "conic-gradient";
3910
+ to(input) {
3911
+ const gradient = input;
3912
+ return { draw: (canvas, width, height) => {
3913
+ const gl = canvas.getContext("webgl");
3914
+ if (!gl) throw new Error("WebGL is not supported.");
3915
+ canvas.width = width;
3916
+ canvas.height = height;
3917
+ gl.viewport(0, 0, width, height);
3918
+ const program = createProgram$3(gl, `
3919
+ attribute vec2 a_position;
3920
+ varying vec2 v_uv;
3921
+
3922
+ void main() {
3923
+ v_uv = a_position * 0.5 + 0.5;
3924
+ gl_Position = vec4(a_position, 0.0, 1.0);
3925
+ }
3926
+ `, `
3927
+ precision mediump float;
3928
+
3929
+ const float PI = 3.141592653589793;
3930
+ const float TWO_PI = 6.283185307179586;
3931
+
3932
+ varying vec2 v_uv;
3933
+
3934
+ uniform vec2 u_center;
3935
+ uniform float u_startAngle;
3936
+ uniform int u_stopCount;
3937
+ uniform float u_positions[${MAX_STOPS$2}];
3938
+ uniform vec4 u_colors[${MAX_STOPS$2}];
3939
+
3940
+ vec4 getGradientColor(float t) {
3941
+ vec4 result = u_colors[0];
3942
+
3943
+ for (int i = 0; i < ${MAX_STOPS$2 - 1}; i++) {
3944
+ if (i >= u_stopCount - 1) {
3945
+ break;
3946
+ }
3947
+
3948
+ float currentPosition = u_positions[i];
3949
+ float nextPosition = u_positions[i + 1];
3950
+
3951
+ if (t <= currentPosition) {
3952
+ return u_colors[i];
3953
+ }
3954
+
3955
+ if (t >= currentPosition && t <= nextPosition) {
3956
+ float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
3957
+ return mix(u_colors[i], u_colors[i + 1], localT);
3958
+ }
3959
+
3960
+ result = u_colors[i + 1];
3961
+ }
3962
+
3963
+ return result;
3964
+ }
3965
+
3966
+ void main() {
3967
+ vec2 delta = v_uv - u_center;
3968
+
3969
+ float angle = atan(delta.y, delta.x);
3970
+ float cssAngle = mod((PI * 0.5) - angle + TWO_PI, TWO_PI);
3971
+
3972
+ float t = mod(cssAngle - u_startAngle + TWO_PI, TWO_PI) / TWO_PI;
3973
+
3974
+ gl_FragColor = getGradientColor(t);
3975
+ }
3976
+ `);
3977
+ gl.useProgram(program);
3978
+ const positionBuffer = gl.createBuffer();
3979
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
3980
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
3981
+ -1,
3982
+ -1,
3983
+ 1,
3984
+ -1,
3985
+ -1,
3986
+ 1,
3987
+ -1,
3988
+ 1,
3989
+ 1,
3990
+ -1,
3991
+ 1,
3992
+ 1
3993
+ ]), gl.STATIC_DRAW);
3994
+ const positionLocation = gl.getAttribLocation(program, "a_position");
3995
+ gl.enableVertexAttribArray(positionLocation);
3996
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
3997
+ const center = resolveConicCenter(gradient.config.position, width, height);
3998
+ const limitedStops = fitStopsToWebGLLimit$2(resolveRenderableGradientStops(gradient, getWebGLSampleCount$2(gradient, MAX_STOPS$2)), MAX_STOPS$2);
3999
+ const positions = new Float32Array(MAX_STOPS$2);
4000
+ const colors = new Float32Array(MAX_STOPS$2 * 4);
4001
+ limitedStops.forEach((stop, index) => {
4002
+ const color = toWebGLColor$2(stop.value);
4003
+ positions[index] = stop.position;
4004
+ colors[index * 4 + 0] = color[0];
4005
+ colors[index * 4 + 1] = color[1];
4006
+ colors[index * 4 + 2] = color[2];
4007
+ colors[index * 4 + 3] = color[3];
4008
+ });
4009
+ gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
4010
+ gl.uniform1f(gl.getUniformLocation(program, "u_startAngle"), resolveAngleToRadians$1(gradient.config.from));
4011
+ gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
4012
+ gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
4013
+ gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
4014
+ gl.clearColor(0, 0, 0, 0);
4015
+ gl.clear(gl.COLOR_BUFFER_BIT);
4016
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
2142
4017
  } };
2143
4018
  }
2144
- _resolvePosition(position, width, height) {
2145
- if (position.kind === "keywords") return {
2146
- x: this._resolveKeywordX(position.x, width),
2147
- y: this._resolveKeywordY(position.y, height)
2148
- };
2149
- return {
2150
- x: this._resolve(position.x, width),
2151
- y: this._resolve(position.y, height)
2152
- };
2153
- }
2154
- _resolve(value, size) {
2155
- if (value.kind === "percent") return value.value / 100 * size;
2156
- if (value.unit === "px") return value.value;
2157
- return value.value;
2158
- }
2159
- _resolveKeywordX(value, width) {
2160
- if (value === "left") return 0;
2161
- if (value === "right") return width;
2162
- return width / 2;
2163
- }
2164
- _resolveKeywordY(value, height) {
2165
- if (value === "top") return 0;
2166
- if (value === "bottom") return height;
2167
- return height / 2;
2168
- }
2169
- _toRad(angle) {
2170
- if (angle.unit === "deg") return degToRad(angle.value);
2171
- if (angle.unit === "turn") return turnToRad(angle.value);
2172
- if (angle.unit === "grad") return gradToRad(angle.value);
2173
- return angle.value;
2174
- }
2175
- _normalizeStops(stops) {
2176
- return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
2177
- position: this._clamp01(stop.position),
2178
- color: this._parseColor(stop.value)
2179
- })).sort((a, b) => a.position - b.position);
2180
- }
2181
- _sampleColor(stops, t) {
2182
- if (stops.length === 1) return stops[0].color;
2183
- const first = stops[0];
2184
- const extended = [...stops, {
2185
- ...first,
2186
- position: first.position + 1
2187
- }];
2188
- let sampleT = t;
2189
- if (sampleT < first.position) sampleT += 1;
2190
- for (let i = 0; i < extended.length - 1; i++) {
2191
- const left = extended[i];
2192
- const right = extended[i + 1];
2193
- if (sampleT >= left.position && sampleT <= right.position) {
2194
- const span = right.position - left.position || 1;
2195
- const localT = (sampleT - left.position) / span;
2196
- return this._mixColor(left.color, right.color, localT);
2197
- }
2198
- }
2199
- return stops[stops.length - 1].color;
2200
- }
2201
- _mixColor(from, to, t) {
2202
- return {
2203
- r: Math.round(from.r + (to.r - from.r) * t),
2204
- g: Math.round(from.g + (to.g - from.g) * t),
2205
- b: Math.round(from.b + (to.b - from.b) * t),
2206
- a: Math.round(from.a + (to.a - from.a) * t)
2207
- };
2208
- }
2209
- _parseColor(input) {
2210
- const color = toRgb$3(input);
2211
- if (!color) throw new Error(`Failed to convert color: ${input}`);
2212
- return {
2213
- r: Math.round((color.r ?? 0) * 255),
2214
- g: Math.round((color.g ?? 0) * 255),
2215
- b: Math.round((color.b ?? 0) * 255),
2216
- a: Math.round((color.alpha ?? 1) * 255)
2217
- };
2218
- }
2219
- _clamp01(value) {
2220
- return Math.max(0, Math.min(1, value));
2221
- }
2222
4019
  };
2223
4020
  //#endregion
2224
- //#region src/gradient-transformer/modules/webgl/ModuleTransformerLinearGradientToWebgl.ts
4021
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
2225
4022
  const toRgb$2 = converter("rgb");
2226
- const MAX_STOPS$2 = 128;
2227
- function toWebGLColor$2(input) {
4023
+ const MAX_STOPS$1 = 128;
4024
+ const MAX_REPEATING_RADIAL_T = 16;
4025
+ function toWebGLColor$1(input) {
2228
4026
  const color = toRgb$2(input);
2229
4027
  if (!color) throw new Error(`Failed to convert color: ${input}`);
2230
4028
  return [
@@ -2234,26 +4032,6 @@ function toWebGLColor$2(input) {
2234
4032
  color.alpha ?? 1
2235
4033
  ];
2236
4034
  }
2237
- function getStopRange(stops) {
2238
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null);
2239
- if (!colorStops.length) return {
2240
- min: 0,
2241
- max: 1,
2242
- stops: []
2243
- };
2244
- return {
2245
- min: Math.min(...colorStops.map((stop) => stop.position)),
2246
- max: Math.max(...colorStops.map((stop) => stop.position)),
2247
- stops: colorStops
2248
- };
2249
- }
2250
- function normalizeStops$1(stops, min, max) {
2251
- const range = max - min || 1;
2252
- return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
2253
- ...stop,
2254
- position: (stop.position - min) / range
2255
- }));
2256
- }
2257
4035
  function createShader$2(gl, type, source) {
2258
4036
  const shader = gl.createShader(type);
2259
4037
  if (!shader) throw new Error("Failed to create WebGL shader.");
@@ -2281,15 +4059,15 @@ function createProgram$2(gl, vertexSource, fragmentSource) {
2281
4059
  }
2282
4060
  return program;
2283
4061
  }
2284
- function getColorStopCount$2(stops) {
4062
+ function getColorStopCount$1(stops) {
2285
4063
  return stops.filter((stop) => stop.type === "color-stop").length;
2286
4064
  }
2287
- function getWebGLSampleCount$2(gradient, maxStops) {
2288
- const colorStopCount = getColorStopCount$2(gradient.stops);
4065
+ function getWebGLSampleCount$1(gradient, maxStops) {
4066
+ const colorStopCount = getColorStopCount$1(gradient.stops);
2289
4067
  const segmentCount = Math.max(1, colorStopCount - 1);
2290
4068
  return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
2291
4069
  }
2292
- function getColorAtPosition$2(stops, position) {
4070
+ function getColorAtPosition$1(stops, position) {
2293
4071
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2294
4072
  if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
2295
4073
  if (position <= colorStops[0].position) return colorStops[0].value;
@@ -2304,25 +4082,187 @@ function getColorAtPosition$2(stops, position) {
2304
4082
  return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
2305
4083
  }
2306
4084
  }
2307
- return lastStop.value;
2308
- }
2309
- function fitStopsToWebGLLimit$2(stops, maxStops) {
2310
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2311
- if (colorStops.length <= maxStops) return colorStops;
2312
- const sampledStops = [];
2313
- for (let index = 0; index < maxStops; index += 1) {
2314
- const position = index / (maxStops - 1);
2315
- sampledStops.push({
2316
- type: "color-stop",
2317
- value: getColorAtPosition$2(colorStops, position),
2318
- position
2319
- });
2320
- }
2321
- return sampledStops;
4085
+ return lastStop.value;
4086
+ }
4087
+ function fitStopsToWebGLLimit$1(stops, maxStops) {
4088
+ const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
4089
+ if (colorStops.length <= maxStops) return colorStops;
4090
+ const sampledStops = [];
4091
+ for (let index = 0; index < maxStops; index += 1) {
4092
+ const position = index / (maxStops - 1);
4093
+ sampledStops.push({
4094
+ type: "color-stop",
4095
+ value: getColorAtPosition$1(colorStops, position),
4096
+ position
4097
+ });
4098
+ }
4099
+ return sampledStops;
4100
+ }
4101
+ function parseLengthPercentage$1(value, reference) {
4102
+ if (value.kind === "percent") return value.value / 100 * reference;
4103
+ if (value.kind === "length") {
4104
+ if (value.unit === "px") return value.value;
4105
+ throw new Error(`Unsupported gradient length unit for WebGL radial gradient: ${value.unit}`);
4106
+ }
4107
+ return value;
4108
+ }
4109
+ function resolveKeywordPositionX$1(x, width) {
4110
+ if (x === "left") return 0;
4111
+ if (x === "center") return width / 2;
4112
+ if (x === "right") return width;
4113
+ return width / 2;
4114
+ }
4115
+ function resolveKeywordPositionY$1(y, height) {
4116
+ if (y === "top") return 0;
4117
+ if (y === "center") return height / 2;
4118
+ if (y === "bottom") return height;
4119
+ return height / 2;
4120
+ }
4121
+ function resolveRadialCenter(position, width, height) {
4122
+ if (position.kind === "keywords") return {
4123
+ x: resolveKeywordPositionX$1(position.x, width),
4124
+ y: resolveKeywordPositionY$1(position.y, height)
4125
+ };
4126
+ if (position.kind === "values") return {
4127
+ x: parseLengthPercentage$1(position.x, width),
4128
+ y: parseLengthPercentage$1(position.y, height)
4129
+ };
4130
+ return {
4131
+ x: width / 2,
4132
+ y: height / 2
4133
+ };
4134
+ }
4135
+ function getDistanceToSide(center, width, height, side) {
4136
+ if (side === "left") return center.x;
4137
+ if (side === "right") return width - center.x;
4138
+ if (side === "top") return center.y;
4139
+ return height - center.y;
4140
+ }
4141
+ function getDistanceToCorner$3(center, corner) {
4142
+ const dx = corner.x - center.x;
4143
+ const dy = corner.y - center.y;
4144
+ return Math.sqrt(dx * dx + dy * dy);
4145
+ }
4146
+ function normalizeStops$1(stops, min, max) {
4147
+ const range = max - min || 1;
4148
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
4149
+ ...stop,
4150
+ position: (stop.position - min) / range
4151
+ }));
4152
+ }
4153
+ function getCornerDeltas$2(center, width, height) {
4154
+ return [
4155
+ {
4156
+ dx: -center.x,
4157
+ dy: -center.y
4158
+ },
4159
+ {
4160
+ dx: width - center.x,
4161
+ dy: -center.y
4162
+ },
4163
+ {
4164
+ dx: -center.x,
4165
+ dy: height - center.y
4166
+ },
4167
+ {
4168
+ dx: width - center.x,
4169
+ dy: height - center.y
4170
+ }
4171
+ ];
4172
+ }
4173
+ function scaleEllipseRadiiToCorner(radiusX, radiusY, dx, dy) {
4174
+ const safeRadiusX = Math.max(radiusX, 1e-4);
4175
+ const safeRadiusY = Math.max(radiusY, 1e-4);
4176
+ const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
4177
+ return {
4178
+ x: safeRadiusX * scale,
4179
+ y: safeRadiusY * scale
4180
+ };
4181
+ }
4182
+ function resolveRadialRadii(size, shape, center, width, height) {
4183
+ if (size.kind === "explicit") {
4184
+ const radiusX = parseLengthPercentage$1(size.x, width);
4185
+ const radiusY = size.y ? parseLengthPercentage$1(size.y, height) : radiusX;
4186
+ return {
4187
+ x: Math.max(radiusX, 1e-4),
4188
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
4189
+ };
4190
+ }
4191
+ const left = getDistanceToSide(center, width, height, "left");
4192
+ const right = getDistanceToSide(center, width, height, "right");
4193
+ const top = getDistanceToSide(center, width, height, "top");
4194
+ const bottom = getDistanceToSide(center, width, height, "bottom");
4195
+ if (shape === "circle") {
4196
+ const cornerDistances = [
4197
+ {
4198
+ x: 0,
4199
+ y: 0
4200
+ },
4201
+ {
4202
+ x: width,
4203
+ y: 0
4204
+ },
4205
+ {
4206
+ x: 0,
4207
+ y: height
4208
+ },
4209
+ {
4210
+ x: width,
4211
+ y: height
4212
+ }
4213
+ ].map((corner) => getDistanceToCorner$3(center, corner));
4214
+ if (size.value === "closest-side") {
4215
+ const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
4216
+ return {
4217
+ x: radius,
4218
+ y: radius
4219
+ };
4220
+ }
4221
+ if (size.value === "farthest-side") {
4222
+ const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
4223
+ return {
4224
+ x: radius,
4225
+ y: radius
4226
+ };
4227
+ }
4228
+ if (size.value === "closest-corner") {
4229
+ const radius = Math.max(Math.min(...cornerDistances), 1e-4);
4230
+ return {
4231
+ x: radius,
4232
+ y: radius
4233
+ };
4234
+ }
4235
+ const radius = Math.max(Math.max(...cornerDistances), 1e-4);
4236
+ return {
4237
+ x: radius,
4238
+ y: radius
4239
+ };
4240
+ }
4241
+ const closestSideRadiusX = Math.min(left, right);
4242
+ const closestSideRadiusY = Math.min(top, bottom);
4243
+ const farthestSideRadiusX = Math.max(left, right);
4244
+ const farthestSideRadiusY = Math.max(top, bottom);
4245
+ if (size.value === "closest-side") return {
4246
+ x: Math.max(closestSideRadiusX, 1e-4),
4247
+ y: Math.max(closestSideRadiusY, 1e-4)
4248
+ };
4249
+ if (size.value === "farthest-side") return {
4250
+ x: Math.max(farthestSideRadiusX, 1e-4),
4251
+ y: Math.max(farthestSideRadiusY, 1e-4)
4252
+ };
4253
+ const corners = getCornerDeltas$2(center, width, height);
4254
+ if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
4255
+ const closestArea = closest.x * closest.y;
4256
+ return current.x * current.y < closestArea ? current : closest;
4257
+ });
4258
+ return corners.map((corner) => scaleEllipseRadiiToCorner(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
4259
+ const farthestArea = farthest.x * farthest.y;
4260
+ return current.x * current.y > farthestArea ? current : farthest;
4261
+ });
2322
4262
  }
2323
- var ModuleTransformerLinearGradientToCanvasWebGL = class {
4263
+ var ModuleTransformerRadialGradientToCanvasWebGL = class {
2324
4264
  target = "canvas-webgl";
2325
- gradientType = "linear-gradient";
4265
+ gradientType = "radial-gradient";
2326
4266
  to(input) {
2327
4267
  const gradient = input;
2328
4268
  return { draw: (canvas, width, height) => {
@@ -2344,16 +4284,17 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2344
4284
 
2345
4285
  varying vec2 v_uv;
2346
4286
 
2347
- uniform vec2 u_start;
2348
- uniform vec2 u_end;
4287
+ uniform vec2 u_center;
4288
+ uniform vec2 u_radius;
2349
4289
  uniform int u_stopCount;
2350
- uniform float u_positions[${MAX_STOPS$2}];
2351
- uniform vec4 u_colors[${MAX_STOPS$2}];
4290
+ uniform float u_positions[${MAX_STOPS$1}];
4291
+ uniform vec4 u_colors[${MAX_STOPS$1}];
4292
+ uniform float u_tMax;
2352
4293
 
2353
4294
  vec4 getGradientColor(float t) {
2354
4295
  vec4 result = u_colors[0];
2355
4296
 
2356
- for (int i = 0; i < ${MAX_STOPS$2 - 1}; i++) {
4297
+ for (int i = 0; i < ${MAX_STOPS$1 - 1}; i++) {
2357
4298
  if (i >= u_stopCount - 1) {
2358
4299
  break;
2359
4300
  }
@@ -2377,13 +4318,12 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2377
4318
  }
2378
4319
 
2379
4320
  void main() {
2380
- vec2 axis = u_end - u_start;
2381
- vec2 point = v_uv;
2382
-
2383
- float axisLengthSquared = dot(axis, axis);
2384
- float t = dot(point - u_start, axis) / axisLengthSquared;
4321
+ vec2 delta = v_uv - u_center;
4322
+ vec2 normalized = delta / max(u_radius, vec2(0.00001));
4323
+ float t = length(normalized);
2385
4324
 
2386
- t = clamp(t, 0.0, 1.0);
4325
+ t = clamp(t, 0.0, u_tMax);
4326
+ t = t / max(u_tMax, 0.00001);
2387
4327
 
2388
4328
  gl_FragColor = getGradientColor(t);
2389
4329
  }
@@ -2408,46 +4348,27 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2408
4348
  const positionLocation = gl.getAttribLocation(program, "a_position");
2409
4349
  gl.enableVertexAttribArray(positionLocation);
2410
4350
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
2411
- const angle = gradient.config.angle;
2412
- const dirX = Math.sin(angle);
2413
- const dirY = -Math.cos(angle);
2414
- const centerX = width / 2;
2415
- const centerY = height / 2;
2416
- const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
2417
- let startX = centerX - dirX * lineLength / 2;
2418
- let startY = centerY - dirY * lineLength / 2;
2419
- let endX = centerX + dirX * lineLength / 2;
2420
- let endY = centerY + dirY * lineLength / 2;
2421
- const { min, max, stops } = getStopRange(resolveRenderableGradientStops(gradient, getWebGLSampleCount$2(gradient, MAX_STOPS$2)));
2422
- let normalizedStops = stops;
2423
- if (min < 0 || max > 1) {
2424
- const vx = endX - startX;
2425
- const vy = endY - startY;
2426
- const baseStartX = startX;
2427
- const baseStartY = startY;
2428
- startX = baseStartX + vx * min;
2429
- startY = baseStartY + vy * min;
2430
- endX = baseStartX + vx * max;
2431
- endY = baseStartY + vy * max;
2432
- normalizedStops = normalizeStops$1(stops, min, max);
2433
- }
2434
- const startU = startX / width;
2435
- const startV = 1 - startY / height;
2436
- const endU = endX / width;
2437
- const endV = 1 - endY / height;
2438
- const limitedStops = fitStopsToWebGLLimit$2(normalizedStops, MAX_STOPS$2);
2439
- const positions = new Float32Array(MAX_STOPS$2);
2440
- const colors = new Float32Array(MAX_STOPS$2 * 4);
4351
+ const center = resolveRadialCenter(gradient.config.position, width, height);
4352
+ const radius = resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
4353
+ const maxVisibleT = getMaxVisibleRadialT(center, radius, width, height);
4354
+ const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount$1(gradient, MAX_STOPS$1));
4355
+ const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_RADIAL_T);
4356
+ const maxT = gradient.isRepeating ? repeatMaxT : 1;
4357
+ const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
4358
+ const limitedStops = fitStopsToWebGLLimit$1(gradient.isRepeating ? normalizeStops$1(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS$1);
4359
+ const positions = new Float32Array(MAX_STOPS$1);
4360
+ const colors = new Float32Array(MAX_STOPS$1 * 4);
2441
4361
  limitedStops.forEach((stop, index) => {
2442
- const color = toWebGLColor$2(stop.value);
4362
+ const color = toWebGLColor$1(stop.value);
2443
4363
  positions[index] = stop.position;
2444
4364
  colors[index * 4 + 0] = color[0];
2445
4365
  colors[index * 4 + 1] = color[1];
2446
4366
  colors[index * 4 + 2] = color[2];
2447
4367
  colors[index * 4 + 3] = color[3];
2448
4368
  });
2449
- gl.uniform2f(gl.getUniformLocation(program, "u_start"), startU, startV);
2450
- gl.uniform2f(gl.getUniformLocation(program, "u_end"), endU, endV);
4369
+ gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
4370
+ gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
4371
+ gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
2451
4372
  gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
2452
4373
  gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
2453
4374
  gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
@@ -2458,11 +4379,11 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
2458
4379
  }
2459
4380
  };
2460
4381
  //#endregion
2461
- //#region src/gradient-transformer/modules/webgl/ModuleTransformerConicGradientToWebgl.ts
4382
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerDiamondGradientToWebgl.ts
2462
4383
  const toRgb$1 = converter("rgb");
2463
- const MAX_STOPS$1 = 128;
2464
- const TWO_PI = Math.PI * 2;
2465
- function toWebGLColor$1(input) {
4384
+ const MAX_STOPS = 128;
4385
+ const MAX_REPEATING_DIAMOND_T = 16;
4386
+ function toWebGLColor(input) {
2466
4387
  const color = toRgb$1(input);
2467
4388
  if (!color) throw new Error(`Failed to convert color: ${input}`);
2468
4389
  return [
@@ -2499,17 +4420,17 @@ function createProgram$1(gl, vertexSource, fragmentSource) {
2499
4420
  }
2500
4421
  return program;
2501
4422
  }
2502
- function getColorStopCount$1(stops) {
4423
+ function getColorStopCount(stops) {
2503
4424
  return stops.filter((stop) => stop.type === "color-stop").length;
2504
4425
  }
2505
- function getWebGLSampleCount$1(gradient, maxStops) {
2506
- const colorStopCount = getColorStopCount$1(gradient.stops);
4426
+ function getWebGLSampleCount(gradient, maxStops) {
4427
+ const colorStopCount = getColorStopCount(gradient.stops);
2507
4428
  const segmentCount = Math.max(1, colorStopCount - 1);
2508
4429
  return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
2509
4430
  }
2510
- function getColorAtPosition$1(stops, position) {
4431
+ function getColorAtPosition(stops, position) {
2511
4432
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2512
- if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
4433
+ if (colorStops.length === 0) throw new Error("Cannot sample color from empty diamond gradient stops.");
2513
4434
  if (position <= colorStops[0].position) return colorStops[0].value;
2514
4435
  const lastStop = colorStops[colorStops.length - 1];
2515
4436
  if (position >= lastStop.position) return lastStop.value;
@@ -2524,7 +4445,7 @@ function getColorAtPosition$1(stops, position) {
2524
4445
  }
2525
4446
  return lastStop.value;
2526
4447
  }
2527
- function fitStopsToWebGLLimit$1(stops, maxStops) {
4448
+ function fitStopsToWebGLLimit(stops, maxStops) {
2528
4449
  const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2529
4450
  if (colorStops.length <= maxStops) return colorStops;
2530
4451
  const sampledStops = [];
@@ -2532,57 +4453,176 @@ function fitStopsToWebGLLimit$1(stops, maxStops) {
2532
4453
  const position = index / (maxStops - 1);
2533
4454
  sampledStops.push({
2534
4455
  type: "color-stop",
2535
- value: getColorAtPosition$1(colorStops, position),
4456
+ value: getColorAtPosition(colorStops, position),
2536
4457
  position
2537
4458
  });
2538
4459
  }
2539
4460
  return sampledStops;
2540
4461
  }
2541
- function resolveLengthPercentage(value, reference) {
2542
- if (value.kind === "percent") return value.value / 100 * reference;
2543
- if (value.kind === "length") {
2544
- if (value.unit === "px") return value.value;
2545
- throw new Error(`Unsupported gradient length unit for WebGL conic gradient: ${value.unit}`);
2546
- }
2547
- return value;
4462
+ function normalizeStops(stops, min, max) {
4463
+ const range = max - min || 1;
4464
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
4465
+ ...stop,
4466
+ position: (stop.position - min) / range
4467
+ }));
2548
4468
  }
2549
- function resolveAngleToRadians(angle) {
2550
- if (angle.unit === "rad") return angle.value;
2551
- if (angle.unit === "deg") return angle.value / 360 * TWO_PI;
2552
- if (angle.unit === "turn") return angle.value * TWO_PI;
2553
- if (angle.unit === "grad") return angle.value / 400 * TWO_PI;
2554
- return angle.unit;
4469
+ function parseLengthPercentage(value, reference) {
4470
+ if (value.kind === "percent") return value.value / 100 * reference;
4471
+ if (value.unit === "px") return value.value;
4472
+ throw new Error(`Unsupported diamond-gradient length unit for WebGL transformer: ${value.unit}`);
2555
4473
  }
2556
- function resolveKeywordPositionX$1(x, width) {
4474
+ function resolveKeywordPositionX(x, width) {
2557
4475
  if (x === "left") return 0;
2558
- if (x === "center") return width / 2;
2559
4476
  if (x === "right") return width;
2560
4477
  return width / 2;
2561
4478
  }
2562
- function resolveKeywordPositionY$1(y, height) {
4479
+ function resolveKeywordPositionY(y, height) {
2563
4480
  if (y === "top") return 0;
2564
- if (y === "center") return height / 2;
2565
4481
  if (y === "bottom") return height;
2566
4482
  return height / 2;
2567
4483
  }
2568
- function resolveConicCenter(position, width, height) {
4484
+ function resolveDiamondCenter(position, width, height) {
2569
4485
  if (position.kind === "keywords") return {
2570
- x: resolveKeywordPositionX$1(position.x, width),
2571
- y: resolveKeywordPositionY$1(position.y, height)
4486
+ x: resolveKeywordPositionX(position.x, width),
4487
+ y: resolveKeywordPositionY(position.y, height)
2572
4488
  };
2573
- if (position.kind === "values") return {
2574
- x: resolveLengthPercentage(position.x, width),
2575
- y: resolveLengthPercentage(position.y, height)
4489
+ return {
4490
+ x: parseLengthPercentage(position.x, width),
4491
+ y: parseLengthPercentage(position.y, height)
4492
+ };
4493
+ }
4494
+ function getDistanceToCorner$2(center, corner) {
4495
+ return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
4496
+ }
4497
+ function getCornerDeltas$1(center, width, height) {
4498
+ return [
4499
+ {
4500
+ dx: -center.x,
4501
+ dy: -center.y
4502
+ },
4503
+ {
4504
+ dx: width - center.x,
4505
+ dy: -center.y
4506
+ },
4507
+ {
4508
+ dx: -center.x,
4509
+ dy: height - center.y
4510
+ },
4511
+ {
4512
+ dx: width - center.x,
4513
+ dy: height - center.y
4514
+ }
4515
+ ];
4516
+ }
4517
+ function scaleDiamondRadiiToCorner$1(radiusX, radiusY, dx, dy) {
4518
+ const safeRadiusX = Math.max(radiusX, 1e-4);
4519
+ const safeRadiusY = Math.max(radiusY, 1e-4);
4520
+ const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
4521
+ return {
4522
+ x: safeRadiusX * scale,
4523
+ y: safeRadiusY * scale
4524
+ };
4525
+ }
4526
+ function resolveDiamondRadii$1(size, shape, center, width, height) {
4527
+ if (size.kind === "explicit") {
4528
+ const radiusX = parseLengthPercentage(size.x, width);
4529
+ const radiusY = size.y ? parseLengthPercentage(size.y, height) : radiusX;
4530
+ return {
4531
+ x: Math.max(radiusX, 1e-4),
4532
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
4533
+ };
4534
+ }
4535
+ const left = center.x;
4536
+ const right = width - center.x;
4537
+ const top = center.y;
4538
+ const bottom = height - center.y;
4539
+ if (shape === "circle") {
4540
+ const cornerDistances = [
4541
+ {
4542
+ x: 0,
4543
+ y: 0
4544
+ },
4545
+ {
4546
+ x: width,
4547
+ y: 0
4548
+ },
4549
+ {
4550
+ x: 0,
4551
+ y: height
4552
+ },
4553
+ {
4554
+ x: width,
4555
+ y: height
4556
+ }
4557
+ ].map((corner) => getDistanceToCorner$2(center, corner));
4558
+ if (size.value === "closest-side") {
4559
+ const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
4560
+ return {
4561
+ x: radius,
4562
+ y: radius
4563
+ };
4564
+ }
4565
+ if (size.value === "farthest-side") {
4566
+ const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
4567
+ return {
4568
+ x: radius,
4569
+ y: radius
4570
+ };
4571
+ }
4572
+ if (size.value === "closest-corner") {
4573
+ const radius = Math.max(Math.min(...cornerDistances), 1e-4);
4574
+ return {
4575
+ x: radius,
4576
+ y: radius
4577
+ };
4578
+ }
4579
+ const radius = Math.max(Math.max(...cornerDistances), 1e-4);
4580
+ return {
4581
+ x: radius,
4582
+ y: radius
4583
+ };
4584
+ }
4585
+ const closestSideRadiusX = Math.min(left, right);
4586
+ const closestSideRadiusY = Math.min(top, bottom);
4587
+ const farthestSideRadiusX = Math.max(left, right);
4588
+ const farthestSideRadiusY = Math.max(top, bottom);
4589
+ if (size.value === "closest-side") return {
4590
+ x: Math.max(closestSideRadiusX, 1e-4),
4591
+ y: Math.max(closestSideRadiusY, 1e-4)
2576
4592
  };
2577
- return {
2578
- x: width / 2,
2579
- y: height / 2
4593
+ if (size.value === "farthest-side") return {
4594
+ x: Math.max(farthestSideRadiusX, 1e-4),
4595
+ y: Math.max(farthestSideRadiusY, 1e-4)
2580
4596
  };
4597
+ const corners = getCornerDeltas$1(center, width, height);
4598
+ if (size.value === "closest-corner") return corners.map((corner) => scaleDiamondRadiiToCorner$1(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => current.x * current.y < closest.x * closest.y ? current : closest);
4599
+ return corners.map((corner) => scaleDiamondRadiiToCorner$1(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => current.x * current.y > farthest.x * farthest.y ? current : farthest);
2581
4600
  }
2582
- var ModuleTransformerConicGradientToCanvasWebGL = class {
4601
+ function getMaxVisibleDiamondT$1(center, radii, width, height) {
4602
+ return Math.max(...[
4603
+ {
4604
+ x: 0,
4605
+ y: 0
4606
+ },
4607
+ {
4608
+ x: width,
4609
+ y: 0
4610
+ },
4611
+ {
4612
+ x: 0,
4613
+ y: height
4614
+ },
4615
+ {
4616
+ x: width,
4617
+ y: height
4618
+ }
4619
+ ].map((corner) => Math.abs(corner.x - center.x) / Math.max(radii.x, 1e-4) + Math.abs(corner.y - center.y) / Math.max(radii.y, 1e-4)));
4620
+ }
4621
+ var ModuleTransformerDiamondGradientToCanvasWebGL = class {
2583
4622
  target = "canvas-webgl";
2584
- gradientType = "conic-gradient";
4623
+ gradientType = "diamond-gradient";
2585
4624
  to(input) {
4625
+ if (!(input instanceof DiamondGradient)) throw new Error("Expected DiamondGradient");
2586
4626
  const gradient = input;
2587
4627
  return { draw: (canvas, width, height) => {
2588
4628
  const gl = canvas.getContext("webgl");
@@ -2601,21 +4641,19 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
2601
4641
  `, `
2602
4642
  precision mediump float;
2603
4643
 
2604
- const float PI = 3.141592653589793;
2605
- const float TWO_PI = 6.283185307179586;
2606
-
2607
4644
  varying vec2 v_uv;
2608
4645
 
2609
4646
  uniform vec2 u_center;
2610
- uniform float u_startAngle;
4647
+ uniform vec2 u_radius;
2611
4648
  uniform int u_stopCount;
2612
- uniform float u_positions[${MAX_STOPS$1}];
2613
- uniform vec4 u_colors[${MAX_STOPS$1}];
4649
+ uniform float u_positions[${MAX_STOPS}];
4650
+ uniform vec4 u_colors[${MAX_STOPS}];
4651
+ uniform float u_tMax;
2614
4652
 
2615
4653
  vec4 getGradientColor(float t) {
2616
4654
  vec4 result = u_colors[0];
2617
4655
 
2618
- for (int i = 0; i < ${MAX_STOPS$1 - 1}; i++) {
4656
+ for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
2619
4657
  if (i >= u_stopCount - 1) {
2620
4658
  break;
2621
4659
  }
@@ -2640,11 +4678,11 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
2640
4678
 
2641
4679
  void main() {
2642
4680
  vec2 delta = v_uv - u_center;
4681
+ vec2 normalized = abs(delta) / max(u_radius, vec2(0.00001));
4682
+ float t = normalized.x + normalized.y;
2643
4683
 
2644
- float angle = atan(delta.y, delta.x);
2645
- float cssAngle = mod((PI * 0.5) - angle + TWO_PI, TWO_PI);
2646
-
2647
- float t = mod(cssAngle - u_startAngle + TWO_PI, TWO_PI) / TWO_PI;
4684
+ t = clamp(t, 0.0, u_tMax);
4685
+ t = t / max(u_tMax, 0.00001);
2648
4686
 
2649
4687
  gl_FragColor = getGradientColor(t);
2650
4688
  }
@@ -2669,12 +4707,18 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
2669
4707
  const positionLocation = gl.getAttribLocation(program, "a_position");
2670
4708
  gl.enableVertexAttribArray(positionLocation);
2671
4709
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
2672
- const center = resolveConicCenter(gradient.config.position, width, height);
2673
- const limitedStops = fitStopsToWebGLLimit$1(resolveRenderableGradientStops(gradient, getWebGLSampleCount$1(gradient, MAX_STOPS$1)), MAX_STOPS$1);
2674
- const positions = new Float32Array(MAX_STOPS$1);
2675
- const colors = new Float32Array(MAX_STOPS$1 * 4);
4710
+ const center = resolveDiamondCenter(gradient.config.position, width, height);
4711
+ const radius = resolveDiamondRadii$1(gradient.config.size, gradient.config.shape, center, width, height);
4712
+ const maxVisibleT = getMaxVisibleDiamondT$1(center, radius, width, height);
4713
+ const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS));
4714
+ const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_DIAMOND_T);
4715
+ const maxT = gradient.isRepeating ? repeatMaxT : 1;
4716
+ const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
4717
+ const limitedStops = fitStopsToWebGLLimit(gradient.isRepeating ? normalizeStops(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS);
4718
+ const positions = new Float32Array(MAX_STOPS);
4719
+ const colors = new Float32Array(MAX_STOPS * 4);
2676
4720
  limitedStops.forEach((stop, index) => {
2677
- const color = toWebGLColor$1(stop.value);
4721
+ const color = toWebGLColor(stop.value);
2678
4722
  positions[index] = stop.position;
2679
4723
  colors[index * 4 + 0] = color[0];
2680
4724
  colors[index * 4 + 1] = color[1];
@@ -2682,7 +4726,8 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
2682
4726
  colors[index * 4 + 3] = color[3];
2683
4727
  });
2684
4728
  gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
2685
- gl.uniform1f(gl.getUniformLocation(program, "u_startAngle"), resolveAngleToRadians(gradient.config.from));
4729
+ gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
4730
+ gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
2686
4731
  gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
2687
4732
  gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
2688
4733
  gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
@@ -2693,20 +4738,8 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
2693
4738
  }
2694
4739
  };
2695
4740
  //#endregion
2696
- //#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
2697
- const toRgb = converter("rgb");
2698
- const MAX_STOPS = 128;
2699
- const MAX_REPEATING_RADIAL_T = 16;
2700
- function toWebGLColor(input) {
2701
- const color = toRgb(input);
2702
- if (!color) throw new Error(`Failed to convert color: ${input}`);
2703
- return [
2704
- color.r ?? 0,
2705
- color.g ?? 0,
2706
- color.b ?? 0,
2707
- color.alpha ?? 1
2708
- ];
2709
- }
4741
+ //#region src/gradient-transformer/modules/webgl/ModuleTransformerMeshGradientToWebgl.ts
4742
+ const BICUBIC_SUBDIVISIONS$1 = 24;
2710
4743
  function createShader(gl, type, source) {
2711
4744
  const shader = gl.createShader(type);
2712
4745
  if (!shader) throw new Error("Failed to create WebGL shader.");
@@ -2734,139 +4767,503 @@ function createProgram(gl, vertexSource, fragmentSource) {
2734
4767
  }
2735
4768
  return program;
2736
4769
  }
2737
- function getColorStopCount(stops) {
2738
- return stops.filter((stop) => stop.type === "color-stop").length;
4770
+ function toClipX(value, width) {
4771
+ return value / width * 2 - 1;
4772
+ }
4773
+ function toClipY(value, height) {
4774
+ return 1 - value / height * 2;
4775
+ }
4776
+ var ModuleTransformerMeshGradientToCanvasWebGL = class {
4777
+ target = "canvas-webgl";
4778
+ gradientType = "mesh-gradient";
4779
+ to(input) {
4780
+ if (!(input instanceof MeshGradient)) throw new Error("Expected MeshGradient");
4781
+ const gradient = input;
4782
+ return { draw: (canvas, width, height) => {
4783
+ const gl = canvas.getContext("webgl");
4784
+ if (!gl) throw new Error("WebGL is not supported.");
4785
+ canvas.width = width;
4786
+ canvas.height = height;
4787
+ gl.viewport(0, 0, width, height);
4788
+ const program = createProgram(gl, `
4789
+ attribute vec2 a_position;
4790
+ attribute vec4 a_color;
4791
+ varying vec4 v_color;
4792
+
4793
+ void main() {
4794
+ v_color = a_color;
4795
+ gl_Position = vec4(a_position, 0.0, 1.0);
4796
+ }
4797
+ `, `
4798
+ precision mediump float;
4799
+ varying vec4 v_color;
4800
+
4801
+ void main() {
4802
+ gl_FragColor = v_color;
4803
+ }
4804
+ `);
4805
+ const vertexMap = buildMeshVertexMap(gradient, width, height);
4806
+ const grid = buildRegularMeshGrid(gradient, vertexMap);
4807
+ const subdivisions = gradient.config.method === "bicubic" ? BICUBIC_SUBDIVISIONS$1 : 1;
4808
+ const edgeTriangles = buildMeshEdgeSkirtTriangles(gradient, grid, width, height, subdivisions);
4809
+ const positions = [];
4810
+ const colors = [];
4811
+ for (const patch of gradient.patches) {
4812
+ const triangles = buildPatchTriangles(gradient, grid, patch, vertexMap, subdivisions);
4813
+ for (const triangle of triangles) for (const vertex of triangle) {
4814
+ positions.push(toClipX(vertex.x, width), toClipY(vertex.y, height));
4815
+ colors.push(...vertex.color);
4816
+ }
4817
+ }
4818
+ for (const triangle of edgeTriangles) for (const vertex of triangle) {
4819
+ positions.push(toClipX(vertex.x, width), toClipY(vertex.y, height));
4820
+ colors.push(...vertex.color);
4821
+ }
4822
+ const positionBuffer = gl.createBuffer();
4823
+ const colorBuffer = gl.createBuffer();
4824
+ gl.useProgram(program);
4825
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
4826
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
4827
+ const positionLocation = gl.getAttribLocation(program, "a_position");
4828
+ gl.enableVertexAttribArray(positionLocation);
4829
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
4830
+ gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
4831
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
4832
+ const colorLocation = gl.getAttribLocation(program, "a_color");
4833
+ gl.enableVertexAttribArray(colorLocation);
4834
+ gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
4835
+ gl.clearColor(0, 0, 0, 0);
4836
+ gl.clear(gl.COLOR_BUFFER_BIT);
4837
+ gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2);
4838
+ } };
4839
+ }
4840
+ };
4841
+ const toRgb = converter("rgb");
4842
+ function escapeXml(value) {
4843
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
4844
+ }
4845
+ function clamp01(value) {
4846
+ return Math.min(1, Math.max(0, value));
4847
+ }
4848
+ function toPercent$1(value) {
4849
+ return `${Number((value * 100).toFixed(3))}%`;
4850
+ }
4851
+ function formatPoint(value) {
4852
+ return `${Number(value.toFixed(3))}%`;
4853
+ }
4854
+ function normalizeSvgStops(stops) {
4855
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
4856
+ }
4857
+ function parseSvgColor(input) {
4858
+ const color = toRgb(input);
4859
+ if (!color) throw new Error(`Failed to convert color: ${input}`);
4860
+ return {
4861
+ r: Math.round((color.r ?? 0) * 255),
4862
+ g: Math.round((color.g ?? 0) * 255),
4863
+ b: Math.round((color.b ?? 0) * 255),
4864
+ a: Math.round((color.alpha ?? 1) * 255)
4865
+ };
4866
+ }
4867
+ function formatSvgColor(color) {
4868
+ if (color.a >= 255) return `rgb(${color.r} ${color.g} ${color.b})`;
4869
+ return `rgba(${color.r}, ${color.g}, ${color.b}, ${Number((color.a / 255).toFixed(4))})`;
4870
+ }
4871
+ function mixSvgColor(from, to, t) {
4872
+ return {
4873
+ r: Math.round(from.r + (to.r - from.r) * t),
4874
+ g: Math.round(from.g + (to.g - from.g) * t),
4875
+ b: Math.round(from.b + (to.b - from.b) * t),
4876
+ a: Math.round(from.a + (to.a - from.a) * t)
4877
+ };
4878
+ }
4879
+ function sampleSvgStops(stops, position) {
4880
+ const colorStops = normalizeSvgStops(stops);
4881
+ if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
4882
+ if (colorStops.length === 1) return parseSvgColor(colorStops[0].value);
4883
+ const first = colorStops[0];
4884
+ const extendedStops = [...colorStops, {
4885
+ ...first,
4886
+ position: first.position + 1
4887
+ }];
4888
+ let samplePosition = position;
4889
+ if (samplePosition < first.position) samplePosition += 1;
4890
+ for (let index = 0; index < extendedStops.length - 1; index += 1) {
4891
+ const current = extendedStops[index];
4892
+ const next = extendedStops[index + 1];
4893
+ if (samplePosition >= current.position && samplePosition <= next.position) {
4894
+ const span = next.position - current.position || 1;
4895
+ const localT = (samplePosition - current.position) / span;
4896
+ return mixSvgColor(parseSvgColor(current.value), parseSvgColor(next.value), localT);
4897
+ }
4898
+ }
4899
+ return parseSvgColor(colorStops[colorStops.length - 1].value);
4900
+ }
4901
+ function encodeSvgDataUrl(svg) {
4902
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
4903
+ }
4904
+ function buildSvgStops(gradient) {
4905
+ return normalizeSvgStops(resolveRenderableGradientStops(gradient, 128)).map((stop) => `<stop offset="${toPercent$1(clamp01(stop.position))}" stop-color="${escapeXml(stop.value)}"/>`).join("");
4906
+ }
4907
+ function buildSvgGradientResult(input) {
4908
+ const defs = `<defs>${input.gradient}</defs>`;
4909
+ return {
4910
+ id: input.id,
4911
+ href: `#${input.id}`,
4912
+ url: `url(#${input.id})`,
4913
+ type: input.type,
4914
+ gradient: input.gradient,
4915
+ defs,
4916
+ svg: `<svg xmlns="http://www.w3.org/2000/svg">${defs}</svg>`
4917
+ };
4918
+ }
4919
+ //#endregion
4920
+ //#region src/gradient-transformer/modules/svg/ModuleTransformerLinearGradientToSvg.ts
4921
+ const DEFAULT_ID$4 = "gradiente-linear-gradient";
4922
+ function getLinearVector(angle) {
4923
+ const dx = Math.sin(angle);
4924
+ const dy = -Math.cos(angle);
4925
+ const scale = Math.max(Math.abs(dx), Math.abs(dy), 1e-4);
4926
+ const unitX = dx / scale;
4927
+ const unitY = dy / scale;
4928
+ return {
4929
+ x1: 50 - unitX * 50,
4930
+ y1: 50 - unitY * 50,
4931
+ x2: 50 + unitX * 50,
4932
+ y2: 50 + unitY * 50
4933
+ };
4934
+ }
4935
+ var ModuleTransformerLinearGradientToSvg = class {
4936
+ target = "svg";
4937
+ gradientType = "linear-gradient";
4938
+ to(input) {
4939
+ if (!(input instanceof LinearGradient)) throw new Error("Expected LinearGradient");
4940
+ const id = DEFAULT_ID$4;
4941
+ const vector = getLinearVector(input.config.angle);
4942
+ const stopsSvg = buildSvgStops(input);
4943
+ return buildSvgGradientResult({
4944
+ id,
4945
+ type: "linearGradient",
4946
+ gradient: [
4947
+ `<linearGradient id="${id}" gradientUnits="objectBoundingBox" x1="${formatPoint(vector.x1)}" y1="${formatPoint(vector.y1)}" x2="${formatPoint(vector.x2)}" y2="${formatPoint(vector.y2)}">`,
4948
+ stopsSvg,
4949
+ "</linearGradient>"
4950
+ ].join("")
4951
+ });
4952
+ }
4953
+ };
4954
+ //#endregion
4955
+ //#region src/gradient-transformer/modules/svg/ModuleTransformerRadialGradientToSvg.ts
4956
+ const DEFAULT_ID$3 = "gradiente-radial-gradient";
4957
+ function resolveKeywordX$2(value) {
4958
+ if (value === "left") return 0;
4959
+ if (value === "right") return 100;
4960
+ return 50;
4961
+ }
4962
+ function resolveKeywordY$2(value) {
4963
+ if (value === "top") return 0;
4964
+ if (value === "bottom") return 100;
4965
+ return 50;
4966
+ }
4967
+ function resolveLengthPercentage$2(value) {
4968
+ if (value.kind === "percent") return value.value;
4969
+ if (value.unit === "px") return value.value;
4970
+ return value.value;
4971
+ }
4972
+ function resolveCenter$2(position) {
4973
+ if (position.kind === "keywords") return {
4974
+ x: resolveKeywordX$2(position.x),
4975
+ y: resolveKeywordY$2(position.y)
4976
+ };
4977
+ return {
4978
+ x: resolveLengthPercentage$2(position.x),
4979
+ y: resolveLengthPercentage$2(position.y)
4980
+ };
4981
+ }
4982
+ function getDistanceToCorner$1(center, corner) {
4983
+ const dx = corner.x - center.x;
4984
+ const dy = corner.y - center.y;
4985
+ return Math.sqrt(dx * dx + dy * dy);
4986
+ }
4987
+ function resolveRadii(size, shape, center) {
4988
+ if (size.kind === "explicit") {
4989
+ const radiusX = resolveLengthPercentage$2(size.x);
4990
+ const radiusY = size.y ? resolveLengthPercentage$2(size.y) : radiusX;
4991
+ return {
4992
+ x: Math.max(radiusX, 1e-4),
4993
+ y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
4994
+ };
4995
+ }
4996
+ const left = center.x;
4997
+ const right = 100 - center.x;
4998
+ const top = center.y;
4999
+ const bottom = 100 - center.y;
5000
+ if (shape === "circle") {
5001
+ const cornerDistances = [
5002
+ {
5003
+ x: 0,
5004
+ y: 0
5005
+ },
5006
+ {
5007
+ x: 100,
5008
+ y: 0
5009
+ },
5010
+ {
5011
+ x: 0,
5012
+ y: 100
5013
+ },
5014
+ {
5015
+ x: 100,
5016
+ y: 100
5017
+ }
5018
+ ].map((corner) => getDistanceToCorner$1(center, corner));
5019
+ if (size.value === "closest-side") {
5020
+ const radius = Math.min(left, right, top, bottom);
5021
+ return {
5022
+ x: radius,
5023
+ y: radius
5024
+ };
5025
+ }
5026
+ if (size.value === "farthest-side") {
5027
+ const radius = Math.max(left, right, top, bottom);
5028
+ return {
5029
+ x: radius,
5030
+ y: radius
5031
+ };
5032
+ }
5033
+ if (size.value === "closest-corner") {
5034
+ const radius = Math.min(...cornerDistances);
5035
+ return {
5036
+ x: radius,
5037
+ y: radius
5038
+ };
5039
+ }
5040
+ const radius = Math.max(...cornerDistances);
5041
+ return {
5042
+ x: radius,
5043
+ y: radius
5044
+ };
5045
+ }
5046
+ if (size.value === "closest-side") return {
5047
+ x: Math.min(left, right),
5048
+ y: Math.min(top, bottom)
5049
+ };
5050
+ if (size.value === "farthest-side") return {
5051
+ x: Math.max(left, right),
5052
+ y: Math.max(top, bottom)
5053
+ };
5054
+ return {
5055
+ x: Math.max(left, right),
5056
+ y: Math.max(top, bottom)
5057
+ };
5058
+ }
5059
+ var ModuleTransformerRadialGradientToSvg = class {
5060
+ target = "svg";
5061
+ gradientType = "radial-gradient";
5062
+ to(input) {
5063
+ if (!(input instanceof RadialGradient)) throw new Error("Expected RadialGradient");
5064
+ const id = DEFAULT_ID$3;
5065
+ const center = resolveCenter$2(input.config.position);
5066
+ const radii = resolveRadii(input.config.size, input.config.shape, center);
5067
+ const radius = Math.max(radii.x, radii.y);
5068
+ const scaleX = radii.x / radius;
5069
+ const scaleY = radii.y / radius;
5070
+ const transform = input.config.shape === "ellipse" ? ` gradientTransform="translate(${center.x} ${center.y}) scale(${scaleX} ${scaleY}) translate(${-center.x} ${-center.y})"` : "";
5071
+ return buildSvgGradientResult({
5072
+ id,
5073
+ type: "radialGradient",
5074
+ gradient: [
5075
+ `<radialGradient id="${id}" gradientUnits="objectBoundingBox" cx="${formatPoint(center.x)}" cy="${formatPoint(center.y)}" r="${formatPoint(radius)}"${transform}>`,
5076
+ buildSvgStops(input),
5077
+ "</radialGradient>"
5078
+ ].join("")
5079
+ });
5080
+ }
5081
+ };
5082
+ //#endregion
5083
+ //#region src/gradient-transformer/modules/svg/ModuleTransformerConicGradientToSvg.ts
5084
+ const DEFAULT_ID$2 = "gradiente-conic-gradient";
5085
+ const TWO_PI = Math.PI * 2;
5086
+ const VIEW_BOX_SIZE$1 = 100;
5087
+ const CONIC_SEGMENT_COUNT = 720;
5088
+ function resolveKeywordX$1(value, size) {
5089
+ if (value === "left") return 0;
5090
+ if (value === "right") return size;
5091
+ return size / 2;
5092
+ }
5093
+ function resolveKeywordY$1(value, size) {
5094
+ if (value === "top") return 0;
5095
+ if (value === "bottom") return size;
5096
+ return size / 2;
5097
+ }
5098
+ function resolveLengthPercentage$1(value, size) {
5099
+ if (value.kind === "percent") return value.value / 100 * size;
5100
+ if (value.unit === "px") return value.value;
5101
+ return value.value;
5102
+ }
5103
+ function resolveCenter$1(position, size) {
5104
+ if (position.kind === "keywords") return {
5105
+ x: resolveKeywordX$1(position.x, size),
5106
+ y: resolveKeywordY$1(position.y, size)
5107
+ };
5108
+ return {
5109
+ x: resolveLengthPercentage$1(position.x, size),
5110
+ y: resolveLengthPercentage$1(position.y, size)
5111
+ };
5112
+ }
5113
+ function resolveAngleToRadians(angle) {
5114
+ if (angle.unit === "deg") return degToRad(angle.value);
5115
+ if (angle.unit === "turn") return turnToRad(angle.value);
5116
+ if (angle.unit === "grad") return gradToRad(angle.value);
5117
+ return angle.value;
5118
+ }
5119
+ function getDistance(from, to) {
5120
+ const dx = to.x - from.x;
5121
+ const dy = to.y - from.y;
5122
+ return Math.sqrt(dx * dx + dy * dy);
5123
+ }
5124
+ function getCoverRadius(center) {
5125
+ return Math.max(...[
5126
+ {
5127
+ x: 0,
5128
+ y: 0
5129
+ },
5130
+ {
5131
+ x: VIEW_BOX_SIZE$1,
5132
+ y: 0
5133
+ },
5134
+ {
5135
+ x: 0,
5136
+ y: VIEW_BOX_SIZE$1
5137
+ },
5138
+ {
5139
+ x: VIEW_BOX_SIZE$1,
5140
+ y: VIEW_BOX_SIZE$1
5141
+ }
5142
+ ].map((corner) => getDistance(center, corner))) * 1.02;
5143
+ }
5144
+ function pointOnConicRay(center, radius, angle) {
5145
+ return {
5146
+ x: center.x + Math.sin(angle) * radius,
5147
+ y: center.y - Math.cos(angle) * radius
5148
+ };
2739
5149
  }
2740
- function getWebGLSampleCount(gradient, maxStops) {
2741
- const colorStopCount = getColorStopCount(gradient.stops);
2742
- const segmentCount = Math.max(1, colorStopCount - 1);
2743
- return Math.max(2, Math.floor((maxStops - 1) / segmentCount));
5150
+ function formatNumber$2(value) {
5151
+ return `${Number(value.toFixed(3))}`;
2744
5152
  }
2745
- function getColorAtPosition(stops, position) {
2746
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2747
- if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
2748
- if (position <= colorStops[0].position) return colorStops[0].value;
2749
- const lastStop = colorStops[colorStops.length - 1];
2750
- if (position >= lastStop.position) return lastStop.value;
2751
- for (let index = 0; index < colorStops.length - 1; index += 1) {
2752
- const current = colorStops[index];
2753
- const next = colorStops[index + 1];
2754
- if (position >= current.position && position <= next.position) {
2755
- const range = next.position - current.position || 1;
2756
- const localT = (position - current.position) / range;
2757
- return formatRgb(interpolate([current.value, next.value], "rgb")(localT));
5153
+ var ModuleTransformerConicGradientToSvg = class {
5154
+ target = "svg";
5155
+ gradientType = "conic-gradient";
5156
+ to(input) {
5157
+ if (!(input instanceof ConicGradient)) throw new Error("Expected ConicGradient");
5158
+ const id = DEFAULT_ID$2;
5159
+ const center = resolveCenter$1(input.config.position, VIEW_BOX_SIZE$1);
5160
+ const from = resolveAngleToRadians(input.config.from);
5161
+ const stops = resolveRenderableGradientStops(input, 128);
5162
+ const radius = getCoverRadius(center);
5163
+ const paths = [];
5164
+ for (let index = 0; index < CONIC_SEGMENT_COUNT; index += 1) {
5165
+ const startT = index / CONIC_SEGMENT_COUNT;
5166
+ const endT = (index + 1) / CONIC_SEGMENT_COUNT;
5167
+ const color = formatSvgColor(sampleSvgStops(stops, startT + (endT - startT) / 2));
5168
+ const start = pointOnConicRay(center, radius, startT * TWO_PI + from);
5169
+ const end = pointOnConicRay(center, radius, endT * TWO_PI + from);
5170
+ paths.push([
5171
+ `<path d="M ${formatNumber$2(center.x)} ${formatNumber$2(center.y)}`,
5172
+ `L ${formatNumber$2(start.x)} ${formatNumber$2(start.y)}`,
5173
+ `A ${formatNumber$2(radius)} ${formatNumber$2(radius)} 0 0 1 ${formatNumber$2(end.x)} ${formatNumber$2(end.y)}`,
5174
+ `Z" fill="${color}" stroke="${color}" stroke-width="0.25"/>`
5175
+ ].join(" "));
2758
5176
  }
2759
- }
2760
- return lastStop.value;
2761
- }
2762
- function fitStopsToWebGLLimit(stops, maxStops) {
2763
- const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
2764
- if (colorStops.length <= maxStops) return colorStops;
2765
- const sampledStops = [];
2766
- for (let index = 0; index < maxStops; index += 1) {
2767
- const position = index / (maxStops - 1);
2768
- sampledStops.push({
2769
- type: "color-stop",
2770
- value: getColorAtPosition(colorStops, position),
2771
- position
5177
+ const vectorSvg = [
5178
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${VIEW_BOX_SIZE$1} ${VIEW_BOX_SIZE$1}" width="${VIEW_BOX_SIZE$1}" height="${VIEW_BOX_SIZE$1}">`,
5179
+ ...paths,
5180
+ "</svg>"
5181
+ ].join("");
5182
+ return buildSvgGradientResult({
5183
+ id,
5184
+ type: "pattern",
5185
+ gradient: [
5186
+ `<pattern id="${id}" patternUnits="objectBoundingBox" width="1" height="1" viewBox="0 0 ${VIEW_BOX_SIZE$1} ${VIEW_BOX_SIZE$1}" preserveAspectRatio="none">`,
5187
+ `<image width="${VIEW_BOX_SIZE$1}" height="${VIEW_BOX_SIZE$1}" href="${escapeXml(encodeSvgDataUrl(vectorSvg))}"/>`,
5188
+ "</pattern>"
5189
+ ].join("")
2772
5190
  });
2773
5191
  }
2774
- return sampledStops;
2775
- }
2776
- function parseLengthPercentage(value, reference) {
2777
- if (value.kind === "percent") return value.value / 100 * reference;
2778
- if (value.kind === "length") {
2779
- if (value.unit === "px") return value.value;
2780
- throw new Error(`Unsupported gradient length unit for WebGL radial gradient: ${value.unit}`);
2781
- }
2782
- return value;
5192
+ };
5193
+ //#endregion
5194
+ //#region src/gradient-transformer/modules/svg/ModuleTransformerDiamondGradientToSvg.ts
5195
+ const DEFAULT_ID$1 = "gradiente-diamond-gradient";
5196
+ const VIEW_BOX_SIZE = 100;
5197
+ function resolveKeywordX(value) {
5198
+ if (value === "left") return 0;
5199
+ if (value === "right") return VIEW_BOX_SIZE;
5200
+ return VIEW_BOX_SIZE / 2;
2783
5201
  }
2784
- function resolveKeywordPositionX(x, width) {
2785
- if (x === "left") return 0;
2786
- if (x === "center") return width / 2;
2787
- if (x === "right") return width;
2788
- return width / 2;
5202
+ function resolveKeywordY(value) {
5203
+ if (value === "top") return 0;
5204
+ if (value === "bottom") return VIEW_BOX_SIZE;
5205
+ return VIEW_BOX_SIZE / 2;
2789
5206
  }
2790
- function resolveKeywordPositionY(y, height) {
2791
- if (y === "top") return 0;
2792
- if (y === "center") return height / 2;
2793
- if (y === "bottom") return height;
2794
- return height / 2;
5207
+ function resolveLengthPercentage(value) {
5208
+ if (value.kind === "percent") return value.value / 100 * VIEW_BOX_SIZE;
5209
+ if (value.unit === "px") return value.value;
5210
+ throw new Error(`Unsupported diamond-gradient length unit for SVG transformer: ${value.unit}`);
2795
5211
  }
2796
- function resolveRadialCenter(position, width, height) {
5212
+ function resolveCenter(position) {
2797
5213
  if (position.kind === "keywords") return {
2798
- x: resolveKeywordPositionX(position.x, width),
2799
- y: resolveKeywordPositionY(position.y, height)
2800
- };
2801
- if (position.kind === "values") return {
2802
- x: parseLengthPercentage(position.x, width),
2803
- y: parseLengthPercentage(position.y, height)
5214
+ x: resolveKeywordX(position.x),
5215
+ y: resolveKeywordY(position.y)
2804
5216
  };
2805
5217
  return {
2806
- x: width / 2,
2807
- y: height / 2
5218
+ x: resolveLengthPercentage(position.x),
5219
+ y: resolveLengthPercentage(position.y)
2808
5220
  };
2809
5221
  }
2810
- function getDistanceToSide(center, width, height, side) {
2811
- if (side === "left") return center.x;
2812
- if (side === "right") return width - center.x;
2813
- if (side === "top") return center.y;
2814
- return height - center.y;
2815
- }
2816
5222
  function getDistanceToCorner(center, corner) {
2817
- const dx = corner.x - center.x;
2818
- const dy = corner.y - center.y;
2819
- return Math.sqrt(dx * dx + dy * dy);
2820
- }
2821
- function normalizeStops(stops, min, max) {
2822
- const range = max - min || 1;
2823
- return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).map((stop) => ({
2824
- ...stop,
2825
- position: (stop.position - min) / range
2826
- }));
5223
+ return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
2827
5224
  }
2828
- function getCornerDeltas(center, width, height) {
5225
+ function getCornerDeltas(center) {
2829
5226
  return [
2830
5227
  {
2831
5228
  dx: -center.x,
2832
5229
  dy: -center.y
2833
5230
  },
2834
5231
  {
2835
- dx: width - center.x,
5232
+ dx: VIEW_BOX_SIZE - center.x,
2836
5233
  dy: -center.y
2837
5234
  },
2838
5235
  {
2839
5236
  dx: -center.x,
2840
- dy: height - center.y
5237
+ dy: VIEW_BOX_SIZE - center.y
2841
5238
  },
2842
5239
  {
2843
- dx: width - center.x,
2844
- dy: height - center.y
5240
+ dx: VIEW_BOX_SIZE - center.x,
5241
+ dy: VIEW_BOX_SIZE - center.y
2845
5242
  }
2846
5243
  ];
2847
5244
  }
2848
- function scaleEllipseRadiiToCorner(radiusX, radiusY, dx, dy) {
5245
+ function scaleDiamondRadiiToCorner(radiusX, radiusY, dx, dy) {
2849
5246
  const safeRadiusX = Math.max(radiusX, 1e-4);
2850
5247
  const safeRadiusY = Math.max(radiusY, 1e-4);
2851
- const scale = Math.sqrt(dx * dx / (safeRadiusX * safeRadiusX) + dy * dy / (safeRadiusY * safeRadiusY));
5248
+ const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
2852
5249
  return {
2853
5250
  x: safeRadiusX * scale,
2854
5251
  y: safeRadiusY * scale
2855
5252
  };
2856
5253
  }
2857
- function resolveRadialRadii(size, shape, center, width, height) {
5254
+ function resolveDiamondRadii(size, shape, center) {
2858
5255
  if (size.kind === "explicit") {
2859
- const radiusX = parseLengthPercentage(size.x, width);
2860
- const radiusY = size.y ? parseLengthPercentage(size.y, height) : radiusX;
5256
+ const radiusX = resolveLengthPercentage(size.x);
5257
+ const radiusY = size.y ? resolveLengthPercentage(size.y) : radiusX;
2861
5258
  return {
2862
5259
  x: Math.max(radiusX, 1e-4),
2863
5260
  y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
2864
5261
  };
2865
5262
  }
2866
- const left = getDistanceToSide(center, width, height, "left");
2867
- const right = getDistanceToSide(center, width, height, "right");
2868
- const top = getDistanceToSide(center, width, height, "top");
2869
- const bottom = getDistanceToSide(center, width, height, "bottom");
5263
+ const left = center.x;
5264
+ const right = VIEW_BOX_SIZE - center.x;
5265
+ const top = center.y;
5266
+ const bottom = VIEW_BOX_SIZE - center.y;
2870
5267
  if (shape === "circle") {
2871
5268
  const cornerDistances = [
2872
5269
  {
@@ -2874,16 +5271,16 @@ function resolveRadialRadii(size, shape, center, width, height) {
2874
5271
  y: 0
2875
5272
  },
2876
5273
  {
2877
- x: width,
5274
+ x: VIEW_BOX_SIZE,
2878
5275
  y: 0
2879
5276
  },
2880
5277
  {
2881
5278
  x: 0,
2882
- y: height
5279
+ y: VIEW_BOX_SIZE
2883
5280
  },
2884
5281
  {
2885
- x: width,
2886
- y: height
5282
+ x: VIEW_BOX_SIZE,
5283
+ y: VIEW_BOX_SIZE
2887
5284
  }
2888
5285
  ].map((corner) => getDistanceToCorner(center, corner));
2889
5286
  if (size.value === "closest-side") {
@@ -2925,132 +5322,139 @@ function resolveRadialRadii(size, shape, center, width, height) {
2925
5322
  x: Math.max(farthestSideRadiusX, 1e-4),
2926
5323
  y: Math.max(farthestSideRadiusY, 1e-4)
2927
5324
  };
2928
- const corners = getCornerDeltas(center, width, height);
2929
- if (size.value === "closest-corner") return corners.map((corner) => scaleEllipseRadiiToCorner(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => {
2930
- const closestArea = closest.x * closest.y;
2931
- return current.x * current.y < closestArea ? current : closest;
2932
- });
2933
- return corners.map((corner) => scaleEllipseRadiiToCorner(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => {
2934
- const farthestArea = farthest.x * farthest.y;
2935
- return current.x * current.y > farthestArea ? current : farthest;
2936
- });
5325
+ const corners = getCornerDeltas(center);
5326
+ if (size.value === "closest-corner") return corners.map((corner) => scaleDiamondRadiiToCorner(closestSideRadiusX, closestSideRadiusY, corner.dx, corner.dy)).reduce((closest, current) => current.x * current.y < closest.x * closest.y ? current : closest);
5327
+ return corners.map((corner) => scaleDiamondRadiiToCorner(farthestSideRadiusX, farthestSideRadiusY, corner.dx, corner.dy)).reduce((farthest, current) => current.x * current.y > farthest.x * farthest.y ? current : farthest);
2937
5328
  }
2938
- var ModuleTransformerRadialGradientToCanvasWebGL = class {
2939
- target = "canvas-webgl";
2940
- gradientType = "radial-gradient";
5329
+ function getMaxVisibleDiamondT(center, radii) {
5330
+ return Math.max(...[
5331
+ {
5332
+ x: 0,
5333
+ y: 0
5334
+ },
5335
+ {
5336
+ x: VIEW_BOX_SIZE,
5337
+ y: 0
5338
+ },
5339
+ {
5340
+ x: 0,
5341
+ y: VIEW_BOX_SIZE
5342
+ },
5343
+ {
5344
+ x: VIEW_BOX_SIZE,
5345
+ y: VIEW_BOX_SIZE
5346
+ }
5347
+ ].map((corner) => Math.abs(corner.x - center.x) / Math.max(radii.x, 1e-4) + Math.abs(corner.y - center.y) / Math.max(radii.y, 1e-4)));
5348
+ }
5349
+ function getColorStops(stops) {
5350
+ return stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
5351
+ }
5352
+ function formatNumber$1(value) {
5353
+ return `${Number(value.toFixed(3))}`;
5354
+ }
5355
+ function buildDiamondPolygon(center, radii, position) {
5356
+ const x = radii.x * position;
5357
+ const y = radii.y * position;
5358
+ return [
5359
+ `${formatNumber$1(center.x)} ${formatNumber$1(center.y - y)}`,
5360
+ `${formatNumber$1(center.x + x)} ${formatNumber$1(center.y)}`,
5361
+ `${formatNumber$1(center.x)} ${formatNumber$1(center.y + y)}`,
5362
+ `${formatNumber$1(center.x - x)} ${formatNumber$1(center.y)}`
5363
+ ].join(" ");
5364
+ }
5365
+ var ModuleTransformerDiamondGradientToSvg = class {
5366
+ target = "svg";
5367
+ gradientType = "diamond-gradient";
2941
5368
  to(input) {
2942
- const gradient = input;
2943
- return { draw: (canvas, width, height) => {
2944
- const gl = canvas.getContext("webgl");
2945
- if (!gl) throw new Error("WebGL is not supported.");
2946
- canvas.width = width;
2947
- canvas.height = height;
2948
- gl.viewport(0, 0, width, height);
2949
- const program = createProgram(gl, `
2950
- attribute vec2 a_position;
2951
- varying vec2 v_uv;
2952
-
2953
- void main() {
2954
- v_uv = a_position * 0.5 + 0.5;
2955
- gl_Position = vec4(a_position, 0.0, 1.0);
2956
- }
2957
- `, `
2958
- precision mediump float;
2959
-
2960
- varying vec2 v_uv;
2961
-
2962
- uniform vec2 u_center;
2963
- uniform vec2 u_radius;
2964
- uniform int u_stopCount;
2965
- uniform float u_positions[${MAX_STOPS}];
2966
- uniform vec4 u_colors[${MAX_STOPS}];
2967
- uniform float u_tMax;
2968
-
2969
- vec4 getGradientColor(float t) {
2970
- vec4 result = u_colors[0];
2971
-
2972
- for (int i = 0; i < ${MAX_STOPS - 1}; i++) {
2973
- if (i >= u_stopCount - 1) {
2974
- break;
2975
- }
2976
-
2977
- float currentPosition = u_positions[i];
2978
- float nextPosition = u_positions[i + 1];
2979
-
2980
- if (t <= currentPosition) {
2981
- return u_colors[i];
2982
- }
2983
-
2984
- if (t >= currentPosition && t <= nextPosition) {
2985
- float localT = (t - currentPosition) / max(nextPosition - currentPosition, 0.00001);
2986
- return mix(u_colors[i], u_colors[i + 1], localT);
2987
- }
2988
-
2989
- result = u_colors[i + 1];
2990
- }
2991
-
2992
- return result;
2993
- }
2994
-
2995
- void main() {
2996
- vec2 delta = v_uv - u_center;
2997
- vec2 normalized = delta / max(u_radius, vec2(0.00001));
2998
- float t = length(normalized);
2999
-
3000
- t = clamp(t, 0.0, u_tMax);
3001
- t = t / max(u_tMax, 0.00001);
3002
-
3003
- gl_FragColor = getGradientColor(t);
3004
- }
3005
- `);
3006
- gl.useProgram(program);
3007
- const positionBuffer = gl.createBuffer();
3008
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
3009
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
3010
- -1,
3011
- -1,
3012
- 1,
3013
- -1,
3014
- -1,
3015
- 1,
3016
- -1,
3017
- 1,
3018
- 1,
3019
- -1,
3020
- 1,
3021
- 1
3022
- ]), gl.STATIC_DRAW);
3023
- const positionLocation = gl.getAttribLocation(program, "a_position");
3024
- gl.enableVertexAttribArray(positionLocation);
3025
- gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
3026
- const center = resolveRadialCenter(gradient.config.position, width, height);
3027
- const radius = resolveRadialRadii(gradient.config.size, gradient.config.shape, center, width, height);
3028
- const maxVisibleT = getMaxVisibleRadialT(center, radius, width, height);
3029
- const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS));
3030
- const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_RADIAL_T);
3031
- const maxT = gradient.isRepeating ? repeatMaxT : 1;
3032
- const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
3033
- const limitedStops = fitStopsToWebGLLimit(gradient.isRepeating ? normalizeStops(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS);
3034
- const positions = new Float32Array(MAX_STOPS);
3035
- const colors = new Float32Array(MAX_STOPS * 4);
3036
- limitedStops.forEach((stop, index) => {
3037
- const color = toWebGLColor(stop.value);
3038
- positions[index] = stop.position;
3039
- colors[index * 4 + 0] = color[0];
3040
- colors[index * 4 + 1] = color[1];
3041
- colors[index * 4 + 2] = color[2];
3042
- colors[index * 4 + 3] = color[3];
3043
- });
3044
- gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
3045
- gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
3046
- gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
3047
- gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
3048
- gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
3049
- gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
3050
- gl.clearColor(0, 0, 0, 0);
3051
- gl.clear(gl.COLOR_BUFFER_BIT);
3052
- gl.drawArrays(gl.TRIANGLES, 0, 6);
3053
- } };
5369
+ if (!(input instanceof DiamondGradient)) throw new Error("Expected DiamondGradient");
5370
+ const id = DEFAULT_ID$1;
5371
+ const center = resolveCenter(input.config.position);
5372
+ const radii = resolveDiamondRadii(input.config.size, input.config.shape, center);
5373
+ const maxVisibleT = getMaxVisibleDiamondT(center, radii);
5374
+ const maxT = input.isRepeating ? maxVisibleT : 1;
5375
+ const baseStops = resolveRenderableGradientStops(input, 128);
5376
+ const stops = getColorStops(input.isRepeating ? expandRepeatingStopsTo(baseStops, 0, maxVisibleT) : baseStops);
5377
+ const outerColor = formatSvgColor(sampleSvgStops(stops, maxT));
5378
+ const sampleCount = Math.max(128, Math.ceil(128 * maxT));
5379
+ const polygons = [];
5380
+ for (let index = sampleCount; index >= 0; index -= 1) {
5381
+ const position = index / sampleCount * maxT;
5382
+ const color = formatSvgColor(sampleSvgStops(stops, position));
5383
+ const points = buildDiamondPolygon(center, radii, position);
5384
+ polygons.push(`<polygon points="${points}" fill="${color}"/>`);
5385
+ }
5386
+ const vectorSvg = [
5387
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${VIEW_BOX_SIZE} ${VIEW_BOX_SIZE}" width="${VIEW_BOX_SIZE}" height="${VIEW_BOX_SIZE}" preserveAspectRatio="none">`,
5388
+ `<rect width="${VIEW_BOX_SIZE}" height="${VIEW_BOX_SIZE}" fill="${outerColor}"/>`,
5389
+ ...polygons,
5390
+ "</svg>"
5391
+ ].join("");
5392
+ return buildSvgGradientResult({
5393
+ id,
5394
+ type: "pattern",
5395
+ gradient: [
5396
+ `<pattern id="${id}" patternUnits="objectBoundingBox" width="1" height="1" viewBox="0 0 ${VIEW_BOX_SIZE} ${VIEW_BOX_SIZE}" preserveAspectRatio="none">`,
5397
+ `<image width="${VIEW_BOX_SIZE}" height="${VIEW_BOX_SIZE}" href="${escapeXml(encodeSvgDataUrl(vectorSvg))}"/>`,
5398
+ "</pattern>"
5399
+ ].join("")
5400
+ });
5401
+ }
5402
+ };
5403
+ //#endregion
5404
+ //#region src/gradient-transformer/modules/svg/ModuleTransformerMeshGradientToSvg.ts
5405
+ const DEFAULT_ID = "gradiente-mesh-gradient";
5406
+ const MESH_VIEW_BOX_SIZE = 100;
5407
+ const BILINEAR_SUBDIVISIONS = 32;
5408
+ const BICUBIC_SUBDIVISIONS = 40;
5409
+ function formatRgba(color) {
5410
+ return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${Number(color[3].toFixed(4))})`;
5411
+ }
5412
+ function getTriangleColor(a, b, c) {
5413
+ return [
5414
+ Math.round((a.color[0] + b.color[0] + c.color[0]) / 3 * 255),
5415
+ Math.round((a.color[1] + b.color[1] + c.color[1]) / 3 * 255),
5416
+ Math.round((a.color[2] + b.color[2] + c.color[2]) / 3 * 255),
5417
+ (a.color[3] + b.color[3] + c.color[3]) / 3
5418
+ ];
5419
+ }
5420
+ function formatNumber(value) {
5421
+ return `${Number(value.toFixed(3))}`;
5422
+ }
5423
+ function triangleToPolygon(triangle) {
5424
+ const [a, b, c] = triangle;
5425
+ const color = formatRgba(getTriangleColor(a, b, c));
5426
+ return `<polygon points="${[
5427
+ `${formatNumber(a.x)} ${formatNumber(a.y)}`,
5428
+ `${formatNumber(b.x)} ${formatNumber(b.y)}`,
5429
+ `${formatNumber(c.x)} ${formatNumber(c.y)}`
5430
+ ].join(" ")}" fill="${color}" stroke="${color}" stroke-width="0.08"/>`;
5431
+ }
5432
+ var ModuleTransformerMeshGradientToSvg = class {
5433
+ target = "svg";
5434
+ gradientType = "mesh-gradient";
5435
+ to(input) {
5436
+ if (!(input instanceof MeshGradient)) throw new Error("Expected MeshGradient");
5437
+ const id = DEFAULT_ID;
5438
+ const vertexMap = buildMeshVertexMap(input, MESH_VIEW_BOX_SIZE, MESH_VIEW_BOX_SIZE);
5439
+ const grid = buildRegularMeshGrid(input, vertexMap);
5440
+ const subdivisions = input.config.method === "bicubic" ? BICUBIC_SUBDIVISIONS : BILINEAR_SUBDIVISIONS;
5441
+ const patchTriangles = input.patches.flatMap((patch) => buildPatchTriangles(input, grid, patch, vertexMap, subdivisions));
5442
+ const edgeTriangles = buildMeshEdgeSkirtTriangles(input, grid, MESH_VIEW_BOX_SIZE, MESH_VIEW_BOX_SIZE, subdivisions);
5443
+ const polygons = [...patchTriangles, ...edgeTriangles].map(triangleToPolygon);
5444
+ const imageSvg = [
5445
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${MESH_VIEW_BOX_SIZE} ${MESH_VIEW_BOX_SIZE}" width="${MESH_VIEW_BOX_SIZE}" height="${MESH_VIEW_BOX_SIZE}" preserveAspectRatio="none">`,
5446
+ ...polygons,
5447
+ "</svg>"
5448
+ ].join("");
5449
+ return buildSvgGradientResult({
5450
+ id,
5451
+ type: "pattern",
5452
+ gradient: [
5453
+ `<pattern id="${id}" patternUnits="objectBoundingBox" width="1" height="1" viewBox="0 0 ${MESH_VIEW_BOX_SIZE} ${MESH_VIEW_BOX_SIZE}" preserveAspectRatio="none">`,
5454
+ `<image width="${MESH_VIEW_BOX_SIZE}" height="${MESH_VIEW_BOX_SIZE}" href="${escapeXml(encodeSvgDataUrl(imageSvg))}"/>`,
5455
+ "</pattern>"
5456
+ ].join("")
5457
+ });
3054
5458
  }
3055
5459
  };
3056
5460
  //#endregion
@@ -3086,13 +5490,24 @@ var GradientTransformer = class {
3086
5490
  this._initialized = true;
3087
5491
  this.add(new ModuleTransformerLinearGradientToCss());
3088
5492
  this.add(new ModuleTransformerRadialGradientToCss());
5493
+ this.add(new ModuleTransformerDiamondGradientToCss());
3089
5494
  this.add(new ModuleTransformerConicGradientToCss());
5495
+ this.add(new ModuleTransformerMeshGradientToCss());
3090
5496
  this.add(new ModuleTransformerLinearGradientToCanvas());
3091
5497
  this.add(new ModuleTransformerRadialGradientToCanvas());
5498
+ this.add(new ModuleTransformerDiamondGradientToCanvas());
3092
5499
  this.add(new ModuleTransformerConicGradientToCanvas());
5500
+ this.add(new ModuleTransformerMeshGradientToCanvas());
3093
5501
  this.add(new ModuleTransformerLinearGradientToCanvasWebGL());
3094
5502
  this.add(new ModuleTransformerRadialGradientToCanvasWebGL());
5503
+ this.add(new ModuleTransformerDiamondGradientToCanvasWebGL());
3095
5504
  this.add(new ModuleTransformerConicGradientToCanvasWebGL());
5505
+ this.add(new ModuleTransformerMeshGradientToCanvasWebGL());
5506
+ this.add(new ModuleTransformerLinearGradientToSvg());
5507
+ this.add(new ModuleTransformerRadialGradientToSvg());
5508
+ this.add(new ModuleTransformerConicGradientToSvg());
5509
+ this.add(new ModuleTransformerDiamondGradientToSvg());
5510
+ this.add(new ModuleTransformerMeshGradientToSvg());
3096
5511
  }
3097
5512
  static _getKey(target, gradientType) {
3098
5513
  return `${target}:${gradientType}`;
@@ -3115,7 +5530,13 @@ var GradientFactory = class {
3115
5530
  return this._registry.delete(functionName);
3116
5531
  }
3117
5532
  static create(input) {
3118
- const abi = typeof input === "string" ? parseStringToAbi(input) : input;
5533
+ if (typeof input === "string") {
5534
+ const functionName = this._getFunctionName(input);
5535
+ const adapter = this.get(functionName);
5536
+ if (!adapter) throw new Error(`No gradient registered for: ${functionName}`);
5537
+ return adapter.fromString(input);
5538
+ }
5539
+ const abi = input;
3119
5540
  const adapter = this.get(abi.functionName);
3120
5541
  if (!adapter) throw new Error(`No gradient registered for: ${abi.functionName}`);
3121
5542
  return adapter.fromAbi(abi);
@@ -3133,7 +5554,17 @@ var GradientFactory = class {
3133
5554
  this._initialized = true;
3134
5555
  this.add("linear-gradient", LinearGradient);
3135
5556
  this.add("radial-gradient", RadialGradient);
5557
+ this.add("diamond-gradient", DiamondGradient);
3136
5558
  this.add("conic-gradient", ConicGradient);
5559
+ this.add("mesh-gradient", MeshGradient);
5560
+ }
5561
+ static _getFunctionName(input) {
5562
+ const source = input.trim();
5563
+ const openIndex = source.indexOf("(");
5564
+ if (openIndex <= 0) throw new Error("Expected function opening parenthesis");
5565
+ let functionName = source.slice(0, openIndex).trim();
5566
+ if (functionName.startsWith("repeating-")) functionName = functionName.slice(10);
5567
+ return functionName;
3137
5568
  }
3138
5569
  };
3139
5570
  function parse(input) {
@@ -3154,4 +5585,4 @@ function transformFrom(target, gradientType, input) {
3154
5585
  return GradientTransformer.from(target, gradientType, input);
3155
5586
  }
3156
5587
  //#endregion
3157
- export { ConicGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCanvasWebGL, ModuleTransformerConicGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCanvasWebGL, ModuleTransformerRadialGradientToCss, PatternTokenKind, RadialGradient, angleValueFromString, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngle, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isGradientColorSpace, isGradientHueInterpolation, isGradientPolarColorSpace, isPatternSyntaxValid, isPatternValid, isValid, matchExpression, normalizeAngleDeg, normalizeAngleRad, parse, parseStringToAbi, radToDeg, roundTo, splitTopLevelByWhitespace, toPercent, tokenizePattern, transformFrom, transformTo, truncTo, turnToRad, validate, validatePattern, validatePatternSemantic, validatePatternStructure, validatePatternSyntax };
5588
+ export { ConicGradient, DiamondGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, MeshGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCanvasWebGL, ModuleTransformerConicGradientToCss, ModuleTransformerConicGradientToSvg, ModuleTransformerDiamondGradientToCanvas, ModuleTransformerDiamondGradientToCanvasWebGL, ModuleTransformerDiamondGradientToCss, ModuleTransformerDiamondGradientToSvg, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerLinearGradientToSvg, ModuleTransformerMeshGradientToCanvas, ModuleTransformerMeshGradientToCanvasWebGL, ModuleTransformerMeshGradientToCss, ModuleTransformerMeshGradientToSvg, ModuleTransformerRadialGradientToCanvas, ModuleTransformerRadialGradientToCanvasWebGL, ModuleTransformerRadialGradientToCss, ModuleTransformerRadialGradientToSvg, PatternTokenKind, RadialGradient, angleValueFromString, ceilTo, clamp, degToRad, floorTo, format, fromPercent, gradToRad, isAngle, isAngleUnit, isColorHint, isColorStop, isConfig, isGradient, isGradientColorSpace, isGradientHueInterpolation, isGradientPolarColorSpace, isPatternSyntaxValid, isPatternValid, isValid, matchExpression, normalizeAngleDeg, normalizeAngleRad, parse, parseStringToAbi, radToDeg, roundTo, splitTopLevelByWhitespace, toPercent, tokenizePattern, transformFrom, transformTo, truncTo, turnToRad, validate, validatePattern, validatePatternSemantic, validatePatternStructure, validatePatternSyntax };