UNPKG

johnny-five

Version:

The JavaScript Robotics and Hardware Programming Framework. Use with: Arduino (all models), Electric Imp, Beagle Bone, Intel Galileo & Edison, Linino One, Pinoccio, pcDuino3, Raspberry Pi, Particle/Spark Core & Photon, Tessel 2, TI Launchpad and more!

471 lines (419 loc) 13.8 kB
const Fn = { debounce: require("lodash.debounce"), cloneDeep: require("lodash.clonedeep"), }; const { ceil, max, min, PI } = Math; /** * Format a number such that it has a given number of digits after the * decimal point. * * @param {Number} number - The number to format * @param {Number} [digits = 0] - The number of digits after the decimal point * @return {Number} Formatted number * @example * Fn.toFixed(5.4564, 2); // -> 5.46 * @example * Fn.toFixed(1.5, 2); // -> 1.5 */ Fn.toFixed = (number, digits) => +(number || 0).toFixed(digits); /** * Map a value (number) from one range to another. Based on Arduino's map(). * * @param {Number} value - value to map * @param {Number} fromLow - low end of originating range * @param {Number} fromHigh - high end of originating range * @param {Number} toLow - low end of target range * @param {Number} toHigh - high end of target range * @return {Number} mapped value (integer) * @example * Fn.map(500, 0, 1000, 0, 255); // -> */ Fn.map = (value, fromLow, fromHigh, toLow, toHigh) => ((value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow) | 0; // Alias Fn.scale = Fn.map; /** * Like map, but returns a Float32 * * For @param info, @see Fn.map * @return {Float32} */ const f32A = new Float32Array(1); Fn.fmap = (value, fromLow, fromHigh, toLow, toHigh) => { f32A[0] = (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; return f32A[0]; }; // Alias Fn.fscale = Fn.fmap; /** * Constrains a number to be within a range. Based on Arduino's constrain() * * @param {Number} value * @param {Number} lower - lower bound of range for constraint * @param {Number} upper - upper bound of range for constraint * @return {Number | NaN} constrained number or NaN if any of the provided * parameters are not a {Number}. */ Fn.constrain = (value, lower, upper) => min(upper, max(lower, value)); /** * Is value between the bounds of lower and upper? * * @param {Number} value * @param {Number} lower - Lower end of bounds to check * @param {Number} upper - Upper ends of bounds to check * @return {Boolean} */ Fn.inRange = (value, lower, upper) => value >= lower && value <= upper; /** * Generate an Array of Numbers with values between lower and upper; the * step (increment/decrement) between each defined by tick. * * @param {Number} lower - The value of the lowest element in the resulting * Array. If `Fn.range` invoked with only one * argument, this parameter will instead define the * length of the Array, which will start from 0. * @param {Number} upper - The value of the final element of the Array. * @param {Number} [tick = 1] - The difference between each element in the * Array. This value may be negative. * @return {Array} of {Numbers} * * @example * Fn.range(5, 10); // -> [5, 6, 7, 8, 9, 10]; * @example * Fn.range(5); // -> [0, 1, 2, 3, 4]; * @example * Fn.range(3, 27, 3); // -> [3, 6, 9, 12, 15, 18, 21, 24, 27]; * @example * Fn.range(0, -9, -3); // -> [0, -3, -6, -9]; */ Fn.range = function(lower, upper, tick) { if (arguments.length === 1) { upper = lower - 1; lower = 0; } lower = lower || 0; upper = upper || 0; tick = tick || 1; const len = max(ceil((upper - lower) / tick), 0); let idx = 0; const range = []; while (idx <= len) { range[idx++] = lower; lower += tick; } return range; }; /** * Generate a reasonably-unique ID string * * @return {String} - 36-character random-ish string */ Fn.uid = () => "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, chr => { const rnd = Math.random() * 16 | 0; return (chr === "x" ? rnd : (rnd & 0x3 | 0x8)).toString(16); }).toUpperCase(); /** * Square your x! * * @param {Number} x * @return {Number| Nan} - x^2—unless you were goofy enough to provide a * non-numeric x, in which case it's NaN for you! */ Fn.square = x => x * x; /** * Get a sum for all the values in an Array. This works best if the elements * in the Array are Numbers. * * @param {Array} values * @return {Number | String} - You probably want a Number so you'll want to * pass a values Array entirely consisting of * numeric elements. */ Fn.sum = function sum(values) { let vals; if (Array.isArray(values)) { vals = values; } else { vals = [].slice.call(arguments); } return vals.reduce((accum, value) => accum + value, 0); }; /** * Fused multiply-add for precise floating-point calculations. */ // fma function // Copyright (c) 2012, Jens Nockert // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // Fn.fma = (a, b, c) => { let aHigh = 134217729 * a; let aLow; aHigh = aHigh + (a - aHigh); aLow = a - aHigh; let bHigh = 134217729 * b; let bLow; bHigh = bHigh + (b - bHigh); bLow = b - bHigh; const r1 = a * b; const r2 = -r1 + aHigh * bHigh + aHigh * bLow + aLow * bHigh + aLow * bLow; const s = r1 + c; const t = (r1 - (s - c)) + (c - (s - r1)); return s + (t + r2); }; // end fma function copyright /** * Return a value with the bit at the position indicated set (to 1). * From avr/io.h "BV" => Bit Value * * An example: logically OR these bits together: * var ORed = _BV(0) | _BV(2) | _BV(7); * * BIT 7 6 5 4 3 2 1 0 * --------------------------------------------------------- * _BV(0) = 0 0 0 0 0 0 0 1 * _BV(2) = 0 0 0 0 0 1 0 0 * _BV(7) = 1 0 0 0 0 0 0 0 * ORed = 1 0 0 0 0 1 0 1 * * ORed === 133; * * @param {Number} bit - bit position to set * @return {Number} * @example * Fn.bitValue(0); // --> 1 * @example * Fn.bitValue(4); // --> 16 * */ Fn._BV = Fn.bitValue = Fn.bv = bit => 1 << bit; /** * int16 Combine two bytes to make an signed 16-bit integer * @param {byte} msb Most signifcant byte * @param {byte} lsb Least signifcant byte * @return {word} Signed 16-bit integer */ Fn.int16 = (msb, lsb) => { const result = (msb << 8) | lsb; // Check highest bit for sign. If on, value is negative return result >> 15 ? ((result ^ 0xFFFF) + 1) * -1 : result; }; /** * uint16 Combine two bytes to make an unsigned 16-bit integer * @param {byte} msb Most signifcant byte * @param {byte} lsb Least signifcant byte * @return {word} unsigned 16-bit integer */ Fn.uint16 = (msb, lsb) => (msb << 8) | lsb; /** * int24 Combine three bytes to make a signed 24-bit integer * @param {byte} b16 b[16:23] * @param {byte} b8 b[8:15] * @param {byte} b0 b[0:7] * @return {word} Signed 24-bit integer */ Fn.int24 = (b16, b8, b0) => { const result = (b16 << 16) | (b8 << 8) | b0; // Check highest bit for sign. If on, value is negative return result >> 23 ? ((result ^ 0xFFFFFF) + 1) * -1 : result; }; /** * uint24 Combine three bytes to make an unsigned 24-bit integer * @param {byte} b16 b[16:23] * @param {byte} b8 b[8:15] * @param {byte} b0 b[0:7] * @return {word} Unsigned 24-bit integer */ Fn.uint24 = (b16, b8, b0) => (b16 << 16) | (b8 << 8) | b0; /** * int32 Combine four bytes to make a signed 24-bit integer * @param {byte} b24 b[24:31] * @param {byte} b16 b[16:23] * @param {byte} b8 b[8:15] * @param {byte} b0 b[0:7] * @return {word} Signed 32-bit integer */ Fn.int32 = (b24, b16, b8, b0) => { const result = (b24 << 24) | (b16 << 16) | (b8 << 8) | b0; // Check highest bit for sign. If on, value is negative return result >> 31 ? ((result ^ 0xFFFFFFFF) + 1) * -1 : result; }; /** * int32 Combine four bytes to make an unsigned 32-bit integer * @param {byte} b24 b[24:31] * @param {byte} b16 b[16:23] * @param {byte} b8 b[8:15] * @param {byte} b0 b[0:7] * @return {Number} unsigned 32-bit integer */ Fn.uint32 = (b24, b16, b8, b0) => // Note: If you left-shift a byte by 24 in JS and that byte's // MSbit is 1, the resulting value will be negative because JS casts // bitwise operands (temporarily) to SIGNED 32-bit numbers. The // final >>> 0 causes the sign bit to be disregarded, making sure our // result is non-negative. ((b24 << 24) | (b16 << 16) | (b8 << 8) | b0) >>> 0; /** * bitSize Get the number of bits in a given number * @param {number} n The number to evaluate * @return {number} The bit count */ Fn.bitSize = n => Math.round(Math.log2(n)); /** * The following generates functions and constants for utility when working * with binary numbers: * - Fn.POW_2_0 through Fn.POW_2_53 * - Fn.u4(value) through Fn.u32(value) * - Fn.s4(value) through Fn.s32(value) */ const POW = "POW_2_"; const U = "u"; const S = "s"; const MAX = Fn.bitSize(Number.MAX_SAFE_INTEGER) + 1; const bitSizes = [ 4, 8, 10, 12, 16, 20, 24, 32 ]; /** * Generate "constants" that represent powers of 2. Available for powers * 0 through 53. * @example * Fn.POW_2_17; // -> 131072 */ for (let i = 0; i < MAX; i++) { Fn[POW + i] = 2 ** i; } bitSizes.forEach(bitSize => { const decimal = Fn[POW + bitSize]; const half = decimal / 2 >>> 0; const halfMinusOne = half - 1; /** * The function Fn["u" + bitSize] will constrain a value to an unsigned * value of that bit size. * * @param {Number} value * @return {Number} constrained to an unsigned int * @example * Fn.u8(255); // --> 255 * Fn.u8(256); // --> 255 * Fn.u8(-255); // --> 0 * Fn.u8(-254); // -- 1 */ Fn[U + bitSize] = value => { if (value < 0) { value += decimal; } return Fn.constrain(value, 0, decimal - 1); }; /** * The function Fn["s" + bitSize] will constrain a value to a signed value * of that bit size. Remember that, e.g., range for signed 8-bit numbers * is -128 to 127. * * @param {Number} value * @return {Number} constrained to a SIGNED integer in bitsize range * @example * Fn.s8(100); // --> 100 * Fn.s8(128); // --> -128 * Fn.s8(127); // --> 127 * Fn.s8(255); // --> -1 */ Fn[S + bitSize] = value => { if (value > halfMinusOne) { value -= decimal; } return Fn.constrain(value, -half, halfMinusOne); }; }); /* Fn.POW_2_0 => 1 Fn.POW_2_1 => 2 Fn.POW_2_2 => 4 Fn.POW_2_3 => 8 Fn.POW_2_4 => 16 Fn.POW_2_5 => 32 Fn.POW_2_6 => 64 Fn.POW_2_7 => 128 Fn.POW_2_8 => 256 Fn.POW_2_9 => 512 Fn.POW_2_10 => 1024 Fn.POW_2_11 => 2048 Fn.POW_2_12 => 4096 Fn.POW_2_13 => 8192 Fn.POW_2_14 => 16384 Fn.POW_2_15 => 32768 Fn.POW_2_16 => 65536 Fn.POW_2_17 => 131072 Fn.POW_2_18 => 262144 Fn.POW_2_19 => 524288 Fn.POW_2_20 => 1048576 Fn.POW_2_21 => 2097152 Fn.POW_2_22 => 4194304 Fn.POW_2_23 => 8388608 Fn.POW_2_24 => 16777216 Fn.POW_2_25 => 33554432 Fn.POW_2_26 => 67108864 Fn.POW_2_27 => 134217728 Fn.POW_2_28 => 268435456 Fn.POW_2_29 => 536870912 Fn.POW_2_30 => 1073741824 Fn.POW_2_31 => 2147483648 Fn.POW_2_32 => 4294967296 Fn.POW_2_33 => 8589934592 Fn.POW_2_34 => 17179869184 Fn.POW_2_35 => 34359738368 Fn.POW_2_36 => 68719476736 Fn.POW_2_37 => 137438953472 Fn.POW_2_38 => 274877906944 Fn.POW_2_39 => 549755813888 Fn.POW_2_40 => 1099511627776 Fn.POW_2_41 => 2199023255552 Fn.POW_2_42 => 4398046511104 Fn.POW_2_43 => 8796093022208 Fn.POW_2_44 => 17592186044416 Fn.POW_2_45 => 35184372088832 Fn.POW_2_46 => 70368744177664 Fn.POW_2_47 => 140737488355328 Fn.POW_2_48 => 281474976710656 Fn.POW_2_49 => 562949953421312 Fn.POW_2_50 => 1125899906842624 Fn.POW_2_51 => 2251799813685248 Fn.POW_2_52 => 4503599627370496 Fn.POW_2_53 => 9007199254740992 Fn.u4(value) => 4-bit Unsigned Integer Fn.s4(value) => 4-bit Signed Integer Fn.u8(value) => 8-bit Unsigned Integer Fn.s8(value) => 8-bit Signed Integer Fn.u10(value) => 10-bit Unsigned Integer Fn.s10(value) => 10-bit Signed Integer Fn.u12(value) => 12-bit Unsigned Integer Fn.s12(value) => 12-bit Signed Integer Fn.u16(value) => 16-bit Unsigned Integer Fn.s16(value) => 16-bit Signed Integer Fn.u20(value) => 20-bit Unsigned Integer Fn.s20(value) => 20-bit Signed Integer Fn.u24(value) => 24-bit Unsigned Integer Fn.s24(value) => 24-bit Signed Integer Fn.u32(value) => 32-bit Unsigned Integer Fn.s32(value) => 32-bit Signed Integer } */ Fn.RAD_TO_DEG = 180 / PI; Fn.DEG_TO_RAD = PI / 180; Fn.TAU = 2 * PI; module.exports = Fn;