gradiente 2.3.0 → 2.4.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 +170 -16
- package/dist/index.js +2134 -327
- 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$9(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$9 = 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,113 +2248,713 @@ 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$8 = converter("rgb");
|
|
2253
|
+
function toColor(input) {
|
|
2254
|
+
const color = toRgb$8(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$2(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$2(vertex.x, width),
|
|
2272
|
+
y: resolveLengthPercentage$2(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$1(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(position.x),
|
|
2583
|
+
y: resolveKeywordY(position.y)
|
|
2584
|
+
};
|
|
2585
|
+
return {
|
|
2586
|
+
x: resolveLengthPercentage$1(position.x, 100),
|
|
2587
|
+
y: resolveLengthPercentage$1(position.y, 100)
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2590
|
+
function resolveKeywordX(value) {
|
|
2591
|
+
if (value === "left") return 0;
|
|
2592
|
+
if (value === "right") return 100;
|
|
2593
|
+
return 50;
|
|
2594
|
+
}
|
|
2595
|
+
function resolveKeywordY(value) {
|
|
2596
|
+
if (value === "top") return 0;
|
|
2597
|
+
if (value === "bottom") return 100;
|
|
2598
|
+
return 50;
|
|
2599
|
+
}
|
|
2600
|
+
function resolveLengthPercentage$1(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$4(center, corner) {
|
|
2606
|
+
return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
|
|
2607
|
+
}
|
|
2608
|
+
function getCornerDeltas$4(center) {
|
|
2609
|
+
return [
|
|
2610
|
+
{
|
|
2611
|
+
dx: -center.x,
|
|
2612
|
+
dy: -center.y
|
|
2613
|
+
},
|
|
2614
|
+
{
|
|
2615
|
+
dx: 100 - center.x,
|
|
2616
|
+
dy: -center.y
|
|
2617
|
+
},
|
|
2618
|
+
{
|
|
2619
|
+
dx: -center.x,
|
|
2620
|
+
dy: 100 - center.y
|
|
2621
|
+
},
|
|
2622
|
+
{
|
|
2623
|
+
dx: 100 - center.x,
|
|
2624
|
+
dy: 100 - center.y
|
|
2625
|
+
}
|
|
2626
|
+
];
|
|
2627
|
+
}
|
|
2628
|
+
function scaleDiamondRadiiToCorner$2(radiusX, radiusY, dx, dy) {
|
|
2629
|
+
const safeRadiusX = Math.max(radiusX, 1e-4);
|
|
2630
|
+
const safeRadiusY = Math.max(radiusY, 1e-4);
|
|
2631
|
+
const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
|
|
2632
|
+
return {
|
|
2633
|
+
x: safeRadiusX * scale,
|
|
2634
|
+
y: safeRadiusY * scale
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
function getMaxVisibleDiamondT$2(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$1(size, shape, center) {
|
|
2658
|
+
if (size.kind === "explicit") {
|
|
2659
|
+
const radiusX = resolveLengthPercentage$1(size.x, 100);
|
|
2660
|
+
const radiusY = size.y ? resolveLengthPercentage$1(size.y, 100) : radiusX;
|
|
2661
|
+
return {
|
|
2662
|
+
x: Math.max(radiusX, 1e-4),
|
|
2663
|
+
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
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$4(center, corner));
|
|
2689
|
+
if (size.value === "closest-side") {
|
|
2690
|
+
const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
|
|
2691
|
+
return {
|
|
2692
|
+
x: radius,
|
|
2693
|
+
y: radius
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
if (size.value === "farthest-side") {
|
|
2697
|
+
const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
|
|
2698
|
+
return {
|
|
2699
|
+
x: radius,
|
|
2700
|
+
y: radius
|
|
2701
|
+
};
|
|
2702
|
+
}
|
|
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
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
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$4(center);
|
|
2729
|
+
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);
|
|
2730
|
+
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);
|
|
2731
|
+
}
|
|
2732
|
+
function formatPoint(value) {
|
|
2733
|
+
return Number(value.toFixed(3)).toString();
|
|
2734
|
+
}
|
|
2735
|
+
function buildDiamondPolygon(center, radii, position) {
|
|
2736
|
+
const x = radii.x * position;
|
|
2737
|
+
const y = radii.y * position;
|
|
2738
|
+
return [
|
|
2739
|
+
`${formatPoint(center.x)} ${formatPoint(center.y - y)}`,
|
|
2740
|
+
`${formatPoint(center.x + x)} ${formatPoint(center.y)}`,
|
|
2741
|
+
`${formatPoint(center.x)} ${formatPoint(center.y + y)}`,
|
|
2742
|
+
`${formatPoint(center.x - x)} ${formatPoint(center.y)}`
|
|
2743
|
+
].join(" ");
|
|
2744
|
+
}
|
|
2745
|
+
function encodeSvgDataUrl$1(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$1(input.config.size, input.config.shape, center);
|
|
2755
|
+
const maxVisibleT = getMaxVisibleDiamondT$2(center, radii);
|
|
2756
|
+
const maxT = input.isRepeating ? maxVisibleT : 1;
|
|
2757
|
+
const baseStops = resolveRenderableGradientStops(input, DIAMOND_SAMPLE_COUNT);
|
|
2758
|
+
const stops = getColorStops$1(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(center, radii, position);
|
|
2766
|
+
polygons.push(`<polygon points="${points}" fill="${color}"/>`);
|
|
2767
|
+
}
|
|
2768
|
+
return encodeSvgDataUrl$1([
|
|
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(""));
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
//#endregion
|
|
2777
|
+
//#region src/gradient-transformer/modules/css/ModuleTransformerConicGradientToCss.ts
|
|
2778
|
+
var ModuleTransformerConicGradientToCss = class {
|
|
2779
|
+
target = "css";
|
|
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$2 = 24;
|
|
2790
|
+
function formatRgba(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(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$2 : 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(color)}"/>`);
|
|
2842
|
+
}
|
|
2843
|
+
return encodeSvgDataUrl([
|
|
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$7 = converter("rgb");
|
|
2853
|
+
function toCanvasColor$2(input) {
|
|
2854
|
+
const color = toRgb$7(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";
|
|
2881
|
+
to(input) {
|
|
2882
|
+
const gradient = input;
|
|
2883
|
+
return { draw: (ctx, width, height) => {
|
|
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);
|
|
2906
|
+
}
|
|
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$6 = converter("rgb");
|
|
2918
|
+
const RADIAL_GRADIENT_SAMPLE_COUNT = 128;
|
|
2919
|
+
function toCanvasColor$1(input) {
|
|
2920
|
+
const color = toRgb$6(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$3(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$3(center, width, height) {
|
|
2956
|
+
return [
|
|
2957
|
+
{
|
|
1928
2958
|
dx: -center.x,
|
|
1929
2959
|
dy: -center.y
|
|
1930
2960
|
},
|
|
@@ -1965,9 +2995,9 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
1965
2995
|
let normalizedStops = stops;
|
|
1966
2996
|
let innerFactor = 0;
|
|
1967
2997
|
let outerFactor = gradient.isRepeating ? maxVisibleT : 1;
|
|
1968
|
-
if (gradient.isRepeating) normalizedStops = normalizeStops$
|
|
2998
|
+
if (gradient.isRepeating) normalizedStops = normalizeStops$3(stops, 0, maxVisibleT);
|
|
1969
2999
|
else if (min < 0 || max > 1) {
|
|
1970
|
-
normalizedStops = normalizeStops$
|
|
3000
|
+
normalizedStops = normalizeStops$3(stops, min, max);
|
|
1971
3001
|
innerFactor = min;
|
|
1972
3002
|
outerFactor = max;
|
|
1973
3003
|
}
|
|
@@ -1976,25 +3006,263 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
1976
3006
|
const innerRadius = Math.max(0, baseRadius * innerFactor);
|
|
1977
3007
|
const outerRadius = Math.max(innerRadius + 1e-4, baseRadius * outerFactor);
|
|
1978
3008
|
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));
|
|
3009
|
+
for (const stop of normalizedStops) g.addColorStop(stop.position, toCanvasColor$1(stop.value));
|
|
1980
3010
|
ctx.fillStyle = g;
|
|
1981
3011
|
ctx.fillRect(0, 0, width, height);
|
|
1982
3012
|
return;
|
|
1983
3013
|
}
|
|
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();
|
|
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$3(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$3(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$5 = converter("rgb");
|
|
3140
|
+
const DIAMOND_GRADIENT_SAMPLE_COUNT = 128;
|
|
3141
|
+
const DIAMOND_COLOR_LOOKUP_SIZE = 1024;
|
|
3142
|
+
function toCanvasColor(input) {
|
|
3143
|
+
const color = toRgb$5(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(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$1(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$2(center, corner) {
|
|
3211
|
+
return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
|
|
3212
|
+
}
|
|
3213
|
+
function getCornerDeltas$2(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$1(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$1(center, radii, width, height);
|
|
3252
|
+
const baseStops = resolveRenderableGradientStops(gradient, DIAMOND_GRADIENT_SAMPLE_COUNT);
|
|
3253
|
+
const colorLookup = buildColorLookup(getColorStops(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);
|
|
1998
3266
|
} };
|
|
1999
3267
|
}
|
|
2000
3268
|
_resolveCenter(position, width, height) {
|
|
@@ -2017,7 +3285,7 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
2017
3285
|
if (value === "center") return height / 2;
|
|
2018
3286
|
return height;
|
|
2019
3287
|
}
|
|
2020
|
-
|
|
3288
|
+
_resolveDiamondRadii(size, shape, center, width, height) {
|
|
2021
3289
|
if (size.kind === "explicit") {
|
|
2022
3290
|
const radiusX = this._resolve(size.x, width);
|
|
2023
3291
|
const radiusY = size.y ? this._resolve(size.y, height) : radiusX;
|
|
@@ -2026,10 +3294,10 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
2026
3294
|
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2027
3295
|
};
|
|
2028
3296
|
}
|
|
2029
|
-
const left =
|
|
2030
|
-
const right =
|
|
2031
|
-
const top =
|
|
2032
|
-
const bottom =
|
|
3297
|
+
const left = center.x;
|
|
3298
|
+
const right = width - center.x;
|
|
3299
|
+
const top = center.y;
|
|
3300
|
+
const bottom = height - center.y;
|
|
2033
3301
|
if (shape === "circle") {
|
|
2034
3302
|
const cornerDistances = [
|
|
2035
3303
|
{
|
|
@@ -2048,29 +3316,29 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
2048
3316
|
x: width,
|
|
2049
3317
|
y: height
|
|
2050
3318
|
}
|
|
2051
|
-
].map((corner) => getDistanceToCorner$
|
|
3319
|
+
].map((corner) => getDistanceToCorner$2(center, corner));
|
|
2052
3320
|
if (size.value === "closest-side") {
|
|
2053
|
-
const radius = Math.min(left, right, top, bottom);
|
|
3321
|
+
const radius = Math.max(Math.min(left, right, top, bottom), 1e-4);
|
|
2054
3322
|
return {
|
|
2055
3323
|
x: radius,
|
|
2056
3324
|
y: radius
|
|
2057
3325
|
};
|
|
2058
3326
|
}
|
|
2059
3327
|
if (size.value === "farthest-side") {
|
|
2060
|
-
const radius = Math.max(left, right, top, bottom);
|
|
3328
|
+
const radius = Math.max(Math.max(left, right, top, bottom), 1e-4);
|
|
2061
3329
|
return {
|
|
2062
3330
|
x: radius,
|
|
2063
3331
|
y: radius
|
|
2064
3332
|
};
|
|
2065
3333
|
}
|
|
2066
3334
|
if (size.value === "closest-corner") {
|
|
2067
|
-
const radius = Math.min(...cornerDistances);
|
|
3335
|
+
const radius = Math.max(Math.min(...cornerDistances), 1e-4);
|
|
2068
3336
|
return {
|
|
2069
3337
|
x: radius,
|
|
2070
3338
|
y: radius
|
|
2071
3339
|
};
|
|
2072
3340
|
}
|
|
2073
|
-
const radius = Math.max(...cornerDistances);
|
|
3341
|
+
const radius = Math.max(Math.max(...cornerDistances), 1e-4);
|
|
2074
3342
|
return {
|
|
2075
3343
|
x: radius,
|
|
2076
3344
|
y: radius
|
|
@@ -2088,26 +3356,20 @@ var ModuleTransformerRadialGradientToCanvas = class {
|
|
|
2088
3356
|
x: Math.max(farthestSideRadiusX, 1e-4),
|
|
2089
3357
|
y: Math.max(farthestSideRadiusY, 1e-4)
|
|
2090
3358
|
};
|
|
2091
|
-
const corners = getCornerDeltas$
|
|
2092
|
-
if (size.value === "closest-corner") return corners.map((corner) =>
|
|
2093
|
-
|
|
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
|
-
});
|
|
3359
|
+
const corners = getCornerDeltas$2(center, width, height);
|
|
3360
|
+
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);
|
|
3361
|
+
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);
|
|
2100
3362
|
}
|
|
2101
3363
|
_resolve(value, size) {
|
|
2102
3364
|
if (value.kind === "percent") return value.value / 100 * size;
|
|
2103
3365
|
if (value.unit === "px") return value.value;
|
|
2104
|
-
|
|
3366
|
+
throw new Error(`Unsupported diamond-gradient length unit for Canvas transformer: ${value.unit}`);
|
|
2105
3367
|
}
|
|
2106
3368
|
};
|
|
2107
3369
|
//#endregion
|
|
2108
3370
|
//#region src/gradient-transformer/modules/canvas/ModuleTransformerConicGradientToCanvas.ts
|
|
2109
3371
|
const CONIC_GRADIENT_SAMPLE_COUNT = 128;
|
|
2110
|
-
const toRgb$
|
|
3372
|
+
const toRgb$4 = converter("rgb");
|
|
2111
3373
|
var ModuleTransformerConicGradientToCanvas = class {
|
|
2112
3374
|
target = "canvas-2d";
|
|
2113
3375
|
gradientType = "conic-gradient";
|
|
@@ -2207,7 +3469,7 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
2207
3469
|
};
|
|
2208
3470
|
}
|
|
2209
3471
|
_parseColor(input) {
|
|
2210
|
-
const color = toRgb$
|
|
3472
|
+
const color = toRgb$4(input);
|
|
2211
3473
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
2212
3474
|
return {
|
|
2213
3475
|
r: Math.round((color.r ?? 0) * 255),
|
|
@@ -2221,9 +3483,310 @@ var ModuleTransformerConicGradientToCanvas = class {
|
|
|
2221
3483
|
}
|
|
2222
3484
|
};
|
|
2223
3485
|
//#endregion
|
|
2224
|
-
//#region src/gradient-transformer/modules/
|
|
3486
|
+
//#region src/gradient-transformer/modules/canvas/ModuleTransformerMeshGradientToCanvas.ts
|
|
3487
|
+
const BICUBIC_SUBDIVISIONS$1 = 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$1 : 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$3 = converter("rgb");
|
|
3551
|
+
const MAX_STOPS$3 = 128;
|
|
3552
|
+
function toWebGLColor$3(input) {
|
|
3553
|
+
const color = toRgb$3(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
|
|
2225
3787
|
const toRgb$2 = converter("rgb");
|
|
2226
3788
|
const MAX_STOPS$2 = 128;
|
|
3789
|
+
const TWO_PI = Math.PI * 2;
|
|
2227
3790
|
function toWebGLColor$2(input) {
|
|
2228
3791
|
const color = toRgb$2(input);
|
|
2229
3792
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
@@ -2234,27 +3797,7 @@ function toWebGLColor$2(input) {
|
|
|
2234
3797
|
color.alpha ?? 1
|
|
2235
3798
|
];
|
|
2236
3799
|
}
|
|
2237
|
-
function
|
|
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
|
-
function createShader$2(gl, type, source) {
|
|
3800
|
+
function createShader$3(gl, type, source) {
|
|
2258
3801
|
const shader = gl.createShader(type);
|
|
2259
3802
|
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
2260
3803
|
gl.shaderSource(shader, source);
|
|
@@ -2266,9 +3809,9 @@ function createShader$2(gl, type, source) {
|
|
|
2266
3809
|
}
|
|
2267
3810
|
return shader;
|
|
2268
3811
|
}
|
|
2269
|
-
function createProgram$
|
|
2270
|
-
const vertexShader = createShader$
|
|
2271
|
-
const fragmentShader = createShader$
|
|
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);
|
|
2272
3815
|
const program = gl.createProgram();
|
|
2273
3816
|
if (!program) throw new Error("Failed to create WebGL program.");
|
|
2274
3817
|
gl.attachShader(program, vertexShader);
|
|
@@ -2320,9 +3863,50 @@ function fitStopsToWebGLLimit$2(stops, maxStops) {
|
|
|
2320
3863
|
}
|
|
2321
3864
|
return sampledStops;
|
|
2322
3865
|
}
|
|
2323
|
-
|
|
3866
|
+
function resolveLengthPercentage(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(angle) {
|
|
3875
|
+
if (angle.unit === "rad") return angle.value;
|
|
3876
|
+
if (angle.unit === "deg") return angle.value / 360 * TWO_PI;
|
|
3877
|
+
if (angle.unit === "turn") return angle.value * TWO_PI;
|
|
3878
|
+
if (angle.unit === "grad") return angle.value / 400 * TWO_PI;
|
|
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(position.x, width),
|
|
3900
|
+
y: resolveLengthPercentage(position.y, height)
|
|
3901
|
+
};
|
|
3902
|
+
return {
|
|
3903
|
+
x: width / 2,
|
|
3904
|
+
y: height / 2
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
2324
3908
|
target = "canvas-webgl";
|
|
2325
|
-
gradientType = "
|
|
3909
|
+
gradientType = "conic-gradient";
|
|
2326
3910
|
to(input) {
|
|
2327
3911
|
const gradient = input;
|
|
2328
3912
|
return { draw: (canvas, width, height) => {
|
|
@@ -2331,7 +3915,7 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2331
3915
|
canvas.width = width;
|
|
2332
3916
|
canvas.height = height;
|
|
2333
3917
|
gl.viewport(0, 0, width, height);
|
|
2334
|
-
const program = createProgram$
|
|
3918
|
+
const program = createProgram$3(gl, `
|
|
2335
3919
|
attribute vec2 a_position;
|
|
2336
3920
|
varying vec2 v_uv;
|
|
2337
3921
|
|
|
@@ -2342,10 +3926,13 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2342
3926
|
`, `
|
|
2343
3927
|
precision mediump float;
|
|
2344
3928
|
|
|
3929
|
+
const float PI = 3.141592653589793;
|
|
3930
|
+
const float TWO_PI = 6.283185307179586;
|
|
3931
|
+
|
|
2345
3932
|
varying vec2 v_uv;
|
|
2346
3933
|
|
|
2347
|
-
uniform vec2
|
|
2348
|
-
uniform
|
|
3934
|
+
uniform vec2 u_center;
|
|
3935
|
+
uniform float u_startAngle;
|
|
2349
3936
|
uniform int u_stopCount;
|
|
2350
3937
|
uniform float u_positions[${MAX_STOPS$2}];
|
|
2351
3938
|
uniform vec4 u_colors[${MAX_STOPS$2}];
|
|
@@ -2377,13 +3964,12 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2377
3964
|
}
|
|
2378
3965
|
|
|
2379
3966
|
void main() {
|
|
2380
|
-
vec2
|
|
2381
|
-
vec2 point = v_uv;
|
|
3967
|
+
vec2 delta = v_uv - u_center;
|
|
2382
3968
|
|
|
2383
|
-
float
|
|
2384
|
-
float
|
|
3969
|
+
float angle = atan(delta.y, delta.x);
|
|
3970
|
+
float cssAngle = mod((PI * 0.5) - angle + TWO_PI, TWO_PI);
|
|
2385
3971
|
|
|
2386
|
-
t =
|
|
3972
|
+
float t = mod(cssAngle - u_startAngle + TWO_PI, TWO_PI) / TWO_PI;
|
|
2387
3973
|
|
|
2388
3974
|
gl_FragColor = getGradientColor(t);
|
|
2389
3975
|
}
|
|
@@ -2408,34 +3994,8 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2408
3994
|
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
2409
3995
|
gl.enableVertexAttribArray(positionLocation);
|
|
2410
3996
|
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
2411
|
-
const
|
|
2412
|
-
const
|
|
2413
|
-
const dirY = -Math.cos(angle);
|
|
2414
|
-
const centerX = width / 2;
|
|
2415
|
-
const centerY = height / 2;
|
|
2416
|
-
const lineLength = Math.abs(width * dirX) + Math.abs(height * dirY);
|
|
2417
|
-
let startX = centerX - dirX * lineLength / 2;
|
|
2418
|
-
let startY = centerY - dirY * lineLength / 2;
|
|
2419
|
-
let endX = centerX + dirX * lineLength / 2;
|
|
2420
|
-
let endY = centerY + dirY * lineLength / 2;
|
|
2421
|
-
const { min, max, stops } = getStopRange(resolveRenderableGradientStops(gradient, getWebGLSampleCount$2(gradient, MAX_STOPS$2)));
|
|
2422
|
-
let normalizedStops = stops;
|
|
2423
|
-
if (min < 0 || max > 1) {
|
|
2424
|
-
const vx = endX - startX;
|
|
2425
|
-
const vy = endY - startY;
|
|
2426
|
-
const baseStartX = startX;
|
|
2427
|
-
const baseStartY = startY;
|
|
2428
|
-
startX = baseStartX + vx * min;
|
|
2429
|
-
startY = baseStartY + vy * min;
|
|
2430
|
-
endX = baseStartX + vx * max;
|
|
2431
|
-
endY = baseStartY + vy * max;
|
|
2432
|
-
normalizedStops = normalizeStops$1(stops, min, max);
|
|
2433
|
-
}
|
|
2434
|
-
const startU = startX / width;
|
|
2435
|
-
const startV = 1 - startY / height;
|
|
2436
|
-
const endU = endX / width;
|
|
2437
|
-
const endV = 1 - endY / height;
|
|
2438
|
-
const limitedStops = fitStopsToWebGLLimit$2(normalizedStops, MAX_STOPS$2);
|
|
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);
|
|
2439
3999
|
const positions = new Float32Array(MAX_STOPS$2);
|
|
2440
4000
|
const colors = new Float32Array(MAX_STOPS$2 * 4);
|
|
2441
4001
|
limitedStops.forEach((stop, index) => {
|
|
@@ -2446,8 +4006,8 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2446
4006
|
colors[index * 4 + 2] = color[2];
|
|
2447
4007
|
colors[index * 4 + 3] = color[3];
|
|
2448
4008
|
});
|
|
2449
|
-
gl.uniform2f(gl.getUniformLocation(program, "
|
|
2450
|
-
gl.
|
|
4009
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
|
|
4010
|
+
gl.uniform1f(gl.getUniformLocation(program, "u_startAngle"), resolveAngleToRadians(gradient.config.from));
|
|
2451
4011
|
gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
|
|
2452
4012
|
gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
|
|
2453
4013
|
gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
|
|
@@ -2458,10 +4018,10 @@ var ModuleTransformerLinearGradientToCanvasWebGL = class {
|
|
|
2458
4018
|
}
|
|
2459
4019
|
};
|
|
2460
4020
|
//#endregion
|
|
2461
|
-
//#region src/gradient-transformer/modules/webgl/
|
|
4021
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerRadialGradientToWebgl.ts
|
|
2462
4022
|
const toRgb$1 = converter("rgb");
|
|
2463
4023
|
const MAX_STOPS$1 = 128;
|
|
2464
|
-
const
|
|
4024
|
+
const MAX_REPEATING_RADIAL_T = 16;
|
|
2465
4025
|
function toWebGLColor$1(input) {
|
|
2466
4026
|
const color = toRgb$1(input);
|
|
2467
4027
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
@@ -2472,7 +4032,7 @@ function toWebGLColor$1(input) {
|
|
|
2472
4032
|
color.alpha ?? 1
|
|
2473
4033
|
];
|
|
2474
4034
|
}
|
|
2475
|
-
function createShader$
|
|
4035
|
+
function createShader$2(gl, type, source) {
|
|
2476
4036
|
const shader = gl.createShader(type);
|
|
2477
4037
|
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
2478
4038
|
gl.shaderSource(shader, source);
|
|
@@ -2484,9 +4044,9 @@ function createShader$1(gl, type, source) {
|
|
|
2484
4044
|
}
|
|
2485
4045
|
return shader;
|
|
2486
4046
|
}
|
|
2487
|
-
function createProgram$
|
|
2488
|
-
const vertexShader = createShader$
|
|
2489
|
-
const fragmentShader = createShader$
|
|
4047
|
+
function createProgram$2(gl, vertexSource, fragmentSource) {
|
|
4048
|
+
const vertexShader = createShader$2(gl, gl.VERTEX_SHADER, vertexSource);
|
|
4049
|
+
const fragmentShader = createShader$2(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
2490
4050
|
const program = gl.createProgram();
|
|
2491
4051
|
if (!program) throw new Error("Failed to create WebGL program.");
|
|
2492
4052
|
gl.attachShader(program, vertexShader);
|
|
@@ -2538,50 +4098,171 @@ function fitStopsToWebGLLimit$1(stops, maxStops) {
|
|
|
2538
4098
|
}
|
|
2539
4099
|
return sampledStops;
|
|
2540
4100
|
}
|
|
2541
|
-
function
|
|
4101
|
+
function parseLengthPercentage$1(value, reference) {
|
|
2542
4102
|
if (value.kind === "percent") return value.value / 100 * reference;
|
|
2543
4103
|
if (value.kind === "length") {
|
|
2544
4104
|
if (value.unit === "px") return value.value;
|
|
2545
|
-
throw new Error(`Unsupported gradient length unit for WebGL
|
|
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$1(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$1(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$1(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
|
+
};
|
|
2546
4240
|
}
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
if (
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
return angle.unit;
|
|
2555
|
-
}
|
|
2556
|
-
function resolveKeywordPositionX$1(x, width) {
|
|
2557
|
-
if (x === "left") return 0;
|
|
2558
|
-
if (x === "center") return width / 2;
|
|
2559
|
-
if (x === "right") return width;
|
|
2560
|
-
return width / 2;
|
|
2561
|
-
}
|
|
2562
|
-
function resolveKeywordPositionY$1(y, height) {
|
|
2563
|
-
if (y === "top") return 0;
|
|
2564
|
-
if (y === "center") return height / 2;
|
|
2565
|
-
if (y === "bottom") return height;
|
|
2566
|
-
return height / 2;
|
|
2567
|
-
}
|
|
2568
|
-
function resolveConicCenter(position, width, height) {
|
|
2569
|
-
if (position.kind === "keywords") return {
|
|
2570
|
-
x: resolveKeywordPositionX$1(position.x, width),
|
|
2571
|
-
y: resolveKeywordPositionY$1(position.y, height)
|
|
2572
|
-
};
|
|
2573
|
-
if (position.kind === "values") return {
|
|
2574
|
-
x: resolveLengthPercentage(position.x, width),
|
|
2575
|
-
y: resolveLengthPercentage(position.y, height)
|
|
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)
|
|
2576
4248
|
};
|
|
2577
|
-
return {
|
|
2578
|
-
x:
|
|
2579
|
-
y:
|
|
4249
|
+
if (size.value === "farthest-side") return {
|
|
4250
|
+
x: Math.max(farthestSideRadiusX, 1e-4),
|
|
4251
|
+
y: Math.max(farthestSideRadiusY, 1e-4)
|
|
2580
4252
|
};
|
|
4253
|
+
const corners = getCornerDeltas$1(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
|
+
});
|
|
2581
4262
|
}
|
|
2582
|
-
var
|
|
4263
|
+
var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
2583
4264
|
target = "canvas-webgl";
|
|
2584
|
-
gradientType = "
|
|
4265
|
+
gradientType = "radial-gradient";
|
|
2585
4266
|
to(input) {
|
|
2586
4267
|
const gradient = input;
|
|
2587
4268
|
return { draw: (canvas, width, height) => {
|
|
@@ -2590,7 +4271,7 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2590
4271
|
canvas.width = width;
|
|
2591
4272
|
canvas.height = height;
|
|
2592
4273
|
gl.viewport(0, 0, width, height);
|
|
2593
|
-
const program = createProgram$
|
|
4274
|
+
const program = createProgram$2(gl, `
|
|
2594
4275
|
attribute vec2 a_position;
|
|
2595
4276
|
varying vec2 v_uv;
|
|
2596
4277
|
|
|
@@ -2601,16 +4282,14 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2601
4282
|
`, `
|
|
2602
4283
|
precision mediump float;
|
|
2603
4284
|
|
|
2604
|
-
const float PI = 3.141592653589793;
|
|
2605
|
-
const float TWO_PI = 6.283185307179586;
|
|
2606
|
-
|
|
2607
4285
|
varying vec2 v_uv;
|
|
2608
4286
|
|
|
2609
4287
|
uniform vec2 u_center;
|
|
2610
|
-
uniform
|
|
4288
|
+
uniform vec2 u_radius;
|
|
2611
4289
|
uniform int u_stopCount;
|
|
2612
4290
|
uniform float u_positions[${MAX_STOPS$1}];
|
|
2613
4291
|
uniform vec4 u_colors[${MAX_STOPS$1}];
|
|
4292
|
+
uniform float u_tMax;
|
|
2614
4293
|
|
|
2615
4294
|
vec4 getGradientColor(float t) {
|
|
2616
4295
|
vec4 result = u_colors[0];
|
|
@@ -2640,11 +4319,11 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2640
4319
|
|
|
2641
4320
|
void main() {
|
|
2642
4321
|
vec2 delta = v_uv - u_center;
|
|
4322
|
+
vec2 normalized = delta / max(u_radius, vec2(0.00001));
|
|
4323
|
+
float t = length(normalized);
|
|
2643
4324
|
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
float t = mod(cssAngle - u_startAngle + TWO_PI, TWO_PI) / TWO_PI;
|
|
4325
|
+
t = clamp(t, 0.0, u_tMax);
|
|
4326
|
+
t = t / max(u_tMax, 0.00001);
|
|
2648
4327
|
|
|
2649
4328
|
gl_FragColor = getGradientColor(t);
|
|
2650
4329
|
}
|
|
@@ -2669,8 +4348,14 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2669
4348
|
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
2670
4349
|
gl.enableVertexAttribArray(positionLocation);
|
|
2671
4350
|
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
2672
|
-
const center =
|
|
2673
|
-
const
|
|
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);
|
|
2674
4359
|
const positions = new Float32Array(MAX_STOPS$1);
|
|
2675
4360
|
const colors = new Float32Array(MAX_STOPS$1 * 4);
|
|
2676
4361
|
limitedStops.forEach((stop, index) => {
|
|
@@ -2682,7 +4367,8 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2682
4367
|
colors[index * 4 + 3] = color[3];
|
|
2683
4368
|
});
|
|
2684
4369
|
gl.uniform2f(gl.getUniformLocation(program, "u_center"), center.x / width, 1 - center.y / height);
|
|
2685
|
-
gl.
|
|
4370
|
+
gl.uniform2f(gl.getUniformLocation(program, "u_radius"), radius.x / width, radius.y / height);
|
|
4371
|
+
gl.uniform1f(gl.getUniformLocation(program, "u_tMax"), maxT);
|
|
2686
4372
|
gl.uniform1i(gl.getUniformLocation(program, "u_stopCount"), limitedStops.length);
|
|
2687
4373
|
gl.uniform1fv(gl.getUniformLocation(program, "u_positions"), positions);
|
|
2688
4374
|
gl.uniform4fv(gl.getUniformLocation(program, "u_colors"), colors);
|
|
@@ -2693,10 +4379,10 @@ var ModuleTransformerConicGradientToCanvasWebGL = class {
|
|
|
2693
4379
|
}
|
|
2694
4380
|
};
|
|
2695
4381
|
//#endregion
|
|
2696
|
-
//#region src/gradient-transformer/modules/webgl/
|
|
4382
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerDiamondGradientToWebgl.ts
|
|
2697
4383
|
const toRgb = converter("rgb");
|
|
2698
4384
|
const MAX_STOPS = 128;
|
|
2699
|
-
const
|
|
4385
|
+
const MAX_REPEATING_DIAMOND_T = 16;
|
|
2700
4386
|
function toWebGLColor(input) {
|
|
2701
4387
|
const color = toRgb(input);
|
|
2702
4388
|
if (!color) throw new Error(`Failed to convert color: ${input}`);
|
|
@@ -2707,7 +4393,7 @@ function toWebGLColor(input) {
|
|
|
2707
4393
|
color.alpha ?? 1
|
|
2708
4394
|
];
|
|
2709
4395
|
}
|
|
2710
|
-
function createShader(gl, type, source) {
|
|
4396
|
+
function createShader$1(gl, type, source) {
|
|
2711
4397
|
const shader = gl.createShader(type);
|
|
2712
4398
|
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
2713
4399
|
gl.shaderSource(shader, source);
|
|
@@ -2719,9 +4405,9 @@ function createShader(gl, type, source) {
|
|
|
2719
4405
|
}
|
|
2720
4406
|
return shader;
|
|
2721
4407
|
}
|
|
2722
|
-
function createProgram(gl, vertexSource, fragmentSource) {
|
|
2723
|
-
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
2724
|
-
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
4408
|
+
function createProgram$1(gl, vertexSource, fragmentSource) {
|
|
4409
|
+
const vertexShader = createShader$1(gl, gl.VERTEX_SHADER, vertexSource);
|
|
4410
|
+
const fragmentShader = createShader$1(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
2725
4411
|
const program = gl.createProgram();
|
|
2726
4412
|
if (!program) throw new Error("Failed to create WebGL program.");
|
|
2727
4413
|
gl.attachShader(program, vertexShader);
|
|
@@ -2744,7 +4430,7 @@ function getWebGLSampleCount(gradient, maxStops) {
|
|
|
2744
4430
|
}
|
|
2745
4431
|
function getColorAtPosition(stops, position) {
|
|
2746
4432
|
const colorStops = stops.filter((stop) => stop.type === "color-stop" && stop.position != null).sort((a, b) => a.position - b.position);
|
|
2747
|
-
if (colorStops.length === 0) throw new Error("Cannot sample color from empty gradient stops.");
|
|
4433
|
+
if (colorStops.length === 0) throw new Error("Cannot sample color from empty diamond gradient stops.");
|
|
2748
4434
|
if (position <= colorStops[0].position) return colorStops[0].value;
|
|
2749
4435
|
const lastStop = colorStops[colorStops.length - 1];
|
|
2750
4436
|
if (position >= lastStop.position) return lastStop.value;
|
|
@@ -2773,57 +4459,40 @@ function fitStopsToWebGLLimit(stops, maxStops) {
|
|
|
2773
4459
|
}
|
|
2774
4460
|
return sampledStops;
|
|
2775
4461
|
}
|
|
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
|
+
}));
|
|
4468
|
+
}
|
|
2776
4469
|
function parseLengthPercentage(value, reference) {
|
|
2777
4470
|
if (value.kind === "percent") return value.value / 100 * reference;
|
|
2778
|
-
if (value.
|
|
2779
|
-
|
|
2780
|
-
throw new Error(`Unsupported gradient length unit for WebGL radial gradient: ${value.unit}`);
|
|
2781
|
-
}
|
|
2782
|
-
return value;
|
|
4471
|
+
if (value.unit === "px") return value.value;
|
|
4472
|
+
throw new Error(`Unsupported diamond-gradient length unit for WebGL transformer: ${value.unit}`);
|
|
2783
4473
|
}
|
|
2784
4474
|
function resolveKeywordPositionX(x, width) {
|
|
2785
4475
|
if (x === "left") return 0;
|
|
2786
|
-
if (x === "center") return width / 2;
|
|
2787
4476
|
if (x === "right") return width;
|
|
2788
4477
|
return width / 2;
|
|
2789
4478
|
}
|
|
2790
4479
|
function resolveKeywordPositionY(y, height) {
|
|
2791
4480
|
if (y === "top") return 0;
|
|
2792
|
-
if (y === "center") return height / 2;
|
|
2793
4481
|
if (y === "bottom") return height;
|
|
2794
4482
|
return height / 2;
|
|
2795
4483
|
}
|
|
2796
|
-
function
|
|
4484
|
+
function resolveDiamondCenter(position, width, height) {
|
|
2797
4485
|
if (position.kind === "keywords") return {
|
|
2798
4486
|
x: resolveKeywordPositionX(position.x, width),
|
|
2799
4487
|
y: resolveKeywordPositionY(position.y, height)
|
|
2800
4488
|
};
|
|
2801
|
-
|
|
4489
|
+
return {
|
|
2802
4490
|
x: parseLengthPercentage(position.x, width),
|
|
2803
4491
|
y: parseLengthPercentage(position.y, height)
|
|
2804
4492
|
};
|
|
2805
|
-
return {
|
|
2806
|
-
x: width / 2,
|
|
2807
|
-
y: height / 2
|
|
2808
|
-
};
|
|
2809
|
-
}
|
|
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
4493
|
}
|
|
2816
4494
|
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
|
-
}));
|
|
4495
|
+
return Math.abs(corner.x - center.x) + Math.abs(corner.y - center.y);
|
|
2827
4496
|
}
|
|
2828
4497
|
function getCornerDeltas(center, width, height) {
|
|
2829
4498
|
return [
|
|
@@ -2845,16 +4514,16 @@ function getCornerDeltas(center, width, height) {
|
|
|
2845
4514
|
}
|
|
2846
4515
|
];
|
|
2847
4516
|
}
|
|
2848
|
-
function
|
|
4517
|
+
function scaleDiamondRadiiToCorner(radiusX, radiusY, dx, dy) {
|
|
2849
4518
|
const safeRadiusX = Math.max(radiusX, 1e-4);
|
|
2850
4519
|
const safeRadiusY = Math.max(radiusY, 1e-4);
|
|
2851
|
-
const scale = Math.
|
|
4520
|
+
const scale = Math.abs(dx) / safeRadiusX + Math.abs(dy) / safeRadiusY;
|
|
2852
4521
|
return {
|
|
2853
4522
|
x: safeRadiusX * scale,
|
|
2854
4523
|
y: safeRadiusY * scale
|
|
2855
4524
|
};
|
|
2856
4525
|
}
|
|
2857
|
-
function
|
|
4526
|
+
function resolveDiamondRadii(size, shape, center, width, height) {
|
|
2858
4527
|
if (size.kind === "explicit") {
|
|
2859
4528
|
const radiusX = parseLengthPercentage(size.x, width);
|
|
2860
4529
|
const radiusY = size.y ? parseLengthPercentage(size.y, height) : radiusX;
|
|
@@ -2863,10 +4532,10 @@ function resolveRadialRadii(size, shape, center, width, height) {
|
|
|
2863
4532
|
y: Math.max(shape === "circle" ? radiusX : radiusY, 1e-4)
|
|
2864
4533
|
};
|
|
2865
4534
|
}
|
|
2866
|
-
const left =
|
|
2867
|
-
const right =
|
|
2868
|
-
const top =
|
|
2869
|
-
const bottom =
|
|
4535
|
+
const left = center.x;
|
|
4536
|
+
const right = width - center.x;
|
|
4537
|
+
const top = center.y;
|
|
4538
|
+
const bottom = height - center.y;
|
|
2870
4539
|
if (shape === "circle") {
|
|
2871
4540
|
const cornerDistances = [
|
|
2872
4541
|
{
|
|
@@ -2926,19 +4595,34 @@ function resolveRadialRadii(size, shape, center, width, height) {
|
|
|
2926
4595
|
y: Math.max(farthestSideRadiusY, 1e-4)
|
|
2927
4596
|
};
|
|
2928
4597
|
const corners = getCornerDeltas(center, width, height);
|
|
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
|
-
});
|
|
4598
|
+
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);
|
|
4599
|
+
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
4600
|
}
|
|
2938
|
-
|
|
4601
|
+
function getMaxVisibleDiamondT(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 {
|
|
2939
4622
|
target = "canvas-webgl";
|
|
2940
|
-
gradientType = "
|
|
4623
|
+
gradientType = "diamond-gradient";
|
|
2941
4624
|
to(input) {
|
|
4625
|
+
if (!(input instanceof DiamondGradient)) throw new Error("Expected DiamondGradient");
|
|
2942
4626
|
const gradient = input;
|
|
2943
4627
|
return { draw: (canvas, width, height) => {
|
|
2944
4628
|
const gl = canvas.getContext("webgl");
|
|
@@ -2946,7 +4630,7 @@ var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
|
2946
4630
|
canvas.width = width;
|
|
2947
4631
|
canvas.height = height;
|
|
2948
4632
|
gl.viewport(0, 0, width, height);
|
|
2949
|
-
const program = createProgram(gl, `
|
|
4633
|
+
const program = createProgram$1(gl, `
|
|
2950
4634
|
attribute vec2 a_position;
|
|
2951
4635
|
varying vec2 v_uv;
|
|
2952
4636
|
|
|
@@ -2994,8 +4678,8 @@ var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
|
2994
4678
|
|
|
2995
4679
|
void main() {
|
|
2996
4680
|
vec2 delta = v_uv - u_center;
|
|
2997
|
-
vec2 normalized = delta / max(u_radius, vec2(0.00001));
|
|
2998
|
-
float t =
|
|
4681
|
+
vec2 normalized = abs(delta) / max(u_radius, vec2(0.00001));
|
|
4682
|
+
float t = normalized.x + normalized.y;
|
|
2999
4683
|
|
|
3000
4684
|
t = clamp(t, 0.0, u_tMax);
|
|
3001
4685
|
t = t / max(u_tMax, 0.00001);
|
|
@@ -3023,11 +4707,11 @@ var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
|
3023
4707
|
const positionLocation = gl.getAttribLocation(program, "a_position");
|
|
3024
4708
|
gl.enableVertexAttribArray(positionLocation);
|
|
3025
4709
|
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
|
3026
|
-
const center =
|
|
3027
|
-
const radius =
|
|
3028
|
-
const maxVisibleT =
|
|
4710
|
+
const center = resolveDiamondCenter(gradient.config.position, width, height);
|
|
4711
|
+
const radius = resolveDiamondRadii(gradient.config.size, gradient.config.shape, center, width, height);
|
|
4712
|
+
const maxVisibleT = getMaxVisibleDiamondT(center, radius, width, height);
|
|
3029
4713
|
const baseStops = resolveRenderableGradientStops(gradient, getWebGLSampleCount(gradient, MAX_STOPS));
|
|
3030
|
-
const repeatMaxT = Math.min(maxVisibleT,
|
|
4714
|
+
const repeatMaxT = Math.min(maxVisibleT, MAX_REPEATING_DIAMOND_T);
|
|
3031
4715
|
const maxT = gradient.isRepeating ? repeatMaxT : 1;
|
|
3032
4716
|
const renderStops = gradient.isRepeating ? expandRepeatingStopsTo(baseStops, 0, repeatMaxT) : baseStops;
|
|
3033
4717
|
const limitedStops = fitStopsToWebGLLimit(gradient.isRepeating ? normalizeStops(renderStops, 0, repeatMaxT) : renderStops, MAX_STOPS);
|
|
@@ -3054,6 +4738,107 @@ var ModuleTransformerRadialGradientToCanvasWebGL = class {
|
|
|
3054
4738
|
}
|
|
3055
4739
|
};
|
|
3056
4740
|
//#endregion
|
|
4741
|
+
//#region src/gradient-transformer/modules/webgl/ModuleTransformerMeshGradientToWebgl.ts
|
|
4742
|
+
const BICUBIC_SUBDIVISIONS = 24;
|
|
4743
|
+
function createShader(gl, type, source) {
|
|
4744
|
+
const shader = gl.createShader(type);
|
|
4745
|
+
if (!shader) throw new Error("Failed to create WebGL shader.");
|
|
4746
|
+
gl.shaderSource(shader, source);
|
|
4747
|
+
gl.compileShader(shader);
|
|
4748
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
4749
|
+
const error = gl.getShaderInfoLog(shader);
|
|
4750
|
+
gl.deleteShader(shader);
|
|
4751
|
+
throw new Error(`WebGL shader compile error: ${error}`);
|
|
4752
|
+
}
|
|
4753
|
+
return shader;
|
|
4754
|
+
}
|
|
4755
|
+
function createProgram(gl, vertexSource, fragmentSource) {
|
|
4756
|
+
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
|
|
4757
|
+
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
|
|
4758
|
+
const program = gl.createProgram();
|
|
4759
|
+
if (!program) throw new Error("Failed to create WebGL program.");
|
|
4760
|
+
gl.attachShader(program, vertexShader);
|
|
4761
|
+
gl.attachShader(program, fragmentShader);
|
|
4762
|
+
gl.linkProgram(program);
|
|
4763
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
4764
|
+
const error = gl.getProgramInfoLog(program);
|
|
4765
|
+
gl.deleteProgram(program);
|
|
4766
|
+
throw new Error(`WebGL program link error: ${error}`);
|
|
4767
|
+
}
|
|
4768
|
+
return program;
|
|
4769
|
+
}
|
|
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;
|
|
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
|
+
//#endregion
|
|
3057
4842
|
//#region src/gradient-transformer/GradientTransformer.ts
|
|
3058
4843
|
var GradientTransformer = class {
|
|
3059
4844
|
static _modules = /* @__PURE__ */ new Map();
|
|
@@ -3086,13 +4871,19 @@ var GradientTransformer = class {
|
|
|
3086
4871
|
this._initialized = true;
|
|
3087
4872
|
this.add(new ModuleTransformerLinearGradientToCss());
|
|
3088
4873
|
this.add(new ModuleTransformerRadialGradientToCss());
|
|
4874
|
+
this.add(new ModuleTransformerDiamondGradientToCss());
|
|
3089
4875
|
this.add(new ModuleTransformerConicGradientToCss());
|
|
4876
|
+
this.add(new ModuleTransformerMeshGradientToCss());
|
|
3090
4877
|
this.add(new ModuleTransformerLinearGradientToCanvas());
|
|
3091
4878
|
this.add(new ModuleTransformerRadialGradientToCanvas());
|
|
4879
|
+
this.add(new ModuleTransformerDiamondGradientToCanvas());
|
|
3092
4880
|
this.add(new ModuleTransformerConicGradientToCanvas());
|
|
4881
|
+
this.add(new ModuleTransformerMeshGradientToCanvas());
|
|
3093
4882
|
this.add(new ModuleTransformerLinearGradientToCanvasWebGL());
|
|
3094
4883
|
this.add(new ModuleTransformerRadialGradientToCanvasWebGL());
|
|
4884
|
+
this.add(new ModuleTransformerDiamondGradientToCanvasWebGL());
|
|
3095
4885
|
this.add(new ModuleTransformerConicGradientToCanvasWebGL());
|
|
4886
|
+
this.add(new ModuleTransformerMeshGradientToCanvasWebGL());
|
|
3096
4887
|
}
|
|
3097
4888
|
static _getKey(target, gradientType) {
|
|
3098
4889
|
return `${target}:${gradientType}`;
|
|
@@ -3115,7 +4906,13 @@ var GradientFactory = class {
|
|
|
3115
4906
|
return this._registry.delete(functionName);
|
|
3116
4907
|
}
|
|
3117
4908
|
static create(input) {
|
|
3118
|
-
|
|
4909
|
+
if (typeof input === "string") {
|
|
4910
|
+
const functionName = this._getFunctionName(input);
|
|
4911
|
+
const adapter = this.get(functionName);
|
|
4912
|
+
if (!adapter) throw new Error(`No gradient registered for: ${functionName}`);
|
|
4913
|
+
return adapter.fromString(input);
|
|
4914
|
+
}
|
|
4915
|
+
const abi = input;
|
|
3119
4916
|
const adapter = this.get(abi.functionName);
|
|
3120
4917
|
if (!adapter) throw new Error(`No gradient registered for: ${abi.functionName}`);
|
|
3121
4918
|
return adapter.fromAbi(abi);
|
|
@@ -3133,7 +4930,17 @@ var GradientFactory = class {
|
|
|
3133
4930
|
this._initialized = true;
|
|
3134
4931
|
this.add("linear-gradient", LinearGradient);
|
|
3135
4932
|
this.add("radial-gradient", RadialGradient);
|
|
4933
|
+
this.add("diamond-gradient", DiamondGradient);
|
|
3136
4934
|
this.add("conic-gradient", ConicGradient);
|
|
4935
|
+
this.add("mesh-gradient", MeshGradient);
|
|
4936
|
+
}
|
|
4937
|
+
static _getFunctionName(input) {
|
|
4938
|
+
const source = input.trim();
|
|
4939
|
+
const openIndex = source.indexOf("(");
|
|
4940
|
+
if (openIndex <= 0) throw new Error("Expected function opening parenthesis");
|
|
4941
|
+
let functionName = source.slice(0, openIndex).trim();
|
|
4942
|
+
if (functionName.startsWith("repeating-")) functionName = functionName.slice(10);
|
|
4943
|
+
return functionName;
|
|
3137
4944
|
}
|
|
3138
4945
|
};
|
|
3139
4946
|
function parse(input) {
|
|
@@ -3154,4 +4961,4 @@ function transformFrom(target, gradientType, input) {
|
|
|
3154
4961
|
return GradientTransformer.from(target, gradientType, input);
|
|
3155
4962
|
}
|
|
3156
4963
|
//#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 };
|
|
4964
|
+
export { ConicGradient, DiamondGradient, GRADIENT_COLOR_SPACE, GRADIENT_HUE_INTERPOLATIONS, GRADIENT_POLAR_COLOR_SPACES, GradientBase, GradientFactory, GradientTransformer, LinearGradient, MeshGradient, ModuleTransformerConicGradientToCanvas, ModuleTransformerConicGradientToCanvasWebGL, ModuleTransformerConicGradientToCss, ModuleTransformerDiamondGradientToCanvas, ModuleTransformerDiamondGradientToCanvasWebGL, ModuleTransformerDiamondGradientToCss, ModuleTransformerLinearGradientToCanvas, ModuleTransformerLinearGradientToCanvasWebGL, ModuleTransformerLinearGradientToCss, ModuleTransformerMeshGradientToCanvas, ModuleTransformerMeshGradientToCanvasWebGL, ModuleTransformerMeshGradientToCss, 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 };
|