UNPKG

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