Support Angle and Pressure

Issue #3 resolved
Conrad Gende created an issue

It would be nice to be able to work with angle and pressure units. These are derived units, so i think its already possible to represent them with a derived units, but it would be good to have full support.

Here a some of the common ones that I would find useful

For Angle:

  • Radian
  • Degree
  • arcminute
  • arcsecond
  • turn/revolution

A more complete list with uncommon ones: https://en.wikipedia.org/wiki/Angle#Units

For pressure:

  • Pascal
  • PSI
  • Bar
  • mmHg
  • inHg
  • atm

https://en.wikipedia.org/wiki/Pressure#Units

On a side note, there’s basically limitless number of units used in different industries. It would be impossible to satisfy every single use case. It would cool if there was a way for users of this package could add units to an existing measurement. Or even add a whole new measurement. But I’ve got no idea if that is feasible or not.

Comments (5)

  1. Bruce Santier repo owner

    Thanks for the request, Conrad!

    I do have a couple of questions for you related to how you will use the units, in particular the angles.

    TLDR; give me some time, and I’ll build these out. Read on if you’re curious how some of this works, and let me know if you have strong opinions on how angles should work (details towards the end).


    You’re right that you can create these yourself using the library. Originally, I had set out to implement just the seven SI base units, but I do understand that it is quite convenient to add in a few derived units. That’s why the Area and Volume classes were added. That said, I wanted to avoid opening the big scary can of worms that is all of the derived units - you correctly pointed out that there are a huge number of these. Rather than implementing them all, I was hoping to produce a library that could be used to quickly and easily support any unit. If a specific unit was missing, it just meant nobody had implemented it yet (or maybe they just hadn’t shared it yet). I think the main problem here is that my documentation is sparse, so it’s not obvious how to customize the library to your needs. Admittedly, if my goal is to allow anyone to make any unit, I’m doing a poor job :)

    In case you’re curious, here’s a quick rundown on how new units can be added. Angles are unitless (distance divided by distance), so you could create one like so:

    Distance arcLength = pi.meters;
    Distance radius = 1.meters;
    var myAngleInRads = arcLength.per(radius);
    double someTrig = cos(myAngleInRads.as(meters, meters));
    

    However, you wouldn’t have any conversions available to you, making this object somewhat useless. To support conversions, you need what the library currently calls an Interpreter. This class basically encapsulates the relationship between the various units available for a measurement. In this case, for example, it would encode the fact that 2π radians equals 360 degrees equals 1 turn, etc. If you implement one of these, you can actually pass it in to the derived unit via asInterpretedBy() to convert your value:

    const degrees = AngleInterpreter.degrees; // knows how to convert from radians to degrees
    double myAngleInDegrees = myAngleInRads.asInterpretedBy(degrees);
    

    NOTE: I realize now that asInterpretedBy() is in my local changes, it is not yet released to pub.dev - I’ll get it released ASAP.

    The rest is for convenience, i.e. allowing things like Angle.degrees(30) or 300.milli.radians to work and introducing more type safety. We create a class for the measurement type (in this case, Angle) that provides some convenience methods and a default interpreter (in our case, probably radians). One final step is to add our interpreters to the extensions, a one-liner in two places for each unit.


    Regarding angles, there are a few more details to flesh out... please let me know if you have any thoughts on these.

    Angles are often used in mathematical contexts, e.g. to perform vector addition. For these purposes it’s sometimes useful to have a “negative” angle, meaning that it is measured in the opposite direction, so that you can simply say angle1 + angle2 and get final result regardless of which way each angle was measured individually (hopefully that made sense). I’m thinking of allowing negative angles in order to facilitate encoding direction with the measurement, as well as a way to get the “positive” value of any angle in case you want to stick to unsigned values (e.g. -90 degrees would be 270 degrees).

    Further, the trigonometric functions may be useful to anyone using angles. Dart’s built-in functions are effective, but simple - they assume you are providing input in radians. This isn’t as type-safe as I would like: cos(30) may not be what you expect. I plan to provide a way to write something like var myRatio = cos(30.degrees), which would eliminate errors in inputs.

    Finally, there is a lot of terminology around angles, e.g. supplementary angles. I could provide convenience methods for the following:

    Angle myAngle = (pi / 2.0).radians;
    Angle mySupplement = myAngle.supplement();
    bool isMySupplement = myAngle.isSupplement(mySupplement);
    

    …and you could imagine similar methods for conjugate angles, getters for isAcute, isObtuse, etc., some const constructors like Angle.right or Angle.straight, or even ways to measure angles between two planes (!). I don’t plan to support the more complex applications here unless you are really interested in them.

    If there is an application you have in mind that I have not listed here, please let me know so I can prioritize that.


    Thanks again!

  2. Bruce Santier repo owner

    I’ve put some documentation on the process for new measurements and units in the wiki:

    Also, you’ll find an initial implementation of Pressure units in version 2.2.2, which I have just uploaded. Let me know if you notice any issues or anything missing.

  3. Conrad Gende reporter

    Thanks for the detailed response. For my use case, I’m just getting angle data from an external source and displaying it to the user in their preferred unit, so all I need are basic angle conversions. Implementing a custom interpreter should be sufficient for me.

    The pressure units implementation looks good at first glance, but I’ll let you know if I run into anything when I start using it.

    For the more advanced stuff with angles, negative angles would definitely be important for anyone doing quick math with them. For trig functions, I think it would be simple enough to users write their own their own type safe implementations which utilize the built-in functions. And perhaps they are beyond the scope of this package. Plus, it looks like they are alternatives for more advanced use cases eg. https://pub.dev/packages/angles (I haven’t looked too much into these though). And lastly, for the terminology stuff I think stuff like isAcute and isObtuse could easily be implemented as needed by users with extensions.

  4. Bruce Santier repo owner

    OK, version 2.2.3 has basic angle support with an interpreter. This means you should be able to do things like the following:

      var myAngle = 90.degrees.withPrecision(Precision(3));
      print(myAngle); // 90.0 °
      print(myAngle.withDefaultUnit(radians)); // 1.57 rad
    
      var myOtherAngle = Angle.sum([90.degrees, 30.arcMinutes]);
      print(myOtherAngle); // 90.5 °
    
      var myCombinedAngle = myAngle + myOtherAngle;
      print(myCombinedAngle); // 180.5 °
      print(myCombinedAngle.withDefaultUnit(turns)); // 0.501 turn
    

    Again, please let me know if you run into any issues!

  5. Log in to comment