Changing display of numbers in Engineering (ENG) mode

Discussions about extensions and frameworks

Changing display of numbers in Engineering (ENG) mode

Postby oliver » Mon Jul 02, 2012 8:31 pm

Further to the discussion in http://forums.naivedesign.com/viewtopic.php?f=2&t=598, here's (the beginning of) a concrete example of changing the display of numbers in a specific mode: Let's pretend you want to change engineering mode so that instead of a power of ten, a SI prefix is used. That is, instead of 2.3e+3, you want to see 2.3 K, etc.

Besides of knowing how to customize display of numbers in general (explained in the link above), the only other thing you need to know is this: the current number display mode is available through calculator.mode.number_representation. The possible values are: "normal", "scientific", "fixed", "engineering".

Your code template to achieve the task, then, is:
Code: Select all
display.stringForNumber_stock = display.stringForNumber;
display.stringForNumber = function(item, n_digits) {
    if (calculator.mode.number_representation == "engineering")
        return myFunc(item);
    else
        return display.stringForNumber_stock(item, n_digits);
}


Now, display in engineering mode is a pretty hard thing, as there's no direct support in JavaScript for this kind of number format. The internal function in MorphEngine for this will be helpful. Here's the code for the stock function:
Code: Select all
   stringForNumber: function(item, n_digits) {
      var numRep = calculator.mode.number_representation;
      
      if (numRep.type == "normal") {
         var str = item.toString();
         ...
         return str;
      }
      else {
         function formattedAsEngineeringIfNecessary(x) { // conversion from scientific to engineering notation
            if (numRep.type == "engineering") {
               var posOfE = x.lastIndexOf('e');
               var exponent = Number(x.slice(posOfE + 1));
               var shiftVal = exponent % 3;
               if (shiftVal) {
                  var isExponentNegative = (shiftVal < 0);
                  if (isExponentNegative) shiftVal += 3;
                  var engExponent = exponent-shiftVal;
                  var dotPos = x.indexOf('.');
                  var hasADot = (dotPos != -1); // in a case like "2e+1", there's no dot
                  if (!hasADot) dotPos = 1;
                  var beginning = x.slice(0, dotPos);
                  var middle = x.slice(dotPos+(hasADot ? 1 : 0), posOfE);
               
                  // zeroes to be inserted, if necessary
                  var zeroes = "";
                  for (var i=0; i<(shiftVal-numRep.val); i++)
                     zeroes += "0";
                  
                  x = beginning + middle + zeroes + x[posOfE] + (isExponentNegative ? "" : x[posOfE+1]) + String(engExponent);
                  if (hasADot)
                     x = x.slice(0, dotPos+shiftVal) + "." + x.slice(dotPos+shiftVal);
               }
            }
            return x;
         }
         function tsep(n,swap) {
            var ts=",", ds="."; // thousands and decimal separators
            if (swap) { ts="."; ds=","; } // swap if requested
            
            var ns = String(n),ps=ns,ss=""; // numString, prefixString, suffixString
            var i = ns.indexOf(".");
            if (i!=-1) { // if ".", then split:
               ps = ns.substring(0,i);
               ss = ds+ns.substring(i+1);
            }
            return ps.replace(/(\d)(?=(\d{3})+([.]|$))/g,"$1"+ts)+ss;
         }
         return (numRep.type == "fixed" ? tsep(item.toFixed(numRep.val), numRep.thousandsSeparator == ".") : formattedAsEngineeringIfNecessary(item.toExponential(numRep.val)));
      }
   }


You could borrow pieces of this code and modify it at the very end, replacing any e[\+\-]\d+ (regex) bits of the result with the corresponding SI prefix.
There's a conversion-related function, calculator.scaleForUnitPrefixFromName, that returns a factor for a given SI prefix name.
Code: Select all
   scaleForUnitPrefixFromName: function(name) { ... var prefix = (name[0] == "'" /*'*/ ? 1 : 0); switch(name[prefix]) { case 'y': return 1e-24; case 'z': return 1e-21; case 'a': return 1e-18; case 'f': return 1e-15; case 'p': return 1e-12; case 'n': return 1e-9; case 'u': return 1e-6; case 'm': return 1e-3; case 'c': return 1e-2; case 'd': return 1e-1; case 'h': return 1e2; case 'k': return 1e3; case 'M': return 1e6; case 'G': return 1e9; case 'T': return 1e12; case 'P': return 1e15; case 'E': return 1e18; case 'Z': return 1e21; case 'Y': return 1e24; } throw Error("unknown SI prefix: " + prefix); }

You want the inverse of this, but this list might be helpful.

An altogether different approach one could take to accomplish this task in MorphEngine is to a) have a DOM change watcher b) have it look for numbers in Engineering format, and do a string replace of the contents swapping again powers of ten with SI prefixes. This would be non-intrusive code that requires no knowledge other than that the stack is an HTML5 document, and be ultimately easier to do for someone familiar with HTML DOM manipulation.

One last thing to consider is not to change engineering mode display, but introduce a new mode to the engine. This is simply accomplished: have a function/key that sets an unused string into calculator.mode.number_representation and act on that value in your overwrite of display.stringForNumber.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Return to Extensions

Who is online

Users browsing this forum: No registered users and 1 guest

cron