UNPKG

14.8 kBJavaScriptView Raw
1var 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 */
18Fn.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
38Fn.map = function(value, fromLow, fromHigh, toLow, toHigh) {
39 return ((value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow) | 0;
40};
41// Alias
42Fn.scale = Fn.map;
43
44/**
45 * Like map, but returns a Float32
46 *
47 * For @param info, @see Fn.map
48 * @return {Float32}
49 */
50var f32A = new Float32Array(1);
51
52Fn.fmap = function(value, fromLow, fromHigh, toLow, toHigh) {
53 f32A[0] = (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow;
54 return f32A[0];
55};
56// Alias
57Fn.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 */
68Fn.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 */
80Fn.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 */
106Fn.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 */
142Fn.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 */
153Fn.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 */
168Fn.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 */
181Fn.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//
221Fn.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 */
268Fn._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 */
278Fn.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 */
291Fn.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 */
302Fn.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 */
316Fn.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 */
328Fn.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 */
342Fn.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 */
356Fn.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 */
367var POW = "POW_2_";
368var U = "u";
369var S = "s";
370var MAX = Fn.bitSize(Number.MAX_SAFE_INTEGER) + 1;
371var 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 */
379for (var i = 0; i < MAX; i++) {
380 Fn[POW + i] = Math.pow(2, i);
381}
382
383bitSizes.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
510Fn.RAD_TO_DEG = 180 / Math.PI;
511Fn.DEG_TO_RAD = Math.PI / 180;
512Fn.TAU = 2 * Math.PI;
513
514module.exports = Fn;