diff --git a/beta/beta-fox.json b/beta/beta-fox.json new file mode 100644 index 00000000..4aa23a3f --- /dev/null +++ b/beta/beta-fox.json @@ -0,0 +1,830 @@ +{ + "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": [ + { + "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], + [2, 1, 4] + ], + "name": "left ear", + "gradient": "left-ear-gradient" + }, + { + "faces": [ + [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], + [55, 59, 57] + ], + "name": "right ear", + "gradient": "right-ear-gradient" + }, + { + "color": [22, 22, 22], + "faces": [[11, 12, 13]], + "name": "left eye" + }, + { + "color": [22, 22, 22], + "faces": [[64, 65, 66]], + "name": "right eye" + }, + { + "faces": [ + [14, 15, 11], + [11, 16, 14] + ], + "name": "left inner eye", + "gradient": "left-inner-eye-gradient" + }, + { + "faces": [[17, 12, 18]], + "name": "left outer eye", + "gradient": "left-outer-eye-gradient" + }, + { + "faces": [[41, 64, 37]], + "name": "right lower inner eye", + "gradient": "right-inner-eye-gradient" + }, + { + "faces": [[67, 68, 66]], + "name": "right outer eye", + "gradient": "right-outer-eye-gradient" + }, + { + "color": [192, 173, 158], + "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] + ], + "name": "lower chin" + }, + { + "color": [215, 193, 179], + "faces": [ + [21, 20, 24], + [24, 31, 21] + ], + "name": "left lower snout" + }, + { + "color": [215, 193, 179], + "faces": [ + [69, 71, 70], + [71, 69, 75] + ], + "name": "right lower snout" + }, + { + "faces": [[31, 24, 18]], + "name": "left upper snout", + "gradient": "left-upper-snout-gradient" + }, + { + "faces": [ + [6, 5, 16], + [16, 17, 6] + ], + "name": "left forehead", + "gradient": "left-forehead-gradient" + }, + { + "faces": [ + [24, 32, 33], + [33, 34, 24] + ], + "name": "left lower cheek", + "gradient": "left-lower-cheek-gradient" + }, + { + "faces": [[5, 4, 35]], + "name": "left top ear", + "gradient": "left-top-ear-gradient" + }, + { + "faces": [[75, 68, 71]], + "name": "right upper snout", + "gradient": "right-upper-snout-gradient" + }, + { + "faces": [ + [58, 67, 40], + [40, 59, 58] + ], + "name": "right forhead", + "gradient": "right-forehead-gradient" + }, + { + "faces": [ + [71, 76, 77], + [77, 78, 71] + ], + "name": "right lower cheek", + "gradient": "right-lower-cheek-gradient" + }, + { + "faces": [[24, 34, 18]], + "name": "left middle cheek", + "gradient": "left-middle-cheek-gradient" + }, + { + "color": [35, 151, 119], + "faces": [ + [16, 13, 12], + [12, 17, 16], + [13, 16, 11] + ], + "name": "left above eye" + }, + { + "faces": [[71, 68, 76]], + "name": "right middle cheek", + "gradient": "right-middle-cheek-gradient" + }, + { + "color": [35, 151, 119], + "faces": [ + [40, 67, 66], + [66, 65, 40], + [65, 64, 40] + ], + "name": "right above eye" + }, + { + "color": [22, 22, 22], + "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] + ], + "name": "nose" + }, + { + "faces": [ + [17, 33, 10], + [17, 18, 34], + [34, 33, 17], + [10, 6, 17] + ], + "name": "left upper cheek", + "gradient": "left-upper-cheek-gradient" + }, + { + "faces": [ + [11, 15, 31], + [31, 18, 11], + [18, 12, 11] + ], + "name": "left below eye", + "gradient": "left-below-eye-gradient" + }, + { + "faces": [ + [14, 16, 40], + [40, 41, 14], + [59, 5, 35], + [35, 79, 59], + [14, 41, 37], + [37, 15, 14], + [5, 59, 40], + [40, 16, 5] + ], + "name": "forehead", + "gradient": "forehead-gradient" + }, + { + "faces": [ + [67, 63, 77], + [67, 77, 76], + [76, 68, 67], + [63, 67, 58] + ], + "name": "right upper cheek", + "gradient": "right-upper-cheek-gradient" + }, + { + "faces": [ + [64, 68, 75], + [75, 37, 64], + [68, 64, 66] + ], + "name": "right below eye", + "gradient": "right-below-eye-gradient" + }, + { + "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], + [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] + ], + "name": "back", + "gradient": "back-gradient" + }, + { + "faces": [[57, 59, 79]], + "name": "right top ear", + "gradient": "right-top-ear-gradient" + }, + { + "faces": [[64, 41, 40]], + "name": "right inner upper eye", + "gradient": "right-inner-eye-gradient" + } + ], + "gradients": { + "forehead-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#23FE4A" + }, + { + "offset": 1, + "stop-color": "#BAD8EF" + } + ], + "x1": "50%", + "y1": "20.232164948453608%", + "x2": "50%", + "y2": "74.87123711340206%", + "gradientUnits": "userSpaceOnUse" + }, + "right-upper-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#20B475" + }, + { + "offset": 1, + "stop-color": "#70BDCE" + } + ], + "x1": "77.19501199040768%", + "y1": "44.68123711340206%", + "x2": "77.19501199040768%", + "y2": "68.2861855670103%", + "gradientUnits": "userSpaceOnUse" + }, + "left-upper-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#20B475" + }, + { + "offset": 1, + "stop-color": "#70BDCE" + } + ], + "x1": "22.820719424460435%", + "y1": "44.68123711340206%", + "x2": "22.820719424460435%", + "y2": "68.2861855670103%", + "gradientUnits": "userSpaceOnUse" + }, + "right-below-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#85BBE1" + }, + { + "offset": 1, + "stop-color": "#7CCACA" + } + ], + "x1": "54.34676258992806%", + "y1": "68.26917525773197%", + "x2": "65.3001438848921%", + "y2": "68.26917525773197%", + "gradientUnits": "userSpaceOnUse" + }, + "left-below-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#7CCACA" + }, + { + "offset": 1, + "stop-color": "#85BBE1" + } + ], + "x1": "34.731223021582736%", + "y1": "68.26917525773197%", + "x2": "45.65323741007194%", + "y2": "68.26917525773197%", + "gradientUnits": "userSpaceOnUse" + }, + "right-ear-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#074F1E" + }, + { + "offset": 0.4286, + "stop-color": "#05541C" + }, + { + "offset": 0.62, + "stop-color": "#006A13" + }, + { + "offset": 1, + "stop-color": "#007514" + } + ], + "x1": "61.443549160671466%", + "y1": "44.51773195876289%", + "x2": "93.802206235012%", + "y2": "24.439072164948456%", + "gradientUnits": "userSpaceOnUse" + }, + "left-ear-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#074F1E" + }, + { + "offset": 0.4286, + "stop-color": "#05541C" + }, + { + "offset": 0.62, + "stop-color": "#006A13" + }, + { + "offset": 1, + "stop-color": "#007514" + } + ], + "x1": "32.7432134292566%", + "y1": "44.33329896907217%", + "x2": "4.853390887290168%", + "y2": "19.18181443298969%", + "gradientUnits": "userSpaceOnUse" + }, + "left-outer-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#43C3A2" + }, + { + "offset": 1, + "stop-color": "#4FAFC0" + }, + { + "offset": 1, + "stop-color": "#4FAFC0" + } + ], + "x1": "27.575539568345324%", + "y1": "60.519278350515464%", + "x2": "34.982350119904076%", + "y2": "60.519278350515464%", + "gradientUnits": "userSpaceOnUse" + }, + "right-outer-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#4FAFC0" + }, + { + "offset": 1, + "stop-color": "#43C3A2" + } + ], + "x1": "65.01764988009592%", + "y1": "60.519278350515464%", + "x2": "72.42446043165468%", + "y2": "60.519278350515464%", + "gradientUnits": "userSpaceOnUse" + }, + "right-lower-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#59ADCB" + }, + { + "offset": 1, + "stop-color": "#436CC8" + } + ], + "x1": "77.93247002398083%", + "y1": "68.15113402061857%", + "x2": "77.93247002398083%", + "y2": "86.82577319587631%", + "gradientUnits": "userSpaceOnUse" + }, + "left-lower-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#59ADCB" + }, + { + "offset": 1, + "stop-color": "#436CC8" + } + ], + "x1": "22.083165467625896%", + "y1": "68.15113402061857%", + "x2": "22.083165467625896%", + "y2": "86.82577319587631%", + "gradientUnits": "userSpaceOnUse" + }, + "left-top-ear-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#0ED54A" + }, + { + "offset": 1, + "stop-color": "#0ED54A" + } + ], + "x1": "13.954513189448441%", + "y1": "22.055670103092787%", + "x2": "44.146762589928066%", + "y2": "22.055670103092787%", + "gradientUnits": "userSpaceOnUse" + }, + "right-top-ear-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#0ED54A" + }, + { + "offset": 1, + "stop-color": "#11EB36" + } + ], + "x1": "55.85333333333334%", + "y1": "22.055670103092787%", + "x2": "86.04556354916068%", + "y2": "22.055670103092787%", + "gradientUnits": "userSpaceOnUse" + }, + "left-forehead-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#15DC5D" + }, + { + "offset": 1, + "stop-color": "#48CA9F" + } + ], + "x1": "36.3947242206235%", + "y1": "34.11144329896908%", + "x2": "36.3947242206235%", + "y2": "53.59649484536083%", + "gradientUnits": "userSpaceOnUse" + }, + "right-forehead-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#15DC5D" + }, + { + "offset": 1, + "stop-color": "#48CA9F" + } + ], + "x1": "63.6052757793765%", + "y1": "34.11144329896908%", + "x2": "63.6052757793765%", + "y2": "53.59649484536083%", + "gradientUnits": "userSpaceOnUse" + }, + "left-upper-snout-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#54A8CF" + }, + { + "offset": 1, + "stop-color": "#5393E3" + } + ], + "x1": "38.829736211031175%", + "y1": "68.28865979381443%", + "x2": "38.829736211031175%", + "y2": "81.55670103092784%", + "gradientUnits": "userSpaceOnUse" + }, + "right-upper-snout-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#54A8CF" + }, + { + "offset": 1, + "stop-color": "#5393E3" + } + ], + "x1": "61.17026378896883%", + "y1": "68.28865979381443%", + "x2": "61.17026378896883%", + "y2": "81.55670103092784%", + "gradientUnits": "userSpaceOnUse" + }, + "right-middle-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#32819D" + }, + { + "offset": 0.3363, + "stop-color": "#447DCD" + } + ], + "x1": "69.9137649880096%", + "y1": "51.063505154639174%", + "x2": "69.9137649880096%", + "y2": "85.81041237113402%", + "gradientUnits": "userSpaceOnUse" + }, + "left-middle-cheek-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#32819D" + }, + { + "offset": 0.3363, + "stop-color": "#447DCD" + } + ], + "x1": "30.086330935251798%", + "y1": "68.15092783505153%", + "x2": "30.086330935251798%", + "y2": "81.55752577319588%", + "gradientUnits": "userSpaceOnUse" + }, + "right-inner-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#53A9CB" + }, + { + "offset": 1, + "stop-color": "#44C0A6" + } + ], + "x1": "55.38244604316547%", + "y1": "74.87123711340206%", + "x2": "55.38244604316547%", + "y2": "53.59659793814433%", + "gradientUnits": "userSpaceOnUse" + }, + "left-inner-eye-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#53A9CB" + }, + { + "offset": 1, + "stop-color": "#44C0A6" + } + ], + "x1": "43.58177458033573%", + "y1": "64.2339175257732%", + "x2": "45.65323741007194%", + "y2": "64.2339175257732%", + "gradientUnits": "userSpaceOnUse" + }, + "back-gradient": { + "type": "linear", + "stops": [ + { + "stop-color": "#27FC4E" + }, + { + "offset": 1, + "stop-color": "#446FC9" + } + ], + "x1": "50%", + "y1": "0%", + "x2": "50%", + "y2": "100%", + "gradientUnits": "userSpaceOnUse" + } + } +} diff --git a/beta/bundle.js b/beta/bundle.js new file mode 100644 index 00000000..4bd1c786 --- /dev/null +++ b/beta/bundle.js @@ -0,0 +1,2471 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":13}],6:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],8:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":6}],9:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],12:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],13:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],14:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"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]); diff --git a/beta/index.html b/beta/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/beta/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/distort/bundle.js b/distort/bundle.js new file mode 100644 index 00000000..00ee187d --- /dev/null +++ b/distort/bundle.js @@ -0,0 +1,1593 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const button = document.createElement('button'); + button.innerText = description; + button.addEventListener('click', () => { + applyDistortion = distortionFn; + }); + document.body.appendChild(button); + }); + + const renderFox = createModelRenderer(container, cameraDistance, modelObj); + const renderScene = (lookCurrent, slowDrift) => { + const rect = container.getBoundingClientRect(); + applyDistortion(positions, origPositions); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +// glitch up and down +function distortGlitch(positions, origPositions) { + const pointCount = positions.length / 3; + for (let polygonIndex = 0; polygonIndex < pointCount; polygonIndex++) { + const x = polygonIndex * 3 + 0; + const y = polygonIndex * 3 + 1; + const z = polygonIndex * 3 + 2; + // strong along x + positions[x] = origPositions[x] + 20 * getSinIntensity() * Math.random(); + positions[y] = origPositions[y] + 20 * getSinIntensity() * Math.random(); + positions[z] = origPositions[z] + 20 * getSinIntensity() * Math.random(); + } +} + +// bug: grow head slowly? +function distortGrow(positions, origPositions) { + const progress = getSinIntensity(); + const pointCount = positions.length / 3; + const polygonProgressWidth = 1 / pointCount; + for (let polygonIndex = 0; polygonIndex < pointCount; polygonIndex++) { + // calculate the current progress for each polygon + const polygonProgressStart = polygonIndex * polygonProgressWidth; + const polygonProgressEnd = polygonProgressStart + polygonProgressWidth; + const polygonProgressUncapped = + (progress - polygonProgressStart) / + (polygonProgressEnd - polygonProgressStart); + const polygonProgress = Math.min(Math.max(polygonProgressUncapped, 0), 1); + // the previous polygon (self referential for the first one) + const prevPolygonIndex = Math.max(polygonIndex, polygonIndex - 1); + const prevX = origPositions[prevPolygonIndex * (3 + 0)]; + const prevY = origPositions[prevPolygonIndex * (3 + 1)]; + const prevZ = origPositions[prevPolygonIndex * (3 + 2)]; + const x = polygonIndex * (3 + 0); + const y = polygonIndex * (3 + 1); + const z = polygonIndex * (3 + 2); + // strong along x + positions[x] = prevX + polygonProgress * origPositions[x]; + positions[y] = prevY + polygonProgress * origPositions[y]; + positions[z] = prevZ + polygonProgress * origPositions[z]; + } +} + +// bug: grow head slowly? +function distortFold(positions, origPositions) { + const progress = getSinIntensity(5000); + const pointCount = positions.length / 3; + const polygonProgressWidth = 1 / pointCount; + // reset positions + Object.assign(positions, origPositions); + for (let polygonIndex = 0; polygonIndex < pointCount; polygonIndex++) { + // calculate the current progress for each polygon + const polygonProgressStart = polygonIndex * polygonProgressWidth; + const polygonProgressEnd = polygonProgressStart + polygonProgressWidth; + const polygonProgressUncapped = + (progress - polygonProgressStart) / + (polygonProgressEnd - polygonProgressStart); + const polygonProgress = Math.min(Math.max(polygonProgressUncapped, 0), 1); + // the previous polygon (self referential for the first one) + const prevPolygonIndex = Math.max(0, polygonIndex - 1); + const prevX = positions[prevPolygonIndex * (3 + 0)]; + const prevY = positions[prevPolygonIndex * (3 + 1)]; + const prevZ = positions[prevPolygonIndex * (3 + 2)]; + const x = polygonIndex * (3 + 0); + const y = polygonIndex * (3 + 1); + const z = polygonIndex * (3 + 2); + // console.log(polygonIndex, polygonProgress) + positions[x] = prevX + polygonProgress * (origPositions[x] - prevX); + positions[y] = prevY + polygonProgress * (origPositions[y] - prevY); + positions[z] = prevZ + polygonProgress * (origPositions[z] - prevZ); + } +} + +// sin between 0-1 +function getSinIntensity(speed = 1000) { + return (Math.sin(Date.now() / speed) + 1) / 2; +} + +},{"../../../fox.json":2,"../../../util":10}],2:[function(require,module,exports){ +module.exports={ + "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] + ] + }, + { + "color": [36, 51, 67], + "faces": [ + [11, 12, 13], + [64, 65, 66] + ] + }, + { + "color": [228, 116, 36], + "faces": [ + [14, 15, 11], + [11, 16, 14], + [17, 12, 18], + [41, 64, 37], + [67, 68, 66] + ] + }, + { + "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] + ] + }, + { + "color": [214, 194, 178], + "faces": [ + [21, 20, 24], + [24, 31, 21], + [69, 71, 70], + [71, 69, 75] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + } + ] +} + +},{}],3:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],4:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],5:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":3}],6:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],8:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],9:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],10:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"gl-mat4/invert":4,"gl-mat4/lookAt":5,"gl-mat4/multiply":6,"gl-mat4/perspective":7,"gl-mat4/rotate":8,"gl-vec3/transformMat4":9}]},{},[1]); diff --git a/distort/index.html b/distort/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/distort/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/fade/bundle.js b/fade/bundle.js new file mode 100644 index 00000000..dee023d9 --- /dev/null +++ b/fade/bundle.js @@ -0,0 +1,1997 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":13}],6:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],8:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":6}],9:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],12:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],13:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],14:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"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]); diff --git a/fade/fade-fox.json b/fade/fade-fox.json new file mode 100644 index 00000000..4f755129 --- /dev/null +++ b/fade/fade-fox.json @@ -0,0 +1,356 @@ +{ + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "color": [72, 102, 134], + "faces": [ + [11, 12, 13], + [64, 65, 66] + ], + "name": "eyes" + }, + { + "color": [228, 116, 36], + "faces": [ + [14, 15, 11], + [11, 16, 14], + [17, 12, 18], + [41, 64, 37], + [67, 68, 66] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "color": [214, 194, 178], + "faces": [ + [21, 20, 24], + [24, 31, 21], + [69, 71, 70], + [71, 69, 75] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + }, + { + "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] + ], + "mask": "everything-but-eyes-mask" + } + ], + "gradients": { + "mask-gradient": { + "gradientUnits": "userSpaceOnUse", + "type": "linear", + "x1": "50%", + "x2": "50%", + "y1": "150%", + "y2": "0%", + "stops": [ + { + "stop-color": "#FFFFFF" + }, + { + "offset": "1", + "stop-color": "#000000" + } + ] + } + }, + "masks": { + "everything-but-eyes-mask": { + "color": "url('#mask-gradient')" + } + } +} diff --git a/fade/index.html b/fade/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/fade/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/flask/bundle.js b/flask/bundle.js new file mode 100644 index 00000000..aa69d68d --- /dev/null +++ b/flask/bundle.js @@ -0,0 +1,2465 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":13}],6:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],8:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":6}],9:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],12:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],13:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],14:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"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]); diff --git a/flask/flask-fox.json b/flask/flask-fox.json new file mode 100644 index 00000000..bc42656e --- /dev/null +++ b/flask/flask-fox.json @@ -0,0 +1,824 @@ +{ + "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": [ + { + "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], + [2, 1, 4] + ], + "name": "left ear", + "gradient": "left-ear-gradient" + }, + { + "faces": [ + [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], + [55, 59, 57] + ], + "name": "right ear", + "gradient": "right-ear-gradient" + }, + { + "color": [22, 22, 22], + "faces": [[11, 12, 13]], + "name": "left eye" + }, + { + "color": [22, 22, 22], + "faces": [[64, 65, 66]], + "name": "right eye" + }, + { + "faces": [ + [14, 15, 11], + [11, 16, 14] + ], + "name": "left inner eye", + "gradient": "left-inner-eye-gradient" + }, + { + "faces": [[17, 12, 18]], + "name": "left outer eye", + "gradient": "left-outer-eye-gradient" + }, + { + "faces": [[41, 64, 37]], + "name": "right lower inner eye", + "gradient": "right-inner-eye-gradient" + }, + { + "faces": [[67, 68, 66]], + "name": "right outer eye", + "gradient": "right-outer-eye-gradient" + }, + { + "color": [223, 117, 84], + "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] + ], + "name": "lower chin" + }, + { + "color": [255, 159, 90], + "faces": [ + [21, 20, 24], + [24, 31, 21] + ], + "name": "left lower snout" + }, + { + "color": [255, 159, 90], + "faces": [ + [69, 71, 70], + [71, 69, 75] + ], + "name": "right lower snout" + }, + { + "color": [147, 131, 250], + "faces": [[31, 24, 18]], + "name": "left upper snout" + }, + { + "faces": [ + [6, 5, 16], + [16, 17, 6] + ], + "name": "left forehead", + "gradient": "left-forehead-gradient" + }, + { + "faces": [ + [24, 32, 33], + [33, 34, 24] + ], + "name": "left lower cheek", + "gradient": "left-lower-cheek-gradient" + }, + { + "faces": [[5, 4, 35]], + "name": "left top ear", + "gradient": "left-top-ear-gradient" + }, + { + "color": [147, 131, 250], + "faces": [[75, 68, 71]], + "name": "right upper snout" + }, + { + "faces": [ + [58, 67, 40], + [40, 59, 58] + ], + "name": "right forhead", + "gradient": "right-forehead-gradient" + }, + { + "faces": [ + [71, 76, 77], + [77, 78, 71] + ], + "name": "right lower cheek", + "gradient": "right-lower-cheek-gradient" + }, + { + "faces": [[24, 34, 18]], + "name": "left middle cheek", + "gradient": "left-middle-cheek-gradient" + }, + { + "color": [156, 90, 221], + "faces": [ + [16, 13, 12], + [12, 17, 16], + [13, 16, 11] + ], + "name": "left above eye" + }, + { + "faces": [[71, 68, 76]], + "name": "right middle cheek", + "gradient": "right-middle-cheek-gradient" + }, + { + "color": [156, 90, 221], + "faces": [ + [40, 67, 66], + [66, 65, 40], + [65, 64, 40] + ], + "name": "right above eye" + }, + { + "color": [22, 22, 22], + "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] + ], + "name": "nose" + }, + { + "faces": [ + [17, 33, 10], + [17, 18, 34], + [34, 33, 17], + [10, 6, 17] + ], + "name": "left upper cheek", + "gradient": "left-upper-cheek-gradient" + }, + { + "faces": [ + [11, 15, 31], + [31, 18, 11], + [18, 12, 11] + ], + "name": "left below eye", + "gradient": "left-below-eye-gradient" + }, + { + "faces": [ + [14, 16, 40], + [40, 41, 14], + [59, 5, 35], + [35, 79, 59], + [14, 41, 37], + [37, 15, 14], + [5, 59, 40], + [40, 16, 5] + ], + "name": "forehead", + "gradient": "forehead-gradient" + }, + { + "faces": [ + [67, 63, 77], + [67, 77, 76], + [76, 68, 67], + [63, 67, 58] + ], + "name": "right upper cheek", + "gradient": "right-upper-cheek-gradient" + }, + { + "faces": [ + [64, 68, 75], + [75, 37, 64], + [68, 64, 66] + ], + "name": "right below eye", + "gradient": "right-below-eye-gradient" + }, + { + "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], + [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] + ], + "name": "back", + "gradient": "back-gradient" + }, + { + "faces": [[57, 59, 79]], + "name": "right top ear", + "gradient": "right-top-ear-gradient" + }, + { + "faces": [[64, 41, 40]], + "name": "right inner upper eye", + "gradient": "right-inner-eye-gradient" + } + ], + "gradients": { + "left-inner-eye-gradient": { + "type": "linear", + "x1": "41.97721822541966%", + "y1": "67.79239690721649%", + "x2": "44.56654676258992%", + "y2": "67.79239690721649%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BA86F3" + }, + { + "offset": "0.5281", + "stop-color": "#B786F4" + }, + { + "offset": "0.8987", + "stop-color": "#AE86F5" + }, + { + "offset": "1", + "stop-color": "#AA86F6" + } + ] + }, + "right-inner-eye-gradient": { + "type": "linear", + "x1": "56.72805755395684%", + "y1": "81.08904639175258%", + "x2": "56.72805755395684%", + "y2": "54.49574742268041%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BA86F3" + }, + { + "offset": "0.5281", + "stop-color": "#B786F4" + }, + { + "offset": "0.8987", + "stop-color": "#AE86F5" + }, + { + "offset": "1", + "stop-color": "#AA86F6" + } + ] + }, + "left-middle-cheek-gradient": { + "type": "linear", + "x1": "25.107913669064747%", + "y1": "72.68865979381442%", + "x2": "25.107913669064747%", + "y2": "89.44690721649484%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6848BA" + }, + { + "offset": "0.3363", + "stop-color": "#6356D5" + } + ] + }, + "right-middle-cheek-gradient": { + "type": "linear", + "x1": "74.89208633093526%", + "y1": "51.32938144329896%", + "x2": "74.89208633093526%", + "y2": "94.76301546391753%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6848BA" + }, + { + "offset": "0.3363", + "stop-color": "#6356D5" + } + ] + }, + "right-forehead-gradient": { + "type": "linear", + "x1": "67.00671462829736%", + "y1": "30.13930412371134%", + "x2": "67.00671462829736%", + "y2": "54.49561855670103%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#DC69E6" + }, + { + "offset": "1", + "stop-color": "#C289F3" + } + ] + }, + "left-forehead-gradient": { + "type": "linear", + "x1": "32.99340527577938%", + "y1": "30.13930412371134%", + "x2": "32.99340527577938%", + "y2": "54.49561855670103%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#DC69E6" + }, + { + "offset": "1", + "stop-color": "#C289F3" + } + ] + }, + "right-top-ear-gradient": { + "type": "linear", + "x1": "95.056858513189448%", + "y1": "15.06958762886598%", + "x2": "57.31654676258992%", + "y2": "15.06958762886598%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BB65ED" + }, + { + "offset": "1", + "stop-color": "#E560E3" + } + ] + }, + "left-top-ear-gradient": { + "type": "linear", + "x1": "4.943141486810552%", + "y1": "15.06958762886598%", + "x2": "42.68345323741008%", + "y2": "15.06958762886598%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BB65ED" + }, + { + "offset": "1", + "stop-color": "#E560E3" + } + ] + }, + "left-lower-cheek-gradient": { + "type": "linear", + "x1": "15.103956834532372%", + "y1": "72.6889175257732%", + "x2": "15.103956834532372%", + "y2": "96.03221649484537%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#906EF7" + }, + { + "offset": "1", + "stop-color": "#575ADE" + } + ] + }, + "right-lower-cheek-gradient": { + "type": "linear", + "x1": "84.91570743405276%", + "y1": "72.6889175257732%", + "x2": "84.91570743405276%", + "y2": "96.03221649484537%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#906EF7" + }, + { + "offset": "1", + "stop-color": "#575ADE" + } + ] + }, + "right-outer-eye-gradient": { + "type": "linear", + "x1": "68.7720623501199%", + "y1": "63.14909793814433%", + "x2": "78.03057553956835%", + "y2": "63.14909793814433%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BA86F3" + }, + { + "offset": "0.5281", + "stop-color": "#B786F4" + }, + { + "offset": "0.8987", + "stop-color": "#AE86F5" + }, + { + "offset": "1", + "stop-color": "#AA86F6" + } + ] + }, + "left-outer-eye-gradient": { + "type": "linear", + "x1": "21.969424460431654%", + "y1": "63.14909793814433%", + "x2": "31.227937649880094%", + "y2": "63.14909793814433%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#BA86F3" + }, + { + "offset": "0.5281", + "stop-color": "#B786F4" + }, + { + "offset": "0.8987", + "stop-color": "#AE86F5" + }, + { + "offset": "1", + "stop-color": "#AA86F6" + } + ] + }, + "left-ear-gradient": { + "type": "linear", + "x1": "50%", + "y1": "30%", + "x2": "4%", + "y2": "4%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#541758" + }, + { + "offset": "0.4286", + "stop-color": "#4F206C" + }, + { + "offset": "0.62", + "stop-color": "#4D2577" + }, + { + "offset": "1", + "stop-color": "#8B45B6" + } + ] + }, + "right-ear-gradient": { + "type": "linear", + "x1": "50%", + "y1": "30%", + "x2": "96%", + "y2": "4%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#541758" + }, + { + "offset": "0.4286", + "stop-color": "#4F206C" + }, + { + "offset": "0.62", + "stop-color": "#4D2577" + }, + { + "offset": "1", + "stop-color": "#8B45B6" + } + ] + }, + "left-below-eye-gradient": { + "type": "linear", + "x1": "30.914028776978412%", + "y1": "72.83646907216496%", + "x2": "44.56654676258992%", + "y2": "72.83646907216496%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#C8A8F7" + }, + { + "offset": "1", + "stop-color": "#BAAAFB" + } + ] + }, + "right-below-eye-gradient": { + "type": "linear", + "x1": "55.43345323741007%", + "y1": "72.83646907216496%", + "x2": "69.12517985611511%", + "y2": "72.83646907216496%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#C8A8F7" + }, + { + "offset": "1", + "stop-color": "#BAAAFB" + } + ] + }, + "left-upper-cheek-gradient": { + "type": "linear", + "x1": "16.02589928057554%", + "y1": "43.35154639175258%", + "x2": "16.02589928057554%", + "y2": "72.85773195876288%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#B65FE5" + }, + { + "offset": "1", + "stop-color": "#ADA2FC" + } + ] + }, + "right-upper-cheek-gradient": { + "type": "linear", + "x1": "83.99364508393285%", + "y1": "43.35154639175258%", + "x2": "83.99364508393285%", + "y2": "72.85773195876288%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#B65FE5" + }, + { + "offset": "1", + "stop-color": "#ADA2FC" + } + ] + }, + "forehead-gradient": { + "type": "linear", + "x1": "50%", + "y1": "12.790180412371136%", + "x2": "50%", + "y2": "81.08904639175258%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#FB7FE4" + }, + { + "offset": "1", + "stop-color": "#BCABFB" + } + ] + }, + "back-gradient": { + "type": "linear", + "x1": "50%", + "y1": "12.790180412371136%", + "x2": "50%", + "y2": "81.08904639175258%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#FB7FE4" + }, + { + "offset": "1", + "stop-color": "#5C5CE0" + } + ] + } + } +} diff --git a/flask/index.html b/flask/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/flask/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/gradient/bundle.js b/gradient/bundle.js new file mode 100644 index 00000000..be549743 --- /dev/null +++ b/gradient/bundle.js @@ -0,0 +1,1978 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":13}],6:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],8:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":6}],9:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],12:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],13:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],14:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"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]); diff --git a/gradient/gradient-fox.json b/gradient/gradient-fox.json new file mode 100644 index 00000000..9ea4f37b --- /dev/null +++ b/gradient/gradient-fox.json @@ -0,0 +1,337 @@ +{ + "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": [ + { + "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] + ], + "gradient": "linear1" + }, + { + "faces": [ + [11, 12, 13], + [64, 65, 66] + ], + "gradient": "linear1" + }, + { + "faces": [ + [14, 15, 11], + [11, 16, 14], + [17, 12, 18], + [41, 64, 37], + [67, 68, 66] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + }, + { + "faces": [ + [21, 20, 24], + [24, 31, 21], + [69, 71, 70], + [71, 69, 75] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + }, + { + "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] + ], + "gradient": "linear1" + } + ], + "gradients": { + "linear1": { + "gradientUnits": "userSpaceOnUse", + "type": "linear", + "stops": [ + { + "stop-color": "#fbaee3" + }, + { + "offset": "1", + "stop-color": "#ffd982" + } + ] + } + } +} diff --git a/gradient/index.html b/gradient/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/gradient/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/index.html b/index.html new file mode 100644 index 00000000..cd06d596 --- /dev/null +++ b/index.html @@ -0,0 +1,25 @@ + + + + + MetaMask Logo + + + + +

Try these demos.

+
    +
  1. normal
  2. +
  3. gradient
  4. +
  5. recolor
  6. +
  7. distort
  8. +
  9. fade
  10. +
  11. beta
  12. +
  13. flask
  14. +
  15. mmi
  16. +
+ + diff --git a/mmi/bundle.js b/mmi/bundle.js new file mode 100644 index 00000000..ef6ad926 --- /dev/null +++ b/mmi/bundle.js @@ -0,0 +1,2465 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":3,"./util":14}],5:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":13}],6:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],8:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":6}],9:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],12:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],13:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],14:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"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]); diff --git a/mmi/index.html b/mmi/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/mmi/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/mmi/mmi-fox.json b/mmi/mmi-fox.json new file mode 100644 index 00000000..763257c4 --- /dev/null +++ b/mmi/mmi-fox.json @@ -0,0 +1,824 @@ +{ + "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": [ + { + "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], + [2, 1, 4] + ], + "name": "left ear", + "gradient": "left-ear-gradient" + }, + { + "faces": [ + [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], + [55, 59, 57] + ], + "name": "right ear", + "gradient": "right-ear-gradient" + }, + { + "color": [22, 22, 22], + "faces": [[11, 12, 13]], + "name": "left eye" + }, + { + "color": [22, 22, 22], + "faces": [[64, 65, 66]], + "name": "right eye" + }, + { + "faces": [ + [14, 15, 11], + [11, 16, 14] + ], + "name": "left inner eye", + "gradient": "left-inner-eye-gradient" + }, + { + "faces": [[17, 12, 18]], + "name": "left outer eye", + "gradient": "left-outer-eye-gradient" + }, + { + "faces": [[41, 64, 37]], + "name": "right lower inner eye", + "gradient": "right-inner-eye-gradient" + }, + { + "faces": [[67, 68, 66]], + "name": "right outer eye", + "gradient": "right-outer-eye-gradient" + }, + { + "color": [215, 193, 179], + "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] + ], + "name": "lower chin" + }, + { + "color": [215, 193, 179], + "faces": [ + [21, 20, 24], + [24, 31, 21] + ], + "name": "left lower snout" + }, + { + "color": [215, 193, 179], + "faces": [ + [69, 71, 70], + [71, 69, 75] + ], + "name": "right lower snout" + }, + { + "color": [109, 149, 249], + "faces": [[31, 24, 18]], + "name": "left upper snout" + }, + { + "faces": [ + [6, 5, 16], + [16, 17, 6] + ], + "name": "left forehead", + "gradient": "left-forehead-gradient" + }, + { + "faces": [ + [24, 32, 33], + [33, 34, 24] + ], + "name": "left lower cheek", + "gradient": "left-lower-cheek-gradient" + }, + { + "faces": [[5, 4, 35]], + "name": "left top ear", + "gradient": "left-top-ear-gradient" + }, + { + "color": [109, 149, 249], + "faces": [[75, 68, 71]], + "name": "right upper snout" + }, + { + "faces": [ + [58, 67, 40], + [40, 59, 58] + ], + "name": "right forhead", + "gradient": "right-forehead-gradient" + }, + { + "faces": [ + [71, 76, 77], + [77, 78, 71] + ], + "name": "right lower cheek", + "gradient": "right-lower-cheek-gradient" + }, + { + "faces": [[24, 34, 18]], + "name": "left middle cheek", + "gradient": "left-middle-cheek-gradient" + }, + { + "color": [73, 123, 248], + "faces": [ + [16, 13, 12], + [12, 17, 16], + [13, 16, 11] + ], + "name": "left above eye" + }, + { + "faces": [[71, 68, 76]], + "name": "right middle cheek", + "gradient": "right-middle-cheek-gradient" + }, + { + "color": [73, 123, 248], + "faces": [ + [40, 67, 66], + [66, 65, 40], + [65, 64, 40] + ], + "name": "right above eye" + }, + { + "color": [22, 22, 22], + "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] + ], + "name": "nose" + }, + { + "faces": [ + [17, 33, 10], + [17, 18, 34], + [34, 33, 17], + [10, 6, 17] + ], + "name": "left upper cheek", + "gradient": "left-upper-cheek-gradient" + }, + { + "faces": [ + [11, 15, 31], + [31, 18, 11], + [18, 12, 11] + ], + "name": "left below eye", + "gradient": "left-below-eye-gradient" + }, + { + "faces": [ + [14, 16, 40], + [40, 41, 14], + [59, 5, 35], + [35, 79, 59], + [14, 41, 37], + [37, 15, 14], + [5, 59, 40], + [40, 16, 5] + ], + "name": "forehead", + "gradient": "forehead-gradient" + }, + { + "faces": [ + [67, 63, 77], + [67, 77, 76], + [76, 68, 67], + [63, 67, 58] + ], + "name": "right upper cheek", + "gradient": "right-upper-cheek-gradient" + }, + { + "faces": [ + [64, 68, 75], + [75, 37, 64], + [68, 64, 66] + ], + "name": "right below eye", + "gradient": "right-below-eye-gradient" + }, + { + "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], + [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] + ], + "name": "back", + "gradient": "back-gradient" + }, + { + "faces": [[57, 59, 79]], + "name": "right top ear", + "gradient": "right-top-ear-gradient" + }, + { + "faces": [[64, 41, 40]], + "name": "right inner upper eye", + "gradient": "right-inner-eye-gradient" + } + ], + "gradients": { + "left-inner-eye-gradient": { + "type": "linear", + "x1": "41.97721822541966%", + "y1": "67.79239690721649%", + "x2": "44.56654676258992%", + "y2": "67.79239690721649%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.5281", + "stop-color": "#6D95F9" + }, + { + "offset": "0.8987", + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "right-inner-eye-gradient": { + "type": "linear", + "x1": "56.72805755395684%", + "y1": "81.08904639175258%", + "x2": "56.72805755395684%", + "y2": "54.49574742268041%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.5281", + "stop-color": "#6D95F9" + }, + { + "offset": "0.8987", + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-middle-cheek-gradient": { + "type": "linear", + "x1": "25.107913669064747%", + "y1": "72.68865979381442%", + "x2": "25.107913669064747%", + "y2": "89.44690721649484%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#497BF8" + }, + { + "offset": "0.3363", + "stop-color": "#497BF8" + } + ] + }, + "right-middle-cheek-gradient": { + "type": "linear", + "x1": "74.89208633093526%", + "y1": "51.32938144329896%", + "x2": "74.89208633093526%", + "y2": "94.76301546391753%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.3363", + "stop-color": "#497BF8" + } + ] + }, + "right-forehead-gradient": { + "type": "linear", + "x1": "67.00671462829736%", + "y1": "30.13930412371134%", + "x2": "67.00671462829736%", + "y2": "54.49561855670103%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-forehead-gradient": { + "type": "linear", + "x1": "32.99340527577938%", + "y1": "30.13930412371134%", + "x2": "32.99340527577938%", + "y2": "54.49561855670103%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "right-top-ear-gradient": { + "type": "linear", + "x1": "95.056858513189448%", + "y1": "15.06958762886598%", + "x2": "57.31654676258992%", + "y2": "15.06958762886598%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-top-ear-gradient": { + "type": "linear", + "x1": "4.943141486810552%", + "y1": "15.06958762886598%", + "x2": "42.68345323741008%", + "y2": "15.06958762886598%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-lower-cheek-gradient": { + "type": "linear", + "x1": "15.103956834532372%", + "y1": "72.6889175257732%", + "x2": "15.103956834532372%", + "y2": "96.03221649484537%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "right-lower-cheek-gradient": { + "type": "linear", + "x1": "84.91570743405276%", + "y1": "72.6889175257732%", + "x2": "84.91570743405276%", + "y2": "96.03221649484537%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "right-outer-eye-gradient": { + "type": "linear", + "x1": "68.7720623501199%", + "y1": "63.14909793814433%", + "x2": "78.03057553956835%", + "y2": "63.14909793814433%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.5281", + "stop-color": "#6D95F9" + }, + { + "offset": "0.8987", + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-outer-eye-gradient": { + "type": "linear", + "x1": "21.969424460431654%", + "y1": "63.14909793814433%", + "x2": "31.227937649880094%", + "y2": "63.14909793814433%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.5281", + "stop-color": "#6D95F9" + }, + { + "offset": "0.8987", + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-ear-gradient": { + "type": "linear", + "x1": "50%", + "y1": "30%", + "x2": "4%", + "y2": "4%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.4286", + "stop-color": "#2C56DD" + }, + { + "offset": "0.62", + "stop-color": "#2C56DD" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "right-ear-gradient": { + "type": "linear", + "x1": "50%", + "y1": "30%", + "x2": "96%", + "y2": "4%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "0.4286", + "stop-color": "#2C56DD" + }, + { + "offset": "0.62", + "stop-color": "#2C56DD" + }, + { + "offset": "1", + "stop-color": "#6D95F9" + } + ] + }, + "left-below-eye-gradient": { + "type": "linear", + "x1": "30.914028776978412%", + "y1": "72.83646907216496%", + "x2": "44.56654676258992%", + "y2": "72.83646907216496%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "right-below-eye-gradient": { + "type": "linear", + "x1": "55.43345323741007%", + "y1": "72.83646907216496%", + "x2": "69.12517985611511%", + "y2": "72.83646907216496%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "left-upper-cheek-gradient": { + "type": "linear", + "x1": "16.02589928057554%", + "y1": "43.35154639175258%", + "x2": "16.02589928057554%", + "y2": "72.85773195876288%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#B6CAFC" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "right-upper-cheek-gradient": { + "type": "linear", + "x1": "83.99364508393285%", + "y1": "43.35154639175258%", + "x2": "83.99364508393285%", + "y2": "72.85773195876288%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#B6CAFC" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "forehead-gradient": { + "type": "linear", + "x1": "50%", + "y1": "12.790180412371136%", + "x2": "50%", + "y2": "81.08904639175258%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#B6CAFC" + }, + { + "offset": "1", + "stop-color": "#B6CAFC" + } + ] + }, + "back-gradient": { + "type": "linear", + "x1": "50%", + "y1": "12.790180412371136%", + "x2": "50%", + "y2": "81.08904639175258%", + "gradientUnits": "userSpaceOnUse", + "stops": [ + { + "stop-color": "#6D95F9" + }, + { + "offset": "1", + "stop-color": "#5C5CE0" + } + ] + } + } +} diff --git a/normal/bundle.js b/normal/bundle.js new file mode 100644 index 00000000..1b3003d2 --- /dev/null +++ b/normal/bundle.js @@ -0,0 +1,1637 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +},{"./fox.json":2,"./util":13}],4:[function(require,module,exports){ +'use strict'; + +var deselectCurrent = require('toggle-selection'); + +var defaultMessage = 'Copy to clipboard: #{key}, Enter'; + +function format(message) { + var copyKey = (/mac os x/i.test(navigator.userAgent) ? '⌘' : 'Ctrl') + '+C'; + return message.replace(/#{\s*key\s*}/g, copyKey); +} + +function copy(text, options) { + var debug, message, reselectPrevious, range, selection, mark, success = false; + if (!options) { options = {}; } + debug = options.debug || false; + try { + reselectPrevious = deselectCurrent(); + + range = document.createRange(); + selection = document.getSelection(); + + mark = document.createElement('span'); + mark.textContent = text; + // reset user styles for span element + mark.style.all = 'unset'; + // prevents scrolling to the end of the page + mark.style.position = 'fixed'; + mark.style.top = 0; + mark.style.clip = 'rect(0, 0, 0, 0)'; + // used to preserve spaces and line breaks + mark.style.whiteSpace = 'pre'; + // do not inherit user-select (it may be `none`) + mark.style.webkitUserSelect = 'text'; + mark.style.MozUserSelect = 'text'; + mark.style.msUserSelect = 'text'; + mark.style.userSelect = 'text'; + + document.body.appendChild(mark); + + range.selectNode(mark); + selection.addRange(range); + + var successful = document.execCommand('copy'); + if (!successful) { + throw new Error('copy command was unsuccessful'); + } + success = true; + } catch (err) { + debug && console.error('unable to copy using execCommand: ', err); + debug && console.warn('trying IE specific stuff'); + try { + window.clipboardData.setData('text', text); + success = true; + } catch (err) { + debug && console.error('unable to copy using clipboardData: ', err); + debug && console.error('falling back to prompt'); + message = format('message' in options ? options.message : defaultMessage); + window.prompt(message, text); + } + } finally { + if (selection) { + if (typeof selection.removeRange == 'function') { + selection.removeRange(range); + } else { + selection.removeAllRanges(); + } + } + + if (mark) { + document.body.removeChild(mark); + } + reselectPrevious(); + } + + return success; +} + +module.exports = copy; + +},{"toggle-selection":12}],5:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],6:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],7:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":5}],8:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],9:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],10:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],11:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],12:[function(require,module,exports){ + +module.exports = function () { + var selection = document.getSelection(); + if (!selection.rangeCount) { + return function () {}; + } + var active = document.activeElement; + + var ranges = []; + for (var i = 0; i < selection.rangeCount; i++) { + ranges.push(selection.getRangeAt(i)); + } + + switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML + case 'INPUT': + case 'TEXTAREA': + active.blur(); + break; + + default: + active = null; + break; + } + + selection.removeAllRanges(); + return function () { + selection.type === 'Caret' && + selection.removeAllRanges(); + + if (!selection.rangeCount) { + ranges.forEach(function(range) { + selection.addRange(range); + }); + } + + active && + active.focus(); + }; +}; + +},{}],13:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"gl-mat4/invert":6,"gl-mat4/lookAt":7,"gl-mat4/multiply":8,"gl-mat4/perspective":9,"gl-mat4/rotate":10,"gl-vec3/transformMat4":11}]},{},[1]); diff --git a/normal/index.html b/normal/index.html new file mode 100644 index 00000000..2077c88e --- /dev/null +++ b/normal/index.html @@ -0,0 +1,14 @@ + + + + + --- + + + + +
+ +

Press C to copy the SVG text of the image.

+ + diff --git a/recolor/bundle.js b/recolor/bundle.js new file mode 100644 index 00000000..25ffc3e2 --- /dev/null +++ b/recolor/bundle.js @@ -0,0 +1,1798 @@ +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i recolor(modelObj), 500); + cycling = true; + } +} + +document.addEventListener('keypress', (event) => { + if (event.keyCode === 99) { + // the C key + saveImage(); + } +}); + +function saveImage() { + const svg = document.querySelector('svg'); + const content = svgElementToSvgImageContent(svg); + download(content, `custom-fox-${colorSeed}.svg`, 'image/svg+xml'); +} + +const viewer = createRecolorLogo({ + width: 0.4, + height: 0.4, + followMouse: true, + followMotion: true, +}); + +const foxDiv = document.querySelector('body div.fox'); +foxDiv.appendChild(viewer.container); + +function createRecolorLogo(options) { + const cameraDistance = options.cameraDistance || 400; + const { height, width } = calculateSizingOptions(options); + + const container = createNode('svg'); + setAttribute(container, 'width', `${width}px`); + setAttribute(container, 'height', `${height}px`); + document.body.appendChild(container); + + const modelObj = loadModelFromJson(foxJson); + const renderFox = createModelRenderer(container, cameraDistance, modelObj); + const renderScene = (lookCurrent, slowDrift) => { + const rect = container.getBoundingClientRect(); + renderFox(rect, lookCurrent, slowDrift); + }; + + const saveButton = document.querySelector('button.save'); + saveButton.addEventListener('click', saveImage); + const recolorButton = document.querySelector('button.recolor'); + recolorButton.addEventListener('click', () => recolor(modelObj)); + const cycleButton = document.querySelector('button.cycle'); + cycleButton.addEventListener('click', () => toggleCycle(modelObj)); + + return createLogoViewer( + container, + renderScene, + Object.assign({ cameraDistance }, options), + ); +} + +function recolor(modelObj) { + colorSeed = pickColorSeed(); + const twister = new MersenneTwister(colorSeed); + const { polygonsByChunk } = modelObj; + for (const polygons of polygonsByChunk) { + const randomIndex = Math.floor(twister.random() * colors.length); + const color = colors[randomIndex]; + for (const polygon of polygons) { + setAttribute(polygon.svg, 'fill', color); + setAttribute(polygon.svg, 'stroke', color); + } + } + viewer.renderCurrentScene(); +} + +function pickColorSeed() { + return Math.floor(Math.random() * 10000000); +} + +// Function to download data to a file +function download(data, filename, type) { + const file = new Blob([data], { type }); + // IE10+ + if (window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveOrOpenBlob(file, filename); + } else { + // Others + const a = document.createElement('a'); + const url = URL.createObjectURL(file); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + setTimeout(function () { + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }, 0); + } +} + +},{"../../../fox.json":2,"../../../util":11,"mersenne-twister":10}],2:[function(require,module,exports){ +module.exports={ + "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] + ] + }, + { + "color": [36, 51, 67], + "faces": [ + [11, 12, 13], + [64, 65, 66] + ] + }, + { + "color": [228, 116, 36], + "faces": [ + [14, 15, 11], + [11, 16, 14], + [17, 12, 18], + [41, 64, 37], + [67, 68, 66] + ] + }, + { + "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] + ] + }, + { + "color": [214, 194, 178], + "faces": [ + [21, 20, 24], + [24, 31, 21], + [69, 71, 70], + [71, 69, 75] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + }, + { + "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] + ] + } + ] +} + +},{}],3:[function(require,module,exports){ +module.exports = identity; + +/** + * Set a mat4 to the identity matrix + * + * @param {mat4} out the receiving matrix + * @returns {mat4} out + */ +function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 1; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = 1; + out[11] = 0; + out[12] = 0; + out[13] = 0; + out[14] = 0; + out[15] = 1; + return out; +}; +},{}],4:[function(require,module,exports){ +module.exports = invert; + +/** + * Inverts a mat4 + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the source matrix + * @returns {mat4} out + */ +function invert(out, a) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return out; +}; +},{}],5:[function(require,module,exports){ +var identity = require('./identity'); + +module.exports = lookAt; + +/** + * Generates a look-at matrix with the given eye position, focal point, and up axis + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {vec3} eye Position of the viewer + * @param {vec3} center Point the viewer is looking at + * @param {vec3} up vec3 pointing up + * @returns {mat4} out + */ +function lookAt(out, eye, center, up) { + var x0, x1, x2, y0, y1, y2, z0, z1, z2, len, + eyex = eye[0], + eyey = eye[1], + eyez = eye[2], + upx = up[0], + upy = up[1], + upz = up[2], + centerx = center[0], + centery = center[1], + centerz = center[2]; + + if (Math.abs(eyex - centerx) < 0.000001 && + Math.abs(eyey - centery) < 0.000001 && + Math.abs(eyez - centerz) < 0.000001) { + return identity(out); + } + + z0 = eyex - centerx; + z1 = eyey - centery; + z2 = eyez - centerz; + + len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); + z0 *= len; + z1 *= len; + z2 *= len; + + x0 = upy * z2 - upz * z1; + x1 = upz * z0 - upx * z2; + x2 = upx * z1 - upy * z0; + len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); + if (!len) { + x0 = 0; + x1 = 0; + x2 = 0; + } else { + len = 1 / len; + x0 *= len; + x1 *= len; + x2 *= len; + } + + y0 = z1 * x2 - z2 * x1; + y1 = z2 * x0 - z0 * x2; + y2 = z0 * x1 - z1 * x0; + + len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); + if (!len) { + y0 = 0; + y1 = 0; + y2 = 0; + } else { + len = 1 / len; + y0 *= len; + y1 *= len; + y2 *= len; + } + + out[0] = x0; + out[1] = y0; + out[2] = z0; + out[3] = 0; + out[4] = x1; + out[5] = y1; + out[6] = z1; + out[7] = 0; + out[8] = x2; + out[9] = y2; + out[10] = z2; + out[11] = 0; + out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); + out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); + out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); + out[15] = 1; + + return out; +}; +},{"./identity":3}],6:[function(require,module,exports){ +module.exports = multiply; + +/** + * Multiplies two mat4's + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the first operand + * @param {mat4} b the second operand + * @returns {mat4} out + */ +function multiply(out, a, b) { + var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; + + // Cache only the current line of the second matrix + var b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; + out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; + out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + + b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; + out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30; + out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31; + out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32; + out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33; + return out; +}; +},{}],7:[function(require,module,exports){ +module.exports = perspective; + +/** + * Generates a perspective projection matrix with the given bounds + * + * @param {mat4} out mat4 frustum matrix will be written into + * @param {number} fovy Vertical field of view in radians + * @param {number} aspect Aspect ratio. typically viewport width/height + * @param {number} near Near bound of the frustum + * @param {number} far Far bound of the frustum + * @returns {mat4} out + */ +function perspective(out, fovy, aspect, near, far) { + var f = 1.0 / Math.tan(fovy / 2), + nf = 1 / (near - far); + out[0] = f / aspect; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = f; + out[6] = 0; + out[7] = 0; + out[8] = 0; + out[9] = 0; + out[10] = (far + near) * nf; + out[11] = -1; + out[12] = 0; + out[13] = 0; + out[14] = (2 * far * near) * nf; + out[15] = 0; + return out; +}; +},{}],8:[function(require,module,exports){ +module.exports = rotate; + +/** + * Rotates a mat4 by the given angle + * + * @param {mat4} out the receiving matrix + * @param {mat4} a the matrix to rotate + * @param {Number} rad the angle to rotate the matrix by + * @param {vec3} axis the axis to rotate around + * @returns {mat4} out + */ +function rotate(out, a, rad, axis) { + var x = axis[0], y = axis[1], z = axis[2], + len = Math.sqrt(x * x + y * y + z * z), + s, c, t, + a00, a01, a02, a03, + a10, a11, a12, a13, + a20, a21, a22, a23, + b00, b01, b02, + b10, b11, b12, + b20, b21, b22; + + if (Math.abs(len) < 0.000001) { return null; } + + len = 1 / len; + x *= len; + y *= len; + z *= len; + + s = Math.sin(rad); + c = Math.cos(rad); + t = 1 - c; + + a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; + a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; + a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; + + // Construct the elements of the rotation matrix + b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; + b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; + b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; + + // Perform rotation-specific matrix multiplication + out[0] = a00 * b00 + a10 * b01 + a20 * b02; + out[1] = a01 * b00 + a11 * b01 + a21 * b02; + out[2] = a02 * b00 + a12 * b01 + a22 * b02; + out[3] = a03 * b00 + a13 * b01 + a23 * b02; + out[4] = a00 * b10 + a10 * b11 + a20 * b12; + out[5] = a01 * b10 + a11 * b11 + a21 * b12; + out[6] = a02 * b10 + a12 * b11 + a22 * b12; + out[7] = a03 * b10 + a13 * b11 + a23 * b12; + out[8] = a00 * b20 + a10 * b21 + a20 * b22; + out[9] = a01 * b20 + a11 * b21 + a21 * b22; + out[10] = a02 * b20 + a12 * b21 + a22 * b22; + out[11] = a03 * b20 + a13 * b21 + a23 * b22; + + if (a !== out) { // If the source and destination differ, copy the unchanged last row + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + return out; +}; +},{}],9:[function(require,module,exports){ +module.exports = transformMat4; + +/** + * Transforms the vec3 with a mat4. + * 4th vector component is implicitly '1' + * + * @param {vec3} out the receiving vector + * @param {vec3} a the vector to transform + * @param {mat4} m matrix to transform with + * @returns {vec3} out + */ +function transformMat4(out, a, m) { + var x = a[0], y = a[1], z = a[2], + w = m[3] * x + m[7] * y + m[11] * z + m[15] + w = w || 1.0 + out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w + out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w + out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w + return out +} +},{}],10:[function(require,module,exports){ +/* + https://github.com/banksean wrapped Makoto Matsumoto and Takuji Nishimura's code in a namespace + so it's better encapsulated. Now you can have multiple random number generators + and they won't stomp all over eachother's state. + + If you want to use this as a substitute for Math.random(), use the random() + method like so: + + var m = new MersenneTwister(); + var randomNumber = m.random(); + + You can also call the other genrand_{foo}() methods on the instance. + + If you want to use a specific seed in order to get a repeatable random + sequence, pass an integer into the constructor: + + var m = new MersenneTwister(123); + + and that will always produce the same random sequence. + + Sean McCullough (banksean@gmail.com) +*/ + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_seed(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + +var MersenneTwister = function(seed) { + if (seed == undefined) { + seed = new Date().getTime(); + } + + /* Period parameters */ + this.N = 624; + this.M = 397; + this.MATRIX_A = 0x9908b0df; /* constant vector a */ + this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ + this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ + + this.mt = new Array(this.N); /* the array for the state vector */ + this.mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */ + + if (seed.constructor == Array) { + this.init_by_array(seed, seed.length); + } + else { + this.init_seed(seed); + } +} + +/* initializes mt[N] with a seed */ +/* origin name init_genrand */ +MersenneTwister.prototype.init_seed = function(s) { + this.mt[0] = s >>> 0; + for (this.mti=1; this.mti>> 30); + this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + + this.mti; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + this.mt[this.mti] >>>= 0; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +MersenneTwister.prototype.init_by_array = function(init_key, key_length) { + var i, j, k; + this.init_seed(19650218); + i=1; j=0; + k = (this.N>key_length ? this.N : key_length); + for (; k; k--) { + var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30) + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + + init_key[j] + j; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=this.N-1; k; k--) { + var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) + - i; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; + if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } + } + + this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0,0xffffffff]-interval */ +/* origin name genrand_int32 */ +MersenneTwister.prototype.random_int = function() { + var y; + var mag01 = new Array(0x0, this.MATRIX_A); + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (this.mti >= this.N) { /* generate N words at one time */ + var kk; + + if (this.mti == this.N+1) /* if init_seed() has not been called, */ + this.init_seed(5489); /* a default initial seed is used */ + + for (kk=0;kk>> 1) ^ mag01[y & 0x1]; + } + for (;kk>> 1) ^ mag01[y & 0x1]; + } + y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); + this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + + /* Tempering */ + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); + + return y >>> 0; +} + +/* generates a random number on [0,0x7fffffff]-interval */ +/* origin name genrand_int31 */ +MersenneTwister.prototype.random_int31 = function() { + return (this.random_int()>>>1); +} + +/* generates a random number on [0,1]-real-interval */ +/* origin name genrand_real1 */ +MersenneTwister.prototype.random_incl = function() { + return this.random_int()*(1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/* generates a random number on [0,1)-real-interval */ +MersenneTwister.prototype.random = function() { + return this.random_int()*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on (0,1)-real-interval */ +/* origin name genrand_real3 */ +MersenneTwister.prototype.random_excl = function() { + return (this.random_int() + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on [0,1) with 53-bit resolution*/ +/* origin name genrand_res53 */ +MersenneTwister.prototype.random_long = function() { + var a=this.random_int()>>>5, b=this.random_int()>>>6; + return(a*67108864.0+b)*(1.0/9007199254740992.0); +} + +/* These real versions are due to Isaku Wada, 2002/01/09 added */ + +module.exports = MersenneTwister; + +},{}],11:[function(require,module,exports){ +const perspective = require('gl-mat4/perspective'); +const multiply = require('gl-mat4/multiply'); +const lookAt = require('gl-mat4/lookAt'); +const invert = require('gl-mat4/invert'); +const rotate = require('gl-mat4/rotate'); +const transform = require('gl-vec3/transformMat4'); + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +// Taken from https://github.com/yuzhe-han/ParentNode-replaceChildren +// This is to support browsers that do not yet support `replaceChildren` +const replaceChildrenPonyfill = function (...addNodes) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + + if (addNodes.length > 0) { + this.append(...addNodes); + } +}; + +module.exports = { + calculateSizingOptions, + createLogoViewer, + createModelRenderer, + loadModelFromJson, + positionsFromModel, + createPolygonsFromModelJson, + createStandardModelPolygon, + createMatrixComputer, + compareZ, + createFaceUpdater, + createNode, + setAttribute, + setGradientDefinitions, + setMaskDefinitions, + svgElementToSvgImageContent, + Polygon, +}; + +/** + * A distance measurement used for SVG attributes. A length is specified as a number followed by a + * unit identifier. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Content_type#length} for further + * information. + * + * @typedef {`${number}${'em' | 'ex' | 'px' | 'in' | 'cm' | 'mm' | 'pt' | 'pc' | '%'}`} SvgLength + */ + +/** + * A definition for a `` SVG element, which defines a color and the position for that color + * on a gradient. This element is always a child of either a `` or + * `` element. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop} for more information + * about the `` element. + * + * @typedef {object} StopDefinition + * @property {number | `${number}%`} [offset] - The location of the gradient stop along the + * gradient vector. + * @property {string} [stop-color] - The color of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/stop}. + * @property {number} [stop-opacity] - The opacity of the gradient stop. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stop-opacity}. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient} for more + * information about the `` element. + * + * @typedef {object} LinearGradientDefinition + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used. + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'linear'} type - The type of the gradient. + * @property {SvgLength} [x1] - The x coordinate of the starting point of the vector gradient. + * @property {SvgLength} [x2] - The x coordinate of the ending point of the vector gradient. + * @property {SvgLength} [y1] - The y coordinate of the starting point of the vector gradient. + * @property {SvgLength} [y2] - The y coordinate of the ending point of the vector gradient. + */ + +/** + * A definition for a `` SVG element. This definition includes all supported + * `` attributes, and it includes a `stops` property which is an array of + * definitions for each `` child node. + * + * See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/radialGradient} for more + * information about the `` element. + * + * @typedef {object} RadialGradientDefinition + * @property {SvgLength} [cx] - The x coordinate of the end circle of the radial gradiant. + * @property {SvgLength} [cy] - The y coordinate of the end circle of the radial gradient. + * @property {SvgLength} [fr] - The radius of the start circle of the radial gradient. + * @property {SvgLength} [fx] - The x coordinate of the start circle of the radial gradient. + * @property {SvgLength} [fy] - The y coordinate of the start circle of the radial gradient. + * @property {string} [gradientTransform] - A transform from the gradient coordinate system to the + * target coordinate system. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientTransform}. + * @property {'userSpaceOnUse' | 'objectBoundingBox'} [gradientUnits] - The coordinate system used + * for the coordinate attributes. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/gradientUnits}. + * @property {SvgLength} [r] - The radius of the end circle of the radial gradient. + * @property {'pad' | 'reflect' | 'repeat'} [spreadMethod] - The method used to fill a shape beyond + * the defined edges of a gradient. See {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/spreadMethod}. + * @property {StopDefinition[]} [stops] - The colors of the gradient, and the position of each + * color along the gradient vector. + * @property {'radial'} type - The type of the gradient. + */ + +function createLogoViewer( + container, + renderScene, + { + followMouse = false, + followMotion = false, + slowDrift = false, + lazyRender = true, + } = {}, +) { + let shouldRender = true; + const mouse = { + x: 0, + y: 0, + }; + const lookCurrent = [0, 0]; + const lookRate = 0.3; + + // closes over scene state + const renderCurrentScene = () => { + updateLookCurrent(); + renderScene(lookCurrent, slowDrift); + }; + + function setLookAtTarget(target) { + const bounds = container.getBoundingClientRect(); + mouse.x = 1.0 - (2.0 * (target.x - bounds.left)) / bounds.width; + mouse.y = 1.0 - (2.0 * (target.y - bounds.top)) / bounds.height; + } + + function stopAnimation() { + shouldRender = false; + } + + function startAnimation() { + shouldRender = true; + } + + function setFollowMouse(state) { + // eslint-disable-next-line no-param-reassign + followMouse = state; + } + + function setFollowMotion(state) { + // eslint-disable-next-line no-param-reassign + followMotion = state; + } + + window.addEventListener('mousemove', function (ev) { + if (!shouldRender) { + startAnimation(); + } + + if (followMouse) { + setLookAtTarget({ + x: ev.clientX, + y: ev.clientY, + }); + renderCurrentScene(); + } + }); + + window.addEventListener('deviceorientation', function (event) { + if (!shouldRender) { + startAnimation(); + } + + if (followMotion) { + // gamma: left to right + const leftToRight = event.gamma; + // beta: front back motion + const frontToBack = event.beta; + // x offset: needed to correct the intial position + const xOffset = 200; + // y offset: needed to correct the intial position + const yOffset = -300; + // acceleration + const acceleration = 10; + + setLookAtTarget({ + x: xOffset + leftToRight * acceleration, + y: yOffset + frontToBack * acceleration, + }); + renderCurrentScene(); + } + }); + + function lookAtAndRender(target) { + // update look target + setLookAtTarget(target); + // this should prolly just call updateLookCurrent or set lookCurrent values to eaxactly lookTarget + // but im not really sure why its different, so im leaving it alone + lookCurrent[0] = mouse.x; + lookCurrent[1] = mouse.y + 0.085 / lookRate; + renderCurrentScene(); + } + + function renderLoop() { + if (!shouldRender) { + return; + } + window.requestAnimationFrame(renderLoop); + renderCurrentScene(); + } + + function updateLookCurrent() { + const li = 1.0 - lookRate; + lookCurrent[0] = li * lookCurrent[0] + lookRate * mouse.x; + lookCurrent[1] = li * lookCurrent[1] + lookRate * mouse.y + 0.085; + } + + if (lazyRender) { + renderCurrentScene(); + } else { + renderLoop(); + } + + return { + container, + lookAt: setLookAtTarget, + setFollowMouse, + setFollowMotion, + stopAnimation, + startAnimation, + lookAtAndRender, + renderCurrentScene, + }; +} + +function loadModelFromJson( + modelJson, + createSvgPolygon = createStandardModelPolygon, +) { + const vertCount = modelJson.positions.length; + const positions = new Float32Array(3 * vertCount); + const transformed = new Float32Array(3 * vertCount); + const { polygons, polygonsByChunk } = createPolygonsFromModelJson( + modelJson, + createSvgPolygon, + ); + positionsFromModel(positions, modelJson); + const updatePositions = createPositionUpdater( + positions, + transformed, + vertCount, + ); + const modelObj = { + updatePositions, + positions, + transformed, + polygons, + polygonsByChunk, + }; + return modelObj; +} + +function createModelRenderer(container, cameraDistance, modelObj) { + const { updatePositions, transformed, polygons } = modelObj; + + for (const polygon of polygons) { + container.appendChild(polygon.svg); + } + + const computeMatrix = createMatrixComputer(cameraDistance); + const updateFaces = createFaceUpdater(container, polygons, transformed); + + return (rect, lookPos, slowDrift) => { + const matrix = computeMatrix(rect, lookPos, slowDrift); + updatePositions(matrix); + updateFaces(rect, container, polygons, transformed); + }; +} + +function positionsFromModel(positions, modelJson) { + const pp = modelJson.positions; + let ptr = 0; + for (let i = 0; i < pp.length; ++i) { + const p = pp[i]; + for (let j = 0; j < 3; ++j) { + positions[ptr] = p[j]; + ptr += 1; + } + } +} + +function createPolygonsFromModelJson(modelJson, createSvgPolygon) { + const polygons = []; + const polygonsByChunk = modelJson.chunks.map((chunk, index) => { + const { faces } = chunk; + return faces.map((face) => { + const svgPolygon = createSvgPolygon(chunk, { + gradients: modelJson.gradients, + index, + masks: modelJson.masks, + }); + const polygon = new Polygon(svgPolygon, face); + polygons.push(polygon); + return polygon; + }); + }); + return { polygons, polygonsByChunk }; +} + +/** + * Create an SVG ` element. + * + * This polygon is assigned the correct `fill` and `stroke` attributes, according to the chunk + * definition provided. But the `points` attribute is always set to a dummy value, as it gets reset + * later to the correct position during each render loop. + * + * @param {object} chunk - The definition for the chunk of the model this polygon is a part of. + * This includes the color or gradient to apply to the polygon. + * @param {object} options - Polygon options. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [options.gradients] - The set of + * all gradient definitions used in this model. + * @param options.index - The index for the chunk this polygon is found in. + * @returns {Element} The `` SVG element. + */ +function createStandardModelPolygon(chunk, { gradients = {}, index, masks }) { + const svgPolygon = createNode('polygon'); + + if (chunk.gradient && chunk.color) { + throw new Error( + `Both gradient and color for chunk '${index}'. These options are mutually exclusive.`, + ); + } else if (chunk.gradient) { + const gradientId = chunk.gradient; + if (!gradients[gradientId]) { + throw new Error(`Gradient ID not found: '${gradientId}'`); + } + + setAttribute(svgPolygon, 'fill', `url('#${gradientId}')`); + setAttribute(svgPolygon, 'stroke', `url('#${gradientId}')`); + } else { + const fill = + typeof chunk.color === 'string' ? chunk.color : `rgb(${chunk.color})`; + setAttribute(svgPolygon, 'fill', fill); + setAttribute(svgPolygon, 'stroke', fill); + } + + if (chunk.mask) { + if (!masks[chunk.mask]) { + throw new Error(`Mask ID not found: '${chunk.mask}'`); + } + setAttribute(svgPolygon, 'mask', `url('#${chunk.mask}')`); + } + + setAttribute(svgPolygon, 'points', '0,0, 10,0, 0,10'); + return svgPolygon; +} + +function createMatrixComputer(distance) { + const objectCenter = new Float32Array(3); + const up = new Float32Array([0, 1, 0]); + const projection = new Float32Array(16); + const model = new Float32Array(16); + const view = lookAt( + new Float32Array(16), + new Float32Array([0, 0, distance]), + objectCenter, + up, + ); + const invView = invert(new Float32Array(16), view); + const invProjection = new Float32Array(16); + const target = new Float32Array(3); + const transformedMatrix = new Float32Array(16); + + const X = new Float32Array([1, 0, 0]); + const Y = new Float32Array([0, 1, 0]); + const Z = new Float32Array([0, 0, 1]); + + return (rect, lookPos, slowDrift) => { + const viewportWidth = rect.width; + const viewportHeight = rect.height; + perspective( + projection, + Math.PI / 4.0, + viewportWidth / viewportHeight, + 100.0, + 1000.0, + ); + invert(invProjection, projection); + target[0] = lookPos[0]; + target[1] = lookPos[1]; + target[2] = 1.2; + transform(target, target, invProjection); + transform(target, target, invView); + lookAt(model, objectCenter, target, up); + + // this shouldnt operate directly on the matrix/model, + // it should likely operate on the lookPos + // if we do want to operate on the matrix/model, it shouldnt happen here + if (slowDrift) { + const time = Date.now() / 1000.0; + rotate(model, model, 0.1 + Math.sin(time / 3) * 0.2, X); + rotate(model, model, -0.1 + Math.sin(time / 2) * 0.03, Z); + rotate(model, model, 0.5 + Math.sin(time / 3) * 0.2, Y); + } + + multiply(transformedMatrix, projection, view); + multiply(transformedMatrix, transformedMatrix, model); + + return transformedMatrix; + }; +} + +function createPositionUpdater(positions, transformed, vertCount) { + return (M) => { + const m00 = M[0]; + const m01 = M[1]; + const m02 = M[2]; + const m03 = M[3]; + const m10 = M[4]; + const m11 = M[5]; + const m12 = M[6]; + const m13 = M[7]; + const m20 = M[8]; + const m21 = M[9]; + const m22 = M[10]; + const m23 = M[11]; + const m30 = M[12]; + const m31 = M[13]; + const m32 = M[14]; + const m33 = M[15]; + + for (let i = 0; i < vertCount; ++i) { + const x = positions[3 * i]; + const y = positions[3 * i + 1]; + const z = positions[3 * i + 2]; + + const tw = x * m03 + y * m13 + z * m23 + m33; + transformed[3 * i] = (x * m00 + y * m10 + z * m20 + m30) / tw; + transformed[3 * i + 1] = (x * m01 + y * m11 + z * m21 + m31) / tw; + transformed[3 * i + 2] = (x * m02 + y * m12 + z * m22 + m32) / tw; + } + }; +} + +function compareZ(a, b) { + return b.zIndex - a.zIndex; +} + +function createFaceUpdater(container, polygons, transformed) { + const toDraw = []; + return (rect) => { + let i; + const w = rect.width; + const h = rect.height; + toDraw.length = 0; + for (i = 0; i < polygons.length; ++i) { + const poly = polygons[i]; + const { indices } = poly; + + const i0 = indices[0]; + const i1 = indices[1]; + const i2 = indices[2]; + const ax = transformed[3 * i0]; + const ay = transformed[3 * i0 + 1]; + const bx = transformed[3 * i1]; + const by = transformed[3 * i1 + 1]; + const cx = transformed[3 * i2]; + const cy = transformed[3 * i2 + 1]; + const det = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax); + if (det < 0) { + continue; + } + + 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( + `${0.5 * w * (1.0 - transformed[3 * idx])},${ + 0.5 * h * (1.0 - transformed[3 * idx + 1]) + }`, + ); + const z = transformed[3 * idx + 2]; + zmax = Math.max(zmax, z); + zmin = Math.min(zmin, z); + } + poly.zIndex = zmax + 0.25 * zmin; + const joinedPoints = points.join(' '); + + if (joinedPoints.indexOf('NaN') === -1) { + setAttribute(element, 'points', joinedPoints); + } + + toDraw.push(poly); + } + toDraw.sort(compareZ); + + const newPolygons = toDraw.map((poly) => poly.svg); + const defs = container.getElementsByTagName('defs'); + const maskChildren = container.getElementsByTagName('mask'); + if (container.replaceChildren) { + container.replaceChildren(...defs, ...maskChildren, ...newPolygons); + } else { + replaceChildrenPonyfill.bind(container)( + ...defs, + ...maskChildren, + ...newPolygons, + ); + } + }; +} + +function calculateSizingOptions(options = {}) { + let width = options.width || 400; + let height = options.height || 400; + + if (!options.pxNotRatio) { + width = Math.floor(window.innerWidth * (options.width || 0.25)); + height = Math.floor(window.innerHeight * options.height || width); + + if ('minWidth' in options && width < options.minWidth) { + width = options.minWidth; + height = Math.floor((options.minWidth * options.height) / options.width); + } + } + return { width, height }; +} + +function createNode(type) { + return document.createElementNS(SVG_NS, type); +} + +function setAttribute(node, attribute, value) { + node.setAttributeNS(null, attribute, value); +} + +function svgElementToSvgImageContent(svgElement) { + const inner = svgElement.innerHTML; + const head = + ` ` + + ``; + const foot = ''; + const content = head + inner + foot; + return content; +} + +function Polygon(svg, indices) { + this.svg = svg; + this.indices = indices; + this.zIndex = 0; +} + +/** + * Parse gradient definitions and construct them in the DOM. + * + * Both `` and `` are supported. All gradients get added to a + * `` element that is added as a direct child of the container element. + * + * @param {Element} container - The `` HTML element that the definitions should be added to. + * @param {(LinearGradientDefinition | RadialGradientDefinition)[]} [gradients] - The gradient definitions. + */ +function setGradientDefinitions(container, gradients) { + if (!gradients || Object.keys(gradients).length === 0) { + return; + } + + const defsContainer = createNode('defs'); + + const linearCoordinateAttributes = ['x1', 'x2', 'y1', 'y2']; + const radialCoordinateAttributes = ['cx', 'cy', 'fr', 'fx', 'fy', 'r']; + const commonAttributes = [ + 'gradientTransform', + 'gradientUnits', + 'spreadMethod', + 'stops', + 'type', + ]; + const allLinearAttributes = [ + ...linearCoordinateAttributes, + ...commonAttributes, + ]; + const allRadialAttributes = [ + ...radialCoordinateAttributes, + ...commonAttributes, + ]; + + for (const [gradientId, gradientDefinition] of Object.entries(gradients)) { + let gradient; + if (gradientDefinition.type === 'linear') { + gradient = createNode('linearGradient'); + + const unsupportedLinearAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allLinearAttributes.includes(attribute), + ); + if (unsupportedLinearAttribute) { + throw new Error( + `Unsupported linear gradient attribute: '${unsupportedLinearAttribute}'`, + ); + } else if ( + linearCoordinateAttributes.some( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ) + ) { + const missingAttributes = linearCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] === undefined, + ); + if (missingAttributes.length > 0) { + throw new Error( + `Missing coordinate attributes: '${missingAttributes.join(', ')}'`, + ); + } + + for (const attribute of linearCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else if (gradientDefinition.type === 'radial') { + gradient = createNode('radialGradient'); + + const presentCoordinateAttributes = radialCoordinateAttributes.filter( + (attributeName) => gradientDefinition[attributeName] !== undefined, + ); + const unsupportedRadialAttribute = Object.keys(gradientDefinition).find( + (attribute) => !allRadialAttributes.includes(attribute), + ); + if (unsupportedRadialAttribute) { + throw new Error( + `Unsupported radial gradient attribute: '${unsupportedRadialAttribute}'`, + ); + } else if (presentCoordinateAttributes.length > 0) { + for (const attribute of presentCoordinateAttributes) { + if (typeof gradientDefinition[attribute] !== 'string') { + throw new Error( + `Type of '${attribute}' option expected to be 'string'. Instead received type '${typeof gradientDefinition[ + attribute + ]}'`, + ); + } + setAttribute(gradient, attribute, gradientDefinition[attribute]); + } + } + } else { + throw new Error( + `Unsupported gradient type: '${gradientDefinition.type}'`, + ); + } + + // Set common attributes + setAttribute(gradient, 'id', gradientId); + if (gradientDefinition.gradientUnits !== undefined) { + if ( + !['userSpaceOnUse', 'objectBoundingBox'].includes( + gradientDefinition.gradientUnits, + ) + ) { + throw new Error( + `Unrecognized value for 'gradientUnits' attribute: '${gradientDefinition.gradientUnits}'`, + ); + } + setAttribute(gradient, 'gradientUnits', gradientDefinition.gradientUnits); + } + + if (gradientDefinition.gradientTransform !== undefined) { + if (typeof gradientDefinition.gradientTransform !== 'string') { + throw new Error( + `Type of 'gradientTransform' option expected to be 'string'. Instead received type '${typeof gradientDefinition.gradientTransform}'`, + ); + } + + setAttribute( + gradient, + 'gradientTransform', + gradientDefinition.gradientTransform, + ); + } + + if (gradientDefinition.spreadMethod !== undefined) { + if ( + !['pad', 'reflect', 'repeat'].includes(gradientDefinition.spreadMethod) + ) { + throw new Error( + `Unrecognized value for 'spreadMethod' attribute: '${gradientDefinition.spreadMethod}'`, + ); + } + setAttribute(gradient, 'spreadMethod', gradientDefinition.spreadMethod); + } + + if (gradientDefinition.stops !== undefined) { + if (!Array.isArray(gradientDefinition.stops)) { + throw new Error(`The 'stop' attribute must be an array`); + } + + for (const stopDefinition of gradientDefinition.stops) { + if (typeof stopDefinition !== 'object') { + throw new Error( + `Each entry in the 'stop' attribute must be an object. Instead received type '${typeof stopDefinition}'`, + ); + } + const stop = createNode('stop'); + + if (stopDefinition.offset !== undefined) { + setAttribute(stop, 'offset', stopDefinition.offset); + } + + if (stopDefinition['stop-color'] !== undefined) { + setAttribute(stop, 'stop-color', stopDefinition['stop-color']); + } + + if (stopDefinition['stop-opacity'] !== undefined) { + setAttribute(stop, 'stop-opacity', stopDefinition['stop-opacity']); + } + + gradient.appendChild(stop); + } + } + + defsContainer.appendChild(gradient); + } + + container.appendChild(defsContainer); +} + +/** + * The properties of a single SVG mask. + * + * @typedef MaskDefinition + * @property {string} color - The color or gradient to apply to the mask. + */ + +/** + * Parse mask definitions and construct them in the DOM. + * + * The `` element contains a single rectangle that should cover the full extent of the SVG + * model. The color of this rectangle can be set to single color or a gradient. Anything the mask + * is applied to will be invisible if under a black pixel, visible if under a white pixel, and + * partially translucent if under a pixel that is between white and black. + * + * Later this could be extended to include custom paths and other shapes, rather than just a single + * rectangle. + * + * @param options - The mask options. + * @param {Element} options.container - The `` HTML element that the mask should be added to. + * @param {Record} [options.masks] - The gradient definitions. + * @param {number} options.height - The height of the SVG container. + * @param {number} options.width - The width of the SVG container. + */ +function setMaskDefinitions({ container, masks, height, width }) { + if (!masks || Object.keys(masks).length === 0) { + return; + } + + for (const [maskId, maskDefinition] of Object.entries(masks)) { + const mask = createNode('mask'); + setAttribute(mask, 'id', maskId); + + const maskedRect = createNode('rect'); + + // Extend mask beyond container to ensure it completely covers the model. + // The model can extend beyond the container as well. + setAttribute(maskedRect, 'width', width * 1.5); + setAttribute(maskedRect, 'height', height * 1.5); + setAttribute(maskedRect, 'x', `-${Math.floor(width / 4)}`); + setAttribute(maskedRect, 'y', `-${Math.floor(height / 4)}`); + + setAttribute(maskedRect, 'fill', maskDefinition.color); + mask.appendChild(maskedRect); + + container.appendChild(mask); + } +} + +},{"gl-mat4/invert":4,"gl-mat4/lookAt":5,"gl-mat4/multiply":6,"gl-mat4/perspective":7,"gl-mat4/rotate":8,"gl-vec3/transformMat4":9}]},{},[1]); diff --git a/recolor/index.html b/recolor/index.html new file mode 100644 index 00000000..83ec60ff --- /dev/null +++ b/recolor/index.html @@ -0,0 +1,17 @@ + + + + + MetaMask Logo + + + + +
+

Press C to save SVG

+ + + + + + \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 00000000..c6d71951 --- /dev/null +++ b/style.css @@ -0,0 +1,19 @@ +html, body{ + height: 100%; +} + +body{ + background: #202123; + text-align: center; + color: white; + font-family: roboto; +} + +div{ + height:140px; + overflow: visible; +} + +svg { + overflow: visible; +} \ No newline at end of file