+
Raða eftir
+
+ Nafni, stafrófsröð
+ Nafni, öfugri stafrófsröð
+ Útskrifarári, elsta fyrst
+ Útskrifarári, elsta seinast
+
+
Uppfæra
+
+
+
+
diff --git a/namsefni/36.functional/daemi/01.sorting-filtering/lib/helpers.js b/namsefni/36.functional/daemi/01.sorting-filtering/lib/helpers.js
new file mode 100644
index 0000000..0792b5b
--- /dev/null
+++ b/namsefni/36.functional/daemi/01.sorting-filtering/lib/helpers.js
@@ -0,0 +1,34 @@
+/**
+ * Býr til element með nafni og bætir við öðrum elementum eða texta nóðum.
+ * @param {string} name Nafn á elementi
+ * @param {...string | HTMLElement} children Önnur element eða strengir
+ * @returns {HTMLElement} Elementi með gefnum börnum
+ */
+export function el(name, ...children) {
+ const e = document.createElement(name);
+
+ for (const child of children) {
+ if (typeof child === 'string') {
+ e.appendChild(document.createTextNode(child));
+ } else if (child) {
+ e.appendChild(child);
+ }
+ }
+
+ return e;
+}
+
+/**
+ * Fjarlægir öll börn `element`.
+ * @param {HTMLElement} element Element sem á að tæma
+ * @returns {undefined} Skilar engu
+ */
+export function empty(element) {
+ if (!element || !element.firstChild) {
+ return;
+ }
+
+ while (element.firstChild) {
+ element.removeChild(element.firstChild);
+ }
+}
diff --git a/namsefni/36.functional/daemi/01.sorting-filtering/lib/ui.js b/namsefni/36.functional/daemi/01.sorting-filtering/lib/ui.js
new file mode 100644
index 0000000..aec3cac
--- /dev/null
+++ b/namsefni/36.functional/daemi/01.sorting-filtering/lib/ui.js
@@ -0,0 +1,18 @@
+import { el, empty } from './helpers.js';
+
+// Tekur inn lista af nemendum og birtir í HTML
+// Hreinsum og birtum allan lista aftur, virkar vel fyrir minni lista
+// fer að verða hægt við hundruði staka
+export function render(students, studentContainer) {
+ empty(studentContainer);
+ students.forEach((student) => {
+ const studentName = el('strong', student.name);
+ let studentGraduation = null;
+
+ if (student.graduation) {
+ studentGraduation = el('em', ` útskrifaðist ${student.graduation}`);
+ }
+ const studentElement = el('li', studentName, studentGraduation);
+ studentContainer.appendChild(studentElement);
+ });
+}
diff --git a/namsefni/36.functional/daemi/01.sorting-filtering/main.js b/namsefni/36.functional/daemi/01.sorting-filtering/main.js
new file mode 100644
index 0000000..4d80a3a
--- /dev/null
+++ b/namsefni/36.functional/daemi/01.sorting-filtering/main.js
@@ -0,0 +1,132 @@
+import { render } from './lib/ui.js';
+
+// Listi af nemendum
+// nöfn frá https://www.behindthename.com/
+const students = [
+ { name: 'Skadi Ferran Wang', graduation: 2020 },
+ { name: 'Tacita Ranjit McGill', graduation: 2019 },
+ { name: 'Heino Pavle Hornick' },
+ { name: 'Sima Gunda Babcock' },
+ { name: 'Shprintza Draco Hollands', graduation: 2000 },
+ { name: 'Melanie Gautstafr Obama', graduation: 2020 },
+ { name: 'Jirou Māui Dávid' },
+ { name: 'Heard Raginald McQueen', graduation: 2019 },
+ { name: 'Mani Altantsetseg Proudfoot', graduation: 2018 },
+ { name: 'Rubye Lea Schumacher' },
+ { name: 'Nicu Guilherme Yamashita', graduation: 2016 },
+ { name: 'Vilhelmo Kaapo Zahariev', graduation: 2005 },
+ { name: 'Junichi Klara Nakai' },
+ { name: 'Ulf Edward Mac Uileagóid', graduation: 2009 },
+ { name: 'Mirela Eadmund Cardoso ', graduation: 2010 },
+ { name: 'Lidziya Thorben Noyer', graduation: 2020 },
+ { name: 'Åse Abdelaziz Narváez', graduation: 2017 },
+ { name: 'Ramon Violeta Van Houten', graduation: 2015 },
+ { name: 'Jehovah Domitianus Janz' },
+ { name: 'Umukoro Karesinda Dallas ', graduation: 2018 },
+ { name: 'Kobe Mikaela Stankiewicz' },
+ { name: 'Jeong-Ho Ema Ó Donndubháin', graduation: 2019 },
+ { name: 'Meir Calixtus Sampson', graduation: 2000 },
+ { name: 'Beate Dušan Bauers' },
+ { name: 'Mikael Saam Donne ', graduation: 2020 },
+];
+
+// Þar sem við „geymum nemendur“
+const studentContainer = document.querySelector('ul');
+
+/**
+ * Tvær leiðir til að nálgast:
+ * 1. Gera allt strax, um leið og smellt í checkbox eða skrifaður texti þá
+ * breytum við viðmóti með nýjum gögnum
+ * 2. Bregðast við þegar form er „submittað“
+ * Í þessu dæmi gerum við ráð fyrir báðu, getum slökkt á því að fáum strax
+ * uppfærslu með `immediateFeedback`.
+ */
+const immediateFeedback = true;
+
+const formEl = document.querySelector('form');
+const searchEl = document.querySelector('input[name=search]');
+const hideGraduatedEl = document.querySelector('input[name=hideGraduated]');
+const hideNotGraduatedEl = document.querySelector('input[name=hideNotGraduated]');
+const sortEl = document.querySelector('select');
+
+// Finnur út hver staða á formi sé og skilar á formi sem `filterStudents` og
+// `sortStudents` kann á
+function formState() {
+ const search = searchEl.value;
+ const hideGraduated = hideGraduatedEl.checked;
+ const hideNotGraduated = hideNotGraduatedEl.checked;
+ const sort = sortEl.value;
+
+ return {
+ search, hideGraduated, hideNotGraduated, sort,
+ };
+}
+
+function filterStudents(
+ students,
+ { search, hideGraduated, hideNotGraduated } = {}
+) {
+ // Nokkrar leiðir að þessu, við förum ekki þá „bestu“
+ return students
+ // Burt með útskrifaða ef svo ber við
+ .filter((student) => hideGraduated ? !student.graduation : true)
+
+ // Burt með óútskrifaða ef svo ber við
+ .filter((student) => hideNotGraduated ? student.graduation : true)
+
+ // Finnum alla sem leit á við
+ .reduce((previous, current) => {
+ // Viljum leita eftir litlum stöfum sérstaklega
+ const matchesSearch = search.length > 0 ?
+ current.name.toLocaleLowerCase().indexOf(search.toLocaleLowerCase()) >= 0 : true;
+ return previous.concat(matchesSearch ? current : null);
+ }, [])
+ // Burt með öll null gildi
+ .filter(Boolean);
+}
+
+function sortStudents(students, sort) {
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sorting_non-ascii_characters
+ let sortFunc = (a, b) => a.name.localeCompare(b.name);
+
+ switch (sort) {
+ case 'graduation':
+ // Túlkum óútskrifaða með hæsta mögulega svo þau komi seinast
+ sortFunc = (a, b) => (a.graduation || 9999) - (b.graduation || 9999);
+ break;
+ case 'graduation-reverse':
+ // Túlkum óútskrifaða með lægsta mögulega svo þau komi seinast
+ sortFunc = (a, b) => (b.graduation || 0) - (a.graduation || 0);
+ break;
+ case 'name-reverse':
+ sortFunc = (a, b) => b.name.localeCompare(a.name);
+ break;
+ case 'name':
+ default:
+ }
+
+ return students.sort(sortFunc);
+}
+
+function renderBasedOnFormState() {
+ const state = formState();
+ const filteredStudents = filterStudents(students, state);
+ const filteredSortedStudents = sortStudents(filteredStudents, state.sort);
+ render(filteredSortedStudents, studentContainer);
+}
+
+formEl.addEventListener('submit', (e) => {
+ e.preventDefault();
+ renderBasedOnFormState()
+});
+
+if (immediateFeedback) {
+ // Ef við viljum leita strax, tengjum við öll input!
+ searchEl.addEventListener('input', renderBasedOnFormState)
+ hideGraduatedEl.addEventListener('input', renderBasedOnFormState)
+ hideNotGraduatedEl.addEventListener('input', renderBasedOnFormState)
+ sortEl.addEventListener('input', renderBasedOnFormState)
+}
+
+// Í byrjun renderum við allt, í default röðun
+render(sortStudents(students, 'name'), studentContainer);
diff --git a/namsefni/36.functional/daemi/01.sorting-filtering/package.json b/namsefni/36.functional/daemi/01.sorting-filtering/package.json
new file mode 100644
index 0000000..9f3466f
--- /dev/null
+++ b/namsefni/36.functional/daemi/01.sorting-filtering/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "01.sorting-filtering",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "dev": "browser-sync start --server --files main.js **/*.js styles.css index.html"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "browser-sync": "^2.27.7"
+ }
+}
diff --git a/namsefni/36.functional/daemi/02.alphabet.js b/namsefni/36.functional/daemi/02.alphabet.js
new file mode 100644
index 0000000..6cb138a
--- /dev/null
+++ b/namsefni/36.functional/daemi/02.alphabet.js
@@ -0,0 +1,1220 @@
+/* eslint-disable no-unused-vars */
+/* eslint-disable max-len */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-mixed-operators */
+
+// Úr https://eloquentjavascript.net/05_higher_order.html
+
+// Generated from the Unicode 10 database and https://en.wikipedia.org/wiki/Script_(Unicode)
+const SCRIPTS = [
+ {
+ name: 'Adlam',
+ ranges: [[125184, 125259], [125264, 125274], [125278, 125280]],
+ direction: 'rtl',
+ year: 1987,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Fula_alphabets#Adlam_alphabet',
+ },
+ {
+ name: 'Caucasian Albanian',
+ ranges: [[66864, 66916], [66927, 66928]],
+ direction: 'ltr',
+ year: 420,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Caucasian_Albanian_alphabet',
+ },
+ {
+ name: 'Ahom',
+ ranges: [[71424, 71450], [71453, 71468], [71472, 71488]],
+ direction: 'ltr',
+ year: 1250,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Ahom_alphabet',
+ },
+ {
+ name: 'Arabic',
+ ranges: [[1536, 1541], [1542, 1548], [1549, 1563], [1564, 1565], [1566, 1567], [1568, 1600], [1601, 1611], [1622, 1648], [1649, 1757], [1758, 1792], [1872, 1920], [2208, 2229], [2230, 2238], [2260, 2274], [2275, 2304], [64336, 64450], [64467, 64830], [64848, 64912], [64914, 64968], [65008, 65022], [65136, 65141], [65142, 65277], [69216, 69247], [126464, 126468], [126469, 126496], [126497, 126499], [126500, 126501], [126503, 126504], [126505, 126515], [126516, 126520], [126521, 126522], [126523, 126524], [126530, 126531], [126535, 126536], [126537, 126538], [126539, 126540], [126541, 126544], [126545, 126547], [126548, 126549], [126551, 126552], [126553, 126554], [126555, 126556], [126557, 126558], [126559, 126560], [126561, 126563], [126564, 126565], [126567, 126571], [126572, 126579], [126580, 126584], [126585, 126589], [126590, 126591], [126592, 126602], [126603, 126620], [126625, 126628], [126629, 126634], [126635, 126652], [126704, 126706]],
+ direction: 'rtl',
+ year: 400,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Arabic_script',
+ },
+ {
+ name: 'Imperial Aramaic',
+ ranges: [[67648, 67670], [67671, 67680]],
+ direction: 'rtl',
+ year: 800,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Aramaic_alphabet',
+ },
+ {
+ name: 'Armenian',
+ ranges: [[1329, 1367], [1369, 1376], [1377, 1416], [1418, 1419], [1421, 1424], [64275, 64280]],
+ direction: 'ltr',
+ year: 405,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Armenian_alphabet',
+ },
+ {
+ name: 'Avestan',
+ ranges: [[68352, 68406], [68409, 68416]],
+ direction: 'rtl',
+ year: 400,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Avestan_alphabet',
+ },
+ {
+ name: 'Balinese',
+ ranges: [[6912, 6988], [6992, 7037]],
+ direction: 'ltr',
+ year: 1000,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Balinese_script',
+ },
+ {
+ name: 'Bamum',
+ ranges: [[42656, 42744], [92160, 92729]],
+ direction: 'ltr',
+ year: 1896,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Bamum_script',
+ },
+ {
+ name: 'Bassa Vah',
+ ranges: [[92880, 92910], [92912, 92918]],
+ direction: 'ltr',
+ year: 1950,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Bassa_alphabet',
+ },
+ {
+ name: 'Batak',
+ ranges: [[7104, 7156], [7164, 7168]],
+ direction: 'ltr',
+ year: 1300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Batak_alphabet',
+ },
+ {
+ name: 'Bengali',
+ ranges: [[2432, 2436], [2437, 2445], [2447, 2449], [2451, 2473], [2474, 2481], [2482, 2483], [2486, 2490], [2492, 2501], [2503, 2505], [2507, 2511], [2519, 2520], [2524, 2526], [2527, 2532], [2534, 2558]],
+ direction: 'ltr',
+ year: 1050,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Bengali_alphabet',
+ },
+ {
+ name: 'Bhaiksuki',
+ ranges: [[72704, 72713], [72714, 72759], [72760, 72774], [72784, 72813]],
+ direction: 'ltr',
+ year: 1050,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Bhaiksuki_alphabet',
+ },
+ {
+ name: 'Bopomofo',
+ ranges: [[746, 748], [12549, 12591], [12704, 12731]],
+ direction: 'ltr',
+ year: 1918,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Bopomofo',
+ },
+ {
+ name: 'Brahmi',
+ ranges: [[69632, 69710], [69714, 69744], [69759, 69760]],
+ direction: 'ltr',
+ year: -250,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Brahmi_script',
+ },
+ {
+ name: 'Braille',
+ ranges: [[10240, 10496]],
+ direction: 'ltr',
+ year: 1824,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Braille',
+ },
+ {
+ name: 'Buginese',
+ ranges: [[6656, 6684], [6686, 6688]],
+ direction: 'ltr',
+ year: 1650,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Lontara_script',
+ },
+ {
+ name: 'Buhid',
+ ranges: [[5952, 5972]],
+ direction: 'ltr',
+ year: 1300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Buhid_alphabet',
+ },
+ {
+ name: 'Chakma',
+ ranges: [[69888, 69941], [69942, 69956]],
+ direction: 'ltr',
+ year: 1050,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Chakma_alphabet',
+ },
+ {
+ name: 'Canadian Aboriginal',
+ ranges: [[5120, 5760], [6320, 6390]],
+ direction: 'ltr',
+ year: 1840,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Canadian_Aboriginal_syllabics',
+ },
+ {
+ name: 'Carian',
+ ranges: [[66208, 66257]],
+ direction: 'ltr',
+ year: -650,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Carian_alphabets',
+ },
+ {
+ name: 'Cham',
+ ranges: [[43520, 43575], [43584, 43598], [43600, 43610], [43612, 43616]],
+ direction: 'ltr',
+ year: 750,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Cham_alphabet',
+ },
+ {
+ name: 'Cherokee',
+ ranges: [[5024, 5110], [5112, 5118], [43888, 43968]],
+ direction: 'ltr',
+ year: 1820,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Cherokee_syllabary',
+ },
+ {
+ name: 'Coptic',
+ ranges: [[994, 1008], [11392, 11508], [11513, 11520]],
+ direction: 'ltr',
+ year: -200,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Coptic_alphabet',
+ },
+ {
+ name: 'Cypriot',
+ ranges: [[67584, 67590], [67592, 67593], [67594, 67638], [67639, 67641], [67644, 67645], [67647, 67648]],
+ direction: 'rtl',
+ year: -1100,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Cypriot_syllabary',
+ },
+ {
+ name: 'Cyrillic',
+ ranges: [[1024, 1157], [1159, 1328], [7296, 7305], [7467, 7468], [7544, 7545], [11744, 11776], [42560, 42656], [65070, 65072]],
+ direction: 'ltr',
+ year: 950,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Cyrillic_script',
+ },
+ {
+ name: 'Devanagari',
+ ranges: [[2304, 2385], [2387, 2404], [2406, 2432], [43232, 43262]],
+ direction: 'ltr',
+ year: 100,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Devanagari',
+ },
+ {
+ name: 'Deseret',
+ ranges: [[66560, 66640]],
+ direction: 'ltr',
+ year: 1854,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Deseret_alphabet',
+ },
+ {
+ name: 'Duployan',
+ ranges: [[113664, 113771], [113776, 113789], [113792, 113801], [113808, 113818], [113820, 113824]],
+ direction: 'ltr',
+ year: 1860,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Duployan_shorthand',
+ },
+ {
+ name: 'Egyptian Hieroglyphs',
+ ranges: [[77824, 78895]],
+ direction: 'ltr',
+ year: -3200,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Egyptian_hieroglyphs',
+ },
+ {
+ name: 'Elbasan',
+ ranges: [[66816, 66856]],
+ direction: 'ltr',
+ year: 1750,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Elbasan_alphabet',
+ },
+ {
+ name: 'Ethiopic',
+ ranges: [[4608, 4681], [4682, 4686], [4688, 4695], [4696, 4697], [4698, 4702], [4704, 4745], [4746, 4750], [4752, 4785], [4786, 4790], [4792, 4799], [4800, 4801], [4802, 4806], [4808, 4823], [4824, 4881], [4882, 4886], [4888, 4955], [4957, 4989], [4992, 5018], [11648, 11671], [11680, 11687], [11688, 11695], [11696, 11703], [11704, 11711], [11712, 11719], [11720, 11727], [11728, 11735], [11736, 11743], [43777, 43783], [43785, 43791], [43793, 43799], [43808, 43815], [43816, 43823]],
+ direction: 'ltr',
+ year: -900,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Ge%27ez_script',
+ },
+ {
+ name: 'Georgian',
+ ranges: [[4256, 4294], [4295, 4296], [4301, 4302], [4304, 4347], [4348, 4352], [11520, 11558], [11559, 11560], [11565, 11566]],
+ direction: 'ltr',
+ year: 430,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Georgian_scripts',
+ },
+ {
+ name: 'Glagolitic',
+ ranges: [[11264, 11311], [11312, 11359], [122880, 122887], [122888, 122905], [122907, 122914], [122915, 122917], [122918, 122923]],
+ direction: 'ltr',
+ year: 862,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Glagolitic_script',
+ },
+ {
+ name: 'Masaram Gondi',
+ ranges: [[72960, 72967], [72968, 72970], [72971, 73015], [73018, 73019], [73020, 73022], [73023, 73032], [73040, 73050]],
+ direction: 'ltr',
+ year: 1918,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Gondi_writing#Masaram',
+ },
+ {
+ name: 'Gothic',
+ ranges: [[66352, 66379]],
+ direction: 'ltr',
+ year: 350,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Gothic_alphabet',
+ },
+ {
+ name: 'Grantha',
+ ranges: [[70400, 70404], [70405, 70413], [70415, 70417], [70419, 70441], [70442, 70449], [70450, 70452], [70453, 70458], [70460, 70469], [70471, 70473], [70475, 70478], [70480, 70481], [70487, 70488], [70493, 70500], [70502, 70509], [70512, 70517]],
+ direction: 'ltr',
+ year: 550,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Grantha_alphabet',
+ },
+ {
+ name: 'Greek',
+ ranges: [[880, 884], [885, 888], [890, 894], [895, 896], [900, 901], [902, 903], [904, 907], [908, 909], [910, 930], [931, 994], [1008, 1024], [7462, 7467], [7517, 7522], [7526, 7531], [7615, 7616], [7936, 7958], [7960, 7966], [7968, 8006], [8008, 8014], [8016, 8024], [8025, 8026], [8027, 8028], [8029, 8030], [8031, 8062], [8064, 8117], [8118, 8133], [8134, 8148], [8150, 8156], [8157, 8176], [8178, 8181], [8182, 8191], [8486, 8487], [43877, 43878], [65856, 65935], [65952, 65953], [119296, 119366]],
+ direction: 'ltr',
+ year: -800,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Greek_alphabet',
+ },
+ {
+ name: 'Gujarati',
+ ranges: [[2689, 2692], [2693, 2702], [2703, 2706], [2707, 2729], [2730, 2737], [2738, 2740], [2741, 2746], [2748, 2758], [2759, 2762], [2763, 2766], [2768, 2769], [2784, 2788], [2790, 2802], [2809, 2816]],
+ direction: 'ltr',
+ year: 1592,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Gujarati_alphabet',
+ },
+ {
+ name: 'Gurmukhi',
+ ranges: [[2561, 2564], [2565, 2571], [2575, 2577], [2579, 2601], [2602, 2609], [2610, 2612], [2613, 2615], [2616, 2618], [2620, 2621], [2622, 2627], [2631, 2633], [2635, 2638], [2641, 2642], [2649, 2653], [2654, 2655], [2662, 2678]],
+ direction: 'ltr',
+ year: 1550,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Gurmukh%C4%AB_alphabet',
+ },
+ {
+ name: 'Hangul',
+ ranges: [[4352, 4608], [12334, 12336], [12593, 12687], [12800, 12831], [12896, 12927], [43360, 43389], [44032, 55204], [55216, 55239], [55243, 55292], [65440, 65471], [65474, 65480], [65482, 65488], [65490, 65496], [65498, 65501]],
+ direction: 'ltr',
+ year: 1443,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Hangul',
+ },
+ {
+ name: 'Han',
+ ranges: [[11904, 11930], [11931, 12020], [12032, 12246], [12293, 12294], [12295, 12296], [12321, 12330], [12344, 12348], [13312, 19894], [19968, 40939], [63744, 64110], [64112, 64218], [131072, 173783], [173824, 177973], [177984, 178206], [178208, 183970], [183984, 191457], [194560, 195102]],
+ direction: 'ltr',
+ year: -1100,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Chinese_characters',
+ },
+ {
+ name: 'Hanunoo',
+ ranges: [[5920, 5941]],
+ direction: 'ltr',
+ year: 1300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Hanun%C3%B3%27o_alphabet',
+ },
+ {
+ name: 'Hatran',
+ ranges: [[67808, 67827], [67828, 67830], [67835, 67840]],
+ direction: 'rtl',
+ year: -40,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Hatran_alphabet',
+ },
+ {
+ name: 'Hebrew',
+ ranges: [[1425, 1480], [1488, 1515], [1520, 1525], [64285, 64311], [64312, 64317], [64318, 64319], [64320, 64322], [64323, 64325], [64326, 64336]],
+ direction: 'rtl',
+ year: -100,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Hebrew_alphabet',
+ },
+ {
+ name: 'Hiragana',
+ ranges: [[12353, 12439], [12445, 12448], [110593, 110879], [127488, 127489]],
+ direction: 'ltr',
+ year: 800,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Hiragana',
+ },
+ {
+ name: 'Anatolian Hieroglyphs',
+ ranges: [[82944, 83527]],
+ direction: 'ltr',
+ year: -1400,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Anatolian_hieroglyphs',
+ },
+ {
+ name: 'Pahawh Hmong',
+ ranges: [[92928, 92998], [93008, 93018], [93019, 93026], [93027, 93048], [93053, 93072]],
+ direction: 'ltr',
+ year: 1959,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Pahawh_Hmong',
+ },
+ {
+ name: 'Old Hungarian',
+ ranges: [[68736, 68787], [68800, 68851], [68858, 68864]],
+ direction: 'rtl',
+ year: 1150,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Old_Hungarian_alphabet',
+ },
+ {
+ name: 'Old Italic',
+ ranges: [[66304, 66340], [66349, 66352]],
+ direction: 'ltr',
+ year: -750,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Old_Italic_script',
+ },
+ {
+ name: 'Javanese',
+ ranges: [[43392, 43470], [43472, 43482], [43486, 43488]],
+ direction: 'ltr',
+ year: 1250,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Javanese_script',
+ },
+ {
+ name: 'Kayah Li',
+ ranges: [[43264, 43310], [43311, 43312]],
+ direction: 'ltr',
+ year: 1962,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Kayah_Li_alphabet',
+ },
+ {
+ name: 'Katakana',
+ ranges: [[12449, 12539], [12541, 12544], [12784, 12800], [13008, 13055], [13056, 13144], [65382, 65392], [65393, 65438], [110592, 110593]],
+ direction: 'ltr',
+ year: 800,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Katakana',
+ },
+ {
+ name: 'Kharoshthi',
+ ranges: [[68096, 68100], [68101, 68103], [68108, 68116], [68117, 68120], [68121, 68148], [68152, 68155], [68159, 68168], [68176, 68185]],
+ direction: 'rtl',
+ year: -400,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Kharosthi',
+ },
+ {
+ name: 'Khmer',
+ ranges: [[6016, 6110], [6112, 6122], [6128, 6138], [6624, 6656]],
+ direction: 'ltr',
+ year: 611,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Khmer_alphabet',
+ },
+ {
+ name: 'Khojki',
+ ranges: [[70144, 70162], [70163, 70207]],
+ direction: 'ltr',
+ year: 1520,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Khojki_script',
+ },
+ {
+ name: 'Kannada',
+ ranges: [[3200, 3204], [3205, 3213], [3214, 3217], [3218, 3241], [3242, 3252], [3253, 3258], [3260, 3269], [3270, 3273], [3274, 3278], [3285, 3287], [3294, 3295], [3296, 3300], [3302, 3312], [3313, 3315]],
+ direction: 'ltr',
+ year: 450,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Kannada_alphabet',
+ },
+ {
+ name: 'Kaithi',
+ ranges: [[69760, 69826]],
+ direction: 'ltr',
+ year: 1550,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Kaithi',
+ },
+ {
+ name: 'Tai Tham',
+ ranges: [[6688, 6751], [6752, 6781], [6783, 6794], [6800, 6810], [6816, 6830]],
+ direction: 'ltr',
+ year: 1300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tai_Tham_alphabet',
+ },
+ {
+ name: 'Lao',
+ ranges: [[3713, 3715], [3716, 3717], [3719, 3721], [3722, 3723], [3725, 3726], [3732, 3736], [3737, 3744], [3745, 3748], [3749, 3750], [3751, 3752], [3754, 3756], [3757, 3770], [3771, 3774], [3776, 3781], [3782, 3783], [3784, 3790], [3792, 3802], [3804, 3808]],
+ direction: 'ltr',
+ year: 1350,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Lao_alphabet',
+ },
+ {
+ name: 'Latin',
+ ranges: [[65, 91], [97, 123], [170, 171], [186, 187], [192, 215], [216, 247], [248, 697], [736, 741], [7424, 7462], [7468, 7517], [7522, 7526], [7531, 7544], [7545, 7615], [7680, 7936], [8305, 8306], [8319, 8320], [8336, 8349], [8490, 8492], [8498, 8499], [8526, 8527], [8544, 8585], [11360, 11392], [42786, 42888], [42891, 42927], [42928, 42936], [42999, 43008], [43824, 43867], [43868, 43877], [64256, 64263], [65313, 65339], [65345, 65371]],
+ direction: 'ltr',
+ year: -700,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Latin_script',
+ },
+ {
+ name: 'Lepcha',
+ ranges: [[7168, 7224], [7227, 7242], [7245, 7248]],
+ direction: 'ltr',
+ year: 1700,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Lepcha_alphabet',
+ },
+ {
+ name: 'Limbu',
+ ranges: [[6400, 6431], [6432, 6444], [6448, 6460], [6464, 6465], [6468, 6480]],
+ direction: 'ltr',
+ year: 1740,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Limbu_alphabet',
+ },
+ {
+ name: 'Linear A',
+ ranges: [[67072, 67383], [67392, 67414], [67424, 67432]],
+ direction: 'ltr',
+ year: -2500,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Linear_A',
+ },
+ {
+ name: 'Linear B',
+ ranges: [[65536, 65548], [65549, 65575], [65576, 65595], [65596, 65598], [65599, 65614], [65616, 65630], [65664, 65787]],
+ direction: 'ltr',
+ year: -1450,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Linear_B',
+ },
+ {
+ name: 'Lisu',
+ ranges: [[42192, 42240]],
+ direction: 'ltr',
+ year: 1915,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Fraser_alphabet',
+ },
+ {
+ name: 'Lycian',
+ ranges: [[66176, 66205]],
+ direction: 'ltr',
+ year: -500,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Lycian_alphabet',
+ },
+ {
+ name: 'Lydian',
+ ranges: [[67872, 67898], [67903, 67904]],
+ direction: 'rtl',
+ year: -700,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Lydian_alphabet',
+ },
+ {
+ name: 'Mahajani',
+ ranges: [[69968, 70007]],
+ direction: 'ltr',
+ year: 1150,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Mahajani',
+ },
+ {
+ name: 'Mandaic',
+ ranges: [[2112, 2140], [2142, 2143]],
+ direction: 'rtl',
+ year: 200,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Mandaic_alphabet',
+ },
+ {
+ name: 'Manichaean',
+ ranges: [[68288, 68327], [68331, 68343]],
+ direction: 'rtl',
+ year: 250,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Manichaean_alphabet',
+ },
+ {
+ name: 'Marchen',
+ ranges: [[72816, 72848], [72850, 72872], [72873, 72887]],
+ direction: 'ltr',
+ year: 650,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Zhang-Zhung_language#Scripts',
+ },
+ {
+ name: 'Mende Kikakui',
+ ranges: [[124928, 125125], [125127, 125143]],
+ direction: 'rtl',
+ year: 1880,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Mende_Kikakui_script',
+ },
+ {
+ name: 'Meroitic Cursive',
+ ranges: [[68000, 68024], [68028, 68048], [68050, 68096]],
+ direction: 'rtl',
+ year: -300,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Meroitic_alphabet',
+ },
+ {
+ name: 'Meroitic Hieroglyphs',
+ ranges: [[67968, 68000]],
+ direction: 'rtl',
+ year: -300,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Meroitic_alphabet',
+ },
+ {
+ name: 'Malayalam',
+ ranges: [[3328, 3332], [3333, 3341], [3342, 3345], [3346, 3397], [3398, 3401], [3402, 3408], [3412, 3428], [3430, 3456]],
+ direction: 'ltr',
+ year: 830,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Malayalam_script',
+ },
+ {
+ name: 'Modi',
+ ranges: [[71168, 71237], [71248, 71258]],
+ direction: 'ltr',
+ year: 1200,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Modi_alphabet',
+ },
+ {
+ name: 'Mongolian',
+ ranges: [[6144, 6146], [6148, 6149], [6150, 6159], [6160, 6170], [6176, 6264], [6272, 6315], [71264, 71277]],
+ direction: 'ttb',
+ year: 1204,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Mongolian_script',
+ },
+ {
+ name: 'Mro',
+ ranges: [[92736, 92767], [92768, 92778], [92782, 92784]],
+ direction: 'ltr',
+ year: 1985,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Mru_language#Alphabet',
+ },
+ {
+ name: 'Meetei Mayek',
+ ranges: [[43744, 43767], [43968, 44014], [44016, 44026]],
+ direction: 'ltr',
+ year: 200,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Meitei_script',
+ },
+ {
+ name: 'Multani',
+ ranges: [[70272, 70279], [70280, 70281], [70282, 70286], [70287, 70302], [70303, 70314]],
+ direction: 'ltr',
+ year: 1750,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Multani_alphabet',
+ },
+ {
+ name: 'Myanmar',
+ ranges: [[4096, 4256], [43488, 43519], [43616, 43648]],
+ direction: 'ltr',
+ year: 984,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Burmese_alphabet',
+ },
+ {
+ name: 'Old North Arabian',
+ ranges: [[68224, 68256]],
+ direction: 'rtl',
+ year: 750,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Ancient_North_Arabian',
+ },
+ {
+ name: 'Nabataean',
+ ranges: [[67712, 67743], [67751, 67760]],
+ direction: 'rtl',
+ year: 150,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Nabataean_alphabet',
+ },
+ {
+ name: 'Newa',
+ ranges: [[70656, 70746], [70747, 70748], [70749, 70750]],
+ direction: 'ltr',
+ year: 1000,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Prachalit_Nepal_alphabet',
+ },
+ {
+ name: 'Nko',
+ ranges: [[1984, 2043]],
+ direction: 'rtl',
+ year: 1949,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/N%27Ko_alphabet',
+ },
+ {
+ name: 'Nushu',
+ ranges: [[94177, 94178], [110960, 111356]],
+ direction: 'ltr',
+ year: 1500,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/N%C3%BCshu_script',
+ },
+ {
+ name: 'Ogham',
+ ranges: [[5760, 5789]],
+ direction: 'ltr',
+ year: 350,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Ogham',
+ },
+ {
+ name: 'Ol Chiki',
+ ranges: [[7248, 7296]],
+ direction: 'ltr',
+ year: 1925,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Ol_Chiki_script',
+ },
+ {
+ name: 'Old Turkic',
+ ranges: [[68608, 68681]],
+ direction: 'rtl',
+ year: 750,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Old_Turkic_alphabet',
+ },
+ {
+ name: 'Oriya',
+ ranges: [[2817, 2820], [2821, 2829], [2831, 2833], [2835, 2857], [2858, 2865], [2866, 2868], [2869, 2874], [2876, 2885], [2887, 2889], [2891, 2894], [2902, 2904], [2908, 2910], [2911, 2916], [2918, 2936]],
+ direction: 'ltr',
+ year: 1060,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Odia_alphabet',
+ },
+ {
+ name: 'Osage',
+ ranges: [[66736, 66772], [66776, 66812]],
+ direction: 'ltr',
+ year: 2006,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Osage_alphabet',
+ },
+ {
+ name: 'Osmanya',
+ ranges: [[66688, 66718], [66720, 66730]],
+ direction: 'ltr',
+ year: 1920,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Osmanya_alphabet',
+ },
+ {
+ name: 'Palmyrene',
+ ranges: [[67680, 67712]],
+ direction: 'rtl',
+ year: -100,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Palmyrene_alphabet',
+ },
+ {
+ name: 'Pau Cin Hau',
+ ranges: [[72384, 72441]],
+ direction: 'ltr',
+ year: 1900,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Pau_Cin_Hau',
+ },
+ {
+ name: 'Old Permic',
+ ranges: [[66384, 66427]],
+ direction: 'ltr',
+ year: 1372,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Old_Permic_alphabet',
+ },
+ {
+ name: 'Phags-pa',
+ ranges: [[43072, 43123], [43124, 43127]],
+ direction: 'ttb',
+ year: 1269,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/%27Phags-pa_script',
+ },
+ {
+ name: 'Inscriptional Pahlavi',
+ ranges: [[68448, 68467], [68472, 68480]],
+ direction: 'rtl',
+ year: -171,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Inscriptional_Pahlavi',
+ },
+ {
+ name: 'Psalter Pahlavi',
+ ranges: [[68480, 68498], [68505, 68509], [68521, 68528]],
+ direction: 'rtl',
+ year: 550,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Psalter_Pahlavi',
+ },
+ {
+ name: 'Phoenician',
+ ranges: [[67840, 67868], [67871, 67872]],
+ direction: 'rtl',
+ year: -1200,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Phoenician_alphabet',
+ },
+ {
+ name: 'Miao',
+ ranges: [[93952, 94021], [94032, 94079], [94095, 94112]],
+ direction: 'ltr',
+ year: 1936,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Pollard_script',
+ },
+ {
+ name: 'Inscriptional Parthian',
+ ranges: [[68416, 68438], [68440, 68448]],
+ direction: 'rtl',
+ year: -250,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Inscriptional_Parthian',
+ },
+ {
+ name: 'Rejang',
+ ranges: [[43312, 43348], [43359, 43360]],
+ direction: 'ltr',
+ year: 1750,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Rejang_script',
+ },
+ {
+ name: 'Runic',
+ ranges: [[5792, 5867], [5870, 5881]],
+ direction: 'ltr',
+ year: 150,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Runes',
+ },
+ {
+ name: 'Samaritan',
+ ranges: [[2048, 2094], [2096, 2111]],
+ direction: 'rtl',
+ year: -600,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Samaritan_alphabet',
+ },
+ {
+ name: 'Old South Arabian',
+ ranges: [[68192, 68224]],
+ direction: 'rtl',
+ year: -850,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Ancient_South_Arabian_script',
+ },
+ {
+ name: 'Saurashtra',
+ ranges: [[43136, 43206], [43214, 43226]],
+ direction: 'ltr',
+ year: 1920,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Saurashtra_alphabet',
+ },
+ {
+ name: 'SignWriting',
+ ranges: [[120832, 121484], [121499, 121504], [121505, 121520]],
+ direction: 'ttb',
+ year: 1974,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/SignWriting',
+ },
+ {
+ name: 'Shavian',
+ ranges: [[66640, 66688]],
+ direction: 'ltr',
+ year: 1960,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Shavian_alphabet',
+ },
+ {
+ name: 'Sharada',
+ ranges: [[70016, 70094], [70096, 70112]],
+ direction: 'ltr',
+ year: 800,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/%C5%9A%C4%81rad%C4%81_script',
+ },
+ {
+ name: 'Siddham',
+ ranges: [[71040, 71094], [71096, 71134]],
+ direction: 'ltr',
+ year: 550,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Siddha%E1%B9%83_script',
+ },
+ {
+ name: 'Khudawadi',
+ ranges: [[70320, 70379], [70384, 70394]],
+ direction: 'ltr',
+ year: 1550,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Khudabadi_script',
+ },
+ {
+ name: 'Sinhala',
+ ranges: [[3458, 3460], [3461, 3479], [3482, 3506], [3507, 3516], [3517, 3518], [3520, 3527], [3530, 3531], [3535, 3541], [3542, 3543], [3544, 3552], [3558, 3568], [3570, 3573], [70113, 70133]],
+ direction: 'ltr',
+ year: 700,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Sinhalese_alphabet',
+ },
+ {
+ name: 'Sora Sompeng',
+ ranges: [[69840, 69865], [69872, 69882]],
+ direction: 'ltr',
+ year: 1936,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Sorang_Sompeng_alphabet',
+ },
+ {
+ name: 'Soyombo',
+ ranges: [[72272, 72324], [72326, 72349], [72350, 72355]],
+ direction: 'ltr',
+ year: 1650,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Soyombo_alphabet',
+ },
+ {
+ name: 'Sundanese',
+ ranges: [[7040, 7104], [7360, 7368]],
+ direction: 'ltr',
+ year: 1350,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Sundanese_script',
+ },
+ {
+ name: 'Syloti Nagri',
+ ranges: [[43008, 43052]],
+ direction: 'ltr',
+ year: 1303,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Sylheti_Nagari',
+ },
+ {
+ name: 'Syriac',
+ ranges: [[1792, 1806], [1807, 1867], [1869, 1872], [2144, 2155]],
+ direction: 'rtl',
+ year: -200,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Syriac_alphabet',
+ },
+ {
+ name: 'Tagbanwa',
+ ranges: [[5984, 5997], [5998, 6001], [6002, 6004]],
+ direction: 'ltr',
+ year: 1300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tagbanwa_script',
+ },
+ {
+ name: 'Takri',
+ ranges: [[71296, 71352], [71360, 71370]],
+ direction: 'ltr',
+ year: 1550,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Takri_alphabet',
+ },
+ {
+ name: 'Tai Le',
+ ranges: [[6480, 6510], [6512, 6517]],
+ direction: 'ltr',
+ year: 1200,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tai_Le_alphabet',
+ },
+ {
+ name: 'New Tai Lue',
+ ranges: [[6528, 6572], [6576, 6602], [6608, 6619], [6622, 6624]],
+ direction: 'ltr',
+ year: 1950,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/New_Tai_Lue_alphabet',
+ },
+ {
+ name: 'Tamil',
+ ranges: [[2946, 2948], [2949, 2955], [2958, 2961], [2962, 2966], [2969, 2971], [2972, 2973], [2974, 2976], [2979, 2981], [2984, 2987], [2990, 3002], [3006, 3011], [3014, 3017], [3018, 3022], [3024, 3025], [3031, 3032], [3046, 3067]],
+ direction: 'ltr',
+ year: 700,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tamil_script',
+ },
+ {
+ name: 'Tangut',
+ ranges: [[94176, 94177], [94208, 100333], [100352, 101107]],
+ direction: 'ltr',
+ year: 1036,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Tangut_script',
+ },
+ {
+ name: 'Tai Viet',
+ ranges: [[43648, 43715], [43739, 43744]],
+ direction: 'ltr',
+ year: 1200,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tai_Dam_language#Writing_system',
+ },
+ {
+ name: 'Telugu',
+ ranges: [[3072, 3076], [3077, 3085], [3086, 3089], [3090, 3113], [3114, 3130], [3133, 3141], [3142, 3145], [3146, 3150], [3157, 3159], [3160, 3163], [3168, 3172], [3174, 3184], [3192, 3200]],
+ direction: 'ltr',
+ year: -900,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Telugu_script',
+ },
+ {
+ name: 'Tifinagh',
+ ranges: [[11568, 11624], [11631, 11633], [11647, 11648]],
+ direction: 'ltr',
+ year: -300,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tifinagh',
+ },
+ {
+ name: 'Tagalog',
+ ranges: [[5888, 5901], [5902, 5909]],
+ direction: 'ltr',
+ year: 1250,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Baybayin',
+ },
+ {
+ name: 'Thaana',
+ ranges: [[1920, 1970]],
+ direction: 'rtl',
+ year: 1599,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Thaana',
+ },
+ {
+ name: 'Thai',
+ ranges: [[3585, 3643], [3648, 3676]],
+ direction: 'ltr',
+ year: 1283,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Thai_alphabet',
+ },
+ {
+ name: 'Tibetan',
+ ranges: [[3840, 3912], [3913, 3949], [3953, 3992], [3993, 4029], [4030, 4045], [4046, 4053], [4057, 4059]],
+ direction: 'ltr',
+ year: 650,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Tibetan_alphabet',
+ },
+ {
+ name: 'Tirhuta',
+ ranges: [[70784, 70856], [70864, 70874]],
+ direction: 'ltr',
+ year: 1450,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Tirhuta',
+ },
+ {
+ name: 'Ugaritic',
+ ranges: [[66432, 66462], [66463, 66464]],
+ direction: 'ltr',
+ year: -1400,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Ugaritic_alphabet',
+ },
+ {
+ name: 'Vai',
+ ranges: [[42240, 42540]],
+ direction: 'ltr',
+ year: 1830,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Vai_syllabary',
+ },
+ {
+ name: 'Warang Citi',
+ ranges: [[71840, 71923], [71935, 71936]],
+ direction: 'ltr',
+ year: 1946,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Warang_Citi',
+ },
+ {
+ name: 'Old Persian',
+ ranges: [[66464, 66500], [66504, 66518]],
+ direction: 'ltr',
+ year: -525,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Old_Persian_cuneiform',
+ },
+ {
+ name: 'Cuneiform',
+ ranges: [[73728, 74650], [74752, 74863], [74864, 74869], [74880, 75076]],
+ direction: 'ltr',
+ year: -3050,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Cuneiform_script',
+ },
+ {
+ name: 'Yi',
+ ranges: [[40960, 42125], [42128, 42183]],
+ direction: 'ltr',
+ year: 1450,
+ living: true,
+ link: 'https://en.wikipedia.org/wiki/Yi_script',
+ },
+ {
+ name: 'Zanabazar Square',
+ ranges: [[72192, 72264]],
+ direction: 'ltr',
+ year: 1700,
+ living: false,
+ link: 'https://en.wikipedia.org/wiki/Mongolian_writing_systems#Horizontal_square_script',
+ },
+];
+
+// Dæmi um útfærslu á filter, alltaf nota stöðluðu útgáfu á Array
+// t.d. [1, 2, 3].filter(i => i > 1)
+function filter(array, test) {
+ const passed = [];
+ for (const element of array) {
+ if (test(element)) {
+ passed.push(element);
+ }
+ }
+ return passed;
+}
+
+// console.log(filter(SCRIPTS, script => script.living));
+
+// --- //
+
+// Dæmi um útfærslu á map, alltaf nota stöðluðu útgáfu á Array
+// t.d. [1, 2, 3].map(i => i * 2)
+function map(array, transform) {
+ const mapped = [];
+ for (const element of array) {
+ mapped.push(transform(element));
+ }
+ return mapped;
+}
+
+const rtlScripts = SCRIPTS.filter((s) => s.direction === 'rtl');
+// console.log(map(rtlScripts, s => s.name));
+
+// --- //
+
+// Dæmi um útfærslu á reduce, alltaf nota stöðluðu útgáfu á Array
+// t.d. [1, 2, 3].reduce((i, v) => i + v))
+function reduce(array, combine, start) {
+ let current = start;
+ for (const element of array) {
+ current = combine(current, element);
+ }
+ return current;
+}
+
+// --- //
+
+// Skilar hversu margir stafir eru í stafrófi fyrir script
+function characterCount(script) {
+ return script.ranges.reduce((count, [from, to]) => count + (to - from), 0);
+}
+
+/* console.log(
+ SCRIPTS.reduce((a, b) => (characterCount(a) < characterCount(b) ? b : a)),
+); */
+
+// console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));
+
+// --- //
+
+function characterScript(code) {
+ for (const script of SCRIPTS) {
+ // er stafakóðinn á a.m.k. einu bili?
+ if (script.ranges.some(([from, to]) => code >= from && code < to)) {
+ return script;
+ }
+ }
+ return null;
+}
+
+// console.log(characterScript(121));
+
+function countBy(items, groupName) {
+ const counts = [];
+ for (const item of items) {
+ const name = groupName(item);
+ const known = counts.findIndex((c) => c.name === name);
+ if (known === -1) {
+ counts.push({ name, count: 1 });
+ } else {
+ counts[known].count += 1;
+ }
+ }
+ return counts;
+}
+
+function textScripts(text) {
+ const scripts = countBy(text, (char) => {
+ const script = characterScript(char.codePointAt(0));
+ return script ? script.name : 'none';
+ }).filter(({ name }) => name !== 'none');
+
+ const total = scripts.reduce((n, { count }) => n + count, 0);
+ if (total === 0) return 'No scripts found';
+
+ return scripts
+ .map(({ name, count }) => `${Math.round(count * 100 / total)}% ${name}`)
+ .join(', ');
+}
+
+// console.log(textScripts('英国的狗说"woof", 俄罗斯的狗说"тяв"'));
diff --git a/namsefni/36.functional/readme.md b/namsefni/36.functional/readme.md
new file mode 100644
index 0000000..7603d11
--- /dev/null
+++ b/namsefni/36.functional/readme.md
@@ -0,0 +1,24 @@
+# Fallaforritun
+
+[Fyrirlestur: Fallaforritun](1.functional.md), [vídeó](https://youtu.be/baNMhgcqD04)
+
+## Lesefni
+
+* [Eloquent JavaScript, kafli 5: Higher-order Functions](https://eloquentjavascript.net/05_higher_order.html)
+
+## Dæmi
+
+* [sorting & filtering](daemi/01.sorting-filtering.html)
+* [alphabet dæmi](daemi/02.alphabet.js)
+
+## Ítarefni
+
+* [Functional Programming](https://en.wikipedia.org/wiki/Functional_programming)
+* [Higher-order function](https://en.wikipedia.org/wiki/Higher-order_function)
+* [`forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
+* [Pure function](https://en.wikipedia.org/wiki/Pure_function)
+* [`every`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
+* [`some`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
+* [`map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
+* [`reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
+* [`filter`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
diff --git a/namsefni/37.tools/1.tools.md b/namsefni/37.tools/1.tools.md
new file mode 100644
index 0000000..c18d15e
--- /dev/null
+++ b/namsefni/37.tools/1.tools.md
@@ -0,0 +1,215 @@
+---
+title: Fyrirlestur – Tæki og tól
+---
+
+# Fyrirlestur – Tæki og tól
+
+## Vefforritun 1 — TÖL107G
+
+### Ólafur Sverrir Kjartansson, [osk@hi.is](mailto:osk@hi.is)
+
+---
+
+> Choose your tools wisely. It would be a terrible shame if you abandoned the resilient approach to web design because of a difference of opinion with a piece of software.
+>
+> –[Resilient Web Design, chapter 7: Challenges](https://resilientwebdesign.com/chapter7/)
+
+***
+
+## Stuðningur við ECMAScript 6/2015
+
+* Vafrar almennt með um 95+% stuðning við ECMAScript 6/2015
+* Nýrri virkni (ECMAScript 7+) með góðan stuðning en ekki nóg til að geta notað almennt
+* [ECMAScript comptaibility table: ES6](https://kangax.github.io/compat-table/es6/)
+
+***
+
+* Ekki sjálfbært að fletta upp og vita hvað er stutt hvar og hvar ekki
+* Notum _transpiler_ til að þýða úr ES6+ í ES5/6 kóða sem allir (valdir) vafrar styðja
+* Þurfum að setja upp tól og build ferli til að geta notað
+* Polyfill fyrir hluti sem vantar í eldri vafra
+
+***
+
+## Polyfill
+
+Polyfill er kóði eða plugin sem veitir þér aðgang að virkni sem þú gerir ráð fyrir að vafrinn bjóði upp á.
+
+***
+
+## Transpiler
+
+* Þýðandi sem þýðir úr forritunarmáli í forritunarmál
+* Leyfir okkur að nota _í dag_ virkni sem kemur ekki fyrr en í framtíðinni
+ * Svipað og polyfill
+* [Babel](https://babeljs.io/) sá vinsælasti fyrir JavaScript
+ * Kemur líka með polyfills!
+
+***
+
+## babel
+
+* Sækjum með npm
+* Setjum upp config sem segir til um virkni, `babel.config.js`
+* Keyrum með scriptu
+
+***
+
+```bash
+npm install --save-dev @babel/core @babel/cli @babel/preset-env
+npm install --save @babel/polyfill
+```
+
+***
+
+```javascript
+const presets = [
+ ['@babel/preset-env', {
+ useBuiltIns: 'usage',
+ corejs: 2,
+ targets: '> 0.25%, not dead',
+ }],
+];
+module.exports = { presets };
+```
+
+***
+
+* `@babel/preset-env` leyfir okkur að skilgreina lista út frá notkun
+ * Vafra með yfir `0,25%` notkun og ekki „dauða“
+* Hægt og rólega detta hlutir út
+
+***
+
+* `useBuiltIns` skilgreinir hvaða og hvernig polyfill eru notuð
+ * Verðum að hafa svo t.d. `async await` virki
+* [`corejs`](https://github.com/zloirock/core-js) er library fyrir almenn polyfill
+
+***
+
+Setjum upp script til að keyra:
+
+```json
+{
+ "scripts": {
+ "babel": "babel src --out-dir dist",
+ "babel-watch": "babel src --out-dir dist --watch"
+ }
+}
+```
+
+***
+
+## src og dist
+
+* `src` er oft notað sem heiti á möppu með grunnkóðanum okkar
+ * _source_
+* `dist` fyrir kóða sem búið er að eiga við, transpileaður grunnkóði
+ * _distribution_
+
+---
+
+## Pökkun á kóða
+
+* Þegar við erum komin með transpiler er næsta skref oft að setja upp tól sem _pakkar kóða_, _bundler_
+* Tekur margar JavaScript skrár og setur þær saman í eina
+ * Eins og við höfum verið að gera með Sass
+* Oft líka kóði frá öðrum, library sem við höfum sótt með npm
+
+***
+
+* Webpack e.t.v. þekktasta tólið sem gerir þetta
+* En mörg önnur
+ * [Rollup](https://rollupjs.org/)
+ * [Parcel](https://parceljs.org/)
+ * [Vite](https://vitejs.dev/)
+ * og „eldri“, [grunt](https://gruntjs.com/), [gulp](https://gulpjs.com/)
+
+***
+
+## Rollup
+
+* [Rollup](https://rollupjs.org/guide/en) er bundler sem er tilltölulega auðvelt að setja upp
+* Hefur stuðning við ES6 modules!
+
+***
+
+* Sækjum með npm
+ * `npm install --save-dev rollup`
+* Skilgreinum config í `rollup.config.js`
+* Skilgreinum scriptur til að keyra
+ * `"rollup": "rollup -c"`
+ * `"rollup-watch": "rollup -c -w"`
+
+***
+
+```javascript
+module.exports = {
+ input: './src/index.js',
+ output: {
+ file: './dist/bundle.js',
+ format: 'iife',
+ sourcemap: true,
+ },
+};
+```
+
+---
+
+## Rollup plugins
+
+* Rollup styður plugins
+* Getum látið rollup keyra babel
+* Tækjakeðjan okkar farin að flækjast...
+
+***
+
+* Sækjum babel og rollup eins og áður
+ * Sækjum aukalega `rollup-plugin-babel` sem tengir tólin saman
+* Skilgreinum config fyrir bæði tól, eða innifelum stillingar fyrir babel í plugin config
+* Skilgreinum scriptur sem keyra rollup, þurfum _ekki_ fyrir babel
+ * Rollup keyrir babel!
+
+***
+
+```bash
+npm install --save-dev rollup
+npm install --save-dev @babel/core @babel/cli @babel/preset-env
+npm install --save-dev rollup-plugin-babel
+```
+
+***
+
+
+
+```javascript
+import babel from 'rollup-plugin-babel';
+module.exports = {
+ input: './src/index.js',
+ output: {
+ file: './dist/bundle.js',
+ format: 'iife',
+ sourcemap: true,
+ },
+ plugins: [
+ babel({
+ sourceMaps: true,
+ presets: [['@babel/preset-env',
+ { targets: '> 0.25%, not dead' }]],
+ }),
+ ]};
+```
+
+***
+
+## Plugins
+
+* Hellingur af öðrum plugins sem geta allskonar, jafnvel sótt og keyrt kóða af NPM
+ * [`@rollup/plugin-node-resolve`](https://github.com/rollup/plugins/tree/master/packages/node-resolve) finnur og pakkar kóða úr node_modules
+ * [`rollup-plugin-terser`](https://github.com/TrySound/rollup-plugin-terser) „uglifyar“ kóðann okkar, þjappar honum saman
+
+***
+
+> Choose your tools wisely. It would be a terrible shame if you abandoned the resilient approach to web design because of a difference of opinion with a piece of software.
+>
+> —[Resilient Web Design, chapter 7: Challenges](https://resilientwebdesign.com/chapter7/)
diff --git a/namsefni/37.tools/daemi/.eslintrc.js b/namsefni/37.tools/daemi/.eslintrc.js
new file mode 100644
index 0000000..021918d
--- /dev/null
+++ b/namsefni/37.tools/daemi/.eslintrc.js
@@ -0,0 +1,13 @@
+module.exports = {
+ rules: {
+ // leyfum for of
+ 'no-restricted-syntax': 0,
+
+ // helper.html dæmi
+ 'function-paren-newline': 0,
+
+ 'import/extensions': 0,
+
+ 'max-len': [true, 100],
+ },
+};
diff --git a/namsefni/37.tools/daemi/dist/bundle.js b/namsefni/37.tools/daemi/dist/bundle.js
new file mode 100644
index 0000000..54a84af
--- /dev/null
+++ b/namsefni/37.tools/daemi/dist/bundle.js
@@ -0,0 +1,888 @@
+(function () {
+ 'use strict';
+
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
+ try {
+ var info = gen[key](arg);
+ var value = info.value;
+ } catch (error) {
+ reject(error);
+ return;
+ }
+
+ if (info.done) {
+ resolve(value);
+ } else {
+ Promise.resolve(value).then(_next, _throw);
+ }
+ }
+
+ function _asyncToGenerator(fn) {
+ return function () {
+ var self = this,
+ args = arguments;
+ return new Promise(function (resolve, reject) {
+ var gen = fn.apply(self, args);
+
+ function _next(value) {
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
+ }
+
+ function _throw(err) {
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
+ }
+
+ _next(undefined);
+ });
+ };
+ }
+
+ var runtime = {exports: {}};
+
+ /**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+ (function (module) {
+ var runtime = (function (exports) {
+
+ var Op = Object.prototype;
+ var hasOwn = Op.hasOwnProperty;
+ var undefined$1; // More compressible than void 0.
+ var $Symbol = typeof Symbol === "function" ? Symbol : {};
+ var iteratorSymbol = $Symbol.iterator || "@@iterator";
+ var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
+ var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
+
+ function define(obj, key, value) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ return obj[key];
+ }
+ try {
+ // IE 8 has a broken Object.defineProperty that only works on DOM objects.
+ define({}, "");
+ } catch (err) {
+ define = function(obj, key, value) {
+ return obj[key] = value;
+ };
+ }
+
+ function wrap(innerFn, outerFn, self, tryLocsList) {
+ // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
+ var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
+ var generator = Object.create(protoGenerator.prototype);
+ var context = new Context(tryLocsList || []);
+
+ // The ._invoke method unifies the implementations of the .next,
+ // .throw, and .return methods.
+ generator._invoke = makeInvokeMethod(innerFn, self, context);
+
+ return generator;
+ }
+ exports.wrap = wrap;
+
+ // Try/catch helper to minimize deoptimizations. Returns a completion
+ // record like context.tryEntries[i].completion. This interface could
+ // have been (and was previously) designed to take a closure to be
+ // invoked without arguments, but in all the cases we care about we
+ // already have an existing method we want to call, so there's no need
+ // to create a new function object. We can even get away with assuming
+ // the method takes exactly one argument, since that happens to be true
+ // in every case, so we don't have to touch the arguments object. The
+ // only additional allocation required is the completion record, which
+ // has a stable shape and so hopefully should be cheap to allocate.
+ function tryCatch(fn, obj, arg) {
+ try {
+ return { type: "normal", arg: fn.call(obj, arg) };
+ } catch (err) {
+ return { type: "throw", arg: err };
+ }
+ }
+
+ var GenStateSuspendedStart = "suspendedStart";
+ var GenStateSuspendedYield = "suspendedYield";
+ var GenStateExecuting = "executing";
+ var GenStateCompleted = "completed";
+
+ // Returning this object from the innerFn has the same effect as
+ // breaking out of the dispatch switch statement.
+ var ContinueSentinel = {};
+
+ // Dummy constructor functions that we use as the .constructor and
+ // .constructor.prototype properties for functions that return Generator
+ // objects. For full spec compliance, you may wish to configure your
+ // minifier not to mangle the names of these two functions.
+ function Generator() {}
+ function GeneratorFunction() {}
+ function GeneratorFunctionPrototype() {}
+
+ // This is a polyfill for %IteratorPrototype% for environments that
+ // don't natively support it.
+ var IteratorPrototype = {};
+ define(IteratorPrototype, iteratorSymbol, function () {
+ return this;
+ });
+
+ var getProto = Object.getPrototypeOf;
+ var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
+ if (NativeIteratorPrototype &&
+ NativeIteratorPrototype !== Op &&
+ hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
+ // This environment has a native %IteratorPrototype%; use it instead
+ // of the polyfill.
+ IteratorPrototype = NativeIteratorPrototype;
+ }
+
+ var Gp = GeneratorFunctionPrototype.prototype =
+ Generator.prototype = Object.create(IteratorPrototype);
+ GeneratorFunction.prototype = GeneratorFunctionPrototype;
+ define(Gp, "constructor", GeneratorFunctionPrototype);
+ define(GeneratorFunctionPrototype, "constructor", GeneratorFunction);
+ GeneratorFunction.displayName = define(
+ GeneratorFunctionPrototype,
+ toStringTagSymbol,
+ "GeneratorFunction"
+ );
+
+ // Helper for defining the .next, .throw, and .return methods of the
+ // Iterator interface in terms of a single ._invoke method.
+ function defineIteratorMethods(prototype) {
+ ["next", "throw", "return"].forEach(function(method) {
+ define(prototype, method, function(arg) {
+ return this._invoke(method, arg);
+ });
+ });
+ }
+
+ exports.isGeneratorFunction = function(genFun) {
+ var ctor = typeof genFun === "function" && genFun.constructor;
+ return ctor
+ ? ctor === GeneratorFunction ||
+ // For the native GeneratorFunction constructor, the best we can
+ // do is to check its .name property.
+ (ctor.displayName || ctor.name) === "GeneratorFunction"
+ : false;
+ };
+
+ exports.mark = function(genFun) {
+ if (Object.setPrototypeOf) {
+ Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
+ } else {
+ genFun.__proto__ = GeneratorFunctionPrototype;
+ define(genFun, toStringTagSymbol, "GeneratorFunction");
+ }
+ genFun.prototype = Object.create(Gp);
+ return genFun;
+ };
+
+ // Within the body of any async function, `await x` is transformed to
+ // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
+ // `hasOwn.call(value, "__await")` to determine if the yielded value is
+ // meant to be awaited.
+ exports.awrap = function(arg) {
+ return { __await: arg };
+ };
+
+ function AsyncIterator(generator, PromiseImpl) {
+ function invoke(method, arg, resolve, reject) {
+ var record = tryCatch(generator[method], generator, arg);
+ if (record.type === "throw") {
+ reject(record.arg);
+ } else {
+ var result = record.arg;
+ var value = result.value;
+ if (value &&
+ typeof value === "object" &&
+ hasOwn.call(value, "__await")) {
+ return PromiseImpl.resolve(value.__await).then(function(value) {
+ invoke("next", value, resolve, reject);
+ }, function(err) {
+ invoke("throw", err, resolve, reject);
+ });
+ }
+
+ return PromiseImpl.resolve(value).then(function(unwrapped) {
+ // When a yielded Promise is resolved, its final value becomes
+ // the .value of the Promise<{value,done}> result for the
+ // current iteration.
+ result.value = unwrapped;
+ resolve(result);
+ }, function(error) {
+ // If a rejected Promise was yielded, throw the rejection back
+ // into the async generator function so it can be handled there.
+ return invoke("throw", error, resolve, reject);
+ });
+ }
+ }
+
+ var previousPromise;
+
+ function enqueue(method, arg) {
+ function callInvokeWithMethodAndArg() {
+ return new PromiseImpl(function(resolve, reject) {
+ invoke(method, arg, resolve, reject);
+ });
+ }
+
+ return previousPromise =
+ // If enqueue has been called before, then we want to wait until
+ // all previous Promises have been resolved before calling invoke,
+ // so that results are always delivered in the correct order. If
+ // enqueue has not been called before, then it is important to
+ // call invoke immediately, without waiting on a callback to fire,
+ // so that the async generator function has the opportunity to do
+ // any necessary setup in a predictable way. This predictability
+ // is why the Promise constructor synchronously invokes its
+ // executor callback, and why async functions synchronously
+ // execute code before the first await. Since we implement simple
+ // async functions in terms of async generators, it is especially
+ // important to get this right, even though it requires care.
+ previousPromise ? previousPromise.then(
+ callInvokeWithMethodAndArg,
+ // Avoid propagating failures to Promises returned by later
+ // invocations of the iterator.
+ callInvokeWithMethodAndArg
+ ) : callInvokeWithMethodAndArg();
+ }
+
+ // Define the unified helper method that is used to implement .next,
+ // .throw, and .return (see defineIteratorMethods).
+ this._invoke = enqueue;
+ }
+
+ defineIteratorMethods(AsyncIterator.prototype);
+ define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
+ return this;
+ });
+ exports.AsyncIterator = AsyncIterator;
+
+ // Note that simple async functions are implemented on top of
+ // AsyncIterator objects; they just return a Promise for the value of
+ // the final result produced by the iterator.
+ exports.async = function(innerFn, outerFn, self, tryLocsList, PromiseImpl) {
+ if (PromiseImpl === void 0) PromiseImpl = Promise;
+
+ var iter = new AsyncIterator(
+ wrap(innerFn, outerFn, self, tryLocsList),
+ PromiseImpl
+ );
+
+ return exports.isGeneratorFunction(outerFn)
+ ? iter // If outerFn is a generator, return the full iterator.
+ : iter.next().then(function(result) {
+ return result.done ? result.value : iter.next();
+ });
+ };
+
+ function makeInvokeMethod(innerFn, self, context) {
+ var state = GenStateSuspendedStart;
+
+ return function invoke(method, arg) {
+ if (state === GenStateExecuting) {
+ throw new Error("Generator is already running");
+ }
+
+ if (state === GenStateCompleted) {
+ if (method === "throw") {
+ throw arg;
+ }
+
+ // Be forgiving, per 25.3.3.3.3 of the spec:
+ // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
+ return doneResult();
+ }
+
+ context.method = method;
+ context.arg = arg;
+
+ while (true) {
+ var delegate = context.delegate;
+ if (delegate) {
+ var delegateResult = maybeInvokeDelegate(delegate, context);
+ if (delegateResult) {
+ if (delegateResult === ContinueSentinel) continue;
+ return delegateResult;
+ }
+ }
+
+ if (context.method === "next") {
+ // Setting context._sent for legacy support of Babel's
+ // function.sent implementation.
+ context.sent = context._sent = context.arg;
+
+ } else if (context.method === "throw") {
+ if (state === GenStateSuspendedStart) {
+ state = GenStateCompleted;
+ throw context.arg;
+ }
+
+ context.dispatchException(context.arg);
+
+ } else if (context.method === "return") {
+ context.abrupt("return", context.arg);
+ }
+
+ state = GenStateExecuting;
+
+ var record = tryCatch(innerFn, self, context);
+ if (record.type === "normal") {
+ // If an exception is thrown from innerFn, we leave state ===
+ // GenStateExecuting and loop back for another invocation.
+ state = context.done
+ ? GenStateCompleted
+ : GenStateSuspendedYield;
+
+ if (record.arg === ContinueSentinel) {
+ continue;
+ }
+
+ return {
+ value: record.arg,
+ done: context.done
+ };
+
+ } else if (record.type === "throw") {
+ state = GenStateCompleted;
+ // Dispatch the exception by looping back around to the
+ // context.dispatchException(context.arg) call above.
+ context.method = "throw";
+ context.arg = record.arg;
+ }
+ }
+ };
+ }
+
+ // Call delegate.iterator[context.method](context.arg) and handle the
+ // result, either by returning a { value, done } result from the
+ // delegate iterator, or by modifying context.method and context.arg,
+ // setting context.delegate to null, and returning the ContinueSentinel.
+ function maybeInvokeDelegate(delegate, context) {
+ var method = delegate.iterator[context.method];
+ if (method === undefined$1) {
+ // A .throw or .return when the delegate iterator has no .throw
+ // method always terminates the yield* loop.
+ context.delegate = null;
+
+ if (context.method === "throw") {
+ // Note: ["return"] must be used for ES3 parsing compatibility.
+ if (delegate.iterator["return"]) {
+ // If the delegate iterator has a return method, give it a
+ // chance to clean up.
+ context.method = "return";
+ context.arg = undefined$1;
+ maybeInvokeDelegate(delegate, context);
+
+ if (context.method === "throw") {
+ // If maybeInvokeDelegate(context) changed context.method from
+ // "return" to "throw", let that override the TypeError below.
+ return ContinueSentinel;
+ }
+ }
+
+ context.method = "throw";
+ context.arg = new TypeError(
+ "The iterator does not provide a 'throw' method");
+ }
+
+ return ContinueSentinel;
+ }
+
+ var record = tryCatch(method, delegate.iterator, context.arg);
+
+ if (record.type === "throw") {
+ context.method = "throw";
+ context.arg = record.arg;
+ context.delegate = null;
+ return ContinueSentinel;
+ }
+
+ var info = record.arg;
+
+ if (! info) {
+ context.method = "throw";
+ context.arg = new TypeError("iterator result is not an object");
+ context.delegate = null;
+ return ContinueSentinel;
+ }
+
+ if (info.done) {
+ // Assign the result of the finished delegate to the temporary
+ // variable specified by delegate.resultName (see delegateYield).
+ context[delegate.resultName] = info.value;
+
+ // Resume execution at the desired location (see delegateYield).
+ context.next = delegate.nextLoc;
+
+ // If context.method was "throw" but the delegate handled the
+ // exception, let the outer generator proceed normally. If
+ // context.method was "next", forget context.arg since it has been
+ // "consumed" by the delegate iterator. If context.method was
+ // "return", allow the original .return call to continue in the
+ // outer generator.
+ if (context.method !== "return") {
+ context.method = "next";
+ context.arg = undefined$1;
+ }
+
+ } else {
+ // Re-yield the result returned by the delegate method.
+ return info;
+ }
+
+ // The delegate iterator is finished, so forget it and continue with
+ // the outer generator.
+ context.delegate = null;
+ return ContinueSentinel;
+ }
+
+ // Define Generator.prototype.{next,throw,return} in terms of the
+ // unified ._invoke helper method.
+ defineIteratorMethods(Gp);
+
+ define(Gp, toStringTagSymbol, "Generator");
+
+ // A Generator should always return itself as the iterator object when the
+ // @@iterator function is called on it. Some browsers' implementations of the
+ // iterator prototype chain incorrectly implement this, causing the Generator
+ // object to not be returned from this call. This ensures that doesn't happen.
+ // See https://github.com/facebook/regenerator/issues/274 for more details.
+ define(Gp, iteratorSymbol, function() {
+ return this;
+ });
+
+ define(Gp, "toString", function() {
+ return "[object Generator]";
+ });
+
+ function pushTryEntry(locs) {
+ var entry = { tryLoc: locs[0] };
+
+ if (1 in locs) {
+ entry.catchLoc = locs[1];
+ }
+
+ if (2 in locs) {
+ entry.finallyLoc = locs[2];
+ entry.afterLoc = locs[3];
+ }
+
+ this.tryEntries.push(entry);
+ }
+
+ function resetTryEntry(entry) {
+ var record = entry.completion || {};
+ record.type = "normal";
+ delete record.arg;
+ entry.completion = record;
+ }
+
+ function Context(tryLocsList) {
+ // The root entry object (effectively a try statement without a catch
+ // or a finally block) gives us a place to store values thrown from
+ // locations where there is no enclosing try statement.
+ this.tryEntries = [{ tryLoc: "root" }];
+ tryLocsList.forEach(pushTryEntry, this);
+ this.reset(true);
+ }
+
+ exports.keys = function(object) {
+ var keys = [];
+ for (var key in object) {
+ keys.push(key);
+ }
+ keys.reverse();
+
+ // Rather than returning an object with a next method, we keep
+ // things simple and return the next function itself.
+ return function next() {
+ while (keys.length) {
+ var key = keys.pop();
+ if (key in object) {
+ next.value = key;
+ next.done = false;
+ return next;
+ }
+ }
+
+ // To avoid creating an additional object, we just hang the .value
+ // and .done properties off the next function object itself. This
+ // also ensures that the minifier will not anonymize the function.
+ next.done = true;
+ return next;
+ };
+ };
+
+ function values(iterable) {
+ if (iterable) {
+ var iteratorMethod = iterable[iteratorSymbol];
+ if (iteratorMethod) {
+ return iteratorMethod.call(iterable);
+ }
+
+ if (typeof iterable.next === "function") {
+ return iterable;
+ }
+
+ if (!isNaN(iterable.length)) {
+ var i = -1, next = function next() {
+ while (++i < iterable.length) {
+ if (hasOwn.call(iterable, i)) {
+ next.value = iterable[i];
+ next.done = false;
+ return next;
+ }
+ }
+
+ next.value = undefined$1;
+ next.done = true;
+
+ return next;
+ };
+
+ return next.next = next;
+ }
+ }
+
+ // Return an iterator with no values.
+ return { next: doneResult };
+ }
+ exports.values = values;
+
+ function doneResult() {
+ return { value: undefined$1, done: true };
+ }
+
+ Context.prototype = {
+ constructor: Context,
+
+ reset: function(skipTempReset) {
+ this.prev = 0;
+ this.next = 0;
+ // Resetting context._sent for legacy support of Babel's
+ // function.sent implementation.
+ this.sent = this._sent = undefined$1;
+ this.done = false;
+ this.delegate = null;
+
+ this.method = "next";
+ this.arg = undefined$1;
+
+ this.tryEntries.forEach(resetTryEntry);
+
+ if (!skipTempReset) {
+ for (var name in this) {
+ // Not sure about the optimal order of these conditions:
+ if (name.charAt(0) === "t" &&
+ hasOwn.call(this, name) &&
+ !isNaN(+name.slice(1))) {
+ this[name] = undefined$1;
+ }
+ }
+ }
+ },
+
+ stop: function() {
+ this.done = true;
+
+ var rootEntry = this.tryEntries[0];
+ var rootRecord = rootEntry.completion;
+ if (rootRecord.type === "throw") {
+ throw rootRecord.arg;
+ }
+
+ return this.rval;
+ },
+
+ dispatchException: function(exception) {
+ if (this.done) {
+ throw exception;
+ }
+
+ var context = this;
+ function handle(loc, caught) {
+ record.type = "throw";
+ record.arg = exception;
+ context.next = loc;
+
+ if (caught) {
+ // If the dispatched exception was caught by a catch block,
+ // then let that catch block handle the exception normally.
+ context.method = "next";
+ context.arg = undefined$1;
+ }
+
+ return !! caught;
+ }
+
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ var record = entry.completion;
+
+ if (entry.tryLoc === "root") {
+ // Exception thrown outside of any try block that could handle
+ // it, so set the completion value of the entire function to
+ // throw the exception.
+ return handle("end");
+ }
+
+ if (entry.tryLoc <= this.prev) {
+ var hasCatch = hasOwn.call(entry, "catchLoc");
+ var hasFinally = hasOwn.call(entry, "finallyLoc");
+
+ if (hasCatch && hasFinally) {
+ if (this.prev < entry.catchLoc) {
+ return handle(entry.catchLoc, true);
+ } else if (this.prev < entry.finallyLoc) {
+ return handle(entry.finallyLoc);
+ }
+
+ } else if (hasCatch) {
+ if (this.prev < entry.catchLoc) {
+ return handle(entry.catchLoc, true);
+ }
+
+ } else if (hasFinally) {
+ if (this.prev < entry.finallyLoc) {
+ return handle(entry.finallyLoc);
+ }
+
+ } else {
+ throw new Error("try statement without catch or finally");
+ }
+ }
+ }
+ },
+
+ abrupt: function(type, arg) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.tryLoc <= this.prev &&
+ hasOwn.call(entry, "finallyLoc") &&
+ this.prev < entry.finallyLoc) {
+ var finallyEntry = entry;
+ break;
+ }
+ }
+
+ if (finallyEntry &&
+ (type === "break" ||
+ type === "continue") &&
+ finallyEntry.tryLoc <= arg &&
+ arg <= finallyEntry.finallyLoc) {
+ // Ignore the finally entry if control is not jumping to a
+ // location outside the try/catch block.
+ finallyEntry = null;
+ }
+
+ var record = finallyEntry ? finallyEntry.completion : {};
+ record.type = type;
+ record.arg = arg;
+
+ if (finallyEntry) {
+ this.method = "next";
+ this.next = finallyEntry.finallyLoc;
+ return ContinueSentinel;
+ }
+
+ return this.complete(record);
+ },
+
+ complete: function(record, afterLoc) {
+ if (record.type === "throw") {
+ throw record.arg;
+ }
+
+ if (record.type === "break" ||
+ record.type === "continue") {
+ this.next = record.arg;
+ } else if (record.type === "return") {
+ this.rval = this.arg = record.arg;
+ this.method = "return";
+ this.next = "end";
+ } else if (record.type === "normal" && afterLoc) {
+ this.next = afterLoc;
+ }
+
+ return ContinueSentinel;
+ },
+
+ finish: function(finallyLoc) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.finallyLoc === finallyLoc) {
+ this.complete(entry.completion, entry.afterLoc);
+ resetTryEntry(entry);
+ return ContinueSentinel;
+ }
+ }
+ },
+
+ "catch": function(tryLoc) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.tryLoc === tryLoc) {
+ var record = entry.completion;
+ if (record.type === "throw") {
+ var thrown = record.arg;
+ resetTryEntry(entry);
+ }
+ return thrown;
+ }
+ }
+
+ // The context.catch method must only be called with a location
+ // argument that corresponds to a known catch block.
+ throw new Error("illegal catch attempt");
+ },
+
+ delegateYield: function(iterable, resultName, nextLoc) {
+ this.delegate = {
+ iterator: values(iterable),
+ resultName: resultName,
+ nextLoc: nextLoc
+ };
+
+ if (this.method === "next") {
+ // Deliberately forget the last sent value so that we don't
+ // accidentally pass it on to the delegate.
+ this.arg = undefined$1;
+ }
+
+ return ContinueSentinel;
+ }
+ };
+
+ // Regardless of whether this script is executing as a CommonJS module
+ // or not, return the runtime object so that we can declare the variable
+ // regeneratorRuntime in the outer scope, which allows this module to be
+ // injected easily by `bin/regenerator --include-runtime script.js`.
+ return exports;
+
+ }(
+ // If this script is executing as a CommonJS module, use module.exports
+ // as the regeneratorRuntime namespace. Otherwise create a new empty
+ // object. Either way, the resulting object will be used to initialize
+ // the regeneratorRuntime variable at the top of this file.
+ module.exports
+ ));
+
+ try {
+ regeneratorRuntime = runtime;
+ } catch (accidentalStrictMode) {
+ // This module should not be running in strict mode, so the above
+ // assignment should always work unless something is misconfigured. Just
+ // in case runtime.js accidentally runs in strict mode, in modern engines
+ // we can explicitly access globalThis. In older engines we can escape
+ // strict mode using a global Function call. This could conceivably fail
+ // if a Content Security Policy forbids using Function, but in that case
+ // the proper solution is to fix the accidental strict mode problem. If
+ // you've misconfigured your bundler to force strict mode and applied a
+ // CSP to forbid Function, and you're not willing to fix either of those
+ // problems, please detail your unique predicament in a GitHub issue.
+ if (typeof globalThis === "object") {
+ globalThis.regeneratorRuntime = runtime;
+ } else {
+ Function("r", "regeneratorRuntime = r")(runtime);
+ }
+ }
+ }(runtime));
+
+ class ShiftCipher{constructor(){this.makeCipherMap({chars:"abcdefghijklmnopqrstuvqxyz",shift:13});}encode(str){return this.rotateChars(str,this.cipherMap)}decode(str){return this.rotateChars(str,this.reversedCipherMap)}makeCipherMap({chars:chars,shift:shift}){shift%=chars.length;let shiftedChars=chars.slice(shift)+chars.slice(0,shift);shiftedChars+=shiftedChars.toUpperCase(),chars+=chars.toUpperCase(),this.cipherMap=new Map(chars.split("").map((i,j)=>[i,shiftedChars[j]]));}get reversedCipherMap(){const reversedChars=[...this.cipherMap.entries()].map(i=>i.reverse());return new Map(reversedChars)}rotateChars(str,cipherMap){return str.split("").map(i=>cipherMap.get(i)||i).join("")}}
+
+ function decode(str, n, alphabet) {
+ var cipher = new ShiftCipher();
+ cipher.makeCipherMap({
+ chars: alphabet,
+ shift: n
+ });
+ return cipher.decode(str.toLocaleUpperCase());
+ }
+ function encode(str, n, alphabet) {
+ var cipher = new ShiftCipher();
+ cipher.makeCipherMap({
+ chars: alphabet,
+ shift: n
+ });
+ return cipher.encode(str.toLocaleUpperCase());
+ }
+
+ function start(letters) {
+ do {
+ var type = prompt('Hvort viltu kóða eða afkóða streng?') || '';
+ var typeLowered = type.toLocaleLowerCase();
+
+ if (typeLowered !== 'kóða' && typeLowered !== 'afkóða') {
+ alert("Veit ekki hva\xF0a a\xF0ger\xF0 \u201E".concat(type, "\u201C er. Reyndu aftur."));
+ continue;
+ }
+
+ var nInput = prompt('Hversu mikið á að hliðra streng?');
+ var n = Number.parseInt(nInput, 10);
+
+ if (!Number.isInteger(n) || n < 1 || n > letters.length - 1) {
+ alert("".concat(nInput, " er ekki r\xE9tt. Reyndu aftur."));
+ continue;
+ }
+
+ var str = prompt('Gefðu upp strenginn sem á að hliðra:');
+
+ if (!str || str.length === 0) {
+ alert('Þú gafst ekki upp streng. Reyndu aftur.');
+ continue;
+ }
+
+ var invalid = [];
+
+ for (var i = 0; i < str.length; i++) {
+ var letter = (str[i] || '').toLocaleUpperCase();
+
+ if (letters.indexOf(letter) < 0) {
+ invalid.push(letter);
+ }
+ }
+
+ if (invalid.length > 0) {
+ alert("\xDE\xFA gafst upp \xF3l\xF6glega stafi: ".concat(invalid.join(', '), "."));
+ continue;
+ }
+
+ var result = '';
+
+ if (typeLowered === 'kóða') {
+ result = encode(str, n, letters);
+ } else {
+ result = decode(str, n, letters);
+ }
+
+ alert("Ni\xF0ursta\xF0a:\n".concat(result)); // eslint-disable-next-line no-restricted-globals
+ } while (confirm('Aftur?'));
+ }
+
+ var LETTERS = 'AÁBDÐEÉFGHIÍJKLMNOÓPRSTUÚVXYÝÞÆÖ'; // erum ekki að nota async await, en með því að merkja fallið sem async
+ // mun babel innifela kóða til að láta virka og stækka bundle helling
+
+ document.addEventListener('DOMContentLoaded', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
+ return regeneratorRuntime.wrap(function _callee$(_context) {
+ while (1) {
+ switch (_context.prev = _context.next) {
+ case 0:
+ console.log(encode('HALLÓ', 3, LETTERS));
+ start(LETTERS);
+
+ case 2:
+ case "end":
+ return _context.stop();
+ }
+ }
+ }, _callee);
+ })));
+
+})();
diff --git a/namsefni/37.tools/daemi/index.html b/namsefni/37.tools/daemi/index.html
new file mode 100644
index 0000000..e113876
--- /dev/null
+++ b/namsefni/37.tools/daemi/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
Fyrirlestur 12 tækja dæmi
+
+
+
shift-cipher
+
+
+
diff --git a/namsefni/37.tools/daemi/package.json b/namsefni/37.tools/daemi/package.json
new file mode 100644
index 0000000..eed2825
--- /dev/null
+++ b/namsefni/37.tools/daemi/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "tools",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "build": "rollup -c",
+ "rollup-watch": "rollup -c -w"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@babel/cli": "^7.16.0",
+ "@babel/core": "^7.16.0",
+ "@babel/preset-env": "^7.16.0",
+ "@rollup/plugin-babel": "^5.3.0",
+ "@rollup/plugin-commonjs": "^21.0.1",
+ "@rollup/plugin-node-resolve": "^13.0.6",
+ "rollup": "^2.59.0",
+ "rollup-plugin-terser": "^7.0.2"
+ },
+ "dependencies": {
+ "shift-cipher": "^1.0.0"
+ }
+}
diff --git a/namsefni/37.tools/daemi/readme.md b/namsefni/37.tools/daemi/readme.md
new file mode 100644
index 0000000..3a6648d
--- /dev/null
+++ b/namsefni/37.tools/daemi/readme.md
@@ -0,0 +1,11 @@
+# Rollup + babel + npm dæmi
+
+Notar Rollup með babel, commonjs og resolve-node plugin til að nýta [`shift-cipher`](https://github.com/Bwca/shift-cipher) til að útfæra Ceasar cipher.
+
+Keyrt með
+
+```bash
+npm run build
+```
+
+opna síðan `index.html` í vafra, _buildaður_ kóði verður í `dist/`.
diff --git a/namsefni/37.tools/daemi/rollup.config.js b/namsefni/37.tools/daemi/rollup.config.js
new file mode 100644
index 0000000..bf577b4
--- /dev/null
+++ b/namsefni/37.tools/daemi/rollup.config.js
@@ -0,0 +1,32 @@
+import { babel } from '@rollup/plugin-babel';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import { terser } from 'rollup-plugin-terser';
+import commonjs from '@rollup/plugin-commonjs';
+
+const config = {
+ input: 'src/index.js',
+ output: {
+ file: 'dist/bundle.js',
+ format: 'iife'
+ },
+ plugins: [
+ nodeResolve({ preferBuiltins: false }),
+ commonjs(),
+ babel({
+ babelHelpers: 'bundled',
+ exclude: 'node_modules/**',
+ presets: [
+ [
+ '@babel/preset-env', {
+ useBuiltIns: 'usage',
+ corejs: 2,
+ targets: '> 0.25%, not dead',
+ },
+ ],
+ ],
+ }),
+ // terser()
+ ]
+};
+
+export default config;
diff --git a/namsefni/37.tools/daemi/src/cipher.js b/namsefni/37.tools/daemi/src/cipher.js
new file mode 100644
index 0000000..f52150c
--- /dev/null
+++ b/namsefni/37.tools/daemi/src/cipher.js
@@ -0,0 +1,23 @@
+import { ShiftCipher } from 'shift-cipher';
+
+export function decode(str, n, alphabet) {
+ const cipher = new ShiftCipher();
+
+ cipher.makeCipherMap({
+ chars: alphabet,
+ shift: n,
+ });
+
+ return cipher.decode(str.toLocaleUpperCase());
+}
+
+export function encode(str, n, alphabet) {
+ const cipher = new ShiftCipher();
+
+ cipher.makeCipherMap({
+ chars: alphabet,
+ shift: n,
+ });
+
+ return cipher.encode(str.toLocaleUpperCase());
+}
diff --git a/namsefni/37.tools/daemi/src/index.js b/namsefni/37.tools/daemi/src/index.js
new file mode 100644
index 0000000..ad94c0a
--- /dev/null
+++ b/namsefni/37.tools/daemi/src/index.js
@@ -0,0 +1,13 @@
+// Grunnskrá sem vírar saman virkni
+
+import { encode } from './cipher';
+import ui from './ui';
+
+const LETTERS = 'AÁBDÐEÉFGHIÍJKLMNOÓPRSTUÚVXYÝÞÆÖ';
+
+// erum ekki að nota async await, en með því að merkja fallið sem async
+// mun babel innifela kóða til að láta virka og stækka bundle helling
+document.addEventListener('DOMContentLoaded', async () => {
+ console.log(encode('HALLÓ', 3, LETTERS));
+ ui(LETTERS);
+});
diff --git a/namsefni/37.tools/daemi/src/ui.js b/namsefni/37.tools/daemi/src/ui.js
new file mode 100644
index 0000000..ac11c90
--- /dev/null
+++ b/namsefni/37.tools/daemi/src/ui.js
@@ -0,0 +1,53 @@
+/* eslint-disable no-alert */
+
+import { decode, encode } from './cipher';
+
+export default function start(letters) {
+ do {
+ const type = prompt('Hvort viltu kóða eða afkóða streng?') || '';
+ const typeLowered = type.toLocaleLowerCase();
+
+ if (typeLowered !== 'kóða' && typeLowered !== 'afkóða') {
+ alert(`Veit ekki hvaða aðgerð „${type}“ er. Reyndu aftur.`);
+ continue;
+ }
+
+ const nInput = prompt('Hversu mikið á að hliðra streng?');
+ const n = Number.parseInt(nInput, 10);
+ if (!Number.isInteger(n) || n < 1 || n > letters.length - 1) {
+ alert(`${nInput} er ekki rétt. Reyndu aftur.`);
+ continue;
+ }
+
+ const str = prompt('Gefðu upp strenginn sem á að hliðra:');
+
+ if (!str || str.length === 0) {
+ alert('Þú gafst ekki upp streng. Reyndu aftur.');
+ continue;
+ }
+
+ const invalid = [];
+ for (let i = 0; i < str.length; i++) {
+ const letter = (str[i] || '').toLocaleUpperCase();
+ if (letters.indexOf(letter) < 0) {
+ invalid.push(letter);
+ }
+ }
+
+ if (invalid.length > 0) {
+ alert(`Þú gafst upp ólöglega stafi: ${invalid.join(', ')}.`);
+ continue;
+ }
+
+ let result = '';
+ if (typeLowered === 'kóða') {
+ result = encode(str, n, letters);
+ } else {
+ result = decode(str, n, letters);
+ }
+
+ alert(`Niðurstaða:\n${result}`);
+
+ // eslint-disable-next-line no-restricted-globals
+ } while (confirm('Aftur?'));
+}
diff --git a/namsefni/37.tools/readme.md b/namsefni/37.tools/readme.md
new file mode 100644
index 0000000..39a3a7a
--- /dev/null
+++ b/namsefni/37.tools/readme.md
@@ -0,0 +1,13 @@
+# Tæki og tól
+
+[Fyrirlestur](1.tools.md), [vídeó](https://youtu.be/ALaPtDXEdgY)
+
+## Dæmi
+
+* [Dæmi](daemi/)
+
+## Ítarefni
+
+* [Babel](https://babeljs.io/)
+* [`corejs`](https://github.com/zloirock/core-js)
+* [Rollup](https://rollupjs.org/)
diff --git a/namsefni/38.objects/1.objects.md b/namsefni/38.objects/1.objects.md
new file mode 100644
index 0000000..b60309e
--- /dev/null
+++ b/namsefni/38.objects/1.objects.md
@@ -0,0 +1,561 @@
+---
+title: Fyrirlestur – Hlutir
+---
+
+# Fyrirlestur – Hlutir
+
+## Vefforritun 1 — TÖL107G
+
+### Ólafur Sverrir Kjartansson, [osk@hi.is](mailto:osk@hi.is)
+
+---
+
+## Hlutir og hjúpun
+
+* Objects & [encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
+* Ein af aðal hugmyndum hlutbundinnar forritunar er að skipta forritum upp og láta hvern hluta bera ábyrgð á eigin stöðu
+* Hjúpum virkni og flækjustig, vinnum með í gegnum _interface_
+
+***
+
+## Methods
+
+* Method er fall á hlut
+* Hvað ef við viljum nálgast gögn á hlutnum úr falli?
+* Notum `this`
+
+***
+
+## `this`
+
+* [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) hagar sér öðruvísi í JavaScript en í öðrum málum
+* Skilgreinist af því hvernig kallað er í fallið
+* Utan hluta er `this` global hluturinn, þ.e. þar sem allt er geymt (í browser `window`)
+
+***
+
+```javascript
+function logThis() { console.log(this); }
+const Obj = {
+ logThis() { console.log(this); },
+};
+const btn = document.querySelector('button');
+btn.addEventListener('click', logThis);
+
+console.log(this); // window
+logThis(); // window
+Obj.logThis(); // {logThis: f}
+btn.click(); //
+```
+
+***
+
+* [`this` dæmi](./daemi/01.this.html)
+
+***
+
+```javascript
+const rabbit = {
+ type: 'the',
+};
+rabbit.speak = (line) => {
+ console.log(
+ `${this.type} rabbit says '${line}'`
+ );
+};
+
+rabbit.speak('I’m alive.');
+// → undefined rabbit says 'I’m alive.'
+```
+
+***
+
+```javascript
+const rabbit = {
+ type: 'the',
+ speak(line) {
+ console.log(
+ `${this.type} rabbit says '${line}'`
+ );
+ }
+};
+
+rabbit.speak('I’m alive.');
+// → the rabbit says 'I’m alive.'
+```
+
+***
+
+## Apply & call
+
+* Leyfir okkur að kalla í föll með ákveðnu `this`
+* `apply` – köllum í fall með gefnu `this` og _fylki_ af arguments
+* `call` – köllum í fall með gefnu `this` og _lista_ af arguments
+
+***
+
+
+
+```javascript
+function speak(line) {
+ console.log(
+ `${this.type} rabbit says ${line}`
+ );
+}
+// festum fallið á hlutinn 👇
+const old = { type: 'old', speak };
+const hungy = { type: 'hungry', speak };
+
+old.speak('Oh my');
+// old rabbit says Oh my
+hungy.speak('I could use a carrot');
+// hungry rabbit says I could use a carrot
+```
+
+***
+
+
+
+```javascript
+function speak(line) {
+ console.log(
+ `${this.type} rabbit says ${line}`
+ );
+}
+
+const old = { type: 'old' };
+
+// 👇 köllum í fallið _með_ hlutnum
+speak.apply(old, ['Oh my']);
+// old rabbit says Oh my
+speak.call({ type: 'hungry' }, 'Yum');
+// hungry rabbit says Yum
+```
+
+***
+
+## Bind
+
+* Leyfir okkur að festa, _binda_, `this` fyrir föll
+* Kemur okkur ekki á óvart hvað `this` er
+* Getum líka bundið argument...
+
+***
+
+## Bind – dæmi
+
+
+
+```javascript
+function speak(line) {
+ console.log(
+ `${this.type} rabbit says ${line}`
+ );
+}
+const blueRabbit = { type: 'blue' };
+
+// bindum fall v/hlut 👇 fáum nýtt fall!
+const bound = speak.bind(blueRabbit);
+bound('hi'); // blue rabbit says hi
+```
+
+
+
+```javascript
+// Getum kallað strax í fall 👇
+speak.bind(blueRabbit, 'bye')();
+// blue rabbit says bye
+```
+
+***
+
+## Arrow föll
+
+* Arrow föll eru öðruvísi—endurskilgreina ekki `this`
+* `this` er skilgreint eins og það var skilgreint í scope kringum fall
+
+***
+
+```javascript
+function normalize() {
+ function inner(n) {
+ // 👇 vísar í window! 🙀
+ return n / this.length;
+ }
+ // 👇 vísar í „réttan“ hlut 🥴
+ console.log(this.coords.map(inner));
+}
+
+const o = { coords: [0, 2, 3], length: 5 };
+
+normalize.call(o);
+// → [NaN, Infinity, Infinity]
+```
+
+***
+
+
+
+```javascript
+function normalize() {
+ console.log(
+ // 👇 vísar í réttan hlut
+ this.coords.map(
+ // 👇 vísar í réttan hlut
+ n => n / this.length
+ )
+ );
+}
+
+const o = { coords: [0, 2, 3], length: 5 };
+
+normalize.call(o);
+// → [0, 0.4, 0.6]
+```
+
+***
+
+```javascript
+function normalize() {
+ const inner = (n) => {
+ // 👇 vísar í réttan hlut 😊
+ return n / this.length;
+ }
+ console.log(this.coords.map(inner));
+}
+
+const o = { coords: [0, 2, 3], length: 5 };
+
+normalize.call(o);
+// → [0, 0.4, 0.6]
+```
+
+---
+
+## Prótótýpur
+
+* [Flestir hlutir hafa _prótótýpu_](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes), hlutur sem geymir fleiri properties
+ * Fyrir utan `null` og `undefined`
+* Ef við biðjum um property á hlut sem er ekki skilgreint á hlut er leitað á prótótýpu hans
+
+***
+
+* Í grunninn höfum við `Object`
+ * Eða nánar tiltekið `Object.prototype`
+* `Object.getPrototypeOf()` skilar _hvaða_ prótótýpu gildi hefur
+
+***
+
+```javascript
+const empty = {};
+console.log(empty.toString);
+// → function toString() { … }
+console.log(empty.toString());
+// → [object Object]
+```
+
+```javascript
+console.log(1.toString()); // → "1"
+```
+
+```javascript
+console.log(NaN.toString()); // → "NaN"
+```
+
+```javascript
+console.log(null.toString());
+// → TypeError: null has no properties
+```
+
+***
+
+## Aðrar prótótýpur
+
+* Aðrir hlutir hafa aðrar prótótýpur
+ * `[]` hefur `Array.prototype`
+ * Föll hafa `Function.prototype`
+* Þessar prótótýpur hafa sínar eigin prótótýpur!
+ * Mynda [prótótýpu keðju](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)
+
+***
+
+
+
+```javascript
+const empty = Object.getPrototypeOf({});
+console.log(
+ empty === Object.prototype
+);
+// → true
+```
+
+
+
+```javascript
+console.log(
+ Object.getPrototypeOf(Object.prototype)
+);
+// → null
+```
+
+***
+
+
+
+```javascript
+const funcProto = Object.getPrototypeOf(
+ Math.max
+);
+
+console.log(
+ funcProto === Function.prototype
+);
+// → true
+```
+
+
+
+```javascript
+console.log(
+ Object.getPrototypeOf(funcProto) ===
+ Object.prototype
+);
+// → true
+```
+
+***
+
+
+
+```javascript
+const array = Object.getPrototypeOf([]);
+
+console.log(
+ array === Array.prototype
+);
+// → true
+```
+
+***
+
+
+
+```javascript
+const objProto = Object.getPrototypeOf({});
+console.log(
+ Object.getPrototypeOf(objProto)
+);
+// → null
+// keðjan endar á Object prototype!
+```
+
+***
+
+## Prótótýpu keðjan
+
+* Hlutir fá eigindi frá prótótýpunni en eigindi á hlutnum hafa aldrei áhrif á keðjuna
+ * Getum ekki skrifað yfir hluti á prótótýpu
+* Að setja eigindi á hlut setur það alltaf á honum sjálfum
+
+***
+
+* Ef við biðjum um eigindi á hlut er reglan:
+ * Ef skilgreint beint á hlut er því skilað
+ * Ef ekki förum við upp prótótýpu keðjuna og leitum þar
+ * Ef finnst hvergi er `undefined` skilað
+
+***
+
+## Hlutir með ákveðna prótótýpu
+
+* `Object.create()` býr til hlut með ákveðinni prótótýpu
+* Leið til að hópa saman föllum
+* Prótótýpan geymir sameiginlegu föllin
+
+***
+
+
+
+```javascript
+const protoRabbit = {
+ speak(line) {
+ console.log(
+ `${this.type} rabbit says '${line}'`
+ );
+ }
+};
+const killer = Object.create(protoRabbit);
+```
+
+```javascript
+killer.type = 'killer';
+// 👇 speak fall kemur frá prototype
+killer.speak('SKREEEE!');
+// → killer rabbit says 'SKREEEE!'
+```
+
+***
+
+## Klasar (classes)
+
+* Í JavaScript höfum við ekki klasa eins og í klassískum hlutbundnum málum
+* Notum prótótýpu til að erfa frá öðrum hlutum
+
+***
+
+* Í klassískum erfðum
+ * Hlutir eru tilvik af klösum
+ * Klasar erfa frá öðrum klösum
+* Með prótótýpum
+ * Engir klasar
+ * Hlutir erfa frá öðrum hlutum
+
+***
+
+## Smiðir og new
+
+* Í JavaScript eru smiðir föll, skýrð með stórum staf (hefð, ekki krafa)
+* Þegar við notum `new` og tilgreinum fall búum við til nýjan hlut þar sem `this` bendir á hlutinn
+* Skilar sjálfkrafa `this` sem hlut vegna `new` ef engu er skilað
+* Bætum við prótótýpu til að bæta við sameiginlegum föllum
+
+***
+
+
+
+```javascript
+function Rabbit(type) {
+ this.type = type;
+}
+Rabbit.prototype.speak = function (line) {
+ console.log(
+ `The ${this.type} rabbit says ${line}`
+ );
+};
+```
+
+
+
+```javascript
+const weirdRabbit = new Rabbit('weird');
+console.log(weirdRabbit);
+// Rabbit {type: "weird"}
+weirdRabbit.speak('🐰');
+// The weird rabbit says '🐰'
+```
+
+***
+
+## Yfirskrifuð eigindi
+
+* Að setja eigindi á hlut setur það alltaf á honum sjálfum
+* Getum nýtt til að yfirskrifa eigindi frá prótótýpu
+ * T.d. `toString()` til að skila sértæku gildi fyrir okkar hlut
+
+***
+
+
+
+```javascript
+Rabbit.prototype.teeth = 'small';
+const killerRabbit = new Rabbit('killer');
+console.log(killerRabbit.teeth);
+// → small
+```
+
+
+
+```javascript
+killerRabbit.teeth = 'long, sharp, bloody';
+console.log(killerRabbit.teeth);
+// → long, sharp, and bloody
+```
+
+***
+
+![Erfðir á rabbits](img/rabbits.svg "Erfðir á rabbits")
+
+***
+
+
+
+```javascript
+function Rabbit(type) {
+ this.type = type;
+}
+
+const fooRabbit = new Rabbit('foo');
+console.log(fooRabbit.toString());
+// "[object Object]"
+
+Rabbit.prototype.toString = function () {
+ return `Rabbit of type ${this.type}`;
+};
+
+console.log(fooRabbit.toString());
+// "Rabbit of type foo"
+```
+
+---
+
+## Classes
+
+* Í ECMAScript 6 er [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) skilgreint
+* _Syntactic sugar_ til að herma eftir klassískum OO hlutum með prótótýpum
+ * Undirliggjandi skilgreining notar áfram prótótýpur
+* `extends`, `constructor`, `super`, `static` og getters og setters
+
+***
+
+
+
+```javascript
+class Rabbit {
+ constructor(type) {
+ this.type = type;
+ }
+ speak(line) {
+ console.log(
+ `${this.type} rabbit says '${line}'`
+ );
+ }
+ toString() {
+ return `Rabbit of type ${this.type}`;
+ }
+}
+
+const killerRabbit = new Rabbit('killer');
+```
+
+***
+
+
+
+```javascript
+class Foo {
+ constructor(name) {
+ this._name = name;
+ }
+
+ hi() { return 'hi!'; }
+
+ static bar() { return 1; }
+
+ get name() { return this._name; }
+
+ set name(value) { this._name = value; }
+}
+```
+
+***
+
+
+
+```javascript
+const foo = new Foo('Asdf');
+foo.hi(); // "hi!"
+foo.name = 'bar';
+foo.name; // "bar"
+
+Foo.bar(); // 1
+```
diff --git a/namsefni/38.objects/daemi/01.this.html b/namsefni/38.objects/daemi/01.this.html
new file mode 100644
index 0000000..914056b
--- /dev/null
+++ b/namsefni/38.objects/daemi/01.this.html
@@ -0,0 +1,29 @@
+
+
+
+
+ this
+
+
+ takki
+
+
+
diff --git a/namsefni/38.objects/img/rabbits.svg b/namsefni/38.objects/img/rabbits.svg
new file mode 100644
index 0000000..5ee2dac
--- /dev/null
+++ b/namsefni/38.objects/img/rabbits.svg
@@ -0,0 +1,13 @@
+
+
+toString: <function> ... teeth: "small" speak: <function> killerRabbit teeth: "long, sharp, ..." type: "killer" Rabbit prototype Object create: <function> prototype ...
diff --git a/namsefni/38.objects/readme.md b/namsefni/38.objects/readme.md
new file mode 100644
index 0000000..76f1463
--- /dev/null
+++ b/namsefni/38.objects/readme.md
@@ -0,0 +1,19 @@
+# Hlutir
+
+[Fyrirlestur: Hlutir](1.objects.md), [vídeó (28:35)](https://youtu.be/wqQz9ETMEdg)
+
+## Lesefni
+
+* [Eloquent JavaScript, kafli 6: The Secret Life of Objects](https://eloquentjavascript.net/06_object.html)
+
+## Dæmi
+
+* [`this` dæmi](./daemi/01.this.html)
+
+## Ítarefni
+
+* [Encapsulation á Wikipedia](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
+* [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)
+* [Object prototypes](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes)
+* [Inheritance and the prototype chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)
+* [Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
diff --git a/namsefni/39.testing/1.testing.md b/namsefni/39.testing/1.testing.md
new file mode 100644
index 0000000..85b9c58
--- /dev/null
+++ b/namsefni/39.testing/1.testing.md
@@ -0,0 +1,245 @@
+---
+title: Fyrirlestur – Prófanir
+---
+
+# Fyrirlestur – Prófanir
+
+## Vefforritun 1 — TÖL107G
+
+### Ólafur Sverrir Kjartansson, [osk@hi.is](mailto:osk@hi.is)
+
+---
+
+## Sjálfvirkar prófanir
+
+* Þegar við skrifum kóða erum við alltaf að athuga hvernig hann virkar
+* Gerum það yfirleitt handvirkt
+* Getum eytt tíma og skrifað próf fyrir þessa handvirku athugun
+* Höfum séð aðeins með `console.assert()`
+
+***
+
+## Kostir prófa
+
+* Getum keyrt mörg próf hratt, aftur og aftur
+* „Notum“ kóðann okkar á meðan við skrifum hann, getum endað með betra API
+* Gefur okkur ákveðið traust á virkni og að við munum ekki brjóta hana seinna meir
+
+***
+
+## Ókostir prófa
+
+* Það tekur töluvert lengri tíma að skrifa próf en að athuga eitthvað handvirkt _í fyrstu_
+* Geta gefið okkur _falskt_ öryggi um að það séu ekki villur í kóðanum okkar því við skrifuðum próf
+* Við breytingar á kóða þarf að uppfæra próf
+ * ef það er erfitt er mun auðveldara að slökkva bara á þeim
+
+***
+
+## Unit test
+
+* [Unit test](https://en.wikipedia.org/wiki/Unit_testing) er ekki vel skilgreint hugtak en..
+* Próf á einni einingu í einu án þess að horfa á alla heildina
+ * Eining gæti verið fall, klasi, módull
+* Sumir segja að unit test eigi _ekki_ að snerta á I/O (fara yfir net, lesa af disk) eða einhverju fyrir utan einingu
+
+***
+
+* Hjálpa okkur við að komast að því hvernig við viljum smíða forritið okkar
+* Fáum endurgjöf hratt og örugglega meðan við erum að skrifa
+* Leyfa okkur að breyta kóða með vissu öryggi — erum með próf til staðar sem grípa villur
+
+***
+
+* Prófin geta komið í stað eða aukið við skjölun, sýna bókstaflega hvernig kerfið virkar
+* Fyrir villur sem finnast getum við skrifað próf áður en við lögum
+ * Minnkum líkur á að villa komi upp aftur
+
+***
+
+## Skilvirk test
+
+* Einföld & DRY (Don't Repeat Yourself)
+ * Einn hlutur í einu
+* Óhað röð sem þau eru keyrð í
+* Endurtakanleg (repeatable) með sömu niðurstöðum
+* _Hröð_, viljum ekki vera að bíða eftir keyrslu, eiga helst að keyra undir 1 sek
+
+***
+
+## Assertions — staðhæfingar
+
+* Við skrifum prófin okkar þ.a. þau staðhæfi eitthvað í lokin
+ * Við gefum rétt gildi og athugum hvort það sé eins
+ * `assert(result === 'foo');`
+* Ættum að hafa færri en fleiri staðhæfingar í hverju prófi
+* Ein leið til að skipuleggja próf er að fylgja [_arrange, act, assert_](http://wiki.c2.com/?ArrangeActAssert)
+
+***
+
+## Arrange, Act, Assert
+
+```javascript
+const input = 'bar'; // Arrange
+
+const result = reverse(input); // Act
+
+assert(result === 'rab'); // Assert
+```
+
+***
+
+## Test-driven development (TDD)
+
+Í [Test-driven development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) ítrum við:
+
+1. Skrifum próf sem bregst
+2. Skrifum kóða sem lætur öll prófi heppnast
+3. Hreinsum/refactorum kóða og keyrum próf
+
+***
+
+![TDD flæði](img/tdd.png "Mynd: https://en.wikipedia.org/wiki/File:Test-driven_development.PNG")
+
+***
+
+## Jest
+
+* [Jest](https://jestjs.io/) er JavaScript testing framework
+* Virkar með allskonar, t.d.: JavaScript, React, TypeScript, Node
+* Sækjum með
+ * `npm install --save-dev jest`
+
+***
+
+```javascript
+import { describe, expect, it } from '@jest/globals';
+import { reverse } from '../src/lib/reverse.js';
+
+describe('main.js', () => {
+ it('should reverse a string', () => {
+ const input = 'bar';
+
+ const result = reverse(input);
+
+ expect(result).toBe('rab');
+ });
+});
+```
+
+***
+
+Til að geta prófað kóða skrifaðann fyrir JavaScript modules, þurfum við að:
+
+* Setja `"type": "module"` í `package.json`
+* Keyra með Node með flagginu `--experimental-vm-modules` sem kveikir á stuðning
+ * `"test": "NODE_OPTIONS='--experimental-vm-modules' jest ./test/*.js"`
+
+---
+
+## Continuous integration
+
+* [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous_integration) er þegar við keyrum öll test við hvert commit í source control
+* „Integration“ kemur frá því að við erum að _integratea_ við `main` branch
+ * Ef það er gert sjaldan getur komið upp staða þar sem gefa á út og það þarf að mergea mörgu í einu
+ * _Integration hell_
+* Ákveðið traust á því að `main` sé alltaf tilbúið til útgáfu
+
+***
+
+## Continuous deployment
+
+* [Continuous deployment (CD)](https://en.wikipedia.org/wiki/Continuous_deployment) er þegar við gefum `main` út á raunkerfi fyrir hverja breytingu sem stenst próf
+* Höldum `main` alltaf í _deployable_ ástandi
+* Hægt að gefa út oft á dag
+* Netlify uppsetningar, eins og við höfum verið að nota þær, nýta continuous deployment
+
+***
+
+## Netlify
+
+* Getum stillt þannig að Netlify keyri prófin okkar _áður_ en gefið út
+* Svo lengi sem testin okkar grípa ákveðnar villur, munu þær ekki komast út á raunkerfi
+* Fáum aukið öryggi í continuous deployment ferlið okkar
+
+***
+
+[Netlify CI dæmi](https://github.com/vefforritun/vef1-2021-netlify-ci)
+
+---
+
+## Önnur tól
+
+* Að nota sjálfvirk próf er gott, en oft þurfum við líka að nota „handvirk“ próf
+* Höfum notað í haust: HTML validator, Jigsaw CSS validator, aXe
+
+***
+
+## Performance atriði
+
+Hversu „performant“ vefirnir okkar er margþætt verkefni sem snertir á öllu sem viðkemur vefforritun, t.d.
+
+* Hvaða forritunarumhverfi við notum í bakenda
+* Hvernig við útfærum bakenda, t.d. tengingar við gagnagrunn og vefþjónustur
+* Hversu hratt við skilum efni á framenda
+
+***
+
+* Hvaða forritunarumhverfi við notum á framenda
+* Hve mikið af JavaScript við hlöðum inn
+* Hve mikið af efni (myndum, myndböndum) við vísum í
+* Hvort við séum að nota CDN
+* o.fl.
+
+---
+
+## Core Web Vitals
+
+* [Core Web Vitals](https://web.dev/vitals/) eru mælistikur frá Google sem eiga að mæla hluti sem hafa raunveruleg áhrif á notendur
+* Í dag eru þrír hlutir mældir
+
+***
+
+## Largest Contentful Paint (LCP)
+
+* [Largest Contentful Paint (LCP)](https://web.dev/lcp/) mælir hversu langan tíma það tekur að birta „mikilvægasta“ efni síðu
+ * Túlka mikilvægast sem stærsta
+* Áður var oft mælt [First Meaningful Paint (FMP)](https://web.dev/first-meaningful-paint/) sem tímann sem það tók fyrsta efni vefs að birtast
+
+***
+
+![LCP skjáskot](./img/lcp1.png)
+
+***
+
+![LCP skjáskot](./img/lcp2.png)
+
+***
+
+## First Input Delay (FID)
+
+* [First Input Delay (FID)](https://web.dev/fid/) mælir hversu langan tíma það tekur frá fyrstu gagnvirkni (interaction) þar til henni er svarað
+* Þetta getur verið hægt í byrjun því verið er að sækja gögn, vinna úr þeim eða eiga við DOM
+
+***
+
+## Cumulative Layout Shift (CLS)
+
+* [Cumulative Layout Shift (CLS)](https://web.dev/cls/) mælir hversu mikið efni á síðu færist til meðan verið er að klára að sækja öll gögn og teikna
+* Efni sem færist mikið til getur verið afsakplega hvimleitt og valdið því að maður ýti vitlaust eða missi af efni
+
+---
+
+## Performance debug
+
+* Ef við erum með hlut sem veldur hægagangi eða lækkar skor hjá okkur getur verið misauðvelt að laga þau
+* [Performance debug](https://developer.chrome.com/docs/devtools/evaluate-performance/) tólið í Chrome er kraftmikið og getur hjálpað við að debugga þetta
+ * Mjög mikið af upplýsingum og getur verið erfitt að átta sig á til að byrja með
+
+***
+
+## Tól
+
+* [Lighthouse](https://developers.google.com/web/tools/lighthouse/) í Chrome keyrir allskonar próf, t.d. fyrir Core Web Vitals og „Best Practices“
+* [PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/) bíður upp á að keyra svipuð próf á miðlægum þjón
+* [WebPageTest](https://www.webpagetest.org/) keyrir hraðamælingar, hversu vel svör frá bakenda eru, CDN notkun o.fl.
diff --git a/namsefni/39.testing/img/lcp1.png b/namsefni/39.testing/img/lcp1.png
new file mode 100644
index 0000000..ac995f4
Binary files /dev/null and b/namsefni/39.testing/img/lcp1.png differ
diff --git a/namsefni/39.testing/img/lcp2.png b/namsefni/39.testing/img/lcp2.png
new file mode 100644
index 0000000..b48ca49
Binary files /dev/null and b/namsefni/39.testing/img/lcp2.png differ
diff --git a/namsefni/39.testing/img/tdd.png b/namsefni/39.testing/img/tdd.png
new file mode 100644
index 0000000..64491d4
Binary files /dev/null and b/namsefni/39.testing/img/tdd.png differ
diff --git a/namsefni/39.testing/readme.md b/namsefni/39.testing/readme.md
new file mode 100644
index 0000000..574967e
--- /dev/null
+++ b/namsefni/39.testing/readme.md
@@ -0,0 +1,21 @@
+# Prófanir
+
+[Fyrirlestur: Prófanir](1.testing.md), [vídeó (44:56)](https://youtu.be/ukPMToSzFRc)
+
+## Ítarefni
+
+* [Encapsulation á Wikipedia](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
+* [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)
+* [Object prototypes](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes)
+* [Inheritance and the prototype chain](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)
+* [Classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
+* [Unit test](https://en.wikipedia.org/wiki/Unit_testing)
+* [Arrange Act Assert](http://wiki.c2.com/?ArrangeActAssert)
+* [Test-driven development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development)
+* [Continuous integration (CI)](https://en.wikipedia.org/wiki/Continuous_integration)
+* [Continuous deployment (CD)](https://en.wikipedia.org/wiki/Continuous_deployment)
+* [Core Web Vitals](https://web.dev/vitals/)
+* [Performance debug](https://developer.chrome.com/docs/devtools/evaluate-performance/)
+* [Lighthouse](https://developers.google.com/web/tools/lighthouse/)
+* [PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/)
+* [WebPageTest](https://www.webpagetest.org/)
diff --git a/namsefni/40.html5/1.html5.md b/namsefni/40.html5/1.html5.md
new file mode 100644
index 0000000..3466b4b
--- /dev/null
+++ b/namsefni/40.html5/1.html5.md
@@ -0,0 +1,125 @@
+---
+title: Fyrirlestur – JavaScript API í vafra
+---
+
+# Fyrirlestur – JavaScript API í vafra
+
+## Vefforritun 1 — TÖL107G
+
+### Ólafur Sverrir Kjartansson, [osk@hi.is](mailto:osk@hi.is)
+
+---
+
+## Vafra API
+
+* Vafrar hafa mörg JavaScript API
+* Leyfa okkur að gera áhugaverða hluti
+* Höfum séð nokkur, lítum á nokkur í viðbót, [enn fleiri til](https://developer.mozilla.org/en-US/docs/Web/API)
+
+***
+
+## localStorage & sessionStorage
+
+* Vistum strengi í vafra með `window.localStorage`
+ * `setItem(key, value)` vistar `value` undir `key`
+ * `removeItem(key)` fjarlægir gögn undir `key`
+ * `getItem(key)` sækir gögn undir `key`
+
+***
+
+* Notum JSON ef við þurfum að vista flóknari gagnategundir, þurfum að sjá um að þýða á milli
+* `sessionStorage` virkar aðeins í þessu „session“, gögn þurrkast út þegar glugga/tab er lokað
+
+[`localStorage` Dæmi](daemi/01.localstorage.html)
+
+***
+
+## Multimedia
+
+* Getum unnið með vídeó og hljóð í gegnum `` og ``
+* Brugðist við atburðum, breytt stöðu o.s.fr.
+
+[`video` dæmi](daemi/02.video.html)
+
+***
+
+## Workers
+
+* JavaScript keyrir í sama þræði og vafri/tab
+* Keyrslur sem taka langan tíma læsa vafra
+* Vinnuþjarkar leysa vandamálið með að taka vinna í nýjum þráð
+
+[workers dæmi](daemi/03.workers.html)
+
+***
+
+## getUserMedia
+
+* Getum einfaldlega beðið um aðgang að myndavél og hljóðnema
+* `navigator.mediaDevices.getUserMedia()` biður um aðgang að því sem við viljum
+* Promise API
+
+[camera dæmi](daemi/04.camera.html)
+
+***
+
+## getCurrentPosition
+
+* Getum beðið um staðsetningu notanda, notar WiFi, sim kort, eða GPS
+* [`navigator.geolocation.getCurrentPosition`](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) biður um aðgang að staðsetningu
+* Callback API
+
+[`getCurrentPosition` dæmi](daemi/05.location.html)
+
+***
+
+## PWA's
+
+* [Progressive web application](https://en.wikipedia.org/wiki/Progressive_web_application)
+* Vefir sem eru á HTTPS, hafa [service worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) og [web manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest)
+* Virka „offline“, geta sent push notification, stungið upp á að bæta við á „home screen“
+
+***
+
+## Canvas
+
+* Með [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) getum við teiknað grafík gegnum JavaScript API
+* Bæði í 2D og 3D
+
+```javascript
+const ctx = document
+ .querySelector('canvas')
+ .getContext('2d');
+
+ctx.fillStyle = 'red';
+ctx.fillRect(0, 0, 100, 100);
+```
+
+***
+
+[einfalt canvas dæmi](daemi/canvas/01.canvas.html)
+
+[greinar með canvas](daemi/canvas/02.branch.html)
+
+[canvas kökurit](daemi/canvas/03.pie.html)
+
+[kall að hlaupa með canvas](daemi/canvas/04.sprite.html)
+
+***
+
+* [Heill kafli í Eloquent JavaScript](https://eloquentjavascript.net/17_canvas.html) um Canvas og 2D
+* Endar á að búa til einfaldan 2D platformer
+
+***
+
+## WebGL
+
+* Með Canvas er líka hægt að búa til 3D umhverfi gegnum [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API)
+* Notar OpenGL API
+* [three.js](https://threejs.org/) er einfaldara API ofan á OpenGL
+
+***
+
+[webgl dæmi](daemi/webgl/webgl.html)
+
+[threejs dæmi](daemi/webgl/threejs.html)
diff --git a/namsefni/40.html5/daemi/01.localstorage.html b/namsefni/40.html5/daemi/01.localstorage.html
new file mode 100644
index 0000000..2e6476b
--- /dev/null
+++ b/namsefni/40.html5/daemi/01.localstorage.html
@@ -0,0 +1,28 @@
+
+
+
+
+ localStorage
+
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/02.video.html b/namsefni/40.html5/daemi/02.video.html
new file mode 100644
index 0000000..4f7817e
--- /dev/null
+++ b/namsefni/40.html5/daemi/02.video.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Video
+
+
+
+
+ Play
+
+
+
diff --git a/namsefni/40.html5/daemi/03.workers.html b/namsefni/40.html5/daemi/03.workers.html
new file mode 100644
index 0000000..138f12f
--- /dev/null
+++ b/namsefni/40.html5/daemi/03.workers.html
@@ -0,0 +1,44 @@
+
+
+
+
+ workers
+
+
+ Reikna fibonacci runu án worker
+ Reikna fibonacci runu með worker
+
+ Niðurstaða:
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/04.camera.html b/namsefni/40.html5/daemi/04.camera.html
new file mode 100644
index 0000000..fdf6d6b
--- /dev/null
+++ b/namsefni/40.html5/daemi/04.camera.html
@@ -0,0 +1,36 @@
+
+
+
+
+ camera
+
+
+
+
+
+ freeky
+
+
+
diff --git a/namsefni/40.html5/daemi/05.location.html b/namsefni/40.html5/daemi/05.location.html
new file mode 100644
index 0000000..1f46abc
--- /dev/null
+++ b/namsefni/40.html5/daemi/05.location.html
@@ -0,0 +1,49 @@
+
+
+
+
+ Location
+
+
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/canvas/01.canvas.html b/namsefni/40.html5/daemi/canvas/01.canvas.html
new file mode 100644
index 0000000..64a49e3
--- /dev/null
+++ b/namsefni/40.html5/daemi/canvas/01.canvas.html
@@ -0,0 +1,25 @@
+
+
+
+
+ canvas
+
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/canvas/02.branch.html b/namsefni/40.html5/daemi/canvas/02.branch.html
new file mode 100644
index 0000000..3210a2d
--- /dev/null
+++ b/namsefni/40.html5/daemi/canvas/02.branch.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Branch
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/canvas/03.pie.html b/namsefni/40.html5/daemi/canvas/03.pie.html
new file mode 100644
index 0000000..12f170a
--- /dev/null
+++ b/namsefni/40.html5/daemi/canvas/03.pie.html
@@ -0,0 +1,37 @@
+
+
+
+
+ Kökurit
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/canvas/04.sprite.html b/namsefni/40.html5/daemi/canvas/04.sprite.html
new file mode 100644
index 0000000..715508d
--- /dev/null
+++ b/namsefni/40.html5/daemi/canvas/04.sprite.html
@@ -0,0 +1,30 @@
+
+
+
+
+ sprites
+
+
+
+
+
+
+
diff --git a/namsefni/40.html5/daemi/canvas/player.png b/namsefni/40.html5/daemi/canvas/player.png
new file mode 100644
index 0000000..de305fc
Binary files /dev/null and b/namsefni/40.html5/daemi/canvas/player.png differ
diff --git a/namsefni/40.html5/daemi/fibo.js b/namsefni/40.html5/daemi/fibo.js
new file mode 100644
index 0000000..8257291
--- /dev/null
+++ b/namsefni/40.html5/daemi/fibo.js
@@ -0,0 +1,18 @@
+onmessage = (e) => {
+ console.log('Hello from worker');
+
+ const { data } = e;
+
+ function fibo(n) {
+ if (n < 2) return 1;
+
+ return fibo(n - 2) + fibo(n - 1);
+ }
+
+ const results = [];
+ for (let i = 0; i < data; i++) {
+ results.push(fibo(i));
+ }
+
+ postMessage(results.join(', '));
+};
diff --git a/namsefni/40.html5/daemi/small.mp4 b/namsefni/40.html5/daemi/small.mp4
new file mode 100644
index 0000000..1fc4788
Binary files /dev/null and b/namsefni/40.html5/daemi/small.mp4 differ
diff --git a/namsefni/40.html5/daemi/webgl/OrbitControls.js b/namsefni/40.html5/daemi/webgl/OrbitControls.js
new file mode 100644
index 0000000..709f45f
--- /dev/null
+++ b/namsefni/40.html5/daemi/webgl/OrbitControls.js
@@ -0,0 +1,1231 @@
+/* eslint-disable */
+
+import {
+ EventDispatcher,
+ MOUSE,
+ Quaternion,
+ Spherical,
+ TOUCH,
+ Vector2,
+ Vector3
+} from "./three.module.js";
+
+// This set of controls performs orbiting, dollying (zooming), and panning.
+// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
+//
+// Orbit - left mouse / touch: one-finger move
+// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
+// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
+
+var OrbitControls = function ( object, domElement ) {
+
+ if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
+ if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
+
+ this.object = object;
+ this.domElement = domElement;
+
+ // Set to false to disable this control
+ this.enabled = true;
+
+ // "target" sets the location of focus, where the object orbits around
+ this.target = new Vector3();
+
+ // How far you can dolly in and out ( PerspectiveCamera only )
+ this.minDistance = 0;
+ this.maxDistance = Infinity;
+
+ // How far you can zoom in and out ( OrthographicCamera only )
+ this.minZoom = 0;
+ this.maxZoom = Infinity;
+
+ // How far you can orbit vertically, upper and lower limits.
+ // Range is 0 to Math.PI radians.
+ this.minPolarAngle = 0; // radians
+ this.maxPolarAngle = Math.PI; // radians
+
+ // How far you can orbit horizontally, upper and lower limits.
+ // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
+ this.minAzimuthAngle = - Infinity; // radians
+ this.maxAzimuthAngle = Infinity; // radians
+
+ // Set to true to enable damping (inertia)
+ // If damping is enabled, you must call controls.update() in your animation loop
+ this.enableDamping = false;
+ this.dampingFactor = 0.05;
+
+ // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
+ // Set to false to disable zooming
+ this.enableZoom = true;
+ this.zoomSpeed = 1.0;
+
+ // Set to false to disable rotating
+ this.enableRotate = true;
+ this.rotateSpeed = 1.0;
+
+ // Set to false to disable panning
+ this.enablePan = true;
+ this.panSpeed = 1.0;
+ this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
+ this.keyPanSpeed = 7.0; // pixels moved per arrow key push
+
+ // Set to true to automatically rotate around the target
+ // If auto-rotate is enabled, you must call controls.update() in your animation loop
+ this.autoRotate = false;
+ this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
+
+ // Set to false to disable use of the keys
+ this.enableKeys = true;
+
+ // The four arrow keys
+ this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
+
+ // Mouse buttons
+ this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
+
+ // Touch fingers
+ this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
+
+ // for reset
+ this.target0 = this.target.clone();
+ this.position0 = this.object.position.clone();
+ this.zoom0 = this.object.zoom;
+
+ //
+ // public methods
+ //
+
+ this.getPolarAngle = function () {
+
+ return spherical.phi;
+
+ };
+
+ this.getAzimuthalAngle = function () {
+
+ return spherical.theta;
+
+ };
+
+ this.saveState = function () {
+
+ scope.target0.copy( scope.target );
+ scope.position0.copy( scope.object.position );
+ scope.zoom0 = scope.object.zoom;
+
+ };
+
+ this.reset = function () {
+
+ scope.target.copy( scope.target0 );
+ scope.object.position.copy( scope.position0 );
+ scope.object.zoom = scope.zoom0;
+
+ scope.object.updateProjectionMatrix();
+ scope.dispatchEvent( changeEvent );
+
+ scope.update();
+
+ state = STATE.NONE;
+
+ };
+
+ // this method is exposed, but perhaps it would be better if we can make it private...
+ this.update = function () {
+
+ var offset = new Vector3();
+
+ // so camera.up is the orbit axis
+ var quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) );
+ var quatInverse = quat.clone().inverse();
+
+ var lastPosition = new Vector3();
+ var lastQuaternion = new Quaternion();
+
+ var twoPI = 2 * Math.PI;
+
+ return function update() {
+
+ var position = scope.object.position;
+
+ offset.copy( position ).sub( scope.target );
+
+ // rotate offset to "y-axis-is-up" space
+ offset.applyQuaternion( quat );
+
+ // angle from z-axis around y-axis
+ spherical.setFromVector3( offset );
+
+ if ( scope.autoRotate && state === STATE.NONE ) {
+
+ rotateLeft( getAutoRotationAngle() );
+
+ }
+
+ if ( scope.enableDamping ) {
+
+ spherical.theta += sphericalDelta.theta * scope.dampingFactor;
+ spherical.phi += sphericalDelta.phi * scope.dampingFactor;
+
+ } else {
+
+ spherical.theta += sphericalDelta.theta;
+ spherical.phi += sphericalDelta.phi;
+
+ }
+
+ // restrict theta to be between desired limits
+
+ var min = scope.minAzimuthAngle;
+ var max = scope.maxAzimuthAngle;
+
+ if ( isFinite( min ) && isFinite( max ) ) {
+
+ if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
+
+ if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
+
+ if ( min < max ) {
+
+ spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
+
+ } else {
+
+ spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ?
+ Math.max( min, spherical.theta ) :
+ Math.min( max, spherical.theta );
+
+ }
+
+ }
+
+ // restrict phi to be between desired limits
+ spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
+
+ spherical.makeSafe();
+
+
+ spherical.radius *= scale;
+
+ // restrict radius to be between desired limits
+ spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
+
+ // move target to panned location
+
+ if ( scope.enableDamping === true ) {
+
+ scope.target.addScaledVector( panOffset, scope.dampingFactor );
+
+ } else {
+
+ scope.target.add( panOffset );
+
+ }
+
+ offset.setFromSpherical( spherical );
+
+ // rotate offset back to "camera-up-vector-is-up" space
+ offset.applyQuaternion( quatInverse );
+
+ position.copy( scope.target ).add( offset );
+
+ scope.object.lookAt( scope.target );
+
+ if ( scope.enableDamping === true ) {
+
+ sphericalDelta.theta *= ( 1 - scope.dampingFactor );
+ sphericalDelta.phi *= ( 1 - scope.dampingFactor );
+
+ panOffset.multiplyScalar( 1 - scope.dampingFactor );
+
+ } else {
+
+ sphericalDelta.set( 0, 0, 0 );
+
+ panOffset.set( 0, 0, 0 );
+
+ }
+
+ scale = 1;
+
+ // update condition is:
+ // min(camera displacement, camera rotation in radians)^2 > EPS
+ // using small-angle approximation cos(x/2) = 1 - x^2 / 8
+
+ if ( zoomChanged ||
+ lastPosition.distanceToSquared( scope.object.position ) > EPS ||
+ 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
+
+ scope.dispatchEvent( changeEvent );
+
+ lastPosition.copy( scope.object.position );
+ lastQuaternion.copy( scope.object.quaternion );
+ zoomChanged = false;
+
+ return true;
+
+ }
+
+ return false;
+
+ };
+
+ }();
+
+ this.dispose = function () {
+
+ scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
+
+ scope.domElement.removeEventListener( 'pointerdown', onPointerDown, false );
+ scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
+
+ scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
+ scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
+ scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
+
+ scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
+ scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
+
+ scope.domElement.removeEventListener( 'keydown', onKeyDown, false );
+
+ //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
+
+ };
+
+ //
+ // internals
+ //
+
+ var scope = this;
+
+ var changeEvent = { type: 'change' };
+ var startEvent = { type: 'start' };
+ var endEvent = { type: 'end' };
+
+ var STATE = {
+ NONE: - 1,
+ ROTATE: 0,
+ DOLLY: 1,
+ PAN: 2,
+ TOUCH_ROTATE: 3,
+ TOUCH_PAN: 4,
+ TOUCH_DOLLY_PAN: 5,
+ TOUCH_DOLLY_ROTATE: 6
+ };
+
+ var state = STATE.NONE;
+
+ var EPS = 0.000001;
+
+ // current position in spherical coordinates
+ var spherical = new Spherical();
+ var sphericalDelta = new Spherical();
+
+ var scale = 1;
+ var panOffset = new Vector3();
+ var zoomChanged = false;
+
+ var rotateStart = new Vector2();
+ var rotateEnd = new Vector2();
+ var rotateDelta = new Vector2();
+
+ var panStart = new Vector2();
+ var panEnd = new Vector2();
+ var panDelta = new Vector2();
+
+ var dollyStart = new Vector2();
+ var dollyEnd = new Vector2();
+ var dollyDelta = new Vector2();
+
+ function getAutoRotationAngle() {
+
+ return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
+
+ }
+
+ function getZoomScale() {
+
+ return Math.pow( 0.95, scope.zoomSpeed );
+
+ }
+
+ function rotateLeft( angle ) {
+
+ sphericalDelta.theta -= angle;
+
+ }
+
+ function rotateUp( angle ) {
+
+ sphericalDelta.phi -= angle;
+
+ }
+
+ var panLeft = function () {
+
+ var v = new Vector3();
+
+ return function panLeft( distance, objectMatrix ) {
+
+ v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
+ v.multiplyScalar( - distance );
+
+ panOffset.add( v );
+
+ };
+
+ }();
+
+ var panUp = function () {
+
+ var v = new Vector3();
+
+ return function panUp( distance, objectMatrix ) {
+
+ if ( scope.screenSpacePanning === true ) {
+
+ v.setFromMatrixColumn( objectMatrix, 1 );
+
+ } else {
+
+ v.setFromMatrixColumn( objectMatrix, 0 );
+ v.crossVectors( scope.object.up, v );
+
+ }
+
+ v.multiplyScalar( distance );
+
+ panOffset.add( v );
+
+ };
+
+ }();
+
+ // deltaX and deltaY are in pixels; right and down are positive
+ var pan = function () {
+
+ var offset = new Vector3();
+
+ return function pan( deltaX, deltaY ) {
+
+ var element = scope.domElement;
+
+ if ( scope.object.isPerspectiveCamera ) {
+
+ // perspective
+ var position = scope.object.position;
+ offset.copy( position ).sub( scope.target );
+ var targetDistance = offset.length();
+
+ // half of the fov is center to top of screen
+ targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
+
+ // we use only clientHeight here so aspect ratio does not distort speed
+ panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
+ panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
+
+ } else if ( scope.object.isOrthographicCamera ) {
+
+ // orthographic
+ panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
+ panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
+
+ } else {
+
+ // camera neither orthographic nor perspective
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
+ scope.enablePan = false;
+
+ }
+
+ };
+
+ }();
+
+ function dollyOut( dollyScale ) {
+
+ if ( scope.object.isPerspectiveCamera ) {
+
+ scale /= dollyScale;
+
+ } else if ( scope.object.isOrthographicCamera ) {
+
+ scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+
+ } else {
+
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
+ scope.enableZoom = false;
+
+ }
+
+ }
+
+ function dollyIn( dollyScale ) {
+
+ if ( scope.object.isPerspectiveCamera ) {
+
+ scale *= dollyScale;
+
+ } else if ( scope.object.isOrthographicCamera ) {
+
+ scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
+ scope.object.updateProjectionMatrix();
+ zoomChanged = true;
+
+ } else {
+
+ console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
+ scope.enableZoom = false;
+
+ }
+
+ }
+
+ //
+ // event callbacks - update the object state
+ //
+
+ function handleMouseDownRotate( event ) {
+
+ rotateStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseDownDolly( event ) {
+
+ dollyStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseDownPan( event ) {
+
+ panStart.set( event.clientX, event.clientY );
+
+ }
+
+ function handleMouseMoveRotate( event ) {
+
+ rotateEnd.set( event.clientX, event.clientY );
+
+ rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
+
+ var element = scope.domElement;
+
+ rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
+
+ rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
+
+ rotateStart.copy( rotateEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseMoveDolly( event ) {
+
+ dollyEnd.set( event.clientX, event.clientY );
+
+ dollyDelta.subVectors( dollyEnd, dollyStart );
+
+ if ( dollyDelta.y > 0 ) {
+
+ dollyOut( getZoomScale() );
+
+ } else if ( dollyDelta.y < 0 ) {
+
+ dollyIn( getZoomScale() );
+
+ }
+
+ dollyStart.copy( dollyEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseMovePan( event ) {
+
+ panEnd.set( event.clientX, event.clientY );
+
+ panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
+
+ pan( panDelta.x, panDelta.y );
+
+ panStart.copy( panEnd );
+
+ scope.update();
+
+ }
+
+ function handleMouseUp( /*event*/ ) {
+
+ // no-op
+
+ }
+
+ function handleMouseWheel( event ) {
+
+ if ( event.deltaY < 0 ) {
+
+ dollyIn( getZoomScale() );
+
+ } else if ( event.deltaY > 0 ) {
+
+ dollyOut( getZoomScale() );
+
+ }
+
+ scope.update();
+
+ }
+
+ function handleKeyDown( event ) {
+
+ var needsUpdate = false;
+
+ switch ( event.keyCode ) {
+
+ case scope.keys.UP:
+ pan( 0, scope.keyPanSpeed );
+ needsUpdate = true;
+ break;
+
+ case scope.keys.BOTTOM:
+ pan( 0, - scope.keyPanSpeed );
+ needsUpdate = true;
+ break;
+
+ case scope.keys.LEFT:
+ pan( scope.keyPanSpeed, 0 );
+ needsUpdate = true;
+ break;
+
+ case scope.keys.RIGHT:
+ pan( - scope.keyPanSpeed, 0 );
+ needsUpdate = true;
+ break;
+
+ }
+
+ if ( needsUpdate ) {
+
+ // prevent the browser from scrolling on cursor keys
+ event.preventDefault();
+
+ scope.update();
+
+ }
+
+
+ }
+
+ function handleTouchStartRotate( event ) {
+
+ if ( event.touches.length == 1 ) {
+
+ rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ } else {
+
+ var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
+ var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
+
+ rotateStart.set( x, y );
+
+ }
+
+ }
+
+ function handleTouchStartPan( event ) {
+
+ if ( event.touches.length == 1 ) {
+
+ panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ } else {
+
+ var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
+ var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
+
+ panStart.set( x, y );
+
+ }
+
+ }
+
+ function handleTouchStartDolly( event ) {
+
+ var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+ var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+
+ var distance = Math.sqrt( dx * dx + dy * dy );
+
+ dollyStart.set( 0, distance );
+
+ }
+
+ function handleTouchStartDollyPan( event ) {
+
+ if ( scope.enableZoom ) handleTouchStartDolly( event );
+
+ if ( scope.enablePan ) handleTouchStartPan( event );
+
+ }
+
+ function handleTouchStartDollyRotate( event ) {
+
+ if ( scope.enableZoom ) handleTouchStartDolly( event );
+
+ if ( scope.enableRotate ) handleTouchStartRotate( event );
+
+ }
+
+ function handleTouchMoveRotate( event ) {
+
+ if ( event.touches.length == 1 ) {
+
+ rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ } else {
+
+ var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
+ var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
+
+ rotateEnd.set( x, y );
+
+ }
+
+ rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
+
+ var element = scope.domElement;
+
+ rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
+
+ rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
+
+ rotateStart.copy( rotateEnd );
+
+ }
+
+ function handleTouchMovePan( event ) {
+
+ if ( event.touches.length == 1 ) {
+
+ panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+
+ } else {
+
+ var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
+ var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
+
+ panEnd.set( x, y );
+
+ }
+
+ panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
+
+ pan( panDelta.x, panDelta.y );
+
+ panStart.copy( panEnd );
+
+ }
+
+ function handleTouchMoveDolly( event ) {
+
+ var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+ var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+
+ var distance = Math.sqrt( dx * dx + dy * dy );
+
+ dollyEnd.set( 0, distance );
+
+ dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
+
+ dollyOut( dollyDelta.y );
+
+ dollyStart.copy( dollyEnd );
+
+ }
+
+ function handleTouchMoveDollyPan( event ) {
+
+ if ( scope.enableZoom ) handleTouchMoveDolly( event );
+
+ if ( scope.enablePan ) handleTouchMovePan( event );
+
+ }
+
+ function handleTouchMoveDollyRotate( event ) {
+
+ if ( scope.enableZoom ) handleTouchMoveDolly( event );
+
+ if ( scope.enableRotate ) handleTouchMoveRotate( event );
+
+ }
+
+ function handleTouchEnd( /*event*/ ) {
+
+ // no-op
+
+ }
+
+ //
+ // event handlers - FSM: listen for events and reset state
+ //
+
+ function onPointerDown( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ switch ( event.pointerType ) {
+
+ case 'mouse':
+ case 'pen':
+ onMouseDown( event );
+ break;
+
+ // TODO touch
+
+ }
+
+ }
+
+ function onPointerMove( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ switch ( event.pointerType ) {
+
+ case 'mouse':
+ case 'pen':
+ onMouseMove( event );
+ break;
+
+ // TODO touch
+
+ }
+
+ }
+
+ function onPointerUp( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ switch ( event.pointerType ) {
+
+ case 'mouse':
+ case 'pen':
+ onMouseUp( event );
+ break;
+
+ // TODO touch
+
+ }
+
+ }
+
+ function onMouseDown( event ) {
+
+ // Prevent the browser from scrolling.
+ event.preventDefault();
+
+ // Manually set the focus since calling preventDefault above
+ // prevents the browser from setting it automatically.
+
+ scope.domElement.focus ? scope.domElement.focus() : window.focus();
+
+ var mouseAction;
+
+ switch ( event.button ) {
+
+ case 0:
+
+ mouseAction = scope.mouseButtons.LEFT;
+ break;
+
+ case 1:
+
+ mouseAction = scope.mouseButtons.MIDDLE;
+ break;
+
+ case 2:
+
+ mouseAction = scope.mouseButtons.RIGHT;
+ break;
+
+ default:
+
+ mouseAction = - 1;
+
+ }
+
+ switch ( mouseAction ) {
+
+ case MOUSE.DOLLY:
+
+ if ( scope.enableZoom === false ) return;
+
+ handleMouseDownDolly( event );
+
+ state = STATE.DOLLY;
+
+ break;
+
+ case MOUSE.ROTATE:
+
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
+
+ if ( scope.enablePan === false ) return;
+
+ handleMouseDownPan( event );
+
+ state = STATE.PAN;
+
+ } else {
+
+ if ( scope.enableRotate === false ) return;
+
+ handleMouseDownRotate( event );
+
+ state = STATE.ROTATE;
+
+ }
+
+ break;
+
+ case MOUSE.PAN:
+
+ if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
+
+ if ( scope.enableRotate === false ) return;
+
+ handleMouseDownRotate( event );
+
+ state = STATE.ROTATE;
+
+ } else {
+
+ if ( scope.enablePan === false ) return;
+
+ handleMouseDownPan( event );
+
+ state = STATE.PAN;
+
+ }
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ if ( state !== STATE.NONE ) {
+
+ scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove, false );
+ scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp, false );
+
+ scope.dispatchEvent( startEvent );
+
+ }
+
+ }
+
+ function onMouseMove( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault();
+
+ switch ( state ) {
+
+ case STATE.ROTATE:
+
+ if ( scope.enableRotate === false ) return;
+
+ handleMouseMoveRotate( event );
+
+ break;
+
+ case STATE.DOLLY:
+
+ if ( scope.enableZoom === false ) return;
+
+ handleMouseMoveDolly( event );
+
+ break;
+
+ case STATE.PAN:
+
+ if ( scope.enablePan === false ) return;
+
+ handleMouseMovePan( event );
+
+ break;
+
+ }
+
+ }
+
+ function onMouseUp( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ handleMouseUp( event );
+
+ scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
+ scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
+
+ scope.dispatchEvent( endEvent );
+
+ state = STATE.NONE;
+
+ }
+
+ function onMouseWheel( event ) {
+
+ if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ scope.dispatchEvent( startEvent );
+
+ handleMouseWheel( event );
+
+ scope.dispatchEvent( endEvent );
+
+ }
+
+ function onKeyDown( event ) {
+
+ if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
+
+ handleKeyDown( event );
+
+ }
+
+ function onTouchStart( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault(); // prevent scrolling
+
+ switch ( event.touches.length ) {
+
+ case 1:
+
+ switch ( scope.touches.ONE ) {
+
+ case TOUCH.ROTATE:
+
+ if ( scope.enableRotate === false ) return;
+
+ handleTouchStartRotate( event );
+
+ state = STATE.TOUCH_ROTATE;
+
+ break;
+
+ case TOUCH.PAN:
+
+ if ( scope.enablePan === false ) return;
+
+ handleTouchStartPan( event );
+
+ state = STATE.TOUCH_PAN;
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ break;
+
+ case 2:
+
+ switch ( scope.touches.TWO ) {
+
+ case TOUCH.DOLLY_PAN:
+
+ if ( scope.enableZoom === false && scope.enablePan === false ) return;
+
+ handleTouchStartDollyPan( event );
+
+ state = STATE.TOUCH_DOLLY_PAN;
+
+ break;
+
+ case TOUCH.DOLLY_ROTATE:
+
+ if ( scope.enableZoom === false && scope.enableRotate === false ) return;
+
+ handleTouchStartDollyRotate( event );
+
+ state = STATE.TOUCH_DOLLY_ROTATE;
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ if ( state !== STATE.NONE ) {
+
+ scope.dispatchEvent( startEvent );
+
+ }
+
+ }
+
+ function onTouchMove( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault(); // prevent scrolling
+ event.stopPropagation();
+
+ switch ( state ) {
+
+ case STATE.TOUCH_ROTATE:
+
+ if ( scope.enableRotate === false ) return;
+
+ handleTouchMoveRotate( event );
+
+ scope.update();
+
+ break;
+
+ case STATE.TOUCH_PAN:
+
+ if ( scope.enablePan === false ) return;
+
+ handleTouchMovePan( event );
+
+ scope.update();
+
+ break;
+
+ case STATE.TOUCH_DOLLY_PAN:
+
+ if ( scope.enableZoom === false && scope.enablePan === false ) return;
+
+ handleTouchMoveDollyPan( event );
+
+ scope.update();
+
+ break;
+
+ case STATE.TOUCH_DOLLY_ROTATE:
+
+ if ( scope.enableZoom === false && scope.enableRotate === false ) return;
+
+ handleTouchMoveDollyRotate( event );
+
+ scope.update();
+
+ break;
+
+ default:
+
+ state = STATE.NONE;
+
+ }
+
+ }
+
+ function onTouchEnd( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ handleTouchEnd( event );
+
+ scope.dispatchEvent( endEvent );
+
+ state = STATE.NONE;
+
+ }
+
+ function onContextMenu( event ) {
+
+ if ( scope.enabled === false ) return;
+
+ event.preventDefault();
+
+ }
+
+ //
+
+ scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
+
+ scope.domElement.addEventListener( 'pointerdown', onPointerDown, false );
+ scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
+
+ scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
+ scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
+ scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
+
+ scope.domElement.addEventListener( 'keydown', onKeyDown, false );
+
+ // make sure element can receive keys.
+
+ if ( scope.domElement.tabIndex === - 1 ) {
+
+ scope.domElement.tabIndex = 0;
+
+ }
+
+ // force an update at start
+
+ this.update();
+
+};
+
+OrbitControls.prototype = Object.create( EventDispatcher.prototype );
+OrbitControls.prototype.constructor = OrbitControls;
+
+
+// This set of controls performs orbiting, dollying (zooming), and panning.
+// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
+// This is very similar to OrbitControls, another set of touch behavior
+//
+// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
+// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
+// Pan - left mouse, or arrow keys / touch: one-finger move
+
+var MapControls = function ( object, domElement ) {
+
+ OrbitControls.call( this, object, domElement );
+
+ this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
+
+ this.mouseButtons.LEFT = MOUSE.PAN;
+ this.mouseButtons.RIGHT = MOUSE.ROTATE;
+
+ this.touches.ONE = TOUCH.PAN;
+ this.touches.TWO = TOUCH.DOLLY_ROTATE;
+
+};
+
+MapControls.prototype = Object.create( EventDispatcher.prototype );
+MapControls.prototype.constructor = MapControls;
+
+export { OrbitControls, MapControls };
diff --git a/namsefni/40.html5/daemi/webgl/crate.gif b/namsefni/40.html5/daemi/webgl/crate.gif
new file mode 100644
index 0000000..d9b475d
Binary files /dev/null and b/namsefni/40.html5/daemi/webgl/crate.gif differ
diff --git a/namsefni/40.html5/daemi/webgl/main.css b/namsefni/40.html5/daemi/webgl/main.css
new file mode 100644
index 0000000..40423e0
--- /dev/null
+++ b/namsefni/40.html5/daemi/webgl/main.css
@@ -0,0 +1,84 @@
+body {
+ margin: 0;
+ background-color: #000;
+ color: #fff;
+ font-family: Monospace;
+ font-size: 13px;
+ line-height: 24px;
+ overscroll-behavior: none;
+}
+
+a {
+ color: #ff0;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+button {
+ cursor: pointer;
+ text-transform: uppercase;
+}
+
+canvas {
+ display: block;
+}
+
+#info {
+ position: absolute;
+ top: 0px;
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ text-align: center;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ pointer-events: none;
+ z-index: 1; /* TODO Solve this in HTML */
+}
+
+a, button, input, select {
+ pointer-events: auto;
+}
+
+.dg.ac {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ z-index: 2 !important; /* TODO Solve this in HTML */
+}
+
+#overlay {
+ position: absolute;
+ z-index: 2;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(0,0,0,0.7);
+}
+
+ #overlay button {
+ background: #ffffff;
+ border: 0;
+ color: #000000;
+ padding: 16px 20px;
+ text-transform: uppercase;
+ cursor: pointer;
+ }
+
+#notSupported {
+ width: 50%;
+ margin: auto;
+ background-color: #f00;
+ margin-top: 20px;
+ padding: 10px;
+}
diff --git a/namsefni/40.html5/daemi/webgl/three.module.js b/namsefni/40.html5/daemi/webgl/three.module.js
new file mode 100644
index 0000000..1a2b943
--- /dev/null
+++ b/namsefni/40.html5/daemi/webgl/three.module.js
@@ -0,0 +1,51013 @@
+/* eslint-disable */
+// threejs.org/license
+const REVISION = '122';
+const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
+const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
+const CullFaceNone = 0;
+const CullFaceBack = 1;
+const CullFaceFront = 2;
+const CullFaceFrontBack = 3;
+const BasicShadowMap = 0;
+const PCFShadowMap = 1;
+const PCFSoftShadowMap = 2;
+const VSMShadowMap = 3;
+const FrontSide = 0;
+const BackSide = 1;
+const DoubleSide = 2;
+const FlatShading = 1;
+const SmoothShading = 2;
+const NoBlending = 0;
+const NormalBlending = 1;
+const AdditiveBlending = 2;
+const SubtractiveBlending = 3;
+const MultiplyBlending = 4;
+const CustomBlending = 5;
+const AddEquation = 100;
+const SubtractEquation = 101;
+const ReverseSubtractEquation = 102;
+const MinEquation = 103;
+const MaxEquation = 104;
+const ZeroFactor = 200;
+const OneFactor = 201;
+const SrcColorFactor = 202;
+const OneMinusSrcColorFactor = 203;
+const SrcAlphaFactor = 204;
+const OneMinusSrcAlphaFactor = 205;
+const DstAlphaFactor = 206;
+const OneMinusDstAlphaFactor = 207;
+const DstColorFactor = 208;
+const OneMinusDstColorFactor = 209;
+const SrcAlphaSaturateFactor = 210;
+const NeverDepth = 0;
+const AlwaysDepth = 1;
+const LessDepth = 2;
+const LessEqualDepth = 3;
+const EqualDepth = 4;
+const GreaterEqualDepth = 5;
+const GreaterDepth = 6;
+const NotEqualDepth = 7;
+const MultiplyOperation = 0;
+const MixOperation = 1;
+const AddOperation = 2;
+const NoToneMapping = 0;
+const LinearToneMapping = 1;
+const ReinhardToneMapping = 2;
+const CineonToneMapping = 3;
+const ACESFilmicToneMapping = 4;
+const CustomToneMapping = 5;
+
+const UVMapping = 300;
+const CubeReflectionMapping = 301;
+const CubeRefractionMapping = 302;
+const EquirectangularReflectionMapping = 303;
+const EquirectangularRefractionMapping = 304;
+const CubeUVReflectionMapping = 306;
+const CubeUVRefractionMapping = 307;
+const RepeatWrapping = 1000;
+const ClampToEdgeWrapping = 1001;
+const MirroredRepeatWrapping = 1002;
+const NearestFilter = 1003;
+const NearestMipmapNearestFilter = 1004;
+const NearestMipMapNearestFilter = 1004;
+const NearestMipmapLinearFilter = 1005;
+const NearestMipMapLinearFilter = 1005;
+const LinearFilter = 1006;
+const LinearMipmapNearestFilter = 1007;
+const LinearMipMapNearestFilter = 1007;
+const LinearMipmapLinearFilter = 1008;
+const LinearMipMapLinearFilter = 1008;
+const UnsignedByteType = 1009;
+const ByteType = 1010;
+const ShortType = 1011;
+const UnsignedShortType = 1012;
+const IntType = 1013;
+const UnsignedIntType = 1014;
+const FloatType = 1015;
+const HalfFloatType = 1016;
+const UnsignedShort4444Type = 1017;
+const UnsignedShort5551Type = 1018;
+const UnsignedShort565Type = 1019;
+const UnsignedInt248Type = 1020;
+const AlphaFormat = 1021;
+const RGBFormat = 1022;
+const RGBAFormat = 1023;
+const LuminanceFormat = 1024;
+const LuminanceAlphaFormat = 1025;
+const RGBEFormat = RGBAFormat;
+const DepthFormat = 1026;
+const DepthStencilFormat = 1027;
+const RedFormat = 1028;
+const RedIntegerFormat = 1029;
+const RGFormat = 1030;
+const RGIntegerFormat = 1031;
+const RGBIntegerFormat = 1032;
+const RGBAIntegerFormat = 1033;
+
+const RGB_S3TC_DXT1_Format = 33776;
+const RGBA_S3TC_DXT1_Format = 33777;
+const RGBA_S3TC_DXT3_Format = 33778;
+const RGBA_S3TC_DXT5_Format = 33779;
+const RGB_PVRTC_4BPPV1_Format = 35840;
+const RGB_PVRTC_2BPPV1_Format = 35841;
+const RGBA_PVRTC_4BPPV1_Format = 35842;
+const RGBA_PVRTC_2BPPV1_Format = 35843;
+const RGB_ETC1_Format = 36196;
+const RGB_ETC2_Format = 37492;
+const RGBA_ETC2_EAC_Format = 37496;
+const RGBA_ASTC_4x4_Format = 37808;
+const RGBA_ASTC_5x4_Format = 37809;
+const RGBA_ASTC_5x5_Format = 37810;
+const RGBA_ASTC_6x5_Format = 37811;
+const RGBA_ASTC_6x6_Format = 37812;
+const RGBA_ASTC_8x5_Format = 37813;
+const RGBA_ASTC_8x6_Format = 37814;
+const RGBA_ASTC_8x8_Format = 37815;
+const RGBA_ASTC_10x5_Format = 37816;
+const RGBA_ASTC_10x6_Format = 37817;
+const RGBA_ASTC_10x8_Format = 37818;
+const RGBA_ASTC_10x10_Format = 37819;
+const RGBA_ASTC_12x10_Format = 37820;
+const RGBA_ASTC_12x12_Format = 37821;
+const RGBA_BPTC_Format = 36492;
+const SRGB8_ALPHA8_ASTC_4x4_Format = 37840;
+const SRGB8_ALPHA8_ASTC_5x4_Format = 37841;
+const SRGB8_ALPHA8_ASTC_5x5_Format = 37842;
+const SRGB8_ALPHA8_ASTC_6x5_Format = 37843;
+const SRGB8_ALPHA8_ASTC_6x6_Format = 37844;
+const SRGB8_ALPHA8_ASTC_8x5_Format = 37845;
+const SRGB8_ALPHA8_ASTC_8x6_Format = 37846;
+const SRGB8_ALPHA8_ASTC_8x8_Format = 37847;
+const SRGB8_ALPHA8_ASTC_10x5_Format = 37848;
+const SRGB8_ALPHA8_ASTC_10x6_Format = 37849;
+const SRGB8_ALPHA8_ASTC_10x8_Format = 37850;
+const SRGB8_ALPHA8_ASTC_10x10_Format = 37851;
+const SRGB8_ALPHA8_ASTC_12x10_Format = 37852;
+const SRGB8_ALPHA8_ASTC_12x12_Format = 37853;
+const LoopOnce = 2200;
+const LoopRepeat = 2201;
+const LoopPingPong = 2202;
+const InterpolateDiscrete = 2300;
+const InterpolateLinear = 2301;
+const InterpolateSmooth = 2302;
+const ZeroCurvatureEnding = 2400;
+const ZeroSlopeEnding = 2401;
+const WrapAroundEnding = 2402;
+const NormalAnimationBlendMode = 2500;
+const AdditiveAnimationBlendMode = 2501;
+const TrianglesDrawMode = 0;
+const TriangleStripDrawMode = 1;
+const TriangleFanDrawMode = 2;
+const LinearEncoding = 3000;
+const sRGBEncoding = 3001;
+const GammaEncoding = 3007;
+const RGBEEncoding = 3002;
+const LogLuvEncoding = 3003;
+const RGBM7Encoding = 3004;
+const RGBM16Encoding = 3005;
+const RGBDEncoding = 3006;
+const BasicDepthPacking = 3200;
+const RGBADepthPacking = 3201;
+const TangentSpaceNormalMap = 0;
+const ObjectSpaceNormalMap = 1;
+
+const ZeroStencilOp = 0;
+const KeepStencilOp = 7680;
+const ReplaceStencilOp = 7681;
+const IncrementStencilOp = 7682;
+const DecrementStencilOp = 7683;
+const IncrementWrapStencilOp = 34055;
+const DecrementWrapStencilOp = 34056;
+const InvertStencilOp = 5386;
+
+const NeverStencilFunc = 512;
+const LessStencilFunc = 513;
+const EqualStencilFunc = 514;
+const LessEqualStencilFunc = 515;
+const GreaterStencilFunc = 516;
+const NotEqualStencilFunc = 517;
+const GreaterEqualStencilFunc = 518;
+const AlwaysStencilFunc = 519;
+
+const StaticDrawUsage = 35044;
+const DynamicDrawUsage = 35048;
+const StreamDrawUsage = 35040;
+const StaticReadUsage = 35045;
+const DynamicReadUsage = 35049;
+const StreamReadUsage = 35041;
+const StaticCopyUsage = 35046;
+const DynamicCopyUsage = 35050;
+const StreamCopyUsage = 35042;
+
+const GLSL1 = "100";
+const GLSL3 = "300 es";
+
+/**
+ * https://github.com/mrdoob/eventdispatcher.js/
+ */
+
+function EventDispatcher() {}
+
+Object.assign( EventDispatcher.prototype, {
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ const listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ const listeners = this._listeners;
+
+ return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ const listeners = this._listeners;
+ const listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ const index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ const listeners = this._listeners;
+ const listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ // Make a copy, in case listeners are removed while iterating.
+ const array = listenerArray.slice( 0 );
+
+ for ( let i = 0, l = array.length; i < l; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+} );
+
+const _lut = [];
+
+for ( let i = 0; i < 256; i ++ ) {
+
+ _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
+
+}
+
+let _seed = 1234567;
+
+const MathUtils = {
+
+ DEG2RAD: Math.PI / 180,
+ RAD2DEG: 180 / Math.PI,
+
+ generateUUID: function () {
+
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+
+ const d0 = Math.random() * 0xffffffff | 0;
+ const d1 = Math.random() * 0xffffffff | 0;
+ const d2 = Math.random() * 0xffffffff | 0;
+ const d3 = Math.random() * 0xffffffff | 0;
+ const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +
+ _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +
+ _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +
+ _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];
+
+ // .toUpperCase() here flattens concatenated strings to save heap memory space.
+ return uuid.toUpperCase();
+
+ },
+
+ clamp: function ( value, min, max ) {
+
+ return Math.max( min, Math.min( max, value ) );
+
+ },
+
+ // compute euclidian modulo of m % n
+ // https://en.wikipedia.org/wiki/Modulo_operation
+
+ euclideanModulo: function ( n, m ) {
+
+ return ( ( n % m ) + m ) % m;
+
+ },
+
+ // Linear mapping from range to range
+
+ mapLinear: function ( x, a1, a2, b1, b2 ) {
+
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+
+ },
+
+ // https://en.wikipedia.org/wiki/Linear_interpolation
+
+ lerp: function ( x, y, t ) {
+
+ return ( 1 - t ) * x + t * y;
+
+ },
+
+ // http://en.wikipedia.org/wiki/Smoothstep
+
+ smoothstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * ( 3 - 2 * x );
+
+ },
+
+ smootherstep: function ( x, min, max ) {
+
+ if ( x <= min ) return 0;
+ if ( x >= max ) return 1;
+
+ x = ( x - min ) / ( max - min );
+
+ return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
+
+ },
+
+ // Random integer from interval
+
+ randInt: function ( low, high ) {
+
+ return low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+ },
+
+ // Random float from interval
+
+ randFloat: function ( low, high ) {
+
+ return low + Math.random() * ( high - low );
+
+ },
+
+ // Random float from <-range/2, range/2> interval
+
+ randFloatSpread: function ( range ) {
+
+ return range * ( 0.5 - Math.random() );
+
+ },
+
+ // Deterministic pseudo-random float in the interval [ 0, 1 ]
+
+ seededRandom: function ( s ) {
+
+ if ( s !== undefined ) _seed = s % 2147483647;
+
+ // Park-Miller algorithm
+
+ _seed = _seed * 16807 % 2147483647;
+
+ return ( _seed - 1 ) / 2147483646;
+
+ },
+
+ degToRad: function ( degrees ) {
+
+ return degrees * MathUtils.DEG2RAD;
+
+ },
+
+ radToDeg: function ( radians ) {
+
+ return radians * MathUtils.RAD2DEG;
+
+ },
+
+ isPowerOfTwo: function ( value ) {
+
+ return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+ },
+
+ ceilPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ floorPowerOfTwo: function ( value ) {
+
+ return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
+
+ },
+
+ setQuaternionFromProperEuler: function ( q, a, b, c, order ) {
+
+ // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles
+
+ // rotations are applied to the axes in the order specified by 'order'
+ // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c'
+ // angles are in radians
+
+ const cos = Math.cos;
+ const sin = Math.sin;
+
+ const c2 = cos( b / 2 );
+ const s2 = sin( b / 2 );
+
+ const c13 = cos( ( a + c ) / 2 );
+ const s13 = sin( ( a + c ) / 2 );
+
+ const c1_3 = cos( ( a - c ) / 2 );
+ const s1_3 = sin( ( a - c ) / 2 );
+
+ const c3_1 = cos( ( c - a ) / 2 );
+ const s3_1 = sin( ( c - a ) / 2 );
+
+ switch ( order ) {
+
+ case 'XYX':
+ q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );
+ break;
+
+ case 'YZY':
+ q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );
+ break;
+
+ case 'ZXZ':
+ q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );
+ break;
+
+ case 'XZX':
+ q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );
+ break;
+
+ case 'YXY':
+ q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );
+ break;
+
+ case 'ZYZ':
+ q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );
+ break;
+
+ default:
+ console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order );
+
+ }
+
+ }
+
+};
+
+class Vector2 {
+
+ constructor( x = 0, y = 0 ) {
+
+ Object.defineProperty( this, 'isVector2', { value: true } );
+
+ this.x = x;
+ this.y = y;
+
+ }
+
+ get width() {
+
+ return this.x;
+
+ }
+
+ set width( value ) {
+
+ this.x = value;
+
+ }
+
+ get height() {
+
+ return this.y;
+
+ }
+
+ set height( value ) {
+
+ this.y = value;
+
+ }
+
+ set( x, y ) {
+
+ this.x = x;
+ this.y = y;
+
+ return this;
+
+ }
+
+ setScalar( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+
+ return this;
+
+ }
+
+ setX( x ) {
+
+ this.x = x;
+
+ return this;
+
+ }
+
+ setY( y ) {
+
+ this.y = y;
+
+ return this;
+
+ }
+
+ setComponent( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ }
+
+ getComponent( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ }
+
+ clone() {
+
+ return new this.constructor( this.x, this.y );
+
+ }
+
+ copy( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+
+ return this;
+
+ }
+
+ add( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+
+ return this;
+
+ }
+
+ addScalar( s ) {
+
+ this.x += s;
+ this.y += s;
+
+ return this;
+
+ }
+
+ addVectors( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+
+ return this;
+
+ }
+
+ addScaledVector( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+
+ return this;
+
+ }
+
+ sub( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+
+ return this;
+
+ }
+
+ subScalar( s ) {
+
+ this.x -= s;
+ this.y -= s;
+
+ return this;
+
+ }
+
+ subVectors( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+
+ return this;
+
+ }
+
+ multiply( v ) {
+
+ this.x *= v.x;
+ this.y *= v.y;
+
+ return this;
+
+ }
+
+ multiplyScalar( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+
+ return this;
+
+ }
+
+ divide( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+
+ return this;
+
+ }
+
+ divideScalar( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ }
+
+ applyMatrix3( m ) {
+
+ const x = this.x, y = this.y;
+ const e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];
+
+ return this;
+
+ }
+
+ min( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+
+ return this;
+
+ }
+
+ max( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+
+ return this;
+
+ }
+
+ clamp( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+
+ return this;
+
+ }
+
+ clampScalar( minVal, maxVal ) {
+
+ this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
+ this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
+
+ return this;
+
+ }
+
+ clampLength( min, max ) {
+
+ const length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ }
+
+ floor() {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+
+ return this;
+
+ }
+
+ ceil() {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+
+ return this;
+
+ }
+
+ round() {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+
+ return this;
+
+ }
+
+ roundToZero() {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+
+ return this;
+
+ }
+
+ negate() {
+
+ this.x = - this.x;
+ this.y = - this.y;
+
+ return this;
+
+ }
+
+ dot( v ) {
+
+ return this.x * v.x + this.y * v.y;
+
+ }
+
+ cross( v ) {
+
+ return this.x * v.y - this.y * v.x;
+
+ }
+
+ lengthSq() {
+
+ return this.x * this.x + this.y * this.y;
+
+ }
+
+ length() {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y );
+
+ }
+
+ manhattanLength() {
+
+ return Math.abs( this.x ) + Math.abs( this.y );
+
+ }
+
+ normalize() {
+
+ return this.divideScalar( this.length() || 1 );
+
+ }
+
+ angle() {
+
+ // computes the angle in radians with respect to the positive x-axis
+
+ const angle = Math.atan2( - this.y, - this.x ) + Math.PI;
+
+ return angle;
+
+ }
+
+ distanceTo( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ }
+
+ distanceToSquared( v ) {
+
+ const dx = this.x - v.x, dy = this.y - v.y;
+ return dx * dx + dy * dy;
+
+ }
+
+ manhattanDistanceTo( v ) {
+
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );
+
+ }
+
+ setLength( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ }
+
+ lerp( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+
+ return this;
+
+ }
+
+ lerpVectors( v1, v2, alpha ) {
+
+ this.x = v1.x + ( v2.x - v1.x ) * alpha;
+ this.y = v1.y + ( v2.y - v1.y ) * alpha;
+
+ return this;
+
+ }
+
+ equals( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) );
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+
+ return array;
+
+ }
+
+ fromBufferAttribute( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+
+ return this;
+
+ }
+
+ rotateAround( center, angle ) {
+
+ const c = Math.cos( angle ), s = Math.sin( angle );
+
+ const x = this.x - center.x;
+ const y = this.y - center.y;
+
+ this.x = x * c - y * s + center.x;
+ this.y = x * s + y * c + center.y;
+
+ return this;
+
+ }
+
+ random() {
+
+ this.x = Math.random();
+ this.y = Math.random();
+
+ return this;
+
+ }
+
+}
+
+class Matrix3 {
+
+ constructor() {
+
+ Object.defineProperty( this, 'isMatrix3', { value: true } );
+
+ this.elements = [
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ ];
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+ }
+
+ set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+ const te = this.elements;
+
+ te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
+ te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
+ te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
+
+ return this;
+
+ }
+
+ identity() {
+
+ this.set(
+
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().fromArray( this.elements );
+
+ }
+
+ copy( m ) {
+
+ const te = this.elements;
+ const me = m.elements;
+
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
+ te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
+ te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
+
+ return this;
+
+ }
+
+ extractBasis( xAxis, yAxis, zAxis ) {
+
+ xAxis.setFromMatrix3Column( this, 0 );
+ yAxis.setFromMatrix3Column( this, 1 );
+ zAxis.setFromMatrix3Column( this, 2 );
+
+ return this;
+
+ }
+
+ setFromMatrix4( m ) {
+
+ const me = m.elements;
+
+ this.set(
+
+ me[ 0 ], me[ 4 ], me[ 8 ],
+ me[ 1 ], me[ 5 ], me[ 9 ],
+ me[ 2 ], me[ 6 ], me[ 10 ]
+
+ );
+
+ return this;
+
+ }
+
+ multiply( m ) {
+
+ return this.multiplyMatrices( this, m );
+
+ }
+
+ premultiply( m ) {
+
+ return this.multiplyMatrices( m, this );
+
+ }
+
+ multiplyMatrices( a, b ) {
+
+ const ae = a.elements;
+ const be = b.elements;
+ const te = this.elements;
+
+ const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
+ const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
+ const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
+
+ const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
+ const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
+ const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
+ te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
+ te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
+ te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
+ te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
+ te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
+ te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
+
+ return this;
+
+ }
+
+ multiplyScalar( s ) {
+
+ const te = this.elements;
+
+ te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
+ te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
+ te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
+
+ return this;
+
+ }
+
+ determinant() {
+
+ const te = this.elements;
+
+ const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
+ d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
+ g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
+
+ return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
+
+ }
+
+ getInverse( matrix, throwOnDegenerate ) {
+
+ if ( throwOnDegenerate !== undefined ) {
+
+ console.warn( "THREE.Matrix3: .getInverse() can no longer be configured to throw on degenerate." );
+
+ }
+
+ const me = matrix.elements,
+ te = this.elements,
+
+ n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
+ n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
+ n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
+
+ t11 = n33 * n22 - n32 * n23,
+ t12 = n32 * n13 - n33 * n12,
+ t13 = n23 * n12 - n22 * n13,
+
+ det = n11 * t11 + n21 * t12 + n31 * t13;
+
+ if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+
+ const detInv = 1 / det;
+
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
+ te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
+
+ te[ 3 ] = t12 * detInv;
+ te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
+ te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
+
+ te[ 6 ] = t13 * detInv;
+ te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
+ te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
+
+ return this;
+
+ }
+
+ transpose() {
+
+ let tmp;
+ const m = this.elements;
+
+ tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
+ tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
+ tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
+
+ return this;
+
+ }
+
+ getNormalMatrix( matrix4 ) {
+
+ return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
+
+ }
+
+ transposeIntoArray( r ) {
+
+ const m = this.elements;
+
+ r[ 0 ] = m[ 0 ];
+ r[ 1 ] = m[ 3 ];
+ r[ 2 ] = m[ 6 ];
+ r[ 3 ] = m[ 1 ];
+ r[ 4 ] = m[ 4 ];
+ r[ 5 ] = m[ 7 ];
+ r[ 6 ] = m[ 2 ];
+ r[ 7 ] = m[ 5 ];
+ r[ 8 ] = m[ 8 ];
+
+ return this;
+
+ }
+
+ setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
+
+ const c = Math.cos( rotation );
+ const s = Math.sin( rotation );
+
+ this.set(
+ sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
+ - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
+ 0, 0, 1
+ );
+
+ }
+
+ scale( sx, sy ) {
+
+ const te = this.elements;
+
+ te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
+ te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
+
+ return this;
+
+ }
+
+ rotate( theta ) {
+
+ const c = Math.cos( theta );
+ const s = Math.sin( theta );
+
+ const te = this.elements;
+
+ const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
+ const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
+
+ te[ 0 ] = c * a11 + s * a21;
+ te[ 3 ] = c * a12 + s * a22;
+ te[ 6 ] = c * a13 + s * a23;
+
+ te[ 1 ] = - s * a11 + c * a21;
+ te[ 4 ] = - s * a12 + c * a22;
+ te[ 7 ] = - s * a13 + c * a23;
+
+ return this;
+
+ }
+
+ translate( tx, ty ) {
+
+ const te = this.elements;
+
+ te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
+ te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
+
+ return this;
+
+ }
+
+ equals( matrix ) {
+
+ const te = this.elements;
+ const me = matrix.elements;
+
+ for ( let i = 0; i < 9; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ for ( let i = 0; i < 9; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ const te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+
+ array[ offset + 3 ] = te[ 3 ];
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+ array[ offset + 8 ] = te[ 8 ];
+
+ return array;
+
+ }
+
+}
+
+let _canvas;
+
+const ImageUtils = {
+
+ getDataURL: function ( image ) {
+
+ if ( /^data:/i.test( image.src ) ) {
+
+ return image.src;
+
+ }
+
+ if ( typeof HTMLCanvasElement == 'undefined' ) {
+
+ return image.src;
+
+ }
+
+ let canvas;
+
+ if ( image instanceof HTMLCanvasElement ) {
+
+ canvas = image;
+
+ } else {
+
+ if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+
+ _canvas.width = image.width;
+ _canvas.height = image.height;
+
+ const context = _canvas.getContext( '2d' );
+
+ if ( image instanceof ImageData ) {
+
+ context.putImageData( image, 0, 0 );
+
+ } else {
+
+ context.drawImage( image, 0, 0, image.width, image.height );
+
+ }
+
+ canvas = _canvas;
+
+ }
+
+ if ( canvas.width > 2048 || canvas.height > 2048 ) {
+
+ return canvas.toDataURL( 'image/jpeg', 0.6 );
+
+ } else {
+
+ return canvas.toDataURL( 'image/png' );
+
+ }
+
+ }
+
+};
+
+let textureId = 0;
+
+function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
+
+ Object.defineProperty( this, 'id', { value: textureId ++ } );
+
+ this.uuid = MathUtils.generateUUID();
+
+ this.name = '';
+
+ this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;
+ this.mipmaps = [];
+
+ this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;
+
+ this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;
+ this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;
+
+ this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : LinearMipmapLinearFilter;
+
+ this.anisotropy = anisotropy !== undefined ? anisotropy : 1;
+
+ this.format = format !== undefined ? format : RGBAFormat;
+ this.internalFormat = null;
+ this.type = type !== undefined ? type : UnsignedByteType;
+
+ this.offset = new Vector2( 0, 0 );
+ this.repeat = new Vector2( 1, 1 );
+ this.center = new Vector2( 0, 0 );
+ this.rotation = 0;
+
+ this.matrixAutoUpdate = true;
+ this.matrix = new Matrix3();
+
+ this.generateMipmaps = true;
+ this.premultiplyAlpha = false;
+ this.flipY = true;
+ this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
+
+ // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
+ //
+ // Also changing the encoding after already used by a Material will not automatically make the Material
+ // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.
+ this.encoding = encoding !== undefined ? encoding : LinearEncoding;
+
+ this.version = 0;
+ this.onUpdate = null;
+
+}
+
+Texture.DEFAULT_IMAGE = undefined;
+Texture.DEFAULT_MAPPING = UVMapping;
+
+Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: Texture,
+
+ isTexture: true,
+
+ updateMatrix: function () {
+
+ this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.image = source.image;
+ this.mipmaps = source.mipmaps.slice( 0 );
+
+ this.mapping = source.mapping;
+
+ this.wrapS = source.wrapS;
+ this.wrapT = source.wrapT;
+
+ this.magFilter = source.magFilter;
+ this.minFilter = source.minFilter;
+
+ this.anisotropy = source.anisotropy;
+
+ this.format = source.format;
+ this.internalFormat = source.internalFormat;
+ this.type = source.type;
+
+ this.offset.copy( source.offset );
+ this.repeat.copy( source.repeat );
+ this.center.copy( source.center );
+ this.rotation = source.rotation;
+
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+ this.matrix.copy( source.matrix );
+
+ this.generateMipmaps = source.generateMipmaps;
+ this.premultiplyAlpha = source.premultiplyAlpha;
+ this.flipY = source.flipY;
+ this.unpackAlignment = source.unpackAlignment;
+ this.encoding = source.encoding;
+
+ return this;
+
+ },
+
+ toJSON: function ( meta ) {
+
+ const isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+ if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {
+
+ return meta.textures[ this.uuid ];
+
+ }
+
+ const output = {
+
+ metadata: {
+ version: 4.5,
+ type: 'Texture',
+ generator: 'Texture.toJSON'
+ },
+
+ uuid: this.uuid,
+ name: this.name,
+
+ mapping: this.mapping,
+
+ repeat: [ this.repeat.x, this.repeat.y ],
+ offset: [ this.offset.x, this.offset.y ],
+ center: [ this.center.x, this.center.y ],
+ rotation: this.rotation,
+
+ wrap: [ this.wrapS, this.wrapT ],
+
+ format: this.format,
+ type: this.type,
+ encoding: this.encoding,
+
+ minFilter: this.minFilter,
+ magFilter: this.magFilter,
+ anisotropy: this.anisotropy,
+
+ flipY: this.flipY,
+
+ premultiplyAlpha: this.premultiplyAlpha,
+ unpackAlignment: this.unpackAlignment
+
+ };
+
+ if ( this.image !== undefined ) {
+
+ // TODO: Move to THREE.Image
+
+ const image = this.image;
+
+ if ( image.uuid === undefined ) {
+
+ image.uuid = MathUtils.generateUUID(); // UGH
+
+ }
+
+ if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {
+
+ let url;
+
+ if ( Array.isArray( image ) ) {
+
+ // process array of images e.g. CubeTexture
+
+ url = [];
+
+ for ( let i = 0, l = image.length; i < l; i ++ ) {
+
+ url.push( ImageUtils.getDataURL( image[ i ] ) );
+
+ }
+
+ } else {
+
+ // process single image
+
+ url = ImageUtils.getDataURL( image );
+
+ }
+
+ meta.images[ image.uuid ] = {
+ uuid: image.uuid,
+ url: url
+ };
+
+ }
+
+ output.image = image.uuid;
+
+ }
+
+ if ( ! isRootObject ) {
+
+ meta.textures[ this.uuid ] = output;
+
+ }
+
+ return output;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ },
+
+ transformUv: function ( uv ) {
+
+ if ( this.mapping !== UVMapping ) return uv;
+
+ uv.applyMatrix3( this.matrix );
+
+ if ( uv.x < 0 || uv.x > 1 ) {
+
+ switch ( this.wrapS ) {
+
+ case RepeatWrapping:
+
+ uv.x = uv.x - Math.floor( uv.x );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.x = uv.x < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+ uv.x = Math.ceil( uv.x ) - uv.x;
+
+ } else {
+
+ uv.x = uv.x - Math.floor( uv.x );
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ if ( uv.y < 0 || uv.y > 1 ) {
+
+ switch ( this.wrapT ) {
+
+ case RepeatWrapping:
+
+ uv.y = uv.y - Math.floor( uv.y );
+ break;
+
+ case ClampToEdgeWrapping:
+
+ uv.y = uv.y < 0 ? 0 : 1;
+ break;
+
+ case MirroredRepeatWrapping:
+
+ if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+ uv.y = Math.ceil( uv.y ) - uv.y;
+
+ } else {
+
+ uv.y = uv.y - Math.floor( uv.y );
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ if ( this.flipY ) {
+
+ uv.y = 1 - uv.y;
+
+ }
+
+ return uv;
+
+ }
+
+} );
+
+Object.defineProperty( Texture.prototype, "needsUpdate", {
+
+ set: function ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+} );
+
+class Vector4 {
+
+ constructor( x = 0, y = 0, z = 0, w = 1 ) {
+
+ Object.defineProperty( this, 'isVector4', { value: true } );
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+
+ }
+
+ get width() {
+
+ return this.z;
+
+ }
+
+ set width( value ) {
+
+ this.z = value;
+
+ }
+
+ get height() {
+
+ return this.w;
+
+ }
+
+ set height( value ) {
+
+ this.w = value;
+
+ }
+
+ set( x, y, z, w ) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+
+ return this;
+
+ }
+
+ setScalar( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+ this.w = scalar;
+
+ return this;
+
+ }
+
+ setX( x ) {
+
+ this.x = x;
+
+ return this;
+
+ }
+
+ setY( y ) {
+
+ this.y = y;
+
+ return this;
+
+ }
+
+ setZ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ }
+
+ setW( w ) {
+
+ this.w = w;
+
+ return this;
+
+ }
+
+ setComponent( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ case 3: this.w = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ }
+
+ getComponent( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ case 3: return this.w;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ }
+
+ clone() {
+
+ return new this.constructor( this.x, this.y, this.z, this.w );
+
+ }
+
+ copy( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+ this.w = ( v.w !== undefined ) ? v.w : 1;
+
+ return this;
+
+ }
+
+ add( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+ this.w += v.w;
+
+ return this;
+
+ }
+
+ addScalar( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+ this.w += s;
+
+ return this;
+
+ }
+
+ addVectors( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+ this.w = a.w + b.w;
+
+ return this;
+
+ }
+
+ addScaledVector( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+ this.w += v.w * s;
+
+ return this;
+
+ }
+
+ sub( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+ this.w -= v.w;
+
+ return this;
+
+ }
+
+ subScalar( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+ this.w -= s;
+
+ return this;
+
+ }
+
+ subVectors( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+ this.w = a.w - b.w;
+
+ return this;
+
+ }
+
+ multiplyScalar( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+ this.w *= scalar;
+
+ return this;
+
+ }
+
+ applyMatrix4( m ) {
+
+ const x = this.x, y = this.y, z = this.z, w = this.w;
+ const e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;
+ this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;
+
+ return this;
+
+ }
+
+ divideScalar( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ }
+
+ setAxisAngleFromQuaternion( q ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+
+ // q is assumed to be normalized
+
+ this.w = 2 * Math.acos( q.w );
+
+ const s = Math.sqrt( 1 - q.w * q.w );
+
+ if ( s < 0.0001 ) {
+
+ this.x = 1;
+ this.y = 0;
+ this.z = 0;
+
+ } else {
+
+ this.x = q.x / s;
+ this.y = q.y / s;
+ this.z = q.z / s;
+
+ }
+
+ return this;
+
+ }
+
+ setAxisAngleFromRotationMatrix( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ let angle, x, y, z; // variables for result
+ const epsilon = 0.01, // margin to allow for rounding errors
+ epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees
+
+ te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ if ( ( Math.abs( m12 - m21 ) < epsilon ) &&
+ ( Math.abs( m13 - m31 ) < epsilon ) &&
+ ( Math.abs( m23 - m32 ) < epsilon ) ) {
+
+ // singularity found
+ // first check for identity matrix which must have +1 for all terms
+ // in leading diagonal and zero in other terms
+
+ if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&
+ ( Math.abs( m13 + m31 ) < epsilon2 ) &&
+ ( Math.abs( m23 + m32 ) < epsilon2 ) &&
+ ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {
+
+ // this singularity is identity matrix so angle = 0
+
+ this.set( 1, 0, 0, 0 );
+
+ return this; // zero angle, arbitrary axis
+
+ }
+
+ // otherwise this singularity is angle = 180
+
+ angle = Math.PI;
+
+ const xx = ( m11 + 1 ) / 2;
+ const yy = ( m22 + 1 ) / 2;
+ const zz = ( m33 + 1 ) / 2;
+ const xy = ( m12 + m21 ) / 4;
+ const xz = ( m13 + m31 ) / 4;
+ const yz = ( m23 + m32 ) / 4;
+
+ if ( ( xx > yy ) && ( xx > zz ) ) {
+
+ // m11 is the largest diagonal term
+
+ if ( xx < epsilon ) {
+
+ x = 0;
+ y = 0.707106781;
+ z = 0.707106781;
+
+ } else {
+
+ x = Math.sqrt( xx );
+ y = xy / x;
+ z = xz / x;
+
+ }
+
+ } else if ( yy > zz ) {
+
+ // m22 is the largest diagonal term
+
+ if ( yy < epsilon ) {
+
+ x = 0.707106781;
+ y = 0;
+ z = 0.707106781;
+
+ } else {
+
+ y = Math.sqrt( yy );
+ x = xy / y;
+ z = yz / y;
+
+ }
+
+ } else {
+
+ // m33 is the largest diagonal term so base result on this
+
+ if ( zz < epsilon ) {
+
+ x = 0.707106781;
+ y = 0.707106781;
+ z = 0;
+
+ } else {
+
+ z = Math.sqrt( zz );
+ x = xz / z;
+ y = yz / z;
+
+ }
+
+ }
+
+ this.set( x, y, z, angle );
+
+ return this; // return 180 deg rotation
+
+ }
+
+ // as we have reached here there are no singularities so we can handle normally
+
+ let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +
+ ( m13 - m31 ) * ( m13 - m31 ) +
+ ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize
+
+ if ( Math.abs( s ) < 0.001 ) s = 1;
+
+ // prevent divide by zero, should not happen if matrix is orthogonal and should be
+ // caught by singularity test above, but I've left it in just in case
+
+ this.x = ( m32 - m23 ) / s;
+ this.y = ( m13 - m31 ) / s;
+ this.z = ( m21 - m12 ) / s;
+ this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );
+
+ return this;
+
+ }
+
+ min( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+ this.w = Math.min( this.w, v.w );
+
+ return this;
+
+ }
+
+ max( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+ this.w = Math.max( this.w, v.w );
+
+ return this;
+
+ }
+
+ clamp( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+ this.w = Math.max( min.w, Math.min( max.w, this.w ) );
+
+ return this;
+
+ }
+
+ clampScalar( minVal, maxVal ) {
+
+ this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
+ this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
+ this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
+ this.w = Math.max( minVal, Math.min( maxVal, this.w ) );
+
+ return this;
+
+ }
+
+ clampLength( min, max ) {
+
+ const length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ }
+
+ floor() {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+ this.w = Math.floor( this.w );
+
+ return this;
+
+ }
+
+ ceil() {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+ this.w = Math.ceil( this.w );
+
+ return this;
+
+ }
+
+ round() {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+ this.w = Math.round( this.w );
+
+ return this;
+
+ }
+
+ roundToZero() {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+ this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );
+
+ return this;
+
+ }
+
+ negate() {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+ this.w = - this.w;
+
+ return this;
+
+ }
+
+ dot( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
+
+ }
+
+ lengthSq() {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
+
+ }
+
+ length() {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
+
+ }
+
+ manhattanLength() {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );
+
+ }
+
+ normalize() {
+
+ return this.divideScalar( this.length() || 1 );
+
+ }
+
+ setLength( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ }
+
+ lerp( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+ this.w += ( v.w - this.w ) * alpha;
+
+ return this;
+
+ }
+
+ lerpVectors( v1, v2, alpha ) {
+
+ this.x = v1.x + ( v2.x - v1.x ) * alpha;
+ this.y = v1.y + ( v2.y - v1.y ) * alpha;
+ this.z = v1.z + ( v2.z - v1.z ) * alpha;
+ this.w = v1.w + ( v2.w - v1.w ) * alpha;
+
+ return this;
+
+ }
+
+ equals( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+ this.w = array[ offset + 3 ];
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+ array[ offset + 3 ] = this.w;
+
+ return array;
+
+ }
+
+ fromBufferAttribute( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+ this.w = attribute.getW( index );
+
+ return this;
+
+ }
+
+ random() {
+
+ this.x = Math.random();
+ this.y = Math.random();
+ this.z = Math.random();
+ this.w = Math.random();
+
+ return this;
+
+ }
+
+}
+
+/*
+ In options, we can specify:
+ * Texture parameters for an auto-generated target texture
+ * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers
+*/
+function WebGLRenderTarget( width, height, options ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.scissor = new Vector4( 0, 0, width, height );
+ this.scissorTest = false;
+
+ this.viewport = new Vector4( 0, 0, width, height );
+
+ options = options || {};
+
+ this.texture = new Texture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
+
+ this.texture.image = {};
+ this.texture.image.width = width;
+ this.texture.image.height = height;
+
+ this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false;
+ this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter;
+
+ this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
+ this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : false;
+ this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
+
+}
+
+WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: WebGLRenderTarget,
+
+ isWebGLRenderTarget: true,
+
+ setSize: function ( width, height ) {
+
+ if ( this.width !== width || this.height !== height ) {
+
+ this.width = width;
+ this.height = height;
+
+ this.texture.image.width = width;
+ this.texture.image.height = height;
+
+ this.dispose();
+
+ }
+
+ this.viewport.set( 0, 0, width, height );
+ this.scissor.set( 0, 0, width, height );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.width = source.width;
+ this.height = source.height;
+
+ this.viewport.copy( source.viewport );
+
+ this.texture = source.texture.clone();
+
+ this.depthBuffer = source.depthBuffer;
+ this.stencilBuffer = source.stencilBuffer;
+ this.depthTexture = source.depthTexture;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+} );
+
+function WebGLMultisampleRenderTarget( width, height, options ) {
+
+ WebGLRenderTarget.call( this, width, height, options );
+
+ this.samples = 4;
+
+}
+
+WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), {
+
+ constructor: WebGLMultisampleRenderTarget,
+
+ isWebGLMultisampleRenderTarget: true,
+
+ copy: function ( source ) {
+
+ WebGLRenderTarget.prototype.copy.call( this, source );
+
+ this.samples = source.samples;
+
+ return this;
+
+ }
+
+} );
+
+class Quaternion {
+
+ constructor( x = 0, y = 0, z = 0, w = 1 ) {
+
+ Object.defineProperty( this, 'isQuaternion', { value: true } );
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ }
+
+ static slerp( qa, qb, qm, t ) {
+
+ return qm.copy( qa ).slerp( qb, t );
+
+ }
+
+ static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
+
+ // fuzz-free, array-based Quaternion SLERP operation
+
+ let x0 = src0[ srcOffset0 + 0 ],
+ y0 = src0[ srcOffset0 + 1 ],
+ z0 = src0[ srcOffset0 + 2 ],
+ w0 = src0[ srcOffset0 + 3 ];
+
+ const x1 = src1[ srcOffset1 + 0 ],
+ y1 = src1[ srcOffset1 + 1 ],
+ z1 = src1[ srcOffset1 + 2 ],
+ w1 = src1[ srcOffset1 + 3 ];
+
+ if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
+
+ let s = 1 - t;
+ const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
+ dir = ( cos >= 0 ? 1 : - 1 ),
+ sqrSin = 1 - cos * cos;
+
+ // Skip the Slerp for tiny steps to avoid numeric problems:
+ if ( sqrSin > Number.EPSILON ) {
+
+ const sin = Math.sqrt( sqrSin ),
+ len = Math.atan2( sin, cos * dir );
+
+ s = Math.sin( s * len ) / sin;
+ t = Math.sin( t * len ) / sin;
+
+ }
+
+ const tDir = t * dir;
+
+ x0 = x0 * s + x1 * tDir;
+ y0 = y0 * s + y1 * tDir;
+ z0 = z0 * s + z1 * tDir;
+ w0 = w0 * s + w1 * tDir;
+
+ // Normalize in case we just did a lerp:
+ if ( s === 1 - t ) {
+
+ const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
+
+ x0 *= f;
+ y0 *= f;
+ z0 *= f;
+ w0 *= f;
+
+ }
+
+ }
+
+ dst[ dstOffset ] = x0;
+ dst[ dstOffset + 1 ] = y0;
+ dst[ dstOffset + 2 ] = z0;
+ dst[ dstOffset + 3 ] = w0;
+
+ }
+
+ static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
+
+ const x0 = src0[ srcOffset0 ];
+ const y0 = src0[ srcOffset0 + 1 ];
+ const z0 = src0[ srcOffset0 + 2 ];
+ const w0 = src0[ srcOffset0 + 3 ];
+
+ const x1 = src1[ srcOffset1 ];
+ const y1 = src1[ srcOffset1 + 1 ];
+ const z1 = src1[ srcOffset1 + 2 ];
+ const w1 = src1[ srcOffset1 + 3 ];
+
+ dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
+ dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
+ dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
+ dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
+
+ return dst;
+
+ }
+
+ get x() {
+
+ return this._x;
+
+ }
+
+ set x( value ) {
+
+ this._x = value;
+ this._onChangeCallback();
+
+ }
+
+ get y() {
+
+ return this._y;
+
+ }
+
+ set y( value ) {
+
+ this._y = value;
+ this._onChangeCallback();
+
+ }
+
+ get z() {
+
+ return this._z;
+
+ }
+
+ set z( value ) {
+
+ this._z = value;
+ this._onChangeCallback();
+
+ }
+
+ get w() {
+
+ return this._w;
+
+ }
+
+ set w( value ) {
+
+ this._w = value;
+ this._onChangeCallback();
+
+ }
+
+ set( x, y, z, w ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._w = w;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor( this._x, this._y, this._z, this._w );
+
+ }
+
+ copy( quaternion ) {
+
+ this._x = quaternion.x;
+ this._y = quaternion.y;
+ this._z = quaternion.z;
+ this._w = quaternion.w;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromEuler( euler, update ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
+
+ // http://www.mathworks.com/matlabcentral/fileexchange/
+ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
+ // content/SpinCalc.m
+
+ const cos = Math.cos;
+ const sin = Math.sin;
+
+ const c1 = cos( x / 2 );
+ const c2 = cos( y / 2 );
+ const c3 = cos( z / 2 );
+
+ const s1 = sin( x / 2 );
+ const s2 = sin( y / 2 );
+ const s3 = sin( z / 2 );
+
+ switch ( order ) {
+
+ case 'XYZ':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
+
+ case 'YXZ':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
+
+ case 'ZXY':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
+
+ case 'ZYX':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
+
+ case 'YZX':
+ this._x = s1 * c2 * c3 + c1 * s2 * s3;
+ this._y = c1 * s2 * c3 + s1 * c2 * s3;
+ this._z = c1 * c2 * s3 - s1 * s2 * c3;
+ this._w = c1 * c2 * c3 - s1 * s2 * s3;
+ break;
+
+ case 'XZY':
+ this._x = s1 * c2 * c3 - c1 * s2 * s3;
+ this._y = c1 * s2 * c3 - s1 * c2 * s3;
+ this._z = c1 * c2 * s3 + s1 * s2 * c3;
+ this._w = c1 * c2 * c3 + s1 * s2 * s3;
+ break;
+
+ default:
+ console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
+
+ }
+
+ if ( update !== false ) this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromAxisAngle( axis, angle ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+ // assumes axis is normalized
+
+ const halfAngle = angle / 2, s = Math.sin( halfAngle );
+
+ this._x = axis.x * s;
+ this._y = axis.y * s;
+ this._z = axis.z * s;
+ this._w = Math.cos( halfAngle );
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromRotationMatrix( m ) {
+
+ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ const te = m.elements,
+
+ m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
+ m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
+ m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
+
+ trace = m11 + m22 + m33;
+
+ if ( trace > 0 ) {
+
+ const s = 0.5 / Math.sqrt( trace + 1.0 );
+
+ this._w = 0.25 / s;
+ this._x = ( m32 - m23 ) * s;
+ this._y = ( m13 - m31 ) * s;
+ this._z = ( m21 - m12 ) * s;
+
+ } else if ( m11 > m22 && m11 > m33 ) {
+
+ const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
+
+ this._w = ( m32 - m23 ) / s;
+ this._x = 0.25 * s;
+ this._y = ( m12 + m21 ) / s;
+ this._z = ( m13 + m31 ) / s;
+
+ } else if ( m22 > m33 ) {
+
+ const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
+
+ this._w = ( m13 - m31 ) / s;
+ this._x = ( m12 + m21 ) / s;
+ this._y = 0.25 * s;
+ this._z = ( m23 + m32 ) / s;
+
+ } else {
+
+ const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
+
+ this._w = ( m21 - m12 ) / s;
+ this._x = ( m13 + m31 ) / s;
+ this._y = ( m23 + m32 ) / s;
+ this._z = 0.25 * s;
+
+ }
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromUnitVectors( vFrom, vTo ) {
+
+ // assumes direction vectors vFrom and vTo are normalized
+
+ const EPS = 0.000001;
+
+ let r = vFrom.dot( vTo ) + 1;
+
+ if ( r < EPS ) {
+
+ r = 0;
+
+ if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
+
+ this._x = - vFrom.y;
+ this._y = vFrom.x;
+ this._z = 0;
+ this._w = r;
+
+ } else {
+
+ this._x = 0;
+ this._y = - vFrom.z;
+ this._z = vFrom.y;
+ this._w = r;
+
+ }
+
+ } else {
+
+ // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
+
+ this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
+ this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
+ this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
+ this._w = r;
+
+ }
+
+ return this.normalize();
+
+ }
+
+ angleTo( q ) {
+
+ return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) );
+
+ }
+
+ rotateTowards( q, step ) {
+
+ const angle = this.angleTo( q );
+
+ if ( angle === 0 ) return this;
+
+ const t = Math.min( 1, step / angle );
+
+ this.slerp( q, t );
+
+ return this;
+
+ }
+
+ identity() {
+
+ return this.set( 0, 0, 0, 1 );
+
+ }
+
+ inverse() {
+
+ // quaternion is assumed to have unit length
+
+ return this.conjugate();
+
+ }
+
+ conjugate() {
+
+ this._x *= - 1;
+ this._y *= - 1;
+ this._z *= - 1;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ dot( v ) {
+
+ return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
+
+ }
+
+ lengthSq() {
+
+ return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
+
+ }
+
+ length() {
+
+ return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
+
+ }
+
+ normalize() {
+
+ let l = this.length();
+
+ if ( l === 0 ) {
+
+ this._x = 0;
+ this._y = 0;
+ this._z = 0;
+ this._w = 1;
+
+ } else {
+
+ l = 1 / l;
+
+ this._x = this._x * l;
+ this._y = this._y * l;
+ this._z = this._z * l;
+ this._w = this._w * l;
+
+ }
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ multiply( q, p ) {
+
+ if ( p !== undefined ) {
+
+ console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
+ return this.multiplyQuaternions( q, p );
+
+ }
+
+ return this.multiplyQuaternions( this, q );
+
+ }
+
+ premultiply( q ) {
+
+ return this.multiplyQuaternions( q, this );
+
+ }
+
+ multiplyQuaternions( a, b ) {
+
+ // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
+
+ const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
+ const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
+
+ this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
+ this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
+ this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
+ this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ slerp( qb, t ) {
+
+ if ( t === 0 ) return this;
+ if ( t === 1 ) return this.copy( qb );
+
+ const x = this._x, y = this._y, z = this._z, w = this._w;
+
+ // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
+
+ let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
+
+ if ( cosHalfTheta < 0 ) {
+
+ this._w = - qb._w;
+ this._x = - qb._x;
+ this._y = - qb._y;
+ this._z = - qb._z;
+
+ cosHalfTheta = - cosHalfTheta;
+
+ } else {
+
+ this.copy( qb );
+
+ }
+
+ if ( cosHalfTheta >= 1.0 ) {
+
+ this._w = w;
+ this._x = x;
+ this._y = y;
+ this._z = z;
+
+ return this;
+
+ }
+
+ const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
+
+ if ( sqrSinHalfTheta <= Number.EPSILON ) {
+
+ const s = 1 - t;
+ this._w = s * w + t * this._w;
+ this._x = s * x + t * this._x;
+ this._y = s * y + t * this._y;
+ this._z = s * z + t * this._z;
+
+ this.normalize();
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ const sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
+ const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
+ const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
+ ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
+
+ this._w = ( w * ratioA + this._w * ratioB );
+ this._x = ( x * ratioA + this._x * ratioB );
+ this._y = ( y * ratioA + this._y * ratioB );
+ this._z = ( z * ratioA + this._z * ratioB );
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ equals( quaternion ) {
+
+ return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ this._x = array[ offset ];
+ this._y = array[ offset + 1 ];
+ this._z = array[ offset + 2 ];
+ this._w = array[ offset + 3 ];
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._w;
+
+ return array;
+
+ }
+
+ fromBufferAttribute( attribute, index ) {
+
+ this._x = attribute.getX( index );
+ this._y = attribute.getY( index );
+ this._z = attribute.getZ( index );
+ this._w = attribute.getW( index );
+
+ return this;
+
+ }
+
+ _onChange( callback ) {
+
+ this._onChangeCallback = callback;
+
+ return this;
+
+ }
+
+ _onChangeCallback() {}
+
+}
+
+class Vector3 {
+
+ constructor( x = 0, y = 0, z = 0 ) {
+
+ Object.defineProperty( this, 'isVector3', { value: true } );
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ }
+
+ set( x, y, z ) {
+
+ if ( z === undefined ) z = this.z; // sprite.scale.set(x,y)
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+
+ return this;
+
+ }
+
+ setScalar( scalar ) {
+
+ this.x = scalar;
+ this.y = scalar;
+ this.z = scalar;
+
+ return this;
+
+ }
+
+ setX( x ) {
+
+ this.x = x;
+
+ return this;
+
+ }
+
+ setY( y ) {
+
+ this.y = y;
+
+ return this;
+
+ }
+
+ setZ( z ) {
+
+ this.z = z;
+
+ return this;
+
+ }
+
+ setComponent( index, value ) {
+
+ switch ( index ) {
+
+ case 0: this.x = value; break;
+ case 1: this.y = value; break;
+ case 2: this.z = value; break;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ return this;
+
+ }
+
+ getComponent( index ) {
+
+ switch ( index ) {
+
+ case 0: return this.x;
+ case 1: return this.y;
+ case 2: return this.z;
+ default: throw new Error( 'index is out of range: ' + index );
+
+ }
+
+ }
+
+ clone() {
+
+ return new this.constructor( this.x, this.y, this.z );
+
+ }
+
+ copy( v ) {
+
+ this.x = v.x;
+ this.y = v.y;
+ this.z = v.z;
+
+ return this;
+
+ }
+
+ add( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
+ return this.addVectors( v, w );
+
+ }
+
+ this.x += v.x;
+ this.y += v.y;
+ this.z += v.z;
+
+ return this;
+
+ }
+
+ addScalar( s ) {
+
+ this.x += s;
+ this.y += s;
+ this.z += s;
+
+ return this;
+
+ }
+
+ addVectors( a, b ) {
+
+ this.x = a.x + b.x;
+ this.y = a.y + b.y;
+ this.z = a.z + b.z;
+
+ return this;
+
+ }
+
+ addScaledVector( v, s ) {
+
+ this.x += v.x * s;
+ this.y += v.y * s;
+ this.z += v.z * s;
+
+ return this;
+
+ }
+
+ sub( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
+ return this.subVectors( v, w );
+
+ }
+
+ this.x -= v.x;
+ this.y -= v.y;
+ this.z -= v.z;
+
+ return this;
+
+ }
+
+ subScalar( s ) {
+
+ this.x -= s;
+ this.y -= s;
+ this.z -= s;
+
+ return this;
+
+ }
+
+ subVectors( a, b ) {
+
+ this.x = a.x - b.x;
+ this.y = a.y - b.y;
+ this.z = a.z - b.z;
+
+ return this;
+
+ }
+
+ multiply( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
+ return this.multiplyVectors( v, w );
+
+ }
+
+ this.x *= v.x;
+ this.y *= v.y;
+ this.z *= v.z;
+
+ return this;
+
+ }
+
+ multiplyScalar( scalar ) {
+
+ this.x *= scalar;
+ this.y *= scalar;
+ this.z *= scalar;
+
+ return this;
+
+ }
+
+ multiplyVectors( a, b ) {
+
+ this.x = a.x * b.x;
+ this.y = a.y * b.y;
+ this.z = a.z * b.z;
+
+ return this;
+
+ }
+
+ applyEuler( euler ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ return this.applyQuaternion( _quaternion.setFromEuler( euler ) );
+
+ }
+
+ applyAxisAngle( axis, angle ) {
+
+ return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) );
+
+ }
+
+ applyMatrix3( m ) {
+
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
+ this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
+ this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
+
+ return this;
+
+ }
+
+ applyNormalMatrix( m ) {
+
+ return this.applyMatrix3( m ).normalize();
+
+ }
+
+ applyMatrix4( m ) {
+
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
+
+ const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
+
+ this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
+ this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
+ this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
+
+ return this;
+
+ }
+
+ applyQuaternion( q ) {
+
+ const x = this.x, y = this.y, z = this.z;
+ const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
+
+ // calculate quat * vector
+
+ const ix = qw * x + qy * z - qz * y;
+ const iy = qw * y + qz * x - qx * z;
+ const iz = qw * z + qx * y - qy * x;
+ const iw = - qx * x - qy * y - qz * z;
+
+ // calculate result * inverse quat
+
+ this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
+ this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
+ this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
+
+ return this;
+
+ }
+
+ project( camera ) {
+
+ return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
+
+ }
+
+ unproject( camera ) {
+
+ return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );
+
+ }
+
+ transformDirection( m ) {
+
+ // input: THREE.Matrix4 affine matrix
+ // vector interpreted as a direction
+
+ const x = this.x, y = this.y, z = this.z;
+ const e = m.elements;
+
+ this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
+ this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
+ this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
+
+ return this.normalize();
+
+ }
+
+ divide( v ) {
+
+ this.x /= v.x;
+ this.y /= v.y;
+ this.z /= v.z;
+
+ return this;
+
+ }
+
+ divideScalar( scalar ) {
+
+ return this.multiplyScalar( 1 / scalar );
+
+ }
+
+ min( v ) {
+
+ this.x = Math.min( this.x, v.x );
+ this.y = Math.min( this.y, v.y );
+ this.z = Math.min( this.z, v.z );
+
+ return this;
+
+ }
+
+ max( v ) {
+
+ this.x = Math.max( this.x, v.x );
+ this.y = Math.max( this.y, v.y );
+ this.z = Math.max( this.z, v.z );
+
+ return this;
+
+ }
+
+ clamp( min, max ) {
+
+ // assumes min < max, componentwise
+
+ this.x = Math.max( min.x, Math.min( max.x, this.x ) );
+ this.y = Math.max( min.y, Math.min( max.y, this.y ) );
+ this.z = Math.max( min.z, Math.min( max.z, this.z ) );
+
+ return this;
+
+ }
+
+ clampScalar( minVal, maxVal ) {
+
+ this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
+ this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
+ this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
+
+ return this;
+
+ }
+
+ clampLength( min, max ) {
+
+ const length = this.length();
+
+ return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
+
+ }
+
+ floor() {
+
+ this.x = Math.floor( this.x );
+ this.y = Math.floor( this.y );
+ this.z = Math.floor( this.z );
+
+ return this;
+
+ }
+
+ ceil() {
+
+ this.x = Math.ceil( this.x );
+ this.y = Math.ceil( this.y );
+ this.z = Math.ceil( this.z );
+
+ return this;
+
+ }
+
+ round() {
+
+ this.x = Math.round( this.x );
+ this.y = Math.round( this.y );
+ this.z = Math.round( this.z );
+
+ return this;
+
+ }
+
+ roundToZero() {
+
+ this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
+ this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
+ this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
+
+ return this;
+
+ }
+
+ negate() {
+
+ this.x = - this.x;
+ this.y = - this.y;
+ this.z = - this.z;
+
+ return this;
+
+ }
+
+ dot( v ) {
+
+ return this.x * v.x + this.y * v.y + this.z * v.z;
+
+ }
+
+ // TODO lengthSquared?
+
+ lengthSq() {
+
+ return this.x * this.x + this.y * this.y + this.z * this.z;
+
+ }
+
+ length() {
+
+ return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
+
+ }
+
+ manhattanLength() {
+
+ return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
+
+ }
+
+ normalize() {
+
+ return this.divideScalar( this.length() || 1 );
+
+ }
+
+ setLength( length ) {
+
+ return this.normalize().multiplyScalar( length );
+
+ }
+
+ lerp( v, alpha ) {
+
+ this.x += ( v.x - this.x ) * alpha;
+ this.y += ( v.y - this.y ) * alpha;
+ this.z += ( v.z - this.z ) * alpha;
+
+ return this;
+
+ }
+
+ lerpVectors( v1, v2, alpha ) {
+
+ this.x = v1.x + ( v2.x - v1.x ) * alpha;
+ this.y = v1.y + ( v2.y - v1.y ) * alpha;
+ this.z = v1.z + ( v2.z - v1.z ) * alpha;
+
+ return this;
+
+ }
+
+ cross( v, w ) {
+
+ if ( w !== undefined ) {
+
+ console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
+ return this.crossVectors( v, w );
+
+ }
+
+ return this.crossVectors( this, v );
+
+ }
+
+ crossVectors( a, b ) {
+
+ const ax = a.x, ay = a.y, az = a.z;
+ const bx = b.x, by = b.y, bz = b.z;
+
+ this.x = ay * bz - az * by;
+ this.y = az * bx - ax * bz;
+ this.z = ax * by - ay * bx;
+
+ return this;
+
+ }
+
+ projectOnVector( v ) {
+
+ const denominator = v.lengthSq();
+
+ if ( denominator === 0 ) return this.set( 0, 0, 0 );
+
+ const scalar = v.dot( this ) / denominator;
+
+ return this.copy( v ).multiplyScalar( scalar );
+
+ }
+
+ projectOnPlane( planeNormal ) {
+
+ _vector.copy( this ).projectOnVector( planeNormal );
+
+ return this.sub( _vector );
+
+ }
+
+ reflect( normal ) {
+
+ // reflect incident vector off plane orthogonal to normal
+ // normal is assumed to have unit length
+
+ return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
+
+ }
+
+ angleTo( v ) {
+
+ const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
+
+ if ( denominator === 0 ) return Math.PI / 2;
+
+ const theta = this.dot( v ) / denominator;
+
+ // clamp, to handle numerical problems
+
+ return Math.acos( MathUtils.clamp( theta, - 1, 1 ) );
+
+ }
+
+ distanceTo( v ) {
+
+ return Math.sqrt( this.distanceToSquared( v ) );
+
+ }
+
+ distanceToSquared( v ) {
+
+ const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
+
+ return dx * dx + dy * dy + dz * dz;
+
+ }
+
+ manhattanDistanceTo( v ) {
+
+ return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
+
+ }
+
+ setFromSpherical( s ) {
+
+ return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
+
+ }
+
+ setFromSphericalCoords( radius, phi, theta ) {
+
+ const sinPhiRadius = Math.sin( phi ) * radius;
+
+ this.x = sinPhiRadius * Math.sin( theta );
+ this.y = Math.cos( phi ) * radius;
+ this.z = sinPhiRadius * Math.cos( theta );
+
+ return this;
+
+ }
+
+ setFromCylindrical( c ) {
+
+ return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
+
+ }
+
+ setFromCylindricalCoords( radius, theta, y ) {
+
+ this.x = radius * Math.sin( theta );
+ this.y = y;
+ this.z = radius * Math.cos( theta );
+
+ return this;
+
+ }
+
+ setFromMatrixPosition( m ) {
+
+ const e = m.elements;
+
+ this.x = e[ 12 ];
+ this.y = e[ 13 ];
+ this.z = e[ 14 ];
+
+ return this;
+
+ }
+
+ setFromMatrixScale( m ) {
+
+ const sx = this.setFromMatrixColumn( m, 0 ).length();
+ const sy = this.setFromMatrixColumn( m, 1 ).length();
+ const sz = this.setFromMatrixColumn( m, 2 ).length();
+
+ this.x = sx;
+ this.y = sy;
+ this.z = sz;
+
+ return this;
+
+ }
+
+ setFromMatrixColumn( m, index ) {
+
+ return this.fromArray( m.elements, index * 4 );
+
+ }
+
+ setFromMatrix3Column( m, index ) {
+
+ return this.fromArray( m.elements, index * 3 );
+
+ }
+
+ equals( v ) {
+
+ return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ this.x = array[ offset ];
+ this.y = array[ offset + 1 ];
+ this.z = array[ offset + 2 ];
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this.x;
+ array[ offset + 1 ] = this.y;
+ array[ offset + 2 ] = this.z;
+
+ return array;
+
+ }
+
+ fromBufferAttribute( attribute, index, offset ) {
+
+ if ( offset !== undefined ) {
+
+ console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
+
+ }
+
+ this.x = attribute.getX( index );
+ this.y = attribute.getY( index );
+ this.z = attribute.getZ( index );
+
+ return this;
+
+ }
+
+ random() {
+
+ this.x = Math.random();
+ this.y = Math.random();
+ this.z = Math.random();
+
+ return this;
+
+ }
+
+}
+
+const _vector = /*@__PURE__*/ new Vector3();
+const _quaternion = /*@__PURE__*/ new Quaternion();
+
+class Box3 {
+
+ constructor( min, max ) {
+
+ Object.defineProperty( this, 'isBox3', { value: true } );
+
+ this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );
+ this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );
+
+ }
+
+ set( min, max ) {
+
+ this.min.copy( min );
+ this.max.copy( max );
+
+ return this;
+
+ }
+
+ setFromArray( array ) {
+
+ let minX = + Infinity;
+ let minY = + Infinity;
+ let minZ = + Infinity;
+
+ let maxX = - Infinity;
+ let maxY = - Infinity;
+ let maxZ = - Infinity;
+
+ for ( let i = 0, l = array.length; i < l; i += 3 ) {
+
+ const x = array[ i ];
+ const y = array[ i + 1 ];
+ const z = array[ i + 2 ];
+
+ if ( x < minX ) minX = x;
+ if ( y < minY ) minY = y;
+ if ( z < minZ ) minZ = z;
+
+ if ( x > maxX ) maxX = x;
+ if ( y > maxY ) maxY = y;
+ if ( z > maxZ ) maxZ = z;
+
+ }
+
+ this.min.set( minX, minY, minZ );
+ this.max.set( maxX, maxY, maxZ );
+
+ return this;
+
+ }
+
+ setFromBufferAttribute( attribute ) {
+
+ let minX = + Infinity;
+ let minY = + Infinity;
+ let minZ = + Infinity;
+
+ let maxX = - Infinity;
+ let maxY = - Infinity;
+ let maxZ = - Infinity;
+
+ for ( let i = 0, l = attribute.count; i < l; i ++ ) {
+
+ const x = attribute.getX( i );
+ const y = attribute.getY( i );
+ const z = attribute.getZ( i );
+
+ if ( x < minX ) minX = x;
+ if ( y < minY ) minY = y;
+ if ( z < minZ ) minZ = z;
+
+ if ( x > maxX ) maxX = x;
+ if ( y > maxY ) maxY = y;
+ if ( z > maxZ ) maxZ = z;
+
+ }
+
+ this.min.set( minX, minY, minZ );
+ this.max.set( maxX, maxY, maxZ );
+
+ return this;
+
+ }
+
+ setFromPoints( points ) {
+
+ this.makeEmpty();
+
+ for ( let i = 0, il = points.length; i < il; i ++ ) {
+
+ this.expandByPoint( points[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ setFromCenterAndSize( center, size ) {
+
+ const halfSize = _vector$1.copy( size ).multiplyScalar( 0.5 );
+
+ this.min.copy( center ).sub( halfSize );
+ this.max.copy( center ).add( halfSize );
+
+ return this;
+
+ }
+
+ setFromObject( object ) {
+
+ this.makeEmpty();
+
+ return this.expandByObject( object );
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( box ) {
+
+ this.min.copy( box.min );
+ this.max.copy( box.max );
+
+ return this;
+
+ }
+
+ makeEmpty() {
+
+ this.min.x = this.min.y = this.min.z = + Infinity;
+ this.max.x = this.max.y = this.max.z = - Infinity;
+
+ return this;
+
+ }
+
+ isEmpty() {
+
+ // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes
+
+ return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );
+
+ }
+
+ getCenter( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getCenter() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );
+
+ }
+
+ getSize( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getSize() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );
+
+ }
+
+ expandByPoint( point ) {
+
+ this.min.min( point );
+ this.max.max( point );
+
+ return this;
+
+ }
+
+ expandByVector( vector ) {
+
+ this.min.sub( vector );
+ this.max.add( vector );
+
+ return this;
+
+ }
+
+ expandByScalar( scalar ) {
+
+ this.min.addScalar( - scalar );
+ this.max.addScalar( scalar );
+
+ return this;
+
+ }
+
+ expandByObject( object ) {
+
+ // Computes the world-axis-aligned bounding box of an object (including its children),
+ // accounting for both the object's, and children's, world transforms
+
+ object.updateWorldMatrix( false, false );
+
+ const geometry = object.geometry;
+
+ if ( geometry !== undefined ) {
+
+ if ( geometry.boundingBox === null ) {
+
+ geometry.computeBoundingBox();
+
+ }
+
+ _box.copy( geometry.boundingBox );
+ _box.applyMatrix4( object.matrixWorld );
+
+ this.union( _box );
+
+ }
+
+ const children = object.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ this.expandByObject( children[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ containsPoint( point ) {
+
+ return point.x < this.min.x || point.x > this.max.x ||
+ point.y < this.min.y || point.y > this.max.y ||
+ point.z < this.min.z || point.z > this.max.z ? false : true;
+
+ }
+
+ containsBox( box ) {
+
+ return this.min.x <= box.min.x && box.max.x <= this.max.x &&
+ this.min.y <= box.min.y && box.max.y <= this.max.y &&
+ this.min.z <= box.min.z && box.max.z <= this.max.z;
+
+ }
+
+ getParameter( point, target ) {
+
+ // This can potentially have a divide by zero if the box
+ // has a size dimension of 0.
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .getParameter() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.set(
+ ( point.x - this.min.x ) / ( this.max.x - this.min.x ),
+ ( point.y - this.min.y ) / ( this.max.y - this.min.y ),
+ ( point.z - this.min.z ) / ( this.max.z - this.min.z )
+ );
+
+ }
+
+ intersectsBox( box ) {
+
+ // using 6 splitting planes to rule out intersections.
+ return box.max.x < this.min.x || box.min.x > this.max.x ||
+ box.max.y < this.min.y || box.min.y > this.max.y ||
+ box.max.z < this.min.z || box.min.z > this.max.z ? false : true;
+
+ }
+
+ intersectsSphere( sphere ) {
+
+ // Find the point on the AABB closest to the sphere center.
+ this.clampPoint( sphere.center, _vector$1 );
+
+ // If that point is inside the sphere, the AABB and sphere intersect.
+ return _vector$1.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
+
+ }
+
+ intersectsPlane( plane ) {
+
+ // We compute the minimum and maximum dot product values. If those values
+ // are on the same side (back or front) of the plane, then there is no intersection.
+
+ let min, max;
+
+ if ( plane.normal.x > 0 ) {
+
+ min = plane.normal.x * this.min.x;
+ max = plane.normal.x * this.max.x;
+
+ } else {
+
+ min = plane.normal.x * this.max.x;
+ max = plane.normal.x * this.min.x;
+
+ }
+
+ if ( plane.normal.y > 0 ) {
+
+ min += plane.normal.y * this.min.y;
+ max += plane.normal.y * this.max.y;
+
+ } else {
+
+ min += plane.normal.y * this.max.y;
+ max += plane.normal.y * this.min.y;
+
+ }
+
+ if ( plane.normal.z > 0 ) {
+
+ min += plane.normal.z * this.min.z;
+ max += plane.normal.z * this.max.z;
+
+ } else {
+
+ min += plane.normal.z * this.max.z;
+ max += plane.normal.z * this.min.z;
+
+ }
+
+ return ( min <= - plane.constant && max >= - plane.constant );
+
+ }
+
+ intersectsTriangle( triangle ) {
+
+ if ( this.isEmpty() ) {
+
+ return false;
+
+ }
+
+ // compute box center and extents
+ this.getCenter( _center );
+ _extents.subVectors( this.max, _center );
+
+ // translate triangle to aabb origin
+ _v0.subVectors( triangle.a, _center );
+ _v1.subVectors( triangle.b, _center );
+ _v2.subVectors( triangle.c, _center );
+
+ // compute edge vectors for triangle
+ _f0.subVectors( _v1, _v0 );
+ _f1.subVectors( _v2, _v1 );
+ _f2.subVectors( _v0, _v2 );
+
+ // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb
+ // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation
+ // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)
+ let axes = [
+ 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y,
+ _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x,
+ - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0
+ ];
+ if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {
+
+ return false;
+
+ }
+
+ // test 3 face normals from the aabb
+ axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];
+ if ( ! satForAxes( axes, _v0, _v1, _v2, _extents ) ) {
+
+ return false;
+
+ }
+
+ // finally testing the face normal of the triangle
+ // use already existing triangle edge vectors here
+ _triangleNormal.crossVectors( _f0, _f1 );
+ axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ];
+
+ return satForAxes( axes, _v0, _v1, _v2, _extents );
+
+ }
+
+ clampPoint( point, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Box3: .clampPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( point ).clamp( this.min, this.max );
+
+ }
+
+ distanceToPoint( point ) {
+
+ const clampedPoint = _vector$1.copy( point ).clamp( this.min, this.max );
+
+ return clampedPoint.sub( point ).length();
+
+ }
+
+ getBoundingSphere( target ) {
+
+ if ( target === undefined ) {
+
+ console.error( 'THREE.Box3: .getBoundingSphere() target is now required' );
+ //target = new Sphere(); // removed to avoid cyclic dependency
+
+ }
+
+ this.getCenter( target.center );
+
+ target.radius = this.getSize( _vector$1 ).length() * 0.5;
+
+ return target;
+
+ }
+
+ intersect( box ) {
+
+ this.min.max( box.min );
+ this.max.min( box.max );
+
+ // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.
+ if ( this.isEmpty() ) this.makeEmpty();
+
+ return this;
+
+ }
+
+ union( box ) {
+
+ this.min.min( box.min );
+ this.max.max( box.max );
+
+ return this;
+
+ }
+
+ applyMatrix4( matrix ) {
+
+ // transform of empty box is an empty box.
+ if ( this.isEmpty() ) return this;
+
+ // NOTE: I am using a binary pattern to specify all 2^3 combinations below
+ _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
+ _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
+ _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
+ _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
+ _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
+ _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
+ _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
+ _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111
+
+ this.setFromPoints( _points );
+
+ return this;
+
+ }
+
+ translate( offset ) {
+
+ this.min.add( offset );
+ this.max.add( offset );
+
+ return this;
+
+ }
+
+ equals( box ) {
+
+ return box.min.equals( this.min ) && box.max.equals( this.max );
+
+ }
+
+}
+
+function satForAxes( axes, v0, v1, v2, extents ) {
+
+ for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) {
+
+ _testAxis.fromArray( axes, i );
+ // project the aabb onto the seperating axis
+ const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z );
+ // project all 3 vertices of the triangle onto the seperating axis
+ const p0 = v0.dot( _testAxis );
+ const p1 = v1.dot( _testAxis );
+ const p2 = v2.dot( _testAxis );
+ // actual test, basically see if either of the most extreme of the triangle points intersects r
+ if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {
+
+ // points of the projected triangle are outside the projected half-length of the aabb
+ // the axis is seperating and we can exit
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+}
+
+const _points = [
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3(),
+ /*@__PURE__*/ new Vector3()
+];
+
+const _vector$1 = /*@__PURE__*/ new Vector3();
+
+const _box = /*@__PURE__*/ new Box3();
+
+// triangle centered vertices
+
+const _v0 = /*@__PURE__*/ new Vector3();
+const _v1 = /*@__PURE__*/ new Vector3();
+const _v2 = /*@__PURE__*/ new Vector3();
+
+// triangle edge vectors
+
+const _f0 = /*@__PURE__*/ new Vector3();
+const _f1 = /*@__PURE__*/ new Vector3();
+const _f2 = /*@__PURE__*/ new Vector3();
+
+const _center = /*@__PURE__*/ new Vector3();
+const _extents = /*@__PURE__*/ new Vector3();
+const _triangleNormal = /*@__PURE__*/ new Vector3();
+const _testAxis = /*@__PURE__*/ new Vector3();
+
+const _box$1 = /*@__PURE__*/ new Box3();
+
+class Sphere {
+
+ constructor( center, radius ) {
+
+ this.center = ( center !== undefined ) ? center : new Vector3();
+ this.radius = ( radius !== undefined ) ? radius : - 1;
+
+ }
+
+ set( center, radius ) {
+
+ this.center.copy( center );
+ this.radius = radius;
+
+ return this;
+
+ }
+
+ setFromPoints( points, optionalCenter ) {
+
+ const center = this.center;
+
+ if ( optionalCenter !== undefined ) {
+
+ center.copy( optionalCenter );
+
+ } else {
+
+ _box$1.setFromPoints( points ).getCenter( center );
+
+ }
+
+ let maxRadiusSq = 0;
+
+ for ( let i = 0, il = points.length; i < il; i ++ ) {
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );
+
+ }
+
+ this.radius = Math.sqrt( maxRadiusSq );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( sphere ) {
+
+ this.center.copy( sphere.center );
+ this.radius = sphere.radius;
+
+ return this;
+
+ }
+
+ isEmpty() {
+
+ return ( this.radius < 0 );
+
+ }
+
+ makeEmpty() {
+
+ this.center.set( 0, 0, 0 );
+ this.radius = - 1;
+
+ return this;
+
+ }
+
+ containsPoint( point ) {
+
+ return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );
+
+ }
+
+ distanceToPoint( point ) {
+
+ return ( point.distanceTo( this.center ) - this.radius );
+
+ }
+
+ intersectsSphere( sphere ) {
+
+ const radiusSum = this.radius + sphere.radius;
+
+ return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );
+
+ }
+
+ intersectsBox( box ) {
+
+ return box.intersectsSphere( this );
+
+ }
+
+ intersectsPlane( plane ) {
+
+ return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
+
+ }
+
+ clampPoint( point, target ) {
+
+ const deltaLengthSq = this.center.distanceToSquared( point );
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Sphere: .clampPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ target.copy( point );
+
+ if ( deltaLengthSq > ( this.radius * this.radius ) ) {
+
+ target.sub( this.center ).normalize();
+ target.multiplyScalar( this.radius ).add( this.center );
+
+ }
+
+ return target;
+
+ }
+
+ getBoundingBox( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );
+ target = new Box3();
+
+ }
+
+ if ( this.isEmpty() ) {
+
+ // Empty sphere produces empty bounding box
+ target.makeEmpty();
+ return target;
+
+ }
+
+ target.set( this.center, this.center );
+ target.expandByScalar( this.radius );
+
+ return target;
+
+ }
+
+ applyMatrix4( matrix ) {
+
+ this.center.applyMatrix4( matrix );
+ this.radius = this.radius * matrix.getMaxScaleOnAxis();
+
+ return this;
+
+ }
+
+ translate( offset ) {
+
+ this.center.add( offset );
+
+ return this;
+
+ }
+
+ equals( sphere ) {
+
+ return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );
+
+ }
+
+}
+
+const _vector$2 = /*@__PURE__*/ new Vector3();
+const _segCenter = /*@__PURE__*/ new Vector3();
+const _segDir = /*@__PURE__*/ new Vector3();
+const _diff = /*@__PURE__*/ new Vector3();
+
+const _edge1 = /*@__PURE__*/ new Vector3();
+const _edge2 = /*@__PURE__*/ new Vector3();
+const _normal = /*@__PURE__*/ new Vector3();
+
+class Ray {
+
+ constructor( origin, direction ) {
+
+ this.origin = ( origin !== undefined ) ? origin : new Vector3();
+ this.direction = ( direction !== undefined ) ? direction : new Vector3( 0, 0, - 1 );
+
+ }
+
+ set( origin, direction ) {
+
+ this.origin.copy( origin );
+ this.direction.copy( direction );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( ray ) {
+
+ this.origin.copy( ray.origin );
+ this.direction.copy( ray.direction );
+
+ return this;
+
+ }
+
+ at( t, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Ray: .at() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );
+
+ }
+
+ lookAt( v ) {
+
+ this.direction.copy( v ).sub( this.origin ).normalize();
+
+ return this;
+
+ }
+
+ recast( t ) {
+
+ this.origin.copy( this.at( t, _vector$2 ) );
+
+ return this;
+
+ }
+
+ closestPointToPoint( point, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ target.subVectors( point, this.origin );
+
+ const directionDistance = target.dot( this.direction );
+
+ if ( directionDistance < 0 ) {
+
+ return target.copy( this.origin );
+
+ }
+
+ return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ }
+
+ distanceToPoint( point ) {
+
+ return Math.sqrt( this.distanceSqToPoint( point ) );
+
+ }
+
+ distanceSqToPoint( point ) {
+
+ const directionDistance = _vector$2.subVectors( point, this.origin ).dot( this.direction );
+
+ // point behind the ray
+
+ if ( directionDistance < 0 ) {
+
+ return this.origin.distanceToSquared( point );
+
+ }
+
+ _vector$2.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );
+
+ return _vector$2.distanceToSquared( point );
+
+ }
+
+ distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {
+
+ // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h
+ // It returns the min distance between the ray and the segment
+ // defined by v0 and v1
+ // It can also set two optional targets :
+ // - The closest point on the ray
+ // - The closest point on the segment
+
+ _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );
+ _segDir.copy( v1 ).sub( v0 ).normalize();
+ _diff.copy( this.origin ).sub( _segCenter );
+
+ const segExtent = v0.distanceTo( v1 ) * 0.5;
+ const a01 = - this.direction.dot( _segDir );
+ const b0 = _diff.dot( this.direction );
+ const b1 = - _diff.dot( _segDir );
+ const c = _diff.lengthSq();
+ const det = Math.abs( 1 - a01 * a01 );
+ let s0, s1, sqrDist, extDet;
+
+ if ( det > 0 ) {
+
+ // The ray and segment are not parallel.
+
+ s0 = a01 * b1 - b0;
+ s1 = a01 * b0 - b1;
+ extDet = segExtent * det;
+
+ if ( s0 >= 0 ) {
+
+ if ( s1 >= - extDet ) {
+
+ if ( s1 <= extDet ) {
+
+ // region 0
+ // Minimum at interior points of ray and segment.
+
+ const invDet = 1 / det;
+ s0 *= invDet;
+ s1 *= invDet;
+ sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 1
+
+ s1 = segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ // region 5
+
+ s1 = - segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ } else {
+
+ if ( s1 <= - extDet ) {
+
+ // region 4
+
+ s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ } else if ( s1 <= extDet ) {
+
+ // region 3
+
+ s0 = 0;
+ s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = s1 * ( s1 + 2 * b1 ) + c;
+
+ } else {
+
+ // region 2
+
+ s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );
+ s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ }
+
+ } else {
+
+ // Ray and segment are parallel.
+
+ s1 = ( a01 > 0 ) ? - segExtent : segExtent;
+ s0 = Math.max( 0, - ( a01 * s1 + b0 ) );
+ sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;
+
+ }
+
+ if ( optionalPointOnRay ) {
+
+ optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );
+
+ }
+
+ if ( optionalPointOnSegment ) {
+
+ optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter );
+
+ }
+
+ return sqrDist;
+
+ }
+
+ intersectSphere( sphere, target ) {
+
+ _vector$2.subVectors( sphere.center, this.origin );
+ const tca = _vector$2.dot( this.direction );
+ const d2 = _vector$2.dot( _vector$2 ) - tca * tca;
+ const radius2 = sphere.radius * sphere.radius;
+
+ if ( d2 > radius2 ) return null;
+
+ const thc = Math.sqrt( radius2 - d2 );
+
+ // t0 = first intersect point - entrance on front of sphere
+ const t0 = tca - thc;
+
+ // t1 = second intersect point - exit point on back of sphere
+ const t1 = tca + thc;
+
+ // test to see if both t0 and t1 are behind the ray - if so, return null
+ if ( t0 < 0 && t1 < 0 ) return null;
+
+ // test to see if t0 is behind the ray:
+ // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,
+ // in order to always return an intersect point that is in front of the ray.
+ if ( t0 < 0 ) return this.at( t1, target );
+
+ // else t0 is in front of the ray, so return the first collision point scaled by t0
+ return this.at( t0, target );
+
+ }
+
+ intersectsSphere( sphere ) {
+
+ return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius );
+
+ }
+
+ distanceToPlane( plane ) {
+
+ const denominator = plane.normal.dot( this.direction );
+
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( plane.distanceToPoint( this.origin ) === 0 ) {
+
+ return 0;
+
+ }
+
+ // Null is preferable to undefined since undefined means.... it is undefined
+
+ return null;
+
+ }
+
+ const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;
+
+ // Return if the ray never intersects the plane
+
+ return t >= 0 ? t : null;
+
+ }
+
+ intersectPlane( plane, target ) {
+
+ const t = this.distanceToPlane( plane );
+
+ if ( t === null ) {
+
+ return null;
+
+ }
+
+ return this.at( t, target );
+
+ }
+
+ intersectsPlane( plane ) {
+
+ // check if the ray lies on the plane first
+
+ const distToPoint = plane.distanceToPoint( this.origin );
+
+ if ( distToPoint === 0 ) {
+
+ return true;
+
+ }
+
+ const denominator = plane.normal.dot( this.direction );
+
+ if ( denominator * distToPoint < 0 ) {
+
+ return true;
+
+ }
+
+ // ray origin is behind the plane (and is pointing behind it)
+
+ return false;
+
+ }
+
+ intersectBox( box, target ) {
+
+ let tmin, tmax, tymin, tymax, tzmin, tzmax;
+
+ const invdirx = 1 / this.direction.x,
+ invdiry = 1 / this.direction.y,
+ invdirz = 1 / this.direction.z;
+
+ const origin = this.origin;
+
+ if ( invdirx >= 0 ) {
+
+ tmin = ( box.min.x - origin.x ) * invdirx;
+ tmax = ( box.max.x - origin.x ) * invdirx;
+
+ } else {
+
+ tmin = ( box.max.x - origin.x ) * invdirx;
+ tmax = ( box.min.x - origin.x ) * invdirx;
+
+ }
+
+ if ( invdiry >= 0 ) {
+
+ tymin = ( box.min.y - origin.y ) * invdiry;
+ tymax = ( box.max.y - origin.y ) * invdiry;
+
+ } else {
+
+ tymin = ( box.max.y - origin.y ) * invdiry;
+ tymax = ( box.min.y - origin.y ) * invdiry;
+
+ }
+
+ if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;
+
+ // These lines also handle the case where tmin or tmax is NaN
+ // (result of 0 * Infinity). x !== x returns true if x is NaN
+
+ if ( tymin > tmin || tmin !== tmin ) tmin = tymin;
+
+ if ( tymax < tmax || tmax !== tmax ) tmax = tymax;
+
+ if ( invdirz >= 0 ) {
+
+ tzmin = ( box.min.z - origin.z ) * invdirz;
+ tzmax = ( box.max.z - origin.z ) * invdirz;
+
+ } else {
+
+ tzmin = ( box.max.z - origin.z ) * invdirz;
+ tzmax = ( box.min.z - origin.z ) * invdirz;
+
+ }
+
+ if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;
+
+ if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
+
+ if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
+
+ //return point closest to the ray (positive side)
+
+ if ( tmax < 0 ) return null;
+
+ return this.at( tmin >= 0 ? tmin : tmax, target );
+
+ }
+
+ intersectsBox( box ) {
+
+ return this.intersectBox( box, _vector$2 ) !== null;
+
+ }
+
+ intersectTriangle( a, b, c, backfaceCulling, target ) {
+
+ // Compute the offset origin, edges, and normal.
+
+ // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
+
+ _edge1.subVectors( b, a );
+ _edge2.subVectors( c, a );
+ _normal.crossVectors( _edge1, _edge2 );
+
+ // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,
+ // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by
+ // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))
+ // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))
+ // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)
+ let DdN = this.direction.dot( _normal );
+ let sign;
+
+ if ( DdN > 0 ) {
+
+ if ( backfaceCulling ) return null;
+ sign = 1;
+
+ } else if ( DdN < 0 ) {
+
+ sign = - 1;
+ DdN = - DdN;
+
+ } else {
+
+ return null;
+
+ }
+
+ _diff.subVectors( this.origin, a );
+ const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) );
+
+ // b1 < 0, no intersection
+ if ( DdQxE2 < 0 ) {
+
+ return null;
+
+ }
+
+ const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) );
+
+ // b2 < 0, no intersection
+ if ( DdE1xQ < 0 ) {
+
+ return null;
+
+ }
+
+ // b1+b2 > 1, no intersection
+ if ( DdQxE2 + DdE1xQ > DdN ) {
+
+ return null;
+
+ }
+
+ // Line intersects triangle, check if ray does.
+ const QdN = - sign * _diff.dot( _normal );
+
+ // t < 0, no intersection
+ if ( QdN < 0 ) {
+
+ return null;
+
+ }
+
+ // Ray intersects triangle.
+ return this.at( QdN / DdN, target );
+
+ }
+
+ applyMatrix4( matrix4 ) {
+
+ this.origin.applyMatrix4( matrix4 );
+ this.direction.transformDirection( matrix4 );
+
+ return this;
+
+ }
+
+ equals( ray ) {
+
+ return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );
+
+ }
+
+}
+
+class Matrix4 {
+
+ constructor() {
+
+ Object.defineProperty( this, 'isMatrix4', { value: true } );
+
+ this.elements = [
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ ];
+
+ if ( arguments.length > 0 ) {
+
+ console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
+
+ }
+
+ }
+
+ set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+ const te = this.elements;
+
+ te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
+ te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
+ te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
+ te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
+
+ return this;
+
+ }
+
+ identity() {
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new Matrix4().fromArray( this.elements );
+
+ }
+
+ copy( m ) {
+
+ const te = this.elements;
+ const me = m.elements;
+
+ te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
+ te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
+ te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
+ te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
+
+ return this;
+
+ }
+
+ copyPosition( m ) {
+
+ const te = this.elements, me = m.elements;
+
+ te[ 12 ] = me[ 12 ];
+ te[ 13 ] = me[ 13 ];
+ te[ 14 ] = me[ 14 ];
+
+ return this;
+
+ }
+
+ extractBasis( xAxis, yAxis, zAxis ) {
+
+ xAxis.setFromMatrixColumn( this, 0 );
+ yAxis.setFromMatrixColumn( this, 1 );
+ zAxis.setFromMatrixColumn( this, 2 );
+
+ return this;
+
+ }
+
+ makeBasis( xAxis, yAxis, zAxis ) {
+
+ this.set(
+ xAxis.x, yAxis.x, zAxis.x, 0,
+ xAxis.y, yAxis.y, zAxis.y, 0,
+ xAxis.z, yAxis.z, zAxis.z, 0,
+ 0, 0, 0, 1
+ );
+
+ return this;
+
+ }
+
+ extractRotation( m ) {
+
+ // this method does not support reflection matrices
+
+ const te = this.elements;
+ const me = m.elements;
+
+ const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length();
+ const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length();
+ const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length();
+
+ te[ 0 ] = me[ 0 ] * scaleX;
+ te[ 1 ] = me[ 1 ] * scaleX;
+ te[ 2 ] = me[ 2 ] * scaleX;
+ te[ 3 ] = 0;
+
+ te[ 4 ] = me[ 4 ] * scaleY;
+ te[ 5 ] = me[ 5 ] * scaleY;
+ te[ 6 ] = me[ 6 ] * scaleY;
+ te[ 7 ] = 0;
+
+ te[ 8 ] = me[ 8 ] * scaleZ;
+ te[ 9 ] = me[ 9 ] * scaleZ;
+ te[ 10 ] = me[ 10 ] * scaleZ;
+ te[ 11 ] = 0;
+
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ }
+
+ makeRotationFromEuler( euler ) {
+
+ if ( ! ( euler && euler.isEuler ) ) {
+
+ console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
+
+ }
+
+ const te = this.elements;
+
+ const x = euler.x, y = euler.y, z = euler.z;
+ const a = Math.cos( x ), b = Math.sin( x );
+ const c = Math.cos( y ), d = Math.sin( y );
+ const e = Math.cos( z ), f = Math.sin( z );
+
+ if ( euler.order === 'XYZ' ) {
+
+ const ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - c * f;
+ te[ 8 ] = d;
+
+ te[ 1 ] = af + be * d;
+ te[ 5 ] = ae - bf * d;
+ te[ 9 ] = - b * c;
+
+ te[ 2 ] = bf - ae * d;
+ te[ 6 ] = be + af * d;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YXZ' ) {
+
+ const ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce + df * b;
+ te[ 4 ] = de * b - cf;
+ te[ 8 ] = a * d;
+
+ te[ 1 ] = a * f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b;
+
+ te[ 2 ] = cf * b - de;
+ te[ 6 ] = df + ce * b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZXY' ) {
+
+ const ce = c * e, cf = c * f, de = d * e, df = d * f;
+
+ te[ 0 ] = ce - df * b;
+ te[ 4 ] = - a * f;
+ te[ 8 ] = de + cf * b;
+
+ te[ 1 ] = cf + de * b;
+ te[ 5 ] = a * e;
+ te[ 9 ] = df - ce * b;
+
+ te[ 2 ] = - a * d;
+ te[ 6 ] = b;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'ZYX' ) {
+
+ const ae = a * e, af = a * f, be = b * e, bf = b * f;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = be * d - af;
+ te[ 8 ] = ae * d + bf;
+
+ te[ 1 ] = c * f;
+ te[ 5 ] = bf * d + ae;
+ te[ 9 ] = af * d - be;
+
+ te[ 2 ] = - d;
+ te[ 6 ] = b * c;
+ te[ 10 ] = a * c;
+
+ } else if ( euler.order === 'YZX' ) {
+
+ const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = bd - ac * f;
+ te[ 8 ] = bc * f + ad;
+
+ te[ 1 ] = f;
+ te[ 5 ] = a * e;
+ te[ 9 ] = - b * e;
+
+ te[ 2 ] = - d * e;
+ te[ 6 ] = ad * f + bc;
+ te[ 10 ] = ac - bd * f;
+
+ } else if ( euler.order === 'XZY' ) {
+
+ const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
+
+ te[ 0 ] = c * e;
+ te[ 4 ] = - f;
+ te[ 8 ] = d * e;
+
+ te[ 1 ] = ac * f + bd;
+ te[ 5 ] = a * e;
+ te[ 9 ] = ad * f - bc;
+
+ te[ 2 ] = bc * f - ad;
+ te[ 6 ] = b * e;
+ te[ 10 ] = bd * f + ac;
+
+ }
+
+ // bottom row
+ te[ 3 ] = 0;
+ te[ 7 ] = 0;
+ te[ 11 ] = 0;
+
+ // last column
+ te[ 12 ] = 0;
+ te[ 13 ] = 0;
+ te[ 14 ] = 0;
+ te[ 15 ] = 1;
+
+ return this;
+
+ }
+
+ makeRotationFromQuaternion( q ) {
+
+ return this.compose( _zero, q, _one );
+
+ }
+
+ lookAt( eye, target, up ) {
+
+ const te = this.elements;
+
+ _z.subVectors( eye, target );
+
+ if ( _z.lengthSq() === 0 ) {
+
+ // eye and target are in the same position
+
+ _z.z = 1;
+
+ }
+
+ _z.normalize();
+ _x.crossVectors( up, _z );
+
+ if ( _x.lengthSq() === 0 ) {
+
+ // up and z are parallel
+
+ if ( Math.abs( up.z ) === 1 ) {
+
+ _z.x += 0.0001;
+
+ } else {
+
+ _z.z += 0.0001;
+
+ }
+
+ _z.normalize();
+ _x.crossVectors( up, _z );
+
+ }
+
+ _x.normalize();
+ _y.crossVectors( _z, _x );
+
+ te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x;
+ te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y;
+ te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z;
+
+ return this;
+
+ }
+
+ multiply( m, n ) {
+
+ if ( n !== undefined ) {
+
+ console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
+ return this.multiplyMatrices( m, n );
+
+ }
+
+ return this.multiplyMatrices( this, m );
+
+ }
+
+ premultiply( m ) {
+
+ return this.multiplyMatrices( m, this );
+
+ }
+
+ multiplyMatrices( a, b ) {
+
+ const ae = a.elements;
+ const be = b.elements;
+ const te = this.elements;
+
+ const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
+ const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
+ const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
+ const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
+
+ const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
+ const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
+ const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
+ const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
+
+ te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
+ te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
+ te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
+ te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
+
+ te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
+ te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
+ te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
+ te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
+
+ te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
+ te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
+ te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
+ te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
+
+ te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
+ te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
+ te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
+ te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
+
+ return this;
+
+ }
+
+ multiplyScalar( s ) {
+
+ const te = this.elements;
+
+ te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
+ te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
+ te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
+ te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
+
+ return this;
+
+ }
+
+ determinant() {
+
+ const te = this.elements;
+
+ const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
+ const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
+ const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
+ const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
+
+ //TODO: make this more efficient
+ //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )
+
+ return (
+ n41 * (
+ + n14 * n23 * n32
+ - n13 * n24 * n32
+ - n14 * n22 * n33
+ + n12 * n24 * n33
+ + n13 * n22 * n34
+ - n12 * n23 * n34
+ ) +
+ n42 * (
+ + n11 * n23 * n34
+ - n11 * n24 * n33
+ + n14 * n21 * n33
+ - n13 * n21 * n34
+ + n13 * n24 * n31
+ - n14 * n23 * n31
+ ) +
+ n43 * (
+ + n11 * n24 * n32
+ - n11 * n22 * n34
+ - n14 * n21 * n32
+ + n12 * n21 * n34
+ + n14 * n22 * n31
+ - n12 * n24 * n31
+ ) +
+ n44 * (
+ - n13 * n22 * n31
+ - n11 * n23 * n32
+ + n11 * n22 * n33
+ + n13 * n21 * n32
+ - n12 * n21 * n33
+ + n12 * n23 * n31
+ )
+
+ );
+
+ }
+
+ transpose() {
+
+ const te = this.elements;
+ let tmp;
+
+ tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
+ tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
+ tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
+
+ tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
+ tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
+ tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
+
+ return this;
+
+ }
+
+ setPosition( x, y, z ) {
+
+ const te = this.elements;
+
+ if ( x.isVector3 ) {
+
+ te[ 12 ] = x.x;
+ te[ 13 ] = x.y;
+ te[ 14 ] = x.z;
+
+ } else {
+
+ te[ 12 ] = x;
+ te[ 13 ] = y;
+ te[ 14 ] = z;
+
+ }
+
+ return this;
+
+ }
+
+ getInverse( m, throwOnDegenerate ) {
+
+ if ( throwOnDegenerate !== undefined ) {
+
+ console.warn( "THREE.Matrix4: .getInverse() can no longer be configured to throw on degenerate." );
+
+ }
+
+ // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
+ const te = this.elements,
+ me = m.elements,
+
+ n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
+ n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
+ n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
+ n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
+
+ t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
+ t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
+ t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
+ t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+
+ const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
+
+ if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+
+ const detInv = 1 / det;
+
+ te[ 0 ] = t11 * detInv;
+ te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
+ te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
+ te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
+
+ te[ 4 ] = t12 * detInv;
+ te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
+ te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
+ te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
+
+ te[ 8 ] = t13 * detInv;
+ te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
+ te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
+ te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
+
+ te[ 12 ] = t14 * detInv;
+ te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
+ te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
+ te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
+
+ return this;
+
+ }
+
+ scale( v ) {
+
+ const te = this.elements;
+ const x = v.x, y = v.y, z = v.z;
+
+ te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
+ te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
+ te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
+ te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
+
+ return this;
+
+ }
+
+ getMaxScaleOnAxis() {
+
+ const te = this.elements;
+
+ const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
+ const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
+ const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
+
+ return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
+
+ }
+
+ makeTranslation( x, y, z ) {
+
+ this.set(
+
+ 1, 0, 0, x,
+ 0, 1, 0, y,
+ 0, 0, 1, z,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeRotationX( theta ) {
+
+ const c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ 1, 0, 0, 0,
+ 0, c, - s, 0,
+ 0, s, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeRotationY( theta ) {
+
+ const c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, 0, s, 0,
+ 0, 1, 0, 0,
+ - s, 0, c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeRotationZ( theta ) {
+
+ const c = Math.cos( theta ), s = Math.sin( theta );
+
+ this.set(
+
+ c, - s, 0, 0,
+ s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeRotationAxis( axis, angle ) {
+
+ // Based on http://www.gamedev.net/reference/articles/article1199.asp
+
+ const c = Math.cos( angle );
+ const s = Math.sin( angle );
+ const t = 1 - c;
+ const x = axis.x, y = axis.y, z = axis.z;
+ const tx = t * x, ty = t * y;
+
+ this.set(
+
+ tx * x + c, tx * y - s * z, tx * z + s * y, 0,
+ tx * y + s * z, ty * y + c, ty * z - s * x, 0,
+ tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeScale( x, y, z ) {
+
+ this.set(
+
+ x, 0, 0, 0,
+ 0, y, 0, 0,
+ 0, 0, z, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ makeShear( x, y, z ) {
+
+ this.set(
+
+ 1, y, z, 0,
+ x, 1, z, 0,
+ x, y, 1, 0,
+ 0, 0, 0, 1
+
+ );
+
+ return this;
+
+ }
+
+ compose( position, quaternion, scale ) {
+
+ const te = this.elements;
+
+ const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
+ const x2 = x + x, y2 = y + y, z2 = z + z;
+ const xx = x * x2, xy = x * y2, xz = x * z2;
+ const yy = y * y2, yz = y * z2, zz = z * z2;
+ const wx = w * x2, wy = w * y2, wz = w * z2;
+
+ const sx = scale.x, sy = scale.y, sz = scale.z;
+
+ te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
+ te[ 1 ] = ( xy + wz ) * sx;
+ te[ 2 ] = ( xz - wy ) * sx;
+ te[ 3 ] = 0;
+
+ te[ 4 ] = ( xy - wz ) * sy;
+ te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
+ te[ 6 ] = ( yz + wx ) * sy;
+ te[ 7 ] = 0;
+
+ te[ 8 ] = ( xz + wy ) * sz;
+ te[ 9 ] = ( yz - wx ) * sz;
+ te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
+ te[ 11 ] = 0;
+
+ te[ 12 ] = position.x;
+ te[ 13 ] = position.y;
+ te[ 14 ] = position.z;
+ te[ 15 ] = 1;
+
+ return this;
+
+ }
+
+ decompose( position, quaternion, scale ) {
+
+ const te = this.elements;
+
+ let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
+ const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
+ const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
+
+ // if determine is negative, we need to invert one scale
+ const det = this.determinant();
+ if ( det < 0 ) sx = - sx;
+
+ position.x = te[ 12 ];
+ position.y = te[ 13 ];
+ position.z = te[ 14 ];
+
+ // scale the rotation part
+ _m1.copy( this );
+
+ const invSX = 1 / sx;
+ const invSY = 1 / sy;
+ const invSZ = 1 / sz;
+
+ _m1.elements[ 0 ] *= invSX;
+ _m1.elements[ 1 ] *= invSX;
+ _m1.elements[ 2 ] *= invSX;
+
+ _m1.elements[ 4 ] *= invSY;
+ _m1.elements[ 5 ] *= invSY;
+ _m1.elements[ 6 ] *= invSY;
+
+ _m1.elements[ 8 ] *= invSZ;
+ _m1.elements[ 9 ] *= invSZ;
+ _m1.elements[ 10 ] *= invSZ;
+
+ quaternion.setFromRotationMatrix( _m1 );
+
+ scale.x = sx;
+ scale.y = sy;
+ scale.z = sz;
+
+ return this;
+
+ }
+
+ makePerspective( left, right, top, bottom, near, far ) {
+
+ if ( far === undefined ) {
+
+ console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
+
+ }
+
+ const te = this.elements;
+ const x = 2 * near / ( right - left );
+ const y = 2 * near / ( top - bottom );
+
+ const a = ( right + left ) / ( right - left );
+ const b = ( top + bottom ) / ( top - bottom );
+ const c = - ( far + near ) / ( far - near );
+ const d = - 2 * far * near / ( far - near );
+
+ te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
+ te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
+
+ return this;
+
+ }
+
+ makeOrthographic( left, right, top, bottom, near, far ) {
+
+ const te = this.elements;
+ const w = 1.0 / ( right - left );
+ const h = 1.0 / ( top - bottom );
+ const p = 1.0 / ( far - near );
+
+ const x = ( right + left ) * w;
+ const y = ( top + bottom ) * h;
+ const z = ( far + near ) * p;
+
+ te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
+ te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
+ te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
+ te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
+
+ return this;
+
+ }
+
+ equals( matrix ) {
+
+ const te = this.elements;
+ const me = matrix.elements;
+
+ for ( let i = 0; i < 16; i ++ ) {
+
+ if ( te[ i ] !== me[ i ] ) return false;
+
+ }
+
+ return true;
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ for ( let i = 0; i < 16; i ++ ) {
+
+ this.elements[ i ] = array[ i + offset ];
+
+ }
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ const te = this.elements;
+
+ array[ offset ] = te[ 0 ];
+ array[ offset + 1 ] = te[ 1 ];
+ array[ offset + 2 ] = te[ 2 ];
+ array[ offset + 3 ] = te[ 3 ];
+
+ array[ offset + 4 ] = te[ 4 ];
+ array[ offset + 5 ] = te[ 5 ];
+ array[ offset + 6 ] = te[ 6 ];
+ array[ offset + 7 ] = te[ 7 ];
+
+ array[ offset + 8 ] = te[ 8 ];
+ array[ offset + 9 ] = te[ 9 ];
+ array[ offset + 10 ] = te[ 10 ];
+ array[ offset + 11 ] = te[ 11 ];
+
+ array[ offset + 12 ] = te[ 12 ];
+ array[ offset + 13 ] = te[ 13 ];
+ array[ offset + 14 ] = te[ 14 ];
+ array[ offset + 15 ] = te[ 15 ];
+
+ return array;
+
+ }
+
+}
+
+const _v1$1 = /*@__PURE__*/ new Vector3();
+const _m1 = /*@__PURE__*/ new Matrix4();
+const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 );
+const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 );
+const _x = /*@__PURE__*/ new Vector3();
+const _y = /*@__PURE__*/ new Vector3();
+const _z = /*@__PURE__*/ new Vector3();
+
+class Euler {
+
+ constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) {
+
+ Object.defineProperty( this, 'isEuler', { value: true } );
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order;
+
+ }
+
+ get x() {
+
+ return this._x;
+
+ }
+
+ set x( value ) {
+
+ this._x = value;
+ this._onChangeCallback();
+
+ }
+
+ get y() {
+
+ return this._y;
+
+ }
+
+ set y( value ) {
+
+ this._y = value;
+ this._onChangeCallback();
+
+ }
+
+ get z() {
+
+ return this._z;
+
+ }
+
+ set z( value ) {
+
+ this._z = value;
+ this._onChangeCallback();
+
+ }
+
+ get order() {
+
+ return this._order;
+
+ }
+
+ set order( value ) {
+
+ this._order = value;
+ this._onChangeCallback();
+
+ }
+
+ set( x, y, z, order ) {
+
+ this._x = x;
+ this._y = y;
+ this._z = z;
+ this._order = order || this._order;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor( this._x, this._y, this._z, this._order );
+
+ }
+
+ copy( euler ) {
+
+ this._x = euler._x;
+ this._y = euler._y;
+ this._z = euler._z;
+ this._order = euler._order;
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromRotationMatrix( m, order, update ) {
+
+ const clamp = MathUtils.clamp;
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ const te = m.elements;
+ const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
+ const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
+ const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
+
+ order = order || this._order;
+
+ switch ( order ) {
+
+ case 'XYZ':
+
+ this._y = Math.asin( clamp( m13, - 1, 1 ) );
+
+ if ( Math.abs( m13 ) < 0.9999999 ) {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._z = Math.atan2( - m12, m11 );
+
+ } else {
+
+ this._x = Math.atan2( m32, m22 );
+ this._z = 0;
+
+ }
+
+ break;
+
+ case 'YXZ':
+
+ this._x = Math.asin( - clamp( m23, - 1, 1 ) );
+
+ if ( Math.abs( m23 ) < 0.9999999 ) {
+
+ this._y = Math.atan2( m13, m33 );
+ this._z = Math.atan2( m21, m22 );
+
+ } else {
+
+ this._y = Math.atan2( - m31, m11 );
+ this._z = 0;
+
+ }
+
+ break;
+
+ case 'ZXY':
+
+ this._x = Math.asin( clamp( m32, - 1, 1 ) );
+
+ if ( Math.abs( m32 ) < 0.9999999 ) {
+
+ this._y = Math.atan2( - m31, m33 );
+ this._z = Math.atan2( - m12, m22 );
+
+ } else {
+
+ this._y = 0;
+ this._z = Math.atan2( m21, m11 );
+
+ }
+
+ break;
+
+ case 'ZYX':
+
+ this._y = Math.asin( - clamp( m31, - 1, 1 ) );
+
+ if ( Math.abs( m31 ) < 0.9999999 ) {
+
+ this._x = Math.atan2( m32, m33 );
+ this._z = Math.atan2( m21, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._z = Math.atan2( - m12, m22 );
+
+ }
+
+ break;
+
+ case 'YZX':
+
+ this._z = Math.asin( clamp( m21, - 1, 1 ) );
+
+ if ( Math.abs( m21 ) < 0.9999999 ) {
+
+ this._x = Math.atan2( - m23, m22 );
+ this._y = Math.atan2( - m31, m11 );
+
+ } else {
+
+ this._x = 0;
+ this._y = Math.atan2( m13, m33 );
+
+ }
+
+ break;
+
+ case 'XZY':
+
+ this._z = Math.asin( - clamp( m12, - 1, 1 ) );
+
+ if ( Math.abs( m12 ) < 0.9999999 ) {
+
+ this._x = Math.atan2( m32, m22 );
+ this._y = Math.atan2( m13, m11 );
+
+ } else {
+
+ this._x = Math.atan2( - m23, m33 );
+ this._y = 0;
+
+ }
+
+ break;
+
+ default:
+
+ console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order );
+
+ }
+
+ this._order = order;
+
+ if ( update !== false ) this._onChangeCallback();
+
+ return this;
+
+ }
+
+ setFromQuaternion( q, order, update ) {
+
+ _matrix.makeRotationFromQuaternion( q );
+
+ return this.setFromRotationMatrix( _matrix, order, update );
+
+ }
+
+ setFromVector3( v, order ) {
+
+ return this.set( v.x, v.y, v.z, order || this._order );
+
+ }
+
+ reorder( newOrder ) {
+
+ // WARNING: this discards revolution information -bhouston
+
+ _quaternion$1.setFromEuler( this );
+
+ return this.setFromQuaternion( _quaternion$1, newOrder );
+
+ }
+
+ equals( euler ) {
+
+ return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+ }
+
+ fromArray( array ) {
+
+ this._x = array[ 0 ];
+ this._y = array[ 1 ];
+ this._z = array[ 2 ];
+ if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+ this._onChangeCallback();
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this._x;
+ array[ offset + 1 ] = this._y;
+ array[ offset + 2 ] = this._z;
+ array[ offset + 3 ] = this._order;
+
+ return array;
+
+ }
+
+ toVector3( optionalResult ) {
+
+ if ( optionalResult ) {
+
+ return optionalResult.set( this._x, this._y, this._z );
+
+ } else {
+
+ return new Vector3( this._x, this._y, this._z );
+
+ }
+
+ }
+
+ _onChange( callback ) {
+
+ this._onChangeCallback = callback;
+
+ return this;
+
+ }
+
+ _onChangeCallback() {}
+
+}
+
+Euler.DefaultOrder = 'XYZ';
+Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+const _matrix = /*@__PURE__*/ new Matrix4();
+const _quaternion$1 = /*@__PURE__*/ new Quaternion();
+
+class Layers {
+
+ constructor() {
+
+ this.mask = 1 | 0;
+
+ }
+
+ set( channel ) {
+
+ this.mask = 1 << channel | 0;
+
+ }
+
+ enable( channel ) {
+
+ this.mask |= 1 << channel | 0;
+
+ }
+
+ enableAll() {
+
+ this.mask = 0xffffffff | 0;
+
+ }
+
+ toggle( channel ) {
+
+ this.mask ^= 1 << channel | 0;
+
+ }
+
+ disable( channel ) {
+
+ this.mask &= ~ ( 1 << channel | 0 );
+
+ }
+
+ disableAll() {
+
+ this.mask = 0;
+
+ }
+
+ test( layers ) {
+
+ return ( this.mask & layers.mask ) !== 0;
+
+ }
+
+}
+
+let _object3DId = 0;
+
+const _v1$2 = new Vector3();
+const _q1 = new Quaternion();
+const _m1$1 = new Matrix4();
+const _target = new Vector3();
+
+const _position = new Vector3();
+const _scale = new Vector3();
+const _quaternion$2 = new Quaternion();
+
+const _xAxis = new Vector3( 1, 0, 0 );
+const _yAxis = new Vector3( 0, 1, 0 );
+const _zAxis = new Vector3( 0, 0, 1 );
+
+const _addedEvent = { type: 'added' };
+const _removedEvent = { type: 'removed' };
+
+function Object3D() {
+
+ Object.defineProperty( this, 'id', { value: _object3DId ++ } );
+
+ this.uuid = MathUtils.generateUUID();
+
+ this.name = '';
+ this.type = 'Object3D';
+
+ this.parent = null;
+ this.children = [];
+
+ this.up = Object3D.DefaultUp.clone();
+
+ const position = new Vector3();
+ const rotation = new Euler();
+ const quaternion = new Quaternion();
+ const scale = new Vector3( 1, 1, 1 );
+
+ function onRotationChange() {
+
+ quaternion.setFromEuler( rotation, false );
+
+ }
+
+ function onQuaternionChange() {
+
+ rotation.setFromQuaternion( quaternion, undefined, false );
+
+ }
+
+ rotation._onChange( onRotationChange );
+ quaternion._onChange( onQuaternionChange );
+
+ Object.defineProperties( this, {
+ position: {
+ configurable: true,
+ enumerable: true,
+ value: position
+ },
+ rotation: {
+ configurable: true,
+ enumerable: true,
+ value: rotation
+ },
+ quaternion: {
+ configurable: true,
+ enumerable: true,
+ value: quaternion
+ },
+ scale: {
+ configurable: true,
+ enumerable: true,
+ value: scale
+ },
+ modelViewMatrix: {
+ value: new Matrix4()
+ },
+ normalMatrix: {
+ value: new Matrix3()
+ }
+ } );
+
+ this.matrix = new Matrix4();
+ this.matrixWorld = new Matrix4();
+
+ this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = false;
+
+ this.layers = new Layers();
+ this.visible = true;
+
+ this.castShadow = false;
+ this.receiveShadow = false;
+
+ this.frustumCulled = true;
+ this.renderOrder = 0;
+
+ this.userData = {};
+
+}
+
+Object3D.DefaultUp = new Vector3( 0, 1, 0 );
+Object3D.DefaultMatrixAutoUpdate = true;
+
+Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: Object3D,
+
+ isObject3D: true,
+
+ onBeforeRender: function () {},
+ onAfterRender: function () {},
+
+ applyMatrix4: function ( matrix ) {
+
+ if ( this.matrixAutoUpdate ) this.updateMatrix();
+
+ this.matrix.premultiply( matrix );
+
+ this.matrix.decompose( this.position, this.quaternion, this.scale );
+
+ },
+
+ applyQuaternion: function ( q ) {
+
+ this.quaternion.premultiply( q );
+
+ return this;
+
+ },
+
+ setRotationFromAxisAngle: function ( axis, angle ) {
+
+ // assumes axis is normalized
+
+ this.quaternion.setFromAxisAngle( axis, angle );
+
+ },
+
+ setRotationFromEuler: function ( euler ) {
+
+ this.quaternion.setFromEuler( euler, true );
+
+ },
+
+ setRotationFromMatrix: function ( m ) {
+
+ // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+ this.quaternion.setFromRotationMatrix( m );
+
+ },
+
+ setRotationFromQuaternion: function ( q ) {
+
+ // assumes q is normalized
+
+ this.quaternion.copy( q );
+
+ },
+
+ rotateOnAxis: function ( axis, angle ) {
+
+ // rotate object on axis in object space
+ // axis is assumed to be normalized
+
+ _q1.setFromAxisAngle( axis, angle );
+
+ this.quaternion.multiply( _q1 );
+
+ return this;
+
+ },
+
+ rotateOnWorldAxis: function ( axis, angle ) {
+
+ // rotate object on axis in world space
+ // axis is assumed to be normalized
+ // method assumes no rotated parent
+
+ _q1.setFromAxisAngle( axis, angle );
+
+ this.quaternion.premultiply( _q1 );
+
+ return this;
+
+ },
+
+ rotateX: function ( angle ) {
+
+ return this.rotateOnAxis( _xAxis, angle );
+
+ },
+
+ rotateY: function ( angle ) {
+
+ return this.rotateOnAxis( _yAxis, angle );
+
+ },
+
+ rotateZ: function ( angle ) {
+
+ return this.rotateOnAxis( _zAxis, angle );
+
+ },
+
+ translateOnAxis: function ( axis, distance ) {
+
+ // translate object by distance along axis in object space
+ // axis is assumed to be normalized
+
+ _v1$2.copy( axis ).applyQuaternion( this.quaternion );
+
+ this.position.add( _v1$2.multiplyScalar( distance ) );
+
+ return this;
+
+ },
+
+ translateX: function ( distance ) {
+
+ return this.translateOnAxis( _xAxis, distance );
+
+ },
+
+ translateY: function ( distance ) {
+
+ return this.translateOnAxis( _yAxis, distance );
+
+ },
+
+ translateZ: function ( distance ) {
+
+ return this.translateOnAxis( _zAxis, distance );
+
+ },
+
+ localToWorld: function ( vector ) {
+
+ return vector.applyMatrix4( this.matrixWorld );
+
+ },
+
+ worldToLocal: function ( vector ) {
+
+ return vector.applyMatrix4( _m1$1.getInverse( this.matrixWorld ) );
+
+ },
+
+ lookAt: function ( x, y, z ) {
+
+ // This method does not support objects having non-uniformly-scaled parent(s)
+
+ if ( x.isVector3 ) {
+
+ _target.copy( x );
+
+ } else {
+
+ _target.set( x, y, z );
+
+ }
+
+ const parent = this.parent;
+
+ this.updateWorldMatrix( true, false );
+
+ _position.setFromMatrixPosition( this.matrixWorld );
+
+ if ( this.isCamera || this.isLight ) {
+
+ _m1$1.lookAt( _position, _target, this.up );
+
+ } else {
+
+ _m1$1.lookAt( _target, _position, this.up );
+
+ }
+
+ this.quaternion.setFromRotationMatrix( _m1$1 );
+
+ if ( parent ) {
+
+ _m1$1.extractRotation( parent.matrixWorld );
+ _q1.setFromRotationMatrix( _m1$1 );
+ this.quaternion.premultiply( _q1.inverse() );
+
+ }
+
+ },
+
+ add: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( let i = 0; i < arguments.length; i ++ ) {
+
+ this.add( arguments[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ if ( object === this ) {
+
+ console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+ return this;
+
+ }
+
+ if ( ( object && object.isObject3D ) ) {
+
+ if ( object.parent !== null ) {
+
+ object.parent.remove( object );
+
+ }
+
+ object.parent = this;
+ this.children.push( object );
+
+ object.dispatchEvent( _addedEvent );
+
+ } else {
+
+ console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
+
+ }
+
+ return this;
+
+ },
+
+ remove: function ( object ) {
+
+ if ( arguments.length > 1 ) {
+
+ for ( let i = 0; i < arguments.length; i ++ ) {
+
+ this.remove( arguments[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ const index = this.children.indexOf( object );
+
+ if ( index !== - 1 ) {
+
+ object.parent = null;
+ this.children.splice( index, 1 );
+
+ object.dispatchEvent( _removedEvent );
+
+ }
+
+ return this;
+
+ },
+
+ clear: function () {
+
+ for ( let i = 0; i < this.children.length; i ++ ) {
+
+ const object = this.children[ i ];
+
+ object.parent = null;
+
+ object.dispatchEvent( _removedEvent );
+
+ }
+
+ this.children.length = 0;
+
+ return this;
+
+
+ },
+
+ attach: function ( object ) {
+
+ // adds object as a child of this, while maintaining the object's world transform
+
+ this.updateWorldMatrix( true, false );
+
+ _m1$1.getInverse( this.matrixWorld );
+
+ if ( object.parent !== null ) {
+
+ object.parent.updateWorldMatrix( true, false );
+
+ _m1$1.multiply( object.parent.matrixWorld );
+
+ }
+
+ object.applyMatrix4( _m1$1 );
+
+ object.updateWorldMatrix( false, false );
+
+ this.add( object );
+
+ return this;
+
+ },
+
+ getObjectById: function ( id ) {
+
+ return this.getObjectByProperty( 'id', id );
+
+ },
+
+ getObjectByName: function ( name ) {
+
+ return this.getObjectByProperty( 'name', name );
+
+ },
+
+ getObjectByProperty: function ( name, value ) {
+
+ if ( this[ name ] === value ) return this;
+
+ for ( let i = 0, l = this.children.length; i < l; i ++ ) {
+
+ const child = this.children[ i ];
+ const object = child.getObjectByProperty( name, value );
+
+ if ( object !== undefined ) {
+
+ return object;
+
+ }
+
+ }
+
+ return undefined;
+
+ },
+
+ getWorldPosition: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );
+ target = new Vector3();
+
+ }
+
+ this.updateWorldMatrix( true, false );
+
+ return target.setFromMatrixPosition( this.matrixWorld );
+
+ },
+
+ getWorldQuaternion: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );
+ target = new Quaternion();
+
+ }
+
+ this.updateWorldMatrix( true, false );
+
+ this.matrixWorld.decompose( _position, target, _scale );
+
+ return target;
+
+ },
+
+ getWorldScale: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );
+ target = new Vector3();
+
+ }
+
+ this.updateWorldMatrix( true, false );
+
+ this.matrixWorld.decompose( _position, _quaternion$2, target );
+
+ return target;
+
+ },
+
+ getWorldDirection: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );
+ target = new Vector3();
+
+ }
+
+ this.updateWorldMatrix( true, false );
+
+ const e = this.matrixWorld.elements;
+
+ return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();
+
+ },
+
+ raycast: function () {},
+
+ traverse: function ( callback ) {
+
+ callback( this );
+
+ const children = this.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverse( callback );
+
+ }
+
+ },
+
+ traverseVisible: function ( callback ) {
+
+ if ( this.visible === false ) return;
+
+ callback( this );
+
+ const children = this.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].traverseVisible( callback );
+
+ }
+
+ },
+
+ traverseAncestors: function ( callback ) {
+
+ const parent = this.parent;
+
+ if ( parent !== null ) {
+
+ callback( parent );
+
+ parent.traverseAncestors( callback );
+
+ }
+
+ },
+
+ updateMatrix: function () {
+
+ this.matrix.compose( this.position, this.quaternion, this.scale );
+
+ this.matrixWorldNeedsUpdate = true;
+
+ },
+
+ updateMatrixWorld: function ( force ) {
+
+ if ( this.matrixAutoUpdate ) this.updateMatrix();
+
+ if ( this.matrixWorldNeedsUpdate || force ) {
+
+ if ( this.parent === null ) {
+
+ this.matrixWorld.copy( this.matrix );
+
+ } else {
+
+ this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+ }
+
+ this.matrixWorldNeedsUpdate = false;
+
+ force = true;
+
+ }
+
+ // update children
+
+ const children = this.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].updateMatrixWorld( force );
+
+ }
+
+ },
+
+ updateWorldMatrix: function ( updateParents, updateChildren ) {
+
+ const parent = this.parent;
+
+ if ( updateParents === true && parent !== null ) {
+
+ parent.updateWorldMatrix( true, false );
+
+ }
+
+ if ( this.matrixAutoUpdate ) this.updateMatrix();
+
+ if ( this.parent === null ) {
+
+ this.matrixWorld.copy( this.matrix );
+
+ } else {
+
+ this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+ }
+
+ // update children
+
+ if ( updateChildren === true ) {
+
+ const children = this.children;
+
+ for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+ children[ i ].updateWorldMatrix( false, true );
+
+ }
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ // meta is a string when called from JSON.stringify
+ const isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+ const output = {};
+
+ // meta is a hash used to collect geometries, materials.
+ // not providing it implies that this is the root object
+ // being serialized.
+ if ( isRootObject ) {
+
+ // initialize meta obj
+ meta = {
+ geometries: {},
+ materials: {},
+ textures: {},
+ images: {},
+ shapes: {}
+ };
+
+ output.metadata = {
+ version: 4.5,
+ type: 'Object',
+ generator: 'Object3D.toJSON'
+ };
+
+ }
+
+ // standard Object3D serialization
+
+ const object = {};
+
+ object.uuid = this.uuid;
+ object.type = this.type;
+
+ if ( this.name !== '' ) object.name = this.name;
+ if ( this.castShadow === true ) object.castShadow = true;
+ if ( this.receiveShadow === true ) object.receiveShadow = true;
+ if ( this.visible === false ) object.visible = false;
+ if ( this.frustumCulled === false ) object.frustumCulled = false;
+ if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
+ if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
+
+ object.layers = this.layers.mask;
+ object.matrix = this.matrix.toArray();
+
+ if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
+
+ // object specific properties
+
+ if ( this.isInstancedMesh ) {
+
+ object.type = 'InstancedMesh';
+ object.count = this.count;
+ object.instanceMatrix = this.instanceMatrix.toJSON();
+
+ }
+
+ //
+
+ function serialize( library, element ) {
+
+ if ( library[ element.uuid ] === undefined ) {
+
+ library[ element.uuid ] = element.toJSON( meta );
+
+ }
+
+ return element.uuid;
+
+ }
+
+ if ( this.isMesh || this.isLine || this.isPoints ) {
+
+ object.geometry = serialize( meta.geometries, this.geometry );
+
+ const parameters = this.geometry.parameters;
+
+ if ( parameters !== undefined && parameters.shapes !== undefined ) {
+
+ const shapes = parameters.shapes;
+
+ if ( Array.isArray( shapes ) ) {
+
+ for ( let i = 0, l = shapes.length; i < l; i ++ ) {
+
+ const shape = shapes[ i ];
+
+ serialize( meta.shapes, shape );
+
+ }
+
+ } else {
+
+ serialize( meta.shapes, shapes );
+
+ }
+
+ }
+
+ }
+
+ if ( this.material !== undefined ) {
+
+ if ( Array.isArray( this.material ) ) {
+
+ const uuids = [];
+
+ for ( let i = 0, l = this.material.length; i < l; i ++ ) {
+
+ uuids.push( serialize( meta.materials, this.material[ i ] ) );
+
+ }
+
+ object.material = uuids;
+
+ } else {
+
+ object.material = serialize( meta.materials, this.material );
+
+ }
+
+ }
+
+ //
+
+ if ( this.children.length > 0 ) {
+
+ object.children = [];
+
+ for ( let i = 0; i < this.children.length; i ++ ) {
+
+ object.children.push( this.children[ i ].toJSON( meta ).object );
+
+ }
+
+ }
+
+ if ( isRootObject ) {
+
+ const geometries = extractFromCache( meta.geometries );
+ const materials = extractFromCache( meta.materials );
+ const textures = extractFromCache( meta.textures );
+ const images = extractFromCache( meta.images );
+ const shapes = extractFromCache( meta.shapes );
+
+ if ( geometries.length > 0 ) output.geometries = geometries;
+ if ( materials.length > 0 ) output.materials = materials;
+ if ( textures.length > 0 ) output.textures = textures;
+ if ( images.length > 0 ) output.images = images;
+ if ( shapes.length > 0 ) output.shapes = shapes;
+
+ }
+
+ output.object = object;
+
+ return output;
+
+ // extract data from the cache hash
+ // remove metadata on each item
+ // and return as array
+ function extractFromCache( cache ) {
+
+ const values = [];
+ for ( const key in cache ) {
+
+ const data = cache[ key ];
+ delete data.metadata;
+ values.push( data );
+
+ }
+
+ return values;
+
+ }
+
+ },
+
+ clone: function ( recursive ) {
+
+ return new this.constructor().copy( this, recursive );
+
+ },
+
+ copy: function ( source, recursive ) {
+
+ if ( recursive === undefined ) recursive = true;
+
+ this.name = source.name;
+
+ this.up.copy( source.up );
+
+ this.position.copy( source.position );
+ this.rotation.order = source.rotation.order;
+ this.quaternion.copy( source.quaternion );
+ this.scale.copy( source.scale );
+
+ this.matrix.copy( source.matrix );
+ this.matrixWorld.copy( source.matrixWorld );
+
+ this.matrixAutoUpdate = source.matrixAutoUpdate;
+ this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
+
+ this.layers.mask = source.layers.mask;
+ this.visible = source.visible;
+
+ this.castShadow = source.castShadow;
+ this.receiveShadow = source.receiveShadow;
+
+ this.frustumCulled = source.frustumCulled;
+ this.renderOrder = source.renderOrder;
+
+ this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
+ if ( recursive === true ) {
+
+ for ( let i = 0; i < source.children.length; i ++ ) {
+
+ const child = source.children[ i ];
+ this.add( child.clone() );
+
+ }
+
+ }
+
+ return this;
+
+ }
+
+} );
+
+const _vector1 = /*@__PURE__*/ new Vector3();
+const _vector2 = /*@__PURE__*/ new Vector3();
+const _normalMatrix = /*@__PURE__*/ new Matrix3();
+
+class Plane {
+
+ constructor( normal, constant ) {
+
+ Object.defineProperty( this, 'isPlane', { value: true } );
+
+ // normal is assumed to be normalized
+
+ this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
+ this.constant = ( constant !== undefined ) ? constant : 0;
+
+ }
+
+ set( normal, constant ) {
+
+ this.normal.copy( normal );
+ this.constant = constant;
+
+ return this;
+
+ }
+
+ setComponents( x, y, z, w ) {
+
+ this.normal.set( x, y, z );
+ this.constant = w;
+
+ return this;
+
+ }
+
+ setFromNormalAndCoplanarPoint( normal, point ) {
+
+ this.normal.copy( normal );
+ this.constant = - point.dot( this.normal );
+
+ return this;
+
+ }
+
+ setFromCoplanarPoints( a, b, c ) {
+
+ const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize();
+
+ // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?
+
+ this.setFromNormalAndCoplanarPoint( normal, a );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( plane ) {
+
+ this.normal.copy( plane.normal );
+ this.constant = plane.constant;
+
+ return this;
+
+ }
+
+ normalize() {
+
+ // Note: will lead to a divide by zero if the plane is invalid.
+
+ const inverseNormalLength = 1.0 / this.normal.length();
+ this.normal.multiplyScalar( inverseNormalLength );
+ this.constant *= inverseNormalLength;
+
+ return this;
+
+ }
+
+ negate() {
+
+ this.constant *= - 1;
+ this.normal.negate();
+
+ return this;
+
+ }
+
+ distanceToPoint( point ) {
+
+ return this.normal.dot( point ) + this.constant;
+
+ }
+
+ distanceToSphere( sphere ) {
+
+ return this.distanceToPoint( sphere.center ) - sphere.radius;
+
+ }
+
+ projectPoint( point, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .projectPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );
+
+ }
+
+ intersectLine( line, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .intersectLine() target is now required' );
+ target = new Vector3();
+
+ }
+
+ const direction = line.delta( _vector1 );
+
+ const denominator = this.normal.dot( direction );
+
+ if ( denominator === 0 ) {
+
+ // line is coplanar, return origin
+ if ( this.distanceToPoint( line.start ) === 0 ) {
+
+ return target.copy( line.start );
+
+ }
+
+ // Unsure if this is the correct method to handle this case.
+ return undefined;
+
+ }
+
+ const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;
+
+ if ( t < 0 || t > 1 ) {
+
+ return undefined;
+
+ }
+
+ return target.copy( direction ).multiplyScalar( t ).add( line.start );
+
+ }
+
+ intersectsLine( line ) {
+
+ // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.
+
+ const startSign = this.distanceToPoint( line.start );
+ const endSign = this.distanceToPoint( line.end );
+
+ return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );
+
+ }
+
+ intersectsBox( box ) {
+
+ return box.intersectsPlane( this );
+
+ }
+
+ intersectsSphere( sphere ) {
+
+ return sphere.intersectsPlane( this );
+
+ }
+
+ coplanarPoint( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.copy( this.normal ).multiplyScalar( - this.constant );
+
+ }
+
+ applyMatrix4( matrix, optionalNormalMatrix ) {
+
+ const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix );
+
+ const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix );
+
+ const normal = this.normal.applyMatrix3( normalMatrix ).normalize();
+
+ this.constant = - referencePoint.dot( normal );
+
+ return this;
+
+ }
+
+ translate( offset ) {
+
+ this.constant -= offset.dot( this.normal );
+
+ return this;
+
+ }
+
+ equals( plane ) {
+
+ return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );
+
+ }
+
+}
+
+const _v0$1 = /*@__PURE__*/ new Vector3();
+const _v1$3 = /*@__PURE__*/ new Vector3();
+const _v2$1 = /*@__PURE__*/ new Vector3();
+const _v3 = /*@__PURE__*/ new Vector3();
+
+const _vab = /*@__PURE__*/ new Vector3();
+const _vac = /*@__PURE__*/ new Vector3();
+const _vbc = /*@__PURE__*/ new Vector3();
+const _vap = /*@__PURE__*/ new Vector3();
+const _vbp = /*@__PURE__*/ new Vector3();
+const _vcp = /*@__PURE__*/ new Vector3();
+
+class Triangle {
+
+ constructor( a, b, c ) {
+
+ this.a = ( a !== undefined ) ? a : new Vector3();
+ this.b = ( b !== undefined ) ? b : new Vector3();
+ this.c = ( c !== undefined ) ? c : new Vector3();
+
+ }
+
+ static getNormal( a, b, c, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Triangle: .getNormal() target is now required' );
+ target = new Vector3();
+
+ }
+
+ target.subVectors( c, b );
+ _v0$1.subVectors( a, b );
+ target.cross( _v0$1 );
+
+ const targetLengthSq = target.lengthSq();
+ if ( targetLengthSq > 0 ) {
+
+ return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );
+
+ }
+
+ return target.set( 0, 0, 0 );
+
+ }
+
+ // static/instance method to calculate barycentric coordinates
+ // based on: http://www.blackpawn.com/texts/pointinpoly/default.html
+ static getBarycoord( point, a, b, c, target ) {
+
+ _v0$1.subVectors( c, a );
+ _v1$3.subVectors( b, a );
+ _v2$1.subVectors( point, a );
+
+ const dot00 = _v0$1.dot( _v0$1 );
+ const dot01 = _v0$1.dot( _v1$3 );
+ const dot02 = _v0$1.dot( _v2$1 );
+ const dot11 = _v1$3.dot( _v1$3 );
+ const dot12 = _v1$3.dot( _v2$1 );
+
+ const denom = ( dot00 * dot11 - dot01 * dot01 );
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Triangle: .getBarycoord() target is now required' );
+ target = new Vector3();
+
+ }
+
+ // collinear or singular triangle
+ if ( denom === 0 ) {
+
+ // arbitrary location outside of triangle?
+ // not sure if this is the best idea, maybe should be returning undefined
+ return target.set( - 2, - 1, - 1 );
+
+ }
+
+ const invDenom = 1 / denom;
+ const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;
+ const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;
+
+ // barycentric coordinates must always sum to 1
+ return target.set( 1 - u - v, v, u );
+
+ }
+
+ static containsPoint( point, a, b, c ) {
+
+ this.getBarycoord( point, a, b, c, _v3 );
+
+ return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 );
+
+ }
+
+ static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) {
+
+ this.getBarycoord( point, p1, p2, p3, _v3 );
+
+ target.set( 0, 0 );
+ target.addScaledVector( uv1, _v3.x );
+ target.addScaledVector( uv2, _v3.y );
+ target.addScaledVector( uv3, _v3.z );
+
+ return target;
+
+ }
+
+ static isFrontFacing( a, b, c, direction ) {
+
+ _v0$1.subVectors( c, b );
+ _v1$3.subVectors( a, b );
+
+ // strictly front facing
+ return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false;
+
+ }
+
+ set( a, b, c ) {
+
+ this.a.copy( a );
+ this.b.copy( b );
+ this.c.copy( c );
+
+ return this;
+
+ }
+
+ setFromPointsAndIndices( points, i0, i1, i2 ) {
+
+ this.a.copy( points[ i0 ] );
+ this.b.copy( points[ i1 ] );
+ this.c.copy( points[ i2 ] );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( triangle ) {
+
+ this.a.copy( triangle.a );
+ this.b.copy( triangle.b );
+ this.c.copy( triangle.c );
+
+ return this;
+
+ }
+
+ getArea() {
+
+ _v0$1.subVectors( this.c, this.b );
+ _v1$3.subVectors( this.a, this.b );
+
+ return _v0$1.cross( _v1$3 ).length() * 0.5;
+
+ }
+
+ getMidpoint( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Triangle: .getMidpoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );
+
+ }
+
+ getNormal( target ) {
+
+ return Triangle.getNormal( this.a, this.b, this.c, target );
+
+ }
+
+ getPlane( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Triangle: .getPlane() target is now required' );
+ target = new Plane();
+
+ }
+
+ return target.setFromCoplanarPoints( this.a, this.b, this.c );
+
+ }
+
+ getBarycoord( point, target ) {
+
+ return Triangle.getBarycoord( point, this.a, this.b, this.c, target );
+
+ }
+
+ getUV( point, uv1, uv2, uv3, target ) {
+
+ return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target );
+
+ }
+
+ containsPoint( point ) {
+
+ return Triangle.containsPoint( point, this.a, this.b, this.c );
+
+ }
+
+ isFrontFacing( direction ) {
+
+ return Triangle.isFrontFacing( this.a, this.b, this.c, direction );
+
+ }
+
+ intersectsBox( box ) {
+
+ return box.intersectsTriangle( this );
+
+ }
+
+ closestPointToPoint( p, target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' );
+ target = new Vector3();
+
+ }
+
+ const a = this.a, b = this.b, c = this.c;
+ let v, w;
+
+ // algorithm thanks to Real-Time Collision Detection by Christer Ericson,
+ // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc.,
+ // under the accompanying license; see chapter 5.1.5 for detailed explanation.
+ // basically, we're distinguishing which of the voronoi regions of the triangle
+ // the point lies in with the minimum amount of redundant computation.
+
+ _vab.subVectors( b, a );
+ _vac.subVectors( c, a );
+ _vap.subVectors( p, a );
+ const d1 = _vab.dot( _vap );
+ const d2 = _vac.dot( _vap );
+ if ( d1 <= 0 && d2 <= 0 ) {
+
+ // vertex region of A; barycentric coords (1, 0, 0)
+ return target.copy( a );
+
+ }
+
+ _vbp.subVectors( p, b );
+ const d3 = _vab.dot( _vbp );
+ const d4 = _vac.dot( _vbp );
+ if ( d3 >= 0 && d4 <= d3 ) {
+
+ // vertex region of B; barycentric coords (0, 1, 0)
+ return target.copy( b );
+
+ }
+
+ const vc = d1 * d4 - d3 * d2;
+ if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) {
+
+ v = d1 / ( d1 - d3 );
+ // edge region of AB; barycentric coords (1-v, v, 0)
+ return target.copy( a ).addScaledVector( _vab, v );
+
+ }
+
+ _vcp.subVectors( p, c );
+ const d5 = _vab.dot( _vcp );
+ const d6 = _vac.dot( _vcp );
+ if ( d6 >= 0 && d5 <= d6 ) {
+
+ // vertex region of C; barycentric coords (0, 0, 1)
+ return target.copy( c );
+
+ }
+
+ const vb = d5 * d2 - d1 * d6;
+ if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) {
+
+ w = d2 / ( d2 - d6 );
+ // edge region of AC; barycentric coords (1-w, 0, w)
+ return target.copy( a ).addScaledVector( _vac, w );
+
+ }
+
+ const va = d3 * d6 - d5 * d4;
+ if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) {
+
+ _vbc.subVectors( c, b );
+ w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) );
+ // edge region of BC; barycentric coords (0, 1-w, w)
+ return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC
+
+ }
+
+ // face region
+ const denom = 1 / ( va + vb + vc );
+ // u = va * denom
+ v = vb * denom;
+ w = vc * denom;
+
+ return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w );
+
+ }
+
+ equals( triangle ) {
+
+ return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );
+
+ }
+
+}
+
+const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,
+ 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,
+ 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,
+ 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,
+ 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,
+ 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,
+ 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,
+ 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,
+ 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,
+ 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,
+ 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,
+ 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,
+ 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,
+ 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,
+ 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,
+ 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,
+ 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
+ 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
+ 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
+ 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+ 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
+ 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
+ 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
+ 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };
+
+const _hslA = { h: 0, s: 0, l: 0 };
+const _hslB = { h: 0, s: 0, l: 0 };
+
+function hue2rgb( p, q, t ) {
+
+ if ( t < 0 ) t += 1;
+ if ( t > 1 ) t -= 1;
+ if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;
+ if ( t < 1 / 2 ) return q;
+ if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );
+ return p;
+
+}
+
+function SRGBToLinear( c ) {
+
+ return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
+
+}
+
+function LinearToSRGB( c ) {
+
+ return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055;
+
+}
+
+class Color {
+
+ constructor( r, g, b ) {
+
+ Object.defineProperty( this, 'isColor', { value: true } );
+
+ if ( g === undefined && b === undefined ) {
+
+ // r is THREE.Color, hex or string
+ return this.set( r );
+
+ }
+
+ return this.setRGB( r, g, b );
+
+ }
+
+ set( value ) {
+
+ if ( value && value.isColor ) {
+
+ this.copy( value );
+
+ } else if ( typeof value === 'number' ) {
+
+ this.setHex( value );
+
+ } else if ( typeof value === 'string' ) {
+
+ this.setStyle( value );
+
+ }
+
+ return this;
+
+ }
+
+ setScalar( scalar ) {
+
+ this.r = scalar;
+ this.g = scalar;
+ this.b = scalar;
+
+ return this;
+
+ }
+
+ setHex( hex ) {
+
+ hex = Math.floor( hex );
+
+ this.r = ( hex >> 16 & 255 ) / 255;
+ this.g = ( hex >> 8 & 255 ) / 255;
+ this.b = ( hex & 255 ) / 255;
+
+ return this;
+
+ }
+
+ setRGB( r, g, b ) {
+
+ this.r = r;
+ this.g = g;
+ this.b = b;
+
+ return this;
+
+ }
+
+ setHSL( h, s, l ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+ h = MathUtils.euclideanModulo( h, 1 );
+ s = MathUtils.clamp( s, 0, 1 );
+ l = MathUtils.clamp( l, 0, 1 );
+
+ if ( s === 0 ) {
+
+ this.r = this.g = this.b = l;
+
+ } else {
+
+ const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );
+ const q = ( 2 * l ) - p;
+
+ this.r = hue2rgb( q, p, h + 1 / 3 );
+ this.g = hue2rgb( q, p, h );
+ this.b = hue2rgb( q, p, h - 1 / 3 );
+
+ }
+
+ return this;
+
+ }
+
+ setStyle( style ) {
+
+ function handleAlpha( string ) {
+
+ if ( string === undefined ) return;
+
+ if ( parseFloat( string ) < 1 ) {
+
+ console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );
+
+ }
+
+ }
+
+
+ let m;
+
+ if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) {
+
+ // rgb / hsl
+
+ let color;
+ const name = m[ 1 ];
+ const components = m[ 2 ];
+
+ switch ( name ) {
+
+ case 'rgb':
+ case 'rgba':
+
+ if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(255,0,0) rgba(255,0,0,0.5)
+ this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;
+ this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;
+ this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)
+ this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;
+ this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;
+ this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this;
+
+ }
+
+ break;
+
+ case 'hsl':
+ case 'hsla':
+
+ if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) {
+
+ // hsl(120,50%,50%) hsla(120,50%,50%,0.5)
+ const h = parseFloat( color[ 1 ] ) / 360;
+ const s = parseInt( color[ 2 ], 10 ) / 100;
+ const l = parseInt( color[ 3 ], 10 ) / 100;
+
+ handleAlpha( color[ 5 ] );
+
+ return this.setHSL( h, s, l );
+
+ }
+
+ break;
+
+ }
+
+ } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) {
+
+ // hex color
+
+ const hex = m[ 1 ];
+ const size = hex.length;
+
+ if ( size === 3 ) {
+
+ // #ff0
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;
+
+ return this;
+
+ } else if ( size === 6 ) {
+
+ // #ff0000
+ this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;
+ this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;
+ this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;
+
+ return this;
+
+ }
+
+ }
+
+ if ( style && style.length > 0 ) {
+
+ return this.setColorName( style );
+
+ }
+
+ return this;
+
+ }
+
+ setColorName( style ) {
+
+ // color keywords
+ const hex = _colorKeywords[ style ];
+
+ if ( hex !== undefined ) {
+
+ // red
+ this.setHex( hex );
+
+ } else {
+
+ // unknown color
+ console.warn( 'THREE.Color: Unknown color ' + style );
+
+ }
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor( this.r, this.g, this.b );
+
+ }
+
+ copy( color ) {
+
+ this.r = color.r;
+ this.g = color.g;
+ this.b = color.b;
+
+ return this;
+
+ }
+
+ copyGammaToLinear( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ this.r = Math.pow( color.r, gammaFactor );
+ this.g = Math.pow( color.g, gammaFactor );
+ this.b = Math.pow( color.b, gammaFactor );
+
+ return this;
+
+ }
+
+ copyLinearToGamma( color, gammaFactor ) {
+
+ if ( gammaFactor === undefined ) gammaFactor = 2.0;
+
+ const safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;
+
+ this.r = Math.pow( color.r, safeInverse );
+ this.g = Math.pow( color.g, safeInverse );
+ this.b = Math.pow( color.b, safeInverse );
+
+ return this;
+
+ }
+
+ convertGammaToLinear( gammaFactor ) {
+
+ this.copyGammaToLinear( this, gammaFactor );
+
+ return this;
+
+ }
+
+ convertLinearToGamma( gammaFactor ) {
+
+ this.copyLinearToGamma( this, gammaFactor );
+
+ return this;
+
+ }
+
+ copySRGBToLinear( color ) {
+
+ this.r = SRGBToLinear( color.r );
+ this.g = SRGBToLinear( color.g );
+ this.b = SRGBToLinear( color.b );
+
+ return this;
+
+ }
+
+ copyLinearToSRGB( color ) {
+
+ this.r = LinearToSRGB( color.r );
+ this.g = LinearToSRGB( color.g );
+ this.b = LinearToSRGB( color.b );
+
+ return this;
+
+ }
+
+ convertSRGBToLinear() {
+
+ this.copySRGBToLinear( this );
+
+ return this;
+
+ }
+
+ convertLinearToSRGB() {
+
+ this.copyLinearToSRGB( this );
+
+ return this;
+
+ }
+
+ getHex() {
+
+ return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
+
+ }
+
+ getHexString() {
+
+ return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );
+
+ }
+
+ getHSL( target ) {
+
+ // h,s,l ranges are in 0.0 - 1.0
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Color: .getHSL() target is now required' );
+ target = { h: 0, s: 0, l: 0 };
+
+ }
+
+ const r = this.r, g = this.g, b = this.b;
+
+ const max = Math.max( r, g, b );
+ const min = Math.min( r, g, b );
+
+ let hue, saturation;
+ const lightness = ( min + max ) / 2.0;
+
+ if ( min === max ) {
+
+ hue = 0;
+ saturation = 0;
+
+ } else {
+
+ const delta = max - min;
+
+ saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );
+
+ switch ( max ) {
+
+ case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;
+ case g: hue = ( b - r ) / delta + 2; break;
+ case b: hue = ( r - g ) / delta + 4; break;
+
+ }
+
+ hue /= 6;
+
+ }
+
+ target.h = hue;
+ target.s = saturation;
+ target.l = lightness;
+
+ return target;
+
+ }
+
+ getStyle() {
+
+ return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';
+
+ }
+
+ offsetHSL( h, s, l ) {
+
+ this.getHSL( _hslA );
+
+ _hslA.h += h; _hslA.s += s; _hslA.l += l;
+
+ this.setHSL( _hslA.h, _hslA.s, _hslA.l );
+
+ return this;
+
+ }
+
+ add( color ) {
+
+ this.r += color.r;
+ this.g += color.g;
+ this.b += color.b;
+
+ return this;
+
+ }
+
+ addColors( color1, color2 ) {
+
+ this.r = color1.r + color2.r;
+ this.g = color1.g + color2.g;
+ this.b = color1.b + color2.b;
+
+ return this;
+
+ }
+
+ addScalar( s ) {
+
+ this.r += s;
+ this.g += s;
+ this.b += s;
+
+ return this;
+
+ }
+
+ sub( color ) {
+
+ this.r = Math.max( 0, this.r - color.r );
+ this.g = Math.max( 0, this.g - color.g );
+ this.b = Math.max( 0, this.b - color.b );
+
+ return this;
+
+ }
+
+ multiply( color ) {
+
+ this.r *= color.r;
+ this.g *= color.g;
+ this.b *= color.b;
+
+ return this;
+
+ }
+
+ multiplyScalar( s ) {
+
+ this.r *= s;
+ this.g *= s;
+ this.b *= s;
+
+ return this;
+
+ }
+
+ lerp( color, alpha ) {
+
+ this.r += ( color.r - this.r ) * alpha;
+ this.g += ( color.g - this.g ) * alpha;
+ this.b += ( color.b - this.b ) * alpha;
+
+ return this;
+
+ }
+
+ lerpHSL( color, alpha ) {
+
+ this.getHSL( _hslA );
+ color.getHSL( _hslB );
+
+ const h = MathUtils.lerp( _hslA.h, _hslB.h, alpha );
+ const s = MathUtils.lerp( _hslA.s, _hslB.s, alpha );
+ const l = MathUtils.lerp( _hslA.l, _hslB.l, alpha );
+
+ this.setHSL( h, s, l );
+
+ return this;
+
+ }
+
+ equals( c ) {
+
+ return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );
+
+ }
+
+ fromArray( array, offset = 0 ) {
+
+ this.r = array[ offset ];
+ this.g = array[ offset + 1 ];
+ this.b = array[ offset + 2 ];
+
+ return this;
+
+ }
+
+ toArray( array = [], offset = 0 ) {
+
+ array[ offset ] = this.r;
+ array[ offset + 1 ] = this.g;
+ array[ offset + 2 ] = this.b;
+
+ return array;
+
+ }
+
+ fromBufferAttribute( attribute, index ) {
+
+ this.r = attribute.getX( index );
+ this.g = attribute.getY( index );
+ this.b = attribute.getZ( index );
+
+ if ( attribute.normalized === true ) {
+
+ // assuming Uint8Array
+
+ this.r /= 255;
+ this.g /= 255;
+ this.b /= 255;
+
+ }
+
+ return this;
+
+ }
+
+ toJSON() {
+
+ return this.getHex();
+
+ }
+
+}
+
+Color.NAMES = _colorKeywords;
+Color.prototype.r = 1;
+Color.prototype.g = 1;
+Color.prototype.b = 1;
+
+class Face3 {
+
+ constructor( a, b, c, normal, color, materialIndex ) {
+
+ this.a = a;
+ this.b = b;
+ this.c = c;
+
+ this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3();
+ this.vertexNormals = Array.isArray( normal ) ? normal : [];
+
+ this.color = ( color && color.isColor ) ? color : new Color();
+ this.vertexColors = Array.isArray( color ) ? color : [];
+
+ this.materialIndex = materialIndex !== undefined ? materialIndex : 0;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( source ) {
+
+ this.a = source.a;
+ this.b = source.b;
+ this.c = source.c;
+
+ this.normal.copy( source.normal );
+ this.color.copy( source.color );
+
+ this.materialIndex = source.materialIndex;
+
+ for ( let i = 0, il = source.vertexNormals.length; i < il; i ++ ) {
+
+ this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();
+
+ }
+
+ for ( let i = 0, il = source.vertexColors.length; i < il; i ++ ) {
+
+ this.vertexColors[ i ] = source.vertexColors[ i ].clone();
+
+ }
+
+ return this;
+
+ }
+
+}
+
+let materialId = 0;
+
+function Material() {
+
+ Object.defineProperty( this, 'id', { value: materialId ++ } );
+
+ this.uuid = MathUtils.generateUUID();
+
+ this.name = '';
+ this.type = 'Material';
+
+ this.fog = true;
+
+ this.blending = NormalBlending;
+ this.side = FrontSide;
+ this.flatShading = false;
+ this.vertexColors = false;
+
+ this.opacity = 1;
+ this.transparent = false;
+
+ this.blendSrc = SrcAlphaFactor;
+ this.blendDst = OneMinusSrcAlphaFactor;
+ this.blendEquation = AddEquation;
+ this.blendSrcAlpha = null;
+ this.blendDstAlpha = null;
+ this.blendEquationAlpha = null;
+
+ this.depthFunc = LessEqualDepth;
+ this.depthTest = true;
+ this.depthWrite = true;
+
+ this.stencilWriteMask = 0xff;
+ this.stencilFunc = AlwaysStencilFunc;
+ this.stencilRef = 0;
+ this.stencilFuncMask = 0xff;
+ this.stencilFail = KeepStencilOp;
+ this.stencilZFail = KeepStencilOp;
+ this.stencilZPass = KeepStencilOp;
+ this.stencilWrite = false;
+
+ this.clippingPlanes = null;
+ this.clipIntersection = false;
+ this.clipShadows = false;
+
+ this.shadowSide = null;
+
+ this.colorWrite = true;
+
+ this.precision = null; // override the renderer's default precision for this material
+
+ this.polygonOffset = false;
+ this.polygonOffsetFactor = 0;
+ this.polygonOffsetUnits = 0;
+
+ this.dithering = false;
+
+ this.alphaTest = 0;
+ this.premultipliedAlpha = false;
+
+ this.visible = true;
+
+ this.toneMapped = true;
+
+ this.userData = {};
+
+ this.version = 0;
+
+}
+
+Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: Material,
+
+ isMaterial: true,
+
+ onBeforeCompile: function ( /* shaderobject, renderer */ ) {},
+
+ customProgramCacheKey: function () {
+
+ return this.onBeforeCompile.toString();
+
+ },
+
+ setValues: function ( values ) {
+
+ if ( values === undefined ) return;
+
+ for ( const key in values ) {
+
+ const newValue = values[ key ];
+
+ if ( newValue === undefined ) {
+
+ console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+ continue;
+
+ }
+
+ // for backward compatability if shading is set in the constructor
+ if ( key === 'shading' ) {
+
+ console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
+ this.flatShading = ( newValue === FlatShading ) ? true : false;
+ continue;
+
+ }
+
+ const currentValue = this[ key ];
+
+ if ( currentValue === undefined ) {
+
+ console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
+ continue;
+
+ }
+
+ if ( currentValue && currentValue.isColor ) {
+
+ currentValue.set( newValue );
+
+ } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {
+
+ currentValue.copy( newValue );
+
+ } else {
+
+ this[ key ] = newValue;
+
+ }
+
+ }
+
+ },
+
+ toJSON: function ( meta ) {
+
+ const isRoot = ( meta === undefined || typeof meta === 'string' );
+
+ if ( isRoot ) {
+
+ meta = {
+ textures: {},
+ images: {}
+ };
+
+ }
+
+ const data = {
+ metadata: {
+ version: 4.5,
+ type: 'Material',
+ generator: 'Material.toJSON'
+ }
+ };
+
+ // standard Material serialization
+ data.uuid = this.uuid;
+ data.type = this.type;
+
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.color && this.color.isColor ) data.color = this.color.getHex();
+
+ if ( this.roughness !== undefined ) data.roughness = this.roughness;
+ if ( this.metalness !== undefined ) data.metalness = this.metalness;
+
+ if ( this.sheen && this.sheen.isColor ) data.sheen = this.sheen.getHex();
+ if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();
+ if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
+
+ if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
+ if ( this.shininess !== undefined ) data.shininess = this.shininess;
+ if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;
+ if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;
+
+ if ( this.clearcoatMap && this.clearcoatMap.isTexture ) {
+
+ data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid;
+
+ }
+
+ if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) {
+
+ data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid;
+
+ }
+
+ if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) {
+
+ data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid;
+ data.clearcoatNormalScale = this.clearcoatNormalScale.toArray();
+
+ }
+
+ if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;
+ if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid;
+ if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
+ if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
+
+ if ( this.aoMap && this.aoMap.isTexture ) {
+
+ data.aoMap = this.aoMap.toJSON( meta ).uuid;
+ data.aoMapIntensity = this.aoMapIntensity;
+
+ }
+
+ if ( this.bumpMap && this.bumpMap.isTexture ) {
+
+ data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
+ data.bumpScale = this.bumpScale;
+
+ }
+
+ if ( this.normalMap && this.normalMap.isTexture ) {
+
+ data.normalMap = this.normalMap.toJSON( meta ).uuid;
+ data.normalMapType = this.normalMapType;
+ data.normalScale = this.normalScale.toArray();
+
+ }
+
+ if ( this.displacementMap && this.displacementMap.isTexture ) {
+
+ data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
+ data.displacementScale = this.displacementScale;
+ data.displacementBias = this.displacementBias;
+
+ }
+
+ if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;
+ if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;
+
+ if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
+ if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
+
+ if ( this.envMap && this.envMap.isTexture ) {
+
+ data.envMap = this.envMap.toJSON( meta ).uuid;
+ data.reflectivity = this.reflectivity; // Scale behind envMap
+ data.refractionRatio = this.refractionRatio;
+
+ if ( this.combine !== undefined ) data.combine = this.combine;
+ if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;
+
+ }
+
+ if ( this.gradientMap && this.gradientMap.isTexture ) {
+
+ data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
+
+ }
+
+ if ( this.size !== undefined ) data.size = this.size;
+ if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
+
+ if ( this.blending !== NormalBlending ) data.blending = this.blending;
+ if ( this.flatShading === true ) data.flatShading = this.flatShading;
+ if ( this.side !== FrontSide ) data.side = this.side;
+ if ( this.vertexColors ) data.vertexColors = true;
+
+ if ( this.opacity < 1 ) data.opacity = this.opacity;
+ if ( this.transparent === true ) data.transparent = this.transparent;
+
+ data.depthFunc = this.depthFunc;
+ data.depthTest = this.depthTest;
+ data.depthWrite = this.depthWrite;
+
+ data.stencilWrite = this.stencilWrite;
+ data.stencilWriteMask = this.stencilWriteMask;
+ data.stencilFunc = this.stencilFunc;
+ data.stencilRef = this.stencilRef;
+ data.stencilFuncMask = this.stencilFuncMask;
+ data.stencilFail = this.stencilFail;
+ data.stencilZFail = this.stencilZFail;
+ data.stencilZPass = this.stencilZPass;
+
+ // rotation (SpriteMaterial)
+ if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation;
+
+ if ( this.polygonOffset === true ) data.polygonOffset = true;
+ if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor;
+ if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits;
+
+ if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth;
+ if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
+ if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
+ if ( this.scale !== undefined ) data.scale = this.scale;
+
+ if ( this.dithering === true ) data.dithering = true;
+
+ if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
+ if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+
+ if ( this.wireframe === true ) data.wireframe = this.wireframe;
+ if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
+ if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
+ if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
+
+ if ( this.morphTargets === true ) data.morphTargets = true;
+ if ( this.morphNormals === true ) data.morphNormals = true;
+ if ( this.skinning === true ) data.skinning = true;
+
+ if ( this.visible === false ) data.visible = false;
+
+ if ( this.toneMapped === false ) data.toneMapped = false;
+
+ if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
+
+ // TODO: Copied from Object3D.toJSON
+
+ function extractFromCache( cache ) {
+
+ const values = [];
+
+ for ( const key in cache ) {
+
+ const data = cache[ key ];
+ delete data.metadata;
+ values.push( data );
+
+ }
+
+ return values;
+
+ }
+
+ if ( isRoot ) {
+
+ const textures = extractFromCache( meta.textures );
+ const images = extractFromCache( meta.images );
+
+ if ( textures.length > 0 ) data.textures = textures;
+ if ( images.length > 0 ) data.images = images;
+
+ }
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+
+ this.fog = source.fog;
+
+ this.blending = source.blending;
+ this.side = source.side;
+ this.flatShading = source.flatShading;
+ this.vertexColors = source.vertexColors;
+
+ this.opacity = source.opacity;
+ this.transparent = source.transparent;
+
+ this.blendSrc = source.blendSrc;
+ this.blendDst = source.blendDst;
+ this.blendEquation = source.blendEquation;
+ this.blendSrcAlpha = source.blendSrcAlpha;
+ this.blendDstAlpha = source.blendDstAlpha;
+ this.blendEquationAlpha = source.blendEquationAlpha;
+
+ this.depthFunc = source.depthFunc;
+ this.depthTest = source.depthTest;
+ this.depthWrite = source.depthWrite;
+
+ this.stencilWriteMask = source.stencilWriteMask;
+ this.stencilFunc = source.stencilFunc;
+ this.stencilRef = source.stencilRef;
+ this.stencilFuncMask = source.stencilFuncMask;
+ this.stencilFail = source.stencilFail;
+ this.stencilZFail = source.stencilZFail;
+ this.stencilZPass = source.stencilZPass;
+ this.stencilWrite = source.stencilWrite;
+
+ const srcPlanes = source.clippingPlanes;
+ let dstPlanes = null;
+
+ if ( srcPlanes !== null ) {
+
+ const n = srcPlanes.length;
+ dstPlanes = new Array( n );
+
+ for ( let i = 0; i !== n; ++ i ) {
+
+ dstPlanes[ i ] = srcPlanes[ i ].clone();
+
+ }
+
+ }
+
+ this.clippingPlanes = dstPlanes;
+ this.clipIntersection = source.clipIntersection;
+ this.clipShadows = source.clipShadows;
+
+ this.shadowSide = source.shadowSide;
+
+ this.colorWrite = source.colorWrite;
+
+ this.precision = source.precision;
+
+ this.polygonOffset = source.polygonOffset;
+ this.polygonOffsetFactor = source.polygonOffsetFactor;
+ this.polygonOffsetUnits = source.polygonOffsetUnits;
+
+ this.dithering = source.dithering;
+
+ this.alphaTest = source.alphaTest;
+ this.premultipliedAlpha = source.premultipliedAlpha;
+
+ this.visible = source.visible;
+
+ this.toneMapped = source.toneMapped;
+
+ this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+} );
+
+Object.defineProperty( Material.prototype, 'needsUpdate', {
+
+ set: function ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+} );
+
+/**
+ * parameters = {
+ * color: ,
+ * opacity: ,
+ * map: new THREE.Texture( ),
+ *
+ * lightMap: new THREE.Texture( ),
+ * lightMapIntensity:
+ *
+ * aoMap: new THREE.Texture( ),
+ * aoMapIntensity:
+ *
+ * specularMap: new THREE.Texture( ),
+ *
+ * alphaMap: new THREE.Texture( ),
+ *
+ * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),
+ * combine: THREE.Multiply,
+ * reflectivity: ,
+ * refractionRatio: ,
+ *
+ * depthTest: ,
+ * depthWrite: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * skinning: ,
+ * morphTargets:
+ * }
+ */
+
+function MeshBasicMaterial( parameters ) {
+
+ Material.call( this );
+
+ this.type = 'MeshBasicMaterial';
+
+ this.color = new Color( 0xffffff ); // emissive
+
+ this.map = null;
+
+ this.lightMap = null;
+ this.lightMapIntensity = 1.0;
+
+ this.aoMap = null;
+ this.aoMapIntensity = 1.0;
+
+ this.specularMap = null;
+
+ this.alphaMap = null;
+
+ this.envMap = null;
+ this.combine = MultiplyOperation;
+ this.reflectivity = 1;
+ this.refractionRatio = 0.98;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+ this.wireframeLinecap = 'round';
+ this.wireframeLinejoin = 'round';
+
+ this.skinning = false;
+ this.morphTargets = false;
+
+ this.setValues( parameters );
+
+}
+
+MeshBasicMaterial.prototype = Object.create( Material.prototype );
+MeshBasicMaterial.prototype.constructor = MeshBasicMaterial;
+
+MeshBasicMaterial.prototype.isMeshBasicMaterial = true;
+
+MeshBasicMaterial.prototype.copy = function ( source ) {
+
+ Material.prototype.copy.call( this, source );
+
+ this.color.copy( source.color );
+
+ this.map = source.map;
+
+ this.lightMap = source.lightMap;
+ this.lightMapIntensity = source.lightMapIntensity;
+
+ this.aoMap = source.aoMap;
+ this.aoMapIntensity = source.aoMapIntensity;
+
+ this.specularMap = source.specularMap;
+
+ this.alphaMap = source.alphaMap;
+
+ this.envMap = source.envMap;
+ this.combine = source.combine;
+ this.reflectivity = source.reflectivity;
+ this.refractionRatio = source.refractionRatio;
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+ this.wireframeLinecap = source.wireframeLinecap;
+ this.wireframeLinejoin = source.wireframeLinejoin;
+
+ this.skinning = source.skinning;
+ this.morphTargets = source.morphTargets;
+
+ return this;
+
+};
+
+const _vector$3 = new Vector3();
+const _vector2$1 = new Vector2();
+
+function BufferAttribute( array, itemSize, normalized ) {
+
+ if ( Array.isArray( array ) ) {
+
+ throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );
+
+ }
+
+ this.name = '';
+
+ this.array = array;
+ this.itemSize = itemSize;
+ this.count = array !== undefined ? array.length / itemSize : 0;
+ this.normalized = normalized === true;
+
+ this.usage = StaticDrawUsage;
+ this.updateRange = { offset: 0, count: - 1 };
+
+ this.version = 0;
+
+}
+
+Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
+
+ set: function ( value ) {
+
+ if ( value === true ) this.version ++;
+
+ }
+
+} );
+
+Object.assign( BufferAttribute.prototype, {
+
+ isBufferAttribute: true,
+
+ onUploadCallback: function () {},
+
+ setUsage: function ( value ) {
+
+ this.usage = value;
+
+ return this;
+
+ },
+
+ copy: function ( source ) {
+
+ this.name = source.name;
+ this.array = new source.array.constructor( source.array );
+ this.itemSize = source.itemSize;
+ this.count = source.count;
+ this.normalized = source.normalized;
+
+ this.usage = source.usage;
+
+ return this;
+
+ },
+
+ copyAt: function ( index1, attribute, index2 ) {
+
+ index1 *= this.itemSize;
+ index2 *= attribute.itemSize;
+
+ for ( let i = 0, l = this.itemSize; i < l; i ++ ) {
+
+ this.array[ index1 + i ] = attribute.array[ index2 + i ];
+
+ }
+
+ return this;
+
+ },
+
+ copyArray: function ( array ) {
+
+ this.array.set( array );
+
+ return this;
+
+ },
+
+ copyColorsArray: function ( colors ) {
+
+ const array = this.array;
+ let offset = 0;
+
+ for ( let i = 0, l = colors.length; i < l; i ++ ) {
+
+ let color = colors[ i ];
+
+ if ( color === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );
+ color = new Color();
+
+ }
+
+ array[ offset ++ ] = color.r;
+ array[ offset ++ ] = color.g;
+ array[ offset ++ ] = color.b;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector2sArray: function ( vectors ) {
+
+ const array = this.array;
+ let offset = 0;
+
+ for ( let i = 0, l = vectors.length; i < l; i ++ ) {
+
+ let vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );
+ vector = new Vector2();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector3sArray: function ( vectors ) {
+
+ const array = this.array;
+ let offset = 0;
+
+ for ( let i = 0, l = vectors.length; i < l; i ++ ) {
+
+ let vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );
+ vector = new Vector3();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+
+ }
+
+ return this;
+
+ },
+
+ copyVector4sArray: function ( vectors ) {
+
+ const array = this.array;
+ let offset = 0;
+
+ for ( let i = 0, l = vectors.length; i < l; i ++ ) {
+
+ let vector = vectors[ i ];
+
+ if ( vector === undefined ) {
+
+ console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );
+ vector = new Vector4();
+
+ }
+
+ array[ offset ++ ] = vector.x;
+ array[ offset ++ ] = vector.y;
+ array[ offset ++ ] = vector.z;
+ array[ offset ++ ] = vector.w;
+
+ }
+
+ return this;
+
+ },
+
+ applyMatrix3: function ( m ) {
+
+ if ( this.itemSize === 2 ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector2$1.fromBufferAttribute( this, i );
+ _vector2$1.applyMatrix3( m );
+
+ this.setXY( i, _vector2$1.x, _vector2$1.y );
+
+ }
+
+ } else if ( this.itemSize === 3 ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector$3.fromBufferAttribute( this, i );
+ _vector$3.applyMatrix3( m );
+
+ this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ applyMatrix4: function ( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector$3.x = this.getX( i );
+ _vector$3.y = this.getY( i );
+ _vector$3.z = this.getZ( i );
+
+ _vector$3.applyMatrix4( m );
+
+ this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
+
+ }
+
+ return this;
+
+ },
+
+ applyNormalMatrix: function ( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector$3.x = this.getX( i );
+ _vector$3.y = this.getY( i );
+ _vector$3.z = this.getZ( i );
+
+ _vector$3.applyNormalMatrix( m );
+
+ this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
+
+ }
+
+ return this;
+
+ },
+
+ transformDirection: function ( m ) {
+
+ for ( let i = 0, l = this.count; i < l; i ++ ) {
+
+ _vector$3.x = this.getX( i );
+ _vector$3.y = this.getY( i );
+ _vector$3.z = this.getZ( i );
+
+ _vector$3.transformDirection( m );
+
+ this.setXYZ( i, _vector$3.x, _vector$3.y, _vector$3.z );
+
+ }
+
+ return this;
+
+ },
+
+ set: function ( value, offset ) {
+
+ if ( offset === undefined ) offset = 0;
+
+ this.array.set( value, offset );
+
+ return this;
+
+ },
+
+ getX: function ( index ) {
+
+ return this.array[ index * this.itemSize ];
+
+ },
+
+ setX: function ( index, x ) {
+
+ this.array[ index * this.itemSize ] = x;
+
+ return this;
+
+ },
+
+ getY: function ( index ) {
+
+ return this.array[ index * this.itemSize + 1 ];
+
+ },
+
+ setY: function ( index, y ) {
+
+ this.array[ index * this.itemSize + 1 ] = y;
+
+ return this;
+
+ },
+
+ getZ: function ( index ) {
+
+ return this.array[ index * this.itemSize + 2 ];
+
+ },
+
+ setZ: function ( index, z ) {
+
+ this.array[ index * this.itemSize + 2 ] = z;
+
+ return this;
+
+ },
+
+ getW: function ( index ) {
+
+ return this.array[ index * this.itemSize + 3 ];
+
+ },
+
+ setW: function ( index, w ) {
+
+ this.array[ index * this.itemSize + 3 ] = w;
+
+ return this;
+
+ },
+
+ setXY: function ( index, x, y ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+
+ return this;
+
+ },
+
+ setXYZ: function ( index, x, y, z ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+
+ return this;
+
+ },
+
+ setXYZW: function ( index, x, y, z, w ) {
+
+ index *= this.itemSize;
+
+ this.array[ index + 0 ] = x;
+ this.array[ index + 1 ] = y;
+ this.array[ index + 2 ] = z;
+ this.array[ index + 3 ] = w;
+
+ return this;
+
+ },
+
+ onUpload: function ( callback ) {
+
+ this.onUploadCallback = callback;
+
+ return this;
+
+ },
+
+ clone: function () {
+
+ return new this.constructor( this.array, this.itemSize ).copy( this );
+
+ },
+
+ toJSON: function () {
+
+ return {
+ itemSize: this.itemSize,
+ type: this.array.constructor.name,
+ array: Array.prototype.slice.call( this.array ),
+ normalized: this.normalized
+ };
+
+ }
+
+} );
+
+//
+
+function Int8BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized );
+
+}
+
+Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Int8BufferAttribute.prototype.constructor = Int8BufferAttribute;
+
+
+function Uint8BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized );
+
+}
+
+Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute;
+
+
+function Uint8ClampedBufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized );
+
+}
+
+Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute;
+
+
+function Int16BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized );
+
+}
+
+Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Int16BufferAttribute.prototype.constructor = Int16BufferAttribute;
+
+
+function Uint16BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );
+
+}
+
+Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute;
+
+
+function Int32BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized );
+
+}
+
+Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Int32BufferAttribute.prototype.constructor = Int32BufferAttribute;
+
+
+function Uint32BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized );
+
+}
+
+Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute;
+
+
+function Float32BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized );
+
+}
+
+Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Float32BufferAttribute.prototype.constructor = Float32BufferAttribute;
+
+
+function Float64BufferAttribute( array, itemSize, normalized ) {
+
+ BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized );
+
+}
+
+Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype );
+Float64BufferAttribute.prototype.constructor = Float64BufferAttribute;
+
+class DirectGeometry {
+
+ constructor() {
+
+ this.vertices = [];
+ this.normals = [];
+ this.colors = [];
+ this.uvs = [];
+ this.uvs2 = [];
+
+ this.groups = [];
+
+ this.morphTargets = {};
+
+ this.skinWeights = [];
+ this.skinIndices = [];
+
+ // this.lineDistances = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // update flags
+
+ this.verticesNeedUpdate = false;
+ this.normalsNeedUpdate = false;
+ this.colorsNeedUpdate = false;
+ this.uvsNeedUpdate = false;
+ this.groupsNeedUpdate = false;
+
+ }
+
+ computeGroups( geometry ) {
+
+ const groups = [];
+
+ let group, i;
+ let materialIndex = undefined;
+
+ const faces = geometry.faces;
+
+ for ( i = 0; i < faces.length; i ++ ) {
+
+ const face = faces[ i ];
+
+ // materials
+
+ if ( face.materialIndex !== materialIndex ) {
+
+ materialIndex = face.materialIndex;
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ group = {
+ start: i * 3,
+ materialIndex: materialIndex
+ };
+
+ }
+
+ }
+
+ if ( group !== undefined ) {
+
+ group.count = ( i * 3 ) - group.start;
+ groups.push( group );
+
+ }
+
+ this.groups = groups;
+
+ }
+
+ fromGeometry( geometry ) {
+
+ const faces = geometry.faces;
+ const vertices = geometry.vertices;
+ const faceVertexUvs = geometry.faceVertexUvs;
+
+ const hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;
+ const hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;
+
+ // morphs
+
+ const morphTargets = geometry.morphTargets;
+ const morphTargetsLength = morphTargets.length;
+
+ let morphTargetsPosition;
+
+ if ( morphTargetsLength > 0 ) {
+
+ morphTargetsPosition = [];
+
+ for ( let i = 0; i < morphTargetsLength; i ++ ) {
+
+ morphTargetsPosition[ i ] = {
+ name: morphTargets[ i ].name,
+ data: []
+ };
+
+ }
+
+ this.morphTargets.position = morphTargetsPosition;
+
+ }
+
+ const morphNormals = geometry.morphNormals;
+ const morphNormalsLength = morphNormals.length;
+
+ let morphTargetsNormal;
+
+ if ( morphNormalsLength > 0 ) {
+
+ morphTargetsNormal = [];
+
+ for ( let i = 0; i < morphNormalsLength; i ++ ) {
+
+ morphTargetsNormal[ i ] = {
+ name: morphNormals[ i ].name,
+ data: []
+ };
+
+ }
+
+ this.morphTargets.normal = morphTargetsNormal;
+
+ }
+
+ // skins
+
+ const skinIndices = geometry.skinIndices;
+ const skinWeights = geometry.skinWeights;
+
+ const hasSkinIndices = skinIndices.length === vertices.length;
+ const hasSkinWeights = skinWeights.length === vertices.length;
+
+ //
+
+ if ( vertices.length > 0 && faces.length === 0 ) {
+
+ console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' );
+
+ }
+
+ for ( let i = 0; i < faces.length; i ++ ) {
+
+ const face = faces[ i ];
+
+ this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );
+
+ const vertexNormals = face.vertexNormals;
+
+ if ( vertexNormals.length === 3 ) {
+
+ this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );
+
+ } else {
+
+ const normal = face.normal;
+
+ this.normals.push( normal, normal, normal );
+
+ }
+
+ const vertexColors = face.vertexColors;
+
+ if ( vertexColors.length === 3 ) {
+
+ this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );
+
+ } else {
+
+ const color = face.color;
+
+ this.colors.push( color, color, color );
+
+ }
+
+ if ( hasFaceVertexUv === true ) {
+
+ const vertexUvs = faceVertexUvs[ 0 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );
+
+ this.uvs.push( new Vector2(), new Vector2(), new Vector2() );
+
+ }
+
+ }
+
+ if ( hasFaceVertexUv2 === true ) {
+
+ const vertexUvs = faceVertexUvs[ 1 ][ i ];
+
+ if ( vertexUvs !== undefined ) {
+
+ this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );
+
+ } else {
+
+ console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );
+
+ this.uvs2.push( new Vector2(), new Vector2(), new Vector2() );
+
+ }
+
+ }
+
+ // morphs
+
+ for ( let j = 0; j < morphTargetsLength; j ++ ) {
+
+ const morphTarget = morphTargets[ j ].vertices;
+
+ morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );
+
+ }
+
+ for ( let j = 0; j < morphNormalsLength; j ++ ) {
+
+ const morphNormal = morphNormals[ j ].vertexNormals[ i ];
+
+ morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c );
+
+ }
+
+ // skins
+
+ if ( hasSkinIndices ) {
+
+ this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );
+
+ }
+
+ if ( hasSkinWeights ) {
+
+ this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );
+
+ }
+
+ }
+
+ this.computeGroups( geometry );
+
+ this.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ this.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ this.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ this.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ this.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ return this;
+
+ }
+
+}
+
+function arrayMax( array ) {
+
+ if ( array.length === 0 ) return - Infinity;
+
+ let max = array[ 0 ];
+
+ for ( let i = 1, l = array.length; i < l; ++ i ) {
+
+ if ( array[ i ] > max ) max = array[ i ];
+
+ }
+
+ return max;
+
+}
+
+let _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id
+
+const _m1$2 = new Matrix4();
+const _obj = new Object3D();
+const _offset = new Vector3();
+const _box$2 = new Box3();
+const _boxMorphTargets = new Box3();
+const _vector$4 = new Vector3();
+
+function BufferGeometry() {
+
+ Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } );
+
+ this.uuid = MathUtils.generateUUID();
+
+ this.name = '';
+ this.type = 'BufferGeometry';
+
+ this.index = null;
+ this.attributes = {};
+
+ this.morphAttributes = {};
+ this.morphTargetsRelative = false;
+
+ this.groups = [];
+
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ this.drawRange = { start: 0, count: Infinity };
+
+ this.userData = {};
+
+}
+
+BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
+
+ constructor: BufferGeometry,
+
+ isBufferGeometry: true,
+
+ getIndex: function () {
+
+ return this.index;
+
+ },
+
+ setIndex: function ( index ) {
+
+ if ( Array.isArray( index ) ) {
+
+ this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );
+
+ } else {
+
+ this.index = index;
+
+ }
+
+ return this;
+
+ },
+
+ getAttribute: function ( name ) {
+
+ return this.attributes[ name ];
+
+ },
+
+ setAttribute: function ( name, attribute ) {
+
+ this.attributes[ name ] = attribute;
+
+ return this;
+
+ },
+
+ deleteAttribute: function ( name ) {
+
+ delete this.attributes[ name ];
+
+ return this;
+
+ },
+
+ addGroup: function ( start, count, materialIndex ) {
+
+ this.groups.push( {
+
+ start: start,
+ count: count,
+ materialIndex: materialIndex !== undefined ? materialIndex : 0
+
+ } );
+
+ },
+
+ clearGroups: function () {
+
+ this.groups = [];
+
+ },
+
+ setDrawRange: function ( start, count ) {
+
+ this.drawRange.start = start;
+ this.drawRange.count = count;
+
+ },
+
+ applyMatrix4: function ( matrix ) {
+
+ const position = this.attributes.position;
+
+ if ( position !== undefined ) {
+
+ position.applyMatrix4( matrix );
+
+ position.needsUpdate = true;
+
+ }
+
+ const normal = this.attributes.normal;
+
+ if ( normal !== undefined ) {
+
+ const normalMatrix = new Matrix3().getNormalMatrix( matrix );
+
+ normal.applyNormalMatrix( normalMatrix );
+
+ normal.needsUpdate = true;
+
+ }
+
+ const tangent = this.attributes.tangent;
+
+ if ( tangent !== undefined ) {
+
+ tangent.transformDirection( matrix );
+
+ tangent.needsUpdate = true;
+
+ }
+
+ if ( this.boundingBox !== null ) {
+
+ this.computeBoundingBox();
+
+ }
+
+ if ( this.boundingSphere !== null ) {
+
+ this.computeBoundingSphere();
+
+ }
+
+ return this;
+
+ },
+
+ rotateX: function ( angle ) {
+
+ // rotate geometry around world x-axis
+
+ _m1$2.makeRotationX( angle );
+
+ this.applyMatrix4( _m1$2 );
+
+ return this;
+
+ },
+
+ rotateY: function ( angle ) {
+
+ // rotate geometry around world y-axis
+
+ _m1$2.makeRotationY( angle );
+
+ this.applyMatrix4( _m1$2 );
+
+ return this;
+
+ },
+
+ rotateZ: function ( angle ) {
+
+ // rotate geometry around world z-axis
+
+ _m1$2.makeRotationZ( angle );
+
+ this.applyMatrix4( _m1$2 );
+
+ return this;
+
+ },
+
+ translate: function ( x, y, z ) {
+
+ // translate geometry
+
+ _m1$2.makeTranslation( x, y, z );
+
+ this.applyMatrix4( _m1$2 );
+
+ return this;
+
+ },
+
+ scale: function ( x, y, z ) {
+
+ // scale geometry
+
+ _m1$2.makeScale( x, y, z );
+
+ this.applyMatrix4( _m1$2 );
+
+ return this;
+
+ },
+
+ lookAt: function ( vector ) {
+
+ _obj.lookAt( vector );
+
+ _obj.updateMatrix();
+
+ this.applyMatrix4( _obj.matrix );
+
+ return this;
+
+ },
+
+ center: function () {
+
+ this.computeBoundingBox();
+
+ this.boundingBox.getCenter( _offset ).negate();
+
+ this.translate( _offset.x, _offset.y, _offset.z );
+
+ return this;
+
+ },
+
+ setFromObject: function ( object ) {
+
+ // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );
+
+ const geometry = object.geometry;
+
+ if ( object.isPoints || object.isLine ) {
+
+ const positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );
+ const colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );
+
+ this.setAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );
+ this.setAttribute( 'color', colors.copyColorsArray( geometry.colors ) );
+
+ if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {
+
+ const lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );
+
+ this.setAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );
+
+ }
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ } else if ( object.isMesh ) {
+
+ if ( geometry && geometry.isGeometry ) {
+
+ this.fromGeometry( geometry );
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ setFromPoints: function ( points ) {
+
+ const position = [];
+
+ for ( let i = 0, l = points.length; i < l; i ++ ) {
+
+ const point = points[ i ];
+ position.push( point.x, point.y, point.z || 0 );
+
+ }
+
+ this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
+
+ return this;
+
+ },
+
+ updateFromObject: function ( object ) {
+
+ let geometry = object.geometry;
+
+ if ( object.isMesh ) {
+
+ let direct = geometry.__directGeometry;
+
+ if ( geometry.elementsNeedUpdate === true ) {
+
+ direct = undefined;
+ geometry.elementsNeedUpdate = false;
+
+ }
+
+ if ( direct === undefined ) {
+
+ return this.fromGeometry( geometry );
+
+ }
+
+ direct.verticesNeedUpdate = geometry.verticesNeedUpdate;
+ direct.normalsNeedUpdate = geometry.normalsNeedUpdate;
+ direct.colorsNeedUpdate = geometry.colorsNeedUpdate;
+ direct.uvsNeedUpdate = geometry.uvsNeedUpdate;
+ direct.groupsNeedUpdate = geometry.groupsNeedUpdate;
+
+ geometry.verticesNeedUpdate = false;
+ geometry.normalsNeedUpdate = false;
+ geometry.colorsNeedUpdate = false;
+ geometry.uvsNeedUpdate = false;
+ geometry.groupsNeedUpdate = false;
+
+ geometry = direct;
+
+ }
+
+ if ( geometry.verticesNeedUpdate === true ) {
+
+ const attribute = this.attributes.position;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.vertices );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.verticesNeedUpdate = false;
+
+ }
+
+ if ( geometry.normalsNeedUpdate === true ) {
+
+ const attribute = this.attributes.normal;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector3sArray( geometry.normals );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.normalsNeedUpdate = false;
+
+ }
+
+ if ( geometry.colorsNeedUpdate === true ) {
+
+ const attribute = this.attributes.color;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyColorsArray( geometry.colors );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.colorsNeedUpdate = false;
+
+ }
+
+ if ( geometry.uvsNeedUpdate ) {
+
+ const attribute = this.attributes.uv;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyVector2sArray( geometry.uvs );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.uvsNeedUpdate = false;
+
+ }
+
+ if ( geometry.lineDistancesNeedUpdate ) {
+
+ const attribute = this.attributes.lineDistance;
+
+ if ( attribute !== undefined ) {
+
+ attribute.copyArray( geometry.lineDistances );
+ attribute.needsUpdate = true;
+
+ }
+
+ geometry.lineDistancesNeedUpdate = false;
+
+ }
+
+ if ( geometry.groupsNeedUpdate ) {
+
+ geometry.computeGroups( object.geometry );
+ this.groups = geometry.groups;
+
+ geometry.groupsNeedUpdate = false;
+
+ }
+
+ return this;
+
+ },
+
+ fromGeometry: function ( geometry ) {
+
+ geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );
+
+ return this.fromDirectGeometry( geometry.__directGeometry );
+
+ },
+
+ fromDirectGeometry: function ( geometry ) {
+
+ const positions = new Float32Array( geometry.vertices.length * 3 );
+ this.setAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );
+
+ if ( geometry.normals.length > 0 ) {
+
+ const normals = new Float32Array( geometry.normals.length * 3 );
+ this.setAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );
+
+ }
+
+ if ( geometry.colors.length > 0 ) {
+
+ const colors = new Float32Array( geometry.colors.length * 3 );
+ this.setAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );
+
+ }
+
+ if ( geometry.uvs.length > 0 ) {
+
+ const uvs = new Float32Array( geometry.uvs.length * 2 );
+ this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );
+
+ }
+
+ if ( geometry.uvs2.length > 0 ) {
+
+ const uvs2 = new Float32Array( geometry.uvs2.length * 2 );
+ this.setAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );
+
+ }
+
+ // groups
+
+ this.groups = geometry.groups;
+
+ // morphs
+
+ for ( const name in geometry.morphTargets ) {
+
+ const array = [];
+ const morphTargets = geometry.morphTargets[ name ];
+
+ for ( let i = 0, l = morphTargets.length; i < l; i ++ ) {
+
+ const morphTarget = morphTargets[ i ];
+
+ const attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 );
+ attribute.name = morphTarget.name;
+
+ array.push( attribute.copyVector3sArray( morphTarget.data ) );
+
+ }
+
+ this.morphAttributes[ name ] = array;
+
+ }
+
+ // skinning
+
+ if ( geometry.skinIndices.length > 0 ) {
+
+ const skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );
+ this.setAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );
+
+ }
+
+ if ( geometry.skinWeights.length > 0 ) {
+
+ const skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );
+ this.setAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );
+
+ }
+
+ //
+
+ if ( geometry.boundingSphere !== null ) {
+
+ this.boundingSphere = geometry.boundingSphere.clone();
+
+ }
+
+ if ( geometry.boundingBox !== null ) {
+
+ this.boundingBox = geometry.boundingBox.clone();
+
+ }
+
+ return this;
+
+ },
+
+ computeBoundingBox: function () {
+
+ if ( this.boundingBox === null ) {
+
+ this.boundingBox = new Box3();
+
+ }
+
+ const position = this.attributes.position;
+ const morphAttributesPosition = this.morphAttributes.position;
+
+ if ( position && position.isGLBufferAttribute ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this );
+
+ this.boundingBox.set(
+ new Vector3( - Infinity, - Infinity, - Infinity ),
+ new Vector3( + Infinity, + Infinity, + Infinity )
+ );
+
+ return;
+
+ }
+
+ if ( position !== undefined ) {
+
+ this.boundingBox.setFromBufferAttribute( position );
+
+ // process morph attributes if present
+
+ if ( morphAttributesPosition ) {
+
+ for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
+
+ const morphAttribute = morphAttributesPosition[ i ];
+ _box$2.setFromBufferAttribute( morphAttribute );
+
+ if ( this.morphTargetsRelative ) {
+
+ _vector$4.addVectors( this.boundingBox.min, _box$2.min );
+ this.boundingBox.expandByPoint( _vector$4 );
+
+ _vector$4.addVectors( this.boundingBox.max, _box$2.max );
+ this.boundingBox.expandByPoint( _vector$4 );
+
+ } else {
+
+ this.boundingBox.expandByPoint( _box$2.min );
+ this.boundingBox.expandByPoint( _box$2.max );
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ this.boundingBox.makeEmpty();
+
+ }
+
+ if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ },
+
+ computeBoundingSphere: function () {
+
+ if ( this.boundingSphere === null ) {
+
+ this.boundingSphere = new Sphere();
+
+ }
+
+ const position = this.attributes.position;
+ const morphAttributesPosition = this.morphAttributes.position;
+
+ if ( position && position.isGLBufferAttribute ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this );
+
+ this.boundingSphere.set( new Vector3(), Infinity );
+
+ return;
+
+ }
+
+ if ( position ) {
+
+ // first, find the center of the bounding sphere
+
+ const center = this.boundingSphere.center;
+
+ _box$2.setFromBufferAttribute( position );
+
+ // process morph attributes if present
+
+ if ( morphAttributesPosition ) {
+
+ for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
+
+ const morphAttribute = morphAttributesPosition[ i ];
+ _boxMorphTargets.setFromBufferAttribute( morphAttribute );
+
+ if ( this.morphTargetsRelative ) {
+
+ _vector$4.addVectors( _box$2.min, _boxMorphTargets.min );
+ _box$2.expandByPoint( _vector$4 );
+
+ _vector$4.addVectors( _box$2.max, _boxMorphTargets.max );
+ _box$2.expandByPoint( _vector$4 );
+
+ } else {
+
+ _box$2.expandByPoint( _boxMorphTargets.min );
+ _box$2.expandByPoint( _boxMorphTargets.max );
+
+ }
+
+ }
+
+ }
+
+ _box$2.getCenter( center );
+
+ // second, try to find a boundingSphere with a radius smaller than the
+ // boundingSphere of the boundingBox: sqrt(3) smaller in the best case
+
+ let maxRadiusSq = 0;
+
+ for ( let i = 0, il = position.count; i < il; i ++ ) {
+
+ _vector$4.fromBufferAttribute( position, i );
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) );
+
+ }
+
+ // process morph attributes if present
+
+ if ( morphAttributesPosition ) {
+
+ for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) {
+
+ const morphAttribute = morphAttributesPosition[ i ];
+ const morphTargetsRelative = this.morphTargetsRelative;
+
+ for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) {
+
+ _vector$4.fromBufferAttribute( morphAttribute, j );
+
+ if ( morphTargetsRelative ) {
+
+ _offset.fromBufferAttribute( position, j );
+ _vector$4.add( _offset );
+
+ }
+
+ maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) );
+
+ }
+
+ }
+
+ }
+
+ this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
+
+ if ( isNaN( this.boundingSphere.radius ) ) {
+
+ console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this );
+
+ }
+
+ }
+
+ },
+
+ computeFaceNormals: function () {
+
+ // backwards compatibility
+
+ },
+
+ computeVertexNormals: function () {
+
+ const index = this.index;
+ const positionAttribute = this.getAttribute( 'position' );
+
+ if ( positionAttribute !== undefined ) {
+
+ let normalAttribute = this.getAttribute( 'normal' );
+
+ if ( normalAttribute === undefined ) {
+
+ normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 );
+ this.setAttribute( 'normal', normalAttribute );
+
+ } else {
+
+ // reset existing normals to zero
+
+ for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) {
+
+ normalAttribute.setXYZ( i, 0, 0, 0 );
+
+ }
+
+ }
+
+ const pA = new Vector3(), pB = new Vector3(), pC = new Vector3();
+ const nA = new Vector3(), nB = new Vector3(), nC = new Vector3();
+ const cb = new Vector3(), ab = new Vector3();
+
+ // indexed elements
+
+ if ( index ) {
+
+ for ( let i = 0, il = index.count; i < il; i += 3 ) {
+
+ const vA = index.getX( i + 0 );
+ const vB = index.getX( i + 1 );
+ const vC = index.getX( i + 2 );
+
+ pA.fromBufferAttribute( positionAttribute, vA );
+ pB.fromBufferAttribute( positionAttribute, vB );
+ pC.fromBufferAttribute( positionAttribute, vC );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ nA.fromBufferAttribute( normalAttribute, vA );
+ nB.fromBufferAttribute( normalAttribute, vB );
+ nC.fromBufferAttribute( normalAttribute, vC );
+
+ nA.add( cb );
+ nB.add( cb );
+ nC.add( cb );
+
+ normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z );
+ normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z );
+ normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z );
+
+ }
+
+ } else {
+
+ // non-indexed elements (unconnected triangle soup)
+
+ for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) {
+
+ pA.fromBufferAttribute( positionAttribute, i + 0 );
+ pB.fromBufferAttribute( positionAttribute, i + 1 );
+ pC.fromBufferAttribute( positionAttribute, i + 2 );
+
+ cb.subVectors( pC, pB );
+ ab.subVectors( pA, pB );
+ cb.cross( ab );
+
+ normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z );
+ normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z );
+ normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z );
+
+ }
+
+ }
+
+ this.normalizeNormals();
+
+ normalAttribute.needsUpdate = true;
+
+ }
+
+ },
+
+ merge: function ( geometry, offset ) {
+
+ if ( ! ( geometry && geometry.isBufferGeometry ) ) {
+
+ console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );
+ return;
+
+ }
+
+ if ( offset === undefined ) {
+
+ offset = 0;
+
+ console.warn(
+ 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '
+ + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'
+ );
+
+ }
+
+ const attributes = this.attributes;
+
+ for ( const key in attributes ) {
+
+ if ( geometry.attributes[ key ] === undefined ) continue;
+
+ const attribute1 = attributes[ key ];
+ const attributeArray1 = attribute1.array;
+
+ const attribute2 = geometry.attributes[ key ];
+ const attributeArray2 = attribute2.array;
+
+ const attributeOffset = attribute2.itemSize * offset;
+ const length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset );
+
+ for ( let i = 0, j = attributeOffset; i < length; i ++, j ++ ) {
+
+ attributeArray1[ j ] = attributeArray2[ i ];
+
+ }
+
+ }
+
+ return this;
+
+ },
+
+ normalizeNormals: function () {
+
+ const normals = this.attributes.normal;
+
+ for ( let i = 0, il = normals.count; i < il; i ++ ) {
+
+ _vector$4.fromBufferAttribute( normals, i );
+
+ _vector$4.normalize();
+
+ normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z );
+
+ }
+
+ },
+
+ toNonIndexed: function () {
+
+ function convertBufferAttribute( attribute, indices ) {
+
+ const array = attribute.array;
+ const itemSize = attribute.itemSize;
+ const normalized = attribute.normalized;
+
+ const array2 = new array.constructor( indices.length * itemSize );
+
+ let index = 0, index2 = 0;
+
+ for ( let i = 0, l = indices.length; i < l; i ++ ) {
+
+ index = indices[ i ] * itemSize;
+
+ for ( let j = 0; j < itemSize; j ++ ) {
+
+ array2[ index2 ++ ] = array[ index ++ ];
+
+ }
+
+ }
+
+ return new BufferAttribute( array2, itemSize, normalized );
+
+ }
+
+ //
+
+ if ( this.index === null ) {
+
+ console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );
+ return this;
+
+ }
+
+ const geometry2 = new BufferGeometry();
+
+ const indices = this.index.array;
+ const attributes = this.attributes;
+
+ // attributes
+
+ for ( const name in attributes ) {
+
+ const attribute = attributes[ name ];
+
+ const newAttribute = convertBufferAttribute( attribute, indices );
+
+ geometry2.setAttribute( name, newAttribute );
+
+ }
+
+ // morph attributes
+
+ const morphAttributes = this.morphAttributes;
+
+ for ( const name in morphAttributes ) {
+
+ const morphArray = [];
+ const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
+
+ for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
+
+ const attribute = morphAttribute[ i ];
+
+ const newAttribute = convertBufferAttribute( attribute, indices );
+
+ morphArray.push( newAttribute );
+
+ }
+
+ geometry2.morphAttributes[ name ] = morphArray;
+
+ }
+
+ geometry2.morphTargetsRelative = this.morphTargetsRelative;
+
+ // groups
+
+ const groups = this.groups;
+
+ for ( let i = 0, l = groups.length; i < l; i ++ ) {
+
+ const group = groups[ i ];
+ geometry2.addGroup( group.start, group.count, group.materialIndex );
+
+ }
+
+ return geometry2;
+
+ },
+
+ toJSON: function () {
+
+ const data = {
+ metadata: {
+ version: 4.5,
+ type: 'BufferGeometry',
+ generator: 'BufferGeometry.toJSON'
+ }
+ };
+
+ // standard BufferGeometry serialization
+
+ data.uuid = this.uuid;
+ data.type = this.type;
+ if ( this.name !== '' ) data.name = this.name;
+ if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData;
+
+ if ( this.parameters !== undefined ) {
+
+ const parameters = this.parameters;
+
+ for ( const key in parameters ) {
+
+ if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];
+
+ }
+
+ return data;
+
+ }
+
+ data.data = { attributes: {} };
+
+ const index = this.index;
+
+ if ( index !== null ) {
+
+ data.data.index = {
+ type: index.array.constructor.name,
+ array: Array.prototype.slice.call( index.array )
+ };
+
+ }
+
+ const attributes = this.attributes;
+
+ for ( const key in attributes ) {
+
+ const attribute = attributes[ key ];
+
+ const attributeData = attribute.toJSON( data.data );
+
+ if ( attribute.name !== '' ) attributeData.name = attribute.name;
+
+ data.data.attributes[ key ] = attributeData;
+
+ }
+
+ const morphAttributes = {};
+ let hasMorphAttributes = false;
+
+ for ( const key in this.morphAttributes ) {
+
+ const attributeArray = this.morphAttributes[ key ];
+
+ const array = [];
+
+ for ( let i = 0, il = attributeArray.length; i < il; i ++ ) {
+
+ const attribute = attributeArray[ i ];
+
+ const attributeData = attribute.toJSON( data.data );
+
+ if ( attribute.name !== '' ) attributeData.name = attribute.name;
+
+ array.push( attributeData );
+
+ }
+
+ if ( array.length > 0 ) {
+
+ morphAttributes[ key ] = array;
+
+ hasMorphAttributes = true;
+
+ }
+
+ }
+
+ if ( hasMorphAttributes ) {
+
+ data.data.morphAttributes = morphAttributes;
+ data.data.morphTargetsRelative = this.morphTargetsRelative;
+
+ }
+
+ const groups = this.groups;
+
+ if ( groups.length > 0 ) {
+
+ data.data.groups = JSON.parse( JSON.stringify( groups ) );
+
+ }
+
+ const boundingSphere = this.boundingSphere;
+
+ if ( boundingSphere !== null ) {
+
+ data.data.boundingSphere = {
+ center: boundingSphere.center.toArray(),
+ radius: boundingSphere.radius
+ };
+
+ }
+
+ return data;
+
+ },
+
+ clone: function () {
+
+ /*
+ // Handle primitives
+
+ const parameters = this.parameters;
+
+ if ( parameters !== undefined ) {
+
+ const values = [];
+
+ for ( const key in parameters ) {
+
+ values.push( parameters[ key ] );
+
+ }
+
+ const geometry = Object.create( this.constructor.prototype );
+ this.constructor.apply( geometry, values );
+ return geometry;
+
+ }
+
+ return new this.constructor().copy( this );
+ */
+
+ return new BufferGeometry().copy( this );
+
+ },
+
+ copy: function ( source ) {
+
+ // reset
+
+ this.index = null;
+ this.attributes = {};
+ this.morphAttributes = {};
+ this.groups = [];
+ this.boundingBox = null;
+ this.boundingSphere = null;
+
+ // used for storing cloned, shared data
+
+ const data = {};
+
+ // name
+
+ this.name = source.name;
+
+ // index
+
+ const index = source.index;
+
+ if ( index !== null ) {
+
+ this.setIndex( index.clone( data ) );
+
+ }
+
+ // attributes
+
+ const attributes = source.attributes;
+
+ for ( const name in attributes ) {
+
+ const attribute = attributes[ name ];
+ this.setAttribute( name, attribute.clone( data ) );
+
+ }
+
+ // morph attributes
+
+ const morphAttributes = source.morphAttributes;
+
+ for ( const name in morphAttributes ) {
+
+ const array = [];
+ const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes
+
+ for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) {
+
+ array.push( morphAttribute[ i ].clone( data ) );
+
+ }
+
+ this.morphAttributes[ name ] = array;
+
+ }
+
+ this.morphTargetsRelative = source.morphTargetsRelative;
+
+ // groups
+
+ const groups = source.groups;
+
+ for ( let i = 0, l = groups.length; i < l; i ++ ) {
+
+ const group = groups[ i ];
+ this.addGroup( group.start, group.count, group.materialIndex );
+
+ }
+
+ // bounding box
+
+ const boundingBox = source.boundingBox;
+
+ if ( boundingBox !== null ) {
+
+ this.boundingBox = boundingBox.clone();
+
+ }
+
+ // bounding sphere
+
+ const boundingSphere = source.boundingSphere;
+
+ if ( boundingSphere !== null ) {
+
+ this.boundingSphere = boundingSphere.clone();
+
+ }
+
+ // draw range
+
+ this.drawRange.start = source.drawRange.start;
+ this.drawRange.count = source.drawRange.count;
+
+ // user data
+
+ this.userData = source.userData;
+
+ return this;
+
+ },
+
+ dispose: function () {
+
+ this.dispatchEvent( { type: 'dispose' } );
+
+ }
+
+} );
+
+const _inverseMatrix = new Matrix4();
+const _ray = new Ray();
+const _sphere = new Sphere();
+
+const _vA = new Vector3();
+const _vB = new Vector3();
+const _vC = new Vector3();
+
+const _tempA = new Vector3();
+const _tempB = new Vector3();
+const _tempC = new Vector3();
+
+const _morphA = new Vector3();
+const _morphB = new Vector3();
+const _morphC = new Vector3();
+
+const _uvA = new Vector2();
+const _uvB = new Vector2();
+const _uvC = new Vector2();
+
+const _intersectionPoint = new Vector3();
+const _intersectionPointWorld = new Vector3();
+
+function Mesh( geometry, material ) {
+
+ Object3D.call( this );
+
+ this.type = 'Mesh';
+
+ this.geometry = geometry !== undefined ? geometry : new BufferGeometry();
+ this.material = material !== undefined ? material : new MeshBasicMaterial();
+
+ this.updateMorphTargets();
+
+}
+
+Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+ constructor: Mesh,
+
+ isMesh: true,
+
+ copy: function ( source ) {
+
+ Object3D.prototype.copy.call( this, source );
+
+ if ( source.morphTargetInfluences !== undefined ) {
+
+ this.morphTargetInfluences = source.morphTargetInfluences.slice();
+
+ }
+
+ if ( source.morphTargetDictionary !== undefined ) {
+
+ this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );
+
+ }
+
+ this.material = source.material;
+ this.geometry = source.geometry;
+
+ return this;
+
+ },
+
+ updateMorphTargets: function () {
+
+ const geometry = this.geometry;
+
+ if ( geometry.isBufferGeometry ) {
+
+ const morphAttributes = geometry.morphAttributes;
+ const keys = Object.keys( morphAttributes );
+
+ if ( keys.length > 0 ) {
+
+ const morphAttribute = morphAttributes[ keys[ 0 ] ];
+
+ if ( morphAttribute !== undefined ) {
+
+ this.morphTargetInfluences = [];
+ this.morphTargetDictionary = {};
+
+ for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) {
+
+ const name = morphAttribute[ m ].name || String( m );
+
+ this.morphTargetInfluences.push( 0 );
+ this.morphTargetDictionary[ name ] = m;
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ const morphTargets = geometry.morphTargets;
+
+ if ( morphTargets !== undefined && morphTargets.length > 0 ) {
+
+ console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' );
+
+ }
+
+ }
+
+ },
+
+ raycast: function ( raycaster, intersects ) {
+
+ const geometry = this.geometry;
+ const material = this.material;
+ const matrixWorld = this.matrixWorld;
+
+ if ( material === undefined ) return;
+
+ // Checking boundingSphere distance to ray
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ _sphere.copy( geometry.boundingSphere );
+ _sphere.applyMatrix4( matrixWorld );
+
+ if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return;
+
+ //
+
+ _inverseMatrix.getInverse( matrixWorld );
+ _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix );
+
+ // Check boundingBox before continuing
+
+ if ( geometry.boundingBox !== null ) {
+
+ if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return;
+
+ }
+
+ let intersection;
+
+ if ( geometry.isBufferGeometry ) {
+
+ const index = geometry.index;
+ const position = geometry.attributes.position;
+ const morphPosition = geometry.morphAttributes.position;
+ const morphTargetsRelative = geometry.morphTargetsRelative;
+ const uv = geometry.attributes.uv;
+ const uv2 = geometry.attributes.uv2;
+ const groups = geometry.groups;
+ const drawRange = geometry.drawRange;
+
+ if ( index !== null ) {
+
+ // indexed buffer geometry
+
+ if ( Array.isArray( material ) ) {
+
+ for ( let i = 0, il = groups.length; i < il; i ++ ) {
+
+ const group = groups[ i ];
+ const groupMaterial = material[ group.materialIndex ];
+
+ const start = Math.max( group.start, drawRange.start );
+ const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
+
+ for ( let j = start, jl = end; j < jl; j += 3 ) {
+
+ const a = index.getX( j );
+ const b = index.getX( j + 1 );
+ const c = index.getX( j + 2 );
+
+ intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics
+ intersection.face.materialIndex = group.materialIndex;
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ const start = Math.max( 0, drawRange.start );
+ const end = Math.min( index.count, ( drawRange.start + drawRange.count ) );
+
+ for ( let i = start, il = end; i < il; i += 3 ) {
+
+ const a = index.getX( i );
+ const b = index.getX( i + 1 );
+ const c = index.getX( i + 2 );
+
+ intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ } else if ( position !== undefined ) {
+
+ // non-indexed buffer geometry
+
+ if ( Array.isArray( material ) ) {
+
+ for ( let i = 0, il = groups.length; i < il; i ++ ) {
+
+ const group = groups[ i ];
+ const groupMaterial = material[ group.materialIndex ];
+
+ const start = Math.max( group.start, drawRange.start );
+ const end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) );
+
+ for ( let j = start, jl = end; j < jl; j += 3 ) {
+
+ const a = j;
+ const b = j + 1;
+ const c = j + 2;
+
+ intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics
+ intersection.face.materialIndex = group.materialIndex;
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ } else {
+
+ const start = Math.max( 0, drawRange.start );
+ const end = Math.min( position.count, ( drawRange.start + drawRange.count ) );
+
+ for ( let i = start, il = end; i < il; i += 3 ) {
+
+ const a = i;
+ const b = i + 1;
+ const c = i + 2;
+
+ intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c );
+
+ if ( intersection ) {
+
+ intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ } else if ( geometry.isGeometry ) {
+
+ const isMultiMaterial = Array.isArray( material );
+
+ const vertices = geometry.vertices;
+ const faces = geometry.faces;
+ let uvs;
+
+ const faceVertexUvs = geometry.faceVertexUvs[ 0 ];
+ if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;
+
+ for ( let f = 0, fl = faces.length; f < fl; f ++ ) {
+
+ const face = faces[ f ];
+ const faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material;
+
+ if ( faceMaterial === undefined ) continue;
+
+ const fvA = vertices[ face.a ];
+ const fvB = vertices[ face.b ];
+ const fvC = vertices[ face.c ];
+
+ intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uvs && uvs[ f ] ) {
+
+ const uvs_f = uvs[ f ];
+ _uvA.copy( uvs_f[ 0 ] );
+ _uvB.copy( uvs_f[ 1 ] );
+ _uvC.copy( uvs_f[ 2 ] );
+
+ intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2() );
+
+ }
+
+ intersection.face = face;
+ intersection.faceIndex = f;
+ intersects.push( intersection );
+
+ }
+
+ }
+
+ }
+
+ }
+
+} );
+
+function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {
+
+ let intersect;
+
+ if ( material.side === BackSide ) {
+
+ intersect = ray.intersectTriangle( pC, pB, pA, true, point );
+
+ } else {
+
+ intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );
+
+ }
+
+ if ( intersect === null ) return null;
+
+ _intersectionPointWorld.copy( point );
+ _intersectionPointWorld.applyMatrix4( object.matrixWorld );
+
+ const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld );
+
+ if ( distance < raycaster.near || distance > raycaster.far ) return null;
+
+ return {
+ distance: distance,
+ point: _intersectionPointWorld.clone(),
+ object: object
+ };
+
+}
+
+function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, morphTargetsRelative, uv, uv2, a, b, c ) {
+
+ _vA.fromBufferAttribute( position, a );
+ _vB.fromBufferAttribute( position, b );
+ _vC.fromBufferAttribute( position, c );
+
+ const morphInfluences = object.morphTargetInfluences;
+
+ if ( material.morphTargets && morphPosition && morphInfluences ) {
+
+ _morphA.set( 0, 0, 0 );
+ _morphB.set( 0, 0, 0 );
+ _morphC.set( 0, 0, 0 );
+
+ for ( let i = 0, il = morphPosition.length; i < il; i ++ ) {
+
+ const influence = morphInfluences[ i ];
+ const morphAttribute = morphPosition[ i ];
+
+ if ( influence === 0 ) continue;
+
+ _tempA.fromBufferAttribute( morphAttribute, a );
+ _tempB.fromBufferAttribute( morphAttribute, b );
+ _tempC.fromBufferAttribute( morphAttribute, c );
+
+ if ( morphTargetsRelative ) {
+
+ _morphA.addScaledVector( _tempA, influence );
+ _morphB.addScaledVector( _tempB, influence );
+ _morphC.addScaledVector( _tempC, influence );
+
+ } else {
+
+ _morphA.addScaledVector( _tempA.sub( _vA ), influence );
+ _morphB.addScaledVector( _tempB.sub( _vB ), influence );
+ _morphC.addScaledVector( _tempC.sub( _vC ), influence );
+
+ }
+
+ }
+
+ _vA.add( _morphA );
+ _vB.add( _morphB );
+ _vC.add( _morphC );
+
+ }
+
+ if ( object.isSkinnedMesh ) {
+
+ object.boneTransform( a, _vA );
+ object.boneTransform( b, _vB );
+ object.boneTransform( c, _vC );
+
+ }
+
+ const intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint );
+
+ if ( intersection ) {
+
+ if ( uv ) {
+
+ _uvA.fromBufferAttribute( uv, a );
+ _uvB.fromBufferAttribute( uv, b );
+ _uvC.fromBufferAttribute( uv, c );
+
+ intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );
+
+ }
+
+ if ( uv2 ) {
+
+ _uvA.fromBufferAttribute( uv2, a );
+ _uvB.fromBufferAttribute( uv2, b );
+ _uvC.fromBufferAttribute( uv2, c );
+
+ intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );
+
+ }
+
+ const face = new Face3( a, b, c );
+ Triangle.getNormal( _vA, _vB, _vC, face.normal );
+
+ intersection.face = face;
+
+ }
+
+ return intersection;
+
+}
+
+class BoxBufferGeometry extends BufferGeometry {
+
+ constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {
+
+ super();
+
+ this.type = 'BoxBufferGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ depth: depth,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments,
+ depthSegments: depthSegments
+ };
+
+ const scope = this;
+
+ // segments
+
+ widthSegments = Math.floor( widthSegments );
+ heightSegments = Math.floor( heightSegments );
+ depthSegments = Math.floor( depthSegments );
+
+ // buffers
+
+ const indices = [];
+ const vertices = [];
+ const normals = [];
+ const uvs = [];
+
+ // helper variables
+
+ let numberOfVertices = 0;
+ let groupStart = 0;
+
+ // build each side of the box geometry
+
+ buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px
+ buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx
+ buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py
+ buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny
+ buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz
+ buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz
+
+ // build geometry
+
+ this.setIndex( indices );
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+ this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+ this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+
+ function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {
+
+ const segmentWidth = width / gridX;
+ const segmentHeight = height / gridY;
+
+ const widthHalf = width / 2;
+ const heightHalf = height / 2;
+ const depthHalf = depth / 2;
+
+ const gridX1 = gridX + 1;
+ const gridY1 = gridY + 1;
+
+ let vertexCounter = 0;
+ let groupCount = 0;
+
+ const vector = new Vector3();
+
+ // generate vertices, normals and uvs
+
+ for ( let iy = 0; iy < gridY1; iy ++ ) {
+
+ const y = iy * segmentHeight - heightHalf;
+
+ for ( let ix = 0; ix < gridX1; ix ++ ) {
+
+ const x = ix * segmentWidth - widthHalf;
+
+ // set values to correct vector component
+
+ vector[ u ] = x * udir;
+ vector[ v ] = y * vdir;
+ vector[ w ] = depthHalf;
+
+ // now apply vector to vertex buffer
+
+ vertices.push( vector.x, vector.y, vector.z );
+
+ // set values to correct vector component
+
+ vector[ u ] = 0;
+ vector[ v ] = 0;
+ vector[ w ] = depth > 0 ? 1 : - 1;
+
+ // now apply vector to normal buffer
+
+ normals.push( vector.x, vector.y, vector.z );
+
+ // uvs
+
+ uvs.push( ix / gridX );
+ uvs.push( 1 - ( iy / gridY ) );
+
+ // counters
+
+ vertexCounter += 1;
+
+ }
+
+ }
+
+ // indices
+
+ // 1. you need three indices to draw a single face
+ // 2. a single segment consists of two faces
+ // 3. so we need to generate six (2*3) indices per segment
+
+ for ( let iy = 0; iy < gridY; iy ++ ) {
+
+ for ( let ix = 0; ix < gridX; ix ++ ) {
+
+ const a = numberOfVertices + ix + gridX1 * iy;
+ const b = numberOfVertices + ix + gridX1 * ( iy + 1 );
+ const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );
+ const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;
+
+ // faces
+
+ indices.push( a, b, d );
+ indices.push( b, c, d );
+
+ // increase counter
+
+ groupCount += 6;
+
+ }
+
+ }
+
+ // add a group to the geometry. this will ensure multi material support
+
+ scope.addGroup( groupStart, groupCount, materialIndex );
+
+ // calculate new start value for groups
+
+ groupStart += groupCount;
+
+ // update total number of vertices
+
+ numberOfVertices += vertexCounter;
+
+ }
+
+ }
+
+}
+
+/**
+ * Uniform Utilities
+ */
+
+function cloneUniforms( src ) {
+
+ const dst = {};
+
+ for ( const u in src ) {
+
+ dst[ u ] = {};
+
+ for ( const p in src[ u ] ) {
+
+ const property = src[ u ][ p ];
+
+ if ( property && ( property.isColor ||
+ property.isMatrix3 || property.isMatrix4 ||
+ property.isVector2 || property.isVector3 || property.isVector4 ||
+ property.isTexture ) ) {
+
+ dst[ u ][ p ] = property.clone();
+
+ } else if ( Array.isArray( property ) ) {
+
+ dst[ u ][ p ] = property.slice();
+
+ } else {
+
+ dst[ u ][ p ] = property;
+
+ }
+
+ }
+
+ }
+
+ return dst;
+
+}
+
+function mergeUniforms( uniforms ) {
+
+ const merged = {};
+
+ for ( let u = 0; u < uniforms.length; u ++ ) {
+
+ const tmp = cloneUniforms( uniforms[ u ] );
+
+ for ( const p in tmp ) {
+
+ merged[ p ] = tmp[ p ];
+
+ }
+
+ }
+
+ return merged;
+
+}
+
+// Legacy
+
+const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms };
+
+var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}";
+
+var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}";
+
+/**
+ * parameters = {
+ * defines: { "label" : "value" },
+ * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } },
+ *
+ * fragmentShader: ,
+ * vertexShader: ,
+ *
+ * wireframe: ,
+ * wireframeLinewidth: ,
+ *
+ * lights: ,
+ *
+ * skinning: ,
+ * morphTargets: ,
+ * morphNormals:
+ * }
+ */
+
+function ShaderMaterial( parameters ) {
+
+ Material.call( this );
+
+ this.type = 'ShaderMaterial';
+
+ this.defines = {};
+ this.uniforms = {};
+
+ this.vertexShader = default_vertex;
+ this.fragmentShader = default_fragment;
+
+ this.linewidth = 1;
+
+ this.wireframe = false;
+ this.wireframeLinewidth = 1;
+
+ this.fog = false; // set to use scene fog
+ this.lights = false; // set to use scene lights
+ this.clipping = false; // set to use user-defined clipping planes
+
+ this.skinning = false; // set to use skinning attribute streams
+ this.morphTargets = false; // set to use morph targets
+ this.morphNormals = false; // set to use morph normals
+
+ this.extensions = {
+ derivatives: false, // set to use derivatives
+ fragDepth: false, // set to use fragment depth values
+ drawBuffers: false, // set to use draw buffers
+ shaderTextureLOD: false // set to use shader texture LOD
+ };
+
+ // When rendered geometry doesn't include these attributes but the material does,
+ // use these default values in WebGL. This avoids errors when buffer data is missing.
+ this.defaultAttributeValues = {
+ 'color': [ 1, 1, 1 ],
+ 'uv': [ 0, 0 ],
+ 'uv2': [ 0, 0 ]
+ };
+
+ this.index0AttributeName = undefined;
+ this.uniformsNeedUpdate = false;
+
+ this.glslVersion = null;
+
+ if ( parameters !== undefined ) {
+
+ if ( parameters.attributes !== undefined ) {
+
+ console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );
+
+ }
+
+ this.setValues( parameters );
+
+ }
+
+}
+
+ShaderMaterial.prototype = Object.create( Material.prototype );
+ShaderMaterial.prototype.constructor = ShaderMaterial;
+
+ShaderMaterial.prototype.isShaderMaterial = true;
+
+ShaderMaterial.prototype.copy = function ( source ) {
+
+ Material.prototype.copy.call( this, source );
+
+ this.fragmentShader = source.fragmentShader;
+ this.vertexShader = source.vertexShader;
+
+ this.uniforms = cloneUniforms( source.uniforms );
+
+ this.defines = Object.assign( {}, source.defines );
+
+ this.wireframe = source.wireframe;
+ this.wireframeLinewidth = source.wireframeLinewidth;
+
+ this.lights = source.lights;
+ this.clipping = source.clipping;
+
+ this.skinning = source.skinning;
+
+ this.morphTargets = source.morphTargets;
+ this.morphNormals = source.morphNormals;
+
+ this.extensions = Object.assign( {}, source.extensions );
+
+ this.glslVersion = source.glslVersion;
+
+ return this;
+
+};
+
+ShaderMaterial.prototype.toJSON = function ( meta ) {
+
+ const data = Material.prototype.toJSON.call( this, meta );
+
+ data.glslVersion = this.glslVersion;
+ data.uniforms = {};
+
+ for ( const name in this.uniforms ) {
+
+ const uniform = this.uniforms[ name ];
+ const value = uniform.value;
+
+ if ( value && value.isTexture ) {
+
+ data.uniforms[ name ] = {
+ type: 't',
+ value: value.toJSON( meta ).uuid
+ };
+
+ } else if ( value && value.isColor ) {
+
+ data.uniforms[ name ] = {
+ type: 'c',
+ value: value.getHex()
+ };
+
+ } else if ( value && value.isVector2 ) {
+
+ data.uniforms[ name ] = {
+ type: 'v2',
+ value: value.toArray()
+ };
+
+ } else if ( value && value.isVector3 ) {
+
+ data.uniforms[ name ] = {
+ type: 'v3',
+ value: value.toArray()
+ };
+
+ } else if ( value && value.isVector4 ) {
+
+ data.uniforms[ name ] = {
+ type: 'v4',
+ value: value.toArray()
+ };
+
+ } else if ( value && value.isMatrix3 ) {
+
+ data.uniforms[ name ] = {
+ type: 'm3',
+ value: value.toArray()
+ };
+
+ } else if ( value && value.isMatrix4 ) {
+
+ data.uniforms[ name ] = {
+ type: 'm4',
+ value: value.toArray()
+ };
+
+ } else {
+
+ data.uniforms[ name ] = {
+ value: value
+ };
+
+ // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far
+
+ }
+
+ }
+
+ if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines;
+
+ data.vertexShader = this.vertexShader;
+ data.fragmentShader = this.fragmentShader;
+
+ const extensions = {};
+
+ for ( const key in this.extensions ) {
+
+ if ( this.extensions[ key ] === true ) extensions[ key ] = true;
+
+ }
+
+ if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions;
+
+ return data;
+
+};
+
+function Camera() {
+
+ Object3D.call( this );
+
+ this.type = 'Camera';
+
+ this.matrixWorldInverse = new Matrix4();
+
+ this.projectionMatrix = new Matrix4();
+ this.projectionMatrixInverse = new Matrix4();
+
+}
+
+Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+ constructor: Camera,
+
+ isCamera: true,
+
+ copy: function ( source, recursive ) {
+
+ Object3D.prototype.copy.call( this, source, recursive );
+
+ this.matrixWorldInverse.copy( source.matrixWorldInverse );
+
+ this.projectionMatrix.copy( source.projectionMatrix );
+ this.projectionMatrixInverse.copy( source.projectionMatrixInverse );
+
+ return this;
+
+ },
+
+ getWorldDirection: function ( target ) {
+
+ if ( target === undefined ) {
+
+ console.warn( 'THREE.Camera: .getWorldDirection() target is now required' );
+ target = new Vector3();
+
+ }
+
+ this.updateWorldMatrix( true, false );
+
+ const e = this.matrixWorld.elements;
+
+ return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize();
+
+ },
+
+ updateMatrixWorld: function ( force ) {
+
+ Object3D.prototype.updateMatrixWorld.call( this, force );
+
+ this.matrixWorldInverse.getInverse( this.matrixWorld );
+
+ },
+
+ updateWorldMatrix: function ( updateParents, updateChildren ) {
+
+ Object3D.prototype.updateWorldMatrix.call( this, updateParents, updateChildren );
+
+ this.matrixWorldInverse.getInverse( this.matrixWorld );
+
+ },
+
+ clone: function () {
+
+ return new this.constructor().copy( this );
+
+ }
+
+} );
+
+function PerspectiveCamera( fov, aspect, near, far ) {
+
+ Camera.call( this );
+
+ this.type = 'PerspectiveCamera';
+
+ this.fov = fov !== undefined ? fov : 50;
+ this.zoom = 1;
+
+ this.near = near !== undefined ? near : 0.1;
+ this.far = far !== undefined ? far : 2000;
+ this.focus = 10;
+
+ this.aspect = aspect !== undefined ? aspect : 1;
+ this.view = null;
+
+ this.filmGauge = 35; // width of the film (default in millimeters)
+ this.filmOffset = 0; // horizontal film offset (same unit as gauge)
+
+ this.updateProjectionMatrix();
+
+}
+
+PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {
+
+ constructor: PerspectiveCamera,
+
+ isPerspectiveCamera: true,
+
+ copy: function ( source, recursive ) {
+
+ Camera.prototype.copy.call( this, source, recursive );
+
+ this.fov = source.fov;
+ this.zoom = source.zoom;
+
+ this.near = source.near;
+ this.far = source.far;
+ this.focus = source.focus;
+
+ this.aspect = source.aspect;
+ this.view = source.view === null ? null : Object.assign( {}, source.view );
+
+ this.filmGauge = source.filmGauge;
+ this.filmOffset = source.filmOffset;
+
+ return this;
+
+ },
+
+ /**
+ * Sets the FOV by focal length in respect to the current .filmGauge.
+ *
+ * The default film gauge is 35, so that the focal length can be specified for
+ * a 35mm (full frame) camera.
+ *
+ * Values for focal length and film gauge must have the same unit.
+ */
+ setFocalLength: function ( focalLength ) {
+
+ // see http://www.bobatkins.com/photography/technical/field_of_view.html
+ const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;
+
+ this.fov = MathUtils.RAD2DEG * 2 * Math.atan( vExtentSlope );
+ this.updateProjectionMatrix();
+
+ },
+
+ /**
+ * Calculates the focal length from the current .fov and .filmGauge.
+ */
+ getFocalLength: function () {
+
+ const vExtentSlope = Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov );
+
+ return 0.5 * this.getFilmHeight() / vExtentSlope;
+
+ },
+
+ getEffectiveFOV: function () {
+
+ return MathUtils.RAD2DEG * 2 * Math.atan(
+ Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom );
+
+ },
+
+ getFilmWidth: function () {
+
+ // film not completely covered in portrait format (aspect < 1)
+ return this.filmGauge * Math.min( this.aspect, 1 );
+
+ },
+
+ getFilmHeight: function () {
+
+ // film not completely covered in landscape format (aspect > 1)
+ return this.filmGauge / Math.max( this.aspect, 1 );
+
+ },
+
+ /**
+ * Sets an offset in a larger frustum. This is useful for multi-window or
+ * multi-monitor/multi-machine setups.
+ *
+ * For example, if you have 3x2 monitors and each monitor is 1920x1080 and
+ * the monitors are in grid like this
+ *
+ * +---+---+---+
+ * | A | B | C |
+ * +---+---+---+
+ * | D | E | F |
+ * +---+---+---+
+ *
+ * then for each monitor you would call it like this
+ *
+ * const w = 1920;
+ * const h = 1080;
+ * const fullWidth = w * 3;
+ * const fullHeight = h * 2;
+ *
+ * --A--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );
+ * --B--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );
+ * --C--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );
+ * --D--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );
+ * --E--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );
+ * --F--
+ * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
+ *
+ * Note there is no reason monitors have to be the same size or in a grid.
+ */
+ setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {
+
+ this.aspect = fullWidth / fullHeight;
+
+ if ( this.view === null ) {
+
+ this.view = {
+ enabled: true,
+ fullWidth: 1,
+ fullHeight: 1,
+ offsetX: 0,
+ offsetY: 0,
+ width: 1,
+ height: 1
+ };
+
+ }
+
+ this.view.enabled = true;
+ this.view.fullWidth = fullWidth;
+ this.view.fullHeight = fullHeight;
+ this.view.offsetX = x;
+ this.view.offsetY = y;
+ this.view.width = width;
+ this.view.height = height;
+
+ this.updateProjectionMatrix();
+
+ },
+
+ clearViewOffset: function () {
+
+ if ( this.view !== null ) {
+
+ this.view.enabled = false;
+
+ }
+
+ this.updateProjectionMatrix();
+
+ },
+
+ updateProjectionMatrix: function () {
+
+ const near = this.near;
+ let top = near * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom;
+ let height = 2 * top;
+ let width = this.aspect * height;
+ let left = - 0.5 * width;
+ const view = this.view;
+
+ if ( this.view !== null && this.view.enabled ) {
+
+ const fullWidth = view.fullWidth,
+ fullHeight = view.fullHeight;
+
+ left += view.offsetX * width / fullWidth;
+ top -= view.offsetY * height / fullHeight;
+ width *= view.width / fullWidth;
+ height *= view.height / fullHeight;
+
+ }
+
+ const skew = this.filmOffset;
+ if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
+
+ this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );
+
+ this.projectionMatrixInverse.getInverse( this.projectionMatrix );
+
+ },
+
+ toJSON: function ( meta ) {
+
+ const data = Object3D.prototype.toJSON.call( this, meta );
+
+ data.object.fov = this.fov;
+ data.object.zoom = this.zoom;
+
+ data.object.near = this.near;
+ data.object.far = this.far;
+ data.object.focus = this.focus;
+
+ data.object.aspect = this.aspect;
+
+ if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );
+
+ data.object.filmGauge = this.filmGauge;
+ data.object.filmOffset = this.filmOffset;
+
+ return data;
+
+ }
+
+} );
+
+const fov = 90, aspect = 1;
+
+function CubeCamera( near, far, renderTarget ) {
+
+ Object3D.call( this );
+
+ this.type = 'CubeCamera';
+
+ if ( renderTarget.isWebGLCubeRenderTarget !== true ) {
+
+ console.error( 'THREE.CubeCamera: The constructor now expects an instance of WebGLCubeRenderTarget as third parameter.' );
+ return;
+
+ }
+
+ this.renderTarget = renderTarget;
+
+ const cameraPX = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPX.layers = this.layers;
+ cameraPX.up.set( 0, - 1, 0 );
+ cameraPX.lookAt( new Vector3( 1, 0, 0 ) );
+ this.add( cameraPX );
+
+ const cameraNX = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNX.layers = this.layers;
+ cameraNX.up.set( 0, - 1, 0 );
+ cameraNX.lookAt( new Vector3( - 1, 0, 0 ) );
+ this.add( cameraNX );
+
+ const cameraPY = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPY.layers = this.layers;
+ cameraPY.up.set( 0, 0, 1 );
+ cameraPY.lookAt( new Vector3( 0, 1, 0 ) );
+ this.add( cameraPY );
+
+ const cameraNY = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNY.layers = this.layers;
+ cameraNY.up.set( 0, 0, - 1 );
+ cameraNY.lookAt( new Vector3( 0, - 1, 0 ) );
+ this.add( cameraNY );
+
+ const cameraPZ = new PerspectiveCamera( fov, aspect, near, far );
+ cameraPZ.layers = this.layers;
+ cameraPZ.up.set( 0, - 1, 0 );
+ cameraPZ.lookAt( new Vector3( 0, 0, 1 ) );
+ this.add( cameraPZ );
+
+ const cameraNZ = new PerspectiveCamera( fov, aspect, near, far );
+ cameraNZ.layers = this.layers;
+ cameraNZ.up.set( 0, - 1, 0 );
+ cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) );
+ this.add( cameraNZ );
+
+ this.update = function ( renderer, scene ) {
+
+ if ( this.parent === null ) this.updateMatrixWorld();
+
+ const currentXrEnabled = renderer.xr.enabled;
+ const currentRenderTarget = renderer.getRenderTarget();
+
+ renderer.xr.enabled = false;
+
+ const generateMipmaps = renderTarget.texture.generateMipmaps;
+
+ renderTarget.texture.generateMipmaps = false;
+
+ renderer.setRenderTarget( renderTarget, 0 );
+ renderer.render( scene, cameraPX );
+
+ renderer.setRenderTarget( renderTarget, 1 );
+ renderer.render( scene, cameraNX );
+
+ renderer.setRenderTarget( renderTarget, 2 );
+ renderer.render( scene, cameraPY );
+
+ renderer.setRenderTarget( renderTarget, 3 );
+ renderer.render( scene, cameraNY );
+
+ renderer.setRenderTarget( renderTarget, 4 );
+ renderer.render( scene, cameraPZ );
+
+ renderTarget.texture.generateMipmaps = generateMipmaps;
+
+ renderer.setRenderTarget( renderTarget, 5 );
+ renderer.render( scene, cameraNZ );
+
+ renderer.setRenderTarget( currentRenderTarget );
+
+ renderer.xr.enabled = currentXrEnabled;
+
+ };
+
+}
+
+CubeCamera.prototype = Object.create( Object3D.prototype );
+CubeCamera.prototype.constructor = CubeCamera;
+
+function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {
+
+ images = images !== undefined ? images : [];
+ mapping = mapping !== undefined ? mapping : CubeReflectionMapping;
+ format = format !== undefined ? format : RGBFormat;
+
+ Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
+
+ this.flipY = false;
+
+ // Why CubeTexture._needsFlipEnvMap is necessary:
+ //
+ // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js)
+ // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words,
+ // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly.
+
+ // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped
+ // and the flag _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false)
+ // when using WebGLCubeRenderTarget.texture as a cube texture.
+
+ this._needsFlipEnvMap = true;
+
+}
+
+CubeTexture.prototype = Object.create( Texture.prototype );
+CubeTexture.prototype.constructor = CubeTexture;
+
+CubeTexture.prototype.isCubeTexture = true;
+
+Object.defineProperty( CubeTexture.prototype, 'images', {
+
+ get: function () {
+
+ return this.image;
+
+ },
+
+ set: function ( value ) {
+
+ this.image = value;
+
+ }
+
+} );
+
+function WebGLCubeRenderTarget( size, options, dummy ) {
+
+ if ( Number.isInteger( options ) ) {
+
+ console.warn( 'THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )' );
+
+ options = dummy;
+
+ }
+
+ WebGLRenderTarget.call( this, size, size, options );
+
+ options = options || {};
+
+ this.texture = new CubeTexture( undefined, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
+
+ this.texture._needsFlipEnvMap = false;
+
+}
+
+WebGLCubeRenderTarget.prototype = Object.create( WebGLRenderTarget.prototype );
+WebGLCubeRenderTarget.prototype.constructor = WebGLCubeRenderTarget;
+
+WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget = true;
+
+WebGLCubeRenderTarget.prototype.fromEquirectangularTexture = function ( renderer, texture ) {
+
+ this.texture.type = texture.type;
+ this.texture.format = RGBAFormat; // see #18859
+ this.texture.encoding = texture.encoding;
+
+ this.texture.generateMipmaps = texture.generateMipmaps;
+ this.texture.minFilter = texture.minFilter;
+ this.texture.magFilter = texture.magFilter;
+
+ const shader = {
+
+ uniforms: {
+ tEquirect: { value: null },
+ },
+
+ vertexShader: /* glsl */`
+
+ varying vec3 vWorldDirection;
+
+ vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
+
+ return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
+
+ }
+
+ void main() {
+
+ vWorldDirection = transformDirection( position, modelMatrix );
+
+ #include
+ #include
+
+ }
+ `,
+
+ fragmentShader: /* glsl */`
+
+ uniform sampler2D tEquirect;
+
+ varying vec3 vWorldDirection;
+
+ #include
+
+ void main() {
+
+ vec3 direction = normalize( vWorldDirection );
+
+ vec2 sampleUV = equirectUv( direction );
+
+ gl_FragColor = texture2D( tEquirect, sampleUV );
+
+ }
+ `
+ };
+
+ const geometry = new BoxBufferGeometry( 5, 5, 5 );
+
+ const material = new ShaderMaterial( {
+
+ name: 'CubemapFromEquirect',
+
+ uniforms: cloneUniforms( shader.uniforms ),
+ vertexShader: shader.vertexShader,
+ fragmentShader: shader.fragmentShader,
+ side: BackSide,
+ blending: NoBlending
+
+ } );
+
+ material.uniforms.tEquirect.value = texture;
+
+ const mesh = new Mesh( geometry, material );
+
+ const currentMinFilter = texture.minFilter;
+
+ // Avoid blurred poles
+ if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter;
+
+ const camera = new CubeCamera( 1, 10, this );
+ camera.update( renderer, mesh );
+
+ texture.minFilter = currentMinFilter;
+
+ mesh.geometry.dispose();
+ mesh.material.dispose();
+
+ return this;
+
+};
+
+WebGLCubeRenderTarget.prototype.clear = function ( renderer, color, depth, stencil ) {
+
+ const currentRenderTarget = renderer.getRenderTarget();
+
+ for ( let i = 0; i < 6; i ++ ) {
+
+ renderer.setRenderTarget( this, i );
+
+ renderer.clear( color, depth, stencil );
+
+ }
+
+ renderer.setRenderTarget( currentRenderTarget );
+
+};
+
+function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {
+
+ Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );
+
+ this.image = { data: data || null, width: width || 1, height: height || 1 };
+
+ this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;
+ this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;
+
+ this.generateMipmaps = false;
+ this.flipY = false;
+ this.unpackAlignment = 1;
+
+ this.needsUpdate = true;
+
+}
+
+DataTexture.prototype = Object.create( Texture.prototype );
+DataTexture.prototype.constructor = DataTexture;
+
+DataTexture.prototype.isDataTexture = true;
+
+const _sphere$1 = /*@__PURE__*/ new Sphere();
+const _vector$5 = /*@__PURE__*/ new Vector3();
+
+class Frustum {
+
+ constructor( p0, p1, p2, p3, p4, p5 ) {
+
+ this.planes = [
+
+ ( p0 !== undefined ) ? p0 : new Plane(),
+ ( p1 !== undefined ) ? p1 : new Plane(),
+ ( p2 !== undefined ) ? p2 : new Plane(),
+ ( p3 !== undefined ) ? p3 : new Plane(),
+ ( p4 !== undefined ) ? p4 : new Plane(),
+ ( p5 !== undefined ) ? p5 : new Plane()
+
+ ];
+
+ }
+
+ set( p0, p1, p2, p3, p4, p5 ) {
+
+ const planes = this.planes;
+
+ planes[ 0 ].copy( p0 );
+ planes[ 1 ].copy( p1 );
+ planes[ 2 ].copy( p2 );
+ planes[ 3 ].copy( p3 );
+ planes[ 4 ].copy( p4 );
+ planes[ 5 ].copy( p5 );
+
+ return this;
+
+ }
+
+ clone() {
+
+ return new this.constructor().copy( this );
+
+ }
+
+ copy( frustum ) {
+
+ const planes = this.planes;
+
+ for ( let i = 0; i < 6; i ++ ) {
+
+ planes[ i ].copy( frustum.planes[ i ] );
+
+ }
+
+ return this;
+
+ }
+
+ setFromProjectionMatrix( m ) {
+
+ const planes = this.planes;
+ const me = m.elements;
+ const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+ const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+ const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+ const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
+
+ planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+ planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+ planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+ planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+ planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+ planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();
+
+ return this;
+
+ }
+
+ intersectsObject( object ) {
+
+ const geometry = object.geometry;
+
+ if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+ _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld );
+
+ return this.intersectsSphere( _sphere$1 );
+
+ }
+
+ intersectsSprite( sprite ) {
+
+ _sphere$1.center.set( 0, 0, 0 );
+ _sphere$1.radius = 0.7071067811865476;
+ _sphere$1.applyMatrix4( sprite.matrixWorld );
+
+ return this.intersectsSphere( _sphere$1 );
+
+ }
+
+ intersectsSphere( sphere ) {
+
+ const planes = this.planes;
+ const center = sphere.center;
+ const negRadius = - sphere.radius;
+
+ for ( let i = 0; i < 6; i ++ ) {
+
+ const distance = planes[ i ].distanceToPoint( center );
+
+ if ( distance < negRadius ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ intersectsBox( box ) {
+
+ const planes = this.planes;
+
+ for ( let i = 0; i < 6; i ++ ) {
+
+ const plane = planes[ i ];
+
+ // corner at max distance
+
+ _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x;
+ _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y;
+ _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z;
+
+ if ( plane.distanceToPoint( _vector$5 ) < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ containsPoint( point ) {
+
+ const planes = this.planes;
+
+ for ( let i = 0; i < 6; i ++ ) {
+
+ if ( planes[ i ].distanceToPoint( point ) < 0 ) {
+
+ return false;
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+}
+
+function WebGLAnimation() {
+
+ let context = null;
+ let isAnimating = false;
+ let animationLoop = null;
+ let requestId = null;
+
+ function onAnimationFrame( time, frame ) {
+
+ animationLoop( time, frame );
+
+ requestId = context.requestAnimationFrame( onAnimationFrame );
+
+ }
+
+ return {
+
+ start: function () {
+
+ if ( isAnimating === true ) return;
+ if ( animationLoop === null ) return;
+
+ requestId = context.requestAnimationFrame( onAnimationFrame );
+
+ isAnimating = true;
+
+ },
+
+ stop: function () {
+
+ context.cancelAnimationFrame( requestId );
+
+ isAnimating = false;
+
+ },
+
+ setAnimationLoop: function ( callback ) {
+
+ animationLoop = callback;
+
+ },
+
+ setContext: function ( value ) {
+
+ context = value;
+
+ }
+
+ };
+
+}
+
+function WebGLAttributes( gl, capabilities ) {
+
+ const isWebGL2 = capabilities.isWebGL2;
+
+ const buffers = new WeakMap();
+
+ function createBuffer( attribute, bufferType ) {
+
+ const array = attribute.array;
+ const usage = attribute.usage;
+
+ const buffer = gl.createBuffer();
+
+ gl.bindBuffer( bufferType, buffer );
+ gl.bufferData( bufferType, array, usage );
+
+ attribute.onUploadCallback();
+
+ let type = 5126;
+
+ if ( array instanceof Float32Array ) {
+
+ type = 5126;
+
+ } else if ( array instanceof Float64Array ) {
+
+ console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );
+
+ } else if ( array instanceof Uint16Array ) {
+
+ type = 5123;
+
+ } else if ( array instanceof Int16Array ) {
+
+ type = 5122;
+
+ } else if ( array instanceof Uint32Array ) {
+
+ type = 5125;
+
+ } else if ( array instanceof Int32Array ) {
+
+ type = 5124;
+
+ } else if ( array instanceof Int8Array ) {
+
+ type = 5120;
+
+ } else if ( array instanceof Uint8Array ) {
+
+ type = 5121;
+
+ }
+
+ return {
+ buffer: buffer,
+ type: type,
+ bytesPerElement: array.BYTES_PER_ELEMENT,
+ version: attribute.version
+ };
+
+ }
+
+ function updateBuffer( buffer, attribute, bufferType ) {
+
+ const array = attribute.array;
+ const updateRange = attribute.updateRange;
+
+ gl.bindBuffer( bufferType, buffer );
+
+ if ( updateRange.count === - 1 ) {
+
+ // Not using update ranges
+
+ gl.bufferSubData( bufferType, 0, array );
+
+ } else {
+
+ if ( isWebGL2 ) {
+
+ gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
+ array, updateRange.offset, updateRange.count );
+
+ } else {
+
+ gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
+ array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );
+
+ }
+
+ updateRange.count = - 1; // reset range
+
+ }
+
+ }
+
+ //
+
+ function get( attribute ) {
+
+ if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
+
+ return buffers.get( attribute );
+
+ }
+
+ function remove( attribute ) {
+
+ if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
+
+ const data = buffers.get( attribute );
+
+ if ( data ) {
+
+ gl.deleteBuffer( data.buffer );
+
+ buffers.delete( attribute );
+
+ }
+
+ }
+
+ function update( attribute, bufferType ) {
+
+ if ( attribute.isGLBufferAttribute ) {
+
+ var cached = buffers.get( attribute );
+
+ if ( ! cached || cached.version < attribute.version ) {
+
+ buffers.set( attribute, {
+ buffer: attribute.buffer,
+ type: attribute.type,
+ bytesPerElement: attribute.elementSize,
+ version: attribute.version
+ } );
+
+ }
+
+ return;
+
+ }
+
+ if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
+
+ const data = buffers.get( attribute );
+
+ if ( data === undefined ) {
+
+ buffers.set( attribute, createBuffer( attribute, bufferType ) );
+
+ } else if ( data.version < attribute.version ) {
+
+ updateBuffer( data.buffer, attribute, bufferType );
+
+ data.version = attribute.version;
+
+ }
+
+ }
+
+ return {
+
+ get: get,
+ remove: remove,
+ update: update
+
+ };
+
+}
+
+class PlaneBufferGeometry extends BufferGeometry {
+
+ constructor( width, height, widthSegments, heightSegments ) {
+
+ super();
+ this.type = 'PlaneBufferGeometry';
+
+ this.parameters = {
+ width: width,
+ height: height,
+ widthSegments: widthSegments,
+ heightSegments: heightSegments
+ };
+
+ width = width || 1;
+ height = height || 1;
+
+ const width_half = width / 2;
+ const height_half = height / 2;
+
+ const gridX = Math.floor( widthSegments ) || 1;
+ const gridY = Math.floor( heightSegments ) || 1;
+
+ const gridX1 = gridX + 1;
+ const gridY1 = gridY + 1;
+
+ const segment_width = width / gridX;
+ const segment_height = height / gridY;
+
+ // buffers
+
+ const indices = [];
+ const vertices = [];
+ const normals = [];
+ const uvs = [];
+
+ // generate vertices, normals and uvs
+
+ for ( let iy = 0; iy < gridY1; iy ++ ) {
+
+ const y = iy * segment_height - height_half;
+
+ for ( let ix = 0; ix < gridX1; ix ++ ) {
+
+ const x = ix * segment_width - width_half;
+
+ vertices.push( x, - y, 0 );
+
+ normals.push( 0, 0, 1 );
+
+ uvs.push( ix / gridX );
+ uvs.push( 1 - ( iy / gridY ) );
+
+ }
+
+ }
+
+ // indices
+
+ for ( let iy = 0; iy < gridY; iy ++ ) {
+
+ for ( let ix = 0; ix < gridX; ix ++ ) {
+
+ const a = ix + gridX1 * iy;
+ const b = ix + gridX1 * ( iy + 1 );
+ const c = ( ix + 1 ) + gridX1 * ( iy + 1 );
+ const d = ( ix + 1 ) + gridX1 * iy;
+
+ // faces
+
+ indices.push( a, b, d );
+ indices.push( b, c, d );
+
+ }
+
+ }
+
+ // build geometry
+
+ this.setIndex( indices );
+ this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+ this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+ this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+
+ }
+
+}
+
+var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif";
+
+var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif";
+
+var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif";
+
+var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif";
+
+var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif";
+
+var begin_vertex = "vec3 transformed = vec3( position );";
+
+var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif";
+
+var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif";
+
+var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif";
+
+var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif";
+
+var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif";
+
+var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif";
+
+var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif";
+
+var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif";
+
+var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif";
+
+var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif";
+
+var color_vertex = "#if defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor.xyz *= color.xyz;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif";
+
+var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}";
+
+var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_maxMipLevel 8.0\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_maxTileSize 256.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\tfloat texelSize = 1.0 / ( 3.0 * cubeUV_maxTileSize );\n\t\tvec2 uv = getUV( direction, face ) * ( faceSize - 1.0 );\n\t\tvec2 f = fract( uv );\n\t\tuv += 0.5 - f;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tif ( mipInt < cubeUV_maxMipLevel ) {\n\t\t\tuv.y += 2.0 * cubeUV_maxTileSize;\n\t\t}\n\t\tuv.y += filterInt * 2.0 * cubeUV_minTileSize;\n\t\tuv.x += 3.0 * max( 0.0, cubeUV_maxTileSize - 2.0 * faceSize );\n\t\tuv *= texelSize;\n\t\tvec3 tl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x += texelSize;\n\t\tvec3 tr = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.y += texelSize;\n\t\tvec3 br = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tuv.x -= texelSize;\n\t\tvec3 bl = envMapTexelToLinear( texture2D( envMap, uv ) ).rgb;\n\t\tvec3 tm = mix( tl, tr, f.x );\n\t\tvec3 bm = mix( bl, br, f.x );\n\t\treturn mix( tm, bm, f.y );\n\t}\n\t#define r0 1.0\n\t#define v0 0.339\n\t#define m0 - 2.0\n\t#define r1 0.8\n\t#define v1 0.276\n\t#define m1 - 1.0\n\t#define r4 0.4\n\t#define v4 0.046\n\t#define m4 2.0\n\t#define r5 0.305\n\t#define v5 0.016\n\t#define m5 3.0\n\t#define r6 0.21\n\t#define v6 0.0038\n\t#define m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= r1 ) {\n\t\t\tmip = ( r0 - roughness ) * ( m1 - m0 ) / ( r0 - r1 ) + m0;\n\t\t} else if ( roughness >= r4 ) {\n\t\t\tmip = ( r1 - roughness ) * ( m4 - m1 ) / ( r1 - r4 ) + m1;\n\t\t} else if ( roughness >= r5 ) {\n\t\t\tmip = ( r4 - roughness ) * ( m5 - m4 ) / ( r4 - r5 ) + m4;\n\t\t} else if ( roughness >= r6 ) {\n\t\t\tmip = ( r5 - roughness ) * ( m6 - m5 ) / ( r5 - r6 ) + m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), m0, cubeUV_maxMipLevel );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif";
+
+var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif";
+
+var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif";
+
+var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vUv ).x * displacementScale + displacementBias );\n#endif";
+
+var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif";
+
+var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif";
+
+var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );";
+
+var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = clamp( floor( D ) / 255.0, 0.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}";
+
+var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 envColor = textureCubeUV( envMap, reflectVec, 0.0 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifndef ENVMAP_TYPE_CUBE_UV\n\t\tenvColor = envMapTexelToLinear( envColor );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif";
+
+var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif";
+
+var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif";
+
+var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif";
+
+var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif";
+
+var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = - mvPosition.z;\n#endif";
+
+var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif";
+
+var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif";
+
+var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif";
+
+var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn texture2D( gradientMap, coord ).rgb;\n\t#else\n\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t#endif\n}";
+
+var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\treflectedLight.indirectDiffuse += PI * lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n#endif";
+
+var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif";
+
+var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\nvIndirectFront += getAmbientLightIrradiance( ambientLightColor );\nvIndirectFront += getLightProbeIrradiance( lightProbe, geometry );\n#ifdef DOUBLE_SIDED\n\tvIndirectBack += getAmbientLightIrradiance( ambientLightColor );\n\tvIndirectBack += getLightProbeIrradiance( lightProbe, backGeometry );\n#endif\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n\t#pragma unroll_loop_end\n#endif";
+
+var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif";
+
+var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif";
+
+var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;";
+
+var lights_toon_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in GeometricContext geometry, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon\n#define Material_LightProbeLOD( material )\t(0)";
+
+var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;";
+
+var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)";
+
+var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.specularRoughness = max( roughnessFactor, 0.0525 );material.specularRoughness += geometryRoughness;\nmaterial.specularRoughness = min( material.specularRoughness, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif";
+
+var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat specularRoughness;\n\tvec3 specularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}";
+
+var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif";
+
+var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\tvec3 lightMapIrradiance = lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif";
+
+var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif";
+
+var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif";
+
+var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif";
+
+var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif";
+
+var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif";
+
+var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif";
+
+var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif";
+
+var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n#endif\n#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif";
+
+var map_particle_pars_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\tuniform mat3 uvTransform;\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif";
+
+var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif";
+
+var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
+
+var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n#endif";
+
+var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifndef USE_MORPHNORMALS\n\t\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\t\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif";
+
+var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t#endif\n#endif";
+
+var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\t#if defined( TANGENTSPACE_NORMALMAP ) || defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;";
+
+var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\t#ifdef USE_TANGENT\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, mapN );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif";
+
+var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 mapN ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tmat3 tsn = mat3( S, T, N );\n\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif";
+
+var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif";
+
+var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\t#ifdef USE_TANGENT\n\t\tclearcoatNormal = normalize( vTBN * clearcoatMapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatMapN );\n\t#endif\n#endif";
+
+var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif";
+
+var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ));\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w);\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}";
+
+var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif";
+
+var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;";
+
+var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif";
+
+var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif";
+
+var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif";
+
+var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
+
+var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), \n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif";
+
+var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif";
+
+var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0 || NUM_SPOT_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0\n\t\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\tvec4 shadowWorldPosition;\n\t#endif\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n#endif";
+
+var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}";
+
+var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
+
+var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
+
+var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif";
+
+var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif";
+
+var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif";
+
+var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif";
+
+var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif";
+
+var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }";
+
+var transmissionmap_fragment = "#ifdef USE_TRANSMISSIONMAP\n\ttotalTransmission *= texture2D( transmissionMap, vUv ).r;\n#endif";
+
+var transmissionmap_pars_fragment = "#ifdef USE_TRANSMISSIONMAP\n\tuniform sampler2D transmissionMap;\n#endif";
+
+var uv_pars_fragment = "#if ( defined( USE_UV ) && ! defined( UVS_VERTEX_ONLY ) )\n\tvarying vec2 vUv;\n#endif";
+
+var uv_pars_vertex = "#ifdef USE_UV\n\t#ifdef UVS_VERTEX_ONLY\n\t\tvec2 vUv;\n\t#else\n\t\tvarying vec2 vUv;\n\t#endif\n\tuniform mat3 uvTransform;\n#endif";
+
+var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif";
+
+var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif";
+
+var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n\tuniform mat3 uv2Transform;\n#endif";
+
+var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = ( uv2Transform * vec3( uv2, 1 ) ).xy;\n#endif";
+
+var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif";
+
+var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}";
+
+var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}";
+
+var cube_frag = "#include \nuniform float opacity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 vReflect = vWorldDirection;\n\t#include \n\tgl_FragColor = envColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}";
+
+var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}";
+
+var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}";
+
+var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}";
+
+var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}";
+
+var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}";
+
+var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}";
+
+var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}";
+
+var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}";
+
+var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}";
+
+var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\n\t\tvec4 lightMapTexel= texture2D( lightMap, vUv2 );\n\t\treflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}";
+
+var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}";
+
+var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}";
+
+var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include