Skip to content

Commit f6f258d

Browse files
committed
WIP
1 parent 4f09fa1 commit f6f258d

File tree

14 files changed

+4101
-101
lines changed

14 files changed

+4101
-101
lines changed

docs/beta/bundle.js

Lines changed: 241 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -693,20 +693,25 @@ const {
693693
createModelRenderer,
694694
createNode,
695695
setAttribute,
696+
setGradientDefinitions,
696697
} = require('./util');
697698

698699
module.exports = createLogo;
699700

700701
function createLogo(options = {}) {
701702
const cameraDistance = options.cameraDistance || 400;
702703
const { height, width } = calculateSizingOptions(options);
704+
const meshJson = options.meshJson || foxJson;
703705

704706
const container = createNode('svg');
705707
setAttribute(container, 'width', `${width}px`);
706708
setAttribute(container, 'height', `${height}px`);
707709
document.body.appendChild(container);
708710

709-
const modelObj = loadModelFromJson(options.meshJson || foxJson);
711+
setGradientDefinitions(container, meshJson.gradients);
712+
setMaskDefinitions(container, meshJson.masks, height, width);
713+
714+
const modelObj = loadModelFromJson(meshJson);
710715
const renderFox = createModelRenderer(container, cameraDistance, modelObj);
711716
const renderScene = (lookCurrent, slowDrift) => {
712717
const rect = container.getBoundingClientRect();
@@ -720,6 +725,27 @@ function createLogo(options = {}) {
720725
);
721726
}
722727

728+
function setMaskDefinitions(container, masks, height, width) {
729+
for (const [maskId, maskDefinition] of Object.entries(masks)) {
730+
const mask = createNode('mask');
731+
setAttribute(mask, 'id', maskId);
732+
733+
const maskedRect = createNode('rect');
734+
735+
// Extend mask beyond container to ensure it completely covers the model.
736+
// The model can extend beyond the container as well.
737+
setAttribute(maskedRect, 'width', width * 1.5);
738+
setAttribute(maskedRect, 'height', height * 1.5);
739+
setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`);
740+
setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`);
741+
742+
setAttribute(maskedRect, 'fill', maskDefinition.maskColor);
743+
mask.appendChild(maskedRect);
744+
745+
container.appendChild(mask);
746+
}
747+
}
748+
723749
},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){
724750
'use strict';
725751

@@ -1202,6 +1228,7 @@ module.exports = {
12021228
createFaceUpdater,
12031229
createNode,
12041230
setAttribute,
1231+
setGradientDefinitions,
12051232
svgElementToSvgImageContent,
12061233
Polygon,
12071234
};
@@ -1375,7 +1402,7 @@ function createModelRenderer(container, cameraDistance, modelObj) {
13751402
return (rect, lookPos, slowDrift) => {
13761403
const matrix = computeMatrix(rect, lookPos, slowDrift);
13771404
updatePositions(matrix);
1378-
updateFaces(rect, container, polygons, transformed);
1405+
updateFaces(rect);
13791406
};
13801407
}
13811408

@@ -1394,22 +1421,51 @@ function positionsFromModel(positions, modelJson) {
13941421
function createPolygonsFromModelJson(modelJson, createSvgPolygon) {
13951422
const polygons = [];
13961423
const polygonsByChunk = modelJson.chunks.map((chunk) => {
1397-
const { faces } = chunk;
1424+
const { faces, mask: maskId } = chunk;
13981425
return faces.map((face) => {
1399-
const svgPolygon = createSvgPolygon(chunk);
1400-
const polygon = new Polygon(svgPolygon, face);
1426+
const svgPolygon = createSvgPolygon(chunk, {
1427+
gradients: modelJson.gradients,
1428+
});
1429+
let maskPolygon;
1430+
if (maskId) {
1431+
if (modelJson.masks[maskId] === undefined) {
1432+
throw new Error(`Unrecognized mask ID: '${maskId}'`);
1433+
}
1434+
1435+
maskPolygon = createSvgPolygon(chunk, {
1436+
gradients: modelJson.gradients,
1437+
mask: modelJson.masks[maskId],
1438+
});
1439+
}
1440+
const polygon = new Polygon(svgPolygon, face, maskPolygon);
14011441
polygons.push(polygon);
14021442
return polygon;
14031443
});
14041444
});
14051445
return { polygons, polygonsByChunk };
14061446
}
14071447

1408-
function createStandardModelPolygon(chunk) {
1409-
const color = `rgb(${chunk.color})`;
1448+
function createStandardModelPolygon(chunk, { gradients = {}, mask }) {
14101449
const svgPolygon = createNode('polygon');
1411-
setAttribute(svgPolygon, 'fill', color);
1412-
setAttribute(svgPolygon, 'stroke', color);
1450+
1451+
if (mask) {
1452+
setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`);
1453+
setAttribute(svgPolygon, 'fill', mask.modelColor);
1454+
setAttribute(svgPolygon, 'stroke', mask.modelColor);
1455+
} else if (chunk.gradient) {
1456+
const gradientId = chunk.gradient;
1457+
if (!gradients[gradientId]) {
1458+
throw new Error(`Gradient ID not found: '${gradientId}'`);
1459+
}
1460+
1461+
setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`);
1462+
setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`);
1463+
} else {
1464+
const fill = `rgb(${chunk.color})`;
1465+
setAttribute(svgPolygon, 'fill', fill);
1466+
setAttribute(svgPolygon, 'stroke', fill);
1467+
}
1468+
14131469
setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10');
14141470
return svgPolygon;
14151471
}
@@ -1533,7 +1589,6 @@ function createFaceUpdater(container, polygons, transformed) {
15331589
const points = [];
15341590
let zmax = -Infinity;
15351591
let zmin = Infinity;
1536-
const element = poly.svg;
15371592
for (let j = 0; j < 3; ++j) {
15381593
const idx = indices[j];
15391594
points.push(
@@ -1545,20 +1600,26 @@ function createFaceUpdater(container, polygons, transformed) {
15451600
zmax = Math.max(zmax, z);
15461601
zmin = Math.min(zmin, z);
15471602
}
1603+
15481604
poly.zIndex = zmax + 0.25 * zmin;
15491605
const joinedPoints = points.join(' ');
15501606

15511607
if (joinedPoints.indexOf('NaN') === -1) {
1552-
setAttribute(element, 'points', joinedPoints);
1608+
setAttribute(poly.svg, 'points', joinedPoints);
1609+
if (poly.maskSvg) {
1610+
setAttribute(poly.maskSvg, 'points', joinedPoints);
1611+
}
15531612
}
1554-
15551613
toDraw.push(poly);
15561614
}
15571615
toDraw.sort(compareZ);
1558-
container.innerHTML = '';
1559-
for (i = 0; i < toDraw.length; ++i) {
1560-
container.appendChild(toDraw[i].svg);
1561-
}
1616+
1617+
const newPolygons = toDraw
1618+
.map((poly) => [poly.svg, ...(poly.maskSvg ? [poly.maskSvg] : [])])
1619+
.flat();
1620+
const defs = container.getElementsByTagName('defs');
1621+
const maskChildren = container.getElementsByTagName('mask');
1622+
container.replaceChildren(...defs, ...maskChildren, ...newPolygons);
15621623
};
15631624
}
15641625

@@ -1596,10 +1657,173 @@ function svgElementToSvgImageContent(svgElement) {
15961657
return content;
15971658
}
15981659

1599-
function Polygon(svg, indices) {
1660+
function Polygon(svg, indices, maskSvg) {
16001661
this.svg = svg;
16011662
this.indices = indices;
16021663
this.zIndex = 0;
1664+
if (maskSvg) {
1665+
this.maskSvg = maskSvg;
1666+
}
1667+
}
1668+
1669+
function setGradientDefinitions(container, gradients) {
1670+
if (!gradients || Object.keys(gradients).length === 0) {
1671+
return;
1672+
}
1673+
1674+
const defsContainer = createNode('defs');
1675+
1676+
for (const [gradientId, gradientDefinition] of Object.entries(gradients)) {
1677+
let gradient;
1678+
if (gradientDefinition.type === 'linear') {
1679+
gradient = createNode('linearGradient');
1680+
1681+
if (gradientDefinition.href !== undefined) {
1682+
if (gradients[gradientDefinition.href] === undefined) {
1683+
throw new Error(
1684+
`Gradient 'href' set to unrecognized gradient ID: '${gradientDefinition.href}'`,
1685+
);
1686+
} else if (gradients[gradientDefinition.href].type !== 'linear') {
1687+
throw new Error(
1688+
`Linear gradiants can only extend other linear gradiants. Attribute 'href' has been set to gradient ID '${gradientDefinition.href}', which not a linear gradiant.`,
1689+
);
1690+
}
1691+
setAttribute(gradient, 'href', `url('#${gradientDefinition.href}')`);
1692+
}
1693+
const coordinateAttributes = ['x1', 'x2', 'y1', 'y2'];
1694+
if (
1695+
coordinateAttributes.some(
1696+
(attributeName) => gradientDefinition[attributeName] !== undefined,
1697+
)
1698+
) {
1699+
const missingAttributes = coordinateAttributes.filter(
1700+
(attributeName) => gradientDefinition[attributeName] === undefined,
1701+
);
1702+
if (missingAttributes.length > 0) {
1703+
throw new Error(
1704+
`Missing coordinate attributes: '${missingAttributes.join(', ')}'`,
1705+
);
1706+
}
1707+
1708+
for (const attribute of coordinateAttributes) {
1709+
if (typeof gradientDefinition[attribute] !== 'string') {
1710+
throw new Error(
1711+
`Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[
1712+
attribute
1713+
]}'`,
1714+
);
1715+
}
1716+
setAttribute(gradient, attribute, gradientDefinition[attribute]);
1717+
}
1718+
}
1719+
} else if (gradientDefinition.type === 'radial') {
1720+
gradient = createNode('radialGradient');
1721+
1722+
if (gradientDefinition.href !== undefined) {
1723+
if (gradients[gradientDefinition.href] === undefined) {
1724+
throw new Error(
1725+
`Gradient 'href' set to unrecognized gradient ID: '${gradientDefinition.href}'`,
1726+
);
1727+
} else if (gradients[gradientDefinition.href].type !== 'radial') {
1728+
throw new Error(
1729+
`Radial gradiants can only extend other radial gradiants. Attribute 'href' has been set to gradient ID '${gradientDefinition.href}', which not a radial gradiant.`,
1730+
);
1731+
}
1732+
setAttribute(gradient, 'href', `url('#${gradientDefinition.href}')`);
1733+
}
1734+
const coordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r'];
1735+
const presentCoordinateAttributes = coordinateAttributes.filter(
1736+
(attributeName) => gradientDefinition[attributeName] !== undefined,
1737+
);
1738+
if (presentCoordinateAttributes.length > 0) {
1739+
for (const attribute of presentCoordinateAttributes) {
1740+
if (typeof gradientDefinition[attribute] !== 'string') {
1741+
throw new Error(
1742+
`Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[
1743+
attribute
1744+
]}'`,
1745+
);
1746+
}
1747+
setAttribute(gradient, attribute, gradientDefinition[attribute]);
1748+
}
1749+
}
1750+
} else {
1751+
throw new Error(`Unsupported gradent type: '${gradientDefinition.type}'`);
1752+
}
1753+
1754+
// Set common attributes
1755+
setAttribute(gradient, 'id', gradientId);
1756+
if (gradientDefinition.gradientUnits !== undefined) {
1757+
if (
1758+
!['userSpaceOnUse', 'objectBoundingBox'].includes(
1759+
gradientDefinition.gradientUnits,
1760+
)
1761+
) {
1762+
throw new Error(
1763+
`Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`,
1764+
);
1765+
}
1766+
setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits);
1767+
}
1768+
1769+
if (gradientDefinition.gradientTransform !== undefined) {
1770+
if (typeof gradientDefinition.gradientTransform !== 'string') {
1771+
throw new Error(
1772+
`Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`,
1773+
);
1774+
}
1775+
1776+
setAttribute(
1777+
gradient,
1778+
'gradientTransform',
1779+
gradientDefinition.gradientTransform,
1780+
);
1781+
}
1782+
1783+
if (gradientDefinition.spreadMethod !== undefined) {
1784+
if (
1785+
!['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod)
1786+
) {
1787+
throw new Error(
1788+
`Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`,
1789+
);
1790+
}
1791+
setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod);
1792+
}
1793+
1794+
if (gradientDefinition.stops !== undefined) {
1795+
if (!Array.isArray(gradientDefinition.stops)) {
1796+
throw new Error(`The 'stop' attribute must be an array`);
1797+
}
1798+
1799+
for (const stopDefinition of gradientDefinition.stops) {
1800+
if (typeof stopDefinition !== 'object') {
1801+
throw new Error(
1802+
`Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`,
1803+
);
1804+
}
1805+
const stop = createNode('stop');
1806+
1807+
if (stopDefinition.offset !== undefined) {
1808+
setAttribute(stop, 'offset', stopDefinition.offset);
1809+
}
1810+
1811+
if (stopDefinition['stop-color'] !== undefined) {
1812+
setAttribute(stop, 'stop-color', stopDefinition['stop-color']);
1813+
}
1814+
1815+
if (stopDefinition['stop-opacity'] !== undefined) {
1816+
setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']);
1817+
}
1818+
1819+
gradient.appendChild(stop);
1820+
}
1821+
}
1822+
1823+
defsContainer.appendChild(gradient);
1824+
}
1825+
1826+
container.appendChild(defsContainer);
16031827
}
16041828

16051829
},{"gl-mat4/invert":7,"gl-mat4/lookAt":8,"gl-mat4/multiply":9,"gl-mat4/perspective":10,"gl-mat4/rotate":11,"gl-vec3/transformMat4":12}]},{},[2]);

0 commit comments

Comments
 (0)