diff --git a/demo/src/index.html b/demo/src/index.html index 6166a61a..1a2a3f45 100644 --- a/demo/src/index.html +++ b/demo/src/index.html @@ -14,6 +14,7 @@
  • recolor
  • distort
  • fade
  • +
  • overlay
  • beta
  • flask
  • diff --git a/demo/src/overlay/index.html b/demo/src/overlay/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/demo/src/overlay/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
    + +

    Press C to copy the SVG text of the image.

    + + diff --git a/demo/src/overlay/overlay-fox.json b/demo/src/overlay/overlay-fox.json new file mode 100644 index 00000000..6fbf7b57 --- /dev/null +++ b/demo/src/overlay/overlay-fox.json @@ -0,0 +1,386 @@ +{ + "positions": [ + [111.024597, 52.604599, 46.225899], + [114.025002, 87.673302, 58.9818], + [66.192001, 80.898003, 55.394299], + [72.113297, 35.491798, 30.871401], + [97.804497, 116.560997, 73.978798], + [16.7623, 58.010899, 58.078201], + [52.608898, 30.3641, 42.556099], + [106.881401, 31.945499, 46.9133], + [113.484596, 38.6049, 49.121498], + [108.6633, 43.2332, 46.315399], + [101.216599, 15.9822, 46.308201], + [16.6605, -16.2883, 93.618698], + [40.775002, -10.2288, 85.276398], + [23.926901, -2.5103, 86.736504], + [11.1691, -7.0037, 99.377602], + [9.5692, -34.393902, 141.671997], + [12.596, 7.1655, 88.740997], + [61.180901, 8.8142, 76.996803], + [39.719501, -28.927099, 88.963799], + [13.7962, -68.575699, 132.057007], + [15.2674, -62.32, 129.688004], + [14.8446, -52.6096, 140.113007], + [12.8917, -49.771599, 144.740997], + [35.604198, -71.758003, 81.063904], + [47.462502, -68.606102, 63.369701], + [38.2486, -64.730202, 38.909901], + [-12.8917, -49.771599, 144.740997], + [-13.7962, -68.575699, 132.057007], + [17.802099, -71.758003, 81.063904], + [19.1243, -69.0168, 49.420101], + [38.2486, -66.275597, 17.776199], + [12.8928, -36.703499, 141.671997], + [109.283997, -93.589897, 27.824301], + [122.117996, -36.8894, 35.025002], + [67.7668, -30.197001, 78.417801], + [33.180698, 101.851997, 25.3186], + [9.4063, -35.589802, 150.722], + [-9.5692, -34.393902, 141.671997], + [-9.4063, -35.589802, 150.722], + [11.4565, -37.899399, 150.722], + [-12.596, 7.1655, 88.740997], + [-11.1691, -7.0037, 99.377602], + [70.236504, 62.836201, -3.9475], + [47.263401, 54.293999, -27.414801], + [28.7302, 91.731102, -24.972601], + [69.167603, 6.5862, -12.7757], + [28.7302, 49.1003, -48.3596], + [31.903, 5.692, -47.821999], + [35.075802, -34.432899, -16.280899], + [115.284103, 48.681499, 48.684101], + [110.842796, 28.4821, 49.176201], + [-19.1243, -69.0168, 49.420101], + [-38.2486, -66.275597, 17.776199], + [-111.024597, 52.604599, 46.225899], + [-72.113297, 35.491798, 30.871401], + [-66.192001, 80.898003, 55.394299], + [-114.025002, 87.673302, 58.9818], + [-97.804497, 116.560997, 73.978798], + [-52.608898, 30.3641, 42.556099], + [-16.7623, 58.010899, 58.078201], + [-106.881401, 31.945499, 46.9133], + [-108.6633, 43.2332, 46.315399], + [-113.484596, 38.6049, 49.121498], + [-101.216599, 15.9822, 46.308201], + [-16.6605, -16.2883, 93.618698], + [-23.926901, -2.5103, 86.736504], + [-40.775002, -10.2288, 85.276398], + [-61.180901, 8.8142, 76.996803], + [-39.719501, -28.927099, 88.963799], + [-14.8446, -52.6096, 140.113007], + [-15.2674, -62.32, 129.688004], + [-47.462502, -68.606102, 63.369701], + [-35.604198, -71.758003, 81.063904], + [-38.2486, -64.730202, 38.909901], + [-17.802099, -71.758003, 81.063904], + [-12.8928, -36.703499, 141.671997], + [-67.7668, -30.197001, 78.417801], + [-122.117996, -36.8894, 35.025002], + [-109.283997, -93.589897, 27.824301], + [-33.180698, 101.851997, 25.3186], + [-11.4565, -37.899399, 150.722], + [-70.236504, 62.836201, -3.9475], + [-28.7302, 91.731102, -24.972601], + [-47.263401, 54.293999, -27.414801], + [-69.167603, 6.5862, -12.7757], + [-28.7302, 49.1003, -48.3596], + [-31.903, 5.692, -47.821999], + [-35.075802, -34.432899, -16.280899], + [-115.284103, 48.681499, 48.684101], + [-110.842796, 28.4821, 49.176201] + ], + "chunks": [ + { + "color": [119, 57, 0], + "faces": [ + [0, 1, 2], + [2, 3, 0], + [4, 5, 2], + [6, 3, 2], + [2, 5, 6], + [7, 8, 9], + [10, 3, 6], + [10, 50, 7], + [7, 3, 10], + [7, 9, 3], + [49, 0, 9], + [3, 9, 0], + [53, 54, 55], + [55, 56, 53], + [57, 56, 55], + [58, 59, 55], + [55, 54, 58], + [60, 61, 62], + [63, 58, 54], + [63, 60, 89], + [60, 63, 54], + [60, 54, 61], + [88, 61, 53], + [54, 53, 61], + [2, 1, 4], + [55, 59, 57] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [36, 51, 67], + "faces": [ + [11, 12, 13], + [64, 65, 66] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [228, 116, 36], + "faces": [ + [14, 15, 11], + [11, 16, 14], + [17, 12, 18], + [41, 64, 37], + [67, 68, 66] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [192, 172, 157], + "faces": [ + [19, 20, 21], + [21, 22, 19], + [20, 19, 23], + [23, 24, 20], + [23, 25, 24], + [19, 22, 26], + [26, 27, 19], + [23, 28, 29], + [23, 29, 30], + [25, 23, 30], + [29, 51, 52], + [52, 30, 29], + [27, 26, 69], + [69, 70, 27], + [70, 71, 72], + [72, 27, 70], + [72, 71, 73], + [51, 74, 72], + [52, 51, 72], + [73, 52, 72], + [19, 27, 74], + [74, 28, 19], + [51, 29, 28], + [28, 74, 51], + [74, 27, 72], + [28, 23, 19] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [214, 194, 178], + "faces": [ + [21, 20, 24], + [24, 31, 21], + [69, 71, 70], + [71, 69, 75] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [228, 119, 25], + "faces": [ + [31, 24, 18], + [6, 5, 16], + [16, 17, 6], + [24, 32, 33], + [33, 34, 24], + [5, 4, 35], + [75, 68, 71], + [58, 67, 40], + [40, 59, 58], + [71, 76, 77], + [77, 78, 71] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [205, 98, 0], + "faces": [ + [24, 34, 18], + [16, 13, 12], + [12, 17, 16], + [13, 16, 11], + [71, 68, 76], + [40, 67, 66], + [66, 65, 40], + [65, 64, 40] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [0, 0, 0], + "faces": [ + [36, 15, 37], + [37, 38, 36], + [31, 39, 22], + [22, 21, 31], + [31, 15, 36], + [36, 39, 31], + [75, 69, 26], + [26, 80, 75], + [75, 80, 38], + [38, 37, 75], + [38, 80, 39], + [39, 36, 38], + [39, 80, 26], + [26, 22, 39] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [247, 132, 25], + "faces": [ + [17, 33, 10], + [17, 18, 34], + [34, 33, 17], + [10, 6, 17], + [11, 15, 31], + [31, 18, 11], + [18, 12, 11], + [14, 16, 40], + [40, 41, 14], + [59, 5, 35], + [35, 79, 59], + [67, 63, 77], + [67, 77, 76], + [76, 68, 67], + [63, 67, 58], + [64, 68, 75], + [75, 37, 64], + [68, 64, 66], + [14, 41, 37], + [37, 15, 14], + [5, 59, 40], + [40, 16, 5] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + }, + { + "color": [225, 119, 25], + "faces": [ + [35, 4, 42], + [4, 1, 42], + [42, 43, 44], + [44, 35, 42], + [45, 43, 42], + [42, 10, 45], + [30, 32, 24], + [24, 25, 30], + [30, 33, 32], + [33, 30, 10], + [44, 43, 46], + [43, 45, 47], + [47, 46, 43], + [48, 47, 45], + [45, 30, 48], + [30, 45, 10], + [49, 42, 0], + [8, 7, 42], + [50, 42, 7], + [50, 10, 42], + [1, 0, 42], + [42, 9, 8], + [42, 49, 9], + [64, 41, 40], + [57, 59, 79], + [79, 81, 57], + [57, 81, 56], + [82, 79, 35], + [35, 44, 82], + [81, 79, 82], + [82, 83, 81], + [84, 63, 81], + [81, 83, 84], + [44, 46, 85], + [85, 82, 44], + [52, 73, 71], + [71, 78, 52], + [52, 78, 77], + [77, 63, 52], + [82, 85, 83], + [83, 85, 86], + [86, 84, 83], + [87, 52, 84], + [84, 86, 87], + [52, 63, 84], + [88, 53, 81], + [62, 81, 60], + [89, 60, 81], + [89, 81, 63], + [56, 81, 53], + [81, 62, 61], + [81, 61, 88], + [48, 87, 86], + [86, 47, 48], + [47, 86, 85], + [85, 46, 47], + [48, 30, 52], + [52, 87, 48] + ], + "overlay": { + "mask": "overlay-mask", + "color": [128, 0, 0] + } + } + ], + "gradients": { + "mask-gradient": { + "gradientUnits": "userSpaceOnUse", + "type": "linear", + "x1": "50%", + "x2": "50%", + "y1": "0%", + "y2": "100%", + "stops": [ + { + "stop-color": "#FFFFFF" + }, + { + "offset": "1", + "stop-color": "#000000" + } + ] + } + }, + "masks": { + "overlay-mask": { + "color": "url('#mask-gradient')" + } + } +} diff --git a/demo/src/overlay/overlay.js b/demo/src/overlay/overlay.js new file mode 100644 index 00000000..4b4757c5 --- /dev/null +++ b/demo/src/overlay/overlay.js @@ -0,0 +1,21 @@ +const copy = require('copy-to-clipboard'); +const createViewer = require('../../..'); +const { svgElementToSvgImageContent } = require('../../../util'); +const meshJson = require('./overlay-fox.json'); + +document.addEventListener('keypress', function (event) { + if (event.keyCode === 99) { + // the c key + const svg = document.querySelector('svg'); + const content = svgElementToSvgImageContent(svg); + copy(content); + } +}); + +createViewer({ + width: 0.4, + height: 0.4, + followMouse: true, + followMotion: true, + meshJson, +}); diff --git a/util.js b/util.js index dbbf9adc..1a2829cf 100644 --- a/util.js +++ b/util.js @@ -301,14 +301,22 @@ function positionsFromModel(positions, modelJson) { function createPolygonsFromModelJson(modelJson, createSvgPolygon) { const polygons = []; const polygonsByChunk = modelJson.chunks.map((chunk, index) => { - const { faces } = chunk; + const { faces, overlay } = chunk; return faces.map((face) => { const svgPolygon = createSvgPolygon(chunk, { gradients: modelJson.gradients, index, masks: modelJson.masks, }); - const polygon = new Polygon(svgPolygon, face); + let overlaySvgPolygon; + if (overlay) { + overlaySvgPolygon = createSvgPolygon(overlay, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + } + const polygon = new Polygon(svgPolygon, face, overlaySvgPolygon); polygons.push(polygon); return polygon; }); @@ -483,7 +491,6 @@ function createFaceUpdater(container, polygons, transformed) { const points = []; let zmax = -Infinity; let zmin = Infinity; - const element = poly.svg; for (let j = 0; j < 3; ++j) { const idx = indices[j]; points.push( @@ -499,14 +506,21 @@ function createFaceUpdater(container, polygons, transformed) { const joinedPoints = points.join(' '); if (joinedPoints.indexOf('NaN') === -1) { - setAttribute(element, 'points', joinedPoints); + setAttribute(poly.svg, 'points', joinedPoints); + if (poly.overlaySvg) { + setAttribute(poly.overlaySvg, 'points', joinedPoints); + } } toDraw.push(poly); } toDraw.sort(compareZ); - const newPolygons = toDraw.map((poly) => poly.svg); + // Polygons must be set in z-index order with the overlay SVG drawn last, so that it covers + // the non-overlay SVG. + const newPolygons = toDraw + .map((poly) => [poly.svg, ...(poly.overlaySvg ? [poly.overlaySvg] : [])]) + .flat(); const defs = container.getElementsByTagName('defs'); const maskChildren = container.getElementsByTagName('mask'); if (container.replaceChildren) { @@ -555,10 +569,21 @@ function svgElementToSvgImageContent(svgElement) { return content; } -function Polygon(svg, indices) { +/** + * A single polygon from the JSON model. + * + * @param {Element} svg - The SVG element representing this polygon. + * @param {[number, number, number]} indices - The position IDs for the three vertices of the polygon. + * @param {Element} [overlaySvg] - The overlay SVG element representing this polygon. If present, + * this is rendered over the non-overlay SVG. + */ +function Polygon(svg, indices, overlaySvg) { this.svg = svg; this.indices = indices; this.zIndex = 0; + if (overlaySvg) { + this.overlaySvg = overlaySvg; + } } /**