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/gradiente.global.iife.js +79 -7
- package/dist/index.d.ts +214 -16
- package/dist/index.js +3166 -735
- package/package.json +1 -1
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$
|
|
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$
|
|
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/
|
|
1822
|
-
const toRgb$
|
|
1823
|
-
function
|
|
1824
|
-
const color = toRgb$
|
|
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
|
|
2256
|
+
return [
|
|
2257
|
+
color.r ?? 0,
|
|
2258
|
+
color.g ?? 0,
|
|
2259
|
+
color.b ?? 0,
|
|
2260
|
+
color.alpha ?? 1
|
|
2261
|
+
];
|
|
1827
2262
|
}
|
|
1828
|
-
function
|
|
1829
|
-
|
|
1830
|
-
if (
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
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
|
|
1842
|
-
|
|
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
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
const
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
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
|
|
1895
|
-
const
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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
|
|
1908
|
-
|
|
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
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
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
|
|
1921
|
-
|
|
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
|
|
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:
|
|
2615
|
+
dx: 100 - center.x,
|
|
1933
2616
|
dy: -center.y
|
|
1934
2617
|
},
|
|
1935
2618
|
{
|
|
1936
2619
|
dx: -center.x,
|
|
1937
|
-
dy:
|
|
2620
|
+
dy: 100 - center.y
|
|
1938
2621
|
},
|
|
1939
2622
|
{
|
|
1940
|
-
dx:
|
|
1941
|
-
dy:
|
|
2623
|
+
dx: 100 - center.x,
|
|
2624
|
+
dy: 100 - center.y
|
|
1942
2625
|
}
|
|
1943
2626
|
];
|
|
1944
2627
|
}
|
|
1945
|
-
function
|
|
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.
|
|
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
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
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:
|
|
2007
|
-
y:
|
|
2662
|
+
x: Math.max(radiusX, 1e-4),
|
|
2663
|
+
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2008
2664
|
};
|
|
2009
2665
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
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:
|
|
2026
|
-
y:
|
|
2692
|
+
x: radius,
|
|
2693
|
+
y: radius
|
|
2027
2694
|
};
|
|
2028
2695
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
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
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
x:
|
|
2089
|
-
y:
|
|
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
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
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/
|
|
2109
|
-
|
|
2110
|
-
|
|
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
|
|
2118
|
-
const
|
|
2119
|
-
const
|
|
2120
|
-
const
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
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
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
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
|
-
|
|
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/
|
|
4021
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
|
|
2225
4022
|
const toRgb$2 = converter("rgb");
|
|
2226
|
-
const MAX_STOPS$
|
|
2227
|
-
|
|
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$
|
|
4062
|
+
function getColorStopCount$1(stops) {
|
|
2285
4063
|
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
2286
4064
|
}
|
|
2287
|
-
function getWebGLSampleCount$
|
|
2288
|
-
const colorStopCount = getColorStopCount$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
4263
|
+
var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
2324
4264
|
target = "canvas-webgl";
|
|
2325
|
-
gradientType = "
|
|
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
|
|
2348
|
-
uniform vec2
|
|
4287
|
+
uniform vec2 u_center;
|
|
4288
|
+
uniform vec2 u_radius;
|
|
2349
4289
|
uniform int u_stopCount;
|
|
2350
|
-
uniform float u_positions[${MAX_STOPS$
|
|
2351
|
-
uniform vec4 u_colors[${MAX_STOPS$
|
|
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$
|
|
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
|
|
2381
|
-
vec2
|
|
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,
|
|
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
|
|
2412
|
-
const
|
|
2413
|
-
const
|
|
2414
|
-
const
|
|
2415
|
-
const
|
|
2416
|
-
const
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
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$
|
|
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, "
|
|
2450
|
-
gl.uniform2f(gl.getUniformLocation(program, "
|
|
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/
|
|
4382
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerDiamondGradientToWebgl.ts
|
|
2462
4383
|
const toRgb$1 = converter("rgb");
|
|
2463
|
-
const MAX_STOPS
|
|
2464
|
-
const
|
|
2465
|
-
function toWebGLColor
|
|
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
|
|
4423
|
+
function getColorStopCount(stops) {
|
|
2503
4424
|
return stops.filter((stop) => stop.type === "color-stop").length;
|
|
2504
4425
|
}
|
|
2505
|
-
function getWebGLSampleCount
|
|
2506
|
-
const colorStopCount = getColorStopCount
|
|
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
|
|
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
|
|
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
|
|
4456
|
+
value: getColorAtPosition(colorStops, position),
|
|
2536
4457
|
position
|
|
2537
4458
|
});
|
|
2538
4459
|
}
|
|
2539
4460
|
return sampledStops;
|
|
2540
4461
|
}
|
|
2541
|
-
function
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
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
|
|
2550
|
-
if (
|
|
2551
|
-
if (
|
|
2552
|
-
|
|
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
|
|
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
|
|
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
|
|
4484
|
+
function resolveDiamondCenter(position, width, height) {
|
|
2569
4485
|
if (position.kind === "keywords") return {
|
|
2570
|
-
x: resolveKeywordPositionX
|
|
2571
|
-
y: resolveKeywordPositionY
|
|
4486
|
+
x: resolveKeywordPositionX(position.x, width),
|
|
4487
|
+
y: resolveKeywordPositionY(position.y, height)
|
|
2572
4488
|
};
|
|
2573
|
-
|
|
2574
|
-
x:
|
|
2575
|
-
y:
|
|
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:
|
|
2579
|
-
y:
|
|
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
|
-
|
|
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 = "
|
|
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
|
|
4647
|
+
uniform vec2 u_radius;
|
|
2611
4648
|
uniform int u_stopCount;
|
|
2612
|
-
uniform float u_positions[${MAX_STOPS
|
|
2613
|
-
uniform vec4 u_colors[${MAX_STOPS
|
|
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
|
|
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
|
-
|
|
2645
|
-
|
|
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 =
|
|
2673
|
-
const
|
|
2674
|
-
const
|
|
2675
|
-
const
|
|
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
|
|
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.
|
|
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/
|
|
2697
|
-
const
|
|
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
|
|
2738
|
-
return
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
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
|
|
2741
|
-
|
|
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
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
const
|
|
2753
|
-
const
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
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
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
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
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
return
|
|
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
|
|
2785
|
-
if (
|
|
2786
|
-
if (
|
|
2787
|
-
|
|
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
|
|
2791
|
-
if (
|
|
2792
|
-
if (
|
|
2793
|
-
|
|
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
|
|
5212
|
+
function resolveCenter(position) {
|
|
2797
5213
|
if (position.kind === "keywords") return {
|
|
2798
|
-
x:
|
|
2799
|
-
y:
|
|
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:
|
|
2807
|
-
y:
|
|
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
|
-
|
|
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
|
|
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:
|
|
5232
|
+
dx: VIEW_BOX_SIZE - center.x,
|
|
2836
5233
|
dy: -center.y
|
|
2837
5234
|
},
|
|
2838
5235
|
{
|
|
2839
5236
|
dx: -center.x,
|
|
2840
|
-
dy:
|
|
5237
|
+
dy: VIEW_BOX_SIZE - center.y
|
|
2841
5238
|
},
|
|
2842
5239
|
{
|
|
2843
|
-
dx:
|
|
2844
|
-
dy:
|
|
5240
|
+
dx: VIEW_BOX_SIZE - center.x,
|
|
5241
|
+
dy: VIEW_BOX_SIZE - center.y
|
|
2845
5242
|
}
|
|
2846
5243
|
];
|
|
2847
5244
|
}
|
|
2848
|
-
function
|
|
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.
|
|
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
|
|
5254
|
+
function resolveDiamondRadii(size, shape, center) {
|
|
2858
5255
|
if (size.kind === "explicit") {
|
|
2859
|
-
const radiusX =
|
|
2860
|
-
const radiusY = size.y ?
|
|
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 =
|
|
2867
|
-
const right =
|
|
2868
|
-
const top =
|
|
2869
|
-
const 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:
|
|
5274
|
+
x: VIEW_BOX_SIZE,
|
|
2878
5275
|
y: 0
|
|
2879
5276
|
},
|
|
2880
5277
|
{
|
|
2881
5278
|
x: 0,
|
|
2882
|
-
y:
|
|
5279
|
+
y: VIEW_BOX_SIZE
|
|
2883
5280
|
},
|
|
2884
5281
|
{
|
|
2885
|
-
x:
|
|
2886
|
-
y:
|
|
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
|
|
2929
|
-
if (size.value === "closest-corner") return corners.map((corner) =>
|
|
2930
|
-
|
|
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
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
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
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
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
|
-
|
|
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 };
|