UNPKG

268 kBJavaScriptView Raw
1function inMain() {
2 return globalThis.document !== undefined
3}
4function inWorker() {
5 return globalThis.WorkerGlobalScope !== undefined
6}
7function inNode() {
8 return typeof globalThis.require !== 'undefined'
9}
10function inDeno() {
11 return typeof globalThis.Deno !== 'undefined'
12}
13function AsyncFunction(argsArray, fcnBody) {
14 const ctor = Object.getPrototypeOf(async function () {}).constructor;
15 const asyncFcn = new ctor(...argsArray, fcnBody);
16 return asyncFcn
17}
18function blobToData(blob, type = 'dataURL') {
19 type = type[0].toUpperCase() + type.slice(1);
20 const types = ['Text', 'ArrayBuffer', 'DataURL'];
21 if (!types.includes(type))
22 throw Error('blobToData: data must be one of ' + types.toString())
23 const reader = new FileReader();
24 return new Promise((resolve, reject) => {
25 reader.addEventListener('load', () => resolve(reader.result));
26 reader.addEventListener('error', e => reject(e));
27 reader['readAs' + type](blob);
28 })
29}
30async function fetchData(url, type = 'blob') {
31 const types = ['arrayBuffer', 'blob', 'json', 'text'];
32 if (!types.includes(type))
33 throw Error('fetchData: data must be one of ' + types.toString())
34 return fetch(url).then(res => res[type]())
35}
36async function fetchJson(url) {
37 return fetchData(url, 'json')
38}
39async function fetchText(url) {
40 return fetchData(url, 'text')
41}
42function toDataURL(data, type = undefined) {
43 if (data.toDataURL) return data.toDataURL(type, type)
44 if (!type) type = 'text/plain;charset=US-ASCII';
45 return `data:${type};base64,${btoa(data)}}`
46}
47async function blobsEqual(blob0, blob1) {
48 const text0 = await blob0.text();
49 const text1 = await blob1.text();
50 return text0 === text1
51}
52function pause(ms = 1000) {
53 return new Promise(resolve => {
54 setTimeout(resolve, ms);
55 })
56}
57async function timeoutLoop(fcn, steps = -1, ms = 0) {
58 let i = 0;
59 while (i++ !== steps) {
60 fcn(i - 1);
61 await pause(ms);
62 }
63}
64function waitUntilDone(done, ms = 10) {
65 return new Promise(resolve => {
66 function waitOnDone() {
67 if (done()) return resolve()
68 else setTimeout(waitOnDone, ms);
69 }
70 waitOnDone();
71 })
72}
73let skipChecks = false;
74function skipErrorChecks(bool) {
75 skipChecks = bool;
76}
77function checkArg$1(arg, type = 'number', name = 'Function') {
78 if (skipChecks) return
79 const argType = typeof arg;
80 if (argType !== type) {
81 throw new Error(`${name} expected a ${type}, got ${arg}`)
82 }
83}
84function checkArgs$1(argsArray, type = 'number', name = 'Function') {
85 if (skipChecks) return
86 if (typeOf(argsArray) === 'arguments') argsArray = Array.from(argsArray);
87 argsArray.forEach(val => {
88 checkArg$1(val, type, name);
89 });
90}
91const logOnceMsgSet = new Set();
92function logOnce(msg, useWarn = false) {
93 if (!logOnceMsgSet.has(msg)) {
94 if (useWarn) {
95 console.warn(msg);
96 } else {
97 console.log(msg);
98 }
99 logOnceMsgSet.add(msg);
100 }
101}
102function warn(msg) {
103 logOnce(msg, true);
104}
105function timeit(f, runs = 1e5, name = 'test') {
106 name = name + '-' + runs;
107 console.time(name);
108 for (let i = 0; i < runs; i++) f(i);
109 console.timeEnd(name);
110}
111function fps() {
112 const timer = typeof performance === 'undefined' ? Date : performance;
113 const start = timer.now();
114 let steps = 0;
115 function perf() {
116 steps++;
117 const ms = timer.now() - start;
118 const fps = parseFloat((steps / (ms / 1000)).toFixed(2));
119 Object.assign(perf, { fps, ms, start, steps });
120 }
121 perf.steps = 0;
122 return perf
123}
124function pps(obj, title = '') {
125 if (title) console.log(title);
126 let count = 1;
127 let str = '';
128 while (obj) {
129 if (typeof obj === 'function') {
130 str = obj.constructor.toString();
131 } else {
132 const okeys = Object.keys(obj);
133 str =
134 okeys.length > 0
135 ? `[${okeys.join(', ')}]`
136 : `[${obj.constructor.name}]`;
137 }
138 console.log(`[${count++}]: ${str}`);
139 obj = Object.getPrototypeOf(obj);
140 }
141}
142function logAll(obj) {
143 Object.keys(obj).forEach(key => console.log(' ', key, obj[key]));
144}
145const PI$1 = Math.PI;
146function randomInt(max) {
147 return Math.floor(Math.random() * max)
148}
149function randomInt2(min, max) {
150 return min + Math.floor(Math.random() * (max - min))
151}
152function randomFloat(max) {
153 return Math.random() * max
154}
155function randomFloat2(min, max) {
156 return min + Math.random() * (max - min)
157}
158function randomCentered(r) {
159 return randomFloat2(-r / 2, r / 2)
160}
161function randomNormal(mean = 0.0, sigma = 1.0) {
162 const [u1, u2] = [1.0 - Math.random(), Math.random()];
163 const norm = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * PI$1 * u2);
164 return norm * sigma + mean
165}
166function randomSeed(seed = 123456) {
167 seed = seed % 2147483647;
168 Math.random = () => {
169 seed = (seed * 16807) % 2147483647;
170 return (seed - 1) / 2147483646
171 };
172}
173function precision(num, digits = 4) {
174 if (Math.abs(num) === 0) return 0
175 if (Array.isArray(num)) return num.map(val => precision(val, digits))
176 const mult = 10 ** digits;
177 return Math.round(num * mult) / mult
178}
179const isPowerOf2 = num => (num & (num - 1)) === 0;
180const nextPowerOf2 = num => Math.pow(2, Math.ceil(Math.log2(num)));
181function mod(v, n) {
182 return ((v % n) + n) % n
183}
184const wrap = (v, min, max) => min + mod(v - min, max - min);
185function clamp(v, min, max) {
186 if (v < min) return min
187 if (v > max) return max
188 return v
189}
190const isBetween = (val, min, max) => min <= val && val <= max;
191const lerp = (lo, hi, scale) =>
192 lo <= hi ? lo + (hi - lo) * scale : lo - (lo - hi) * scale;
193function lerpScale(number, lo, hi) {
194 if (lo === hi) throw Error('lerpScale: lo === hi')
195 number = clamp(number, lo, hi);
196 return (number - lo) / (hi - lo)
197}
198const toDeg$1 = 180 / Math.PI;
199const toRad$1 = Math.PI / 180;
200function degToRad(degrees) {
201 return mod2pi(degrees * toRad$1)
202}
203function radToDeg(radians) {
204 return mod360(radians * toDeg$1)
205}
206const degToHeading = degrees => mod360(90 - degrees);
207const headingToDeg = heading => mod360(90 - heading);
208function mod360(degrees) {
209 return mod(degrees, 360)
210}
211function mod2pi(radians) {
212 return mod(radians, 2 * PI$1)
213}
214function mod180180(degrees) {
215 let theta = mod360(degrees);
216 if (theta > 180) theta -= 360;
217 return theta
218}
219function radToHeading(radians) {
220 const deg = radians * toDeg$1;
221 return mod360(90 - deg)
222}
223function headingToRad(heading) {
224 const deg = mod360(90 - heading);
225 return deg * toRad$1
226}
227function radToHeadingAngle(radians) {
228 return -radToDeg(radians)
229}
230function headingAngleToRad(headingAngle) {
231 return -degToRad(headingAngle)
232}
233function degreesEqual(deg1, deg2) {
234 return mod360(deg1) === mod360(deg2)
235}
236function radsEqual(rads1, rads2) {
237 return mod2pi(rads1) === mod2pi(rads2)
238}
239const headingsEq = degreesEqual;
240function subtractRadians(rad1, rad0) {
241 let dr = mod2pi(rad1 - rad0);
242 if (dr > PI$1) dr = dr - 2 * PI$1;
243 return dr
244}
245function subtractDegrees(deg1, deg0) {
246 let dAngle = mod360(deg1 - deg0);
247 if (dAngle > 180) dAngle = dAngle - 360;
248 return dAngle
249}
250function subtractHeadings(head1, head0) {
251 return -subtractDegrees(head1, head0)
252}
253function radiansTowardXY(x, y, x1, y1) {
254 return Math.atan2(y1 - y, x1 - x)
255}
256function headingTowardXY(x, y, x1, y1) {
257 return radToHeading(radiansTowardXY(x, y, x1, y1))
258}
259function degreesTowardXY(x, y, x1, y1) {
260 return radToDeg(radiansTowardXY(x, y, x1, y1))
261}
262function inCone(x, y, radius, coneAngle, direction, x0, y0) {
263 if (sqDistance(x0, y0, x, y) > radius * radius) return false
264 const angle12 = radiansTowardXY(x0, y0, x, y);
265 return coneAngle / 2 >= Math.abs(subtractRadians(direction, angle12))
266}
267const sqDistance = (x, y, x1, y1) => (x - x1) ** 2 + (y - y1) ** 2;
268const distance = (x, y, x1, y1) => Math.sqrt(sqDistance(x, y, x1, y1));
269const sqDistance3 = (x, y, z, x1, y1, z1) =>
270 (x - x1) ** 2 + (y - y1) ** 2 + (z - z1) ** 2;
271const distance3 = (x, y, z, x1, y1, z1) =>
272 Math.sqrt(sqDistance3(x, y, z, x1, y1, z1));
273async function runModel(model, steps = 500, useSeed = true) {
274 console.log('runModel: model', model);
275 if (useSeed) randomSeed();
276 if (isString(model)) model = (await import(model)).default;
277 if (isFunction(model)) model = new model();
278 await model.startup();
279 model.setup();
280 if (inMain()) {
281 await timeoutLoop(() => {
282 model.step();
283 }, steps);
284 } else {
285 for (let i = 0; i < steps; i++) model.step();
286 }
287 return model
288}
289function classHasStartup(Class) {
290 console.log('classHasStartup?', Class);
291 const str = Class.toString();
292 let lines = str.split('\n');
293 lines = lines.filter(line => !/^ *\/\//.test(line));
294 lines = lines.filter(line => /startup\(\)/.test(line));
295 return lines.length > 0
296}
297function toJSON(obj, indent = 0, topLevelArrayOK = true) {
298 let firstCall = topLevelArrayOK;
299 const blackList = ['rectCache'];
300 const json = JSON.stringify(
301 obj,
302 (key, val) => {
303 if (blackList.includes(key)) {
304 return undefined
305 }
306 const isAgentArray =
307 Array.isArray(val) &&
308 val.length > 0 &&
309 Number.isInteger(val[0].id);
310 if (isAgentArray && !firstCall) {
311 return val.map(v => v.id)
312 }
313 firstCall = false;
314 return val
315 },
316 indent
317 );
318 return json
319}
320function sampleModel(model) {
321 const obj = {
322 ticks: model.ticks,
323 model: Object.keys(model),
324 patches: model.patches.length,
325 patch: model.patches.oneOf(),
326 turtles: model.turtles.length,
327 turtle: model.turtles.oneOf(),
328 links: model.links.length,
329 link: model.links.oneOf(),
330 };
331 const json = toJSON(obj);
332 return JSON.parse(json)
333}
334const identityFcn = o => o;
335const noopFcn = () => {};
336const propFcn = prop => o => o[prop];
337function arraysEqual(a1, a2) {
338 if (a1.length !== a2.length) return false
339 for (let i = 0; i < a1.length; i++) {
340 if (a1[i] !== a2[i]) return false
341 }
342 return true
343}
344function removeArrayItem(array, item) {
345 const ix = array.indexOf(item);
346 if (ix !== -1) {
347 array.splice(ix, 1);
348 } else {
349 throw Error(`removeArrayItem: ${item} not in array`)
350 }
351 return array
352}
353const arraysToString = arrays => arrays.map(a => `${a}`).join(',');
354function forLoop(arrayOrObj, fcn) {
355 if (arrayOrObj.slice) {
356 for (let i = 0, len = arrayOrObj.length; i < len; i++) {
357 fcn(arrayOrObj[i], i, arrayOrObj);
358 }
359 } else {
360 Object.keys(arrayOrObj).forEach(k => fcn(arrayOrObj[k], k, arrayOrObj));
361 }
362}
363function repeat(n, f, a = []) {
364 for (let i = 0; i < n; i++) f(i, a);
365 return a
366}
367function step(n, step, f) {
368 for (let i = 0; i < n; i += step) f(i);
369}
370function range(length) {
371 return repeat(length, (i, a) => {
372 a[i] = i;
373 })
374}
375function grep(array, regex) {
376 return array.reduce((acc, val) => {
377 if (regex.test(val)) acc.push(val);
378 return acc
379 }, [])
380}
381function concatArrays(array1, array2) {
382 const Type = array1.constructor;
383 if (Type === Array) {
384 return array1.concat(convertArrayType(array2, Array))
385 }
386 const array = new Type(array1.length + array2.length);
387 array.set(array1);
388 array.set(array2, array1.length);
389 return array
390}
391function objectToString(obj, indent = 2, jsKeys = true) {
392 let str = JSON.stringify(obj, null, indent);
393 if (jsKeys) str = str.replace(/"([^"]+)":/gm, '$1:');
394 return str
395}
396function objectLength(obj) {
397 return Object.keys(obj).length
398}
399const objectsEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
400function oneOf(array) {
401 return array[randomInt(array.length)]
402}
403function otherOneOf(array, item) {
404 if (array.length < 2) throw Error('otherOneOf: array.length < 2')
405 let other;
406 do {
407 other = oneOf(array);
408 } while (item === other)
409 return other
410}
411const oneKeyOf = obj => oneOf(Object.keys(obj));
412const oneValOf = obj => obj[oneKeyOf(obj)];
413function sortNums(array, ascending = true) {
414 return array.sort((a, b) => (ascending ? a - b : b - a))
415}
416function sortObjs(array, fcn, ascending = true) {
417 if (typeof fcn === 'string') fcn = propFcn(fcn);
418 const comp = (a, b) => fcn(a) - fcn(b);
419 return array.sort((a, b) => (ascending ? comp(a, b) : -comp(a, b)))
420}
421function shuffle(array) {
422 for (let i = array.length - 1; i > 0; i--) {
423 const j = randomInt(i)
424 ;[array[j], array[i]] = [array[i], array[j]];
425 }
426 return array
427}
428function union(a1, a2) {
429 return Array.from(new Set(a1.concat(a2)))
430}
431function intersection(a1, a2) {
432 const set2 = new Set(a2);
433 return a1.filter(x => set2.has(x))
434}
435function difference(a1, a2) {
436 const set2 = new Set(a2);
437 return a1.filter(x => !set2.has(x))
438}
439function floatRamp(start, stop, numItems) {
440 if (numItems <= 1) throw Error('floatRamp: numItems must be > 1')
441 const a = [];
442 for (let i = 0; i < numItems; i++) {
443 a.push(start + (stop - start) * (i / (numItems - 1)));
444 }
445 return a
446}
447function integerRamp(start, stop, numItems = stop - start + 1) {
448 return floatRamp(start, stop, numItems).map(a => Math.round(a))
449}
450function nestedProperty(obj, path) {
451 if (typeof path === 'string') path = path.split('.');
452 switch (path.length) {
453 case 1:
454 return obj[path[0]]
455 case 2:
456 return obj[path[0]][path[1]]
457 case 3:
458 return obj[path[0]][path[1]][path[2]]
459 case 4:
460 return obj[path[0]][path[1]][path[2]][path[3]]
461 default:
462 return path.reduce((obj, param) => obj[param], obj)
463 }
464}
465const arrayLast = array => array[array.length - 1];
466const arrayMax = array => array.reduce((a, b) => Math.max(a, b));
467const arrayMin = array => array.reduce((a, b) => Math.min(a, b));
468const arrayExtent = array => [arrayMin(array), arrayMax(array)];
469const arraysDiff = (a1, a2, ifcn = i => i) => {
470 if (a1.length !== a2.length)
471 return console.log('lengths differ', a1.length, a2.length)
472 const diffs = [];
473 for (let i = 0; i < a1.length; i++) {
474 if (a1[i] !== a2[i]) diffs.push([ifcn(i), a1[i], a2[i]]);
475 }
476 return diffs
477};
478function arrayToMatrix(array, width, height) {
479 if (array.length !== width * height)
480 throw Error('arrayToMatrix: length !== width * height')
481 const matrix = [];
482 for (let i = 0; i < height; i++) {
483 const row = array.slice(i * width, (i + 1) * width);
484 matrix.push(row);
485 }
486 return matrix
487}
488const matrixToArray = matrix => matrix.flat();
489function isOofA(data) {
490 if (!isObject$1(data)) return false
491 return Object.values(data).every(v => isTypedArray(v))
492}
493function toOofA(aofo, spec) {
494 const length = aofo.length;
495 const keys = Object.keys(spec);
496 const oofa = {};
497 keys.forEach(k => {
498 oofa[k] = new spec[k](length);
499 });
500 forLoop(aofo, (o, i) => {
501 keys.forEach(key => (oofa[key][i] = o[key]));
502 });
503 return oofa
504}
505function oofaObject(oofa, i, keys) {
506 const obj = {};
507 keys.forEach(key => {
508 obj[key] = oofa[key][i];
509 });
510 return obj
511}
512function toAofO(oofa, keys = Object.keys(oofa)) {
513 const length = oofa[keys[0]].length;
514 const aofo = new Array(length);
515 forLoop(aofo, (val, i) => {
516 aofo[i] = oofaObject(oofa, i, keys);
517 });
518 return aofo
519}
520function oofaBuffers(postData) {
521 const buffers = [];
522 forLoop(postData, obj => forLoop(obj, a => buffers.push(a.buffer)));
523 return buffers
524}
525const typeOf = obj =>
526 ({}.toString
527 .call(obj)
528 .match(/\s(\w+)/)[1]
529 .toLowerCase());
530const isType = (obj, string) => typeOf(obj) === string;
531const isOneOfTypes = (obj, array) => array.includes(typeOf(obj));
532const isString = obj => isType(obj, 'string');
533const isObject$1 = obj => isType(obj, 'object');
534const isArray = obj => Array.isArray(obj);
535const isNumber$1 = obj => isType(obj, 'number');
536const isInteger = n => Number.isInteger(n);
537const isFunction = obj => isType(obj, 'function');
538const isImage = obj => isType(obj, 'image');
539const isCanvas = obj =>
540 isOneOfTypes(obj, ['htmlcanvaselement', 'offscreencanvas']);
541const isImageable = obj =>
542 isOneOfTypes(obj, [
543 'image',
544 'htmlimageelement',
545 'htmlcanvaselement',
546 'offscreencanvas',
547 'imagebitmap',
548 ]);
549const isTypedArray = obj => typeOf(obj.buffer) === 'arraybuffer';
550const isUintArray = obj => /^uint.*array$/.test(typeOf(obj));
551const isIntArray = obj => /^int.*array$/.test(typeOf(obj));
552const isFloatArray = obj => /^float.*array$/.test(typeOf(obj));
553const isArrayLike = obj => isArray(obj) || isTypedArray(obj);
554const isColorLikeArray = obj =>
555 isArrayLike(obj) &&
556 [3, 4].includes(obj.length) &&
557 obj.every(
558 i =>
559 (isInteger(i) && isBetween(i, 0, 255)) ||
560 (isNumber$1(i) && isBetween(i, 0, 1))
561 );
562function isLittleEndian() {
563 const d32 = new Uint32Array([0x01020304]);
564 return new Uint8ClampedArray(d32.buffer)[0] === 4
565}
566function convertArrayType(array, Type) {
567 const Type0 = array.constructor;
568 if (Type0 === Type) return array
569 return Type.from(array)
570}
571function isDataSet(obj) {
572 return typeOf(obj) === 'object' && obj.width && obj.height && obj.data
573}
574function downloadCanvas(can, name = 'download.png', quality = null) {
575 if (!(name.endsWith('.png') || name.endsWith('.jpeg'))) name = name + '.png';
576 const type = name.endsWith('.png') ? 'image/png' : 'image/jpeg';
577 const url = typeOf(can) === 'string' ? can : can.toDataURL(type, quality);
578 const link = document.createElement('a');
579 link.download = name;
580 link.href = url;
581 link.click();
582}
583function downloadBlob(blobable, name = 'download', format = true) {
584 if (isDataSet(blobable) && !Array.isArray(blobable.data))
585 blobable.data = Array.from(blobable.data);
586 if (isTypedArray(blobable)) blobable = Array.from(blobable);
587 if (isObject$1(blobable) || Array.isArray(blobable))
588 blobable = format
589 ? JSON.stringify(blobable, null, 2)
590 : JSON.stringify(blobable);
591 const blob = typeOf(blobable) === 'blob' ? blobable : new Blob([blobable]);
592 const url = URL.createObjectURL(blob);
593 const link = document.createElement('a');
594 link.download = name;
595 link.href = url;
596 link.click();
597 URL.revokeObjectURL(url);
598}
599async function imagePromise(url, preferDOM = true) {
600 if ((inMain() && preferDOM) || inDeno()) {
601 return new Promise((resolve, reject) => {
602 const img = new Image();
603 img.crossOrigin = 'Anonymous';
604 img.onload = () => resolve(img);
605 img.onerror = () => reject(`Could not load image ${url}`);
606 img.src = url;
607 })
608 } else if (inWorker() || !preferDOM) {
609 const blob = await fetch(url).then(response => response.blob());
610 return createImageBitmap(blob)
611 }
612}
613async function fetchImage(url) {
614 return new Promise((resolve, reject) => {
615 const img = new Image();
616 img.crossOrigin = 'Anonymous';
617 img.onload = () => resolve(img);
618 img.onerror = () => reject(`Could not load image ${url}`);
619 img.src = url;
620 })
621}
622async function fetchImageBitmap(url) {
623 const blob = await fetchData(url, 'blob');
624 return createImageBitmap(blob)
625}
626function createCanvas(width, height, preferDOM = true) {
627 if (inMain() && preferDOM) {
628 const can = document.createElement('canvas');
629 can.width = width;
630 can.height = height;
631 return can
632 } else if (inDeno()) {
633 return globalThis.createCanvas(width, height)
634 } else if (inWorker() || !preferDOM) {
635 return new OffscreenCanvas(width, height)
636 }
637}
638function createCtx(width, height, preferDOM = true, attrs = {}) {
639 const can = createCanvas(width, height, preferDOM);
640 const ctx = can.getContext('2d', attrs);
641 if (inDeno()) {
642 const ctxObj = {
643 canvas: can,
644 };
645 Object.setPrototypeOf(ctxObj, ctx);
646 return ctxObj
647 } else {
648 return ctx
649 }
650}
651function cloneCanvas(can, preferDOM = true) {
652 const ctx = createCtx(can.width, can.height, preferDOM);
653 ctx.drawImage(can, 0, 0);
654 return ctx.canvas
655}
656function resizeCtx(ctx, width, height) {
657 const copy = cloneCanvas(ctx.canvas);
658 ctx.canvas.width = width;
659 ctx.canvas.height = height;
660 ctx.drawImage(copy, 0, 0);
661}
662function setCanvasSize(can, width, height) {
663 if (can.width !== width || can.height != height) {
664 can.width = width;
665 can.height = height;
666 }
667}
668function setIdentity(ctx) {
669 ctx.save();
670 ctx.resetTransform();
671}
672function setTextProperties(
673 ctx,
674 font,
675 textAlign = 'center',
676 textBaseline = 'middle'
677) {
678 Object.assign(ctx, { font, textAlign, textBaseline });
679}
680let bboxCtx;
681function stringMetrics(
682 string,
683 font,
684 textAlign = 'center',
685 textBaseline = 'middle'
686) {
687 if (!bboxCtx) bboxCtx = createCtx(0, 0);
688 setTextProperties(bboxCtx, font, textAlign, textBaseline);
689 const metrics = bboxCtx.measureText(string);
690 metrics.height =
691 metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
692 return metrics
693}
694function drawText(ctx, string, x, y, color, useIdentity = true) {
695 if (useIdentity) setIdentity(ctx);
696 ctx.fillStyle = color.css || color;
697 ctx.fillText(string, x, y);
698 if (useIdentity) ctx.restore();
699}
700function ctxImageData(ctx) {
701 return ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height)
702}
703function ctxImageColors(ctx) {
704 const typedArray = ctxImageData(ctx).data;
705 const colors = [];
706 step(typedArray.length, 4, i => colors.push(typedArray.subarray(i, i + 4)));
707 return colors
708}
709function ctxImagePixels(ctx) {
710 const imageData = ctxImageData(ctx);
711 const pixels = new Uint32Array(imageData.data.buffer);
712 return pixels
713}
714function clearCtx(ctx, cssColor = undefined) {
715 const { width, height } = ctx.canvas;
716 setIdentity(ctx);
717 if (!cssColor || cssColor === 'transparent') {
718 ctx.clearRect(0, 0, width, height);
719 } else {
720 cssColor = cssColor.css || cssColor;
721 ctx.fillStyle = cssColor;
722 ctx.fillRect(0, 0, width, height);
723 }
724 ctx.restore();
725}
726function imageToCtx(img) {
727 const { width, height } = img;
728 const ctx = createCtx(width, height);
729 fillCtxWithImage(ctx, img);
730 return ctx
731}
732function imageToCanvas(img) {
733 return imageToCtx(img).canvas
734}
735function fillCtxWithImage(ctx, img) {
736 setIdentity(ctx);
737 ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
738 ctx.restore();
739}
740function setCtxImage(ctx, img) {
741 setCanvasSize(ctx.canvas, img.width, img.height);
742 fillCtxWithImage(ctx, img);
743}
744function toWindow(obj) {
745 Object.assign(window, obj);
746 console.log('toWindow:', Object.keys(obj).join(', '));
747}
748function dump(model = window.model) {
749 const { patches: ps, turtles: ts, links: ls } = model;
750 Object.assign(window, { ps, ts, ls });
751 window.p = ps.length > 0 ? ps.oneOf() : {};
752 window.t = ts.length > 0 ? ts.oneOf() : {};
753 window.l = ls.length > 0 ? ls.oneOf() : {};
754 console.log('debug: ps, ts, ls, p, t, l dumped to window');
755}
756function addCssLink(url) {
757 const link = document.createElement('link');
758 link.setAttribute('rel', 'stylesheet');
759 link.setAttribute('href', url);
760 document.head.appendChild(link);
761}
762async function fetchCssStyle(url) {
763 if (url.startsWith('../')) {
764 console.log('fetchCssStyle relative url', url);
765 url = import.meta.resolve(url);
766 console.log(' absolute url', url);
767 }
768 const response = await fetch(url);
769 if (!response.ok) throw Error(`fetchCssStyle: Not found: ${url}`)
770 const css = await response.text();
771 addCssStyle(css);
772 return css
773}
774function addCssStyle(css) {
775 const style = document.createElement('style');
776 style.innerHTML = css;
777 document.head.appendChild(style);
778}
779function getQueryString() {
780 return window.location.search.substr(1)
781}
782function parseQueryString(
783 paramsString = getQueryString()
784) {
785 const results = {};
786 const searchParams = new URLSearchParams(paramsString);
787 for (const pair of searchParams.entries()) {
788 let [key, val] = pair;
789 if (val.match(/^[0-9.]+$/) || val.match(/^[0-9.]+e[0-9]+$/))
790 val = Number(val);
791 if (['true', 't', ''].includes(val)) val = true;
792 if (['false', 'f'].includes(val)) val = false;
793 results[key] = val;
794 }
795 return results
796}
797function RESTapi(parameters) {
798 return Object.assign(parameters, parseQueryString())
799}
800function printToPage(msg, element = document.body) {
801 if (typeof msg === 'object') {
802 msg = JSON.stringify(msg, null, 2);
803 }
804 msg = '<pre>' + msg + '</pre>';
805 if (typeof element === 'string') {
806 element = document.getElementById(element);
807 }
808 element.style.fontFamily = 'monospace';
809 element.innerHTML += msg;
810}
811function getEventXY(element, evt) {
812 const rect = element.getBoundingClientRect();
813 return [evt.clientX - rect.left, evt.clientY - rect.top]
814}
815
816var util = /*#__PURE__*/Object.freeze({
817 __proto__: null,
818 AsyncFunction: AsyncFunction,
819 PI: PI$1,
820 RESTapi: RESTapi,
821 addCssLink: addCssLink,
822 addCssStyle: addCssStyle,
823 arrayExtent: arrayExtent,
824 arrayLast: arrayLast,
825 arrayMax: arrayMax,
826 arrayMin: arrayMin,
827 arrayToMatrix: arrayToMatrix,
828 arraysDiff: arraysDiff,
829 arraysEqual: arraysEqual,
830 arraysToString: arraysToString,
831 blobToData: blobToData,
832 blobsEqual: blobsEqual,
833 checkArg: checkArg$1,
834 checkArgs: checkArgs$1,
835 clamp: clamp,
836 classHasStartup: classHasStartup,
837 clearCtx: clearCtx,
838 cloneCanvas: cloneCanvas,
839 concatArrays: concatArrays,
840 convertArrayType: convertArrayType,
841 createCanvas: createCanvas,
842 createCtx: createCtx,
843 ctxImageColors: ctxImageColors,
844 ctxImageData: ctxImageData,
845 ctxImagePixels: ctxImagePixels,
846 degToHeading: degToHeading,
847 degToRad: degToRad,
848 degreesEqual: degreesEqual,
849 degreesTowardXY: degreesTowardXY,
850 difference: difference,
851 distance: distance,
852 distance3: distance3,
853 downloadBlob: downloadBlob,
854 downloadCanvas: downloadCanvas,
855 drawText: drawText,
856 dump: dump,
857 fetchCssStyle: fetchCssStyle,
858 fetchData: fetchData,
859 fetchImage: fetchImage,
860 fetchImageBitmap: fetchImageBitmap,
861 fetchJson: fetchJson,
862 fetchText: fetchText,
863 fillCtxWithImage: fillCtxWithImage,
864 floatRamp: floatRamp,
865 forLoop: forLoop,
866 fps: fps,
867 getEventXY: getEventXY,
868 getQueryString: getQueryString,
869 grep: grep,
870 headingAngleToRad: headingAngleToRad,
871 headingToDeg: headingToDeg,
872 headingToRad: headingToRad,
873 headingTowardXY: headingTowardXY,
874 headingsEq: headingsEq,
875 identityFcn: identityFcn,
876 imagePromise: imagePromise,
877 imageToCanvas: imageToCanvas,
878 imageToCtx: imageToCtx,
879 inCone: inCone,
880 inDeno: inDeno,
881 inMain: inMain,
882 inNode: inNode,
883 inWorker: inWorker,
884 integerRamp: integerRamp,
885 intersection: intersection,
886 isArray: isArray,
887 isArrayLike: isArrayLike,
888 isBetween: isBetween,
889 isCanvas: isCanvas,
890 isColorLikeArray: isColorLikeArray,
891 isDataSet: isDataSet,
892 isFloatArray: isFloatArray,
893 isFunction: isFunction,
894 isImage: isImage,
895 isImageable: isImageable,
896 isIntArray: isIntArray,
897 isInteger: isInteger,
898 isLittleEndian: isLittleEndian,
899 isNumber: isNumber$1,
900 isObject: isObject$1,
901 isOneOfTypes: isOneOfTypes,
902 isOofA: isOofA,
903 isPowerOf2: isPowerOf2,
904 isString: isString,
905 isType: isType,
906 isTypedArray: isTypedArray,
907 isUintArray: isUintArray,
908 lerp: lerp,
909 lerpScale: lerpScale,
910 logAll: logAll,
911 logOnce: logOnce,
912 matrixToArray: matrixToArray,
913 mod: mod,
914 mod180180: mod180180,
915 mod2pi: mod2pi,
916 mod360: mod360,
917 nestedProperty: nestedProperty,
918 nextPowerOf2: nextPowerOf2,
919 noopFcn: noopFcn,
920 objectLength: objectLength,
921 objectToString: objectToString,
922 objectsEqual: objectsEqual,
923 oneKeyOf: oneKeyOf,
924 oneOf: oneOf,
925 oneValOf: oneValOf,
926 oofaBuffers: oofaBuffers,
927 oofaObject: oofaObject,
928 otherOneOf: otherOneOf,
929 parseQueryString: parseQueryString,
930 pause: pause,
931 pps: pps,
932 precision: precision,
933 printToPage: printToPage,
934 propFcn: propFcn,
935 radToDeg: radToDeg,
936 radToHeading: radToHeading,
937 radToHeadingAngle: radToHeadingAngle,
938 radiansTowardXY: radiansTowardXY,
939 radsEqual: radsEqual,
940 randomCentered: randomCentered,
941 randomFloat: randomFloat,
942 randomFloat2: randomFloat2,
943 randomInt: randomInt,
944 randomInt2: randomInt2,
945 randomNormal: randomNormal,
946 randomSeed: randomSeed,
947 range: range,
948 removeArrayItem: removeArrayItem,
949 repeat: repeat,
950 resizeCtx: resizeCtx,
951 runModel: runModel,
952 sampleModel: sampleModel,
953 setCanvasSize: setCanvasSize,
954 setCtxImage: setCtxImage,
955 setIdentity: setIdentity,
956 setTextProperties: setTextProperties,
957 shuffle: shuffle,
958 skipErrorChecks: skipErrorChecks,
959 sortNums: sortNums,
960 sortObjs: sortObjs,
961 sqDistance: sqDistance,
962 sqDistance3: sqDistance3,
963 step: step,
964 stringMetrics: stringMetrics,
965 subtractDegrees: subtractDegrees,
966 subtractHeadings: subtractHeadings,
967 subtractRadians: subtractRadians,
968 timeit: timeit,
969 timeoutLoop: timeoutLoop,
970 toAofO: toAofO,
971 toDataURL: toDataURL,
972 toDeg: toDeg$1,
973 toJSON: toJSON,
974 toOofA: toOofA,
975 toRad: toRad$1,
976 toWindow: toWindow,
977 typeOf: typeOf,
978 union: union,
979 waitUntilDone: waitUntilDone,
980 warn: warn,
981 wrap: wrap
982});
983
984async function toContext(img) {
985 const type = typeOf(img);
986 switch (type) {
987 case 'string':
988 img = await imagePromise(img);
989 case 'htmlimageelement':
990 return imageToCtx(img)
991 case 'htmlcanvaselement':
992 case 'offscreencanvas':
993 return img.getContext('2d')
994 case 'canvasrenderingcontext2d':
995 return img
996 default:
997 throw Error('toContext: bad img type: ' + type)
998 }
999}
1000function toUint8Array(msg) {
1001 const type = typeOf(msg);
1002 switch (type) {
1003 case 'number':
1004 msg = String.fromCharCode(msg);
1005 case 'string':
1006 return new TextEncoder().encode(msg)
1007 case 'uint8array':
1008 case 'uint8clampedarray':
1009 return msg
1010 default:
1011 throw Error('toUint8Array: bad msg type: ' + type)
1012 }
1013}
1014function charToBits(char) {
1015 return [
1016 char >> bits[0].shift,
1017 (char >> bits[1].shift) & bits[1].msgMask,
1018 char & bits[2].msgMask,
1019 ]
1020}
1021const bits = [
1022 { shift: 5, msgMask: 0b00000111, dataMask: 0b11111000 },
1023 { shift: 3, msgMask: 0b00000011, dataMask: 0b11111100 },
1024 { shift: 0, msgMask: 0b00000111, dataMask: 0b11111000 },
1025];
1026function checkSize(msg, width, height) {
1027 const imgSize = width * height;
1028 if (imgSize < msg.length)
1029 throw Error(`encode: image size < msg.length: ${imgSize} ${msg.length}`)
1030}
1031function stegMsgSize(imgData) {
1032 for (let i = 3; i < imgData.length; i = i + 4) {
1033 if (imgData[i] === 254) return (i - 3) / 4
1034 }
1035 throw Error(
1036 `decode: no message terminator in image data, length = ${imgData.length}`
1037 )
1038}
1039async function encode(img, msg) {
1040 const ctx = await toContext(img);
1041 const { width, height } = ctx.canvas;
1042 checkSize(msg, width, height);
1043 const msgArray = toUint8Array(msg);
1044 console.log('msg buffer', msgArray);
1045 const imageData = ctx.getImageData(0, 0, width, height);
1046 const data = imageData.data;
1047 console.log('imgageData.data', data);
1048 let ix;
1049 msgArray.forEach((char, i) => {
1050 const [ch0, ch1, ch2] = charToBits(char);
1051 ix = i * 4;
1052 data[ix] = (data[ix++] & bits[0].dataMask) + ch0;
1053 data[ix] = (data[ix++] & bits[1].dataMask) + ch1;
1054 data[ix] = (data[ix++] & bits[2].dataMask) + ch2;
1055 data[ix] = 255;
1056 });
1057 data[ix + 4] = 254;
1058 console.log('encoded imgageData.data', data);
1059 ctx.putImageData(imageData, 0, 0);
1060 console.log('msg length', msg.length);
1061 console.log('encode: embedded msg size', stegMsgSize(data));
1062 return ctx
1063}
1064async function decode(img, returnU8 = false) {
1065 const ctx = await toContext(img);
1066 const { width, height } = ctx.canvas;
1067 const data = ctx.getImageData(0, 0, width, height).data;
1068 const msgSize = stegMsgSize(data);
1069 console.log('decode: embedded msg size', msgSize);
1070 const msgArray = new Uint8Array(msgSize);
1071 msgArray.forEach((char, i) => {
1072 let ix = i * 4;
1073 const ch0 = (bits[0].msgMask & data[ix++]) << bits[0].shift;
1074 const ch1 = (bits[1].msgMask & data[ix++]) << bits[1].shift;
1075 const ch2 = (bits[2].msgMask & data[ix++]) << bits[2].shift;
1076 msgArray[i] = ch0 + ch1 + ch2;
1077 });
1078 console.log('decode msgArray', msgArray);
1079 if (returnU8) return msgArray
1080 return new TextDecoder().decode(msgArray)
1081}
1082
1083var steg = /*#__PURE__*/Object.freeze({
1084 __proto__: null,
1085 decode: decode,
1086 encode: encode,
1087 stegMsgSize: stegMsgSize
1088});
1089
1090var earthRadius = 6371008.8;
1091var factors = {
1092 centimeters: earthRadius * 100,
1093 centimetres: earthRadius * 100,
1094 degrees: earthRadius / 111325,
1095 feet: earthRadius * 3.28084,
1096 inches: earthRadius * 39.37,
1097 kilometers: earthRadius / 1000,
1098 kilometres: earthRadius / 1000,
1099 meters: earthRadius,
1100 metres: earthRadius,
1101 miles: earthRadius / 1609.344,
1102 millimeters: earthRadius * 1000,
1103 millimetres: earthRadius * 1000,
1104 nauticalmiles: earthRadius / 1852,
1105 radians: 1,
1106 yards: earthRadius * 1.0936,
1107};
1108var areaFactors = {
1109 acres: 0.000247105,
1110 centimeters: 10000,
1111 centimetres: 10000,
1112 feet: 10.763910417,
1113 hectares: 0.0001,
1114 inches: 1550.003100006,
1115 kilometers: 0.000001,
1116 kilometres: 0.000001,
1117 meters: 1,
1118 metres: 1,
1119 miles: 3.86e-7,
1120 millimeters: 1000000,
1121 millimetres: 1000000,
1122 yards: 1.195990046,
1123};
1124function feature(geom, properties, options) {
1125 if (options === void 0) { options = {}; }
1126 var feat = { type: "Feature" };
1127 if (options.id === 0 || options.id) {
1128 feat.id = options.id;
1129 }
1130 if (options.bbox) {
1131 feat.bbox = options.bbox;
1132 }
1133 feat.properties = properties || {};
1134 feat.geometry = geom;
1135 return feat;
1136}
1137function geometry(type, coordinates, _options) {
1138 switch (type) {
1139 case "Point":
1140 return point(coordinates).geometry;
1141 case "LineString":
1142 return lineString(coordinates).geometry;
1143 case "Polygon":
1144 return polygon(coordinates).geometry;
1145 case "MultiPoint":
1146 return multiPoint(coordinates).geometry;
1147 case "MultiLineString":
1148 return multiLineString(coordinates).geometry;
1149 case "MultiPolygon":
1150 return multiPolygon(coordinates).geometry;
1151 default:
1152 throw new Error(type + " is invalid");
1153 }
1154}
1155function point(coordinates, properties, options) {
1156 if (options === void 0) { options = {}; }
1157 if (!coordinates) {
1158 throw new Error("coordinates is required");
1159 }
1160 if (!Array.isArray(coordinates)) {
1161 throw new Error("coordinates must be an Array");
1162 }
1163 if (coordinates.length < 2) {
1164 throw new Error("coordinates must be at least 2 numbers long");
1165 }
1166 if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {
1167 throw new Error("coordinates must contain numbers");
1168 }
1169 var geom = {
1170 type: "Point",
1171 coordinates: coordinates,
1172 };
1173 return feature(geom, properties, options);
1174}
1175function points(coordinates, properties, options) {
1176 if (options === void 0) { options = {}; }
1177 return featureCollection(coordinates.map(function (coords) {
1178 return point(coords, properties);
1179 }), options);
1180}
1181function polygon(coordinates, properties, options) {
1182 if (options === void 0) { options = {}; }
1183 for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
1184 var ring = coordinates_1[_i];
1185 if (ring.length < 4) {
1186 throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
1187 }
1188 for (var j = 0; j < ring[ring.length - 1].length; j++) {
1189 if (ring[ring.length - 1][j] !== ring[0][j]) {
1190 throw new Error("First and last Position are not equivalent.");
1191 }
1192 }
1193 }
1194 var geom = {
1195 type: "Polygon",
1196 coordinates: coordinates,
1197 };
1198 return feature(geom, properties, options);
1199}
1200function polygons(coordinates, properties, options) {
1201 if (options === void 0) { options = {}; }
1202 return featureCollection(coordinates.map(function (coords) {
1203 return polygon(coords, properties);
1204 }), options);
1205}
1206function lineString(coordinates, properties, options) {
1207 if (options === void 0) { options = {}; }
1208 if (coordinates.length < 2) {
1209 throw new Error("coordinates must be an array of two or more positions");
1210 }
1211 var geom = {
1212 type: "LineString",
1213 coordinates: coordinates,
1214 };
1215 return feature(geom, properties, options);
1216}
1217function lineStrings(coordinates, properties, options) {
1218 if (options === void 0) { options = {}; }
1219 return featureCollection(coordinates.map(function (coords) {
1220 return lineString(coords, properties);
1221 }), options);
1222}
1223function featureCollection(features, options) {
1224 if (options === void 0) { options = {}; }
1225 var fc = { type: "FeatureCollection" };
1226 if (options.id) {
1227 fc.id = options.id;
1228 }
1229 if (options.bbox) {
1230 fc.bbox = options.bbox;
1231 }
1232 fc.features = features;
1233 return fc;
1234}
1235function multiLineString(coordinates, properties, options) {
1236 if (options === void 0) { options = {}; }
1237 var geom = {
1238 type: "MultiLineString",
1239 coordinates: coordinates,
1240 };
1241 return feature(geom, properties, options);
1242}
1243function multiPoint(coordinates, properties, options) {
1244 if (options === void 0) { options = {}; }
1245 var geom = {
1246 type: "MultiPoint",
1247 coordinates: coordinates,
1248 };
1249 return feature(geom, properties, options);
1250}
1251function multiPolygon(coordinates, properties, options) {
1252 if (options === void 0) { options = {}; }
1253 var geom = {
1254 type: "MultiPolygon",
1255 coordinates: coordinates,
1256 };
1257 return feature(geom, properties, options);
1258}
1259function geometryCollection(geometries, properties, options) {
1260 if (options === void 0) { options = {}; }
1261 var geom = {
1262 type: "GeometryCollection",
1263 geometries: geometries,
1264 };
1265 return feature(geom, properties, options);
1266}
1267function radiansToLength(radians, units) {
1268 if (units === void 0) { units = "kilometers"; }
1269 var factor = factors[units];
1270 if (!factor) {
1271 throw new Error(units + " units is invalid");
1272 }
1273 return radians * factor;
1274}
1275function lengthToRadians(distance, units) {
1276 if (units === void 0) { units = "kilometers"; }
1277 var factor = factors[units];
1278 if (!factor) {
1279 throw new Error(units + " units is invalid");
1280 }
1281 return distance / factor;
1282}
1283function lengthToDegrees(distance, units) {
1284 return radiansToDegrees(lengthToRadians(distance, units));
1285}
1286function bearingToAzimuth(bearing) {
1287 var angle = bearing % 360;
1288 if (angle < 0) {
1289 angle += 360;
1290 }
1291 return angle;
1292}
1293function radiansToDegrees(radians) {
1294 var degrees = radians % (2 * Math.PI);
1295 return (degrees * 180) / Math.PI;
1296}
1297function convertLength(length, originalUnit, finalUnit) {
1298 if (originalUnit === void 0) { originalUnit = "kilometers"; }
1299 if (finalUnit === void 0) { finalUnit = "kilometers"; }
1300 if (!(length >= 0)) {
1301 throw new Error("length must be a positive number");
1302 }
1303 return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
1304}
1305function convertArea(area, originalUnit, finalUnit) {
1306 if (originalUnit === void 0) { originalUnit = "meters"; }
1307 if (finalUnit === void 0) { finalUnit = "kilometers"; }
1308 if (!(area >= 0)) {
1309 throw new Error("area must be a positive number");
1310 }
1311 var startFactor = areaFactors[originalUnit];
1312 if (!startFactor) {
1313 throw new Error("invalid original units");
1314 }
1315 var finalFactor = areaFactors[finalUnit];
1316 if (!finalFactor) {
1317 throw new Error("invalid final units");
1318 }
1319 return (area / startFactor) * finalFactor;
1320}
1321function isNumber(num) {
1322 return !isNaN(num) && num !== null && !Array.isArray(num);
1323}
1324function isObject(input) {
1325 return !!input && input.constructor === Object;
1326}
1327function coordEach(geojson, callback, excludeWrapCoord) {
1328 if (geojson === null) return;
1329 var j,
1330 k,
1331 l,
1332 geometry,
1333 stopG,
1334 coords,
1335 geometryMaybeCollection,
1336 wrapShrink = 0,
1337 coordIndex = 0,
1338 isGeometryCollection,
1339 type = geojson.type,
1340 isFeatureCollection = type === "FeatureCollection",
1341 isFeature = type === "Feature",
1342 stop = isFeatureCollection ? geojson.features.length : 1;
1343 for (var featureIndex = 0; featureIndex < stop; featureIndex++) {
1344 geometryMaybeCollection = isFeatureCollection
1345 ? geojson.features[featureIndex].geometry
1346 : isFeature
1347 ? geojson.geometry
1348 : geojson;
1349 isGeometryCollection = geometryMaybeCollection
1350 ? geometryMaybeCollection.type === "GeometryCollection"
1351 : false;
1352 stopG = isGeometryCollection
1353 ? geometryMaybeCollection.geometries.length
1354 : 1;
1355 for (var geomIndex = 0; geomIndex < stopG; geomIndex++) {
1356 var multiFeatureIndex = 0;
1357 var geometryIndex = 0;
1358 geometry = isGeometryCollection
1359 ? geometryMaybeCollection.geometries[geomIndex]
1360 : geometryMaybeCollection;
1361 if (geometry === null) continue;
1362 coords = geometry.coordinates;
1363 var geomType = geometry.type;
1364 wrapShrink =
1365 excludeWrapCoord &&
1366 (geomType === "Polygon" || geomType === "MultiPolygon")
1367 ? 1
1368 : 0;
1369 switch (geomType) {
1370 case null:
1371 break;
1372 case "Point":
1373 if (
1374 callback(
1375 coords,
1376 coordIndex,
1377 featureIndex,
1378 multiFeatureIndex,
1379 geometryIndex
1380 ) === false
1381 )
1382 return false;
1383 coordIndex++;
1384 multiFeatureIndex++;
1385 break;
1386 case "LineString":
1387 case "MultiPoint":
1388 for (j = 0; j < coords.length; j++) {
1389 if (
1390 callback(
1391 coords[j],
1392 coordIndex,
1393 featureIndex,
1394 multiFeatureIndex,
1395 geometryIndex
1396 ) === false
1397 )
1398 return false;
1399 coordIndex++;
1400 if (geomType === "MultiPoint") multiFeatureIndex++;
1401 }
1402 if (geomType === "LineString") multiFeatureIndex++;
1403 break;
1404 case "Polygon":
1405 case "MultiLineString":
1406 for (j = 0; j < coords.length; j++) {
1407 for (k = 0; k < coords[j].length - wrapShrink; k++) {
1408 if (
1409 callback(
1410 coords[j][k],
1411 coordIndex,
1412 featureIndex,
1413 multiFeatureIndex,
1414 geometryIndex
1415 ) === false
1416 )
1417 return false;
1418 coordIndex++;
1419 }
1420 if (geomType === "MultiLineString") multiFeatureIndex++;
1421 if (geomType === "Polygon") geometryIndex++;
1422 }
1423 if (geomType === "Polygon") multiFeatureIndex++;
1424 break;
1425 case "MultiPolygon":
1426 for (j = 0; j < coords.length; j++) {
1427 geometryIndex = 0;
1428 for (k = 0; k < coords[j].length; k++) {
1429 for (l = 0; l < coords[j][k].length - wrapShrink; l++) {
1430 if (
1431 callback(
1432 coords[j][k][l],
1433 coordIndex,
1434 featureIndex,
1435 multiFeatureIndex,
1436 geometryIndex
1437 ) === false
1438 )
1439 return false;
1440 coordIndex++;
1441 }
1442 geometryIndex++;
1443 }
1444 multiFeatureIndex++;
1445 }
1446 break;
1447 case "GeometryCollection":
1448 for (j = 0; j < geometry.geometries.length; j++)
1449 if (
1450 coordEach(geometry.geometries[j], callback, excludeWrapCoord) ===
1451 false
1452 )
1453 return false;
1454 break;
1455 default:
1456 throw new Error("Unknown Geometry Type");
1457 }
1458 }
1459 }
1460}
1461function coordReduce(geojson, callback, initialValue, excludeWrapCoord) {
1462 var previousValue = initialValue;
1463 coordEach(
1464 geojson,
1465 function (
1466 currentCoord,
1467 coordIndex,
1468 featureIndex,
1469 multiFeatureIndex,
1470 geometryIndex
1471 ) {
1472 if (coordIndex === 0 && initialValue === undefined)
1473 previousValue = currentCoord;
1474 else
1475 previousValue = callback(
1476 previousValue,
1477 currentCoord,
1478 coordIndex,
1479 featureIndex,
1480 multiFeatureIndex,
1481 geometryIndex
1482 );
1483 },
1484 excludeWrapCoord
1485 );
1486 return previousValue;
1487}
1488function propEach(geojson, callback) {
1489 var i;
1490 switch (geojson.type) {
1491 case "FeatureCollection":
1492 for (i = 0; i < geojson.features.length; i++) {
1493 if (callback(geojson.features[i].properties, i) === false) break;
1494 }
1495 break;
1496 case "Feature":
1497 callback(geojson.properties, 0);
1498 break;
1499 }
1500}
1501function propReduce(geojson, callback, initialValue) {
1502 var previousValue = initialValue;
1503 propEach(geojson, function (currentProperties, featureIndex) {
1504 if (featureIndex === 0 && initialValue === undefined)
1505 previousValue = currentProperties;
1506 else
1507 previousValue = callback(previousValue, currentProperties, featureIndex);
1508 });
1509 return previousValue;
1510}
1511function featureEach(geojson, callback) {
1512 if (geojson.type === "Feature") {
1513 callback(geojson, 0);
1514 } else if (geojson.type === "FeatureCollection") {
1515 for (var i = 0; i < geojson.features.length; i++) {
1516 if (callback(geojson.features[i], i) === false) break;
1517 }
1518 }
1519}
1520function featureReduce(geojson, callback, initialValue) {
1521 var previousValue = initialValue;
1522 featureEach(geojson, function (currentFeature, featureIndex) {
1523 if (featureIndex === 0 && initialValue === undefined)
1524 previousValue = currentFeature;
1525 else previousValue = callback(previousValue, currentFeature, featureIndex);
1526 });
1527 return previousValue;
1528}
1529function coordAll(geojson) {
1530 var coords = [];
1531 coordEach(geojson, function (coord) {
1532 coords.push(coord);
1533 });
1534 return coords;
1535}
1536function geomEach(geojson, callback) {
1537 var i,
1538 j,
1539 g,
1540 geometry,
1541 stopG,
1542 geometryMaybeCollection,
1543 isGeometryCollection,
1544 featureProperties,
1545 featureBBox,
1546 featureId,
1547 featureIndex = 0,
1548 isFeatureCollection = geojson.type === "FeatureCollection",
1549 isFeature = geojson.type === "Feature",
1550 stop = isFeatureCollection ? geojson.features.length : 1;
1551 for (i = 0; i < stop; i++) {
1552 geometryMaybeCollection = isFeatureCollection
1553 ? geojson.features[i].geometry
1554 : isFeature
1555 ? geojson.geometry
1556 : geojson;
1557 featureProperties = isFeatureCollection
1558 ? geojson.features[i].properties
1559 : isFeature
1560 ? geojson.properties
1561 : {};
1562 featureBBox = isFeatureCollection
1563 ? geojson.features[i].bbox
1564 : isFeature
1565 ? geojson.bbox
1566 : undefined;
1567 featureId = isFeatureCollection
1568 ? geojson.features[i].id
1569 : isFeature
1570 ? geojson.id
1571 : undefined;
1572 isGeometryCollection = geometryMaybeCollection
1573 ? geometryMaybeCollection.type === "GeometryCollection"
1574 : false;
1575 stopG = isGeometryCollection
1576 ? geometryMaybeCollection.geometries.length
1577 : 1;
1578 for (g = 0; g < stopG; g++) {
1579 geometry = isGeometryCollection
1580 ? geometryMaybeCollection.geometries[g]
1581 : geometryMaybeCollection;
1582 if (geometry === null) {
1583 if (
1584 callback(
1585 null,
1586 featureIndex,
1587 featureProperties,
1588 featureBBox,
1589 featureId
1590 ) === false
1591 )
1592 return false;
1593 continue;
1594 }
1595 switch (geometry.type) {
1596 case "Point":
1597 case "LineString":
1598 case "MultiPoint":
1599 case "Polygon":
1600 case "MultiLineString":
1601 case "MultiPolygon": {
1602 if (
1603 callback(
1604 geometry,
1605 featureIndex,
1606 featureProperties,
1607 featureBBox,
1608 featureId
1609 ) === false
1610 )
1611 return false;
1612 break;
1613 }
1614 case "GeometryCollection": {
1615 for (j = 0; j < geometry.geometries.length; j++) {
1616 if (
1617 callback(
1618 geometry.geometries[j],
1619 featureIndex,
1620 featureProperties,
1621 featureBBox,
1622 featureId
1623 ) === false
1624 )
1625 return false;
1626 }
1627 break;
1628 }
1629 default:
1630 throw new Error("Unknown Geometry Type");
1631 }
1632 }
1633 featureIndex++;
1634 }
1635}
1636function geomReduce(geojson, callback, initialValue) {
1637 var previousValue = initialValue;
1638 geomEach(
1639 geojson,
1640 function (
1641 currentGeometry,
1642 featureIndex,
1643 featureProperties,
1644 featureBBox,
1645 featureId
1646 ) {
1647 if (featureIndex === 0 && initialValue === undefined)
1648 previousValue = currentGeometry;
1649 else
1650 previousValue = callback(
1651 previousValue,
1652 currentGeometry,
1653 featureIndex,
1654 featureProperties,
1655 featureBBox,
1656 featureId
1657 );
1658 }
1659 );
1660 return previousValue;
1661}
1662function flattenEach(geojson, callback) {
1663 geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
1664 var type = geometry === null ? null : geometry.type;
1665 switch (type) {
1666 case null:
1667 case "Point":
1668 case "LineString":
1669 case "Polygon":
1670 if (
1671 callback(
1672 feature(geometry, properties, { bbox: bbox, id: id }),
1673 featureIndex,
1674 0
1675 ) === false
1676 )
1677 return false;
1678 return;
1679 }
1680 var geomType;
1681 switch (type) {
1682 case "MultiPoint":
1683 geomType = "Point";
1684 break;
1685 case "MultiLineString":
1686 geomType = "LineString";
1687 break;
1688 case "MultiPolygon":
1689 geomType = "Polygon";
1690 break;
1691 }
1692 for (
1693 var multiFeatureIndex = 0;
1694 multiFeatureIndex < geometry.coordinates.length;
1695 multiFeatureIndex++
1696 ) {
1697 var coordinate = geometry.coordinates[multiFeatureIndex];
1698 var geom = {
1699 type: geomType,
1700 coordinates: coordinate,
1701 };
1702 if (
1703 callback(feature(geom, properties), featureIndex, multiFeatureIndex) ===
1704 false
1705 )
1706 return false;
1707 }
1708 });
1709}
1710function flattenReduce(geojson, callback, initialValue) {
1711 var previousValue = initialValue;
1712 flattenEach(
1713 geojson,
1714 function (currentFeature, featureIndex, multiFeatureIndex) {
1715 if (
1716 featureIndex === 0 &&
1717 multiFeatureIndex === 0 &&
1718 initialValue === undefined
1719 )
1720 previousValue = currentFeature;
1721 else
1722 previousValue = callback(
1723 previousValue,
1724 currentFeature,
1725 featureIndex,
1726 multiFeatureIndex
1727 );
1728 }
1729 );
1730 return previousValue;
1731}
1732function segmentEach(geojson, callback) {
1733 flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
1734 var segmentIndex = 0;
1735 if (!feature.geometry) return;
1736 var type = feature.geometry.type;
1737 if (type === "Point" || type === "MultiPoint") return;
1738 var previousCoords;
1739 var previousFeatureIndex = 0;
1740 var previousMultiIndex = 0;
1741 var prevGeomIndex = 0;
1742 if (
1743 coordEach(
1744 feature,
1745 function (
1746 currentCoord,
1747 coordIndex,
1748 featureIndexCoord,
1749 multiPartIndexCoord,
1750 geometryIndex
1751 ) {
1752 if (
1753 previousCoords === undefined ||
1754 featureIndex > previousFeatureIndex ||
1755 multiPartIndexCoord > previousMultiIndex ||
1756 geometryIndex > prevGeomIndex
1757 ) {
1758 previousCoords = currentCoord;
1759 previousFeatureIndex = featureIndex;
1760 previousMultiIndex = multiPartIndexCoord;
1761 prevGeomIndex = geometryIndex;
1762 segmentIndex = 0;
1763 return;
1764 }
1765 var currentSegment = lineString(
1766 [previousCoords, currentCoord],
1767 feature.properties
1768 );
1769 if (
1770 callback(
1771 currentSegment,
1772 featureIndex,
1773 multiFeatureIndex,
1774 geometryIndex,
1775 segmentIndex
1776 ) === false
1777 )
1778 return false;
1779 segmentIndex++;
1780 previousCoords = currentCoord;
1781 }
1782 ) === false
1783 )
1784 return false;
1785 });
1786}
1787function segmentReduce(geojson, callback, initialValue) {
1788 var previousValue = initialValue;
1789 var started = false;
1790 segmentEach(
1791 geojson,
1792 function (
1793 currentSegment,
1794 featureIndex,
1795 multiFeatureIndex,
1796 geometryIndex,
1797 segmentIndex
1798 ) {
1799 if (started === false && initialValue === undefined)
1800 previousValue = currentSegment;
1801 else
1802 previousValue = callback(
1803 previousValue,
1804 currentSegment,
1805 featureIndex,
1806 multiFeatureIndex,
1807 geometryIndex,
1808 segmentIndex
1809 );
1810 started = true;
1811 }
1812 );
1813 return previousValue;
1814}
1815function lineEach(geojson, callback) {
1816 if (!geojson) throw new Error("geojson is required");
1817 flattenEach(geojson, function (feature, featureIndex, multiFeatureIndex) {
1818 if (feature.geometry === null) return;
1819 var type = feature.geometry.type;
1820 var coords = feature.geometry.coordinates;
1821 switch (type) {
1822 case "LineString":
1823 if (callback(feature, featureIndex, multiFeatureIndex, 0, 0) === false)
1824 return false;
1825 break;
1826 case "Polygon":
1827 for (
1828 var geometryIndex = 0;
1829 geometryIndex < coords.length;
1830 geometryIndex++
1831 ) {
1832 if (
1833 callback(
1834 lineString(coords[geometryIndex], feature.properties),
1835 featureIndex,
1836 multiFeatureIndex,
1837 geometryIndex
1838 ) === false
1839 )
1840 return false;
1841 }
1842 break;
1843 }
1844 });
1845}
1846function lineReduce(geojson, callback, initialValue) {
1847 var previousValue = initialValue;
1848 lineEach(
1849 geojson,
1850 function (currentLine, featureIndex, multiFeatureIndex, geometryIndex) {
1851 if (featureIndex === 0 && initialValue === undefined)
1852 previousValue = currentLine;
1853 else
1854 previousValue = callback(
1855 previousValue,
1856 currentLine,
1857 featureIndex,
1858 multiFeatureIndex,
1859 geometryIndex
1860 );
1861 }
1862 );
1863 return previousValue;
1864}
1865function findSegment(geojson, options) {
1866 options = options || {};
1867 if (!isObject(options)) throw new Error("options is invalid");
1868 var featureIndex = options.featureIndex || 0;
1869 var multiFeatureIndex = options.multiFeatureIndex || 0;
1870 var geometryIndex = options.geometryIndex || 0;
1871 var segmentIndex = options.segmentIndex || 0;
1872 var properties = options.properties;
1873 var geometry;
1874 switch (geojson.type) {
1875 case "FeatureCollection":
1876 if (featureIndex < 0)
1877 featureIndex = geojson.features.length + featureIndex;
1878 properties = properties || geojson.features[featureIndex].properties;
1879 geometry = geojson.features[featureIndex].geometry;
1880 break;
1881 case "Feature":
1882 properties = properties || geojson.properties;
1883 geometry = geojson.geometry;
1884 break;
1885 case "Point":
1886 case "MultiPoint":
1887 return null;
1888 case "LineString":
1889 case "Polygon":
1890 case "MultiLineString":
1891 case "MultiPolygon":
1892 geometry = geojson;
1893 break;
1894 default:
1895 throw new Error("geojson is invalid");
1896 }
1897 if (geometry === null) return null;
1898 var coords = geometry.coordinates;
1899 switch (geometry.type) {
1900 case "Point":
1901 case "MultiPoint":
1902 return null;
1903 case "LineString":
1904 if (segmentIndex < 0) segmentIndex = coords.length + segmentIndex - 1;
1905 return lineString(
1906 [coords[segmentIndex], coords[segmentIndex + 1]],
1907 properties,
1908 options
1909 );
1910 case "Polygon":
1911 if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
1912 if (segmentIndex < 0)
1913 segmentIndex = coords[geometryIndex].length + segmentIndex - 1;
1914 return lineString(
1915 [
1916 coords[geometryIndex][segmentIndex],
1917 coords[geometryIndex][segmentIndex + 1],
1918 ],
1919 properties,
1920 options
1921 );
1922 case "MultiLineString":
1923 if (multiFeatureIndex < 0)
1924 multiFeatureIndex = coords.length + multiFeatureIndex;
1925 if (segmentIndex < 0)
1926 segmentIndex = coords[multiFeatureIndex].length + segmentIndex - 1;
1927 return lineString(
1928 [
1929 coords[multiFeatureIndex][segmentIndex],
1930 coords[multiFeatureIndex][segmentIndex + 1],
1931 ],
1932 properties,
1933 options
1934 );
1935 case "MultiPolygon":
1936 if (multiFeatureIndex < 0)
1937 multiFeatureIndex = coords.length + multiFeatureIndex;
1938 if (geometryIndex < 0)
1939 geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
1940 if (segmentIndex < 0)
1941 segmentIndex =
1942 coords[multiFeatureIndex][geometryIndex].length - segmentIndex - 1;
1943 return lineString(
1944 [
1945 coords[multiFeatureIndex][geometryIndex][segmentIndex],
1946 coords[multiFeatureIndex][geometryIndex][segmentIndex + 1],
1947 ],
1948 properties,
1949 options
1950 );
1951 }
1952 throw new Error("geojson is invalid");
1953}
1954function findPoint(geojson, options) {
1955 options = options || {};
1956 if (!isObject(options)) throw new Error("options is invalid");
1957 var featureIndex = options.featureIndex || 0;
1958 var multiFeatureIndex = options.multiFeatureIndex || 0;
1959 var geometryIndex = options.geometryIndex || 0;
1960 var coordIndex = options.coordIndex || 0;
1961 var properties = options.properties;
1962 var geometry;
1963 switch (geojson.type) {
1964 case "FeatureCollection":
1965 if (featureIndex < 0)
1966 featureIndex = geojson.features.length + featureIndex;
1967 properties = properties || geojson.features[featureIndex].properties;
1968 geometry = geojson.features[featureIndex].geometry;
1969 break;
1970 case "Feature":
1971 properties = properties || geojson.properties;
1972 geometry = geojson.geometry;
1973 break;
1974 case "Point":
1975 case "MultiPoint":
1976 return null;
1977 case "LineString":
1978 case "Polygon":
1979 case "MultiLineString":
1980 case "MultiPolygon":
1981 geometry = geojson;
1982 break;
1983 default:
1984 throw new Error("geojson is invalid");
1985 }
1986 if (geometry === null) return null;
1987 var coords = geometry.coordinates;
1988 switch (geometry.type) {
1989 case "Point":
1990 return point(coords, properties, options);
1991 case "MultiPoint":
1992 if (multiFeatureIndex < 0)
1993 multiFeatureIndex = coords.length + multiFeatureIndex;
1994 return point(coords[multiFeatureIndex], properties, options);
1995 case "LineString":
1996 if (coordIndex < 0) coordIndex = coords.length + coordIndex;
1997 return point(coords[coordIndex], properties, options);
1998 case "Polygon":
1999 if (geometryIndex < 0) geometryIndex = coords.length + geometryIndex;
2000 if (coordIndex < 0)
2001 coordIndex = coords[geometryIndex].length + coordIndex;
2002 return point(coords[geometryIndex][coordIndex], properties, options);
2003 case "MultiLineString":
2004 if (multiFeatureIndex < 0)
2005 multiFeatureIndex = coords.length + multiFeatureIndex;
2006 if (coordIndex < 0)
2007 coordIndex = coords[multiFeatureIndex].length + coordIndex;
2008 return point(coords[multiFeatureIndex][coordIndex], properties, options);
2009 case "MultiPolygon":
2010 if (multiFeatureIndex < 0)
2011 multiFeatureIndex = coords.length + multiFeatureIndex;
2012 if (geometryIndex < 0)
2013 geometryIndex = coords[multiFeatureIndex].length + geometryIndex;
2014 if (coordIndex < 0)
2015 coordIndex =
2016 coords[multiFeatureIndex][geometryIndex].length - coordIndex;
2017 return point(
2018 coords[multiFeatureIndex][geometryIndex][coordIndex],
2019 properties,
2020 options
2021 );
2022 }
2023 throw new Error("geojson is invalid");
2024}
2025function bbox(geojson) {
2026 var result = [Infinity, Infinity, -Infinity, -Infinity];
2027 coordEach(geojson, function (coord) {
2028 if (result[0] > coord[0]) {
2029 result[0] = coord[0];
2030 }
2031 if (result[1] > coord[1]) {
2032 result[1] = coord[1];
2033 }
2034 if (result[2] < coord[0]) {
2035 result[2] = coord[0];
2036 }
2037 if (result[3] < coord[1]) {
2038 result[3] = coord[1];
2039 }
2040 });
2041 return result;
2042}
2043bbox["default"] = bbox;
2044function bboxPolygon(bbox, options) {
2045 if (options === void 0) { options = {}; }
2046 var west = Number(bbox[0]);
2047 var south = Number(bbox[1]);
2048 var east = Number(bbox[2]);
2049 var north = Number(bbox[3]);
2050 if (bbox.length === 6) {
2051 throw new Error("@turf/bbox-polygon does not support BBox with 6 positions");
2052 }
2053 var lowLeft = [west, south];
2054 var topLeft = [west, north];
2055 var topRight = [east, north];
2056 var lowRight = [east, south];
2057 return polygon([[lowLeft, lowRight, topRight, topLeft, lowLeft]], options.properties, { bbox: bbox, id: options.id });
2058}
2059function getCoord(coord) {
2060 if (!coord) {
2061 throw new Error("coord is required");
2062 }
2063 if (!Array.isArray(coord)) {
2064 if (coord.type === "Feature" &&
2065 coord.geometry !== null &&
2066 coord.geometry.type === "Point") {
2067 return coord.geometry.coordinates;
2068 }
2069 if (coord.type === "Point") {
2070 return coord.coordinates;
2071 }
2072 }
2073 if (Array.isArray(coord) &&
2074 coord.length >= 2 &&
2075 !Array.isArray(coord[0]) &&
2076 !Array.isArray(coord[1])) {
2077 return coord;
2078 }
2079 throw new Error("coord must be GeoJSON Point or an Array of numbers");
2080}
2081function getCoords(coords) {
2082 if (Array.isArray(coords)) {
2083 return coords;
2084 }
2085 if (coords.type === "Feature") {
2086 if (coords.geometry !== null) {
2087 return coords.geometry.coordinates;
2088 }
2089 }
2090 else {
2091 if (coords.coordinates) {
2092 return coords.coordinates;
2093 }
2094 }
2095 throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
2096}
2097function containsNumber(coordinates) {
2098 if (coordinates.length > 1 &&
2099 isNumber(coordinates[0]) &&
2100 isNumber(coordinates[1])) {
2101 return true;
2102 }
2103 if (Array.isArray(coordinates[0]) && coordinates[0].length) {
2104 return containsNumber(coordinates[0]);
2105 }
2106 throw new Error("coordinates must only contain numbers");
2107}
2108function geojsonType(value, type, name) {
2109 if (!type || !name) {
2110 throw new Error("type and name required");
2111 }
2112 if (!value || value.type !== type) {
2113 throw new Error("Invalid input to " +
2114 name +
2115 ": must be a " +
2116 type +
2117 ", given " +
2118 value.type);
2119 }
2120}
2121function featureOf(feature, type, name) {
2122 if (!feature) {
2123 throw new Error("No feature passed");
2124 }
2125 if (!name) {
2126 throw new Error(".featureOf() requires a name");
2127 }
2128 if (!feature || feature.type !== "Feature" || !feature.geometry) {
2129 throw new Error("Invalid input to " + name + ", Feature with geometry required");
2130 }
2131 if (!feature.geometry || feature.geometry.type !== type) {
2132 throw new Error("Invalid input to " +
2133 name +
2134 ": must be a " +
2135 type +
2136 ", given " +
2137 feature.geometry.type);
2138 }
2139}
2140function collectionOf(featureCollection, type, name) {
2141 if (!featureCollection) {
2142 throw new Error("No featureCollection passed");
2143 }
2144 if (!name) {
2145 throw new Error(".collectionOf() requires a name");
2146 }
2147 if (!featureCollection || featureCollection.type !== "FeatureCollection") {
2148 throw new Error("Invalid input to " + name + ", FeatureCollection required");
2149 }
2150 for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
2151 var feature = _a[_i];
2152 if (!feature || feature.type !== "Feature" || !feature.geometry) {
2153 throw new Error("Invalid input to " + name + ", Feature with geometry required");
2154 }
2155 if (!feature.geometry || feature.geometry.type !== type) {
2156 throw new Error("Invalid input to " +
2157 name +
2158 ": must be a " +
2159 type +
2160 ", given " +
2161 feature.geometry.type);
2162 }
2163 }
2164}
2165function getGeom(geojson) {
2166 if (geojson.type === "Feature") {
2167 return geojson.geometry;
2168 }
2169 return geojson;
2170}
2171function getType(geojson, _name) {
2172 if (geojson.type === "FeatureCollection") {
2173 return "FeatureCollection";
2174 }
2175 if (geojson.type === "GeometryCollection") {
2176 return "GeometryCollection";
2177 }
2178 if (geojson.type === "Feature" && geojson.geometry !== null) {
2179 return geojson.geometry.type;
2180 }
2181 return geojson.type;
2182}
2183function booleanPointInPolygon(point, polygon, options) {
2184 if (options === void 0) { options = {}; }
2185 if (!point) {
2186 throw new Error("point is required");
2187 }
2188 if (!polygon) {
2189 throw new Error("polygon is required");
2190 }
2191 var pt = getCoord(point);
2192 var geom = getGeom(polygon);
2193 var type = geom.type;
2194 var bbox = polygon.bbox;
2195 var polys = geom.coordinates;
2196 if (bbox && inBBox(pt, bbox) === false) {
2197 return false;
2198 }
2199 if (type === "Polygon") {
2200 polys = [polys];
2201 }
2202 var insidePoly = false;
2203 for (var i = 0; i < polys.length && !insidePoly; i++) {
2204 if (inRing(pt, polys[i][0], options.ignoreBoundary)) {
2205 var inHole = false;
2206 var k = 1;
2207 while (k < polys[i].length && !inHole) {
2208 if (inRing(pt, polys[i][k], !options.ignoreBoundary)) {
2209 inHole = true;
2210 }
2211 k++;
2212 }
2213 if (!inHole) {
2214 insidePoly = true;
2215 }
2216 }
2217 }
2218 return insidePoly;
2219}
2220function inRing(pt, ring, ignoreBoundary) {
2221 var isInside = false;
2222 if (ring[0][0] === ring[ring.length - 1][0] &&
2223 ring[0][1] === ring[ring.length - 1][1]) {
2224 ring = ring.slice(0, ring.length - 1);
2225 }
2226 for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) {
2227 var xi = ring[i][0];
2228 var yi = ring[i][1];
2229 var xj = ring[j][0];
2230 var yj = ring[j][1];
2231 var onBoundary = pt[1] * (xi - xj) + yi * (xj - pt[0]) + yj * (pt[0] - xi) === 0 &&
2232 (xi - pt[0]) * (xj - pt[0]) <= 0 &&
2233 (yi - pt[1]) * (yj - pt[1]) <= 0;
2234 if (onBoundary) {
2235 return !ignoreBoundary;
2236 }
2237 var intersect = yi > pt[1] !== yj > pt[1] &&
2238 pt[0] < ((xj - xi) * (pt[1] - yi)) / (yj - yi) + xi;
2239 if (intersect) {
2240 isInside = !isInside;
2241 }
2242 }
2243 return isInside;
2244}
2245function inBBox(pt, bbox) {
2246 return (bbox[0] <= pt[0] && bbox[1] <= pt[1] && bbox[2] >= pt[0] && bbox[3] >= pt[1]);
2247}
2248
2249var turfImports = /*#__PURE__*/Object.freeze({
2250 __proto__: null,
2251 bbox: bbox,
2252 bboxPolygon: bboxPolygon,
2253 bearingToAzimuth: bearingToAzimuth,
2254 booleanPointInPolygon: booleanPointInPolygon,
2255 collectionOf: collectionOf,
2256 containsNumber: containsNumber,
2257 convertArea: convertArea,
2258 convertLength: convertLength,
2259 coordAll: coordAll,
2260 coordEach: coordEach,
2261 coordReduce: coordReduce,
2262 feature: feature,
2263 featureCollection: featureCollection,
2264 featureEach: featureEach,
2265 featureOf: featureOf,
2266 featureReduce: featureReduce,
2267 findPoint: findPoint,
2268 findSegment: findSegment,
2269 flattenEach: flattenEach,
2270 flattenReduce: flattenReduce,
2271 geojsonType: geojsonType,
2272 geomEach: geomEach,
2273 geomReduce: geomReduce,
2274 geometry: geometry,
2275 geometryCollection: geometryCollection,
2276 getCoord: getCoord,
2277 getCoords: getCoords,
2278 getGeom: getGeom,
2279 getType: getType,
2280 lengthToDegrees: lengthToDegrees,
2281 lengthToRadians: lengthToRadians,
2282 lineEach: lineEach,
2283 lineReduce: lineReduce,
2284 lineString: lineString,
2285 lineStrings: lineStrings,
2286 multiLineString: multiLineString,
2287 multiPoint: multiPoint,
2288 multiPolygon: multiPolygon,
2289 point: point,
2290 points: points,
2291 polygon: polygon,
2292 polygons: polygons,
2293 propEach: propEach,
2294 propReduce: propReduce,
2295 radiansToLength: radiansToLength,
2296 segmentEach: segmentEach,
2297 segmentReduce: segmentReduce
2298});
2299
2300class AgentArray extends Array {
2301 static get [Symbol.species]() {
2302 return AgentArray
2303 }
2304 static fromArray(array) {
2305 const aarray = Object.setPrototypeOf(array, AgentArray.prototype);
2306 return aarray
2307 }
2308 constructor(...args) {
2309 super(...args);
2310 }
2311 toArray() {
2312 Object.setPrototypeOf(this, Array.prototype);
2313 return this
2314 }
2315 isEmpty() {
2316 return this.length === 0
2317 }
2318 first() {
2319 return this[0]
2320 }
2321 last() {
2322 return this[this.length - 1]
2323 }
2324 atIndex(i) {
2325 if (this.length === 0) return undefined
2326 const index = mod(i, this.length);
2327 return this[index]
2328 }
2329 all(fcn) {
2330 return this.every(fcn)
2331 }
2332 props(key, type = AgentArray) {
2333 const result = new type(this.length);
2334 for (let i = 0; i < this.length; i++) {
2335 result[i] = this[i][key];
2336 }
2337 return result
2338 }
2339 typedSample(obj) {
2340 const result = {};
2341 forLoop(obj, (val, key) => {
2342 result[key] = this.props(key, val);
2343 });
2344 return result
2345 }
2346 uniq() {
2347 return AgentArray.from(new Set(this))
2348 }
2349 forLoop(fcn) {
2350 for (let i = 0, len = this.length; i < len; i++) {
2351 fcn(this[i], i, this);
2352 }
2353 return this
2354 }
2355 ask(fcn) {
2356 const length = this.length;
2357 for (let i = 0; i < Math.min(length, this.length); i++) {
2358 fcn(this[i], i, this);
2359 }
2360 if (length != this.length) {
2361 const name = this.name || this.constructor.name;
2362 const direction = this.length < length ? 'decreasing' : 'increasing';
2363 warn(`AgentArray.ask array mutation: ${name}: ${direction}`);
2364 }
2365 }
2366 with(fcn) {
2367 return this.filter(fcn)
2368 }
2369 other(t) {
2370 return this.filter(o => o !== t)
2371 }
2372 getValues(fcn) {
2373 const props = new AgentArray();
2374 if (isString(fcn)) {
2375 this.forLoop(obj => props.push(obj[fcn]));
2376 } else {
2377 this.forLoop(obj => props.push(fcn(obj)));
2378 }
2379 return props
2380 }
2381 count(reporter) {
2382 return this.reduce((prev, o) => prev + (reporter(o) ? 1 : 0), 0)
2383 }
2384 sum(key) {
2385 return this.reduce((prev, o) => prev + (key ? o[key] : o), 0)
2386 }
2387 avg(key) {
2388 return this.sum(key) / this.length
2389 }
2390 min(key) {
2391 return this.reduce(
2392 (prev, o) => Math.min(prev, key ? o[key] : o),
2393 Infinity
2394 )
2395 }
2396 max(key) {
2397 return this.reduce(
2398 (prev, o) => Math.max(prev, key ? o[key] : o),
2399 -Infinity
2400 )
2401 }
2402 extent(key) {
2403 return [this.min(key), this.max(key)]
2404 }
2405 histogram(key, bins = 10, min = this.min(key), max = this.max(key)) {
2406 const binSize = (max - min) / bins;
2407 const aa = new AgentArray(bins);
2408 aa.fill(0);
2409 this.ask(a => {
2410 const val = key ? a[key] : a;
2411 if (val < min || val > max) {
2412 warn(`histogram bounds error: ${val}: ${min}-${max}`);
2413 } else {
2414 let bin = Math.floor((val - min) / binSize);
2415 if (bin === bins) bin--;
2416 aa[bin]++;
2417 }
2418 });
2419 aa.parameters = { key, bins, min, max, binSize, arraySize: this.length };
2420 return aa
2421 }
2422 clone() {
2423 return this.slice(0)
2424 }
2425 shuffle() {
2426 return shuffle(this)
2427 }
2428 sortBy(reporter, ascending = true) {
2429 sortObjs(this, reporter, ascending);
2430 return this
2431 }
2432 remove(o, f) {
2433 const i = this.agentIndex(o, f);
2434 if (i !== -1) {
2435 this.splice(i, 1);
2436 } else {
2437 console.log(`remove: ${o.id} not in AgentArray`);
2438 }
2439 return this
2440 }
2441 insert(o, f) {
2442 const i = this.sortedIndex(o, f);
2443 if (this[i] === o) {
2444 console.log(`insert: item ${o.id} already in AgentArray`);
2445 return
2446 }
2447 this.splice(i, 0, o);
2448 }
2449 sortedIndex(item, f = identityFcn) {
2450 if (isString(f)) f = propFcn(f);
2451 const value = f(item);
2452 let low = 0;
2453 let high = this.length;
2454 while (low < high) {
2455 const mid = (low + high) >>> 1;
2456 if (f(this[mid]) < value) {
2457 low = mid + 1;
2458 } else {
2459 high = mid;
2460 }
2461 }
2462 return low
2463 }
2464 agentIndex(item, property) {
2465 if (!property) return this.indexOf(item)
2466 const i = this.sortedIndex(item, property);
2467 return this[i] === item ? i : -1
2468 }
2469 contains(item, f) {
2470 return this.agentIndex(item, f) >= 0
2471 }
2472 oneOf() {
2473 return oneOf(this)
2474 }
2475 otherOneOf(agent) {
2476 return otherOneOf(this, agent)
2477 }
2478 otherNOf(n, item) {
2479 if (this.length < n) throw Error('AgentArray: otherNOf: length < N')
2480 return this.clone().remove(item).shuffle().slice(0, n)
2481 }
2482 minOrMaxOf(min, reporter, valueToo = false) {
2483 if (this.isEmpty()) throw Error('min/max OneOf: empty array')
2484 if (typeof reporter === 'string') reporter = propFcn(reporter);
2485 let o = null;
2486 let val = min ? Infinity : -Infinity;
2487 for (let i = 0; i < this.length; i++) {
2488 const a = this[i];
2489 const aval = reporter(a);
2490 if ((min && aval < val) || (!min && aval > val)) {
2491[o, val] = [a, aval];
2492 }
2493 }
2494 return valueToo ? [o, val] : o
2495 }
2496 minOneOf(reporter) {
2497 return this.minOrMaxOf(true, reporter)
2498 }
2499 maxOneOf(reporter) {
2500 return this.minOrMaxOf(false, reporter)
2501 }
2502 minValOf(reporter) {
2503 return this.minOrMaxOf(true, reporter, true)
2504 }
2505 maxValOf(reporter) {
2506 return this.minOrMaxOf(false, reporter, true)
2507 }
2508 nOf(n) {
2509 if (n > this.length)
2510 throw Error(`nOf: n larger than AgentArray: ${n} ${this.length}`)
2511 if (n === this.length) return this
2512 const result = new AgentArray();
2513 while (result.length < n) {
2514 const o = this.oneOf();
2515 if (!(o in result)) result.push(o);
2516 }
2517 return result
2518 }
2519 minOrMaxNOf(min, n, reporter) {
2520 if (n > this.length) {
2521 throw Error('min/max nOf: n larger than AgentArray')
2522 }
2523 const as = this.clone().sortBy(reporter);
2524 return min ? as.slice(0, n) : as.slice(as.length - n)
2525 }
2526 minNOf(n, reporter) {
2527 return this.minOrMaxNOf(true, n, reporter)
2528 }
2529 maxNOf(n, reporter) {
2530 return this.minOrMaxNOf(false, n, reporter)
2531 }
2532}
2533
2534class AgentList extends AgentArray {
2535 constructor(model, ...args) {
2536 if (!model) throw Error('AgentList requires model')
2537 super(...args);
2538 this.model = model;
2539 }
2540 inRect(agent, dx, dy = dx, meToo = false) {
2541 const agents = new AgentList(this.model);
2542 const minX = agent.x - dx;
2543 const maxX = agent.x + dx;
2544 const minY = agent.y - dy;
2545 const maxY = agent.y + dy;
2546 this.ask(a => {
2547 if (minX <= a.x && a.x <= maxX && minY <= a.y && a.y <= maxY) {
2548 if (meToo || agent !== a) agents.push(a);
2549 }
2550 });
2551 return agents
2552 }
2553 inRadius(agent, radius, meToo = false) {
2554 const agents = new AgentList(this.model);
2555 const d2 = radius * radius;
2556 const sqDistance$1 = sqDistance;
2557 this.ask(a => {
2558 if (sqDistance$1(agent.x, agent.y, a.x, a.y) <= d2) {
2559 if (meToo || agent !== a) agents.push(a);
2560 }
2561 });
2562 return agents
2563 }
2564 inCone(agent, radius, coneAngle, heading, meToo = false) {
2565 heading = this.model.toRads(heading);
2566 coneAngle = this.model.toAngleRads(coneAngle);
2567 const agents = new AgentList(this.model);
2568 this.ask(a => {
2569 if (
2570 inCone(
2571 a.x,
2572 a.y,
2573 radius,
2574 coneAngle,
2575 heading,
2576 agent.x,
2577 agent.y
2578 )
2579 ) {
2580 if (meToo || agent !== a) agents.push(a);
2581 }
2582 });
2583 return agents
2584 }
2585}
2586
2587class AgentSet extends AgentArray {
2588 model
2589 name
2590 baseSet
2591 AgentClass
2592 static get [Symbol.species]() {
2593 return AgentArray
2594 }
2595 constructor(model, AgentClass, name, baseSet = null) {
2596 if (name.length === 0) debugger
2597 super();
2598 baseSet = baseSet || this;
2599 Object.assign(this, { model, name, baseSet, AgentClass });
2600 if (this.isBaseSet()) {
2601 this.breeds = {};
2602 this.ID = 0;
2603 } else {
2604 Object.setPrototypeOf(this, Object.getPrototypeOf(baseSet));
2605 this.baseSet.breeds[name] = this;
2606 }
2607 this.protoMixin(AgentClass);
2608 }
2609 protoMixin(AgentClass) {
2610 this.agentProto = new AgentClass();
2611 const agentProto = this.agentProto;
2612 Object.assign(agentProto, {
2613 agentSet: this,
2614 model: this.model,
2615 });
2616 agentProto[this.baseSet.name] = this.baseSet;
2617 if (!agentProto.setBreed) {
2618 Object.assign(agentProto, {
2619 setBreed(breed) {
2620 breed.setBreed(this);
2621 },
2622 getBreed() {
2623 return this.agentSet
2624 },
2625 isBreed(breed) {
2626 return this.agentSet === breed
2627 },
2628 });
2629 Object.defineProperty(agentProto, 'breed', {
2630 get: function () {
2631 return this.agentSet
2632 },
2633 });
2634 } else {
2635 console.log('protoMixin: agentClass.proto already set', AgentClass);
2636 }
2637 }
2638 addAgent(o = undefined) {
2639 o = o || this.agentProto.newInstance(this.agentProto);
2640 if (this.isBreedSet()) {
2641 this.baseSet.addAgent(o);
2642 } else {
2643 o.id = this.ID++;
2644 }
2645 this.push(o);
2646 return o
2647 }
2648 removeAgent(o) {
2649 if (o.id != -1) {
2650 if (this.isBreedSet()) this.baseSet.remove(o, 'id');
2651 this.remove(o, 'id');
2652 }
2653 return this
2654 }
2655 setBreed(a) {
2656 if (a.breed.name === this.name) {
2657 return
2658 }
2659 if (a.agentSet.isBreedSet()) {
2660 a.agentSet.remove(a, 'id');
2661 }
2662 if (this.isBreedSet()) {
2663 this.insert(a, 'id');
2664 }
2665 return Object.setPrototypeOf(a, this.agentProto)
2666 }
2667 setDefault(name, value) {
2668 this.agentProto[name] = value;
2669 return this
2670 }
2671 getDefault(name) {
2672 return this.agentProto[name]
2673 }
2674 newBreed(name) {
2675 return new AgentSet(this.model, this.AgentClass, name, this)
2676 }
2677 isBreedSet() {
2678 return this.baseSet !== this
2679 }
2680 isBaseSet() {
2681 return this.baseSet === this
2682 }
2683 withBreed(breed) {
2684 return this.filter(a => a.agentSet === breed)
2685 }
2686 create() {
2687 console.log(`AgentSet: Abstract method called: ${this}`);
2688 }
2689 clear() {
2690 while (!this.isEmpty()) this.last().die();
2691 }
2692 ask(fcn) {
2693 if (this.length === 0) return
2694 const lastID = this.last().id;
2695 for (let i = 0; i < this.length && this[i].id <= lastID; i++) {
2696 fcn(this[i], i, this);
2697 }
2698 }
2699}
2700
2701class DataSet {
2702 width
2703 height
2704 data
2705 static emptyDataSet(width, height, Type = Array) {
2706 return new DataSet(width, height, new Type(width * height))
2707 }
2708 constructor(width, height, data) {
2709 if (data.length !== width * height) {
2710 throw Error(
2711 `new DataSet length: ${data.length} !== ${width} * ${height}`
2712 )
2713 }
2714 Object.assign(this, { width, height, data });
2715 }
2716 checkXY(x, y) {
2717 if (!this.inBounds(x, y)) {
2718 throw Error(`DataSet: x,y out of range: ${x}, ${y}`)
2719 }
2720 }
2721 inBounds(x, y) {
2722 return (
2723 isBetween(x, 0, this.width - 1) &&
2724 isBetween(y, 0, this.height - 1)
2725 )
2726 }
2727 dataType() {
2728 return this.data.constructor
2729 }
2730 type() {
2731 return this.constructor
2732 }
2733 toIndex(x, y) {
2734 return x + y * this.width
2735 }
2736 toXY(i) {
2737 return [i % this.width, Math.floor(i / this.width)]
2738 }
2739 getXY(x, y) {
2740 return this.data[this.toIndex(x, y)]
2741 }
2742 setXY(x, y, num) {
2743 this.data[this.toIndex(x, y)] = num;
2744 }
2745 sample(x, y, useNearest = true) {
2746 this.checkXY(x, y);
2747 return useNearest ? this.nearest(x, y) : this.bilinear(x, y)
2748 }
2749 nearest(x, y) {
2750 return this.getXY(Math.round(x), Math.round(y))
2751 }
2752 bilinear(x, y) {
2753 const x0 = Math.floor(x);
2754 const y0 = Math.floor(y);
2755 const i = this.toIndex(x0, y0);
2756 const w = this.width;
2757 const dx = x - x0;
2758 const dy = y - y0;
2759 const dx1 = 1 - dx;
2760 const dy1 = 1 - dy;
2761 const f00 = this.data[i];
2762 const f10 = this.data[i + 1] || 0;
2763 const f01 = this.data[i + w] || 0;
2764 const f11 = this.data[i + 1 + w] || 0;
2765 return f00 * dx1 * dy1 + f10 * dx * dy1 + f01 * dx1 * dy + f11 * dx * dy
2766 }
2767 clone() {
2768 return new DataSet(this.width, this.height, this.data.slice(0))
2769 }
2770 emptyDataSet(width, height, type = this.dataType()) {
2771 return DataSet.emptyDataSet(width, height, type)
2772 }
2773 emptyArray(length) {
2774 const Type = this.type();
2775 return new Type(length)
2776 }
2777 resample(width, height, useNearest = true, Type = Array) {
2778 if (width === this.width && height === this.height) return this.clone()
2779 const ds = DataSet.emptyDataSet(width, height, Type);
2780 for (let y = 0; y < height; y++) {
2781 for (let x = 0; x < width; x++) {
2782 ds.setXY(
2783 x,
2784 y,
2785 this.sample(
2786 (x * (this.width - 1)) / (width - 1),
2787 (y * (this.height - 1)) / (height - 1),
2788 useNearest
2789 )
2790 );
2791 }
2792 }
2793 return ds
2794 }
2795 scale(min, max) {
2796 const dsMin = this.min();
2797 const dsMax = this.max();
2798 const dsDelta = dsMax - dsMin;
2799 const delta = max - min;
2800 const m = delta / dsDelta;
2801 const b = min - m * dsMin;
2802 return this.map(x => m * x + b)
2803 }
2804 subset(x, y, width, height) {
2805 if (x + width > this.width || y + height > this.height) {
2806 console.log('subset: x+width', x + width, 'this.width', this.width);
2807 console.log(
2808 'subset: y+height',
2809 y + height,
2810 'this.height',
2811 this.height
2812 );
2813 throw Error('DataSet.subSet: params out of range')
2814 }
2815 const ds = this.emptyDataSet(width, height);
2816 for (let i = 0; i < width; i++) {
2817 for (let j = 0; j < height; j++) {
2818 ds.setXY(i, j, this.getXY(i + x, j + y));
2819 }
2820 }
2821 return ds
2822 }
2823 crop(top, bottom, left, right) {
2824 if (bottom === undefined) {
2825 var { top, bottom, left, right } = top;
2826 }
2827 const width = this.width - left - right;
2828 const height = this.height - top - bottom;
2829 return this.subset(left, top, width, height)
2830 }
2831 map(f) {
2832 return new DataSet(this.width, this.height, this.data.map(f))
2833 }
2834 col(x) {
2835 const [w, h, data] = [this.width, this.height, this.data];
2836 if (x >= w) throw Error(`col: x out of range width: ${w} x: ${x}`)
2837 const colData = this.emptyArray(h);
2838 for (let i = 0; i < h; i++) colData[i] = data[x + i * w];
2839 return colData
2840 }
2841 row(y) {
2842 const [w, h] = [this.width, this.height];
2843 if (y >= h) throw Error(`row: y out of range height: ${h} x: ${y}`)
2844 return this.data.slice(y * w, (y + 1) * w)
2845 }
2846 convertType(type) {
2847 this.data = convertArrayType(this.data, type);
2848 }
2849 concatEast(ds) {
2850 const [w, h] = [this.width, this.height];
2851 const [w1, h1] = [ds.width, ds.height];
2852 if (h !== h1) throw Error(`concatEast: heights not equal ${h}, ${h1}`)
2853 const ds1 = this.emptyDataSet(w + w1, h);
2854 for (let x = 0; x < w; x++) {
2855 for (let y = 0; y < h; y++) {
2856 ds1.setXY(x, y, this.getXY(x, y));
2857 }
2858 }
2859 for (let x = 0; x < w1; x++) {
2860 for (let y = 0; y < h1; y++) {
2861 ds1.setXY(x + w, y, ds.getXY(x, y));
2862 }
2863 }
2864 return ds1
2865 }
2866 concatSouth(dataset) {
2867 const [w, h, data] = [this.width, this.height, this.data];
2868 if (w !== dataset.width) {
2869 throw Error(`concatSouth: widths not equal ${w}, ${dataset.width}`)
2870 }
2871 const data1 = concatArrays(data, dataset.data);
2872 return new DataSet(w, h + dataset.height, data1)
2873 }
2874 transformCoords(x, y, tlx, tly, w, h) {
2875 const xs = ((x - tlx) * (this.width - 1)) / w;
2876 const ys = ((tly - y) * (this.height - 1)) / h;
2877 return [xs, ys]
2878 }
2879 coordSample(x, y, tlx, tly, w, h, useNearest = true) {
2880 const [xs, ys] = this.transformCoords(x, y, tlx, tly, w, h);
2881 return this.sample(xs, ys, useNearest)
2882 }
2883 neighborhood(x, y, array = []) {
2884 array.length = 0;
2885 const clampNeeded =
2886 x === 0 || x === this.width - 1 || y === 0 || y === this.height - 1;
2887 for (let dy = -1; dy <= +1; dy++) {
2888 for (let dx = -1; dx <= +1; dx++) {
2889 let x0 = x + dx;
2890 let y0 = y + dy;
2891 if (clampNeeded) {
2892 x0 = clamp(x0, 0, this.width - 1);
2893 y0 = clamp(y0, 0, this.height - 1);
2894 }
2895 array.push(this.data[this.toIndex(x0, y0)]);
2896 }
2897 }
2898 return array
2899 }
2900 convolve(kernel, factor = 1, crop = false) {
2901 const [x0, y0, h, w] = crop
2902 ? [1, 1, this.height - 1, this.width - 1]
2903 : [0, 0, this.height, this.width];
2904 const newDS = this.emptyDataSet(w, h);
2905 const newData = newDS.data;
2906 let i = 0;
2907 for (let y = y0; y < h; y++) {
2908 for (let x = x0; x < w; x++) {
2909 const nei = this.neighborhood(x, y);
2910 let sum2 = 0;
2911 for (let i2 = 0; i2 < kernel.length; i2++) {
2912 sum2 = sum2 + kernel[i2] * nei[i2];
2913 }
2914 newData[i++] = sum2 * factor;
2915 }
2916 }
2917 return newDS
2918 }
2919 dzdx(n = 2, factor = 1 / 8) {
2920 return this.convolve([-1, 0, 1, -n, 0, n, -1, 0, 1], factor)
2921 }
2922 dzdy(n = 2, factor = 1 / 8) {
2923 return this.convolve([1, n, 1, 0, 0, 0, -1, -n, -1], factor)
2924 }
2925 laplace8() {
2926 return this.convolve([-1, -1, -1, -1, 8, -1, -1, -1, -1])
2927 }
2928 laplace4() {
2929 return this.convolve([0, -1, 0, -1, 4, -1, 0, -1, 0])
2930 }
2931 blur(factor = 0.0625) {
2932 return this.convolve([1, 2, 1, 2, 4, 2, 1, 2, 1], factor)
2933 }
2934 edge() {
2935 return this.convolve([1, 1, 1, 1, -7, 1, 1, 1, 1])
2936 }
2937 slopeAndAspect(cellSize = 1, posAngle = true) {
2938 const dzdx = this.dzdx();
2939 const dzdy = this.dzdy();
2940 let [aspect, slope] = [[], []];
2941 const [h, w] = [dzdx.height, dzdx.width];
2942 for (let y = 0; y < h; y++) {
2943 for (let x = 0; x < w; x++) {
2944 const [gx, gy] = [dzdx.getXY(x, y), dzdy.getXY(x, y)];
2945 slope.push(Math.atan(distance(0, 0, gx, gy)) / cellSize);
2946 let rad = Math.atan2(-gy, -gx);
2947 if (posAngle && rad < 0) rad += 2 * Math.PI;
2948 aspect.push(rad);
2949 }
2950 }
2951 slope = new DataSet(w, h, slope);
2952 aspect = new DataSet(w, h, aspect);
2953 return { slope, aspect, dzdx, dzdy }
2954 }
2955 max() {
2956 return this.data.reduce((a, b) => Math.max(a, b))
2957 }
2958 min() {
2959 return this.data.reduce((a, b) => Math.min(a, b))
2960 }
2961 extent() {
2962 return [this.min(), this.max()]
2963 }
2964 sum() {
2965 return this.data.reduce((a, b) => a + b)
2966 }
2967 normalize(lo = 0, hi = 1, round = false) {
2968 const [min, max] = this.extent();
2969 const scale = 1 / (max - min);
2970 let data = this.data.map(n => lerp(lo, hi, scale * (n - min)));
2971 if (round) data = data.map(n => Math.round(n));
2972 return new DataSet(this.width, this.height, data)
2973 }
2974 equals(dataset) {
2975 return (
2976 this.width === dataset.width &&
2977 this.height === dataset.height &&
2978 arraysEqual(this.data, dataset.data)
2979 )
2980 }
2981}
2982
2983class Link {
2984 static defaults = {
2985 width: 1,
2986 hidden: false,
2987 agentSet: null,
2988 model: null,
2989 name: null,
2990 }
2991 static variables = {
2992 id: null,
2993 theta: 0,
2994 x: 0,
2995 y: 0,
2996 }
2997 constructor() {
2998 Object.assign(this, Link.defaults);
2999 }
3000 newInstance(agentProto) {
3001 const insstance = Object.create(agentProto);
3002 Object.assign(insstance, Link.variables);
3003 return insstance
3004 }
3005 init(from, to) {
3006 this.end0 = from;
3007 this.end1 = to;
3008 from.links.push(this);
3009 to.links.push(this);
3010 }
3011 die() {
3012 if (this.id === -1) return
3013 this.agentSet.removeAgent(this);
3014 removeArrayItem(this.end0.links, this);
3015 removeArrayItem(this.end1.links, this);
3016 this.id = -1;
3017 }
3018 isDead() {
3019 return this.id === -1
3020 }
3021 bothEnds() {
3022 return AgentArray.fromArray([this.end0, this.end1])
3023 }
3024 length() {
3025 return this.end0.distance(this.end1)
3026 }
3027 get heading() {
3028 const { x0, x1, y0, y1 } = this;
3029 const rads = Math.atan2(y1 - y0, x1 - x0);
3030 return this.model.fromRads(rads)
3031 }
3032 otherEnd(turtle) {
3033 if (turtle === this.end0) return this.end1
3034 if (turtle === this.end1) return this.end0
3035 throw Error(`Link.otherEnd: turtle not a link turtle: ${turtle}`)
3036 }
3037 distanceXY(x, y) {
3038 return (
3039 this.bothEnds()
3040 .map(t => t.distanceXY(x, y))
3041 .sum() - this.length()
3042 )
3043 }
3044 get x0() {
3045 return this.end0.x
3046 }
3047 get y0() {
3048 return this.end0.y
3049 }
3050 get z0() {
3051 return this.end0.z ? this.end0.z : 0
3052 }
3053 get x1() {
3054 return this.end1.x
3055 }
3056 get y1() {
3057 return this.end1.y
3058 }
3059 get z1() {
3060 return this.end1.z ? this.end1.z : 0
3061 }
3062}
3063
3064class Links extends AgentSet {
3065 constructor(model, AgentClass, name, baseSet = null) {
3066 super(model, AgentClass, name, baseSet);
3067 }
3068 createOne(from, to, initFcn = link => {}) {
3069 const link = this.addAgent();
3070 link.init(from, to);
3071 initFcn(link);
3072 return link
3073 }
3074 create(from, to, initFcn = link => {}) {
3075 if (!Array.isArray(to)) to = [to];
3076 return to.map(t => {
3077 return this.createOne(from, t, initFcn)
3078 })
3079 }
3080}
3081
3082class World {
3083 maxX = 16
3084 minX = -16
3085 maxY = 16
3086 minY = -16
3087 maxZ = 16
3088 minZ = -16
3089 static defaultOptions(maxX = 16, maxY = maxX, maxZ = Math.max(maxX, maxY)) {
3090 return {
3091 minX: -maxX,
3092 maxX: maxX,
3093 minY: -maxY,
3094 maxY: maxY,
3095 minZ: -maxZ,
3096 maxZ: maxZ,
3097 }
3098 }
3099 static defaultWorld(maxX = 16, maxY = maxX, maxZ = maxX) {
3100 return new World(World.defaultOptions(maxX, maxY, maxZ))
3101 }
3102 constructor(options = {}) {
3103 Object.assign(this, options);
3104 this.setWorld();
3105 }
3106 setWorld() {
3107 let { minX, maxX, minY, maxY, minZ, maxZ } = this;
3108 forLoop({ minX, maxX, minY, maxY, minZ, maxZ }, (val, key) => {
3109 if (!Number.isInteger(val))
3110 throw Error(`World: ${key}:${val} must be an integer`)
3111 });
3112 this.numX = this.width = maxX - minX + 1;
3113 this.numY = this.height = maxY - minY + 1;
3114 this.numZ = this.depth = maxZ - minZ + 1;
3115 this.minXcor = minX - 0.5;
3116 this.maxXcor = maxX + 0.5;
3117 this.minYcor = minY - 0.5;
3118 this.maxYcor = maxY + 0.5;
3119 this.minZcor = minZ - 0.5;
3120 this.maxZcor = maxZ + 0.5;
3121 this.centerX = (minX + maxX) / 2;
3122 this.centerY = (minY + maxY) / 2;
3123 this.centerZ = (minZ + maxZ) / 2;
3124 this.numPatches = this.numX * this.numY;
3125 }
3126 getOptions() {
3127 const { minX, minY, minZ, maxX, maxY, maxZ } = this;
3128 return { minX, minY, minZ, maxX, maxY, maxZ }
3129 }
3130 randomPoint() {
3131 return [
3132 randomFloat2(this.minXcor, this.maxXcor),
3133 randomFloat2(this.minYcor, this.maxYcor),
3134 ]
3135 }
3136 random3DPoint() {
3137 return [
3138 randomFloat2(this.minXcor, this.maxXcor),
3139 randomFloat2(this.minYcor, this.maxYcor),
3140 randomFloat2(this.minZcor, this.maxZcor),
3141 ]
3142 }
3143 randomPatchPoint() {
3144 return [
3145 randomInt2(this.minX, this.maxX),
3146 randomInt2(this.minY, this.maxY),
3147 ]
3148 }
3149 isOnWorld(x, y, z = this.centerZ) {
3150 return (
3151 this.minXcor <= x &&
3152 x <= this.maxXcor &&
3153 this.minYcor <= y &&
3154 y <= this.maxYcor &&
3155 this.minZcor <= z &&
3156 z <= this.maxZcor
3157 )
3158 }
3159 bboxTransform(minX, minY, maxX, maxY) {
3160 return new BBoxTransform(minX, minY, maxX, maxY, this)
3161 }
3162 getWorldSize(patchSize = 1) {
3163 return [this.numX * patchSize, this.numY * patchSize]
3164 }
3165 setEuclideanTransform(ctx, patchSize) {
3166 this.setCanvasSize(ctx.canvas, patchSize);
3167 ctx.restore();
3168 ctx.save();
3169 ctx.scale(patchSize, -patchSize);
3170 ctx.translate(-this.minXcor, -this.maxYcor);
3171 }
3172 patchSize(canvas) {
3173 const { numX, numY } = this;
3174 const { clientWidth: width, clientHeight: height } = canvas;
3175 const xSize = width / numX;
3176 const ySize = height / numY;
3177 if (xSize !== ySize) {
3178 throw Error(`World patchSize: x/y sizes differ ${xSize}, ${ySize}`)
3179 }
3180 return xSize
3181 }
3182 setCanvasSize(canvas, patchSize) {
3183 const [width, height] = this.getWorldSize(patchSize);
3184 setCanvasSize(canvas, width, height);
3185 }
3186 pixelXYtoPatchXY(x, y, patchSize) {
3187 return [this.minXcor + x / patchSize, this.maxYcor - y / patchSize]
3188 }
3189 patchXYtoPixelXY(x, y, patchSize) {
3190 return [(x - this.minXcor) * patchSize, (this.maxYcor - y) * patchSize]
3191 }
3192 xyToPatchIndex(x, y) {
3193 if (!this.isOnWorld(x, y)) return undefined
3194 const { minX, maxX, maxY, numX, maxXcor, maxYcor } = this;
3195 x = x === maxXcor ? maxX : Math.round(x);
3196 y = y === maxYcor ? maxY : Math.round(y);
3197 return x - minX + numX * (maxY - y)
3198 }
3199}
3200class BBoxTransform {
3201 constructor(minX, minY, maxX, maxY, world) {
3202 this.bbox = [minX, minY, maxX, maxY];
3203 if (minX < maxX) console.log('flipX');
3204 if (maxY < minY) console.log('flipY');
3205 if (minX < maxX) [minX, maxX] = [maxX, minX];
3206 if (maxY < minY) [maxY, minY] = [minY, maxY];
3207 const { maxXcor, maxYcor, minXcor, minYcor } = world;
3208 const mx = (minX - maxX) / (maxXcor - minXcor);
3209 const my = (maxY - minY) / (maxYcor - minYcor);
3210 const bx = (minX + maxX - mx * (maxXcor + minXcor)) / 2;
3211 const by = (maxY + minY - my * (maxYcor + minYcor)) / 2;
3212 Object.assign(this, { mx, my, bx, by });
3213 }
3214 toWorld(bboxPoint) {
3215 const { mx, my, bx, by } = this;
3216 const [bboxX, bboxY] = bboxPoint;
3217 const x = (bboxX - bx) / mx;
3218 const y = (bboxY - by) / my;
3219 return [x, y]
3220 }
3221 toBBox(worldPoint) {
3222 const { mx, my, bx, by } = this;
3223 const [worldX, worldY] = worldPoint;
3224 const x = mx * worldX + bx;
3225 const y = my * worldY + by;
3226 return [x, y]
3227 }
3228}
3229
3230const { PI, atan, atan2, cos, floor, log, pow, sin, sinh, sqrt, tan, abs } =
3231 Math;
3232const radians = degrees => (degrees * PI) / 180;
3233const degrees = radians => (radians * 180) / PI;
3234function latlon(lonlat) {
3235 if (typeof lonlat[0] !== 'number') return lonlat.map(val => latlon(val))
3236 return [lonlat[1], lonlat[0]]
3237}
3238function lonz2xFloat(lon, z) {
3239 return ((lon + 180) / 360) * pow(2, z)
3240}
3241function lonz2x(lon, z) {
3242 return floor(lonz2xFloat(lon, z))
3243}
3244function latz2yFloat(lat, z) {
3245 const latRads = radians(lat);
3246 return (1 - log(tan(latRads) + 1 / cos(latRads)) / PI) * pow(2, z - 1)
3247}
3248function latz2y(lat, z) {
3249 return floor(latz2yFloat(lat, z))
3250}
3251function lonlatz2xyFloat(lon, lat, z) {
3252 return [lonz2xFloat(lon, z), latz2yFloat(lat, z)]
3253}
3254function lonlatz2xy(lon, lat, z) {
3255 return [lonz2x(lon, z), latz2y(lat, z)]
3256}
3257function xz2lon(x, z) {
3258 return (x / pow(2, z)) * 360 - 180
3259}
3260function yz2lat(y, z) {
3261 const rads = atan(sinh(PI - (2 * PI * y) / pow(2, z)));
3262 return degrees(rads)
3263}
3264function xyz2lonlat(x, y, z) {
3265 return [xz2lon(x, z), yz2lat(y, z)]
3266}
3267function xyz2centerLonlat(x, y, z) {
3268 return [xz2lon(x + 0.5, z), yz2lat(y + 0.5, z)]
3269}
3270function xyz2bbox(x, y, z, digits = null) {
3271 const [west, north] = xyz2lonlat(x, y, z);
3272 const [east, south] = xyz2lonlat(x + 1, y + 1, z);
3273 if (!digits) return [west, south, east, north]
3274 return precision([west, south, east, north], digits)
3275}
3276function xyInBBox(bbox, pt) {
3277 const [west, south, east, north] = bbox;
3278 const [x, y] = pt;
3279 return isBetween(x, west, east) && isBetween(y, south, north)
3280}
3281function lonLatz2bbox(lon, lat, z) {
3282 const [x, y] = lonlatz2xy(lon, lat, z);
3283 return xyz2bbox(x, y, z)
3284}
3285function Lbounds2bbox(leafletBounds) {
3286 let { lng: west, lat: north } = leafletBounds.getNorthWest();
3287 let { lng: east, lat: south } = leafletBounds.getSouthEast();
3288 return [west, south, east, north]
3289}
3290function tilesBBox(bbox, z) {
3291 const [west, south, east, north] = bbox;
3292 const [westX, northY] = lonlatz2xy(west, north, z);
3293 let [eastX, southY] = lonlatz2xyFloat(east, south, z);
3294 eastX = floor(eastX);
3295 southY = Number.isInteger(southY) ? southY - 1 : floor(southY);
3296 return [westX, southY, eastX, northY]
3297}
3298function bboxCenter(bbox) {
3299 const [west, south, east, north] = bbox;
3300 return [(west + east) / 2, (south + north) / 2]
3301}
3302function bboxCoords(bbox) {
3303 const [west, south, east, north] = bbox;
3304 return [
3305 [west, north],
3306 [east, north],
3307 [east, south],
3308 [west, south],
3309 ]
3310}
3311function bboxBounds(bbox) {
3312 const [west, south, east, north] = bbox;
3313 return [
3314 [west, north],
3315 [east, south],
3316 ]
3317}
3318function bboxFeature$1(bbox, properties = {}) {
3319 const coords = bboxCoords(bbox);
3320 coords.push(coords[0]);
3321 return {
3322 type: 'Feature',
3323 geometry: {
3324 type: 'Polygon',
3325 coordinates: [coords],
3326 },
3327 properties,
3328 }
3329}
3330function bboxFromCenter(center, dLon = 1, dLat = dLon) {
3331 let [lon, lat] = center;
3332 return [lon - dLon, lat - dLat, lon + dLon, lat + dLat]
3333}
3334const santaFeCenter = [-105.978, 35.66];
3335const santaFeBBox = bboxFromCenter(santaFeCenter, 0.2, 0.1);
3336const newMexicoBBox = [-109.050044, 31.332301, -103.001964, 37.000104];
3337const newMexicoCenter = bboxCenter(newMexicoBBox);
3338const usaBBox = [-124.733174, 24.544701, -66.949895, 49.384358];
3339const usaCenter = bboxCenter(usaBBox);
3340function bboxSize(bbox) {
3341 const [west, south, east, north] = bbox;
3342 const width = abs(west - east);
3343 const height = abs(north - south);
3344 return [width, height]
3345}
3346function bboxAspect(bbox) {
3347 const [width, height] = bboxSize(bbox);
3348 return width / height
3349}
3350function bboxMetricSize(bbox) {
3351 const [west, south, east, north] = bbox;
3352 const topLeft = [west, north];
3353 const botLeft = [west, south];
3354 const topRight = [east, north];
3355 const width = lonLat2meters(topLeft, topRight);
3356 const height = lonLat2meters(topLeft, botLeft);
3357 return [width, height]
3358}
3359function bboxMetricAspect(bbox) {
3360 const [width, height] = bboxMetricSize(bbox);
3361 return width / height
3362}
3363function getOsmURL(south, west, north, east) {
3364 const url = 'https://overpass-api.de/api/interpreter?data=';
3365 const params = `\
3366[out:json][timeout:180][bbox:${south},${west},${north},${east}];
3367way[highway];
3368(._;>;);
3369out;`;
3370 return url + encodeURIComponent(params)
3371}
3372async function bbox2osm(bbox) {
3373 const [west, south, east, north] = bbox;
3374 const url = getOsmURL(south, west, north, east);
3375 const osm = await fetch(url).then(resp => resp.json());
3376 return osm
3377}
3378function lonLat2meters(pt1, pt2) {
3379 const [lon1, lat1] = pt1.map(val => radians(val));
3380 const [lon2, lat2] = pt2.map(val => radians(val));
3381 const R = 6378.137;
3382 const dLat = lat2 - lat1;
3383 const dLon = lon2 - lon1;
3384 const a = sin(dLat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dLon / 2) ** 2;
3385 const c = 2 * atan2(sqrt(a), sqrt(1 - a));
3386 const d = R * c;
3387 return d * 1000
3388}
3389function attribution(who = 'osm') {
3390 const prefix = 'Map data &copy; ';
3391 switch (who) {
3392 case 'osm':
3393 return (
3394 prefix + '<a href="https://openstreetmap.org">OpenStreetMap</a>'
3395 )
3396 case 'topo':
3397 return prefix + '<a href="https://opentopomap.org">OpenTopoMap</a>'
3398 case 'topo1':
3399 return (
3400 prefix + '<a href="https://www.maptiler.com">OpenTopoMap</a>'
3401 )
3402 case 'smooth':
3403 return prefix + '<a href="https://stadiamaps.com/">Stadia Maps</a>'
3404 case 'usgs':
3405 return (
3406 prefix +
3407 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>'
3408 )
3409 }
3410 throw Error('gis.attribution: name unknown:', who)
3411}
3412function template(who = 'osm') {
3413 switch (who) {
3414 case 'osm':
3415 return 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'
3416 case 'topo':
3417 return 'https://a.tile.opentopomap.org/{z}/{x}/{y}.png'
3418 case 'topo1':
3419 return 'https://api.maptiler.com/maps/topo/{z}/{x}/{y}.png?key=iQurAP6lArV1UP4gfSVs'
3420 case 'smooth':
3421 return 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png'
3422 case 'usgs':
3423 return 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}'
3424 case 'contour':
3425 return 'https://api.maptiler.com/tiles/contours/tiles.json?key=iQurAP6lArV1UP4gfSVs'
3426 }
3427 throw Error('gis.template: name unknown:', who)
3428}
3429function url(z, x, y, who = 'osm') {
3430 switch (who) {
3431 case 'osm':
3432 return `https://tile.openstreetmap.org/${z}/${x}/${y}.png`
3433 case 'topo':
3434 return `https://tile.opentopomap.org/${z}/${x}/${y}.png`
3435 case 'topo1':
3436 return `https://api.maptiler.com/maps/topo/${z}/${x}/${y}.png?key=iQurAP6lArV1UP4gfSVs`
3437 case 'smooth':
3438 return `https://tiles.stadiamaps.com/tiles/alidade_smooth/${z}/${x}/${y}{r}.png`
3439 case 'usgs':
3440 return `https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/${z}/${y}/${x}`
3441 }
3442 throw Error('gis.url: name unknown:', who)
3443}
3444function elevationTemplate(who = 'mapzen') {
3445 switch (who) {
3446 case 'mapzen':
3447 return `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png`
3448 case 'maptiler':
3449 return `https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=iQurAP6lArV1UP4gfSVs`
3450 case 'redfishUSA':
3451 return `https://s3-us-west-2.amazonaws.com/simtable-elevation-tiles/{z}/{x}/{y}.png`
3452 case 'redfishWorld':
3453 return `https://s3-us-west-2.amazonaws.com/world-elevation-tiles/DEM_tiles/{z}/{x}/{y}.png`
3454 case 'mapbox':
3455 return (
3456 `https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.png?access_token=` +
3457 'pk.eyJ1IjoiYmFja3NwYWNlcyIsImEiOiJjanVrbzI4dncwOXl3M3ptcGJtN3oxMmhoIn0.x9iSCrtm0iADEqixVgPwqQ'
3458 )
3459 }
3460 throw Error('gis.elevationTemplate: name unknown:', who)
3461}
3462
3463var gis = /*#__PURE__*/Object.freeze({
3464 __proto__: null,
3465 Lbounds2bbox: Lbounds2bbox,
3466 attribution: attribution,
3467 bbox2osm: bbox2osm,
3468 bboxAspect: bboxAspect,
3469 bboxBounds: bboxBounds,
3470 bboxCenter: bboxCenter,
3471 bboxCoords: bboxCoords,
3472 bboxFeature: bboxFeature$1,
3473 bboxFromCenter: bboxFromCenter,
3474 bboxMetricAspect: bboxMetricAspect,
3475 bboxMetricSize: bboxMetricSize,
3476 bboxSize: bboxSize,
3477 elevationTemplate: elevationTemplate,
3478 getOsmURL: getOsmURL,
3479 latlon: latlon,
3480 latz2y: latz2y,
3481 latz2yFloat: latz2yFloat,
3482 lonLat2meters: lonLat2meters,
3483 lonLatz2bbox: lonLatz2bbox,
3484 lonlatz2xy: lonlatz2xy,
3485 lonlatz2xyFloat: lonlatz2xyFloat,
3486 lonz2x: lonz2x,
3487 lonz2xFloat: lonz2xFloat,
3488 newMexicoBBox: newMexicoBBox,
3489 newMexicoCenter: newMexicoCenter,
3490 santaFeBBox: santaFeBBox,
3491 santaFeCenter: santaFeCenter,
3492 template: template,
3493 tilesBBox: tilesBBox,
3494 url: url,
3495 usaBBox: usaBBox,
3496 usaCenter: usaCenter,
3497 xyInBBox: xyInBBox,
3498 xyz2bbox: xyz2bbox,
3499 xyz2centerLonlat: xyz2centerLonlat,
3500 xyz2lonlat: xyz2lonlat,
3501 xz2lon: xz2lon,
3502 yz2lat: yz2lat
3503});
3504
3505class GeoWorld extends World {
3506 static defaultOptions(bbox = newMexicoBBox, patchesWidth = 100) {
3507 return {
3508 bbox,
3509 patchesWidth,
3510 }
3511 }
3512 constructor(options = GeoWorld.defaultOptions()) {
3513 let { bbox: bbox$1, patchesWidth } = options;
3514 let json;
3515 if (!Array.isArray(bbox$1)) {
3516 json = bbox$1;
3517 bbox$1 = bbox(json);
3518 }
3519 const aspect = bboxMetricAspect(bbox$1);
3520 const maxZ = Math.round(patchesWidth / 2);
3521 super({
3522 minX: 0,
3523 maxX: patchesWidth - 1,
3524 minY: 0,
3525 maxY: Math.round(patchesWidth / aspect),
3526 minZ: -maxZ,
3527 maxZ: maxZ,
3528 });
3529 this.bbox = bbox$1;
3530 this.xfm = this.bboxTransform(...bbox$1);
3531 if (json) this.geojson = json;
3532 }
3533 toGeo(x, y) {
3534 return this.xfm.toBBox([x, y])
3535 }
3536 toWorld(geoX, geoY) {
3537 return this.xfm.toWorld([geoX, geoY])
3538 }
3539 bboxCenter() {
3540 return bboxCenter(this.bbox)
3541 }
3542 bboxCoords() {
3543 return bboxCoords(this.bbox)
3544 }
3545 bboxFeature(options = {}) {
3546 return bboxFeature$1(this.bbox, options)
3547 }
3548}
3549
3550class Patches extends AgentSet {
3551 constructor(model, AgentClass, name, baseSet = null) {
3552 super(model, AgentClass, name, baseSet);
3553 if (this.isBreedSet()) return
3554 this.populate();
3555 this.labels = [];
3556 }
3557 populate() {
3558 repeat(this.model.world.numX * this.model.world.numY, i => {
3559 this.addAgent();
3560 });
3561 }
3562 neighborsOffsets(x, y) {
3563 const { minX, maxX, minY, maxY, numX } = this.model.world;
3564 if (x === minX) {
3565 if (y === minY) return [-numX, -numX + 1, 1]
3566 if (y === maxY) return [1, numX + 1, numX]
3567 return [-numX, -numX + 1, 1, numX + 1, numX]
3568 }
3569 if (x === maxX) {
3570 if (y === minY) return [-numX - 1, -numX, -1]
3571 if (y === maxY) return [numX, numX - 1, -1]
3572 return [-numX - 1, -numX, numX, numX - 1, -1]
3573 }
3574 if (y === minY) return [-numX - 1, -numX, -numX + 1, 1, -1]
3575 if (y === maxY) return [1, numX + 1, numX, numX - 1, -1]
3576 return [-numX - 1, -numX, -numX + 1, 1, numX + 1, numX, numX - 1, -1]
3577 }
3578 neighbors4Offsets(x, y) {
3579 const numX = this.model.world.numX;
3580 return this.neighborsOffsets(x, y).filter(
3581 n => Math.abs(n) === 1 || Math.abs(n) === numX
3582 )
3583 }
3584 neighbors(patch) {
3585 const { id, x, y } = patch;
3586 const offsets = this.neighborsOffsets(x, y);
3587 const as = new AgentList(this.model, offsets.length);
3588 offsets.forEach((o, i) => {
3589 as[i] = this[o + id];
3590 });
3591 return as
3592 }
3593 neighbors4(patch) {
3594 const { id, x, y } = patch;
3595 const offsets = this.neighbors4Offsets(x, y);
3596 const as = new AgentList(this.model, offsets.length);
3597 offsets.forEach((o, i) => {
3598 as[i] = this[o + id];
3599 });
3600 return as
3601 }
3602 importDataSet(dataSet, property, useNearest = false) {
3603 if (this.isBreedSet()) {
3604 warn('Patches: exportDataSet called with breed, using patches');
3605 this.baseSet.importDataSet(dataSet, property, useNearest);
3606 return
3607 }
3608 const { numX, numY } = this.model.world;
3609 const dataset = dataSet.resample(numX, numY, useNearest);
3610 this.ask(p => {
3611 p[property] = dataset.data[p.id];
3612 });
3613 }
3614 exportDataSet(property, Type = Array) {
3615 if (this.isBreedSet()) {
3616 warn('Patches: exportDataSet called with breed, using patches');
3617 return this.baseSet.exportDataSet(property, Type)
3618 }
3619 const { numX, numY } = this.model.world;
3620 let data = this.props(property);
3621 data = convertArrayType(data, Type);
3622 return new DataSet(numX, numY, data)
3623 }
3624 patchIndex(x, y) {
3625 const { minX, maxY, numX } = this.model.world;
3626 return x - minX + numX * (maxY - y)
3627 }
3628 patch(x, y) {
3629 if (!this.model.world.isOnWorld(x, y)) return undefined
3630 const intX =
3631 x === this.model.world.maxXcor
3632 ? this.model.world.maxX
3633 : Math.round(x);
3634 const intY =
3635 y === this.model.world.maxYcor
3636 ? this.model.world.maxY
3637 : Math.round(y);
3638 return this.patchXY(intX, intY)
3639 }
3640 patchXY(x, y) {
3641 return this[this.patchIndex(x, y)]
3642 }
3643 patchRect(p, dx, dy = dx, meToo = true) {
3644 if (p.rectCache) {
3645 const index = this.cacheIndex(dx, dy, meToo);
3646 const rect = p.rectCache[index];
3647 if (rect) return rect
3648 }
3649 const rect = new AgentList(this.model);
3650 let { minX, maxX, minY, maxY } = this.model.world;
3651 minX = Math.max(minX, p.x - dx);
3652 maxX = Math.min(maxX, p.x + dx);
3653 minY = Math.max(minY, p.y - dy);
3654 maxY = Math.min(maxY, p.y + dy);
3655 for (let y = minY; y <= maxY; y++) {
3656 for (let x = minX; x <= maxX; x++) {
3657 const pnext = this.patchXY(x, y);
3658 if (p !== pnext || meToo) rect.push(pnext);
3659 }
3660 }
3661 return rect
3662 }
3663 patchRectXY(x, y, dx, dy = dx, meToo = true) {
3664 return this.patchRect(this.patch(x, y), dx, dy, meToo)
3665 }
3666 cacheIndex(dx, dy = dx, meToo = true) {
3667 return (2 * dx + 1) * (2 * dy + 1) + (meToo ? 0 : -1)
3668 }
3669 cacheRect(dx, dy = dx, meToo = true, clear = true) {
3670 const index = this.cacheIndex(dx, dy, meToo);
3671 this.ask(p => {
3672 if (!p.rectCache || clear) p.rectCache = [];
3673 const rect = this.inRect(p, dx, dy, meToo);
3674 p.rectCache[index] = rect;
3675 });
3676 }
3677 inRect(patch, dx, dy = dx, meToo = true) {
3678 const pRect = this.patchRect(patch, dx, dy, meToo);
3679 if (this.isBaseSet()) return pRect
3680 return pRect.withBreed(this)
3681 }
3682 inRadius(patch, radius, meToo = true) {
3683 const dxy = Math.ceil(radius);
3684 const pRect = this.inRect(patch, dxy, dxy, meToo);
3685 return pRect.inRadius(patch, radius, meToo)
3686 }
3687 inCone(patch, radius, coneAngle, heading, meToo = true) {
3688 const dxy = Math.ceil(radius);
3689 const pRect = this.inRect(patch, dxy, dxy, meToo);
3690 return pRect.inCone(patch, radius, coneAngle, heading, meToo)
3691 }
3692 patchAtHeadingAndDistance(agent, heading, distance) {
3693 heading = this.model.toRads(heading);
3694 let { x, y } = agent;
3695 x = x + distance * Math.cos(heading);
3696 y = y + distance * Math.sin(heading);
3697 return this.patch(x, y)
3698 }
3699 isOnEdge(patch) {
3700 const { x, y } = patch;
3701 const { minX, maxX, minY, maxY } = this.model.world;
3702 return x === minX || x === maxX || y === minY || y === maxY
3703 }
3704 edgePatches() {
3705 return this.filter(p => this.isOnEdge(p))
3706 }
3707 diffuse(v, rate) {
3708 this.diffuseN(8, v, rate);
3709 }
3710 diffuse4(v, rate) {
3711 this.diffuseN(4, v, rate);
3712 }
3713 diffuseN(n, v, rate) {
3714 if (this[0]._diffuseNext === undefined) {
3715 for (let i = 0; i < this.length; i++) this[i]._diffuseNext = 0;
3716 }
3717 for (let i = 0; i < this.length; i++) {
3718 const p = this[i];
3719 const dv = p[v] * rate;
3720 const dvn = dv / n;
3721 const neighbors = n === 8 ? p.neighbors : p.neighbors4;
3722 const nn = neighbors.length;
3723 p._diffuseNext += p[v] - dv + (n - nn) * dvn;
3724 for (let i = 0; i < neighbors.length; i++) {
3725 neighbors[i]._diffuseNext += dvn;
3726 }
3727 }
3728 for (let i = 0; i < this.length; i++) {
3729 const p = this[i];
3730 p[v] = p._diffuseNext;
3731 p._diffuseNext = 0;
3732 }
3733 }
3734}
3735
3736class Patch {
3737 static defaults = {
3738 turtles: null,
3739 z: 0,
3740 agentSet: null,
3741 model: null,
3742 name: null,
3743 }
3744 static variables = {
3745 }
3746 constructor() {
3747 Object.assign(this, Patch.defaults);
3748 }
3749 newInstance(agentProto) {
3750 const insstance = Object.create(agentProto);
3751 Object.assign(insstance, Patch.variables);
3752 return insstance
3753 }
3754 get x() {
3755 return (this.id % this.model.world.width) + this.model.world.minX
3756 }
3757 get y() {
3758 return (
3759 this.model.world.maxY - Math.floor(this.id / this.model.world.width)
3760 )
3761 }
3762 isOnEdge() {
3763 return this.patches.isOnEdge(this)
3764 }
3765 get neighbors() {
3766 const n = this.patches.neighbors(this);
3767 Object.defineProperty(this, 'neighbors', { value: n, enumerable: true });
3768 return n
3769 }
3770 get neighbors4() {
3771 const n = this.patches.neighbors4(this);
3772 Object.defineProperty(this, 'neighbors4', {
3773 value: n,
3774 enumerable: true,
3775 });
3776 return n
3777 }
3778 get turtlesHere() {
3779 if (this.turtles == null) {
3780 this.patches.ask(p => {
3781 p.turtles = new AgentList(this.model);
3782 });
3783 this.model.turtles.ask(t => {
3784 t.patch.turtles.push(t);
3785 });
3786 }
3787 return this.turtles
3788 }
3789 breedsHere(breed) {
3790 const turtles = this.turtlesHere;
3791 return turtles.withBreed(breed)
3792 }
3793 distanceXY(x, y, z = null) {
3794 const useZ = z != null && this.z != null;
3795 return useZ
3796 ? distance3(this.x, this.y, this.z, x, y, z)
3797 : distance(this.x, this.y, x, y)
3798 }
3799 distance(agent) {
3800 const { x, y, z } = agent;
3801 return this.distanceXY(x, y, z)
3802 }
3803 towards(agent) {
3804 return this.towardsXY(agent.x, agent.y)
3805 }
3806 towardsXY(x, y) {
3807 let rads = radiansTowardXY(this.x, this.y, x, y);
3808 return this.model.fromRads(rads)
3809 }
3810 patchAt(dx, dy) {
3811 return this.patches.patch(this.x + dx, this.y + dy)
3812 }
3813 patchAtHeadingAndDistance(heading, distance) {
3814 return this.patches.patchAtHeadingAndDistance(this, heading, distance)
3815 }
3816 sprout(num = 1, breed = this.model.turtles, initFcn = turtle => {}) {
3817 return breed.create(num, turtle => {
3818 turtle.setxy(this.x, this.y);
3819 initFcn(turtle);
3820 })
3821 }
3822}
3823
3824class Turtles extends AgentSet {
3825 constructor(model, AgentClass, name, baseSet = null) {
3826 super(model, AgentClass, name, baseSet);
3827 if (!name) console.log('Turtles empty name', this);
3828 }
3829 createOne(initFcn = turtle => {}) {
3830 const turtle = this.addAgent();
3831 turtle.theta = randomFloat(Math.PI * 2);
3832 const p = turtle.patch;
3833 if (p.turtles != null) {
3834 p.turtles.push(turtle);
3835 }
3836 initFcn(turtle);
3837 return turtle
3838 }
3839 create(num, initFcn = turtle => {}) {
3840 return repeat(num, (i, a) => {
3841 a.push(this.createOne(initFcn));
3842 })
3843 }
3844 closestTurtle(x, y, radius) {
3845 const ts = this.inPatchRectXY(x, y, radius);
3846 if (ts.length === 0) return null
3847 return ts.minOneOf(t => t.distanceXY(x, y))
3848 }
3849 inPatches(patches) {
3850 let array = new AgentList(this.model);
3851 for (const p of patches) array.push(...p.turtlesHere);
3852 if (this.isBreedSet()) array = array.filter(a => a.agentSet === this);
3853 return array
3854 }
3855 inPatchRect(turtle, dx, dy = dx, meToo = false) {
3856 const agents = this.inPatchRectXY(turtle.x, turtle.y, dx, dy);
3857 if (!meToo) removeArrayItem(agents, turtle);
3858 return agents
3859 }
3860 inPatchRectXY(x, y, dx, dy = dx) {
3861 const patches = this.model.patches.patchRectXY(x, y, dx, dy, true);
3862 return this.inPatches(patches)
3863 }
3864 inRadius(turtle, radius, meToo = false) {
3865 const agents = this.inPatchRect(turtle, radius, radius, true);
3866 return agents.inRadius(turtle, radius, meToo)
3867 }
3868 inCone(turtle, radius, coneAngle, meToo = false) {
3869 const agents = this.inPatchRect(turtle, radius, radius, true);
3870 return agents.inCone(turtle, radius, coneAngle, turtle.heading, meToo)
3871 }
3872 layoutCircle(radius = this.model.world.maxX * 0.9, center = [0, 0]) {
3873 const startAngle = Math.PI / 2;
3874 const direction = -1;
3875 const dTheta = (2 * Math.PI) / this.length;
3876 const [x0, y0] = center;
3877 this.ask((turtle, i) => {
3878 turtle.setxy(x0, y0);
3879 turtle.theta = startAngle + direction * dTheta * i;
3880 turtle.forward(radius);
3881 });
3882 }
3883}
3884
3885class Turtle {
3886 static defaults = {
3887 atEdge: 'wrap',
3888 hidden: false,
3889 z: 0,
3890 agentSet: null,
3891 model: null,
3892 name: null,
3893 }
3894 static variables = {
3895 id: null,
3896 theta: 0,
3897 x: 0,
3898 y: 0,
3899 }
3900 constructor() {
3901 Object.assign(this, Turtle.defaults);
3902 }
3903 newInstance(agentProto) {
3904 const insstance = Object.create(agentProto);
3905 Object.assign(insstance, Turtle.variables);
3906 return insstance
3907 }
3908 die() {
3909 if (this.id === -1) return
3910 this.agentSet.removeAgent(this);
3911 if (this.hasOwnProperty('links')) {
3912 while (this.links.length > 0) this.links[0].die();
3913 }
3914 if (this.patch && this.patch.turtles)
3915 removeArrayItem(this.patch.turtles, this);
3916 this.id = -1;
3917 }
3918 isDead() {
3919 return this.id === -1
3920 }
3921 hatch(num = 1, breed = this.agentSet, init = turtle => {}) {
3922 return breed.create(num, turtle => {
3923 turtle.setxy(this.x, this.y, this.z);
3924 turtle.theta = this.theta;
3925 init(turtle);
3926 })
3927 }
3928 get links() {
3929 Object.defineProperty(this, 'links', {
3930 value: new AgentList(this.model),
3931 enumerable: true,
3932 });
3933 return this.links
3934 }
3935 get patch() {
3936 return this.model.patches.patch(this.x, this.y)
3937 }
3938 get heading() {
3939 return this.model.fromRads(this.theta)
3940 }
3941 set heading(heading) {
3942 this.theta = this.model.toRads(heading);
3943 }
3944 subtractHeading(heading) {
3945 return subtractHeadings(heading, this.heading)
3946 }
3947 setxy(x, y, z = undefined) {
3948 const p0 = this.patch;
3949 this.x = x;
3950 this.y = y;
3951 if (z != null) this.z = z;
3952 this.checkXYZ(p0);
3953 }
3954 checkXYZ(p0) {
3955 this.checkEdge();
3956 this.checkPatch(p0);
3957 }
3958 checkEdge() {
3959 const { x, y, z } = this;
3960 if (!this.model.world.isOnWorld(x, y, z) && this.atEdge !== 'OK') {
3961 this.handleEdge(x, y, z);
3962 }
3963 }
3964 checkPatch(p0) {
3965 const p = this.patch;
3966 if (p != p0) {
3967 if (p0 && p0.turtles) removeArrayItem(p0.turtles, this);
3968 if (p && p.turtles) p.turtles.push(this);
3969 }
3970 }
3971 handleEdge(x, y, z = undefined) {
3972 let atEdge = this.atEdge;
3973 if (isString(atEdge)) {
3974 const { minXcor, maxXcor, minYcor, maxYcor, minZcor, maxZcor } =
3975 this.model.world;
3976 if (atEdge === 'wrap') {
3977 this.x = wrap(x, minXcor, maxXcor);
3978 this.y = wrap(y, minYcor, maxYcor);
3979 if (z != null) this.z = wrap(z, minZcor, maxZcor);
3980 } else if (atEdge === 'die') {
3981 this.die();
3982 } else if (atEdge === 'random') {
3983 this.setxy(...this.model.world.randomPoint());
3984 } else if (atEdge === 'clamp' || atEdge === 'bounce') {
3985 this.x = clamp(x, minXcor, maxXcor);
3986 this.y = clamp(y, minYcor, maxYcor);
3987 if (z != null) this.z = clamp(z, minZcor, maxZcor);
3988 if (atEdge === 'bounce') {
3989 if (this.x === minXcor || this.x === maxXcor) {
3990 this.theta = Math.PI - this.theta;
3991 } else if (this.y === minYcor || this.y === maxYcor) {
3992 this.theta = -this.theta;
3993 } else if (this.z === minZcor || this.z === maxZcor) {
3994 if (this.pitch) {
3995 this.pitch = -this.pitch;
3996 } else {
3997 this.z = wrap(z, minZcor, maxZcor);
3998 }
3999 }
4000 }
4001 } else {
4002 throw Error(`turtle.handleEdge: bad atEdge: ${atEdge}`)
4003 }
4004 } else {
4005 this.atEdge(this);
4006 }
4007 }
4008 moveTo(agent) {
4009 this.setxy(agent.x, agent.y, agent.z);
4010 }
4011 forward(d) {
4012 this.setxy(
4013 this.x + d * Math.cos(this.theta),
4014 this.y + d * Math.sin(this.theta)
4015 );
4016 }
4017 rotate(angle) {
4018 angle = this.model.toCCW(angle);
4019 this.heading += angle;
4020 }
4021 right(angle) {
4022 this.rotate(-angle);
4023 }
4024 left(angle) {
4025 this.rotate(angle);
4026 }
4027 face(agent) {
4028 this.heading = this.towards(agent);
4029 }
4030 facexy(x, y) {
4031 this.heading = this.towardsXY(x, y);
4032 }
4033 patchAhead(distance) {
4034 return this.patchAtHeadingAndDistance(this.heading, distance)
4035 }
4036 patchRightAndAhead(angle, distance) {
4037 angle = this.model.toCCW(angle);
4038 return this.patchAtHeadingAndDistance(this.heading - angle, distance)
4039 }
4040 patchLeftAndAhead(angle, distance) {
4041 return this.patchRightAndAhead(-angle, distance)
4042 }
4043 canMove(distance) {
4044 return this.patchAhead(distance) != null
4045 }
4046 distanceXY(x, y, z = null) {
4047 const useZ = z != null && this.z != null;
4048 return useZ
4049 ? distance3(this.x, this.y, this.z, x, y, z)
4050 : distance(this.x, this.y, x, y)
4051 }
4052 distance(agent) {
4053 const { x, y, z } = agent;
4054 return this.distanceXY(x, y, z)
4055 }
4056 get dx() {
4057 return Math.cos(this.theta)
4058 }
4059 get dy() {
4060 return Math.sin(this.theta)
4061 }
4062 towards(agent) {
4063 return this.towardsXY(agent.x, agent.y)
4064 }
4065 towardsXY(x, y) {
4066 let rads = radiansTowardXY(this.x, this.y, x, y);
4067 return this.model.fromRads(rads)
4068 }
4069 patchAt(dx, dy) {
4070 return this.model.patches.patch(this.x + dx, this.y + dy)
4071 }
4072 patchAtHeadingAndDistance(heading, distance) {
4073 return this.model.patches.patchAtHeadingAndDistance(
4074 this,
4075 heading,
4076 distance
4077 )
4078 }
4079 otherEnd(l) {
4080 return l.end0 === this ? l.end1 : l.end0
4081 }
4082 linkNeighbors() {
4083 return this.links.map(l => this.otherEnd(l))
4084 }
4085 isLinkNeighbor(t) {
4086 return t in this.linkNeighbors()
4087 }
4088}
4089
4090class Model {
4091 world
4092 patches
4093 turtles
4094 links
4095 ticks
4096 constructor(worldOptions = World.defaultOptions()) {
4097 this.resetModel(worldOptions);
4098 this.setAutoTick(true);
4099 this.setGeometry('heading');
4100 }
4101 initAgentSet(name, AgentsetClass, AgentClass) {
4102 this[name] = new AgentsetClass(this, AgentClass, name);
4103 }
4104 options2world(worldOptions) {
4105 return worldOptions.bbox
4106 ? new GeoWorld(worldOptions)
4107 : new World(worldOptions)
4108 }
4109 resetModel(worldOptions) {
4110 this.ticks = 0;
4111 this.world =
4112 worldOptions.maxXcor === undefined
4113 ? this.options2world(worldOptions)
4114 : worldOptions;
4115 this.initAgentSet('patches', Patches, Patch);
4116 this.initAgentSet('turtles', Turtles, Turtle);
4117 this.initAgentSet('links', Links, Link);
4118 }
4119 reset(worldOptions = this.world) {
4120 this.resetModel(worldOptions);
4121 }
4122 tick() {
4123 this.ticks++;
4124 }
4125 async startup() {}
4126 setup() {}
4127 step() {}
4128 setAutoTick(autoTick = true) {
4129 const isAutoTick = !!this.stepTarget;
4130 if (autoTick) {
4131 if (isAutoTick) return
4132 this.stepTarget = this.step;
4133 this.step = new Proxy(this.stepTarget, {
4134 apply: (target, thisArg, args) => {
4135 this.stepTarget();
4136 this.tick();
4137 },
4138 });
4139 } else {
4140 this.step = this.stepTarget;
4141 delete this.stepTarget;
4142 }
4143 }
4144 patchBreeds(breedNames) {
4145 for (const breedName of breedNames.split(' ')) {
4146 this[breedName] = this.patches.newBreed(breedName);
4147 }
4148 }
4149 turtleBreeds(breedNames) {
4150 for (const breedName of breedNames.split(' ')) {
4151 this[breedName] = this.turtles.newBreed(breedName);
4152 }
4153 }
4154 linkBreeds(breedNames) {
4155 for (const breedName of breedNames.split(' ')) {
4156 this[breedName] = this.links.newBreed(breedName);
4157 }
4158 }
4159 setGeometry(name = 'heading') {
4160 const geometry = geometries[name];
4161 if (!geometry) throw Error(`setGeometry: ${name} geometry not defined`)
4162 Object.assign(this, geometry);
4163 }
4164}
4165const toDeg = 180 / Math.PI;
4166const toRad = Math.PI / 180;
4167const geometries = {
4168 radians: {
4169 toRads: rads => rads,
4170 fromRads: rads => rads,
4171 toAngleRads: rads => rads,
4172 fromAngleRads: rads => rads,
4173 toCCW: angle => angle,
4174 },
4175 degrees: {
4176 toRads: deg => deg * toRad,
4177 fromRads: rads => rads * toDeg,
4178 toAngleRads: deg => deg * toRad,
4179 fromAngleRads: rads => rads * toDeg,
4180 toCCW: angle => angle,
4181 },
4182 heading: {
4183 toRads: deg => (90 - deg) * toRad,
4184 fromRads: rads => 90 - rads * toDeg,
4185 toAngleRads: deg => deg * toRad,
4186 fromAngleRads: rads => rads * toDeg,
4187 toCCW: angle => -angle,
4188 },
4189};
4190
4191const _lut = [];
4192for ( let i = 0; i < 256; i ++ ) {
4193 _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
4194}
4195let _seed = 1234567;
4196const MathUtils = {
4197 DEG2RAD: Math.PI / 180,
4198 RAD2DEG: 180 / Math.PI,
4199 generateUUID: function () {
4200 const d0 = Math.random() * 0xffffffff | 0;
4201 const d1 = Math.random() * 0xffffffff | 0;
4202 const d2 = Math.random() * 0xffffffff | 0;
4203 const d3 = Math.random() * 0xffffffff | 0;
4204 const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' +
4205 _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' +
4206 _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] +
4207 _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ];
4208 return uuid.toUpperCase();
4209 },
4210 clamp: function ( value, min, max ) {
4211 return Math.max( min, Math.min( max, value ) );
4212 },
4213 euclideanModulo: function ( n, m ) {
4214 return ( ( n % m ) + m ) % m;
4215 },
4216 mapLinear: function ( x, a1, a2, b1, b2 ) {
4217 return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
4218 },
4219 lerp: function ( x, y, t ) {
4220 return ( 1 - t ) * x + t * y;
4221 },
4222 smoothstep: function ( x, min, max ) {
4223 if ( x <= min ) return 0;
4224 if ( x >= max ) return 1;
4225 x = ( x - min ) / ( max - min );
4226 return x * x * ( 3 - 2 * x );
4227 },
4228 smootherstep: function ( x, min, max ) {
4229 if ( x <= min ) return 0;
4230 if ( x >= max ) return 1;
4231 x = ( x - min ) / ( max - min );
4232 return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
4233 },
4234 randInt: function ( low, high ) {
4235 return low + Math.floor( Math.random() * ( high - low + 1 ) );
4236 },
4237 randFloat: function ( low, high ) {
4238 return low + Math.random() * ( high - low );
4239 },
4240 randFloatSpread: function ( range ) {
4241 return range * ( 0.5 - Math.random() );
4242 },
4243 seededRandom: function ( s ) {
4244 if ( s !== undefined ) _seed = s % 2147483647;
4245 _seed = _seed * 16807 % 2147483647;
4246 return ( _seed - 1 ) / 2147483646;
4247 },
4248 degToRad: function ( degrees ) {
4249 return degrees * MathUtils.DEG2RAD;
4250 },
4251 radToDeg: function ( radians ) {
4252 return radians * MathUtils.RAD2DEG;
4253 },
4254 isPowerOfTwo: function ( value ) {
4255 return ( value & ( value - 1 ) ) === 0 && value !== 0;
4256 },
4257 ceilPowerOfTwo: function ( value ) {
4258 return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
4259 },
4260 floorPowerOfTwo: function ( value ) {
4261 return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
4262 },
4263 setQuaternionFromProperEuler: function ( q, a, b, c, order ) {
4264 const cos = Math.cos;
4265 const sin = Math.sin;
4266 const c2 = cos( b / 2 );
4267 const s2 = sin( b / 2 );
4268 const c13 = cos( ( a + c ) / 2 );
4269 const s13 = sin( ( a + c ) / 2 );
4270 const c1_3 = cos( ( a - c ) / 2 );
4271 const s1_3 = sin( ( a - c ) / 2 );
4272 const c3_1 = cos( ( c - a ) / 2 );
4273 const s3_1 = sin( ( c - a ) / 2 );
4274 switch ( order ) {
4275 case 'XYX':
4276 q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 );
4277 break;
4278 case 'YZY':
4279 q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 );
4280 break;
4281 case 'ZXZ':
4282 q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 );
4283 break;
4284 case 'XZX':
4285 q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 );
4286 break;
4287 case 'YXY':
4288 q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 );
4289 break;
4290 case 'ZYZ':
4291 q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 );
4292 break;
4293 default:
4294 console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order );
4295 }
4296 }
4297};
4298class Quaternion {
4299 constructor( x = 0, y = 0, z = 0, w = 1 ) {
4300 Object.defineProperty( this, 'isQuaternion', { value: true } );
4301 this._x = x;
4302 this._y = y;
4303 this._z = z;
4304 this._w = w;
4305 }
4306 static slerp( qa, qb, qm, t ) {
4307 return qm.copy( qa ).slerp( qb, t );
4308 }
4309 static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {
4310 let x0 = src0[ srcOffset0 + 0 ],
4311 y0 = src0[ srcOffset0 + 1 ],
4312 z0 = src0[ srcOffset0 + 2 ],
4313 w0 = src0[ srcOffset0 + 3 ];
4314 const x1 = src1[ srcOffset1 + 0 ],
4315 y1 = src1[ srcOffset1 + 1 ],
4316 z1 = src1[ srcOffset1 + 2 ],
4317 w1 = src1[ srcOffset1 + 3 ];
4318 if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {
4319 let s = 1 - t;
4320 const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,
4321 dir = ( cos >= 0 ? 1 : - 1 ),
4322 sqrSin = 1 - cos * cos;
4323 if ( sqrSin > Number.EPSILON ) {
4324 const sin = Math.sqrt( sqrSin ),
4325 len = Math.atan2( sin, cos * dir );
4326 s = Math.sin( s * len ) / sin;
4327 t = Math.sin( t * len ) / sin;
4328 }
4329 const tDir = t * dir;
4330 x0 = x0 * s + x1 * tDir;
4331 y0 = y0 * s + y1 * tDir;
4332 z0 = z0 * s + z1 * tDir;
4333 w0 = w0 * s + w1 * tDir;
4334 if ( s === 1 - t ) {
4335 const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );
4336 x0 *= f;
4337 y0 *= f;
4338 z0 *= f;
4339 w0 *= f;
4340 }
4341 }
4342 dst[ dstOffset ] = x0;
4343 dst[ dstOffset + 1 ] = y0;
4344 dst[ dstOffset + 2 ] = z0;
4345 dst[ dstOffset + 3 ] = w0;
4346 }
4347 static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) {
4348 const x0 = src0[ srcOffset0 ];
4349 const y0 = src0[ srcOffset0 + 1 ];
4350 const z0 = src0[ srcOffset0 + 2 ];
4351 const w0 = src0[ srcOffset0 + 3 ];
4352 const x1 = src1[ srcOffset1 ];
4353 const y1 = src1[ srcOffset1 + 1 ];
4354 const z1 = src1[ srcOffset1 + 2 ];
4355 const w1 = src1[ srcOffset1 + 3 ];
4356 dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1;
4357 dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1;
4358 dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1;
4359 dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1;
4360 return dst;
4361 }
4362 get x() {
4363 return this._x;
4364 }
4365 set x( value ) {
4366 this._x = value;
4367 this._onChangeCallback();
4368 }
4369 get y() {
4370 return this._y;
4371 }
4372 set y( value ) {
4373 this._y = value;
4374 this._onChangeCallback();
4375 }
4376 get z() {
4377 return this._z;
4378 }
4379 set z( value ) {
4380 this._z = value;
4381 this._onChangeCallback();
4382 }
4383 get w() {
4384 return this._w;
4385 }
4386 set w( value ) {
4387 this._w = value;
4388 this._onChangeCallback();
4389 }
4390 set( x, y, z, w ) {
4391 this._x = x;
4392 this._y = y;
4393 this._z = z;
4394 this._w = w;
4395 this._onChangeCallback();
4396 return this;
4397 }
4398 clone() {
4399 return new this.constructor( this._x, this._y, this._z, this._w );
4400 }
4401 copy( quaternion ) {
4402 this._x = quaternion.x;
4403 this._y = quaternion.y;
4404 this._z = quaternion.z;
4405 this._w = quaternion.w;
4406 this._onChangeCallback();
4407 return this;
4408 }
4409 setFromEuler( euler, update ) {
4410 if ( ! ( euler && euler.isEuler ) ) {
4411 throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );
4412 }
4413 const x = euler._x, y = euler._y, z = euler._z, order = euler._order;
4414 const cos = Math.cos;
4415 const sin = Math.sin;
4416 const c1 = cos( x / 2 );
4417 const c2 = cos( y / 2 );
4418 const c3 = cos( z / 2 );
4419 const s1 = sin( x / 2 );
4420 const s2 = sin( y / 2 );
4421 const s3 = sin( z / 2 );
4422 switch ( order ) {
4423 case 'XYZ':
4424 this._x = s1 * c2 * c3 + c1 * s2 * s3;
4425 this._y = c1 * s2 * c3 - s1 * c2 * s3;
4426 this._z = c1 * c2 * s3 + s1 * s2 * c3;
4427 this._w = c1 * c2 * c3 - s1 * s2 * s3;
4428 break;
4429 case 'YXZ':
4430 this._x = s1 * c2 * c3 + c1 * s2 * s3;
4431 this._y = c1 * s2 * c3 - s1 * c2 * s3;
4432 this._z = c1 * c2 * s3 - s1 * s2 * c3;
4433 this._w = c1 * c2 * c3 + s1 * s2 * s3;
4434 break;
4435 case 'ZXY':
4436 this._x = s1 * c2 * c3 - c1 * s2 * s3;
4437 this._y = c1 * s2 * c3 + s1 * c2 * s3;
4438 this._z = c1 * c2 * s3 + s1 * s2 * c3;
4439 this._w = c1 * c2 * c3 - s1 * s2 * s3;
4440 break;
4441 case 'ZYX':
4442 this._x = s1 * c2 * c3 - c1 * s2 * s3;
4443 this._y = c1 * s2 * c3 + s1 * c2 * s3;
4444 this._z = c1 * c2 * s3 - s1 * s2 * c3;
4445 this._w = c1 * c2 * c3 + s1 * s2 * s3;
4446 break;
4447 case 'YZX':
4448 this._x = s1 * c2 * c3 + c1 * s2 * s3;
4449 this._y = c1 * s2 * c3 + s1 * c2 * s3;
4450 this._z = c1 * c2 * s3 - s1 * s2 * c3;
4451 this._w = c1 * c2 * c3 - s1 * s2 * s3;
4452 break;
4453 case 'XZY':
4454 this._x = s1 * c2 * c3 - c1 * s2 * s3;
4455 this._y = c1 * s2 * c3 - s1 * c2 * s3;
4456 this._z = c1 * c2 * s3 + s1 * s2 * c3;
4457 this._w = c1 * c2 * c3 + s1 * s2 * s3;
4458 break;
4459 default:
4460 console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order );
4461 }
4462 if ( update !== false ) this._onChangeCallback();
4463 return this;
4464 }
4465 setFromAxisAngle( axis, angle ) {
4466 const halfAngle = angle / 2, s = Math.sin( halfAngle );
4467 this._x = axis.x * s;
4468 this._y = axis.y * s;
4469 this._z = axis.z * s;
4470 this._w = Math.cos( halfAngle );
4471 this._onChangeCallback();
4472 return this;
4473 }
4474 setFromRotationMatrix( m ) {
4475 const te = m.elements,
4476 m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
4477 m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
4478 m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
4479 trace = m11 + m22 + m33;
4480 if ( trace > 0 ) {
4481 const s = 0.5 / Math.sqrt( trace + 1.0 );
4482 this._w = 0.25 / s;
4483 this._x = ( m32 - m23 ) * s;
4484 this._y = ( m13 - m31 ) * s;
4485 this._z = ( m21 - m12 ) * s;
4486 } else if ( m11 > m22 && m11 > m33 ) {
4487 const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
4488 this._w = ( m32 - m23 ) / s;
4489 this._x = 0.25 * s;
4490 this._y = ( m12 + m21 ) / s;
4491 this._z = ( m13 + m31 ) / s;
4492 } else if ( m22 > m33 ) {
4493 const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
4494 this._w = ( m13 - m31 ) / s;
4495 this._x = ( m12 + m21 ) / s;
4496 this._y = 0.25 * s;
4497 this._z = ( m23 + m32 ) / s;
4498 } else {
4499 const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
4500 this._w = ( m21 - m12 ) / s;
4501 this._x = ( m13 + m31 ) / s;
4502 this._y = ( m23 + m32 ) / s;
4503 this._z = 0.25 * s;
4504 }
4505 this._onChangeCallback();
4506 return this;
4507 }
4508 setFromUnitVectors( vFrom, vTo ) {
4509 const EPS = 0.000001;
4510 let r = vFrom.dot( vTo ) + 1;
4511 if ( r < EPS ) {
4512 r = 0;
4513 if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
4514 this._x = - vFrom.y;
4515 this._y = vFrom.x;
4516 this._z = 0;
4517 this._w = r;
4518 } else {
4519 this._x = 0;
4520 this._y = - vFrom.z;
4521 this._z = vFrom.y;
4522 this._w = r;
4523 }
4524 } else {
4525 this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
4526 this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
4527 this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
4528 this._w = r;
4529 }
4530 return this.normalize();
4531 }
4532 angleTo( q ) {
4533 return 2 * Math.acos( Math.abs( MathUtils.clamp( this.dot( q ), - 1, 1 ) ) );
4534 }
4535 rotateTowards( q, step ) {
4536 const angle = this.angleTo( q );
4537 if ( angle === 0 ) return this;
4538 const t = Math.min( 1, step / angle );
4539 this.slerp( q, t );
4540 return this;
4541 }
4542 identity() {
4543 return this.set( 0, 0, 0, 1 );
4544 }
4545 inverse() {
4546 return this.conjugate();
4547 }
4548 conjugate() {
4549 this._x *= - 1;
4550 this._y *= - 1;
4551 this._z *= - 1;
4552 this._onChangeCallback();
4553 return this;
4554 }
4555 dot( v ) {
4556 return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
4557 }
4558 lengthSq() {
4559 return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
4560 }
4561 length() {
4562 return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
4563 }
4564 normalize() {
4565 let l = this.length();
4566 if ( l === 0 ) {
4567 this._x = 0;
4568 this._y = 0;
4569 this._z = 0;
4570 this._w = 1;
4571 } else {
4572 l = 1 / l;
4573 this._x = this._x * l;
4574 this._y = this._y * l;
4575 this._z = this._z * l;
4576 this._w = this._w * l;
4577 }
4578 this._onChangeCallback();
4579 return this;
4580 }
4581 multiply( q, p ) {
4582 if ( p !== undefined ) {
4583 console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
4584 return this.multiplyQuaternions( q, p );
4585 }
4586 return this.multiplyQuaternions( this, q );
4587 }
4588 premultiply( q ) {
4589 return this.multiplyQuaternions( q, this );
4590 }
4591 multiplyQuaternions( a, b ) {
4592 const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
4593 const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
4594 this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
4595 this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
4596 this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
4597 this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
4598 this._onChangeCallback();
4599 return this;
4600 }
4601 slerp( qb, t ) {
4602 if ( t === 0 ) return this;
4603 if ( t === 1 ) return this.copy( qb );
4604 const x = this._x, y = this._y, z = this._z, w = this._w;
4605 let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
4606 if ( cosHalfTheta < 0 ) {
4607 this._w = - qb._w;
4608 this._x = - qb._x;
4609 this._y = - qb._y;
4610 this._z = - qb._z;
4611 cosHalfTheta = - cosHalfTheta;
4612 } else {
4613 this.copy( qb );
4614 }
4615 if ( cosHalfTheta >= 1.0 ) {
4616 this._w = w;
4617 this._x = x;
4618 this._y = y;
4619 this._z = z;
4620 return this;
4621 }
4622 const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta;
4623 if ( sqrSinHalfTheta <= Number.EPSILON ) {
4624 const s = 1 - t;
4625 this._w = s * w + t * this._w;
4626 this._x = s * x + t * this._x;
4627 this._y = s * y + t * this._y;
4628 this._z = s * z + t * this._z;
4629 this.normalize();
4630 this._onChangeCallback();
4631 return this;
4632 }
4633 const sinHalfTheta = Math.sqrt( sqrSinHalfTheta );
4634 const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );
4635 const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
4636 ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
4637 this._w = ( w * ratioA + this._w * ratioB );
4638 this._x = ( x * ratioA + this._x * ratioB );
4639 this._y = ( y * ratioA + this._y * ratioB );
4640 this._z = ( z * ratioA + this._z * ratioB );
4641 this._onChangeCallback();
4642 return this;
4643 }
4644 equals( quaternion ) {
4645 return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
4646 }
4647 fromArray( array, offset ) {
4648 if ( offset === undefined ) offset = 0;
4649 this._x = array[ offset ];
4650 this._y = array[ offset + 1 ];
4651 this._z = array[ offset + 2 ];
4652 this._w = array[ offset + 3 ];
4653 this._onChangeCallback();
4654 return this;
4655 }
4656 toArray( array, offset ) {
4657 if ( array === undefined ) array = [];
4658 if ( offset === undefined ) offset = 0;
4659 array[ offset ] = this._x;
4660 array[ offset + 1 ] = this._y;
4661 array[ offset + 2 ] = this._z;
4662 array[ offset + 3 ] = this._w;
4663 return array;
4664 }
4665 fromBufferAttribute( attribute, index ) {
4666 this._x = attribute.getX( index );
4667 this._y = attribute.getY( index );
4668 this._z = attribute.getZ( index );
4669 this._w = attribute.getW( index );
4670 return this;
4671 }
4672 _onChange( callback ) {
4673 this._onChangeCallback = callback;
4674 return this;
4675 }
4676 _onChangeCallback() {}
4677}
4678class Vector3 {
4679 constructor( x = 0, y = 0, z = 0 ) {
4680 Object.defineProperty( this, 'isVector3', { value: true } );
4681 this.x = x;
4682 this.y = y;
4683 this.z = z;
4684 }
4685 set( x, y, z ) {
4686 if ( z === undefined ) z = this.z;
4687 this.x = x;
4688 this.y = y;
4689 this.z = z;
4690 return this;
4691 }
4692 setScalar( scalar ) {
4693 this.x = scalar;
4694 this.y = scalar;
4695 this.z = scalar;
4696 return this;
4697 }
4698 setX( x ) {
4699 this.x = x;
4700 return this;
4701 }
4702 setY( y ) {
4703 this.y = y;
4704 return this;
4705 }
4706 setZ( z ) {
4707 this.z = z;
4708 return this;
4709 }
4710 setComponent( index, value ) {
4711 switch ( index ) {
4712 case 0: this.x = value; break;
4713 case 1: this.y = value; break;
4714 case 2: this.z = value; break;
4715 default: throw new Error( 'index is out of range: ' + index );
4716 }
4717 return this;
4718 }
4719 getComponent( index ) {
4720 switch ( index ) {
4721 case 0: return this.x;
4722 case 1: return this.y;
4723 case 2: return this.z;
4724 default: throw new Error( 'index is out of range: ' + index );
4725 }
4726 }
4727 clone() {
4728 return new this.constructor( this.x, this.y, this.z );
4729 }
4730 copy( v ) {
4731 this.x = v.x;
4732 this.y = v.y;
4733 this.z = v.z;
4734 return this;
4735 }
4736 add( v, w ) {
4737 if ( w !== undefined ) {
4738 console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
4739 return this.addVectors( v, w );
4740 }
4741 this.x += v.x;
4742 this.y += v.y;
4743 this.z += v.z;
4744 return this;
4745 }
4746 addScalar( s ) {
4747 this.x += s;
4748 this.y += s;
4749 this.z += s;
4750 return this;
4751 }
4752 addVectors( a, b ) {
4753 this.x = a.x + b.x;
4754 this.y = a.y + b.y;
4755 this.z = a.z + b.z;
4756 return this;
4757 }
4758 addScaledVector( v, s ) {
4759 this.x += v.x * s;
4760 this.y += v.y * s;
4761 this.z += v.z * s;
4762 return this;
4763 }
4764 sub( v, w ) {
4765 if ( w !== undefined ) {
4766 console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
4767 return this.subVectors( v, w );
4768 }
4769 this.x -= v.x;
4770 this.y -= v.y;
4771 this.z -= v.z;
4772 return this;
4773 }
4774 subScalar( s ) {
4775 this.x -= s;
4776 this.y -= s;
4777 this.z -= s;
4778 return this;
4779 }
4780 subVectors( a, b ) {
4781 this.x = a.x - b.x;
4782 this.y = a.y - b.y;
4783 this.z = a.z - b.z;
4784 return this;
4785 }
4786 multiply( v, w ) {
4787 if ( w !== undefined ) {
4788 console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
4789 return this.multiplyVectors( v, w );
4790 }
4791 this.x *= v.x;
4792 this.y *= v.y;
4793 this.z *= v.z;
4794 return this;
4795 }
4796 multiplyScalar( scalar ) {
4797 this.x *= scalar;
4798 this.y *= scalar;
4799 this.z *= scalar;
4800 return this;
4801 }
4802 multiplyVectors( a, b ) {
4803 this.x = a.x * b.x;
4804 this.y = a.y * b.y;
4805 this.z = a.z * b.z;
4806 return this;
4807 }
4808 applyEuler( euler ) {
4809 if ( ! ( euler && euler.isEuler ) ) {
4810 console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );
4811 }
4812 return this.applyQuaternion( _quaternion$2.setFromEuler( euler ) );
4813 }
4814 applyAxisAngle( axis, angle ) {
4815 return this.applyQuaternion( _quaternion$2.setFromAxisAngle( axis, angle ) );
4816 }
4817 applyMatrix3( m ) {
4818 const x = this.x, y = this.y, z = this.z;
4819 const e = m.elements;
4820 this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
4821 this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
4822 this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
4823 return this;
4824 }
4825 applyNormalMatrix( m ) {
4826 return this.applyMatrix3( m ).normalize();
4827 }
4828 applyMatrix4( m ) {
4829 const x = this.x, y = this.y, z = this.z;
4830 const e = m.elements;
4831 const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );
4832 this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;
4833 this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;
4834 this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;
4835 return this;
4836 }
4837 applyQuaternion( q ) {
4838 const x = this.x, y = this.y, z = this.z;
4839 const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
4840 const ix = qw * x + qy * z - qz * y;
4841 const iy = qw * y + qz * x - qx * z;
4842 const iz = qw * z + qx * y - qy * x;
4843 const iw = - qx * x - qy * y - qz * z;
4844 this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
4845 this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
4846 this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
4847 return this;
4848 }
4849 project( camera ) {
4850 return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
4851 }
4852 unproject( camera ) {
4853 return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld );
4854 }
4855 transformDirection( m ) {
4856 const x = this.x, y = this.y, z = this.z;
4857 const e = m.elements;
4858 this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
4859 this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
4860 this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
4861 return this.normalize();
4862 }
4863 divide( v ) {
4864 this.x /= v.x;
4865 this.y /= v.y;
4866 this.z /= v.z;
4867 return this;
4868 }
4869 divideScalar( scalar ) {
4870 return this.multiplyScalar( 1 / scalar );
4871 }
4872 min( v ) {
4873 this.x = Math.min( this.x, v.x );
4874 this.y = Math.min( this.y, v.y );
4875 this.z = Math.min( this.z, v.z );
4876 return this;
4877 }
4878 max( v ) {
4879 this.x = Math.max( this.x, v.x );
4880 this.y = Math.max( this.y, v.y );
4881 this.z = Math.max( this.z, v.z );
4882 return this;
4883 }
4884 clamp( min, max ) {
4885 this.x = Math.max( min.x, Math.min( max.x, this.x ) );
4886 this.y = Math.max( min.y, Math.min( max.y, this.y ) );
4887 this.z = Math.max( min.z, Math.min( max.z, this.z ) );
4888 return this;
4889 }
4890 clampScalar( minVal, maxVal ) {
4891 this.x = Math.max( minVal, Math.min( maxVal, this.x ) );
4892 this.y = Math.max( minVal, Math.min( maxVal, this.y ) );
4893 this.z = Math.max( minVal, Math.min( maxVal, this.z ) );
4894 return this;
4895 }
4896 clampLength( min, max ) {
4897 const length = this.length();
4898 return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );
4899 }
4900 floor() {
4901 this.x = Math.floor( this.x );
4902 this.y = Math.floor( this.y );
4903 this.z = Math.floor( this.z );
4904 return this;
4905 }
4906 ceil() {
4907 this.x = Math.ceil( this.x );
4908 this.y = Math.ceil( this.y );
4909 this.z = Math.ceil( this.z );
4910 return this;
4911 }
4912 round() {
4913 this.x = Math.round( this.x );
4914 this.y = Math.round( this.y );
4915 this.z = Math.round( this.z );
4916 return this;
4917 }
4918 roundToZero() {
4919 this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
4920 this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
4921 this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
4922 return this;
4923 }
4924 negate() {
4925 this.x = - this.x;
4926 this.y = - this.y;
4927 this.z = - this.z;
4928 return this;
4929 }
4930 dot( v ) {
4931 return this.x * v.x + this.y * v.y + this.z * v.z;
4932 }
4933 lengthSq() {
4934 return this.x * this.x + this.y * this.y + this.z * this.z;
4935 }
4936 length() {
4937 return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
4938 }
4939 manhattanLength() {
4940 return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
4941 }
4942 normalize() {
4943 return this.divideScalar( this.length() || 1 );
4944 }
4945 setLength( length ) {
4946 return this.normalize().multiplyScalar( length );
4947 }
4948 lerp( v, alpha ) {
4949 this.x += ( v.x - this.x ) * alpha;
4950 this.y += ( v.y - this.y ) * alpha;
4951 this.z += ( v.z - this.z ) * alpha;
4952 return this;
4953 }
4954 lerpVectors( v1, v2, alpha ) {
4955 this.x = v1.x + ( v2.x - v1.x ) * alpha;
4956 this.y = v1.y + ( v2.y - v1.y ) * alpha;
4957 this.z = v1.z + ( v2.z - v1.z ) * alpha;
4958 return this;
4959 }
4960 cross( v, w ) {
4961 if ( w !== undefined ) {
4962 console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
4963 return this.crossVectors( v, w );
4964 }
4965 return this.crossVectors( this, v );
4966 }
4967 crossVectors( a, b ) {
4968 const ax = a.x, ay = a.y, az = a.z;
4969 const bx = b.x, by = b.y, bz = b.z;
4970 this.x = ay * bz - az * by;
4971 this.y = az * bx - ax * bz;
4972 this.z = ax * by - ay * bx;
4973 return this;
4974 }
4975 projectOnVector( v ) {
4976 const denominator = v.lengthSq();
4977 if ( denominator === 0 ) return this.set( 0, 0, 0 );
4978 const scalar = v.dot( this ) / denominator;
4979 return this.copy( v ).multiplyScalar( scalar );
4980 }
4981 projectOnPlane( planeNormal ) {
4982 _vector.copy( this ).projectOnVector( planeNormal );
4983 return this.sub( _vector );
4984 }
4985 reflect( normal ) {
4986 return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
4987 }
4988 angleTo( v ) {
4989 const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
4990 if ( denominator === 0 ) return Math.PI / 2;
4991 const theta = this.dot( v ) / denominator;
4992 return Math.acos( MathUtils.clamp( theta, - 1, 1 ) );
4993 }
4994 distanceTo( v ) {
4995 return Math.sqrt( this.distanceToSquared( v ) );
4996 }
4997 distanceToSquared( v ) {
4998 const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
4999 return dx * dx + dy * dy + dz * dz;
5000 }
5001 manhattanDistanceTo( v ) {
5002 return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );
5003 }
5004 setFromSpherical( s ) {
5005 return this.setFromSphericalCoords( s.radius, s.phi, s.theta );
5006 }
5007 setFromSphericalCoords( radius, phi, theta ) {
5008 const sinPhiRadius = Math.sin( phi ) * radius;
5009 this.x = sinPhiRadius * Math.sin( theta );
5010 this.y = Math.cos( phi ) * radius;
5011 this.z = sinPhiRadius * Math.cos( theta );
5012 return this;
5013 }
5014 setFromCylindrical( c ) {
5015 return this.setFromCylindricalCoords( c.radius, c.theta, c.y );
5016 }
5017 setFromCylindricalCoords( radius, theta, y ) {
5018 this.x = radius * Math.sin( theta );
5019 this.y = y;
5020 this.z = radius * Math.cos( theta );
5021 return this;
5022 }
5023 setFromMatrixPosition( m ) {
5024 const e = m.elements;
5025 this.x = e[ 12 ];
5026 this.y = e[ 13 ];
5027 this.z = e[ 14 ];
5028 return this;
5029 }
5030 setFromMatrixScale( m ) {
5031 const sx = this.setFromMatrixColumn( m, 0 ).length();
5032 const sy = this.setFromMatrixColumn( m, 1 ).length();
5033 const sz = this.setFromMatrixColumn( m, 2 ).length();
5034 this.x = sx;
5035 this.y = sy;
5036 this.z = sz;
5037 return this;
5038 }
5039 setFromMatrixColumn( m, index ) {
5040 return this.fromArray( m.elements, index * 4 );
5041 }
5042 setFromMatrix3Column( m, index ) {
5043 return this.fromArray( m.elements, index * 3 );
5044 }
5045 equals( v ) {
5046 return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
5047 }
5048 fromArray( array, offset ) {
5049 if ( offset === undefined ) offset = 0;
5050 this.x = array[ offset ];
5051 this.y = array[ offset + 1 ];
5052 this.z = array[ offset + 2 ];
5053 return this;
5054 }
5055 toArray( array, offset ) {
5056 if ( array === undefined ) array = [];
5057 if ( offset === undefined ) offset = 0;
5058 array[ offset ] = this.x;
5059 array[ offset + 1 ] = this.y;
5060 array[ offset + 2 ] = this.z;
5061 return array;
5062 }
5063 fromBufferAttribute( attribute, index, offset ) {
5064 if ( offset !== undefined ) {
5065 console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );
5066 }
5067 this.x = attribute.getX( index );
5068 this.y = attribute.getY( index );
5069 this.z = attribute.getZ( index );
5070 return this;
5071 }
5072 random() {
5073 this.x = Math.random();
5074 this.y = Math.random();
5075 this.z = Math.random();
5076 return this;
5077 }
5078}
5079const _vector = new Vector3();
5080const _quaternion$2 = new Quaternion();
5081class Matrix4 {
5082 constructor() {
5083 Object.defineProperty( this, 'isMatrix4', { value: true } );
5084 this.elements = [
5085 1, 0, 0, 0,
5086 0, 1, 0, 0,
5087 0, 0, 1, 0,
5088 0, 0, 0, 1
5089 ];
5090 if ( arguments.length > 0 ) {
5091 console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );
5092 }
5093 }
5094 set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
5095 const te = this.elements;
5096 te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
5097 te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
5098 te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
5099 te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
5100 return this;
5101 }
5102 identity() {
5103 this.set(
5104 1, 0, 0, 0,
5105 0, 1, 0, 0,
5106 0, 0, 1, 0,
5107 0, 0, 0, 1
5108 );
5109 return this;
5110 }
5111 clone() {
5112 return new Matrix4().fromArray( this.elements );
5113 }
5114 copy( m ) {
5115 const te = this.elements;
5116 const me = m.elements;
5117 te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];
5118 te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];
5119 te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];
5120 te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];
5121 return this;
5122 }
5123 copyPosition( m ) {
5124 const te = this.elements, me = m.elements;
5125 te[ 12 ] = me[ 12 ];
5126 te[ 13 ] = me[ 13 ];
5127 te[ 14 ] = me[ 14 ];
5128 return this;
5129 }
5130 extractBasis( xAxis, yAxis, zAxis ) {
5131 xAxis.setFromMatrixColumn( this, 0 );
5132 yAxis.setFromMatrixColumn( this, 1 );
5133 zAxis.setFromMatrixColumn( this, 2 );
5134 return this;
5135 }
5136 makeBasis( xAxis, yAxis, zAxis ) {
5137 this.set(
5138 xAxis.x, yAxis.x, zAxis.x, 0,
5139 xAxis.y, yAxis.y, zAxis.y, 0,
5140 xAxis.z, yAxis.z, zAxis.z, 0,
5141 0, 0, 0, 1
5142 );
5143 return this;
5144 }
5145 extractRotation( m ) {
5146 const te = this.elements;
5147 const me = m.elements;
5148 const scaleX = 1 / _v1$1.setFromMatrixColumn( m, 0 ).length();
5149 const scaleY = 1 / _v1$1.setFromMatrixColumn( m, 1 ).length();
5150 const scaleZ = 1 / _v1$1.setFromMatrixColumn( m, 2 ).length();
5151 te[ 0 ] = me[ 0 ] * scaleX;
5152 te[ 1 ] = me[ 1 ] * scaleX;
5153 te[ 2 ] = me[ 2 ] * scaleX;
5154 te[ 3 ] = 0;
5155 te[ 4 ] = me[ 4 ] * scaleY;
5156 te[ 5 ] = me[ 5 ] * scaleY;
5157 te[ 6 ] = me[ 6 ] * scaleY;
5158 te[ 7 ] = 0;
5159 te[ 8 ] = me[ 8 ] * scaleZ;
5160 te[ 9 ] = me[ 9 ] * scaleZ;
5161 te[ 10 ] = me[ 10 ] * scaleZ;
5162 te[ 11 ] = 0;
5163 te[ 12 ] = 0;
5164 te[ 13 ] = 0;
5165 te[ 14 ] = 0;
5166 te[ 15 ] = 1;
5167 return this;
5168 }
5169 makeRotationFromEuler( euler ) {
5170 if ( ! ( euler && euler.isEuler ) ) {
5171 console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
5172 }
5173 const te = this.elements;
5174 const x = euler.x, y = euler.y, z = euler.z;
5175 const a = Math.cos( x ), b = Math.sin( x );
5176 const c = Math.cos( y ), d = Math.sin( y );
5177 const e = Math.cos( z ), f = Math.sin( z );
5178 if ( euler.order === 'XYZ' ) {
5179 const ae = a * e, af = a * f, be = b * e, bf = b * f;
5180 te[ 0 ] = c * e;
5181 te[ 4 ] = - c * f;
5182 te[ 8 ] = d;
5183 te[ 1 ] = af + be * d;
5184 te[ 5 ] = ae - bf * d;
5185 te[ 9 ] = - b * c;
5186 te[ 2 ] = bf - ae * d;
5187 te[ 6 ] = be + af * d;
5188 te[ 10 ] = a * c;
5189 } else if ( euler.order === 'YXZ' ) {
5190 const ce = c * e, cf = c * f, de = d * e, df = d * f;
5191 te[ 0 ] = ce + df * b;
5192 te[ 4 ] = de * b - cf;
5193 te[ 8 ] = a * d;
5194 te[ 1 ] = a * f;
5195 te[ 5 ] = a * e;
5196 te[ 9 ] = - b;
5197 te[ 2 ] = cf * b - de;
5198 te[ 6 ] = df + ce * b;
5199 te[ 10 ] = a * c;
5200 } else if ( euler.order === 'ZXY' ) {
5201 const ce = c * e, cf = c * f, de = d * e, df = d * f;
5202 te[ 0 ] = ce - df * b;
5203 te[ 4 ] = - a * f;
5204 te[ 8 ] = de + cf * b;
5205 te[ 1 ] = cf + de * b;
5206 te[ 5 ] = a * e;
5207 te[ 9 ] = df - ce * b;
5208 te[ 2 ] = - a * d;
5209 te[ 6 ] = b;
5210 te[ 10 ] = a * c;
5211 } else if ( euler.order === 'ZYX' ) {
5212 const ae = a * e, af = a * f, be = b * e, bf = b * f;
5213 te[ 0 ] = c * e;
5214 te[ 4 ] = be * d - af;
5215 te[ 8 ] = ae * d + bf;
5216 te[ 1 ] = c * f;
5217 te[ 5 ] = bf * d + ae;
5218 te[ 9 ] = af * d - be;
5219 te[ 2 ] = - d;
5220 te[ 6 ] = b * c;
5221 te[ 10 ] = a * c;
5222 } else if ( euler.order === 'YZX' ) {
5223 const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
5224 te[ 0 ] = c * e;
5225 te[ 4 ] = bd - ac * f;
5226 te[ 8 ] = bc * f + ad;
5227 te[ 1 ] = f;
5228 te[ 5 ] = a * e;
5229 te[ 9 ] = - b * e;
5230 te[ 2 ] = - d * e;
5231 te[ 6 ] = ad * f + bc;
5232 te[ 10 ] = ac - bd * f;
5233 } else if ( euler.order === 'XZY' ) {
5234 const ac = a * c, ad = a * d, bc = b * c, bd = b * d;
5235 te[ 0 ] = c * e;
5236 te[ 4 ] = - f;
5237 te[ 8 ] = d * e;
5238 te[ 1 ] = ac * f + bd;
5239 te[ 5 ] = a * e;
5240 te[ 9 ] = ad * f - bc;
5241 te[ 2 ] = bc * f - ad;
5242 te[ 6 ] = b * e;
5243 te[ 10 ] = bd * f + ac;
5244 }
5245 te[ 3 ] = 0;
5246 te[ 7 ] = 0;
5247 te[ 11 ] = 0;
5248 te[ 12 ] = 0;
5249 te[ 13 ] = 0;
5250 te[ 14 ] = 0;
5251 te[ 15 ] = 1;
5252 return this;
5253 }
5254 makeRotationFromQuaternion( q ) {
5255 return this.compose( _zero, q, _one );
5256 }
5257 lookAt( eye, target, up ) {
5258 const te = this.elements;
5259 _z.subVectors( eye, target );
5260 if ( _z.lengthSq() === 0 ) {
5261 _z.z = 1;
5262 }
5263 _z.normalize();
5264 _x.crossVectors( up, _z );
5265 if ( _x.lengthSq() === 0 ) {
5266 if ( Math.abs( up.z ) === 1 ) {
5267 _z.x += 0.0001;
5268 } else {
5269 _z.z += 0.0001;
5270 }
5271 _z.normalize();
5272 _x.crossVectors( up, _z );
5273 }
5274 _x.normalize();
5275 _y.crossVectors( _z, _x );
5276 te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x;
5277 te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y;
5278 te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z;
5279 return this;
5280 }
5281 multiply( m, n ) {
5282 if ( n !== undefined ) {
5283 console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );
5284 return this.multiplyMatrices( m, n );
5285 }
5286 return this.multiplyMatrices( this, m );
5287 }
5288 premultiply( m ) {
5289 return this.multiplyMatrices( m, this );
5290 }
5291 multiplyMatrices( a, b ) {
5292 const ae = a.elements;
5293 const be = b.elements;
5294 const te = this.elements;
5295 const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];
5296 const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];
5297 const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];
5298 const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];
5299 const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];
5300 const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];
5301 const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];
5302 const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];
5303 te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
5304 te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
5305 te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
5306 te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
5307 te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
5308 te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
5309 te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
5310 te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
5311 te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
5312 te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
5313 te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
5314 te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
5315 te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
5316 te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
5317 te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
5318 te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
5319 return this;
5320 }
5321 multiplyScalar( s ) {
5322 const te = this.elements;
5323 te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;
5324 te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;
5325 te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;
5326 te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;
5327 return this;
5328 }
5329 determinant() {
5330 const te = this.elements;
5331 const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];
5332 const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];
5333 const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];
5334 const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];
5335 return (
5336 n41 * (
5337 + n14 * n23 * n32
5338 - n13 * n24 * n32
5339 - n14 * n22 * n33
5340 + n12 * n24 * n33
5341 + n13 * n22 * n34
5342 - n12 * n23 * n34
5343 ) +
5344 n42 * (
5345 + n11 * n23 * n34
5346 - n11 * n24 * n33
5347 + n14 * n21 * n33
5348 - n13 * n21 * n34
5349 + n13 * n24 * n31
5350 - n14 * n23 * n31
5351 ) +
5352 n43 * (
5353 + n11 * n24 * n32
5354 - n11 * n22 * n34
5355 - n14 * n21 * n32
5356 + n12 * n21 * n34
5357 + n14 * n22 * n31
5358 - n12 * n24 * n31
5359 ) +
5360 n44 * (
5361 - n13 * n22 * n31
5362 - n11 * n23 * n32
5363 + n11 * n22 * n33
5364 + n13 * n21 * n32
5365 - n12 * n21 * n33
5366 + n12 * n23 * n31
5367 )
5368 );
5369 }
5370 transpose() {
5371 const te = this.elements;
5372 let tmp;
5373 tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;
5374 tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;
5375 tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;
5376 tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;
5377 tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;
5378 tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;
5379 return this;
5380 }
5381 setPosition( x, y, z ) {
5382 const te = this.elements;
5383 if ( x.isVector3 ) {
5384 te[ 12 ] = x.x;
5385 te[ 13 ] = x.y;
5386 te[ 14 ] = x.z;
5387 } else {
5388 te[ 12 ] = x;
5389 te[ 13 ] = y;
5390 te[ 14 ] = z;
5391 }
5392 return this;
5393 }
5394 getInverse( m, throwOnDegenerate ) {
5395 if ( throwOnDegenerate !== undefined ) {
5396 console.warn( "THREE.Matrix4: .getInverse() can no longer be configured to throw on degenerate." );
5397 }
5398 const te = this.elements,
5399 me = m.elements,
5400 n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],
5401 n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],
5402 n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],
5403 n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],
5404 t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
5405 t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
5406 t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
5407 t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
5408 const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
5409 if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
5410 const detInv = 1 / det;
5411 te[ 0 ] = t11 * detInv;
5412 te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;
5413 te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;
5414 te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;
5415 te[ 4 ] = t12 * detInv;
5416 te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;
5417 te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;
5418 te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;
5419 te[ 8 ] = t13 * detInv;
5420 te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;
5421 te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;
5422 te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;
5423 te[ 12 ] = t14 * detInv;
5424 te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;
5425 te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;
5426 te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;
5427 return this;
5428 }
5429 scale( v ) {
5430 const te = this.elements;
5431 const x = v.x, y = v.y, z = v.z;
5432 te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;
5433 te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;
5434 te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;
5435 te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;
5436 return this;
5437 }
5438 getMaxScaleOnAxis() {
5439 const te = this.elements;
5440 const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];
5441 const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];
5442 const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];
5443 return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );
5444 }
5445 makeTranslation( x, y, z ) {
5446 this.set(
5447 1, 0, 0, x,
5448 0, 1, 0, y,
5449 0, 0, 1, z,
5450 0, 0, 0, 1
5451 );
5452 return this;
5453 }
5454 makeRotationX( theta ) {
5455 const c = Math.cos( theta ), s = Math.sin( theta );
5456 this.set(
5457 1, 0, 0, 0,
5458 0, c, - s, 0,
5459 0, s, c, 0,
5460 0, 0, 0, 1
5461 );
5462 return this;
5463 }
5464 makeRotationY( theta ) {
5465 const c = Math.cos( theta ), s = Math.sin( theta );
5466 this.set(
5467 c, 0, s, 0,
5468 0, 1, 0, 0,
5469 - s, 0, c, 0,
5470 0, 0, 0, 1
5471 );
5472 return this;
5473 }
5474 makeRotationZ( theta ) {
5475 const c = Math.cos( theta ), s = Math.sin( theta );
5476 this.set(
5477 c, - s, 0, 0,
5478 s, c, 0, 0,
5479 0, 0, 1, 0,
5480 0, 0, 0, 1
5481 );
5482 return this;
5483 }
5484 makeRotationAxis( axis, angle ) {
5485 const c = Math.cos( angle );
5486 const s = Math.sin( angle );
5487 const t = 1 - c;
5488 const x = axis.x, y = axis.y, z = axis.z;
5489 const tx = t * x, ty = t * y;
5490 this.set(
5491 tx * x + c, tx * y - s * z, tx * z + s * y, 0,
5492 tx * y + s * z, ty * y + c, ty * z - s * x, 0,
5493 tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
5494 0, 0, 0, 1
5495 );
5496 return this;
5497 }
5498 makeScale( x, y, z ) {
5499 this.set(
5500 x, 0, 0, 0,
5501 0, y, 0, 0,
5502 0, 0, z, 0,
5503 0, 0, 0, 1
5504 );
5505 return this;
5506 }
5507 makeShear( x, y, z ) {
5508 this.set(
5509 1, y, z, 0,
5510 x, 1, z, 0,
5511 x, y, 1, 0,
5512 0, 0, 0, 1
5513 );
5514 return this;
5515 }
5516 compose( position, quaternion, scale ) {
5517 const te = this.elements;
5518 const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
5519 const x2 = x + x, y2 = y + y, z2 = z + z;
5520 const xx = x * x2, xy = x * y2, xz = x * z2;
5521 const yy = y * y2, yz = y * z2, zz = z * z2;
5522 const wx = w * x2, wy = w * y2, wz = w * z2;
5523 const sx = scale.x, sy = scale.y, sz = scale.z;
5524 te[ 0 ] = ( 1 - ( yy + zz ) ) * sx;
5525 te[ 1 ] = ( xy + wz ) * sx;
5526 te[ 2 ] = ( xz - wy ) * sx;
5527 te[ 3 ] = 0;
5528 te[ 4 ] = ( xy - wz ) * sy;
5529 te[ 5 ] = ( 1 - ( xx + zz ) ) * sy;
5530 te[ 6 ] = ( yz + wx ) * sy;
5531 te[ 7 ] = 0;
5532 te[ 8 ] = ( xz + wy ) * sz;
5533 te[ 9 ] = ( yz - wx ) * sz;
5534 te[ 10 ] = ( 1 - ( xx + yy ) ) * sz;
5535 te[ 11 ] = 0;
5536 te[ 12 ] = position.x;
5537 te[ 13 ] = position.y;
5538 te[ 14 ] = position.z;
5539 te[ 15 ] = 1;
5540 return this;
5541 }
5542 decompose( position, quaternion, scale ) {
5543 const te = this.elements;
5544 let sx = _v1$1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();
5545 const sy = _v1$1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();
5546 const sz = _v1$1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();
5547 const det = this.determinant();
5548 if ( det < 0 ) sx = - sx;
5549 position.x = te[ 12 ];
5550 position.y = te[ 13 ];
5551 position.z = te[ 14 ];
5552 _m1$1.copy( this );
5553 const invSX = 1 / sx;
5554 const invSY = 1 / sy;
5555 const invSZ = 1 / sz;
5556 _m1$1.elements[ 0 ] *= invSX;
5557 _m1$1.elements[ 1 ] *= invSX;
5558 _m1$1.elements[ 2 ] *= invSX;
5559 _m1$1.elements[ 4 ] *= invSY;
5560 _m1$1.elements[ 5 ] *= invSY;
5561 _m1$1.elements[ 6 ] *= invSY;
5562 _m1$1.elements[ 8 ] *= invSZ;
5563 _m1$1.elements[ 9 ] *= invSZ;
5564 _m1$1.elements[ 10 ] *= invSZ;
5565 quaternion.setFromRotationMatrix( _m1$1 );
5566 scale.x = sx;
5567 scale.y = sy;
5568 scale.z = sz;
5569 return this;
5570 }
5571 makePerspective( left, right, top, bottom, near, far ) {
5572 if ( far === undefined ) {
5573 console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );
5574 }
5575 const te = this.elements;
5576 const x = 2 * near / ( right - left );
5577 const y = 2 * near / ( top - bottom );
5578 const a = ( right + left ) / ( right - left );
5579 const b = ( top + bottom ) / ( top - bottom );
5580 const c = - ( far + near ) / ( far - near );
5581 const d = - 2 * far * near / ( far - near );
5582 te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;
5583 te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;
5584 te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;
5585 te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;
5586 return this;
5587 }
5588 makeOrthographic( left, right, top, bottom, near, far ) {
5589 const te = this.elements;
5590 const w = 1.0 / ( right - left );
5591 const h = 1.0 / ( top - bottom );
5592 const p = 1.0 / ( far - near );
5593 const x = ( right + left ) * w;
5594 const y = ( top + bottom ) * h;
5595 const z = ( far + near ) * p;
5596 te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;
5597 te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;
5598 te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;
5599 te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;
5600 return this;
5601 }
5602 equals( matrix ) {
5603 const te = this.elements;
5604 const me = matrix.elements;
5605 for ( let i = 0; i < 16; i ++ ) {
5606 if ( te[ i ] !== me[ i ] ) return false;
5607 }
5608 return true;
5609 }
5610 fromArray( array, offset ) {
5611 if ( offset === undefined ) offset = 0;
5612 for ( let i = 0; i < 16; i ++ ) {
5613 this.elements[ i ] = array[ i + offset ];
5614 }
5615 return this;
5616 }
5617 toArray( array, offset ) {
5618 if ( array === undefined ) array = [];
5619 if ( offset === undefined ) offset = 0;
5620 const te = this.elements;
5621 array[ offset ] = te[ 0 ];
5622 array[ offset + 1 ] = te[ 1 ];
5623 array[ offset + 2 ] = te[ 2 ];
5624 array[ offset + 3 ] = te[ 3 ];
5625 array[ offset + 4 ] = te[ 4 ];
5626 array[ offset + 5 ] = te[ 5 ];
5627 array[ offset + 6 ] = te[ 6 ];
5628 array[ offset + 7 ] = te[ 7 ];
5629 array[ offset + 8 ] = te[ 8 ];
5630 array[ offset + 9 ] = te[ 9 ];
5631 array[ offset + 10 ] = te[ 10 ];
5632 array[ offset + 11 ] = te[ 11 ];
5633 array[ offset + 12 ] = te[ 12 ];
5634 array[ offset + 13 ] = te[ 13 ];
5635 array[ offset + 14 ] = te[ 14 ];
5636 array[ offset + 15 ] = te[ 15 ];
5637 return array;
5638 }
5639}
5640const _v1$1 = new Vector3();
5641const _m1$1 = new Matrix4();
5642const _zero = new Vector3( 0, 0, 0 );
5643const _one = new Vector3( 1, 1, 1 );
5644const _x = new Vector3();
5645const _y = new Vector3();
5646const _z = new Vector3();
5647function EventDispatcher() {}
5648Object.assign( EventDispatcher.prototype, {
5649 addEventListener: function ( type, listener ) {
5650 if ( this._listeners === undefined ) this._listeners = {};
5651 const listeners = this._listeners;
5652 if ( listeners[ type ] === undefined ) {
5653 listeners[ type ] = [];
5654 }
5655 if ( listeners[ type ].indexOf( listener ) === - 1 ) {
5656 listeners[ type ].push( listener );
5657 }
5658 },
5659 hasEventListener: function ( type, listener ) {
5660 if ( this._listeners === undefined ) return false;
5661 const listeners = this._listeners;
5662 return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
5663 },
5664 removeEventListener: function ( type, listener ) {
5665 if ( this._listeners === undefined ) return;
5666 const listeners = this._listeners;
5667 const listenerArray = listeners[ type ];
5668 if ( listenerArray !== undefined ) {
5669 const index = listenerArray.indexOf( listener );
5670 if ( index !== - 1 ) {
5671 listenerArray.splice( index, 1 );
5672 }
5673 }
5674 },
5675 dispatchEvent: function ( event ) {
5676 if ( this._listeners === undefined ) return;
5677 const listeners = this._listeners;
5678 const listenerArray = listeners[ event.type ];
5679 if ( listenerArray !== undefined ) {
5680 event.target = this;
5681 const array = listenerArray.slice( 0 );
5682 for ( let i = 0, l = array.length; i < l; i ++ ) {
5683 array[ i ].call( this, event );
5684 }
5685 }
5686 }
5687} );
5688class Euler {
5689 constructor( x = 0, y = 0, z = 0, order = Euler.DefaultOrder ) {
5690 Object.defineProperty( this, 'isEuler', { value: true } );
5691 this._x = x;
5692 this._y = y;
5693 this._z = z;
5694 this._order = order;
5695 }
5696 get x() {
5697 return this._x;
5698 }
5699 set x( value ) {
5700 this._x = value;
5701 this._onChangeCallback();
5702 }
5703 get y() {
5704 return this._y;
5705 }
5706 set y( value ) {
5707 this._y = value;
5708 this._onChangeCallback();
5709 }
5710 get z() {
5711 return this._z;
5712 }
5713 set z( value ) {
5714 this._z = value;
5715 this._onChangeCallback();
5716 }
5717 get order() {
5718 return this._order;
5719 }
5720 set order( value ) {
5721 this._order = value;
5722 this._onChangeCallback();
5723 }
5724 set( x, y, z, order ) {
5725 this._x = x;
5726 this._y = y;
5727 this._z = z;
5728 this._order = order || this._order;
5729 this._onChangeCallback();
5730 return this;
5731 }
5732 clone() {
5733 return new this.constructor( this._x, this._y, this._z, this._order );
5734 }
5735 copy( euler ) {
5736 this._x = euler._x;
5737 this._y = euler._y;
5738 this._z = euler._z;
5739 this._order = euler._order;
5740 this._onChangeCallback();
5741 return this;
5742 }
5743 setFromRotationMatrix( m, order, update ) {
5744 const clamp = MathUtils.clamp;
5745 const te = m.elements;
5746 const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
5747 const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
5748 const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
5749 order = order || this._order;
5750 switch ( order ) {
5751 case 'XYZ':
5752 this._y = Math.asin( clamp( m13, - 1, 1 ) );
5753 if ( Math.abs( m13 ) < 0.9999999 ) {
5754 this._x = Math.atan2( - m23, m33 );
5755 this._z = Math.atan2( - m12, m11 );
5756 } else {
5757 this._x = Math.atan2( m32, m22 );
5758 this._z = 0;
5759 }
5760 break;
5761 case 'YXZ':
5762 this._x = Math.asin( - clamp( m23, - 1, 1 ) );
5763 if ( Math.abs( m23 ) < 0.9999999 ) {
5764 this._y = Math.atan2( m13, m33 );
5765 this._z = Math.atan2( m21, m22 );
5766 } else {
5767 this._y = Math.atan2( - m31, m11 );
5768 this._z = 0;
5769 }
5770 break;
5771 case 'ZXY':
5772 this._x = Math.asin( clamp( m32, - 1, 1 ) );
5773 if ( Math.abs( m32 ) < 0.9999999 ) {
5774 this._y = Math.atan2( - m31, m33 );
5775 this._z = Math.atan2( - m12, m22 );
5776 } else {
5777 this._y = 0;
5778 this._z = Math.atan2( m21, m11 );
5779 }
5780 break;
5781 case 'ZYX':
5782 this._y = Math.asin( - clamp( m31, - 1, 1 ) );
5783 if ( Math.abs( m31 ) < 0.9999999 ) {
5784 this._x = Math.atan2( m32, m33 );
5785 this._z = Math.atan2( m21, m11 );
5786 } else {
5787 this._x = 0;
5788 this._z = Math.atan2( - m12, m22 );
5789 }
5790 break;
5791 case 'YZX':
5792 this._z = Math.asin( clamp( m21, - 1, 1 ) );
5793 if ( Math.abs( m21 ) < 0.9999999 ) {
5794 this._x = Math.atan2( - m23, m22 );
5795 this._y = Math.atan2( - m31, m11 );
5796 } else {
5797 this._x = 0;
5798 this._y = Math.atan2( m13, m33 );
5799 }
5800 break;
5801 case 'XZY':
5802 this._z = Math.asin( - clamp( m12, - 1, 1 ) );
5803 if ( Math.abs( m12 ) < 0.9999999 ) {
5804 this._x = Math.atan2( m32, m22 );
5805 this._y = Math.atan2( m13, m11 );
5806 } else {
5807 this._x = Math.atan2( - m23, m33 );
5808 this._y = 0;
5809 }
5810 break;
5811 default:
5812 console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order );
5813 }
5814 this._order = order;
5815 if ( update !== false ) this._onChangeCallback();
5816 return this;
5817 }
5818 setFromQuaternion( q, order, update ) {
5819 _matrix.makeRotationFromQuaternion( q );
5820 return this.setFromRotationMatrix( _matrix, order, update );
5821 }
5822 setFromVector3( v, order ) {
5823 return this.set( v.x, v.y, v.z, order || this._order );
5824 }
5825 reorder( newOrder ) {
5826 _quaternion$1.setFromEuler( this );
5827 return this.setFromQuaternion( _quaternion$1, newOrder );
5828 }
5829 equals( euler ) {
5830 return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
5831 }
5832 fromArray( array ) {
5833 this._x = array[ 0 ];
5834 this._y = array[ 1 ];
5835 this._z = array[ 2 ];
5836 if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
5837 this._onChangeCallback();
5838 return this;
5839 }
5840 toArray( array, offset ) {
5841 if ( array === undefined ) array = [];
5842 if ( offset === undefined ) offset = 0;
5843 array[ offset ] = this._x;
5844 array[ offset + 1 ] = this._y;
5845 array[ offset + 2 ] = this._z;
5846 array[ offset + 3 ] = this._order;
5847 return array;
5848 }
5849 toVector3( optionalResult ) {
5850 if ( optionalResult ) {
5851 return optionalResult.set( this._x, this._y, this._z );
5852 } else {
5853 return new Vector3( this._x, this._y, this._z );
5854 }
5855 }
5856 _onChange( callback ) {
5857 this._onChangeCallback = callback;
5858 return this;
5859 }
5860 _onChangeCallback() {}
5861}
5862Euler.DefaultOrder = 'XYZ';
5863Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
5864const _matrix = new Matrix4();
5865const _quaternion$1 = new Quaternion();
5866class Layers {
5867 constructor() {
5868 this.mask = 1 | 0;
5869 }
5870 set( channel ) {
5871 this.mask = 1 << channel | 0;
5872 }
5873 enable( channel ) {
5874 this.mask |= 1 << channel | 0;
5875 }
5876 enableAll() {
5877 this.mask = 0xffffffff | 0;
5878 }
5879 toggle( channel ) {
5880 this.mask ^= 1 << channel | 0;
5881 }
5882 disable( channel ) {
5883 this.mask &= ~ ( 1 << channel | 0 );
5884 }
5885 disableAll() {
5886 this.mask = 0;
5887 }
5888 test( layers ) {
5889 return ( this.mask & layers.mask ) !== 0;
5890 }
5891}
5892class Matrix3 {
5893 constructor() {
5894 Object.defineProperty( this, 'isMatrix3', { value: true } );
5895 this.elements = [
5896 1, 0, 0,
5897 0, 1, 0,
5898 0, 0, 1
5899 ];
5900 if ( arguments.length > 0 ) {
5901 console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );
5902 }
5903 }
5904 set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
5905 const te = this.elements;
5906 te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;
5907 te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;
5908 te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;
5909 return this;
5910 }
5911 identity() {
5912 this.set(
5913 1, 0, 0,
5914 0, 1, 0,
5915 0, 0, 1
5916 );
5917 return this;
5918 }
5919 clone() {
5920 return new this.constructor().fromArray( this.elements );
5921 }
5922 copy( m ) {
5923 const te = this.elements;
5924 const me = m.elements;
5925 te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];
5926 te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];
5927 te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];
5928 return this;
5929 }
5930 extractBasis( xAxis, yAxis, zAxis ) {
5931 xAxis.setFromMatrix3Column( this, 0 );
5932 yAxis.setFromMatrix3Column( this, 1 );
5933 zAxis.setFromMatrix3Column( this, 2 );
5934 return this;
5935 }
5936 setFromMatrix4( m ) {
5937 const me = m.elements;
5938 this.set(
5939 me[ 0 ], me[ 4 ], me[ 8 ],
5940 me[ 1 ], me[ 5 ], me[ 9 ],
5941 me[ 2 ], me[ 6 ], me[ 10 ]
5942 );
5943 return this;
5944 }
5945 multiply( m ) {
5946 return this.multiplyMatrices( this, m );
5947 }
5948 premultiply( m ) {
5949 return this.multiplyMatrices( m, this );
5950 }
5951 multiplyMatrices( a, b ) {
5952 const ae = a.elements;
5953 const be = b.elements;
5954 const te = this.elements;
5955 const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];
5956 const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];
5957 const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];
5958 const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];
5959 const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];
5960 const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];
5961 te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;
5962 te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;
5963 te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;
5964 te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;
5965 te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;
5966 te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;
5967 te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;
5968 te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;
5969 te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;
5970 return this;
5971 }
5972 multiplyScalar( s ) {
5973 const te = this.elements;
5974 te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;
5975 te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;
5976 te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;
5977 return this;
5978 }
5979 determinant() {
5980 const te = this.elements;
5981 const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],
5982 d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],
5983 g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];
5984 return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;
5985 }
5986 getInverse( matrix, throwOnDegenerate ) {
5987 if ( throwOnDegenerate !== undefined ) {
5988 console.warn( "THREE.Matrix3: .getInverse() can no longer be configured to throw on degenerate." );
5989 }
5990 const me = matrix.elements,
5991 te = this.elements,
5992 n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],
5993 n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],
5994 n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],
5995 t11 = n33 * n22 - n32 * n23,
5996 t12 = n32 * n13 - n33 * n12,
5997 t13 = n23 * n12 - n22 * n13,
5998 det = n11 * t11 + n21 * t12 + n31 * t13;
5999 if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 );
6000 const detInv = 1 / det;
6001 te[ 0 ] = t11 * detInv;
6002 te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;
6003 te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;
6004 te[ 3 ] = t12 * detInv;
6005 te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;
6006 te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;
6007 te[ 6 ] = t13 * detInv;
6008 te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;
6009 te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;
6010 return this;
6011 }
6012 transpose() {
6013 let tmp;
6014 const m = this.elements;
6015 tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;
6016 tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;
6017 tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;
6018 return this;
6019 }
6020 getNormalMatrix( matrix4 ) {
6021 return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();
6022 }
6023 transposeIntoArray( r ) {
6024 const m = this.elements;
6025 r[ 0 ] = m[ 0 ];
6026 r[ 1 ] = m[ 3 ];
6027 r[ 2 ] = m[ 6 ];
6028 r[ 3 ] = m[ 1 ];
6029 r[ 4 ] = m[ 4 ];
6030 r[ 5 ] = m[ 7 ];
6031 r[ 6 ] = m[ 2 ];
6032 r[ 7 ] = m[ 5 ];
6033 r[ 8 ] = m[ 8 ];
6034 return this;
6035 }
6036 setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) {
6037 const c = Math.cos( rotation );
6038 const s = Math.sin( rotation );
6039 this.set(
6040 sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,
6041 - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,
6042 0, 0, 1
6043 );
6044 }
6045 scale( sx, sy ) {
6046 const te = this.elements;
6047 te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;
6048 te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;
6049 return this;
6050 }
6051 rotate( theta ) {
6052 const c = Math.cos( theta );
6053 const s = Math.sin( theta );
6054 const te = this.elements;
6055 const a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];
6056 const a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];
6057 te[ 0 ] = c * a11 + s * a21;
6058 te[ 3 ] = c * a12 + s * a22;
6059 te[ 6 ] = c * a13 + s * a23;
6060 te[ 1 ] = - s * a11 + c * a21;
6061 te[ 4 ] = - s * a12 + c * a22;
6062 te[ 7 ] = - s * a13 + c * a23;
6063 return this;
6064 }
6065 translate( tx, ty ) {
6066 const te = this.elements;
6067 te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];
6068 te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];
6069 return this;
6070 }
6071 equals( matrix ) {
6072 const te = this.elements;
6073 const me = matrix.elements;
6074 for ( let i = 0; i < 9; i ++ ) {
6075 if ( te[ i ] !== me[ i ] ) return false;
6076 }
6077 return true;
6078 }
6079 fromArray( array, offset ) {
6080 if ( offset === undefined ) offset = 0;
6081 for ( let i = 0; i < 9; i ++ ) {
6082 this.elements[ i ] = array[ i + offset ];
6083 }
6084 return this;
6085 }
6086 toArray( array, offset ) {
6087 if ( array === undefined ) array = [];
6088 if ( offset === undefined ) offset = 0;
6089 const te = this.elements;
6090 array[ offset ] = te[ 0 ];
6091 array[ offset + 1 ] = te[ 1 ];
6092 array[ offset + 2 ] = te[ 2 ];
6093 array[ offset + 3 ] = te[ 3 ];
6094 array[ offset + 4 ] = te[ 4 ];
6095 array[ offset + 5 ] = te[ 5 ];
6096 array[ offset + 6 ] = te[ 6 ];
6097 array[ offset + 7 ] = te[ 7 ];
6098 array[ offset + 8 ] = te[ 8 ];
6099 return array;
6100 }
6101}
6102let _object3DId = 0;
6103const _v1 = new Vector3();
6104const _q1 = new Quaternion();
6105const _m1 = new Matrix4();
6106const _target = new Vector3();
6107const _position = new Vector3();
6108const _scale = new Vector3();
6109const _quaternion = new Quaternion();
6110const _xAxis = new Vector3( 1, 0, 0 );
6111const _yAxis = new Vector3( 0, 1, 0 );
6112const _zAxis = new Vector3( 0, 0, 1 );
6113const _addedEvent = { type: 'added' };
6114const _removedEvent = { type: 'removed' };
6115function Object3D() {
6116 Object.defineProperty( this, 'id', { value: _object3DId ++ } );
6117 this.uuid = MathUtils.generateUUID();
6118 this.name = '';
6119 this.type = 'Object3D';
6120 this.parent = null;
6121 this.children = [];
6122 this.up = Object3D.DefaultUp.clone();
6123 const position = new Vector3();
6124 const rotation = new Euler();
6125 const quaternion = new Quaternion();
6126 const scale = new Vector3( 1, 1, 1 );
6127 function onRotationChange() {
6128 quaternion.setFromEuler( rotation, false );
6129 }
6130 function onQuaternionChange() {
6131 rotation.setFromQuaternion( quaternion, undefined, false );
6132 }
6133 rotation._onChange( onRotationChange );
6134 quaternion._onChange( onQuaternionChange );
6135 Object.defineProperties( this, {
6136 position: {
6137 configurable: true,
6138 enumerable: true,
6139 value: position
6140 },
6141 rotation: {
6142 configurable: true,
6143 enumerable: true,
6144 value: rotation
6145 },
6146 quaternion: {
6147 configurable: true,
6148 enumerable: true,
6149 value: quaternion
6150 },
6151 scale: {
6152 configurable: true,
6153 enumerable: true,
6154 value: scale
6155 },
6156 modelViewMatrix: {
6157 value: new Matrix4()
6158 },
6159 normalMatrix: {
6160 value: new Matrix3()
6161 }
6162 } );
6163 this.matrix = new Matrix4();
6164 this.matrixWorld = new Matrix4();
6165 this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;
6166 this.matrixWorldNeedsUpdate = false;
6167 this.layers = new Layers();
6168 this.visible = true;
6169 this.castShadow = false;
6170 this.receiveShadow = false;
6171 this.frustumCulled = true;
6172 this.renderOrder = 0;
6173 this.userData = {};
6174}
6175Object3D.DefaultUp = new Vector3( 0, 1, 0 );
6176Object3D.DefaultMatrixAutoUpdate = true;
6177Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
6178 constructor: Object3D,
6179 isObject3D: true,
6180 onBeforeRender: function () {},
6181 onAfterRender: function () {},
6182 applyMatrix4: function ( matrix ) {
6183 if ( this.matrixAutoUpdate ) this.updateMatrix();
6184 this.matrix.premultiply( matrix );
6185 this.matrix.decompose( this.position, this.quaternion, this.scale );
6186 },
6187 applyQuaternion: function ( q ) {
6188 this.quaternion.premultiply( q );
6189 return this;
6190 },
6191 setRotationFromAxisAngle: function ( axis, angle ) {
6192 this.quaternion.setFromAxisAngle( axis, angle );
6193 },
6194 setRotationFromEuler: function ( euler ) {
6195 this.quaternion.setFromEuler( euler, true );
6196 },
6197 setRotationFromMatrix: function ( m ) {
6198 this.quaternion.setFromRotationMatrix( m );
6199 },
6200 setRotationFromQuaternion: function ( q ) {
6201 this.quaternion.copy( q );
6202 },
6203 rotateOnAxis: function ( axis, angle ) {
6204 _q1.setFromAxisAngle( axis, angle );
6205 this.quaternion.multiply( _q1 );
6206 return this;
6207 },
6208 rotateOnWorldAxis: function ( axis, angle ) {
6209 _q1.setFromAxisAngle( axis, angle );
6210 this.quaternion.premultiply( _q1 );
6211 return this;
6212 },
6213 rotateX: function ( angle ) {
6214 return this.rotateOnAxis( _xAxis, angle );
6215 },
6216 rotateY: function ( angle ) {
6217 return this.rotateOnAxis( _yAxis, angle );
6218 },
6219 rotateZ: function ( angle ) {
6220 return this.rotateOnAxis( _zAxis, angle );
6221 },
6222 translateOnAxis: function ( axis, distance ) {
6223 _v1.copy( axis ).applyQuaternion( this.quaternion );
6224 this.position.add( _v1.multiplyScalar( distance ) );
6225 return this;
6226 },
6227 translateX: function ( distance ) {
6228 return this.translateOnAxis( _xAxis, distance );
6229 },
6230 translateY: function ( distance ) {
6231 return this.translateOnAxis( _yAxis, distance );
6232 },
6233 translateZ: function ( distance ) {
6234 return this.translateOnAxis( _zAxis, distance );
6235 },
6236 localToWorld: function ( vector ) {
6237 return vector.applyMatrix4( this.matrixWorld );
6238 },
6239 worldToLocal: function ( vector ) {
6240 return vector.applyMatrix4( _m1.getInverse( this.matrixWorld ) );
6241 },
6242 lookAt: function ( x, y, z ) {
6243 if ( x.isVector3 ) {
6244 _target.copy( x );
6245 } else {
6246 _target.set( x, y, z );
6247 }
6248 const parent = this.parent;
6249 this.updateWorldMatrix( true, false );
6250 _position.setFromMatrixPosition( this.matrixWorld );
6251 if ( this.isCamera || this.isLight ) {
6252 _m1.lookAt( _position, _target, this.up );
6253 } else {
6254 _m1.lookAt( _target, _position, this.up );
6255 }
6256 this.quaternion.setFromRotationMatrix( _m1 );
6257 if ( parent ) {
6258 _m1.extractRotation( parent.matrixWorld );
6259 _q1.setFromRotationMatrix( _m1 );
6260 this.quaternion.premultiply( _q1.inverse() );
6261 }
6262 },
6263 add: function ( object ) {
6264 if ( arguments.length > 1 ) {
6265 for ( let i = 0; i < arguments.length; i ++ ) {
6266 this.add( arguments[ i ] );
6267 }
6268 return this;
6269 }
6270 if ( object === this ) {
6271 console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
6272 return this;
6273 }
6274 if ( ( object && object.isObject3D ) ) {
6275 if ( object.parent !== null ) {
6276 object.parent.remove( object );
6277 }
6278 object.parent = this;
6279 this.children.push( object );
6280 object.dispatchEvent( _addedEvent );
6281 } else {
6282 console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
6283 }
6284 return this;
6285 },
6286 remove: function ( object ) {
6287 if ( arguments.length > 1 ) {
6288 for ( let i = 0; i < arguments.length; i ++ ) {
6289 this.remove( arguments[ i ] );
6290 }
6291 return this;
6292 }
6293 const index = this.children.indexOf( object );
6294 if ( index !== - 1 ) {
6295 object.parent = null;
6296 this.children.splice( index, 1 );
6297 object.dispatchEvent( _removedEvent );
6298 }
6299 return this;
6300 },
6301 attach: function ( object ) {
6302 this.updateWorldMatrix( true, false );
6303 _m1.getInverse( this.matrixWorld );
6304 if ( object.parent !== null ) {
6305 object.parent.updateWorldMatrix( true, false );
6306 _m1.multiply( object.parent.matrixWorld );
6307 }
6308 object.applyMatrix4( _m1 );
6309 object.updateWorldMatrix( false, false );
6310 this.add( object );
6311 return this;
6312 },
6313 getObjectById: function ( id ) {
6314 return this.getObjectByProperty( 'id', id );
6315 },
6316 getObjectByName: function ( name ) {
6317 return this.getObjectByProperty( 'name', name );
6318 },
6319 getObjectByProperty: function ( name, value ) {
6320 if ( this[ name ] === value ) return this;
6321 for ( let i = 0, l = this.children.length; i < l; i ++ ) {
6322 const child = this.children[ i ];
6323 const object = child.getObjectByProperty( name, value );
6324 if ( object !== undefined ) {
6325 return object;
6326 }
6327 }
6328 return undefined;
6329 },
6330 getWorldPosition: function ( target ) {
6331 if ( target === undefined ) {
6332 console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );
6333 target = new Vector3();
6334 }
6335 this.updateMatrixWorld( true );
6336 return target.setFromMatrixPosition( this.matrixWorld );
6337 },
6338 getWorldQuaternion: function ( target ) {
6339 if ( target === undefined ) {
6340 console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );
6341 target = new Quaternion();
6342 }
6343 this.updateMatrixWorld( true );
6344 this.matrixWorld.decompose( _position, target, _scale );
6345 return target;
6346 },
6347 getWorldScale: function ( target ) {
6348 if ( target === undefined ) {
6349 console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );
6350 target = new Vector3();
6351 }
6352 this.updateMatrixWorld( true );
6353 this.matrixWorld.decompose( _position, _quaternion, target );
6354 return target;
6355 },
6356 getWorldDirection: function ( target ) {
6357 if ( target === undefined ) {
6358 console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );
6359 target = new Vector3();
6360 }
6361 this.updateMatrixWorld( true );
6362 const e = this.matrixWorld.elements;
6363 return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize();
6364 },
6365 raycast: function () {},
6366 traverse: function ( callback ) {
6367 callback( this );
6368 const children = this.children;
6369 for ( let i = 0, l = children.length; i < l; i ++ ) {
6370 children[ i ].traverse( callback );
6371 }
6372 },
6373 traverseVisible: function ( callback ) {
6374 if ( this.visible === false ) return;
6375 callback( this );
6376 const children = this.children;
6377 for ( let i = 0, l = children.length; i < l; i ++ ) {
6378 children[ i ].traverseVisible( callback );
6379 }
6380 },
6381 traverseAncestors: function ( callback ) {
6382 const parent = this.parent;
6383 if ( parent !== null ) {
6384 callback( parent );
6385 parent.traverseAncestors( callback );
6386 }
6387 },
6388 updateMatrix: function () {
6389 this.matrix.compose( this.position, this.quaternion, this.scale );
6390 this.matrixWorldNeedsUpdate = true;
6391 },
6392 updateMatrixWorld: function ( force ) {
6393 if ( this.matrixAutoUpdate ) this.updateMatrix();
6394 if ( this.matrixWorldNeedsUpdate || force ) {
6395 if ( this.parent === null ) {
6396 this.matrixWorld.copy( this.matrix );
6397 } else {
6398 this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
6399 }
6400 this.matrixWorldNeedsUpdate = false;
6401 force = true;
6402 }
6403 const children = this.children;
6404 for ( let i = 0, l = children.length; i < l; i ++ ) {
6405 children[ i ].updateMatrixWorld( force );
6406 }
6407 },
6408 updateWorldMatrix: function ( updateParents, updateChildren ) {
6409 const parent = this.parent;
6410 if ( updateParents === true && parent !== null ) {
6411 parent.updateWorldMatrix( true, false );
6412 }
6413 if ( this.matrixAutoUpdate ) this.updateMatrix();
6414 if ( this.parent === null ) {
6415 this.matrixWorld.copy( this.matrix );
6416 } else {
6417 this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
6418 }
6419 if ( updateChildren === true ) {
6420 const children = this.children;
6421 for ( let i = 0, l = children.length; i < l; i ++ ) {
6422 children[ i ].updateWorldMatrix( false, true );
6423 }
6424 }
6425 },
6426 toJSON: function ( meta ) {
6427 const isRootObject = ( meta === undefined || typeof meta === 'string' );
6428 const output = {};
6429 if ( isRootObject ) {
6430 meta = {
6431 geometries: {},
6432 materials: {},
6433 textures: {},
6434 images: {},
6435 shapes: {}
6436 };
6437 output.metadata = {
6438 version: 4.5,
6439 type: 'Object',
6440 generator: 'Object3D.toJSON'
6441 };
6442 }
6443 const object = {};
6444 object.uuid = this.uuid;
6445 object.type = this.type;
6446 if ( this.name !== '' ) object.name = this.name;
6447 if ( this.castShadow === true ) object.castShadow = true;
6448 if ( this.receiveShadow === true ) object.receiveShadow = true;
6449 if ( this.visible === false ) object.visible = false;
6450 if ( this.frustumCulled === false ) object.frustumCulled = false;
6451 if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;
6452 if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
6453 object.layers = this.layers.mask;
6454 object.matrix = this.matrix.toArray();
6455 if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false;
6456 if ( this.isInstancedMesh ) {
6457 object.type = 'InstancedMesh';
6458 object.count = this.count;
6459 object.instanceMatrix = this.instanceMatrix.toJSON();
6460 }
6461 function serialize( library, element ) {
6462 if ( library[ element.uuid ] === undefined ) {
6463 library[ element.uuid ] = element.toJSON( meta );
6464 }
6465 return element.uuid;
6466 }
6467 if ( this.isMesh || this.isLine || this.isPoints ) {
6468 object.geometry = serialize( meta.geometries, this.geometry );
6469 const parameters = this.geometry.parameters;
6470 if ( parameters !== undefined && parameters.shapes !== undefined ) {
6471 const shapes = parameters.shapes;
6472 if ( Array.isArray( shapes ) ) {
6473 for ( let i = 0, l = shapes.length; i < l; i ++ ) {
6474 const shape = shapes[ i ];
6475 serialize( meta.shapes, shape );
6476 }
6477 } else {
6478 serialize( meta.shapes, shapes );
6479 }
6480 }
6481 }
6482 if ( this.material !== undefined ) {
6483 if ( Array.isArray( this.material ) ) {
6484 const uuids = [];
6485 for ( let i = 0, l = this.material.length; i < l; i ++ ) {
6486 uuids.push( serialize( meta.materials, this.material[ i ] ) );
6487 }
6488 object.material = uuids;
6489 } else {
6490 object.material = serialize( meta.materials, this.material );
6491 }
6492 }
6493 if ( this.children.length > 0 ) {
6494 object.children = [];
6495 for ( let i = 0; i < this.children.length; i ++ ) {
6496 object.children.push( this.children[ i ].toJSON( meta ).object );
6497 }
6498 }
6499 if ( isRootObject ) {
6500 const geometries = extractFromCache( meta.geometries );
6501 const materials = extractFromCache( meta.materials );
6502 const textures = extractFromCache( meta.textures );
6503 const images = extractFromCache( meta.images );
6504 const shapes = extractFromCache( meta.shapes );
6505 if ( geometries.length > 0 ) output.geometries = geometries;
6506 if ( materials.length > 0 ) output.materials = materials;
6507 if ( textures.length > 0 ) output.textures = textures;
6508 if ( images.length > 0 ) output.images = images;
6509 if ( shapes.length > 0 ) output.shapes = shapes;
6510 }
6511 output.object = object;
6512 return output;
6513 function extractFromCache( cache ) {
6514 const values = [];
6515 for ( const key in cache ) {
6516 const data = cache[ key ];
6517 delete data.metadata;
6518 values.push( data );
6519 }
6520 return values;
6521 }
6522 },
6523 clone: function ( recursive ) {
6524 return new this.constructor().copy( this, recursive );
6525 },
6526 copy: function ( source, recursive ) {
6527 if ( recursive === undefined ) recursive = true;
6528 this.name = source.name;
6529 this.up.copy( source.up );
6530 this.position.copy( source.position );
6531 this.rotation.order = source.rotation.order;
6532 this.quaternion.copy( source.quaternion );
6533 this.scale.copy( source.scale );
6534 this.matrix.copy( source.matrix );
6535 this.matrixWorld.copy( source.matrixWorld );
6536 this.matrixAutoUpdate = source.matrixAutoUpdate;
6537 this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;
6538 this.layers.mask = source.layers.mask;
6539 this.visible = source.visible;
6540 this.castShadow = source.castShadow;
6541 this.receiveShadow = source.receiveShadow;
6542 this.frustumCulled = source.frustumCulled;
6543 this.renderOrder = source.renderOrder;
6544 this.userData = JSON.parse( JSON.stringify( source.userData ) );
6545 if ( recursive === true ) {
6546 for ( let i = 0; i < source.children.length; i ++ ) {
6547 const child = source.children[ i ];
6548 this.add( child.clone() );
6549 }
6550 }
6551 return this;
6552 }
6553} );
6554
6555const { checkArg, checkArgs } = util;
6556class Turtle3D extends Turtle {
6557 constructor() {
6558 super();
6559 this.obj3d = new Object3D();
6560 }
6561 newInstance(agentProto) {
6562 const insstance = super.newInstance(agentProto);
6563 insstance.obj3d = new Object3D();
6564 insstance.obj3d.rotation.order = 'ZYX';
6565 insstance.reset();
6566 return insstance
6567 }
6568 reset() {
6569 this.obj3d.position.set(0, 0, 0);
6570 this.obj3d.rotation.set(0, 0, 0);
6571 this.heading = 0;
6572 }
6573 setxyz(x, y, z) {
6574 checkArgs(arguments);
6575 super.setxy(x, y, z);
6576 }
6577 getxyz() {
6578 return this.obj3d.position.toArray()
6579 }
6580 setRotation(x, y, z) {
6581 checkArgs(arguments);
6582 this.obj3d.rotation.set(x, y, z);
6583 }
6584 getRotation() {
6585 const { x, y, z } = this.obj3d.rotation;
6586 return [x, y, z]
6587 }
6588 getThetaPhiPsi() {
6589 return this.getRotation().reverse()
6590 }
6591 getHeadingPitchRoll() {
6592 const [psi, phi, theta] = this.getRotation();
6593 const heading = radToHeading(theta);
6594 const pitch = radToDeg(-phi);
6595 const roll = radToDeg(psi);
6596 return [heading, pitch, roll]
6597 }
6598 getDxDyDz() {
6599 return [this.dx, this.dy, this.dz]
6600 }
6601 get x() {
6602 return this.obj3d.position.x
6603 }
6604 set x(d) {
6605 checkArg(d);
6606 this.obj3d.position.x = d;
6607 }
6608 get y() {
6609 return this.obj3d.position.y
6610 }
6611 set y(d) {
6612 checkArg(d);
6613 this.obj3d.position.y = d;
6614 }
6615 get z() {
6616 return this.obj3d.position.z
6617 }
6618 set z(d) {
6619 checkArg(d);
6620 if (this.obj3d) this.obj3d.position.z = d;
6621 }
6622 get theta() {
6623 return this.obj3d.rotation.z
6624 }
6625 set theta(rad) {
6626 checkArg(rad);
6627 if (this.obj3d) this.obj3d.rotation.z = rad;
6628 }
6629 get heading() {
6630 return this.model.fromRads(this.obj3d.rotation.z)
6631 }
6632 set heading(angle) {
6633 checkArg(angle);
6634 this.obj3d.rotation.z = this.model.toRads(angle);
6635 }
6636 get pitch() {
6637 return -this.model.fromAngleRads(this.obj3d.rotation.y)
6638 }
6639 set pitch(angle) {
6640 checkArg(angle);
6641 this.obj3d.rotation.y = -this.model.toAngleRads(angle);
6642 }
6643 get roll() {
6644 return this.model.fromAngleRads(this.obj3d.rotation.x)
6645 }
6646 set roll(angle) {
6647 checkArg(angle);
6648 this.obj3d.rotation.x = this.model.toAngleRads(angle);
6649 }
6650 forward(d) {
6651 checkArg(d);
6652 const p0 = this.patch;
6653 this.obj3d.translateX(d);
6654 super.checkXYZ(p0);
6655 }
6656 right(angle) {
6657 this.left(-angle);
6658 }
6659 left(angle) {
6660 checkArg(angle);
6661 this.obj3d.rotateZ(this.model.toAngleRads(angle));
6662 }
6663 tiltUp(angle) {
6664 this.tiltDown(-angle);
6665 }
6666 tiltDown(angle) {
6667 checkArg(angle);
6668 this.obj3d.rotateY(this.model.toAngleRads(angle));
6669 }
6670 rollRight(angle) {
6671 checkArg(angle);
6672 this.obj3d.rotateX(this.model.toAngleRads(angle));
6673 }
6674 rollLeft(angle) {
6675 this.rollRight(-angle);
6676 }
6677 facexyz(x1, y1, z1) {
6678 checkArgs(arguments);
6679 const headingTowards = this.towardsXY(x1, y1);
6680 const pitchTowards = this.towardsPitchXYZ(x1, y1, z1);
6681 this.heading = headingTowards;
6682 this.pitch = pitchTowards;
6683 }
6684 face(agent) {
6685 checkArg(agent, 'object');
6686 const { x, y, z } = agent;
6687 this.facexyz(x, y, z);
6688 }
6689 towardsPitchXYZ(x1, y1, z1) {
6690 checkArgs(arguments);
6691 const [x, y, z] = this.getxyz();
6692 const [dx, dy, dz] = [x1 - x, y1 - y, z1 - z];
6693 const xyhypot = Math.hypot(dx, dy);
6694 const pitchRads = Math.atan2(dz, xyhypot);
6695 return this.model.fromAngleRads(pitchRads)
6696 }
6697 towardsPitch(agent) {
6698 checkArg(agent, 'object');
6699 const { x, y, z } = agent;
6700 this.towardsPitchXYZ(x, y, z);
6701 }
6702 distance(agent) {
6703 checkArg(agent, 'object');
6704 const { x, y, z } = agent;
6705 return this.distanceXYZ(x, y, z)
6706 }
6707 distanceXYZ(x1, y1, z1) {
6708 checkArgs(arguments);
6709 const { x, y, z } = this;
6710 return distance3(x, y, z, x1, y1, z1)
6711 }
6712 get dx() {
6713 const { y: pitch, z: heading } = this.obj3d.rotation;
6714 return Math.cos(pitch) * Math.cos(heading)
6715 }
6716 get dy() {
6717 const { y: pitch, z: heading } = this.obj3d.rotation;
6718 return Math.cos(pitch) * Math.sin(heading)
6719 }
6720 get dz() {
6721 const pitch = this.obj3d.rotation.y;
6722 return Math.sin(pitch)
6723 }
6724}
6725
6726class Model3D extends Model {
6727 initAgentSet(name, AgentsetClass, AgentClass) {
6728 if (name === 'turtles') AgentClass = Turtle3D;
6729 super.initAgentSet(name, AgentsetClass, AgentClass);
6730 }
6731}
6732
6733const Color = {
6734 rgbaCssColor(r, g, b, a = 255) {
6735 a = a / 255;
6736 const a2 = a.toPrecision(2);
6737 return a === 1 ? `rgb(${r},${g},${b})` : `rgba(${r},${g},${b},${a2})`
6738 },
6739 hslCssColor(h, s = 100, l = 50, a = 255) {
6740 a = a / 255;
6741 const a4 = a.toPrecision(4);
6742 return a === 1
6743 ? `hsl(${h},${s}%,${l}%)`
6744 : `hsla(${h},${s}%,${l}%,${a4})`
6745 },
6746 hexCssColor(r, g, b) {
6747 return `#${(0x1000000 | (b | (g << 8) | (r << 16)))
6748 .toString(16)
6749 .slice(-6)}`
6750 },
6751 cssColor(r, g, b, a = 255) {
6752 return a === 255
6753 ? this.hexCssColor(r, g, b)
6754 : this.rgbaCssColor(r, g, b, a)
6755 },
6756 randomCssColor() {
6757 const r255 = () => randomInt(256);
6758 return this.cssColor(r255(), r255(), r255())
6759 },
6760 randomGrayCssColor(min = 0, max = 255) {
6761 const gray = randomInt2(min, max);
6762 return this.cssColor(gray, gray, gray)
6763 },
6764 cssToPixel(string) {
6765 const rgba = this.cssToUint8Array(string);
6766 return this.rgbaToPixel(...rgba)
6767 },
6768 rgbaToPixel(r, g, b, a = 255) {
6769 const rgba = new Uint8Array([r, g, b, a]);
6770 const pixels = new Uint32Array(rgba.buffer);
6771 return pixels[0]
6772 },
6773 randomPixel() {
6774 const r255 = () => randomInt(256);
6775 return this.rgbaToPixel(r255(), r255(), r255())
6776 },
6777 randomGrayPixel(min = 0, max = 255) {
6778 const gray = randomInt2(min, max);
6779 return this.rgbaToPixel(gray, gray, gray)
6780 },
6781 sharedCtx1x1: createCtx(1, 1, false, { willReadFrequently: true }),
6782 cssToUint8Array(string) {
6783 this.sharedCtx1x1.clearRect(0, 0, 1, 1);
6784 this.sharedCtx1x1.fillStyle = string;
6785 this.sharedCtx1x1.fillRect(0, 0, 1, 1);
6786 return this.sharedCtx1x1.getImageData(0, 0, 1, 1).data
6787 },
6788 typedColor(r, g, b, a = 255) {
6789 if (g === undefined) return this.toTypedColor(r)
6790 const u8array = new Uint8ClampedArray([r, g, b, a]);
6791 u8array.pixelArray = new Uint32Array(u8array.buffer);
6792 Object.setPrototypeOf(u8array, TypedColorProto);
6793 return u8array
6794 },
6795 isTypedColor(any) {
6796 return any && any.constructor === Uint8ClampedArray && any.pixelArray
6797 },
6798 toTypedColor(value, colorType) {
6799 if (this.isTypedColor(value)) return value
6800 const tc = this.typedColor(0, 0, 0, 0);
6801 if (colorType == null) {
6802 if (isString(value)) tc.css = value;
6803 else if (isNumber$1(value)) tc.pixel = value;
6804 else if (isArray(value)) tc.rgb = value;
6805 else if (isTypedArray(value)) tc.rgb = value;
6806 else throw Error(`toTypedColor: illegal value ${value}`)
6807 } else {
6808 tc[colorType] = value;
6809 }
6810 return tc
6811 },
6812 randomTypedColor() {
6813 const r255 = () => randomInt(256);
6814 return this.typedColor(r255(), r255(), r255())
6815 },
6816 randomGrayTypedColor(min = 0, max = 255) {
6817 const gray = randomInt2(min, max);
6818 return this.typedColor(gray, gray, gray)
6819 },
6820 randomColorArray(length) {
6821 const colors = new Array(length);
6822 forLoop(colors, (c, i) => (colors[i] = this.randomTypedColor()));
6823 return colors
6824 },
6825 randomGrayArray(length, min = 0, max = 255) {
6826 const grays = new Array(length);
6827 forLoop(
6828 grays,
6829 (g, i) => (grays[i] = this.randomGrayTypedColor(min, max))
6830 );
6831 return grays
6832 },
6833};
6834const TypedColorProto = {
6835 __proto__: Uint8ClampedArray.prototype,
6836 setColor(r, g, b, a = 255) {
6837 this.checkColorChange();
6838 this[0] = r;
6839 this[1] = g;
6840 this[2] = b;
6841 this[3] = a;
6842 },
6843 set rgb(rgbaArray) {
6844 this.setColor(...rgbaArray);
6845 },
6846 get rgb() {
6847 return this
6848 },
6849 setAlpha(alpha) {
6850 this.checkColorChange();
6851 this[3] = alpha;
6852 },
6853 getAlpha() {
6854 return this[3]
6855 },
6856 get alpha() {
6857 return this.getAlpha()
6858 },
6859 set alpha(alpha) {
6860 this.setAlpha(alpha);
6861 },
6862 setPixel(pixel) {
6863 this.checkColorChange();
6864 this.pixelArray[0] = pixel;
6865 },
6866 getPixel() {
6867 return this.pixelArray[0]
6868 },
6869 get pixel() {
6870 return this.getPixel()
6871 },
6872 set pixel(pixel) {
6873 this.setPixel(pixel);
6874 },
6875 setCss(string) {
6876 return this.setColor(...Color.cssToUint8Array(string))
6877 },
6878 getCss() {
6879 if (this.string == null) this.string = Color.cssColor(...this);
6880 return this.string
6881 },
6882 get css() {
6883 return this.getCss()
6884 },
6885 set css(string) {
6886 this.setCss(string);
6887 },
6888 setWebgl(array) {
6889 if (array.length !== 3)
6890 throw Error(
6891 'setWebgl array length must be 3, length:',
6892 array.length
6893 )
6894 this.setColor(
6895 array[0] * 255,
6896 array[1] * 255,
6897 array[2] * 255
6898 );
6899 },
6900 getWebgl() {
6901 return [this[0] / 255, this[1] / 255, this[2] / 255]
6902 },
6903 get webgl() {
6904 return this.getWebgl()
6905 },
6906 set webgl(array) {
6907 this.setWebgl(array);
6908 },
6909 checkColorChange() {
6910 this.string = null;
6911 },
6912 equals(color) {
6913 return this.getPixel() === color.getPixel()
6914 },
6915 toString() {
6916 return `[${Array.from(this).toString()}]`
6917 },
6918 rgbDistance(r, g, b) {
6919 const [r1, g1, b1] = this;
6920 const rMean = Math.round((r1 + r) / 2);
6921 const [dr, dg, db] = [r1 - r, g1 - g, b1 - b];
6922 const [dr2, dg2, db2] = [dr * dr, dg * dg, db * db];
6923 const distanceSq =
6924 (((512 + rMean) * dr2) >> 8) +
6925 4 * dg2 +
6926 (((767 - rMean) * db2) >> 8);
6927 return distanceSq
6928 },
6929};
6930
6931function getPixel(color) {
6932 if (typeof color === 'number') return color
6933 if (color.pixel) return color.pixel
6934 if (color === 'transparent') return 0
6935 return Color.toTypedColor(color).pixel
6936}
6937class PatchesView {
6938 constructor(width, height) {
6939 this.ctx = createCtx(width, height);
6940 this.resetImageData();
6941 this.useImageSmoothing = false;
6942 }
6943 resetImageData() {
6944 this.imageData = ctxImageData(this.ctx);
6945 this.pixels = new Uint32Array(this.imageData.data.buffer);
6946 }
6947 setPatchesSmoothing(smoothting) {
6948 this.useImageSmoothing = smoothting;
6949 }
6950 setPixels(data, pixelFcn = d => d) {
6951 if (isOofA(data)) data = toAofO(data);
6952 if (data.length !== this.pixels.length) {
6953 throw Error(
6954 'setPixels, data.length != pixels.length ' +
6955 data.length +
6956 ' ' +
6957 this.pixels.length
6958 )
6959 }
6960 forLoop(data, (d, i) => {
6961 this.pixels[i] = getPixel(pixelFcn(d));
6962 });
6963 }
6964 createPixels(pixelFcn) {
6965 repeat(this.pixels.length, i => {
6966 this.pixels[i] = getPixel(pixelFcn(i));
6967 });
6968 }
6969 setPixel(index, pixel) {
6970 this.pixels[index] = getPixel(pixel);
6971 }
6972 draw(ctx) {
6973 const smoothing = this.ctx.imageSmoothingEnabled;
6974 ctx.imageSmoothingEnabled = this.useImageSmoothing;
6975 this.ctx.putImageData(this.imageData, 0, 0);
6976 fillCtxWithImage(ctx, this.ctx.canvas);
6977 ctx.imageSmoothingEnabled = smoothing;
6978 }
6979 clear(color) {
6980 color = color.css || color;
6981 if (!color || typeof color === 'string') {
6982 clearCtx(this.ctx, color);
6983 } else if (typeof color === 'number') {
6984 this.createPixels(() => color);
6985 } else {
6986 throw Error('patchesView.clear(): illegal color ' + color)
6987 }
6988 if (typeof color === 'number') {
6989 this.updateCanvas();
6990 } else {
6991 this.resetImageData();
6992 }
6993 }
6994 getImageBitmap(options = {}) {
6995 return createImageBitmap(this.imageData, options)
6996 }
6997 drawImageBitmap(ctx, options = {}) {
6998 createImageBitmap(this.imageData, options).then(img =>
6999 fillCtxWithImage(ctx, img)
7000 );
7001 }
7002 updateCanvas() {
7003 this.ctx.putImageData(this.imageData, 0, 0);
7004 return this.ctx.canvas
7005 }
7006}
7007
7008class RGBDataSet extends DataSet {
7009 static rgbToInt24(r, g, b) {
7010 return r * 256 * 256 + g * 256 + b
7011 }
7012 static rgbScaleFunction(min, scale) {
7013 return (r, g, b) => min + rgbToInt24(r, g, b) * scale
7014 }
7015 constructor(
7016 img,
7017 rgbToData = RGBDataSet.rgbToInt24,
7018 ArrayType = Float32Array
7019 ) {
7020 const { width, height } = img;
7021 super(width, height, new ArrayType(width * height));
7022 const ctx = imageToCtx(img);
7023 const imgData = ctxImageData(ctx);
7024 const convertedData = this.data;
7025 for (var i = 0; i < convertedData.length; i++) {
7026 const r = imgData.data[4 * i];
7027 const g = imgData.data[4 * i + 1];
7028 const b = imgData.data[4 * i + 2];
7029 convertedData[i] = rgbToData(r, g, b);
7030 }
7031 }
7032}
7033
7034class RGBADataSet extends DataSet {
7035 constructor(img, Type = Float32Array, options = {}) {
7036 const bytes = imageToBytes(img);
7037 const data = new Type(bytes.buffer);
7038 const dataPerPixel = (4 * data.length) / bytes.length;
7039 const width = dataPerPixel * img.width;
7040 const height = img.height;
7041 super(width, height, data);
7042 Object.assign(this, options);
7043 this.src = img.src;
7044 }
7045}
7046let imageToBytesCtx = null;
7047function imageToBytes(img, flipY = false, imgFormat = 'RGBA') {
7048 if (!imageToBytesCtx) {
7049 const can = createCanvas(0, 0);
7050 imageToBytesCtx = can.getContext('webgl', {
7051 premultipliedAlpha: false,
7052 });
7053 }
7054 const { width, height } = img;
7055 const gl = imageToBytesCtx;
7056 Object.assign(gl.canvas, { width, height });
7057 const fmt = gl[imgFormat];
7058 const texture = gl.createTexture();
7059 gl.bindTexture(gl.TEXTURE_2D, texture);
7060 if (flipY) {
7061 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
7062 }
7063 gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
7064 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
7065 gl.texImage2D(gl.TEXTURE_2D, 0, fmt, fmt, gl.UNSIGNED_BYTE, img);
7066 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
7067 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
7068 const framebuffer = gl.createFramebuffer();
7069 gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
7070 gl.framebufferTexture2D(
7071 gl.FRAMEBUFFER,
7072 gl.COLOR_ATTACHMENT0,
7073 gl.TEXTURE_2D,
7074 texture,
7075 0
7076 );
7077 const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
7078 if (status !== gl.FRAMEBUFFER_COMPLETE) {
7079 throw Error(`imageToBytes: status not FRAMEBUFFER_COMPLETE: ${status}`)
7080 }
7081 const pixSize = imgFormat === 'RGB' ? 3 : 4;
7082 const pixels = new Uint8Array(pixSize * width * height);
7083 gl.readPixels(0, 0, width, height, fmt, gl.UNSIGNED_BYTE, pixels);
7084 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
7085 return pixels
7086}
7087
7088var RGBADataSet$1 = /*#__PURE__*/Object.freeze({
7089 __proto__: null,
7090 default: RGBADataSet,
7091 imageToBytes: imageToBytes
7092});
7093
7094class GeoDataSet extends DataSet {
7095 constructor(width, height, bbox, data) {
7096 super(width, height, data);
7097 this.bbox = bbox;
7098 }
7099 static viewFromDataSet(dataSet, bbox) {
7100 return new GeoDataSet(dataSet.width, dataSet.height, bbox, dataSet.data)
7101 }
7102 lat2y(lat) {
7103 const [west, south, east, north] = this.bbox;
7104 const y = Math.round(this.height * (lat - south) / (north - south));
7105 return y
7106 }
7107 lon2x(lng) {
7108 const [west, south, east, north] = this.bbox;
7109 const x = Math.round(this.width * (lng - west) / (east - west));
7110 return x
7111 }
7112 toPixel(geoX, geoY) {
7113 return [this.lon2x(geoX), this.lat2y(geoY)]
7114 }
7115 getGeo(geoX, geoY) {
7116 const [x, y] = this.toPixel(geoX, geoY);
7117 return this.getXY(x, y)
7118 }
7119 setGeo(geoX, geoY, value) {
7120 const [x, y] = this.toPixel(geoX, geoY);
7121 return this.setXY(x, y, value)
7122 }
7123 sampleGeo(geoX, geoY, useNearest = true) {
7124 const [x, y] = this.toPixel(geoX, geoY);
7125 return this.sample(x, y, useNearest)
7126 }
7127 dzdx() {
7128 const [widthMeters, heightMeters] = bboxMetricSize(this.bbox);
7129 const pixelScale = widthMeters / this.width;
7130 const dzdx = super.dzdx(2, (1 / 8) * (1 / pixelScale));
7131 const dzdx2 = GeoDataSet.viewFromDataSet(dzdx, this.bbox);
7132 return dzdx2
7133 }
7134 dzdy() {
7135 const [widthMeters, heightMeters] = bboxMetricSize(this.bbox);
7136 const pixelScale = heightMeters / this.height;
7137 const dzdy = super.dzdy(2, (1 / 8) * (1 / pixelScale));
7138 const dzdy2 = GeoDataSet.viewFromDataSet(dzdy, this.bbox);
7139 return dzdy2
7140 }
7141 slopeAndAspect() {
7142 const dzdx = this.dzdx();
7143 const dzdy = this.dzdy();
7144 const slope = this.slope(dzdx, dzdy);
7145 const aspect = this.aspect(dzdx, dzdy);
7146 return { dzdx, dzdy, slope, aspect }
7147 }
7148 aspect(dzdx = this.dzdx(), dzdy = this.dzdy()) {
7149 const asp = dzdx.map((x, i) => {
7150 const y = dzdy.data[i];
7151 const a = Math.atan2(-y, -x);
7152 return a
7153 });
7154 return asp
7155 }
7156 slope(dzdx = this.dzdx(), dzdy = this.dzdy()) {
7157 const slop = dzdx.map((x, i) => {
7158 const y = dzdy.data[i];
7159 const a = Math.hypot(-x, -y);
7160 const sl = (Math.PI / 2) - Math.atan2(1, a);
7161 return sl
7162 });
7163 return slop
7164 }
7165 clone() {
7166 return new GeoDataSet(this.width, this.height, this.bbox, this.data)
7167 }
7168 resample(width, height, useNearest = true, Type = Array) {
7169 const a = super.resample(width, height, useNearest, Type);
7170 const b = GeoDataSet.viewFromDataSet(a, this.bbox);
7171 return b
7172 }
7173 convolve(kernel, factor = 1, crop = false) {
7174 const a = super.convolve(kernel, factor, crop);
7175 const b = GeoDataSet.viewFromDataSet(a, this.bbox);
7176 return b
7177 }
7178 normalize(lo = 0, hi = 1, round = false) {
7179 const a = super.normalize(lo, hi, round);
7180 const b = GeoDataSet.viewFromDataSet(a, this.bbox);
7181 return b
7182 }
7183 map(f) {
7184 const a = super.map(f);
7185 const b = GeoDataSet.viewFromDataSet(a, this.bbox);
7186 return b
7187 }
7188}
7189
7190function rgbToInt24$1(r, g, b) {
7191 return r * 256 * 256 + g * 256 + b
7192}
7193function rgbScaleFunction(min, scale) {
7194 return (r, g, b) => min + rgbToInt24$1(r, g, b) * scale
7195}
7196function redfishElevation(r, g, b) {
7197 let negative = 1;
7198 if (r > 63) {
7199 negative = -1;
7200 r = 0;
7201 }
7202 return (negative * rgbToInt24$1(r, g, b)) / 10
7203}
7204const sharedTileObject = {
7205 zxyToTile: async function (z, x, y) {
7206 const tileUrl = this.zxyUrl(z, x, y);
7207 const img = await imagePromise(tileUrl);
7208 img.zxy = [z, x, y];
7209 return img
7210 },
7211 zxyToDataSet: async function (z, x, y, ArrayType = Float32Array) {
7212 const img = await this.zxyToTile(z, x, y);
7213 const dataSet = this.tileDataSet(img, ArrayType);
7214 const bbox = xyz2bbox(x, y, z);
7215 const geoDS = GeoDataSet.viewFromDataSet(dataSet, bbox);
7216 geoDS.zxy = [z, x, y];
7217 return geoDS
7218 },
7219 tileDataSet: function (img, ArrayType = Float32Array) {
7220 const tileDecoder = this.elevationFcn;
7221 return new RGBDataSet(img, tileDecoder, ArrayType)
7222 },
7223 tileSize: 256,
7224};
7225const maptiler = Object.assign(
7226 {
7227 elevationFcn: rgbScaleFunction(-10000, 0.1),
7228 zxyUrl: (z, x, y) =>
7229 `https://api.maptiler.com/tiles/terrain-rgb/${z}/${x}/${y}.png?key=iQurAP6lArV1UP4gfSVs`,
7230 zxyTemplate:
7231 'https://api.maptiler.com/tiles/terrain-rgb/{z}/{x}/{y}.png?key=iQurAP6lArV1UP4gfSVs',
7232 minZoom: 0,
7233 maxZoom: 15,
7234 },
7235 sharedTileObject
7236);
7237const mapzen = Object.assign(
7238 {
7239 elevationFcn: rgbScaleFunction(-32768, 1 / 256),
7240 zxyUrl: (z, x, y) =>
7241 `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/${z}/${x}/${y}.png`,
7242 zxyTemplate:
7243 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',
7244 minZoom: 0,
7245 maxZoom: 15,
7246 },
7247 sharedTileObject
7248);
7249const redfishUSA = Object.assign(
7250 {
7251 elevationFcn: redfishElevation,
7252 zxyUrl: (z, x, y) =>
7253 `https://s3-us-west-2.amazonaws.com/simtable-elevation-tiles/${z}/${x}/${y}.png`,
7254 zxyTemplate:
7255 'https://s3-us-west-2.amazonaws.com/simtable-elevation-tiles/{z}/{x}/{y}.png',
7256 minZoom: 10,
7257 maxZoom: 14,
7258 },
7259 sharedTileObject
7260);
7261const redfishWorld = Object.assign(
7262 {
7263 elevationFcn: redfishElevation,
7264 zxyUrl: (z, x, y) =>
7265 `https://s3-us-west-2.amazonaws.com/world-elevation-tiles/DEM_tiles/${z}/${x}/${y}.png`,
7266 zxyTemplate:
7267 'https://s3-us-west-2.amazonaws.com/world-elevation-tiles/DEM_tiles/{z}/{x}/{y}.png',
7268 minZoom: 7,
7269 maxZoom: 13,
7270 },
7271 sharedTileObject
7272);
7273const mapboxToken =
7274 'pk.eyJ1IjoiYmFja3NwYWNlcyIsImEiOiJjanVrbzI4dncwOXl3M3ptcGJtN3oxMmhoIn0.x9iSCrtm0iADEqixVgPwqQ';
7275const mapbox = Object.assign(
7276 {
7277 elevationFcn: rgbScaleFunction(-10000, 0.1),
7278 zxyUrl: (z, x, y) =>
7279 `https://api.mapbox.com/v4/mapbox.terrain-rgb/${z}/${x}/${y}.png?access_token=` +
7280 mapboxToken,
7281 zxyTemplate:
7282 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.png?access_token=' +
7283 mapboxToken,
7284 minZoom: 0,
7285 maxZoom: 18,
7286 },
7287 sharedTileObject
7288);
7289const maplibre = mapzen;
7290
7291var TileData = /*#__PURE__*/Object.freeze({
7292 __proto__: null,
7293 mapbox: mapbox,
7294 maplibre: maplibre,
7295 maptiler: maptiler,
7296 mapzen: mapzen,
7297 redfishUSA: redfishUSA,
7298 redfishWorld: redfishWorld
7299});
7300
7301const ColorMap = {
7302 gradientImageData(nColors, stops, locs) {
7303 const ctx = createCtx(nColors, 1);
7304 if (!locs) locs = floatRamp(0, 1, stops.length);
7305 const grad = ctx.createLinearGradient(0, 0, nColors, 0);
7306 repeat(stops.length, i => grad.addColorStop(locs[i], stops[i]));
7307 ctx.fillStyle = grad;
7308 ctx.fillRect(0, 0, nColors, 1);
7309 return ctxImageColors(ctx)
7310 },
7311 arrayToTypedColors(array) {
7312 return array.map(a => Color.toTypedColor(a))
7313 },
7314 permuteArrays(A1, A2 = A1, A3 = A1) {
7315 const array = [];
7316 for (const a3 of A3) {
7317 for (const a2 of A2) for (const a1 of A1) array.push([a1, a2, a3]);
7318 }
7319 return array
7320 },
7321 permuteRGBColors(numRs, numGs = numRs, numBs = numRs) {
7322 const toRamp = num => integerRamp(0, 255, num);
7323 const ramps = [numRs, numGs, numBs].map(toRamp);
7324 return this.permuteArrays(...ramps)
7325 },
7326 ColorMapProto: {
7327 __proto__: Array.prototype,
7328 createIndex() {
7329 this.index = [];
7330 repeat(this.length, i => {
7331 const px = this[i].getPixel();
7332 this.index[px] = i;
7333 if (this.cssNames) this.index[this.cssNames[i]] = i;
7334 });
7335 },
7336 randomColor() {
7337 return this[randomInt(this.length)]
7338 },
7339 setAlpha(alpha) {
7340 forLoop(this, color => color.setAlpha(alpha));
7341 },
7342 clone() {
7343 return this.cloneColorMap(this)
7344 },
7345 atIndex(index) {
7346 return this[index % this.length]
7347 },
7348 indexOf(color) {
7349 if (this.index) return this.index[color.getPixel()]
7350 for (let i = 0; i < this.length; i++) {
7351 if (color.equals(this[i])) return i
7352 }
7353 return undefined
7354 },
7355 scaleColor(number, min = 0, max = this.length - 1) {
7356 if (min === max) return this[min]
7357 const scale = lerpScale(number, min, max);
7358 const index = Math.round(lerp(0, this.length - 1, scale));
7359 return this[index]
7360 },
7361 toString() {
7362 return `${this.length} ${arraysToString(this)}`
7363 },
7364 rgbClosestIndex(r, g, b) {
7365 let minDist = Infinity;
7366 let ixMin = 0;
7367 for (var i = 0; i < this.length; i++) {
7368 const d = this[i].rgbDistance(r, g, b);
7369 if (d < minDist) {
7370 minDist = d;
7371 ixMin = i;
7372 if (d === 0) return ixMin
7373 }
7374 }
7375 return ixMin
7376 },
7377 rgbClosestColor(r, g, b) {
7378 return this[this.rgbClosestIndex(r, g, b)]
7379 },
7380 cubeClosestIndex(r, g, b) {
7381 const cube = this.cube;
7382 if (!cube) throw Error('cubeClosestIndex: requires the cube arrays')
7383 const rgbSteps = cube.map(c => 255 / (c - 1));
7384 const rgbLocs = [r, g, b].map((c, i) => Math.round(c / rgbSteps[i]));
7385 const [rLoc, gLoc, bLoc] = rgbLocs;
7386 return rLoc + gLoc * cube[0] + bLoc * cube[0] * cube[1]
7387 },
7388 cubeClosestColor(r, g, b) {
7389 return this[this.cubeClosestIndex(r, g, b)]
7390 },
7391 closestIndex(r, g, b) {
7392 return this.cube
7393 ? this.cubeClosestIndex(r, g, b)
7394 : this.rgbClosestIndex(r, g, b)
7395 },
7396 closestColor(r, g, b) {
7397 return this[this.closestIndex(r, g, b)]
7398 },
7399 },
7400 basicColorMap(colors) {
7401 colors = this.arrayToTypedColors(colors);
7402 Object.setPrototypeOf(colors, this.ColorMapProto);
7403 return colors
7404 },
7405 grayColorMap(min = 0, max = 255, size = max - min + 1) {
7406 const ramp = integerRamp(min, max, size);
7407 return this.basicColorMap(ramp.map(i => [i, i, i]))
7408 },
7409 rgbColorCube(numRs, numGs = numRs, numBs = numRs) {
7410 const array = this.permuteRGBColors(numRs, numGs, numBs);
7411 const map = this.basicColorMap(array);
7412 map.cube = [numRs, numGs, numBs];
7413 return map
7414 },
7415 rgbColorMap(R, G, B) {
7416 const array = this.permuteArrays(R, G, B);
7417 return this.basicColorMap(array)
7418 },
7419 hslColorMap(num = 360, S = 100, L = 50) {
7420 const hues = integerRamp(1, 360, num);
7421 const colors = hues.map(h => Color.hslCssColor(h));
7422 const typedColors = colors.map(c => Color.toTypedColor(c));
7423 return this.basicColorMap(typedColors)
7424 },
7425 transparentColorMap(num = 1) {
7426 return this.staticColorMap(0, num)
7427 },
7428 staticColorMap(color, num = 1) {
7429 color = Color.toTypedColor(color);
7430 const array = Array(num).fill(color);
7431 return this.basicColorMap(array)
7432 },
7433 gradientColorMap(nColors, stops, locs) {
7434 stops = stops.map(c => c.css || c);
7435 const uint8arrays = this.gradientImageData(nColors, stops, locs);
7436 const typedColors = this.arrayToTypedColors(uint8arrays);
7437 Object.setPrototypeOf(typedColors, this.ColorMapProto);
7438 return typedColors
7439 },
7440 jetColors: [
7441 'rgb(0, 0, 127)',
7442 'rgb(0, 0, 255)',
7443 'rgb(0, 127, 255)',
7444 'rgb(0, 255, 255)',
7445 'rgb(127, 255, 127)',
7446 'rgb(255, 255, 0)',
7447 'rgb(255, 127, 0)',
7448 'rgb(255, 0, 0)',
7449 'rgb(127, 0, 0)',
7450 ],
7451 basicColorNames:
7452 'white silver gray black red maroon yellow orange olive lime green cyan teal blue navy magenta purple'.split(
7453 ' '
7454 ),
7455 brightColorNames:
7456 'white silver red maroon yellow orange olive lime green cyan teal blue navy magenta purple'.split(
7457 ' '
7458 ),
7459 cssColorMap(cssArray, createNameIndex = false) {
7460 const array = cssArray.map(str => Color.cssToUint8Array(str));
7461 const map = this.basicColorMap(array);
7462 map.cssNames = cssArray;
7463 if (createNameIndex) {
7464 cssArray.forEach((name, ix) => {
7465 map[name] = map[ix];
7466 });
7467 if (map.cyan) map.aqua = map.cyan;
7468 if (map.magenta) map.fuchsia = map.magenta;
7469 }
7470 return map
7471 },
7472 cloneColorMap(colorMap) {
7473 const keys = Object.keys(colorMap);
7474 const clone = this.basicColorMap(colorMap);
7475 forLoop(keys, (val, i) => {
7476 if (clone[i] === undefined) clone[val] = colorMap[val];
7477 });
7478 return clone
7479 },
7480 LazyMap(name, map) {
7481 Object.defineProperty(this, name, { value: map, enumerable: true });
7482 return map
7483 },
7484 get Gray() {
7485 return this.LazyMap('Gray', this.grayColorMap())
7486 },
7487 get Hue() {
7488 return this.LazyMap('Hue', this.hslColorMap())
7489 },
7490 get LightGray() {
7491 return this.LazyMap('LightGray', this.grayColorMap(200))
7492 },
7493 get DarkGray() {
7494 return this.LazyMap('DarkGray', this.grayColorMap(0, 100))
7495 },
7496 get Jet() {
7497 return this.LazyMap('Jet', this.gradientColorMap(256, this.jetColors))
7498 },
7499 get Rgb256() {
7500 return this.LazyMap('Rgb256', this.rgbColorCube(8, 8, 4))
7501 },
7502 get Rgb() {
7503 return this.LazyMap('Rgb', this.rgbColorCube(16))
7504 },
7505 get Transparent() {
7506 return this.LazyMap('Transparent', this.transparentColorMap())
7507 },
7508 get Basic16() {
7509 return this.LazyMap(
7510 'Basic16',
7511 this.cssColorMap(this.basicColorNames, true)
7512 )
7513 },
7514};
7515
7516function cssColor$1(color) {
7517 if (color) return color.css || color
7518 return color
7519}
7520class Shapes {
7521 constructor() {
7522 this.cache = {};
7523 this.paths = paths;
7524 }
7525 addPath(name, pathFunction) {
7526 if (this.getPath(name)) ;
7527 paths[name] = pathFunction;
7528 }
7529 needsStrokeColor(shapeName) {
7530 return shapeName.endsWith('2')
7531 }
7532 getPathNames() {
7533 return Object.keys(paths)
7534 }
7535 getPath(name) {
7536 return paths[name]
7537 }
7538 oneOf() {
7539 return oneValOf(paths)
7540 }
7541 nameAtIndex(index) {
7542 const names = this.getPathNames();
7543 return names[mod(index, names.length)]
7544 }
7545 atIndex(index) {
7546 const name = this.nameAtIndex(index);
7547 return paths[name]
7548 }
7549 imagePathPromise(name, imgURL) {
7550 return imagePromise(imgURL).then(img => {
7551 this.createImagePath(name, img);
7552 })
7553 }
7554 createImagePath(name, img, flip = true) {
7555 if (!isImageable(img)) {
7556 throw Error('Shapes createImagePath: img not an imageable ' + img)
7557 }
7558 if (flip) img = flipImage(img);
7559 function imagePath(ctx) {
7560 ctx.drawImage(img, -0.5, -0.5, 1, 1);
7561 }
7562 this.addPath(name, imagePath);
7563 }
7564 imageName(name, pixels, fill, stroke) {
7565 const path = this.getPath(name);
7566 if (!Number.isInteger(pixels))
7567 throw Error(`imageName: pixels is not integer: ${name}`)
7568 if (!path) throw Error(`imageName: ${name} not in Shapes`)
7569 if (path.name === 'imagePath') return `${name}_${pixels}_image`
7570 if (!fill) throw Error(`imageName: No color for shape ${name}`)
7571 if (!this.needsStrokeColor(name)) stroke = null;
7572 return `${name}_${pixels}_${fill}${stroke ? `_${stroke}` : ''}`
7573 }
7574 shapeToImage(name, pixels, fill, stroke) {
7575 pixels = Math.ceil(pixels);
7576 const imgName = this.imageName(name, pixels, fill, stroke);
7577 if (this.cache && this.cache[imgName]) return this.cache[imgName]
7578 const ctx = createCtx(pixels, pixels);
7579 ctx.fillStyle = cssColor$1(fill);
7580 ctx.strokeStyle = cssColor$1(stroke);
7581 ctx.scale(pixels, -pixels);
7582 ctx.translate(0.5, -0.5);
7583 ctx.beginPath();
7584 paths[name](ctx);
7585 ctx.closePath();
7586 ctx.fill();
7587 ctx.canvas.name = imgName;
7588 if (this.cache) this.cache[imgName] = ctx.canvas;
7589 return ctx.canvas
7590 }
7591 imageNameToImage(imageName) {
7592 const [name, pixels, fill, stroke] = imageName.split('_');
7593 return this.shapeToImage(name, pixels, fill, stroke)
7594 }
7595}
7596function flipImage(img) {
7597 const { width, height } = img;
7598 const ctx = createCtx(width, height);
7599 ctx.scale(1, -1);
7600 ctx.drawImage(img, 0, -height);
7601 return ctx.canvas
7602}
7603function poly(ctx, points) {
7604 points.forEach((pt, i) => {
7605 if (i === 0) ctx.moveTo(pt[0], pt[1]);
7606 else ctx.lineTo(pt[0], pt[1]);
7607 });
7608}
7609function circle(ctx, x, y, radius, anticlockwise = false) {
7610 ctx.arc(x, y, radius, 0, 2 * Math.PI, anticlockwise);
7611}
7612function square(ctx, x, y, size) {
7613 ctx.fillRect(x - size / 2, y - size / 2, size, size);
7614}
7615const paths = {
7616 arrow(ctx) {
7617 poly(ctx, [
7618 [0.5, 0],
7619 [0, 0.5],
7620 [0, 0.2],
7621 [-0.5, 0.2],
7622 [-0.5, -0.2],
7623 [0, -0.2],
7624 [0, -0.5],
7625 ]);
7626 },
7627 bug(ctx) {
7628 ctx.strokeStyle = ctx.fillStyle;
7629 this.bug2(ctx);
7630 },
7631 bug2(ctx) {
7632 ctx.lineWidth = 0.05;
7633 poly(ctx, [
7634 [0.4, 0.225],
7635 [0.2, 0],
7636 [0.4, -0.225],
7637 ]);
7638 ctx.stroke();
7639 ctx.beginPath();
7640 circle(ctx, 0.12, 0, 0.13);
7641 circle(ctx, -0.05, 0, 0.13);
7642 circle(ctx, -0.27, 0, 0.2);
7643 },
7644 circle(ctx) {
7645 circle(ctx, 0, 0, 0.5);
7646 },
7647 dart(ctx) {
7648 poly(ctx, [
7649 [0.5, 0],
7650 [-0.5, 0.4],
7651 [-0.25, 0],
7652 [-0.5, -0.4],
7653 ]);
7654 },
7655 frame(ctx) {
7656 const inset = 0.2;
7657 const r = 0.5 - inset;
7658 poly(ctx, [
7659 [-0.5, -0.5],
7660 [0.5, -0.5],
7661 [0.5, 0.5],
7662 [-0.5, 0.5],
7663 ]);
7664 ctx.closePath();
7665 poly(ctx, [
7666 [-r, -r],
7667 [-r, r],
7668 [r, r],
7669 [r, -r],
7670 ]);
7671 },
7672 frame2(ctx) {
7673 const inset = 0.2;
7674 square(ctx, 0, 0, 1);
7675 ctx.fillStyle = ctx.strokeStyle;
7676 square(ctx, 0, 0, 1 - 2 * inset);
7677 },
7678 person(ctx) {
7679 ctx.strokeStyle = ctx.fillStyle;
7680 this.person2(ctx);
7681 },
7682 person2(ctx) {
7683 poly(ctx, [
7684 [0.15, 0.2],
7685 [0.3, 0],
7686 [0.125, -0.1],
7687 [0.125, 0.05],
7688 [0.1, -0.15],
7689 [0.25, -0.5],
7690 [0.05, -0.5],
7691 [0, -0.25],
7692 [-0.05, -0.5],
7693 [-0.25, -0.5],
7694 [-0.1, -0.15],
7695 [-0.125, 0.05],
7696 [-0.125, -0.1],
7697 [-0.3, 0],
7698 [-0.15, 0.2],
7699 ]);
7700 ctx.closePath();
7701 ctx.fill();
7702 ctx.beginPath();
7703 ctx.fillStyle = ctx.strokeStyle;
7704 circle(ctx, 0, 0.35, 0.15);
7705 },
7706 ring(ctx) {
7707 const [rOuter, rInner] = [0.5, 0.3];
7708 circle(ctx, 0, 0, rOuter);
7709 ctx.lineTo(rInner, 0);
7710 circle(ctx, 0, 0, rInner, true);
7711 },
7712 ring2(ctx) {
7713 const [rOuter, rInner] = [0.5, 0.3];
7714 circle(ctx, 0, 0, rOuter);
7715 ctx.closePath();
7716 ctx.fill();
7717 ctx.beginPath();
7718 ctx.fillStyle = ctx.strokeStyle;
7719 circle(ctx, 0, 0, rInner);
7720 },
7721 square(ctx) {
7722 square(ctx, 0, 0, 1);
7723 },
7724 triangle(ctx) {
7725 poly(ctx, [
7726 [0.5, 0],
7727 [-0.5, -0.4],
7728 [-0.5, 0.4],
7729 ]);
7730 },
7731};
7732
7733class SpriteSheet {
7734 constructor(spriteSize = 64, cols = 16, usePowerOf2 = false) {
7735 spriteSize = Math.ceil(spriteSize);
7736 Object.assign(this, { spriteSize, cols, usePowerOf2 });
7737 this.rows = 1;
7738 this.nextCol = 0;
7739 this.nextRow = 0;
7740 this.spritesIndex = {};
7741 this.sprites = [];
7742 this.shapes = new Shapes();
7743 if (usePowerOf2) this.checkPowerOf2();
7744 this.ctx = createCtx(this.width, this.height);
7745 this.texture = null;
7746 }
7747 getSprite(shapeName, color, strokeColor) {
7748 return this.newSprite(shapeName, color, strokeColor)
7749 }
7750 oneOf() {
7751 return oneOf(this.sprites)
7752 }
7753 draw(ctx, sprite, x, y, theta, world, patchSize, noRotate = false) {
7754 const [x0, y0] = world.patchXYtoPixelXY(x, y, patchSize);
7755 const theta0 = -theta;
7756 this.drawCanvas(ctx, sprite, x0, y0, theta0, noRotate);
7757 }
7758 drawCanvas(ctx, sprite, x, y, theta = 0, noRotate = false) {
7759 const { x: x0, y: y0, size } = sprite;
7760 const halfSize = size / 2;
7761 if (noRotate) theta = 0;
7762 if (theta === 0) {
7763 ctx.drawImage(
7764 this.ctx.canvas,
7765 x0,
7766 y0,
7767 size,
7768 size,
7769 x - halfSize,
7770 y - halfSize,
7771 size,
7772 size
7773 );
7774 } else {
7775 ctx.save();
7776 ctx.translate(x, y);
7777 ctx.rotate(theta);
7778 ctx.drawImage(
7779 this.ctx.canvas,
7780 x0,
7781 y0,
7782 size,
7783 size,
7784 -halfSize,
7785 -halfSize,
7786 size,
7787 size
7788 );
7789 ctx.restore();
7790 }
7791 }
7792 newSprite(shapeName, color, strokeColor = null) {
7793 const name = this.shapes.imageName(
7794 shapeName,
7795 this.spriteSize,
7796 color,
7797 strokeColor
7798 );
7799 if (this.spritesIndex[name]) return this.spritesIndex[name]
7800 const img = this.shapes.shapeToImage(
7801 shapeName,
7802 this.spriteSize,
7803 color,
7804 strokeColor
7805 );
7806 this.checkSheetSize();
7807 const [x, y, size] = [this.nextX, this.nextY, this.spriteSize];
7808 this.ctx.drawImage(img, x, y, size, size);
7809 const { nextRow: row, nextCol: col } = this;
7810 const sprite = {
7811 name,
7812 id: this.sprites.length,
7813 x,
7814 y,
7815 row,
7816 col,
7817 size,
7818 sheet: this,
7819 };
7820 sprite.uvs = this.getUVs(sprite);
7821 this.incrementRowCol();
7822 this.spritesIndex[name] = sprite;
7823 this.sprites.push(sprite);
7824 if (this.texture) this.texture.needsUpdate = true;
7825 return sprite
7826 }
7827 get width() {
7828 return this.spriteSize * this.cols
7829 }
7830 get height() {
7831 return this.spriteSize * this.rows
7832 }
7833 get nextX() {
7834 return this.spriteSize * this.nextCol
7835 }
7836 get nextY() {
7837 return this.spriteSize * this.nextRow
7838 }
7839 checkPowerOf2() {
7840 const { width, height } = this;
7841 if (!(isPowerOf2(width) && isPowerOf2(height))) {
7842 throw Error(`SpriteSheet non power of 2: ${width}x${height}`)
7843 }
7844 }
7845 checkSheetSize() {
7846 if (this.nextRow === this.rows) {
7847 this.rows = this.usePowerOf2 ? this.rows * 2 : this.rows + 1;
7848 resizeCtx(this.ctx, this.width, this.height);
7849 forLoop(this.sprites, sprite => {
7850 sprite.uvs = this.getUVs(sprite);
7851 });
7852 }
7853 }
7854 incrementRowCol() {
7855 this.nextCol += 1;
7856 if (this.nextCol < this.cols) return
7857 this.nextCol = 0;
7858 this.nextRow += 1;
7859 }
7860 getUVs(sprite) {
7861 const { row, col } = sprite;
7862 const { rows, cols } = this;
7863 const u0 = col / cols;
7864 const v0 = (rows - (row + 1)) / rows;
7865 const u1 = (col + 1) / cols;
7866 const v1 = (rows - row) / rows;
7867 return [u0, v0, u1, v0, u1, v1, u0, v1]
7868 }
7869}
7870
7871function cssColor(color) {
7872 if (color) return color.css || color
7873 return color
7874}
7875class TurtlesView {
7876 static defaultOptions() {
7877 return {
7878 useSprites: false,
7879 patchSize: 10,
7880 }
7881 }
7882 constructor(ctx, world, options = {}) {
7883 options = Object.assign(TurtlesView.defaultOptions(), options);
7884 Object.assign(this, { ctx, world }, options);
7885 this.shapes = new Shapes();
7886 this.reset(this.patchSize, this.useSprites);
7887 }
7888 reset(patchSize, useSprites = this.useSprites) {
7889 this.useSprites = useSprites;
7890 this.resetCtx(patchSize);
7891 }
7892 getImageBitmap() {
7893 return createImageBitmap(this.ctx.canvas)
7894 }
7895 resetCtx(patchSize) {
7896 this.patchSize = patchSize;
7897 if (this.useSprites) {
7898 this.world.setCanvasSize(this.ctx.canvas, patchSize);
7899 this.ctx.restore();
7900 } else {
7901 this.world.setEuclideanTransform(this.ctx, patchSize);
7902 }
7903 }
7904 drawTurtles(data, viewFcn) {
7905 if (isOofA(data)) data = toAofO(data);
7906 const constantView = isObject$1(viewFcn);
7907 forLoop(data, (turtle, i) => {
7908 const viewData = constantView ? viewFcn : viewFcn(turtle, i, data);
7909 this.drawTurtle(turtle, viewData);
7910 });
7911 }
7912 drawTurtle(turtle, viewData) {
7913 if (turtle.hidden) return
7914 if (viewData.size === 0) return
7915 if (this.useSprites) {
7916 let { sprite, noRotate } = viewData;
7917 if (!sprite) {
7918 const { shape, color, strokeColor, size } = viewData;
7919 const pixels = size * this.patchSize;
7920 sprite = this.shapes.shapeToImage(
7921 shape,
7922 pixels,
7923 color,
7924 strokeColor
7925 );
7926 }
7927 if (sprite.sheet) {
7928 sprite.sheet.draw(
7929 this.ctx,
7930 sprite,
7931 turtle.x,
7932 turtle.y,
7933 turtle.theta,
7934 this.world,
7935 this.patchSize,
7936 noRotate
7937 );
7938 } else {
7939 this.drawImage(
7940 sprite,
7941 turtle.x,
7942 turtle.y,
7943 noRotate ? 0 : turtle.theta
7944 );
7945 }
7946 } else {
7947 const { shape, color, strokeColor, size, noRotate } = viewData;
7948 this.drawShape(
7949 shape,
7950 turtle.x,
7951 turtle.y,
7952 noRotate ? 0 : turtle.theta,
7953 size,
7954 color,
7955 strokeColor
7956 );
7957 }
7958 }
7959 drawShape(name, x, y, theta = 0, size = 1, fill, stroke) {
7960 const ctx = this.ctx;
7961 ctx.save();
7962 ctx.fillStyle = cssColor(fill);
7963 ctx.strokeStyle = cssColor(stroke);
7964 ctx.translate(x, y);
7965 ctx.scale(size, size);
7966 if (theta !== 0) ctx.rotate(theta);
7967 ctx.beginPath();
7968 this.shapes.paths[name](ctx);
7969 ctx.closePath();
7970 ctx.fill();
7971 ctx.restore();
7972 }
7973 drawImage(img, x, y, theta = 0) {
7974 const halfPix = img.width / 2;
7975 const patchSize = this.patchSize;
7976 const [x0, y0] = this.world.patchXYtoPixelXY(x, y, patchSize);
7977 const ctx = this.ctx;
7978 if (theta === 0) {
7979 ctx.drawImage(img, x0 - halfPix, y0 - halfPix);
7980 } else {
7981 ctx.save();
7982 ctx.translate(x0, y0);
7983 ctx.rotate(-theta);
7984 ctx.drawImage(img, -halfPix, -halfPix);
7985 ctx.restore();
7986 }
7987 }
7988 drawLinks(data, viewFcn) {
7989 if (isOofA(data)) data = toAofO(data);
7990 const uniformLinks = isObject$1(viewFcn);
7991 const ctx = this.ctx;
7992 setIdentity(this.ctx);
7993 if (uniformLinks) {
7994 ctx.strokeStyle = cssColor(viewFcn.color);
7995 ctx.lineWidth = viewFcn.width || 1;
7996 ctx.beginPath();
7997 }
7998 forLoop(data, (link, i) => {
7999 if (uniformLinks) {
8000 this.drawLink(link);
8001 } else {
8002 const { color, width } = viewFcn(link, i, data);
8003 this.drawLink(link, color, width);
8004 }
8005 });
8006 if (uniformLinks) {
8007 ctx.closePath();
8008 ctx.stroke();
8009 }
8010 this.ctx.restore();
8011 }
8012 drawLink(link, color, width = 1) {
8013 this.drawLine(link.x0, link.y0, link.x1, link.y1, color, width);
8014 }
8015 drawLine(x0, y0, x1, y1, stroke, width = 1) {
8016 const ctx = this.ctx
8017 ;[x0, y0] = this.world.patchXYtoPixelXY(x0, y0, this.patchSize)
8018 ;[x1, y1] = this.world.patchXYtoPixelXY(x1, y1, this.patchSize);
8019 if (stroke) {
8020 ctx.strokeStyle = cssColor(stroke);
8021 ctx.lineWidth = width;
8022 ctx.beginPath();
8023 }
8024 ctx.moveTo(x0, y0);
8025 ctx.lineTo(x1, y1);
8026 if (stroke) {
8027 ctx.closePath();
8028 ctx.stroke();
8029 }
8030 }
8031}
8032
8033class TwoView {
8034 static defaultOptions() {
8035 return {
8036 div: document.body,
8037 useSprites: false,
8038 patchSize: 10,
8039 }
8040 }
8041 constructor(world, options = {}) {
8042 if (world.world) world = world.world;
8043 options = Object.assign(TwoView.defaultOptions(), options);
8044 if (options.width) {
8045 options.patchSize = options.width / world.width;
8046 delete options.width;
8047 }
8048 let div = options.div;
8049 let can = div;
8050 div = isString(div) ? document.getElementById(div) : div;
8051 if (!isCanvas(can)) {
8052 can = createCanvas(0, 0, true);
8053 div.appendChild(can);
8054 }
8055 this.ctx = can.getContext('2d');
8056 this.world = world;
8057 this.patchesView = new PatchesView(this.world.width, this.world.height);
8058 this.turtlesView = new TurtlesView(this.ctx, this.world, options);
8059 this.ticks = 0;
8060 this.clear();
8061 }
8062 tick() {
8063 this.ticks++;
8064 }
8065 get canvas() {
8066 return this.ctx.canvas
8067 }
8068 reset(patchSize, useSprites = this.useSprites) {
8069 this.turtlesView.reset(patchSize, useSprites);
8070 }
8071 downloadCanvas(name = undefined) {
8072 if (!name)
8073 name = this.model.constructor.name
8074 .toLowerCase()
8075 .replace(/model$/, '');
8076 downloadCanvas(this.canvas, name);
8077 }
8078 get width() {
8079 return this.world.width * this.patchSize
8080 }
8081 set width(val) {
8082 this.reset(val / this.world.width);
8083 }
8084 get patchSize() {
8085 return this.turtlesView.patchSize
8086 }
8087 set patchSize(val) {
8088 this.reset(val);
8089 }
8090 get useSprites() {
8091 return this.turtlesView.useSprites
8092 }
8093 set useSprites(val) {
8094 this.reset(this.patchSize, val);
8095 }
8096 clear(cssColor) {
8097 clearCtx(this.ctx, cssColor);
8098 }
8099 createPatchPixels(pixelFcn) {
8100 this.patchesView.createPixels(pixelFcn);
8101 }
8102 setPatchPixel(index, pixel) {
8103 this.patchesView.setPixel(index, pixel);
8104 }
8105 setPatchesPixels(data, pixelFcn) {
8106 this.patchesView.setPixels(data, pixelFcn);
8107 }
8108 setPatchesSmoothing(smoothing) {
8109 this.patchesView.setPatchesSmoothing(smoothing);
8110 }
8111 drawPatchesImage(img) {
8112 fillCtxWithImage(this.ctx, img);
8113 }
8114 drawPatches(data, pixelFcn) {
8115 if (data != null) {
8116 this.patchesView.setPixels(data, pixelFcn);
8117 }
8118 this.patchesView.draw(this.ctx);
8119 }
8120 drawTurtles(data, viewFcn) {
8121 this.turtlesView.drawTurtles(data, viewFcn);
8122 }
8123 drawLinks(data, viewFcn) {
8124 this.turtlesView.drawLinks(data, viewFcn);
8125 }
8126 setTextProperties(font, textAlign = 'center', textBaseline = 'middle') {
8127 if (typeof font === 'number')
8128 font = `${this.patchSize * font}px sans-serif`;
8129 setTextProperties(this.ctx, font, textAlign, textBaseline);
8130 }
8131 drawText(string, x, y, color = 'black') {
8132[x, y] = this.world.patchXYtoPixelXY(x, y, this.patchSize);
8133 string = '' + string;
8134 drawText(this.ctx, string, x, y, color);
8135 }
8136}
8137
8138class TwoDraw extends TwoView {
8139 static defaultOptions(model) {
8140 return {
8141 patchesColor: 'random',
8142 initPatches: null,
8143 turtles: model.turtles,
8144 turtlesColor: 'random',
8145 turtlesStrokeColor: 'random',
8146 turtlesShape: 'dart',
8147 turtlesSize: 1,
8148 turtlesRotate: true,
8149 links: model.links,
8150 linksColor: 'random',
8151 linksWidth: 1,
8152 textProperty: null,
8153 textSize: 0.5,
8154 textColor: 'black',
8155 patchesMap: 'DarkGray',
8156 turtlesMap: 'Basic16',
8157 }
8158 }
8159 static separateDrawOptions(viewOptions, drawOptions) {
8160 if (viewOptions.drawOptions) {
8161 Object.assign(drawOptions, viewOptions.drawOptions);
8162 delete viewOptions.drawOptions;
8163 }
8164 return drawOptions
8165 }
8166 constructor(model, viewOptions = {}, drawOptions = {}) {
8167 drawOptions = TwoDraw.separateDrawOptions(viewOptions, drawOptions);
8168 drawOptions = Object.assign(
8169 TwoDraw.defaultOptions(model),
8170 drawOptions
8171 );
8172 super(model, viewOptions);
8173 this.model = model;
8174 this.checkOptions(drawOptions);
8175 this.drawOptions = drawOptions;
8176 }
8177 checkOptions(drawOptions) {
8178 const keys = Object.keys(drawOptions);
8179 const defaults = TwoDraw.defaultOptions(this.model);
8180 keys.forEach(k => {
8181 if (defaults[k] === undefined) {
8182 console.log(
8183 'Legal TwoDraw parameters',
8184 Object.keys(TwoDraw.defaultOptions(this.model))
8185 );
8186 throw Error('Unknown TwoDraw parameter: ' + k)
8187 }
8188 });
8189 if (typeof drawOptions.patchesMap === 'string') {
8190 drawOptions.patchesMap = ColorMap[drawOptions.patchesMap];
8191 if (!drawOptions.patchesMap)
8192 Error('Unknown patchMap: ' + drawOptions.patchesMap);
8193 }
8194 if (typeof drawOptions.turtlesMap === 'string') {
8195 drawOptions.turtlesMap = ColorMap[drawOptions.turtlesMap];
8196 if (!drawOptions.turtlesMap)
8197 Error('Unknown turtlesMap: ' + drawOptions.turtlesMap);
8198 }
8199 }
8200 resetOptions(drawOptions = this.drawOptions) {
8201 this.checkOptions(drawOptions);
8202 this.drawOptions = drawOptions;
8203 this.ticks = 0;
8204 return drawOptions
8205 }
8206 draw() {
8207 const model = this.model;
8208 const view = this;
8209 let {
8210 patchesColor,
8211 initPatches,
8212 turtles,
8213 turtlesColor,
8214 turtlesStrokeColor,
8215 turtlesShape,
8216 turtlesSize,
8217 turtlesRotate,
8218 links,
8219 linksColor,
8220 linksWidth,
8221 textProperty,
8222 textSize,
8223 textColor,
8224 patchesMap,
8225 turtlesMap,
8226 } = this.drawOptions;
8227 if (view.ticks === 0) {
8228 if (textProperty) view.setTextProperties(textSize);
8229 if (initPatches) {
8230 const colors = initPatches(model, view);
8231 view.createPatchPixels(i => colors[i]);
8232 } else if (patchesColor === 'random') {
8233 view.createPatchPixels(i => patchesMap.randomColor());
8234 }
8235 }
8236 if (isImageable(patchesColor)) {
8237 view.drawPatchesImage(patchesColor);
8238 } else {
8239 if (patchesColor === 'random' || initPatches) {
8240 view.clear();
8241 view.drawPatches();
8242 } else if (isFunction(patchesColor)) {
8243 view.clear();
8244 view.drawPatches(model.patches, p => patchesColor(p));
8245 } else {
8246 view.clear(patchesColor);
8247 }
8248 }
8249 const checkColor = (agent, color) =>
8250 color === 'random' ? turtlesMap.atIndex(agent.id).css : color;
8251 view.drawLinks(links, l => ({
8252 color:
8253 linksColor === 'random'
8254 ? turtlesMap.atIndex(l.id)
8255 : typeof linksColor === 'function'
8256 ? checkColor(l, linksColor(l))
8257 : linksColor,
8258 width:
8259 typeof linksWidth === 'function' ? linksWidth(l) : linksWidth,
8260 }));
8261 view.drawTurtles(turtles, t => ({
8262 shape:
8263 typeof turtlesShape === 'function'
8264 ? turtlesShape(t)
8265 : turtlesShape,
8266 color:
8267 turtlesColor === 'random'
8268 ? turtlesMap.atIndex(t.id).css
8269 : typeof turtlesColor === 'function'
8270 ? checkColor(t, turtlesColor(t))
8271 : turtlesColor,
8272 strokeColor:
8273 turtlesStrokeColor === 'random'
8274 ? turtlesMap.atIndex(t.id + 4).css
8275 : typeof turtlesColor === 'function'
8276 ? checkColor(t, turtlesColor(t))
8277 : turtlesColor,
8278 size:
8279 typeof turtlesSize === 'function'
8280 ? turtlesSize(t)
8281 : turtlesSize,
8282 noRotate:
8283 typeof turtlesRotate === 'function'
8284 ? !turtlesRotate(t)
8285 : !turtlesRotate,
8286 }));
8287 if (textProperty) {
8288 turtles.ask(t => {
8289 if (t[textProperty] != null)
8290 view.drawText(t[textProperty], t.x, t.y, textColor);
8291 });
8292 }
8293 view.tick();
8294 }
8295}
8296
8297var Stats = function () {
8298 var mode = 0;
8299 var container = document.createElement( 'div' );
8300 container.style.cssText = 'position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000';
8301 container.addEventListener( 'click', function ( event ) {
8302 event.preventDefault();
8303 showPanel( ++ mode % container.children.length );
8304 }, false );
8305 function addPanel( panel ) {
8306 container.appendChild( panel.dom );
8307 return panel;
8308 }
8309 function showPanel( id ) {
8310 for ( var i = 0; i < container.children.length; i ++ ) {
8311 container.children[ i ].style.display = i === id ? 'block' : 'none';
8312 }
8313 mode = id;
8314 }
8315 var beginTime = ( performance || Date ).now(), prevTime = beginTime, frames = 0;
8316 var fpsPanel = addPanel( new Stats.Panel( 'FPS', '#0ff', '#002' ) );
8317 var msPanel = addPanel( new Stats.Panel( 'MS', '#0f0', '#020' ) );
8318 if ( self.performance && self.performance.memory ) {
8319 var memPanel = addPanel( new Stats.Panel( 'MB', '#f08', '#201' ) );
8320 }
8321 showPanel( 0 );
8322 return {
8323 REVISION: 16,
8324 dom: container,
8325 addPanel: addPanel,
8326 showPanel: showPanel,
8327 begin: function () {
8328 beginTime = ( performance || Date ).now();
8329 },
8330 end: function () {
8331 frames ++;
8332 var time = ( performance || Date ).now();
8333 msPanel.update( time - beginTime, 200 );
8334 if ( time >= prevTime + 1000 ) {
8335 fpsPanel.update( ( frames * 1000 ) / ( time - prevTime ), 100 );
8336 prevTime = time;
8337 frames = 0;
8338 if ( memPanel ) {
8339 var memory = performance.memory;
8340 memPanel.update( memory.usedJSHeapSize / 1048576, memory.jsHeapSizeLimit / 1048576 );
8341 }
8342 }
8343 return time;
8344 },
8345 update: function () {
8346 beginTime = this.end();
8347 },
8348 domElement: container,
8349 setMode: showPanel
8350 };
8351};
8352Stats.Panel = function ( name, fg, bg ) {
8353 var min = Infinity, max = 0, round = Math.round;
8354 var PR = round( window.devicePixelRatio || 1 );
8355 var WIDTH = 80 * PR, HEIGHT = 48 * PR,
8356 TEXT_X = 3 * PR, TEXT_Y = 2 * PR,
8357 GRAPH_X = 3 * PR, GRAPH_Y = 15 * PR,
8358 GRAPH_WIDTH = 74 * PR, GRAPH_HEIGHT = 30 * PR;
8359 var canvas = document.createElement( 'canvas' );
8360 canvas.width = WIDTH;
8361 canvas.height = HEIGHT;
8362 canvas.style.cssText = 'width:80px;height:48px';
8363 var context = canvas.getContext( '2d' );
8364 context.font = 'bold ' + ( 9 * PR ) + 'px Helvetica,Arial,sans-serif';
8365 context.textBaseline = 'top';
8366 context.fillStyle = bg;
8367 context.fillRect( 0, 0, WIDTH, HEIGHT );
8368 context.fillStyle = fg;
8369 context.fillText( name, TEXT_X, TEXT_Y );
8370 context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
8371 context.fillStyle = bg;
8372 context.globalAlpha = 0.9;
8373 context.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT );
8374 return {
8375 dom: canvas,
8376 update: function ( value, maxValue ) {
8377 min = Math.min( min, value );
8378 max = Math.max( max, value );
8379 context.fillStyle = bg;
8380 context.globalAlpha = 1;
8381 context.fillRect( 0, 0, WIDTH, GRAPH_Y );
8382 context.fillStyle = fg;
8383 context.fillText( round( value ) + ' ' + name + ' (' + round( min ) + '-' + round( max ) + ')', TEXT_X, TEXT_Y );
8384 context.drawImage( canvas, GRAPH_X + PR, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT, GRAPH_X, GRAPH_Y, GRAPH_WIDTH - PR, GRAPH_HEIGHT );
8385 context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, GRAPH_HEIGHT );
8386 context.fillStyle = bg;
8387 context.globalAlpha = 0.9;
8388 context.fillRect( GRAPH_X + GRAPH_WIDTH - PR, GRAPH_Y, PR, round( ( 1 - ( value / maxValue ) ) * GRAPH_HEIGHT ) );
8389 }
8390 };
8391};
8392
8393class Animator {
8394 constructor(fcn, steps = -1, fps = 30) {
8395 Object.assign(this, { fcn, steps, fps, ticks: 0 });
8396 this.stats = null;
8397 this.timeoutID = null;
8398 this.idle = null;
8399 this.idleFps = null;
8400 this.idleID = null;
8401 this.start();
8402 }
8403 start() {
8404 this.clearIDs();
8405 this.timeoutID = setInterval(() => this.step(), 1000 / this.fps);
8406 return this
8407 }
8408 stop() {
8409 this.clearIDs();
8410 if (this.idle)
8411 this.idleID = setInterval(() => this.idle(), 1000 / this.idleFps);
8412 return this
8413 }
8414 step() {
8415 if (this.ticks === this.steps) return this.stop()
8416 this.ticks++;
8417 this.fcn();
8418 if (this.stats) this.stats.update();
8419 return this
8420 }
8421 clearIDs() {
8422 if (this.timeoutID) this.timeoutID = clearInterval(this.timeoutID);
8423 if (this.idleID) this.idleID = clearInterval(this.idleID);
8424 }
8425 isRunning() {
8426 return this.timeoutID != null
8427 }
8428 startStats(left = '0px') {
8429 if (this.stats) return console.log('startStats: already running')
8430 this.stats = new Stats();
8431 document.body.appendChild(this.stats.dom);
8432 this.stats.dom.style.left = left;
8433 return this
8434 }
8435 setFps(fps) {
8436 this.reset(this.steps, fps);
8437 }
8438 setSteps(steps) {
8439 this.reset(steps, this.fps);
8440 }
8441 reset(steps = this.steps, fps = this.fps) {
8442 const wasRunning = this.isRunning();
8443 if (wasRunning) this.stop();
8444 this.steps = steps;
8445 this.ticks = 0;
8446 this.fps = fps;
8447 if (wasRunning) this.start();
8448 }
8449 toggle() {
8450 if (this.isRunning()) this.stop();
8451 else this.start();
8452 }
8453 once() {
8454 this.stop();
8455 this.step();
8456 }
8457 setIdle(fcn, fps = 4) {
8458 this.idle = fcn;
8459 this.idleFps = fps;
8460 }
8461}
8462
8463class Evented {
8464 events = {}
8465 on(name, callback) {
8466 if (!this.events[name]) {
8467 this.events[name] = [];
8468 }
8469 this.events[name].push(callback);
8470 return callback
8471 }
8472 off(name, callback = null) {
8473 if (this.events[name]) {
8474 if (callback) {
8475 this.events[name] = this.events[name].filter(
8476 cb => cb !== callback
8477 );
8478 }
8479 if (this.events[name].length === 0 || !callback) {
8480 delete this.events[name];
8481 }
8482 }
8483 }
8484 emit(name, ...args) {
8485 if (this.events[name]) {
8486 this.events[name].forEach(callback => callback(...args));
8487 }
8488 }
8489}
8490
8491class Mouse {
8492 constructor(model, view, callback) {
8493 Object.assign(this, { model, view, callback });
8494 this.canvas = view.canvas;
8495 this.world = model.world;
8496 this.callMouseHandler = e => this.mouseHandler(e);
8497 this.isRunning = this.mouseDown = false;
8498 this.x = this.y = this.action = null;
8499 this.setContinuous(false);
8500 }
8501 setContinuous(continuous = true) {
8502 this.continuous = continuous;
8503 return this
8504 }
8505 start() {
8506 this.canvas.addEventListener('mousedown', this.callMouseHandler);
8507 if (this.continuous) this.startMouse();
8508 this.isRunning = true;
8509 return this
8510 }
8511 stop() {
8512 this.canvas.removeEventListener('mousedown', this.callMouseHandler);
8513 this.stopMouse();
8514 this.isRunning = false;
8515 return this
8516 }
8517 startMouse() {
8518 document.body.addEventListener('mouseup', this.callMouseHandler);
8519 this.canvas.addEventListener('mousemove', this.callMouseHandler);
8520 }
8521 stopMouse() {
8522 document.body.removeEventListener('mouseup', this.callMouseHandler);
8523 this.canvas.removeEventListener('mousemove', this.callMouseHandler);
8524 }
8525 mouseHandler(e) {
8526 if (e.type === 'mousedown') {
8527 if (!this.continuous) this.startMouse();
8528 this.mouseDown = true;
8529 }
8530 if (e.type === 'mouseup') {
8531 if (!this.continuous) this.stopMouse();
8532 this.mouseDown = false;
8533 }
8534 this.action = e.type;
8535 if (e.type === 'mousemove' && this.mouseDown) {
8536 this.action = 'mousedrag';
8537 }
8538 this.setXY(e);
8539 this.callback(this);
8540 }
8541 setXY(e) {
8542 const { canvas, world } = this;
8543 const patchSize = world.patchSize(canvas);
8544 const rect = this.canvas.getBoundingClientRect();
8545 const pixX = e.clientX - rect.left;
8546 const pixY = e.clientY - rect.top;
8547 const [x, y] = world.pixelXYtoPatchXY(pixX, pixY, patchSize);
8548 Object.assign(this, { x, y });
8549 }
8550}
8551
8552function isGeojson(obj) {
8553 return typeof obj === 'object' && obj.type === 'FeatureCollection'
8554}
8555function clone(json) {
8556 return JSON.parse(JSON.stringify(json))
8557}
8558function minify(json) {
8559 if (typeof json === 'string') json = JSON.parse(json);
8560 const str = JSON.stringify(json);
8561 return str.replace(/},{/g, '},\n\n{')
8562}
8563function bboxFeature(bbox, properties = {}) {
8564 const coords = bboxCoords(bbox);
8565 coords.push(coords[0]);
8566 return {
8567 type: 'Feature',
8568 geometry: {
8569 cordinates: coords,
8570 type: 'Polygon',
8571 },
8572 properties,
8573 }
8574}
8575function flattenMultiLineStrings(geojson, cloneJson = true) {
8576 if (cloneJson) geojson = clone(geojson);
8577 const features = geojson.features || geojson;
8578 const lineStrings = features.reduce((acc, obj) => {
8579 const geom = obj.geometry;
8580 if (geom.type === 'LineString') {
8581 geom.coordinates.properties = obj.properties;
8582 acc.push(geom.coordinates);
8583 } else if (geom.type === 'MultiLineString') {
8584 geom.coordinates.forEach(a => {
8585 a.properties = obj.properties;
8586 acc.push(a);
8587 });
8588 }
8589 return acc
8590 }, []);
8591 return lineStrings
8592}
8593function lineStringsToLinks(model, bbox, lineStrings) {
8594 const xfm = model.world.xfm || model.world.bboxTransform(...bbox);
8595 lineStrings = flattenMultiLineStrings(lineStrings);
8596 const nodeCache = {};
8597 const newTurtles = [];
8598 const newLinks = [];
8599 function getNode(pt) {
8600 const key = pt.toString();
8601 let node = nodeCache[key];
8602 if (node) return node
8603 node = model.turtles.createOne(t => {
8604 t.setxy(...xfm.toWorld(pt));
8605 t.lon = pt[0];
8606 t.lat = pt[1];
8607 });
8608 nodeCache[key] = node;
8609 newTurtles.push(node);
8610 return node
8611 }
8612 function newLink(pt0, pt1) {
8613 const t0 = getNode(pt0);
8614 const t1 = getNode(pt1);
8615 const link = model.links.createOne(t0, t1);
8616 newLinks.push(link);
8617 return link
8618 }
8619 function lineStringToLinks(lineString) {
8620 lineString.reduce((acc, pt, i, a) => {
8621 const link = newLink(a[i - 1], pt);
8622 if (i === 1) {
8623 acc = [link];
8624 acc.properties = lineString.properties;
8625 } else {
8626 acc.push(link);
8627 }
8628 link.lineString = acc;
8629 return acc
8630 });
8631 }
8632 lineStrings.forEach(lineString => lineStringToLinks(lineString));
8633 return [newTurtles, newLinks]
8634}
8635function flatten(gj, cloneJson = true) {
8636 if (cloneJson) gj = clone(gj);
8637 switch ((gj && gj.type) || null) {
8638 case 'FeatureCollection':
8639 gj.features = gj.features.reduce(function (mem, feature) {
8640 return mem.concat(flatten(feature))
8641 }, []);
8642 return gj
8643 case 'Feature':
8644 if (!gj.geometry) return [gj]
8645 return flatten(gj.geometry).map(function (geom) {
8646 var data = {
8647 type: 'Feature',
8648 properties: JSON.parse(JSON.stringify(gj.properties)),
8649 geometry: geom,
8650 };
8651 if (gj.id !== undefined) {
8652 data.id = gj.id;
8653 }
8654 return data
8655 })
8656 case 'MultiPoint':
8657 return gj.coordinates.map(function (_) {
8658 return { type: 'Point', coordinates: _ }
8659 })
8660 case 'MultiPolygon':
8661 return gj.coordinates.map(function (_) {
8662 return { type: 'Polygon', coordinates: _ }
8663 })
8664 case 'MultiLineString':
8665 return gj.coordinates.map(function (_) {
8666 return { type: 'LineString', coordinates: _ }
8667 })
8668 case 'GeometryCollection':
8669 return gj.geometries.map(flatten).reduce(function (memo, geoms) {
8670 return memo.concat(geoms)
8671 }, [])
8672 case 'Point':
8673 case 'Polygon':
8674 case 'LineString':
8675 return [gj]
8676 }
8677}
8678function geojsonBBox(gj) {
8679 var coords, bbox;
8680 if (!gj.hasOwnProperty('type')) return
8681 coords = getCoordinates(gj);
8682 bbox = [
8683 Number.POSITIVE_INFINITY,
8684 Number.POSITIVE_INFINITY,
8685 Number.NEGATIVE_INFINITY,
8686 Number.NEGATIVE_INFINITY,
8687 ];
8688 return coords.reduce(function (prev, coord) {
8689 return [
8690 Math.min(coord[0], prev[0]),
8691 Math.min(coord[1], prev[1]),
8692 Math.max(coord[0], prev[2]),
8693 Math.max(coord[1], prev[3]),
8694 ]
8695 }, bbox)
8696}
8697function getCoordinates(gj) {
8698 switch (gj.type) {
8699 case 'Point':
8700 return [gj.coordinates]
8701 case 'LineString':
8702 case 'MultiPoint':
8703 return gj.coordinates
8704 case 'Polygon':
8705 case 'MultiLineString':
8706 return gj.coordinates.reduce(function (dump, part) {
8707 return dump.concat(part)
8708 }, [])
8709 case 'MultiPolygon':
8710 return gj.coordinates.reduce(function (dump, poly) {
8711 return dump.concat(
8712 poly.reduce(function (points, part) {
8713 return points.concat(part)
8714 }, [])
8715 )
8716 }, [])
8717 case 'Feature':
8718 return getCoordinates(gj.geometry)
8719 case 'GeometryCollection':
8720 return gj.geometries.reduce(function (dump, g) {
8721 return dump.concat(getCoordinates(g))
8722 }, [])
8723 case 'FeatureCollection':
8724 return gj.features.reduce(function (dump, f) {
8725 return dump.concat(getCoordinates(f))
8726 }, [])
8727 }
8728}
8729
8730var geojson = /*#__PURE__*/Object.freeze({
8731 __proto__: null,
8732 bboxFeature: bboxFeature,
8733 clone: clone,
8734 flatten: flatten,
8735 flattenMultiLineStrings: flattenMultiLineStrings,
8736 geojsonBBox: geojsonBBox,
8737 getCoordinates: getCoordinates,
8738 isGeojson: isGeojson,
8739 lineStringsToLinks: lineStringsToLinks,
8740 minify: minify
8741});
8742
8743export { AgentArray, AgentList, AgentSet, Animator, Color, ColorMap, DataSet, Evented, GeoWorld, Link, Links, Model, Model3D, Mouse, Object3D, Patch, Patches, PatchesView, RGBADataSet$1 as RGBADataSet, RGBDataSet, Shapes, SpriteSheet, TileData, Turtle, Turtle3D, Turtles, TurtlesView, TwoDraw, TwoView, World, geojson, gis, steg, turfImports, util };