array-kit 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/NDArray.js +109 -25
  2. package/package.json +1 -1
package/lib/NDArray.js CHANGED
@@ -43,9 +43,13 @@
43
43
  order: array of coord order stored in the storage, by default [ 0 , 1 , 2 , ..., N ], for 2D it's row-first,
44
44
  if you want column-first, use [ 1 , 0 ], or params.reverse = true
45
45
  reverse: syntactic sugar, like params.order = [ N , ... , 2 , 1 , 0 ]
46
- offset: the offset if the ND-array does not start at the begining of the data storage (e.g. create a view of a Buffer)
46
+ dataStart: the index in the data storage where the ND-array starts (e.g. the start of the view in the Buffer)
47
47
  stride: (default: 1) the stride of the fastest moving dimension, usually 1 except if there is more dimensions fastest
48
48
  dimensions that are removed from the view
49
+
50
+ Not added yet:
51
+ offset: the offset of the first logical element of the ND-array
52
+ dataEnd: the index in the data storage where the ND-array ends, excluded (e.g. the end of the view in the Buffer)
49
53
  */
50
54
  function NDArray( dataOrConstructor , sizes , params ) {
51
55
  let dimensions , noInit = false ;
@@ -64,7 +68,9 @@ function NDArray( dataOrConstructor , sizes , params ) {
64
68
  // Define the size in each axis
65
69
  this.sizes = new Array( dimensions ) ;
66
70
 
67
- // The full size of the ND-array, each axis-size multiplied
71
+ // The full logical size of the ND-array, each axis-size multiplied
72
+ // /!\ IT'S NOT THE PHYSICAL SIZE /!\
73
+ // See .dataStart and .dataEnd for that.
68
74
  this.size = 1 ;
69
75
 
70
76
  // Define the min for each axis, when the ND-array is not zero-based
@@ -80,9 +86,13 @@ function NDArray( dataOrConstructor , sizes , params ) {
80
86
  // Define the layout of the array in memory, strides are products of all faster dimensions
81
87
  this.strides = new Array( dimensions ) ;
82
88
 
83
- // When the store is only partially used, this is the offset in the store natural index
89
+ // This is the index of the first logical element in the physical data storage
84
90
  this.offset = 0 ;
85
91
 
92
+ // This is physical start and end of the ND-array in the data storage
93
+ this.dataStart = 0 ;
94
+ this.dataEnd = 0 ;
95
+
86
96
  // The underlying data store: Array, TypedArray, Buffer... anything that is indexed
87
97
  this.data = null ;
88
98
 
@@ -120,24 +130,28 @@ NDArray.prototype._init = function( dataOrConstructor , sizes , params = {} ) {
120
130
  // Compute the stride
121
131
  this._initStridesUsingSizes( params.stride ) ;
122
132
 
123
- // Data store start and end
124
- this.offset = params.offset || 0 ;
133
+ // The index where the ND-array region starts
134
+ this.dataStart = params.dataStart || 0 ;
135
+
136
+ // Compute data store start and end
137
+ //this._initStartEndFromOffsetSizesStrides() ;
138
+ this._initOffsetEndFromStartSizesStrides() ;
125
139
 
126
140
  // Set or create a new data store
127
141
  if ( typeof dataOrConstructor === 'function' ) {
128
142
  if ( typeof dataOrConstructor.allocUnsafe === 'function' ) {
129
143
  // Detect Node.js's Buffer referencing it (avoid browser compatibility layer)
130
- this.data = dataOrConstructor.allocUnsafe( this.offset + this.size ) ;
144
+ this.data = dataOrConstructor.allocUnsafe( this.dataEnd ) ;
131
145
  }
132
146
  else {
133
- this.data = new dataOrConstructor( this.offset + this.size ) ;
147
+ this.data = new dataOrConstructor( this.dataEnd ) ;
134
148
  }
135
149
  }
136
150
  else {
137
151
  /*
138
152
  // Not sure it's good to check the length, since Array is extensible and only rarer TypedArray is fixed
139
- if ( dataOrConstructor.length < this.offset + this.size ) {
140
- throw new RangeError( "Provided data store is too small (expecting at least " + ( this.offset + this.size ) + " but got " + dataOrConstructor.length ) ;
153
+ if ( dataOrConstructor.length < this.dataEnd ) {
154
+ throw new RangeError( "Provided data store is too small (expecting at least " + this.dataEnd + " but got " + dataOrConstructor.length ) ;
141
155
  }
142
156
  */
143
157
  this.data = dataOrConstructor ;
@@ -193,9 +207,44 @@ NDArray.prototype._initStridesUsingSizes = function( stride = 1 ) {
193
207
  }
194
208
  } ;
195
209
 
210
+ // Offset, sizes and strides are not passed as argument, they should already be computed on the instance
211
+ NDArray.prototype._initOffsetEndFromStartSizesStrides = function() {
212
+ this.dataEnd = this.dataStart + 1 ;
213
+ this.offset = this.dataStart ;
214
+
215
+ for ( let d = 0 ; d < this.dimensions ; d ++ ) {
216
+ const shift = this.strides[ d ] * ( this.sizes[ d ] - 1 ) ;
217
+
218
+ if ( shift >= 0 ) {
219
+ this.dataEnd += shift ;
220
+ }
221
+ else {
222
+ this.dataEnd -= shift ;
223
+ this.offset -= shift ;
224
+ }
225
+ }
226
+ } ;
196
227
 
228
+ /* Not sure if it's useful to compute based on offset
229
+ // Offset, sizes and strides are not passed as argument, they should already be computed on the instance
230
+ NDArray.prototype._initStartEndFromOffsetSizesStrides = function() {
231
+ this.dataStart = this.offset ;
232
+ this.dataEnd = this.offset + 1 ;
197
233
 
198
- // Make the array zero-based, or based to the provided mins vector
234
+ for ( const d of this.order ) {
235
+ if ( this.strides[ d ] >= 0 ) {
236
+ this.dataEnd += this.strides[ d ] * ( this.sizes[ d ] - 1 ) ;
237
+ }
238
+ else {
239
+ this.dataStart += this.strides[ d ] * ( this.sizes[ d ] - 1 ) ;
240
+ }
241
+ }
242
+ } ;
243
+ */
244
+
245
+
246
+
247
+ // Make the array zero-based, or based on the provided mins vector
199
248
  NDArray.prototype.rebase = function( mins ) {
200
249
  if ( mins ) {
201
250
  for ( let d = 0 ; d < this.dimensions ; d ++ ) {
@@ -215,8 +264,41 @@ NDArray.prototype.rebase = function( mins ) {
215
264
 
216
265
 
217
266
 
267
+ // Translate, moving the coordinates of each value
268
+ NDArray.prototype.translate = function( translation ) {
269
+ for ( let d = 0 ; d < this.dimensions ; d ++ ) {
270
+ this.mins[ d ] += translation[ d ] ;
271
+ this.maxs[ d ] += translation[ d ] ;
272
+ }
273
+
274
+ return this ;
275
+ } ;
276
+
277
+
278
+
279
+ // Flip coordinates along a dimension, but preserving min and max
280
+ NDArray.prototype.flip = function( dimension ) {
281
+ if ( dimension < 0 || dimension >= this.dimensions ) {
282
+ throw new RangeError( "Bad dimension, should be between [0," + this.dimensions + ") but got: " + dimension ) ;
283
+ }
284
+
285
+ this.strides[ dimension ] *= - 1 ;
286
+ this._initOffsetEndFromStartSizesStrides() ;
287
+
288
+ return this ;
289
+ } ;
290
+
291
+ // Flip coordinates along all dimensions at once, but preserving min and max
292
+ NDArray.prototype.flipAll = function() {
293
+ for ( let d = 0 ; d < this.dimensions ; d ++ ) { this.strides[ d ] *= - 1 ; }
294
+ this._initOffsetEndFromStartSizesStrides() ;
295
+ return this ;
296
+ } ;
297
+
298
+
299
+
218
300
  // Create a new clone of the current NDArray with the same geometry but an empty (but ready) data
219
- // The new data is limited to the used size (offset = 0).
301
+ // The new data is limited to the used size (dataStart = 0).
220
302
  NDArray.prototype._cloneGeometry = function() {
221
303
  const newNDArray = new NDArray( this.dimensions ) ;
222
304
 
@@ -228,6 +310,10 @@ NDArray.prototype._cloneGeometry = function() {
228
310
  Object.assign( newNDArray.maxs , this.maxs ) ;
229
311
  Object.assign( newNDArray.strides , this.strides ) ;
230
312
 
313
+ // Start at index=0 in the physical storage
314
+ newNDArray.offset = this.offset - this.dataStart ;
315
+ newNDArray.dataEnd = this.dataEnd - this.dataStart ;
316
+
231
317
  return newNDArray ;
232
318
  } ;
233
319
 
@@ -240,6 +326,7 @@ NDArray.prototype._resizeGeometry = function( mins , maxs ) {
240
326
  Object.assign( newNDArray.order , this.order ) ;
241
327
  newNDArray._initSizesFromMinsMaxs( mins , maxs ) ;
242
328
  newNDArray._initStridesUsingSizes() ;
329
+ newNDArray._initOffsetEndFromStartSizesStrides() ;
243
330
 
244
331
  return newNDArray ;
245
332
  } ;
@@ -247,7 +334,7 @@ NDArray.prototype._resizeGeometry = function( mins , maxs ) {
247
334
 
248
335
 
249
336
  // Create a new data store of the same type
250
- NDArray.prototype._newStorageOfTheSameKind = function( size = this.size ) {
337
+ NDArray.prototype._newStorageOfTheSameKind = function( size = this.dataEnd - this.dataStart ) {
251
338
  if ( typeof this.data?.constructor?.allocUnsafe === 'function' ) {
252
339
  // Detect Node.js's Buffer referencing it (avoid browser compatibility layer)
253
340
  return this.data.constructor.allocUnsafe( size ) ;
@@ -308,8 +395,8 @@ NDArray.prototype._getCoords = function( index , coords = new Array( this.dimens
308
395
 
309
396
  NDArray.prototype._getCoordsCheck =
310
397
  NDArray.prototype.getCoords = function( index , coords ) {
311
- if ( index < this.offset || index >= this.offset + this.size ) {
312
- throw new RangeError( "Index " + index + " out of bounds, which is [" + this.offset + "," + ( this.offset + this.size ) + ")" ) ;
398
+ if ( index < this.dataStart || index >= this.dataEnd ) {
399
+ throw new RangeError( "Index " + index + " out of bounds, which is [" + this.dataStart + "," + this.dataEnd + ")" ) ;
313
400
  }
314
401
 
315
402
  coords = coords ?? new Array( this.dimensions ) ;
@@ -453,14 +540,13 @@ NDArray.prototype._forEach = function( callback ) {
453
540
  // Usually =1, but later we could group neighbours (e.g. for RGBA images => stride0 = 4)
454
541
  const stride0 = this.strides[ this.order[ 0 ] ] ;
455
542
  const coords = Array.from( this.mins ) ;
456
- const dataEnd = this.offset + this.size ;
457
- let index = this.offset ;
543
+ let index = this.dataStart ;
458
544
 
459
545
  for ( ;; ) {
460
546
  callback( this.data[ index ] , coords , index , this ) ;
461
547
 
462
548
  index += stride0 ;
463
- if ( index >= dataEnd ) { return ; }
549
+ if ( index >= this.dataEnd ) { return ; }
464
550
 
465
551
  for ( let i = 0 ; i < this.dimensions ; i ++ ) {
466
552
  const d = this.order[ i ] ;
@@ -845,10 +931,8 @@ NDArray.prototype._eachVectorIndexInRegionBackward = function*( strideStartCoord
845
931
 
846
932
 
847
933
  NDArray.prototype.fill = function( value ) {
848
- for ( let i = this.offset , end = this.offset + this.size ; i < end ; i ++ ) {
849
- this.data[ i ] = value ;
850
- }
851
-
934
+ const stride0 = this.strides[ this.order[ 0 ] ] ;
935
+ for ( let i = this.dataStart ; i < this.dataEnd ; i += stride0 ) { this.data[ i ] = value ; }
852
936
  return this ;
853
937
  } ;
854
938
 
@@ -892,7 +976,7 @@ NDArray.prototype.map = function( callback ) {
892
976
  newNDArray.data = this._newStorageOfTheSameKind() ;
893
977
 
894
978
  this._forEach( ( value , coords , index ) => {
895
- newNDArray.data[ index - this.offset ] = callback( value , coords , index , this ) ;
979
+ newNDArray.data[ index - this.dataStart ] = callback( value , coords , index , this ) ;
896
980
  } ) ;
897
981
 
898
982
  return newNDArray ;
@@ -913,7 +997,7 @@ NDArray.prototype.mapInRegion = function( mins , maxs , callback ) {
913
997
 
914
998
  this._checkRegion( mins , maxs ) ;
915
999
  const newNDArray = this._resizeGeometry( mins , maxs ) ;
916
- newNDArray.data = this._newStorageOfTheSameKind( newNDArray.size ) ;
1000
+ newNDArray.data = this._newStorageOfTheSameKind( newNDArray.dataEnd ) ;
917
1001
 
918
1002
  this._dualStepCallback( newNDArray , mins , maxs , mins , maxs , callback ) ;
919
1003
 
@@ -944,7 +1028,7 @@ NDArray.prototype.mapVectorInRegion = function( mins , maxs , callback ) {
944
1028
 
945
1029
  this._checkRegion( strideStartCoords , strideEndCoords , vectorDimension ) ;
946
1030
  const newNDArray = this._resizeGeometry( strideStartCoords , strideEndCoords ) ;
947
- newNDArray.data = this._newStorageOfTheSameKind( newNDArray.size ) ;
1031
+ newNDArray.data = this._newStorageOfTheSameKind( newNDArray.dataEnd ) ;
948
1032
 
949
1033
  this._dualStepVectorCallback( newNDArray , strideStartCoords , strideEndCoords , strideStartCoords , strideEndCoords , vectorDimension , callback ) ;
950
1034
 
@@ -1029,7 +1113,7 @@ NDArray.prototype.extractRegion = function( mins , maxs ) {
1029
1113
 
1030
1114
  this._checkRegion( mins , maxs ) ;
1031
1115
  const newNDArray = this._resizeGeometry( mins , maxs ) ;
1032
- newNDArray.data = this._newStorageOfTheSameKind( newNDArray.size ) ;
1116
+ newNDArray.data = this._newStorageOfTheSameKind( newNDArray.dataEnd ) ;
1033
1117
 
1034
1118
  this._dualStepCopy( newNDArray , mins , maxs , mins , maxs ) ;
1035
1119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "array-kit",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "An array manipulation toolbox.",
5
5
  "main": "lib/array-kit.js",
6
6
  "directories": {