gentooboontoo / js-quantities

JavaScript library for quantity calculation and unit conversion
http://gentooboontoo.github.io/js-quantities/
MIT License
396 stars 103 forks source link

Add feature .toEng() for engineering notation based labels #30

Open bombledmonk opened 9 years ago

bombledmonk commented 9 years ago

This may not be necessary, but it would be nice to have a feature that could normalize labels to powers of 10^(3*n) given the prominence of engineering notation.

Example: Qty('.0001 mA').toEng() // output = 100 uA

sseiche commented 9 years ago

Yep, would be very nice. Currently trying to write this myself, but i'm not very good in JS.

gentooboontoo commented 9 years ago

It would be an interesting feature to implement. I would like it leverages extensibility through formatters (https://github.com/gentooboontoo/js-quantities#formatting-quantities).

var engFormatter = function(scalar, units) { /*...*/ };
// ...
qty.format(engFormatter);
// or by setting engineering notation as default formatter
Qty.formatter = engFormatter;
qty.format();

If this formatter is included in the library, a Qty#toEng helper method could also be added.

librilex commented 7 years ago

I wrote a little code to get this done. It sure isn't pretty, but it gets the deal done. I was not able to create it as a formatter, just as a method. Maybe someone else knows how to do it? This is the way you can use it:

Qty('.0001 mA').toEng() // outputs quantity of '100 nA' (this is correct, 100 µA would be 0.1 mA)
Qty('1200 W').toEng() // outputs quantity of '1.2 MW'
Qty('0.8 W').toEng() // outputs quantity of '800 mW' 

As my toPrefix()-method, this function does not work with unit-less quantities. I used external functions as I rely on them with my toPrefix() as well.

Here is the code:

// Get prefix from unit, eg. 'kW' --> 'k'
function get_prefix(qty) {
    var prefix;
    if (UNITS[qty.numerator[0]][2] == "prefix") {
        prefix = UNITS[qty.numerator[0]][0][0];
    }
    else {
        prefix = "";
    }
    return prefix;
}

// Get unit base, eg. 'kW' --> 'W'
function get_unit_base(qty) {
    var unit_base, prefix;
    prefix = get_prefix(qty);
    if (prefix.length === 1) {
        unit_base = qty.units().substr(1);
    }
    else {
        unit_base = qty.units();
    }
    return unit_base;
}

// Convert unit to defined prefix,
// eg. no prefix: '0.1 kW' --> '100 W'  or  prefix 'm': '0.35 W' --> '350 mW'
function convert_prefix(qty, prefix) {
    var prefix, unit_base, target, qty, q;
    if (prefix == undefined || prefix == null || prefix == 1) {
        prefix = "";
    }
    unit_base = get_unit_base(qty);
    target = Qty(prefix+unit_base);
    q = divSafe(qty.baseScalar, target.baseScalar);
    target = Qty({"scalar": q, "numerator": target.numerator, "denominator": target.denominator});
    return target;
}

// Format quantity to engineering style (only 10^(3*n) prefixes)
function formatEng(scalar, units) {
    var qty, q, subtrhnd, pow_10, unit_base, prefix, target;
    var powerPrefix = {
    "24"  : "<yotta>",
    "21"  : "<zetta>",
    "18"  : "<exa>",
    "15"  : "<peta>",
    "12"  : "<tera>",
     "9"  : "<giga>",
     "6"  : "<mega>",
     "3"  : "<kilo>",
    "-3"  : "<milli>",
    "-6"  : "<micro>",
    "-9"  : "<nano>",
    "-12" : "<pico>",
    "-15" : "<femto>",
    "-18" : "<atto>",
    "-21" : "<zepto>",
    "-24" : "<yocto>"};

    qty = Qty(scalar,units);

    q = divSafe(Math.log(qty.baseScalar), Math.log(1e3));
    if (Number.isInteger(q)) {
        subtrhnd = 0; // so that for example '3 km' does not get converted to '3000 m'
    }
    else {
        subtrhnd = 1;
    }

    pow_10 = 3 * Math.ceil(q - subtrhnd);
    if (pow_10 === -0 || pow_10 === +0) {
        prefix = "";
    }
    else {
        prefix = UNITS[powerPrefix[pow_10]][0][0];
    }

    unit_base = get_unit_base(qty);
    target = Qty(qty.baseScalar * Math.pow(10, -pow_10) + ' ' + prefix + unit_base);
    return target;
}

// ... //

assign(Qty.prototype, {
  // ... //
  toEng: function() {
    return formatEng(this.baseScalar,this.units())
  },
  // ... //
});