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