1 | var Fn = {
|
2 | debounce: require("lodash.debounce"),
|
3 | cloneDeep: require("lodash.clonedeep"),
|
4 | };
|
5 |
|
6 | /**
|
7 | * Format a number such that it has a given number of digits after the
|
8 | * decimal point.
|
9 | *
|
10 | * @param {Number} number - The number to format
|
11 | * @param {Number} [digits = 0] - The number of digits after the decimal point
|
12 | * @return {Number} Formatted number
|
13 | * @example
|
14 | * Fn.toFixed(5.4564, 2); // -> 5.46
|
15 | * @example
|
16 | * Fn.toFixed(1.5, 2); // -> 1.5
|
17 | */
|
18 | Fn.toFixed = function(number, digits) {
|
19 | // Guard against error when number is null or undefined
|
20 | // Cast result as number
|
21 | return +(number || 0).toFixed(digits);
|
22 | };
|
23 |
|
24 |
|
25 | /**
|
26 | * Map a value (number) from one range to another. Based on Arduino's map().
|
27 | *
|
28 | * @param {Number} value - value to map
|
29 | * @param {Number} fromLow - low end of originating range
|
30 | * @param {Number} fromHigh - high end of originating range
|
31 | * @param {Number} toLow - low end of target range
|
32 | * @param {Number} toHigh - high end of target range
|
33 | * @return {Number} mapped value (integer)
|
34 | * @example
|
35 | * Fn.map(500, 0, 1000, 0, 255); // ->
|
36 | */
|
37 |
|
38 | Fn.map = function(value, fromLow, fromHigh, toLow, toHigh) {
|
39 | return ((value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow) | 0;
|
40 | };
|
41 | // Alias
|
42 | Fn.scale = Fn.map;
|
43 |
|
44 | /**
|
45 | * Like map, but returns a Float32
|
46 | *
|
47 | * For @param info, @see Fn.map
|
48 | * @return {Float32}
|
49 | */
|
50 | var f32A = new Float32Array(1);
|
51 |
|
52 | Fn.fmap = function(value, fromLow, fromHigh, toLow, toHigh) {
|
53 | f32A[0] = (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow;
|
54 | return f32A[0];
|
55 | };
|
56 | // Alias
|
57 | Fn.fscale = Fn.fmap;
|
58 |
|
59 | /**
|
60 | * Constrains a number to be within a range. Based on Arduino's constrain()
|
61 | *
|
62 | * @param {Number} value
|
63 | * @param {Number} lower - lower bound of range for constraint
|
64 | * @param {Number} upper - upper bound of range for constraint
|
65 | * @return {Number | NaN} constrained number or NaN if any of the provided
|
66 | * parameters are not a {Number}.
|
67 | */
|
68 | Fn.constrain = function(value, lower, upper) {
|
69 | return Math.min(upper, Math.max(lower, value));
|
70 | };
|
71 |
|
72 | /**
|
73 | * Is value between the bounds of lower and upper?
|
74 | *
|
75 | * @param {Number} value
|
76 | * @param {Number} lower - Lower end of bounds to check
|
77 | * @param {Number} upper - Upper ends of bounds to check
|
78 | * @return {Boolean}
|
79 | */
|
80 | Fn.inRange = function(value, lower, upper) {
|
81 | return value >= lower && value <= upper;
|
82 | };
|
83 |
|
84 | /**
|
85 | * Generate an Array of Numbers with values between lower and upper; the
|
86 | * step (increment/decrement) between each defined by tick.
|
87 | *
|
88 | * @param {Number} lower - The value of the lowest element in the resulting
|
89 | * Array. If `Fn.range` invoked with only one
|
90 | * argument, this parameter will instead define the
|
91 | * length of the Array, which will start from 0.
|
92 | * @param {Number} upper - The value of the final element of the Array.
|
93 | * @param {Number} [tick = 1] - The difference between each element in the
|
94 | * Array. This value may be negative.
|
95 | * @return {Array} of {Numbers}
|
96 | *
|
97 | * @example
|
98 | * Fn.range(5, 10); // -> [5, 6, 7, 8, 9, 10];
|
99 | * @example
|
100 | * Fn.range(5); // -> [0, 1, 2, 3, 4];
|
101 | * @example
|
102 | * Fn.range(3, 27, 3); // -> [3, 6, 9, 12, 15, 18, 21, 24, 27];
|
103 | * @example
|
104 | * Fn.range(0, -9, -3); // -> [0, -3, -6, -9];
|
105 | */
|
106 | Fn.range = function(lower, upper, tick) {
|
107 |
|
108 | if (arguments.length === 1) {
|
109 | upper = lower - 1;
|
110 | lower = 0;
|
111 | }
|
112 |
|
113 | lower = lower || 0;
|
114 | upper = upper || 0;
|
115 | tick = tick || 1;
|
116 |
|
117 | var len = Math.max(Math.ceil((upper - lower) / tick), 0),
|
118 | idx = 0,
|
119 | range = [];
|
120 |
|
121 | while (idx <= len) {
|
122 | range[idx++] = lower;
|
123 | lower += tick;
|
124 | }
|
125 |
|
126 | return range;
|
127 | };
|
128 |
|
129 | /**
|
130 | * Adds prefix to each element in the range Array returned by Fn.range.
|
131 | *
|
132 | * Fn.range.prefixed( prefix, upper )
|
133 | * Fn.range.prefixed( prefix, lower, upper )
|
134 | * Fn.range.prefixed( prefix, lower, upper, tick )
|
135 | *
|
136 | * @param {*} prefix - You probably want to use a string, but you don't have to.
|
137 | * @return Array per range parameters, each element prefixed.
|
138 | * @see Fn.range
|
139 | * @example
|
140 | * Fn.range.prefixed("A", 0, 10, 2); // -> ["A0", "A2", "A4", "A6", "A8", "A10"]
|
141 | */
|
142 | Fn.range.prefixed = function(prefix) {
|
143 | return Fn.range.apply(null, [].slice.call(arguments, 1)).map(function(val) {
|
144 | return prefix + val;
|
145 | });
|
146 | };
|
147 |
|
148 | /**
|
149 | * Generate a reasonably-unique ID string
|
150 | *
|
151 | * @return {String} - 36-character random-ish string
|
152 | */
|
153 | Fn.uid = function() {
|
154 | return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(chr) {
|
155 | var rnd = Math.random() * 16 | 0;
|
156 | return (chr === "x" ? rnd : (rnd & 0x3 | 0x8)).toString(16);
|
157 | }).toUpperCase();
|
158 | };
|
159 |
|
160 |
|
161 | /**
|
162 | * Square your x!
|
163 | *
|
164 | * @param {Number} x
|
165 | * @return {Number| Nan} - x^2—unless you were goofy enough to provide a
|
166 | * non-numeric x, in which case it's NaN for you!
|
167 | */
|
168 | Fn.square = function(x) {
|
169 | return x * x;
|
170 | };
|
171 |
|
172 | /**
|
173 | * Get a sum for all the values in an Array. This works best if the elements
|
174 | * in the Array are Numbers.
|
175 | *
|
176 | * @param {Array} values
|
177 | * @return {Number | String} - You probably want a Number so you'll want to
|
178 | * pass a values Array entirely consisting of
|
179 | * numeric elements.
|
180 | */
|
181 | Fn.sum = function sum(values) {
|
182 | var vals;
|
183 | if (Array.isArray(values)) {
|
184 | vals = values;
|
185 | } else {
|
186 | vals = [].slice.call(arguments);
|
187 | }
|
188 | return vals.reduce(function(accum, value) {
|
189 | return accum + value;
|
190 | }, 0);
|
191 | };
|
192 |
|
193 | /**
|
194 | * Fused multiply-add for precise floating-point calculations.
|
195 | */
|
196 | // fma function
|
197 | // Copyright (c) 2012, Jens Nockert
|
198 | // All rights reserved.
|
199 | //
|
200 | // Redistribution and use in source and binary forms, with or without
|
201 | // modification, are permitted provided that the following conditions are met:
|
202 | //
|
203 | // 1. Redistributions of source code must retain the above copyright notice,
|
204 | // this list of conditions and the following disclaimer.
|
205 | // 2. Redistributions in binary form must reproduce the above copyright notice,
|
206 | // this list of conditions and the following disclaimer in the documentation
|
207 | // and/or other materials provided with the distribution.
|
208 | //
|
209 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
210 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
211 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
212 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
213 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
214 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
215 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
216 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
217 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
218 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
219 | // POSSIBILITY OF SUCH DAMAGE.
|
220 | //
|
221 | Fn.fma = function(a, b, c) {
|
222 | var aHigh = 134217729 * a;
|
223 | var aLow;
|
224 |
|
225 | aHigh = aHigh + (a - aHigh);
|
226 | aLow = a - aHigh;
|
227 |
|
228 | var bHigh = 134217729 * b;
|
229 | var bLow;
|
230 |
|
231 | bHigh = bHigh + (b - bHigh);
|
232 | bLow = b - bHigh;
|
233 |
|
234 | var r1 = a * b;
|
235 | var r2 = -r1 + aHigh * bHigh + aHigh * bLow + aLow * bHigh + aLow * bLow;
|
236 |
|
237 | var s = r1 + c;
|
238 | var t = (r1 - (s - c)) + (c - (s - r1));
|
239 |
|
240 | return s + (t + r2);
|
241 | };
|
242 | // end fma function copyright
|
243 |
|
244 | /**
|
245 | * Return a value with the bit at the position indicated set (to 1).
|
246 | * From avr/io.h "BV" => Bit Value
|
247 | *
|
248 | * An example: logically OR these bits together:
|
249 | * var ORed = _BV(0) | _BV(2) | _BV(7);
|
250 | *
|
251 | * BIT 7 6 5 4 3 2 1 0
|
252 | * ---------------------------------------------------------
|
253 | * _BV(0) = 0 0 0 0 0 0 0 1
|
254 | * _BV(2) = 0 0 0 0 0 1 0 0
|
255 | * _BV(7) = 1 0 0 0 0 0 0 0
|
256 | * ORed = 1 0 0 0 0 1 0 1
|
257 | *
|
258 | * ORed === 133;
|
259 | *
|
260 | * @param {Number} bit - bit position to set
|
261 | * @return {Number}
|
262 | * @example
|
263 | * Fn.bitValue(0); // --> 1
|
264 | * @example
|
265 | * Fn.bitValue(4); // --> 16
|
266 | *
|
267 | */
|
268 | Fn._BV = Fn.bitValue = Fn.bv = function(bit) {
|
269 | return 1 << bit;
|
270 | };
|
271 |
|
272 | /**
|
273 | * int16 Combine two bytes to make an signed 16-bit integer
|
274 | * @param {byte} msb Most signifcant byte
|
275 | * @param {byte} lsb Least signifcant byte
|
276 | * @return {word} Signed 16-bit integer
|
277 | */
|
278 | Fn.int16 = function(msb, lsb) {
|
279 | var result = (msb << 8) | lsb;
|
280 |
|
281 | // Check highest bit for sign. If on, value is negative
|
282 | return result >> 15 ? ((result ^ 0xFFFF) + 1) * -1 : result;
|
283 | };
|
284 |
|
285 | /**
|
286 | * uint16 Combine two bytes to make an unsigned 16-bit integer
|
287 | * @param {byte} msb Most signifcant byte
|
288 | * @param {byte} lsb Least signifcant byte
|
289 | * @return {word} unsigned 16-bit integer
|
290 | */
|
291 | Fn.uint16 = function(msb, lsb) {
|
292 | return (msb << 8) | lsb;
|
293 | };
|
294 |
|
295 | /**
|
296 | * int24 Combine three bytes to make a signed 24-bit integer
|
297 | * @param {byte} b16 b[16:23]
|
298 | * @param {byte} b8 b[8:15]
|
299 | * @param {byte} b0 b[0:7]
|
300 | * @return {word} Signed 24-bit integer
|
301 | */
|
302 | Fn.int24 = function(b16, b8, b0) {
|
303 | var result = (b16 << 16) | (b8 << 8) | b0;
|
304 |
|
305 | // Check highest bit for sign. If on, value is negative
|
306 | return result >> 23 ? ((result ^ 0xFFFFFF) + 1) * -1 : result;
|
307 | };
|
308 |
|
309 | /**
|
310 | * uint24 Combine three bytes to make an unsigned 24-bit integer
|
311 | * @param {byte} b16 b[16:23]
|
312 | * @param {byte} b8 b[8:15]
|
313 | * @param {byte} b0 b[0:7]
|
314 | * @return {word} Unsigned 24-bit integer
|
315 | */
|
316 | Fn.uint24 = function(b16, b8, b0) {
|
317 | return (b16 << 16) | (b8 << 8) | b0;
|
318 | };
|
319 |
|
320 | /**
|
321 | * int32 Combine four bytes to make a signed 24-bit integer
|
322 | * @param {byte} b24 b[24:31]
|
323 | * @param {byte} b16 b[16:23]
|
324 | * @param {byte} b8 b[8:15]
|
325 | * @param {byte} b0 b[0:7]
|
326 | * @return {word} Signed 32-bit integer
|
327 | */
|
328 | Fn.int32 = function(b24, b16, b8, b0) {
|
329 | var result = (b24 << 24) | (b16 << 16) | (b8 << 8) | b0;
|
330 | // Check highest bit for sign. If on, value is negative
|
331 | return result >> 31 ? ((result ^ 0xFFFFFFFF) + 1) * -1 : result;
|
332 | };
|
333 |
|
334 | /**
|
335 | * int32 Combine four bytes to make an unsigned 32-bit integer
|
336 | * @param {byte} b24 b[24:31]
|
337 | * @param {byte} b16 b[16:23]
|
338 | * @param {byte} b8 b[8:15]
|
339 | * @param {byte} b0 b[0:7]
|
340 | * @return {Number} unsigned 32-bit integer
|
341 | */
|
342 | Fn.uint32 = function(b24, b16, b8, b0) {
|
343 | // Note: If you left-shift a byte by 24 in JS and that byte's
|
344 | // MSbit is 1, the resulting value will be negative because JS casts
|
345 | // bitwise operands (temporarily) to SIGNED 32-bit numbers. The
|
346 | // final >>> 0 causes the sign bit to be disregarded, making sure our
|
347 | // result is non-negative.
|
348 | return ((b24 << 24) | (b16 << 16) | (b8 << 8) | b0) >>> 0;
|
349 | };
|
350 |
|
351 | /**
|
352 | * bitSize Get the number of bits in a given number
|
353 | * @param {number} n The number to evaluate
|
354 | * @return {number} The bit count
|
355 | */
|
356 | Fn.bitSize = function(n) {
|
357 | return Math.round(Math.log2(n));
|
358 | };
|
359 |
|
360 | /**
|
361 | * The following generates functions and constants for utility when working
|
362 | * with binary numbers:
|
363 | * - Fn.POW_2_0 through Fn.POW_2_53
|
364 | * - Fn.u4(value) through Fn.u32(value)
|
365 | * - Fn.s4(value) through Fn.s32(value)
|
366 | */
|
367 | var POW = "POW_2_";
|
368 | var U = "u";
|
369 | var S = "s";
|
370 | var MAX = Fn.bitSize(Number.MAX_SAFE_INTEGER) + 1;
|
371 | var bitSizes = [ 4, 8, 10, 12, 16, 20, 24, 32 ];
|
372 |
|
373 | /**
|
374 | * Generate "constants" that represent powers of 2. Available for powers
|
375 | * 0 through 53.
|
376 | * @example
|
377 | * Fn.POW_2_17; // -> 131072
|
378 | */
|
379 | for (var i = 0; i < MAX; i++) {
|
380 | Fn[POW + i] = Math.pow(2, i);
|
381 | }
|
382 |
|
383 | bitSizes.forEach(function(bitSize) {
|
384 | var decimal = Fn[POW + bitSize];
|
385 | var half = decimal / 2 >>> 0;
|
386 | var halfMinusOne = half - 1;
|
387 |
|
388 | /**
|
389 | * The function Fn["u" + bitSize] will constrain a value to an unsigned
|
390 | * value of that bit size.
|
391 | *
|
392 | * @param {Number} value
|
393 | * @return {Number} constrained to an unsigned int
|
394 | * @example
|
395 | * Fn.u8(255); // --> 255
|
396 | * Fn.u8(256); // --> 255
|
397 | * Fn.u8(-255); // --> 0
|
398 | * Fn.u8(-254); // -- 1
|
399 | */
|
400 | Fn[U + bitSize] = function(value) {
|
401 | if (value < 0) {
|
402 | value += decimal;
|
403 | }
|
404 | return Fn.constrain(value, 0, decimal - 1);
|
405 | };
|
406 |
|
407 | /**
|
408 | * The function Fn["s" + bitSize] will constrain a value to a signed value
|
409 | * of that bit size. Remember that, e.g., range for signed 8-bit numbers
|
410 | * is -128 to 127.
|
411 | *
|
412 | * @param {Number} value
|
413 | * @return {Number} constrained to a SIGNED integer in bitsize range
|
414 | * @example
|
415 | * Fn.s8(100); // --> 100
|
416 | * Fn.s8(128); // --> -128
|
417 | * Fn.s8(127); // --> 127
|
418 | * Fn.s8(255); // --> -1
|
419 | */
|
420 | Fn[S + bitSize] = function(value) {
|
421 | if (value > halfMinusOne) {
|
422 | value -= decimal;
|
423 | }
|
424 | return Fn.constrain(value, -half, halfMinusOne);
|
425 | };
|
426 | });
|
427 | /*
|
428 | Fn.POW_2_0 => 1
|
429 | Fn.POW_2_1 => 2
|
430 | Fn.POW_2_2 => 4
|
431 | Fn.POW_2_3 => 8
|
432 | Fn.POW_2_4 => 16
|
433 | Fn.POW_2_5 => 32
|
434 | Fn.POW_2_6 => 64
|
435 | Fn.POW_2_7 => 128
|
436 | Fn.POW_2_8 => 256
|
437 | Fn.POW_2_9 => 512
|
438 | Fn.POW_2_10 => 1024
|
439 | Fn.POW_2_11 => 2048
|
440 | Fn.POW_2_12 => 4096
|
441 | Fn.POW_2_13 => 8192
|
442 | Fn.POW_2_14 => 16384
|
443 | Fn.POW_2_15 => 32768
|
444 | Fn.POW_2_16 => 65536
|
445 | Fn.POW_2_17 => 131072
|
446 | Fn.POW_2_18 => 262144
|
447 | Fn.POW_2_19 => 524288
|
448 | Fn.POW_2_20 => 1048576
|
449 | Fn.POW_2_21 => 2097152
|
450 | Fn.POW_2_22 => 4194304
|
451 | Fn.POW_2_23 => 8388608
|
452 | Fn.POW_2_24 => 16777216
|
453 | Fn.POW_2_25 => 33554432
|
454 | Fn.POW_2_26 => 67108864
|
455 | Fn.POW_2_27 => 134217728
|
456 | Fn.POW_2_28 => 268435456
|
457 | Fn.POW_2_29 => 536870912
|
458 | Fn.POW_2_30 => 1073741824
|
459 | Fn.POW_2_31 => 2147483648
|
460 | Fn.POW_2_32 => 4294967296
|
461 | Fn.POW_2_33 => 8589934592
|
462 | Fn.POW_2_34 => 17179869184
|
463 | Fn.POW_2_35 => 34359738368
|
464 | Fn.POW_2_36 => 68719476736
|
465 | Fn.POW_2_37 => 137438953472
|
466 | Fn.POW_2_38 => 274877906944
|
467 | Fn.POW_2_39 => 549755813888
|
468 | Fn.POW_2_40 => 1099511627776
|
469 | Fn.POW_2_41 => 2199023255552
|
470 | Fn.POW_2_42 => 4398046511104
|
471 | Fn.POW_2_43 => 8796093022208
|
472 | Fn.POW_2_44 => 17592186044416
|
473 | Fn.POW_2_45 => 35184372088832
|
474 | Fn.POW_2_46 => 70368744177664
|
475 | Fn.POW_2_47 => 140737488355328
|
476 | Fn.POW_2_48 => 281474976710656
|
477 | Fn.POW_2_49 => 562949953421312
|
478 | Fn.POW_2_50 => 1125899906842624
|
479 | Fn.POW_2_51 => 2251799813685248
|
480 | Fn.POW_2_52 => 4503599627370496
|
481 | Fn.POW_2_53 => 9007199254740992
|
482 |
|
483 | Fn.u4(value) => 4-bit Unsigned Integer
|
484 | Fn.s4(value) => 4-bit Signed Integer
|
485 |
|
486 | Fn.u8(value) => 8-bit Unsigned Integer
|
487 | Fn.s8(value) => 8-bit Signed Integer
|
488 |
|
489 | Fn.u10(value) => 10-bit Unsigned Integer
|
490 | Fn.s10(value) => 10-bit Signed Integer
|
491 |
|
492 | Fn.u12(value) => 12-bit Unsigned Integer
|
493 | Fn.s12(value) => 12-bit Signed Integer
|
494 |
|
495 | Fn.u16(value) => 16-bit Unsigned Integer
|
496 | Fn.s16(value) => 16-bit Signed Integer
|
497 |
|
498 | Fn.u20(value) => 20-bit Unsigned Integer
|
499 | Fn.s20(value) => 20-bit Signed Integer
|
500 |
|
501 | Fn.u24(value) => 24-bit Unsigned Integer
|
502 | Fn.s24(value) => 24-bit Signed Integer
|
503 |
|
504 | Fn.u32(value) => 32-bit Unsigned Integer
|
505 | Fn.s32(value) => 32-bit Signed Integer
|
506 |
|
507 | }
|
508 | */
|
509 |
|
510 | Fn.RAD_TO_DEG = 180 / Math.PI;
|
511 | Fn.DEG_TO_RAD = Math.PI / 180;
|
512 | Fn.TAU = 2 * Math.PI;
|
513 |
|
514 | module.exports = Fn;
|