-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathDataSet.js.html
540 lines (498 loc) · 36 KB
/
DataSet.js.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
<!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="../favicon.ico"><title>Source: DataSet.js</title><!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Home</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="AgentArray.html">AgentArray</a></div><div class="sidebar-section-children"><a href="AgentList.html">AgentList</a></div><div class="sidebar-section-children"><a href="AgentSet.html">AgentSet</a></div><div class="sidebar-section-children"><a href="Animator.html">Animator</a></div><div class="sidebar-section-children"><a href="DataSet.html">DataSet</a></div><div class="sidebar-section-children"><a href="GUI.html">GUI</a></div><div class="sidebar-section-children"><a href="GeoDataSet.html">GeoDataSet</a></div><div class="sidebar-section-children"><a href="Keyboard.html">Keyboard</a></div><div class="sidebar-section-children"><a href="Link.html">Link</a></div><div class="sidebar-section-children"><a href="Links.html">Links</a></div><div class="sidebar-section-children"><a href="Model.html">Model</a></div><div class="sidebar-section-children"><a href="Model3D.html">Model3D</a></div><div class="sidebar-section-children"><a href="Mouse.html">Mouse</a></div><div class="sidebar-section-children"><a href="Patch.html">Patch</a></div><div class="sidebar-section-children"><a href="Patches.html">Patches</a></div><div class="sidebar-section-children"><a href="RGBDataSet.html">RGBDataSet</a></div><div class="sidebar-section-children"><a href="ThreeDraw.html">ThreeDraw</a></div><div class="sidebar-section-children"><a href="Turtle.html">Turtle</a></div><div class="sidebar-section-children"><a href="Turtle3D.html">Turtle3D</a></div><div class="sidebar-section-children"><a href="Turtles.html">Turtles</a></div><div class="sidebar-section-children"><a href="TwoDraw.html">TwoDraw</a></div><div class="sidebar-section-children"><a href="World.html">World</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-src_geojson.html">src/geojson</a></div><div class="sidebar-section-children"><a href="module-src_gis.html">src/gis</a></div><div class="sidebar-section-children"><a href="module-src_utils.html">src/utils</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-1 - AgentScript.html">1 - AgentScript</a></div><div class="sidebar-section-children"><a href="tutorial-2 - JavaScript.html">2 - JavaScript</a></div><div class="sidebar-section-children"><a href="tutorial-3 - Browser.html">3 - Browser</a></div><div class="sidebar-section-children"><a href="tutorial-4 - Model.html">4 - Model</a></div><div class="sidebar-section-children"><a href="tutorial-5 - View.html">5 - View</a></div><div class="sidebar-section-children"><a href="tutorial-6.1 - AnimatorController.html">6.1 - AnimatorController</a></div><div class="sidebar-section-children"><a href="tutorial-6.2 - KeyboardController.html">6.2 - KeyboardController</a></div><div class="sidebar-section-children"><a href="tutorial-6.3 - GuiController.html">6.3 - GuiController</a></div><div class="sidebar-section-children"><a href="tutorial-6.4 - MouseController.html">6.4 - MouseController</a></div><div class="sidebar-section-children"><a href="tutorial-7 - CodePenServer.html">7 - CodePenServer</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="github" href="https://github.com/backspaces/agentscript" target="">Github</a></div><div class="navbar-item"><a id="AgentScript" href="https://code.agentscript.org" target="">AgentScript.org</a></div><div class="navbar-item"><a id="npm" href="https://www.npmjs.com/package/agentscript" target="">npm</a></div><div class="navbar-item"><a id="unpkg" href="https://unpkg.com/browse/agentscript/" target="">unpkg</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">DataSet.js</h1></header><article><pre class="prettyprint source lang-js"><code>import * as util from './utils.js'
/**
* A DataSet is an object with width/height and an array
* of numbers of length = width * height.
*
* The array can be a TypedArray or a JavaScript Array.
*/
class DataSet {
width
height
data
// Return an empty dataset of given width, height, dataType
/**
* Factory method returning an empty dataset of given
* width, height, dataType
*
* @static
* @param {number} width The integer width of the array
* @param {number} height The integer height of the array
* @param {Object} Type Array (default) or one of the typed array types
* @returns {DataSet} The resulting DataSet with no values assigned
*/
static emptyDataSet(width, height, Type = Array) {
return new DataSet(width, height, new Type(width * height))
}
/**
* Creates an instance of DataSet.
* Checks data is right size, throws an error if not.
*
* @param {number} width The integer width of the array
* @param {number} height The integer height of the array
* @param {Array} data The array of numbers of length width * height
*/
constructor(width, height, data) {
if (data.length !== width * height) {
throw Error(
`new DataSet length: ${data.length} !== ${width} * ${height}`
)
}
Object.assign(this, { width, height, data })
}
// Checks x,y are within DataSet. Throw error if not.
checkXY(x, y) {
if (!this.inBounds(x, y)) {
throw Error(`DataSet: x,y out of range: ${x}, ${y}`)
}
}
// true if x,y in dataset bounds
inBounds(x, y) {
return (
util.isBetween(x, 0, this.width - 1) &&
util.isBetween(y, 0, this.height - 1)
)
}
dataType() {
return this.data.constructor
}
type() {
return this.constructor
}
// Given x,y in data space, return index into data
toIndex(x, y) {
return x + y * this.width
}
// Given index into data, return dataset [x, y] position
toXY(i) {
return [i % this.width, Math.floor(i / this.width)]
}
// Get dataset value at x,y, assuming that x,y valididated previously
// getXY (x, y) { return this.data[this.toIndex(Math.floor(x), Math.floor(y))] }
getXY(x, y) {
return this.data[this.toIndex(x, y)]
}
// Set the data value at x,y to num. assume x,y valid
// setXY (x, y, num) { this.data[this.toIndex(Math.floor(x), Math.floor(y))] = num }
setXY(x, y, num) {
this.data[this.toIndex(x, y)] = num
}
// Wrapper for sampling, defaults to "nearest". Checks x,y valid as well.
// Use this for individual sampling.
sample(x, y, useNearest = true) {
this.checkXY(x, y)
return useNearest ? this.nearest(x, y) : this.bilinear(x, y)
}
// Nearest neighbor sampling, w/o x,y validity check, i.e. our inner loops
nearest(x, y) {
return this.getXY(Math.round(x), Math.round(y))
}
// Billinear sampling w/o x,y validity check, i.e. our inner loops
bilinear(x, y) {
// Billinear sampling works by making two linear interpolations (lerps)
// in the x direction, and a third in the y direction, between the
// two x results. See wikipedia:
// [bilinear sampling](http://en.wikipedia.org/wiki/Bilinear_interpolation)
// The diagram shows the three lerps
// const [x0, y0] = [Math.floor(x), Math.floor(y)] // replaced by next line for speed
const x0 = Math.floor(x)
const y0 = Math.floor(y)
const i = this.toIndex(x0, y0)
const w = this.width
const dx = x - x0
const dy = y - y0
const dx1 = 1 - dx
const dy1 = 1 - dy
const f00 = this.data[i]
// Edge case: fij is 0 if beyond data array; undefined -> 0.
// This cancels the given component's factor in the result.
const f10 = this.data[i + 1] || 0 // 0 at bottom right corner
const f01 = this.data[i + w] || 0 // 0 at all bottom row
const f11 = this.data[i + 1 + w] || 0 // 0 at end of next to bottom row
// This is a bit involved but:
// ```
// If dx = 0; dx1 = 1, dy != 0
// -> vertical linear interpolation
// fxy = f00(1-dy) + f01(dy) i.e. y-lerp
//
// If dx != 0; dy = 0, dx !=0
// -> horizontal linear interpolation
// fxy = f00(1-dx) + f10(dx) i.e. x-lerp
// ```
return f00 * dx1 * dy1 + f10 * dx * dy1 + f01 * dx1 * dy + f11 * dx * dy
}
// Return a copy of this, with new data array
clone() {
return new DataSet(this.width, this.height, this.data.slice(0))
}
// Return new (empty) dataset, defaulting to this type
emptyDataSet(width, height, type = this.dataType()) {
return DataSet.emptyDataSet(width, height, type) // see static above
}
// Return new (empty) array of this type
emptyArray(length) {
const Type = this.type()
return new Type(length)
}
// Create new dataset of size width/height/type by resampling each point.
// Type is not this.type() due to integer/float differences. Default Array.
// If same size, return a copy of this.
// NOTE: This used to calc an x & y scale and apply it:
// const xScale = (this.width - 1) / (width - 1)
// const yScale = (this.height - 1) / (height - 1)
// ...
// ds.setXY(x, y, this.sample(x * xScale, y * yScale, useNearest))
// .. which had precision errors.
// Multiplying first, then dividing more accurate.
resample(width, height, useNearest = true, Type = Array) {
if (width === this.width && height === this.height) return this.clone()
const ds = DataSet.emptyDataSet(width, height, Type)
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
ds.setXY(
x,
y,
this.sample(
(x * (this.width - 1)) / (width - 1),
(y * (this.height - 1)) / (height - 1),
useNearest
)
)
}
}
return ds
}
// Scale each data element to be between min/max
// This is a linear scale from this dataset's min/max
// y = mx + b
// utils.objects.js:
// export function normalize(array, lo = 0, hi = 1) {
// const [min, max] = [arrayMin(array), arrayMax(array)]
// const scale = 1 / (max - min)
// return array.map(n => lerp(lo, hi, scale * (n - min)))
// }
scale(min, max) {
// const data = ds.data
const dsMin = this.min()
const dsMax = this.max()
const dsDelta = dsMax - dsMin
const delta = max - min
const m = delta / dsDelta
const b = min - m * dsMin
// const scaledData = data.map((x) => m * x + b)
// return new DataSet(ds.width, ds.height, scaledData)
return this.map(x => m * x + b)
}
// Return a rectangular subset of the dataset.
// Returned dataset is of same array type as this.
subset(x, y, width, height) {
if (x + width > this.width || y + height > this.height) {
console.log('subset: x+width', x + width, 'this.width', this.width)
console.log(
'subset: y+height',
y + height,
'this.height',
this.height
)
throw Error('DataSet.subSet: params out of range')
}
const ds = this.emptyDataSet(width, height)
for (let i = 0; i < width; i++) {
for (let j = 0; j < height; j++) {
ds.setXY(i, j, this.getXY(i + x, j + y))
}
}
return ds
}
// Crop this dataSet by removing top, bottom rows, left, right columns.
// You may pass in an obj with top, bottom, left, right key/val pairs
crop(top, bottom, left, right) {
if (bottom === undefined) {
// note var required, let/const have initialization error
var { top, bottom, left, right } = top
}
const width = this.width - left - right
const height = this.height - top - bottom
// console.log('crop top bottom left right', top, bottom, left, right)
// console.log('crop height', this.height, '->', height)
// console.log('crop width', this.width, '->', width)
// console.log('crop this width/height', this.width, this.height)
return this.subset(left, top, width, height)
}
// Return maped dataset by applying f to each dataset element
map(f) {
return new DataSet(this.width, this.height, this.data.map(f))
}
// Return the column of data at position x as this array's type
col(x) {
const [w, h, data] = [this.width, this.height, this.data]
if (x >= w) throw Error(`col: x out of range width: ${w} x: ${x}`)
const colData = this.emptyArray(h)
for (let i = 0; i < h; i++) colData[i] = data[x + i * w]
return colData
}
// Return the row of data at position y as this array's type
row(y) {
const [w, h] = [this.width, this.height]
if (y >= h) throw Error(`row: y out of range height: ${h} x: ${y}`)
return this.data.slice(y * w, (y + 1) * w)
}
// Convert this dataset's data to new type. Precision may be lost.
// Does nothing if current data is already of this Type.
convertType(type) {
this.data = util.convertArrayType(this.data, type)
}
// Concatinate a dataset of equal height to my right to my east.
// New DataSet is of same type as this.
//
// NOTE: concatWest is dataset.concatEast(this)
concatEast(ds) {
const [w, h] = [this.width, this.height]
const [w1, h1] = [ds.width, ds.height]
if (h !== h1) throw Error(`concatEast: heights not equal ${h}, ${h1}`)
const ds1 = this.emptyDataSet(w + w1, h)
// copy this into new dataset
for (let x = 0; x < w; x++) {
for (let y = 0; y < h; y++) {
ds1.setXY(x, y, this.getXY(x, y))
}
}
// copy ds to the left side
for (let x = 0; x < w1; x++) {
for (let y = 0; y < h1; y++) {
ds1.setXY(x + w, y, ds.getXY(x, y))
}
}
return ds1
}
// Concatinate a dataset of equal width to my south, returning new DataSet.
// New DataSet is of same type as this.
//
// NOTE: concatNorth is dataset.concatSouth(this)
concatSouth(dataset) {
const [w, h, data] = [this.width, this.height, this.data]
if (w !== dataset.width) {
throw Error(`concatSouth: widths not equal ${w}, ${dataset.width}`)
}
const data1 = util.concatArrays(data, dataset.data)
return new DataSet(w, h + dataset.height, data1)
}
// return dataset x,y given x,y in a euclidean space defined by tlx, tly, w, h
// x,y is in topleft-bottomright box: [tlx,tly,tlx+w,tly-h], y positive util.
// Ex: NetLogo's coords: x, y, minXcor, maxYcor, numX, numY
transformCoords(x, y, tlx, tly, w, h) {
const xs = ((x - tlx) * (this.width - 1)) / w
const ys = ((tly - y) * (this.height - 1)) / h
return [xs, ys]
}
// get a sample using a transformed euclidean coord system; see above
coordSample(x, y, tlx, tly, w, h, useNearest = true) {
const [xs, ys] = this.transformCoords(x, y, tlx, tly, w, h)
return this.sample(xs, ys, useNearest)
}
// Return Array 3x3 neighbor values of the given x,y of the dataset.
// Off-edge neighbors revert to nearest edge value.
neighborhood(x, y, array = []) {
array.length = 0 // in case user supplied an array to reduce GC
const clampNeeded =
x === 0 || x === this.width - 1 || y === 0 || y === this.height - 1
for (let dy = -1; dy <= +1; dy++) {
for (let dx = -1; dx <= +1; dx++) {
let x0 = x + dx
let y0 = y + dy
if (clampNeeded) {
x0 = util.clamp(x0, 0, this.width - 1)
y0 = util.clamp(y0, 0, this.height - 1)
}
array.push(this.data[this.toIndex(x0, y0)])
}
}
return array
}
// Return a new dataset of this array type convolved with the
// given kernel 3x3 matrix.
// See [Convolution](https://en.wikipedia.org/wiki/Kernel_(image_processing))
//
// If cropped, do not convolve the edges, returning a smaller dataset.
// If not, convolve the edges by extending edge values, returning
// dataset of same size.
convolve(kernel, factor = 1, crop = false) {
const [x0, y0, h, w] = crop // optimization not needed, only called once
? [1, 1, this.height - 1, this.width - 1]
: [0, 0, this.height, this.width]
const newDS = this.emptyDataSet(w, h)
const newData = newDS.data
let i = 0
for (let y = y0; y < h; y++) {
for (let x = x0; x < w; x++) {
const nei = this.neighborhood(x, y)
// remind: use reduce if performant
let sum2 = 0
for (let i2 = 0; i2 < kernel.length; i2++) {
// sum2 += kernel[i2] * nei[i2] // Chrome can't optimize compound let
sum2 = sum2 + kernel[i2] * nei[i2]
}
newData[i++] = sum2 * factor // newDS.data[newDS.toIndex(x, y)] = sum2 * factor
}
}
return newDS
}
// A few common convolutions. dzdx/y are also called horiz/vert Sobel
dzdx(n = 2, factor = 1 / 8) {
return this.convolve([-1, 0, 1, -n, 0, n, -1, 0, 1], factor)
}
dzdy(n = 2, factor = 1 / 8) {
return this.convolve([1, n, 1, 0, 0, 0, -1, -n, -1], factor)
}
laplace8() {
return this.convolve([-1, -1, -1, -1, 8, -1, -1, -1, -1])
}
laplace4() {
return this.convolve([0, -1, 0, -1, 4, -1, 0, -1, 0])
}
blur(factor = 0.0625) {
// 1/16 = 0.0625
return this.convolve([1, 2, 1, 2, 4, 2, 1, 2, 1], factor)
}
edge() {
return this.convolve([1, 1, 1, 1, -7, 1, 1, 1, 1])
}
// Create two new Array convolved datasets, slope and aspect, common in
// the use of an elevation data set. See Esri tutorials for
// [slope](http://goo.gl/ZcOl08) and [aspect](http://goo.gl/KoI4y5)
//
// It also returns the two derivitive DataSets, dzdx, dzdy for
// those wanting to use the results of the two convolutions.
//
// Use this.convertType to convert to typed array
slopeAndAspect(cellSize = 1, posAngle = true) {
const dzdx = this.dzdx() // sub left z from right
const dzdy = this.dzdy() // sub bottom z from top
let [aspect, slope] = [[], []]
const [h, w] = [dzdx.height, dzdx.width]
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const [gx, gy] = [dzdx.getXY(x, y), dzdy.getXY(x, y)]
// slope.push(Math.atan(util.distance(gx, gy)) / cellSize) // radians
slope.push(Math.atan(util.distance(0, 0, gx, gy)) / cellSize)
// if (noNaNs)
// while (gx === gy) {
// gx += util.randomNormal(0, 0.0001)
// gy += util.randomNormal(0, 0.0001)
// }
// radians in [-PI,PI], downhill
// let rad = (gx === gy && gy === 0) ? NaN : Math.atan2(-gy, -gx)
let rad = Math.atan2(-gy, -gx)
// positive radians in [0,2PI] if desired
if (posAngle && rad < 0) rad += 2 * Math.PI
aspect.push(rad)
}
}
slope = new DataSet(w, h, slope)
aspect = new DataSet(w, h, aspect)
return { slope, aspect, dzdx, dzdy }
}
// Return max/min/extent/sum of data
max() {
// return util.arrayMax(this.data)
return this.data.reduce((a, b) => Math.max(a, b))
}
min() {
// return util.arrayMin(this.data)
return this.data.reduce((a, b) => Math.min(a, b))
}
extent() {
return [this.min(), this.max()]
}
sum() {
return this.data.reduce((a, b) => a + b)
// return this.data.reduce((a, b) => a + b, 0)
}
// Return new dataset scaled between lo, hi values
normalize(lo = 0, hi = 1, round = false) {
const [min, max] = this.extent()
const scale = 1 / (max - min)
let data = this.data.map(n => util.lerp(lo, hi, scale * (n - min)))
if (round) data = data.map(n => Math.round(n))
return new DataSet(this.width, this.height, data)
}
// Test that this has same width, height, data as dataset.
// Note: does not require equal array type (Array or TypedArray)
equals(dataset) {
return (
this.width === dataset.width &&
this.height === dataset.height &&
util.arraysEqual(this.data, dataset.data)
)
}
}
export default DataSet
// REMIND: limit to data that can be 24 bit. Error otherwise.
// DataType of Int8, 16, Int24 OK, others need testing.
// Possibly use precision to minimize byte size to 3, rgb?
//
// Convert dataset to an image context object.
//
// This can be used to "visualize" the data by normalizing
// which will scale the data to use the entire RGB space.
// It can also be used to create tiles or image-as-data if
// the defaults are used.
//
// Due to
// [alpha-premultiply](https://en.wikipedia.org/wiki/Alpha_compositing),
// the best we can do as data is 24 bit ints.
// You can simulate floats/fixed by multiplying the dataset
// the dividing on conversion back.
//
// Our preferred transport is in the works, likely in the
// tile datasets via blobs or arraybuffers. Sigh.
// toContext (normalize = false, gray = false, alpha = 255) {
// const [w, h, data] = [this.width, this.height, this.data]
// let idata
// if (normalize) {
// idata = gray
// ? util.normalize8(data) : util.normalizeInt(data, 0, Math.pow(2, 24) - 1)
// } else {
// idata = data.map((a) => Math.round(a))
// }
// const ctx = util.createCtx(w, h)
// const id = ctx.getImageData(0, 0, w, h)
// const ta = id.data // ta short for typed array
// for (let i = 0; i < idata.length; i++) {
// const [num, j] = [idata[i], 4 * i] // j = byte index into ta
// if (gray) {
// ta[j] = ta[j + 1] = ta[j + 2] = Math.floor(num); ta[j + 3] = alpha
// } else {
// ta[j] = (num >> 16) & 0xff
// ta[j + 1] = (num >> 8) & 0xff
// ta[j + 2] = num & 0xff
// ta[j + 3] = alpha // if not 255, image will be premultiplied.
// }
// }
// ctx.putImageData(id, 0, 0)
// return ctx
// }
//
// // Convert dataset to a canvas, which can be used as an image
// toCanvas (normalize = false, gray = false, alpha = 255) {
// return this.toContext(gray, normalize, alpha).canvas
// }
// // Convert dataset to a base64 string
// toDataUrl (normalize = false, gray = false, alpha = 255) {
// return util.ctxToDataUrl(this.toContext(gray, normalize, alpha))
// }
// // Get/Set name, useful for storage key.
// setName(string) {
// this.name = string
// return this
// }
// getName() {
// return this.name ? this.name : this.makeName()
// }
// makeName() {
// const { width, height } = this
// const sum = this.sum().toFixed(2)
// return `${this.dataType().name}-${width}-${height}-${sum}`
// }
</code></pre></article></section><footer class="footer" id="PeOAagUepe"><div class="wrapper">AgentScript version: 0.10.20 using clean-jsdoc-theme</div></footer></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Home</a><div class="mobile-nav-links"><div class="navbar-item"><a id="github-mobile" href="https://github.com/backspaces/agentscript" target="">Github</a></div><div class="navbar-item"><a id="AgentScript-mobile" href="https://code.agentscript.org" target="">AgentScript.org</a></div><div class="navbar-item"><a id="npm-mobile" href="https://www.npmjs.com/package/agentscript" target="">npm</a></div><div class="navbar-item"><a id="unpkg-mobile" href="https://unpkg.com/browse/agentscript/" target="">unpkg</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="AgentArray.html">AgentArray</a></div><div class="sidebar-section-children"><a href="AgentList.html">AgentList</a></div><div class="sidebar-section-children"><a href="AgentSet.html">AgentSet</a></div><div class="sidebar-section-children"><a href="Animator.html">Animator</a></div><div class="sidebar-section-children"><a href="DataSet.html">DataSet</a></div><div class="sidebar-section-children"><a href="GUI.html">GUI</a></div><div class="sidebar-section-children"><a href="GeoDataSet.html">GeoDataSet</a></div><div class="sidebar-section-children"><a href="Keyboard.html">Keyboard</a></div><div class="sidebar-section-children"><a href="Link.html">Link</a></div><div class="sidebar-section-children"><a href="Links.html">Links</a></div><div class="sidebar-section-children"><a href="Model.html">Model</a></div><div class="sidebar-section-children"><a href="Model3D.html">Model3D</a></div><div class="sidebar-section-children"><a href="Mouse.html">Mouse</a></div><div class="sidebar-section-children"><a href="Patch.html">Patch</a></div><div class="sidebar-section-children"><a href="Patches.html">Patches</a></div><div class="sidebar-section-children"><a href="RGBDataSet.html">RGBDataSet</a></div><div class="sidebar-section-children"><a href="ThreeDraw.html">ThreeDraw</a></div><div class="sidebar-section-children"><a href="Turtle.html">Turtle</a></div><div class="sidebar-section-children"><a href="Turtle3D.html">Turtle3D</a></div><div class="sidebar-section-children"><a href="Turtles.html">Turtles</a></div><div class="sidebar-section-children"><a href="TwoDraw.html">TwoDraw</a></div><div class="sidebar-section-children"><a href="World.html">World</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-src_geojson.html">src/geojson</a></div><div class="sidebar-section-children"><a href="module-src_gis.html">src/gis</a></div><div class="sidebar-section-children"><a href="module-src_utils.html">src/utils</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-1 - AgentScript.html">1 - AgentScript</a></div><div class="sidebar-section-children"><a href="tutorial-2 - JavaScript.html">2 - JavaScript</a></div><div class="sidebar-section-children"><a href="tutorial-3 - Browser.html">3 - Browser</a></div><div class="sidebar-section-children"><a href="tutorial-4 - Model.html">4 - Model</a></div><div class="sidebar-section-children"><a href="tutorial-5 - View.html">5 - View</a></div><div class="sidebar-section-children"><a href="tutorial-6.1 - AnimatorController.html">6.1 - AnimatorController</a></div><div class="sidebar-section-children"><a href="tutorial-6.2 - KeyboardController.html">6.2 - KeyboardController</a></div><div class="sidebar-section-children"><a href="tutorial-6.3 - GuiController.html">6.3 - GuiController</a></div><div class="sidebar-section-children"><a href="tutorial-6.4 - MouseController.html">6.4 - MouseController</a></div><div class="sidebar-section-children"><a href="tutorial-7 - CodePenServer.html">7 - CodePenServer</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>