├── .travis.yml ├── .gitattributes ├── .gitignore ├── examples ├── elm.json ├── Eur.elm └── Usd.elm ├── AUTHORS ├── src ├── Resistance.elm ├── LuminousFlux.elm ├── Voltage.elm ├── Charge.elm ├── Torque.elm ├── Current.elm ├── Illuminance.elm ├── Energy.elm ├── Luminance.elm ├── Capacitance.elm ├── LuminousIntensity.elm ├── Inductance.elm ├── Speed.elm ├── Force.elm ├── AngularAcceleration.elm ├── Acceleration.elm ├── Pressure.elm ├── Density.elm ├── AngularSpeed.elm ├── Power.elm ├── Mass.elm ├── SubstanceAmount.elm ├── Molarity.elm ├── Pixels.elm ├── SolidAngle.elm ├── Area.elm ├── Constants.elm ├── Duration.elm ├── Angle.elm ├── Length.elm ├── Volume.elm └── Temperature.elm ├── elm.json ├── LICENSE ├── doc └── CustomUnits.md ├── README.md └── tests └── Tests.elm /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elm 2 | dist: focal 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # elm-package generated files 2 | elm-stuff 3 | # elm-repl generated files 4 | repl-temp-* 5 | elm.exe 6 | docs.json 7 | -------------------------------------------------------------------------------- /examples/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | ".", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.0", 8 | "dependencies": { 9 | "direct": { 10 | "elm/core": "1.0.0", 11 | "elm/json": "1.0.0", 12 | "elm/time": "1.0.0" 13 | }, 14 | "indirect": {} 15 | }, 16 | "test-dependencies": { 17 | "direct": {}, 18 | "indirect": {} 19 | } 20 | } -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This is a (likely incomplete) list of the people who have contributed to the 2 | elm-units package. If you submit a patch to this repository, please add your 3 | name to the end of this list! 4 | 5 | Ian Mackenzie 6 | Matthias Devlamynck 7 | Katja Mordaunt 8 | Karim Ulzhabayev <248163264@bk.ru> 9 | Andrew Lenards 10 | Chris Wells Wood 11 | Guilherme Riekes Belmonte 12 | Miguel Carvalho 13 | Carlos Junior Felix Rodrigues 14 | -------------------------------------------------------------------------------- /examples/Eur.elm: -------------------------------------------------------------------------------- 1 | module Eur exposing 2 | ( Cents(..) 3 | , amount 4 | , cents 5 | , euros 6 | , inCents 7 | , inEuros 8 | ) 9 | 10 | import Quantity exposing (Quantity(..)) 11 | 12 | 13 | type Cents 14 | = Cents 15 | 16 | 17 | amount : number -> number -> Quantity number Cents 18 | amount numEuros numCents = 19 | Quantity (numEuros * 100 + numCents) 20 | 21 | 22 | cents : number -> Quantity number Cents 23 | cents numCents = 24 | Quantity numCents 25 | 26 | 27 | inCents : Quantity number Cents -> number 28 | inCents (Quantity numCents) = 29 | numCents 30 | 31 | 32 | euros : number -> Quantity number Cents 33 | euros numEuros = 34 | Quantity (100 * numEuros) 35 | 36 | 37 | inEuros : Quantity Float Cents -> Float 38 | inEuros (Quantity numCents) = 39 | numCents / 100 40 | -------------------------------------------------------------------------------- /examples/Usd.elm: -------------------------------------------------------------------------------- 1 | module Usd exposing 2 | ( Cents(..) 3 | , amount 4 | , cents 5 | , dollars 6 | , inCents 7 | , inDollars 8 | ) 9 | 10 | import Quantity exposing (Quantity(..)) 11 | 12 | 13 | type Cents 14 | = Cents 15 | 16 | 17 | amount : number -> number -> Quantity number Cents 18 | amount numDollars numCents = 19 | Quantity (numDollars * 100 + numCents) 20 | 21 | 22 | cents : number -> Quantity number Cents 23 | cents numCents = 24 | Quantity numCents 25 | 26 | 27 | inCents : Quantity number Cents -> number 28 | inCents (Quantity numCents) = 29 | numCents 30 | 31 | 32 | dollars : number -> Quantity number Cents 33 | dollars numDollars = 34 | Quantity (100 * numDollars) 35 | 36 | 37 | inDollars : Quantity Float Cents -> Float 38 | inDollars (Quantity numCents) = 39 | numCents / 100 40 | -------------------------------------------------------------------------------- /src/Resistance.elm: -------------------------------------------------------------------------------- 1 | module Resistance exposing 2 | ( Resistance, Ohms 3 | , ohms, inOhms 4 | ) 5 | 6 | {-| A `Resistance` value represents an electrical resistance in ohms. 7 | 8 | Note that since `Ohms` is defined as `Rate Volts Amperes` (voltage per unit 9 | current), you can construct a `Resistance` value using `Quantity.per`: 10 | 11 | resistance = 12 | voltage |> Quantity.per current 13 | 14 | You can also do rate-related calculations with `Resistance` values to compute 15 | `Voltage` or `Current`: 16 | 17 | voltage = 18 | current |> Quantity.at resistance 19 | 20 | current = 21 | voltage |> Quantity.at_ resistance 22 | 23 | @docs Resistance, Ohms 24 | 25 | @docs ohms, inOhms 26 | 27 | -} 28 | 29 | import Current exposing (Amperes) 30 | import Quantity exposing (Quantity(..), Rate) 31 | import Voltage exposing (Volts) 32 | 33 | 34 | {-| -} 35 | type alias Ohms = 36 | Rate Volts Amperes 37 | 38 | 39 | {-| -} 40 | type alias Resistance = 41 | Quantity Float Ohms 42 | 43 | 44 | {-| Construct a resistance from a number of ohms. 45 | -} 46 | ohms : Float -> Resistance 47 | ohms numOhms = 48 | Quantity numOhms 49 | 50 | 51 | {-| Convert a resistance to a number of ohms. 52 | -} 53 | inOhms : Resistance -> Float 54 | inOhms (Quantity numOhms) = 55 | numOhms 56 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "ianmackenzie/elm-units", 4 | "summary": "Simple, safe and convenient unit types and conversions for Elm", 5 | "license": "BSD-3-Clause", 6 | "version": "2.10.0", 7 | "exposed-modules": [ 8 | "Acceleration", 9 | "Angle", 10 | "AngularAcceleration", 11 | "AngularSpeed", 12 | "Area", 13 | "Charge", 14 | "Capacitance", 15 | "Current", 16 | "Density", 17 | "Duration", 18 | "Energy", 19 | "Force", 20 | "Illuminance", 21 | "Inductance", 22 | "Length", 23 | "Luminance", 24 | "LuminousFlux", 25 | "LuminousIntensity", 26 | "Mass", 27 | "Molarity", 28 | "Pixels", 29 | "Power", 30 | "Pressure", 31 | "Quantity", 32 | "Resistance", 33 | "SolidAngle", 34 | "Speed", 35 | "SubstanceAmount", 36 | "Temperature", 37 | "Torque", 38 | "Voltage", 39 | "Volume" 40 | ], 41 | "elm-version": "0.19.0 <= v < 0.20.0", 42 | "dependencies": { 43 | "elm/core": "1.0.0 <= v < 2.0.0", 44 | "elm/time": "1.0.0 <= v < 2.0.0" 45 | }, 46 | "test-dependencies": { 47 | "elm-explorations/test": "1.0.0 <= v < 2.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/LuminousFlux.elm: -------------------------------------------------------------------------------- 1 | module LuminousFlux exposing (Lumens, LuminousFlux, lumens, inLumens) 2 | 3 | {-| A `LuminousFlux` value represents the total amount of light emitted by a 4 | light source. You can think of it as roughly "photons per second", although 5 | [it's a bit more complicated than that][wp-luminous-flux]. 6 | 7 | Luminous flux is stored in [lumens][wp-lumen]. It's often used to describe the 8 | total output of a light bulb; for example, a 50 watt incandescent bulb and a 6 9 | watt LED bulb might each have an output of 400 lumens. 10 | 11 | [wp-luminous-flux]: https://en.wikipedia.org/wiki/Luminous_flux 12 | [wp-lumen]: https://en.wikipedia.org/wiki/Lumen_(unit) 13 | 14 | @docs Lumens, LuminousFlux, lumens, inLumens 15 | 16 | -} 17 | 18 | import Quantity exposing (Quantity(..)) 19 | 20 | 21 | {-| -} 22 | type Lumens 23 | = Lumens 24 | 25 | 26 | {-| -} 27 | type alias LuminousFlux = 28 | Quantity Float Lumens 29 | 30 | 31 | {-| Construct a luminous flux value from a number of lumens. See 32 | [here][wp-luminous-flux-examples] and [here][wp-lumen-lighting] for the number 33 | of lumens emitted by some common light sources. 34 | 35 | [wp-luminous-flux-examples]: https://en.wikipedia.org/wiki/Luminous_flux#Examples 36 | [wp-lumen-lighting]: https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting 37 | 38 | -} 39 | lumens : Float -> LuminousFlux 40 | lumens numLumens = 41 | Quantity numLumens 42 | 43 | 44 | {-| Convert a luminous flux value to a number of lumens. 45 | -} 46 | inLumens : LuminousFlux -> Float 47 | inLumens (Quantity numLumens) = 48 | numLumens 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Ian Mackenzie 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/Voltage.elm: -------------------------------------------------------------------------------- 1 | module Voltage exposing 2 | ( Voltage, Volts 3 | , volts, inVolts 4 | ) 5 | 6 | {-| A `Voltage` value represents a voltage (electric potential difference, if 7 | we're being picky) in volts. 8 | 9 | Note that since `Volts` is defined as `Rate Watts Amperes` (power per unit 10 | current), you can do rate-related calculations with `Voltage` values to compute 11 | `Power` or `Current`: 12 | 13 | -- elm-units version of 'P = V * I' 14 | power = 15 | current |> Quantity.at voltage 16 | 17 | -- I = P / V 18 | current = 19 | power |> Quantity.at_ voltage 20 | 21 | Just for fun, note that since you can also [express `Voltage` in terms of 22 | `Current` and `Resistance`](Resistance), you could rewrite the second example 23 | above as 24 | 25 | -- P = I^2 * R 26 | power = 27 | current 28 | |> Quantity.at 29 | (current 30 | |> Quantity.at resistance 31 | ) 32 | 33 | @docs Voltage, Volts 34 | 35 | @docs volts, inVolts 36 | 37 | -} 38 | 39 | import Current exposing (Amperes) 40 | import Power exposing (Watts) 41 | import Quantity exposing (Quantity(..), Rate) 42 | 43 | 44 | {-| -} 45 | type alias Volts = 46 | Rate Watts Amperes 47 | 48 | 49 | {-| -} 50 | type alias Voltage = 51 | Quantity Float Volts 52 | 53 | 54 | {-| Construct a voltage from a number of volts. 55 | -} 56 | volts : Float -> Voltage 57 | volts numVolts = 58 | Quantity numVolts 59 | 60 | 61 | {-| Convert a voltage to a number of volts. 62 | -} 63 | inVolts : Voltage -> Float 64 | inVolts (Quantity numVolts) = 65 | numVolts 66 | -------------------------------------------------------------------------------- /doc/CustomUnits.md: -------------------------------------------------------------------------------- 1 | # Custom Units 2 | 3 | `elm-units` defines many standard unit types, but you can easily define your 4 | own! For example, if you were working on a 2D tile-based game, you might define 5 | a new `Tiles` units type: 6 | 7 | ```elm 8 | module Game 9 | 10 | import Quantity exposing (Quantity(..)) 11 | 12 | {-| Units in the game world 13 | -} 14 | type Tiles = 15 | Tiles 16 | ``` 17 | 18 | You would then add a matching pair of constructor/extractor functions: 19 | 20 | ```elm 21 | {-| Construct a quantity representing a given number of tiles 22 | -} 23 | tiles : number -> Quantity number Tiles 24 | tiles numTiles = 25 | Quantity numTiles 26 | 27 | {-| Convert a quantity of tiles back to a plain Float or Int 28 | -} 29 | inTiles : Quantity number Tiles -> number 30 | inTiles (Quantity numTiles) = 31 | numTiles 32 | ``` 33 | 34 | Note that using the generic `number` type when defining these functions means 35 | that they can be used to define/work with either whole (`Int`) or partial 36 | (`Float`) numbers of tiles. 37 | 38 | Then you can start doing math with tiles, converting safely back and forth 39 | between tiles and pixels, etc.: 40 | 41 | ```elm 42 | import Game exposing (tiles, inTiles) 43 | import Pixels exposing (pixels, inPixels) 44 | import Duration exposing (seconds, milliseconds) 45 | import Quantity 46 | 47 | Quantity.sum [ tiles 5, tiles 2.3, tiles 0.6 ] 48 | --> tiles 7.9 49 | 50 | pixelsPerTile : Quantity Float (Rate Pixels Tiles) 51 | pixelsPerTile = 52 | pixels 24 |> Quantity.per (tiles 1) 53 | 54 | tiles 3 |> Quantity.at pixelsPerTile 55 | --> pixels 72 56 | 57 | speed : Quantity Float (Rate Tiles Seconds) 58 | speed = 59 | tiles 12 |> Quantity.per (seconds 1) 60 | 61 | milliseconds 30 |> Quantity.at speed 62 | --> tiles 0.36 63 | ``` 64 | -------------------------------------------------------------------------------- /src/Charge.elm: -------------------------------------------------------------------------------- 1 | module Charge exposing 2 | ( Charge, Coulombs 3 | , coulombs, inCoulombs, ampereHours, inAmpereHours, milliampereHours, inMilliampereHours 4 | ) 5 | 6 | {-| A `Charge` value represents an electrical charge in coulombs or ampere 7 | hours. It is stored as a number of coulombs. 8 | 9 | @docs Charge, Coulombs 10 | 11 | 12 | ## Conversions 13 | 14 | @docs coulombs, inCoulombs, ampereHours, inAmpereHours, milliampereHours, inMilliampereHours 15 | 16 | -} 17 | 18 | import Constants 19 | import Quantity exposing (Quantity(..)) 20 | 21 | 22 | {-| -} 23 | type Coulombs 24 | = Coulombs 25 | 26 | 27 | {-| -} 28 | type alias Charge = 29 | Quantity Float Coulombs 30 | 31 | 32 | {-| Construct a charge from a number of coulombs. 33 | -} 34 | coulombs : Float -> Charge 35 | coulombs numCoulombs = 36 | Quantity numCoulombs 37 | 38 | 39 | {-| Convert a charge to a number of coulombs. 40 | -} 41 | inCoulombs : Charge -> Float 42 | inCoulombs (Quantity numCoulombs) = 43 | numCoulombs 44 | 45 | 46 | {-| Construct a charge from a number of ampere hours. 47 | -} 48 | ampereHours : Float -> Charge 49 | ampereHours numAmpereHours = 50 | coulombs (Constants.hour * numAmpereHours) 51 | 52 | 53 | {-| Convert a charge to a number of ampere hours. 54 | -} 55 | inAmpereHours : Charge -> Float 56 | inAmpereHours charge = 57 | inCoulombs charge / Constants.hour 58 | 59 | 60 | {-| Construct a charge from a number of milliampere hours. 61 | -} 62 | milliampereHours : Float -> Charge 63 | milliampereHours numMilliampereHours = 64 | coulombs (Constants.hour * numMilliampereHours / 1000) 65 | 66 | 67 | {-| Convert a charge to a number of milliampere hours. 68 | -} 69 | inMilliampereHours : Charge -> Float 70 | inMilliampereHours charge = 71 | inCoulombs charge * 1000 / Constants.hour 72 | -------------------------------------------------------------------------------- /src/Torque.elm: -------------------------------------------------------------------------------- 1 | module Torque exposing 2 | ( Torque, NewtonMeters 3 | , newtonMeters, inNewtonMeters 4 | , poundFeet, inPoundFeet 5 | ) 6 | 7 | {-| Torque is the rotational analogue of linear force. It is also referred to as the moment of force 8 | (also abbreviated to moment). It describes the rate of change of angular momentum that would be 9 | imparted to an isolated body. 10 | 11 | @docs Torque, NewtonMeters 12 | 13 | 14 | ## Metric 15 | 16 | @docs newtonMeters, inNewtonMeters 17 | 18 | 19 | ## Imperial 20 | 21 | @docs poundFeet, inPoundFeet 22 | 23 | -} 24 | 25 | import Constants 26 | import Force exposing (Newtons) 27 | import Length exposing (Meters) 28 | import Quantity exposing (Product, Quantity(..)) 29 | 30 | 31 | {-| NB: You may notice that the type here is exactly the same as for `Energy.Joules`. 32 | 33 | This means the type checker will not help you in distinguishing between these units. 34 | 35 | -} 36 | type alias NewtonMeters = 37 | Product Newtons Meters 38 | 39 | 40 | {-| -} 41 | type alias Torque = 42 | Quantity Float NewtonMeters 43 | 44 | 45 | {-| Construct a torque value from a number of newton-meters. 46 | -} 47 | newtonMeters : Float -> Torque 48 | newtonMeters numNewtonMeters = 49 | Quantity numNewtonMeters 50 | 51 | 52 | {-| Convert a torque value to a number of newton-meters. 53 | -} 54 | inNewtonMeters : Torque -> Float 55 | inNewtonMeters (Quantity numNewtonMeters) = 56 | numNewtonMeters 57 | 58 | 59 | {-| Construct a torque value from a number of pound-feet (sometimes called foot-pounds). 60 | -} 61 | poundFeet : Float -> Torque 62 | poundFeet numPoundFeet = 63 | newtonMeters (Constants.poundFoot * numPoundFeet) 64 | 65 | 66 | {-| Convert a torque value to a number of pound-feet (sometimes called foot-pounds). 67 | -} 68 | inPoundFeet : Torque -> Float 69 | inPoundFeet torque = 70 | inNewtonMeters torque / Constants.poundFoot 71 | -------------------------------------------------------------------------------- /src/Current.elm: -------------------------------------------------------------------------------- 1 | module Current exposing 2 | ( Current, Amperes 3 | , amperes, inAmperes, milliamperes, inMilliamperes 4 | ) 5 | 6 | {-| A `Current` value represents an electrical current in amperes. 7 | 8 | Note that since `Amperes` is defined as `Rate Coulombs Seconds` (charge 9 | per unit time), you can construct a `Current` value using `Quantity.per`: 10 | 11 | current = 12 | charge |> Quantity.per duration 13 | 14 | You can also do rate-related calculations with `Current` values to compute 15 | `Charge` or `Duration`: 16 | 17 | charge = 18 | current |> Quantity.for duration 19 | 20 | alsoCharge = 21 | duration |> Quantity.at current 22 | 23 | duration = 24 | charge |> Quantity.at_ current 25 | 26 | @docs Current, Amperes 27 | 28 | 29 | ## Conversions 30 | 31 | @docs amperes, inAmperes, milliamperes, inMilliamperes 32 | 33 | -} 34 | 35 | import Charge exposing (Coulombs) 36 | import Duration exposing (Seconds) 37 | import Quantity exposing (Quantity(..), Rate) 38 | 39 | 40 | {-| -} 41 | type alias Amperes = 42 | Rate Coulombs Seconds 43 | 44 | 45 | {-| -} 46 | type alias Current = 47 | Quantity Float Amperes 48 | 49 | 50 | {-| Construct a current from a number of amperes. 51 | -} 52 | amperes : Float -> Current 53 | amperes numAmperes = 54 | Quantity numAmperes 55 | 56 | 57 | {-| Convert a current to a number of amperes. 58 | 59 | Charge.coulombs 10 60 | |> Quantity.per (Duration.seconds 2) 61 | |> Current.inAmperes 62 | --> 5 63 | 64 | -} 65 | inAmperes : Current -> Float 66 | inAmperes (Quantity numAmperes) = 67 | numAmperes 68 | 69 | 70 | {-| Construct a current from a number of milliamperes. 71 | 72 | Current.milliamperes 500 73 | --> Current.amperes 0.5 74 | 75 | -} 76 | milliamperes : Float -> Current 77 | milliamperes numMilliamperes = 78 | amperes (numMilliamperes * 1.0e-3) 79 | 80 | 81 | {-| Convert a current to number of milliamperes. 82 | 83 | Current.amperes 2 |> Current.inMilliamperes 84 | --> 2000 85 | 86 | -} 87 | inMilliamperes : Current -> Float 88 | inMilliamperes current = 89 | inAmperes current / 1.0e-3 90 | -------------------------------------------------------------------------------- /src/Illuminance.elm: -------------------------------------------------------------------------------- 1 | module Illuminance exposing 2 | ( Illuminance, Lux 3 | , lux, inLux, footCandles, inFootCandles 4 | ) 5 | 6 | {-| [Illuminance][wp-illuminance] is a measure of how much light is striking a 7 | surface: [luminous flux](LuminousFlux) per unit area. It is measured in 8 | [lux][wp-lux]. 9 | 10 | Illuminance is useful as a measure of how brightly a surface is lit. For 11 | example, on an overcast day, outside surfaces have an illuminance of 12 | approximately 1000 lux; inside an office might be more like 400 lux and under a 13 | full moon might be only 0.2 lux. 14 | 15 | [wp-illuminance]: https://en.wikipedia.org/wiki/Illuminance 16 | [wp-lux]: https://en.wikipedia.org/wiki/Lux> 17 | 18 | @docs Illuminance, Lux 19 | 20 | 21 | ## Conversions 22 | 23 | @docs lux, inLux, footCandles, inFootCandles 24 | 25 | -} 26 | 27 | import Area exposing (SquareMeters) 28 | import LuminousFlux exposing (Lumens) 29 | import Quantity exposing (Quantity(..), Rate) 30 | 31 | 32 | {-| -} 33 | type alias Lux = 34 | Rate Lumens SquareMeters 35 | 36 | 37 | {-| -} 38 | type alias Illuminance = 39 | Quantity Float Lux 40 | 41 | 42 | {-| Construct an illuminance value from a number of lux. One lux is equal to one 43 | lumen per square meter. See [here][wp-lux-illuminance] for a table of 44 | illuminance values in lux for common environments. 45 | 46 | [wp-lux-illuminance]: https://en.wikipedia.org/wiki/Lux#Illuminance 47 | 48 | -} 49 | lux : Float -> Illuminance 50 | lux numLux = 51 | Quantity numLux 52 | 53 | 54 | {-| Convert an illuminance value to a number of lux. 55 | -} 56 | inLux : Illuminance -> Float 57 | inLux (Quantity numLux) = 58 | numLux 59 | 60 | 61 | {-| Construct an illuminance value from a number of 62 | [foot-candles][wp-foot-candles]. One foot-candle is equal to one lumen per 63 | square foot. 64 | 65 | [wp-foot-candles]: https://en.wikipedia.org/wiki/Foot-candle 66 | 67 | -} 68 | footCandles : Float -> Illuminance 69 | footCandles numFootCandles = 70 | LuminousFlux.lumens numFootCandles |> Quantity.per (Area.squareFeet 1) 71 | 72 | 73 | {-| Convert an illuminance value to a number of foot-candles. 74 | -} 75 | inFootCandles : Illuminance -> Float 76 | inFootCandles illuminance = 77 | Area.squareFeet 1 |> Quantity.at illuminance |> LuminousFlux.inLumens 78 | -------------------------------------------------------------------------------- /src/Energy.elm: -------------------------------------------------------------------------------- 1 | module Energy exposing 2 | ( Energy, Joules 3 | , joules, inJoules, kilojoules, inKilojoules, megajoules, inMegajoules, kilowattHours, inKilowattHours 4 | ) 5 | 6 | {-| An `Energy` value represents an amount of energy (or work) in joules, 7 | kilowatt hours etc. It is stored as a number of joules. 8 | 9 | Note that since `Joules` is defined as `Product Newtons Meters`, you can compute 10 | energy directly as a product of force and distance: 11 | 12 | Force.newtons 5 |> Quantity.times (Length.meters 4) 13 | --> Energy.joules 20 14 | 15 | @docs Energy, Joules 16 | 17 | 18 | ## Conversions 19 | 20 | @docs joules, inJoules, kilojoules, inKilojoules, megajoules, inMegajoules, kilowattHours, inKilowattHours 21 | 22 | -} 23 | 24 | import Force exposing (Newtons) 25 | import Length exposing (Meters) 26 | import Quantity exposing (Product, Quantity(..)) 27 | 28 | 29 | {-| -} 30 | type alias Joules = 31 | Product Newtons Meters 32 | 33 | 34 | {-| -} 35 | type alias Energy = 36 | Quantity Float Joules 37 | 38 | 39 | {-| Construct an energy value from a number of joules. 40 | -} 41 | joules : Float -> Energy 42 | joules numJoules = 43 | Quantity numJoules 44 | 45 | 46 | {-| Convert an energy value to a number of joules. 47 | -} 48 | inJoules : Energy -> Float 49 | inJoules (Quantity numJoules) = 50 | numJoules 51 | 52 | 53 | {-| Construct an energy value from a number of kilojoules. 54 | -} 55 | kilojoules : Float -> Energy 56 | kilojoules numKilojoules = 57 | joules (1000 * numKilojoules) 58 | 59 | 60 | {-| Convert an energy value to a number of kilojoules. 61 | -} 62 | inKilojoules : Energy -> Float 63 | inKilojoules energy = 64 | inJoules energy / 1000 65 | 66 | 67 | {-| Construct an energy value from a number of megajoules. 68 | -} 69 | megajoules : Float -> Energy 70 | megajoules numMegajoules = 71 | joules (1.0e6 * numMegajoules) 72 | 73 | 74 | {-| Convert an energy value to a number of megajoules. 75 | -} 76 | inMegajoules : Energy -> Float 77 | inMegajoules energy = 78 | inJoules energy / 1.0e6 79 | 80 | 81 | {-| Construct an energy value from a number of kilowatt hours. 82 | 83 | Energy.kilowattHours 1 84 | --> Energy.megajoules 3.6 85 | 86 | -} 87 | kilowattHours : Float -> Energy 88 | kilowattHours numKilowattHours = 89 | joules (3.6e6 * numKilowattHours) 90 | 91 | 92 | {-| Convert an energy value to a number of kilowatt hours. 93 | -} 94 | inKilowattHours : Energy -> Float 95 | inKilowattHours energy = 96 | inJoules energy / 3.6e6 97 | -------------------------------------------------------------------------------- /src/Luminance.elm: -------------------------------------------------------------------------------- 1 | module Luminance exposing 2 | ( Luminance, Nits 3 | , nits, inNits, footLamberts, inFootLamberts 4 | ) 5 | 6 | {-| [Luminance][wp-luminance] is [luminous intensity](LuminousIntensity) per 7 | unit area or equivalently [illuminance](Illuminance) per [solid 8 | angle](SolidAngle), and is measured in [nits][wp-nits] (or, to use standard SI 9 | terminology, candelas per square meter - the two terms are equivalent). 10 | Luminance is often used to describe the brightness of a particular surface as 11 | viewed from a particular direction; for example, a computer monitor might be 12 | described as having a brightness of 300 nits (but that would likely only be true 13 | when viewing straight on instead of at an angle). See [here][wp-luminance-values] 14 | for some common approximate luminance values. 15 | 16 | [wp-luminance]: https://en.wikipedia.org/wiki/Luminance 17 | [wp-nits]: https://en.wikipedia.org/wiki/Candela_per_square_metre 18 | [wp-luminance-values]: https://en.wikipedia.org/wiki/Orders_of_magnitude_(luminance) 19 | 20 | @docs Luminance, Nits 21 | 22 | 23 | ## Conversions 24 | 25 | @docs nits, inNits, footLamberts, inFootLamberts 26 | 27 | -} 28 | 29 | import Area exposing (SquareMeters) 30 | import LuminousIntensity exposing (Candelas) 31 | import Quantity exposing (Quantity(..), Rate) 32 | 33 | 34 | {-| -} 35 | type alias Nits = 36 | Rate Candelas SquareMeters 37 | 38 | 39 | {-| -} 40 | type alias Luminance = 41 | Quantity Float Nits 42 | 43 | 44 | {-| Construct a luminance value from a number of nits. One nit is equal to one 45 | [candela](LuminousIntensity) per square meter, or equivalently one 46 | [lux](Illuminance) per [steradian](SolidAngle). 47 | -} 48 | nits : Float -> Luminance 49 | nits numNits = 50 | Quantity numNits 51 | 52 | 53 | {-| Convert a luminance value to a number of nits. 54 | -} 55 | inNits : Luminance -> Float 56 | inNits (Quantity numNits) = 57 | numNits 58 | 59 | 60 | {-| Construct a luminance value from a number of 61 | [foot-lamberts][wp-foot-lambert]. 62 | 63 | [wp-foot-lambert]: https://en.wikipedia.org/wiki/Foot-lambert 64 | 65 | -} 66 | footLamberts : Float -> Luminance 67 | footLamberts numFootLamberts = 68 | LuminousIntensity.candelas numFootLamberts 69 | |> Quantity.per (Area.squareFeet pi) 70 | 71 | 72 | {-| Convert a luminance value to a number of foot-lamberts. 73 | -} 74 | inFootLamberts : Luminance -> Float 75 | inFootLamberts luminance = 76 | Area.squareFeet pi 77 | |> Quantity.at luminance 78 | |> LuminousIntensity.inCandelas 79 | -------------------------------------------------------------------------------- /src/Capacitance.elm: -------------------------------------------------------------------------------- 1 | module Capacitance exposing 2 | ( Capacitance, Farads 3 | , farads, inFarads 4 | , picofarads, inPicofarads, nanofarads, inNanofarads, microfarads, inMicrofarads 5 | ) 6 | 7 | {-| A `Capacitance` value represents an electrical capacitance in farads. 8 | 9 | Note that since `Capacitance` is defined as `Rate Coulombs Volts` (charge per 10 | voltage), you can construct a `Capacitance` value using `Quantity.per`: 11 | 12 | capacitance = 13 | charge |> Quantity.per voltage 14 | 15 | You can also compute `Charge` and `Voltage` using `Capacitance`: 16 | 17 | charge = 18 | voltage |> Quantity.at capacitance 19 | 20 | voltage = 21 | charge |> Quantity.at_ capacitance 22 | 23 | @docs Capacitance, Farads 24 | 25 | 26 | ## Conversions 27 | 28 | @docs farads, inFarads 29 | @docs picofarads, inPicofarads, nanofarads, inNanofarads, microfarads, inMicrofarads 30 | 31 | -} 32 | 33 | import Charge exposing (Coulombs) 34 | import Quantity exposing (Quantity(..), Rate) 35 | import Voltage exposing (Volts) 36 | 37 | 38 | {-| -} 39 | type Farads 40 | = Rate Coulombs Volts 41 | 42 | 43 | {-| -} 44 | type alias Capacitance = 45 | Quantity Float Farads 46 | 47 | 48 | {-| Construct capacitance from a number of farads. 49 | -} 50 | farads : Float -> Capacitance 51 | farads numFarads = 52 | Quantity numFarads 53 | 54 | 55 | {-| Convert capacitance to a number of farads. 56 | -} 57 | inFarads : Capacitance -> Float 58 | inFarads (Quantity numFarads) = 59 | numFarads 60 | 61 | 62 | {-| Construct a capacitance from a number of microfarads. 63 | -} 64 | microfarads : Float -> Capacitance 65 | microfarads numMicrofarads = 66 | farads (numMicrofarads * 1.0e-6) 67 | 68 | 69 | {-| Convert a capacitance to a number of microfarads 70 | -} 71 | inMicrofarads : Capacitance -> Float 72 | inMicrofarads capacitance = 73 | inFarads capacitance / 1.0e-6 74 | 75 | 76 | {-| Construct a capacitance from a number of nanofarads 77 | -} 78 | nanofarads : Float -> Capacitance 79 | nanofarads numNanofarads = 80 | farads (numNanofarads * 1.0e-9) 81 | 82 | 83 | {-| Convert a capacitance to a number of nanofarads 84 | -} 85 | inNanofarads : Capacitance -> Float 86 | inNanofarads capacitance = 87 | inFarads capacitance / 1.0e-9 88 | 89 | 90 | {-| Construct capacitance from a number of picofarads. 91 | -} 92 | picofarads : Float -> Capacitance 93 | picofarads numPicofarads = 94 | farads (numPicofarads * 1.0e-12) 95 | 96 | 97 | {-| Convert a capacitance to a number of picofarads. 98 | -} 99 | inPicofarads : Capacitance -> Float 100 | inPicofarads capacitance = 101 | inFarads capacitance / 1.0e-12 102 | -------------------------------------------------------------------------------- /src/LuminousIntensity.elm: -------------------------------------------------------------------------------- 1 | module LuminousIntensity exposing 2 | ( Candelas, LuminousIntensity 3 | , candelas, inCandelas 4 | ) 5 | 6 | {-| [Luminous intensity][wp-luminous-intensity] is a measure of the amount of 7 | light produced ([luminous flux](LuminousFlux)) per unit [solid 8 | angle](SolidAngle). 9 | 10 | Consider a light bulb that emits light in all directions and a spotlight that 11 | only emits light in a cone. If both lights had the same luminous flux (same 12 | total amount of light produced), then the spotlight would have higher luminous 13 | intensity since its light is concentrated into a smaller solid angle (and the 14 | light from the spotlight would appear brighter if viewed from the same 15 | distance). 16 | 17 | On the other hand, if both lights had the same luminous intensity, then they 18 | would appear equally bright when viewed from the same distance (something lit by 19 | the spotlight would appear equally bright as the same object lit by the light 20 | bulb) but the spotlight would have lower luminous flux since its light covers a 21 | smaller solid angle. 22 | 23 | Luminous intensity is measured in [candelas][wp-candelas]. 24 | 25 | [wp-luminous-intensity]: https://en.wikipedia.org/wiki/Luminous_intensity 26 | [wp-candelas]: https://en.wikipedia.org/wiki/Candela 27 | 28 | @docs Candelas, LuminousIntensity 29 | 30 | @docs candelas, inCandelas 31 | 32 | -} 33 | 34 | import LuminousFlux exposing (Lumens) 35 | import Quantity exposing (Quantity(..), Rate) 36 | import SolidAngle exposing (Steradians) 37 | 38 | 39 | {-| -} 40 | type alias Candelas = 41 | Rate Lumens Steradians 42 | 43 | 44 | {-| -} 45 | type alias LuminousIntensity = 46 | Quantity Float Candelas 47 | 48 | 49 | {-| Construct a luminous intensity value from a number of candelas. One candela 50 | is roughly equivalent to the luminous intensity of a single wax candle. 51 | -} 52 | candelas : Float -> LuminousIntensity 53 | candelas numCandelas = 54 | Quantity numCandelas 55 | 56 | 57 | {-| Convert a luminous intensity to a number of candelas. For example, to 58 | compute the luminous intensity of a light bulb with an output of 470 lumens 59 | which emits light equally in all directions: 60 | 61 | LuminousFlux.lumens 470 62 | |> Quantity.per (SolidAngle.spats 1) 63 | |> LuminousIntensity.inCandelas 64 | --> 37.4014 65 | 66 | If the same amount of light was emitted over a hemisphere instead of a full 67 | sphere, the luminous intensity would be twice as great: 68 | 69 | LuminousFlux.lumens 470 70 | |> Quantity.per (SolidAngle.spats 0.5) 71 | |> LuminousIntensity.inCandelas 72 | --> 74.8028 73 | 74 | -} 75 | inCandelas : LuminousIntensity -> Float 76 | inCandelas (Quantity numCandelas) = 77 | numCandelas 78 | -------------------------------------------------------------------------------- /src/Inductance.elm: -------------------------------------------------------------------------------- 1 | module Inductance exposing 2 | ( Inductance, Henries(..) 3 | , henries, inHenries 4 | , nanohenries, inNanohenries, microhenries, inMicrohenries, millihenries, inMillihenries, kilohenries, inKilohenries 5 | ) 6 | 7 | {-| A `Inductance` value represents an electrical inductance in henries. 8 | 9 | @docs Inductance, Henries 10 | 11 | 12 | ## Conversions 13 | 14 | @docs henries, inHenries 15 | @docs nanohenries, inNanohenries, microhenries, inMicrohenries, millihenries, inMillihenries, kilohenries, inKilohenries 16 | 17 | -} 18 | 19 | import Current exposing (Amperes) 20 | import Duration exposing (Seconds) 21 | import Quantity exposing (Quantity(..), Rate) 22 | import Voltage exposing (Volts) 23 | 24 | 25 | {-| -} 26 | type Henries 27 | = Rate Volts (Rate Amperes Seconds) 28 | 29 | 30 | {-| -} 31 | type alias Inductance = 32 | Quantity Float Henries 33 | 34 | 35 | {-| Construct an inductance from a number of henries. 36 | -} 37 | henries : Float -> Inductance 38 | henries numHenries = 39 | Quantity numHenries 40 | 41 | 42 | {-| Convert an inductance to a number of henries. 43 | -} 44 | inHenries : Inductance -> Float 45 | inHenries (Quantity numHneries) = 46 | numHneries 47 | 48 | 49 | {-| Construct an inductance from a number of millihenries. 50 | -} 51 | millihenries : Float -> Inductance 52 | millihenries numMilliHenries = 53 | henries (numMilliHenries * 1.0e-3) 54 | 55 | 56 | {-| Convert an inductance to a number of millihenries. 57 | -} 58 | inMillihenries : Inductance -> Float 59 | inMillihenries inductance = 60 | inHenries inductance / 1.0e-3 61 | 62 | 63 | {-| Construct an inductance from a number of microhenries. 64 | -} 65 | microhenries : Float -> Inductance 66 | microhenries numMicroHenries = 67 | henries (numMicroHenries * 1.0e-6) 68 | 69 | 70 | {-| Convert an inductance to a number of microhenries. 71 | -} 72 | inMicrohenries : Inductance -> Float 73 | inMicrohenries inductance = 74 | inHenries inductance / 1.0e-6 75 | 76 | 77 | {-| Construct an inductance from a number of nanohenries. 78 | -} 79 | nanohenries : Float -> Inductance 80 | nanohenries numNanoHenries = 81 | henries (numNanoHenries * 1.0e-9) 82 | 83 | 84 | {-| Convert an inductance to a number of nanohenries. 85 | -} 86 | inNanohenries : Inductance -> Float 87 | inNanohenries inductance = 88 | inHenries inductance / 1.0e-9 89 | 90 | 91 | {-| Construct an inductance from a number of kilohenries. 92 | -} 93 | kilohenries : Float -> Inductance 94 | kilohenries numKiloHenries = 95 | henries (numKiloHenries * 1.0e3) 96 | 97 | 98 | {-| Convert an inductance to a number of kilohenries. 99 | -} 100 | inKilohenries : Inductance -> Float 101 | inKilohenries inductance = 102 | inHenries inductance / 1.0e3 103 | -------------------------------------------------------------------------------- /src/Speed.elm: -------------------------------------------------------------------------------- 1 | module Speed exposing 2 | ( Speed, MetersPerSecond 3 | , metersPerSecond, inMetersPerSecond, kilometersPerHour, inKilometersPerHour 4 | , feetPerSecond, inFeetPerSecond, milesPerHour, inMilesPerHour 5 | ) 6 | 7 | {-| A `Speed` value represents a speed in meters per second, miles per hour etc. 8 | It is stored as a number of meters per second. 9 | 10 | Note that since `MetersPerSecond` is defined as `Rate Meters Seconds` (length 11 | per unit time), you can construct a `Speed` value using `Quantity.per`: 12 | 13 | speed = 14 | length |> Quantity.per duration 15 | 16 | You can also do rate-related calculations with `Speed` values to compute 17 | `Length` or `Duration`: 18 | 19 | length = 20 | speed |> Quantity.for duration 21 | 22 | alsoLength = 23 | duration |> Quantity.at speed 24 | 25 | duration = 26 | length |> Quantity.at_ speed 27 | 28 | @docs Speed, MetersPerSecond 29 | 30 | 31 | ## Metric 32 | 33 | @docs metersPerSecond, inMetersPerSecond, kilometersPerHour, inKilometersPerHour 34 | 35 | 36 | ## Imperial 37 | 38 | @docs feetPerSecond, inFeetPerSecond, milesPerHour, inMilesPerHour 39 | 40 | -} 41 | 42 | import Constants 43 | import Duration exposing (Seconds) 44 | import Length exposing (Meters) 45 | import Quantity exposing (Quantity(..), Rate) 46 | 47 | 48 | {-| -} 49 | type alias MetersPerSecond = 50 | Rate Meters Seconds 51 | 52 | 53 | {-| -} 54 | type alias Speed = 55 | Quantity Float MetersPerSecond 56 | 57 | 58 | {-| Construct a speed from a number of meters per second. 59 | -} 60 | metersPerSecond : Float -> Speed 61 | metersPerSecond numMetersPerSecond = 62 | Quantity numMetersPerSecond 63 | 64 | 65 | {-| Convert a speed to a number of meters per second. 66 | -} 67 | inMetersPerSecond : Speed -> Float 68 | inMetersPerSecond (Quantity numMetersPerSecond) = 69 | numMetersPerSecond 70 | 71 | 72 | {-| Construct a speed from a number of feet per second. 73 | -} 74 | feetPerSecond : Float -> Speed 75 | feetPerSecond numFeetPerSecond = 76 | metersPerSecond (Constants.foot * numFeetPerSecond) 77 | 78 | 79 | {-| Convert a speed to a number of feet per second. 80 | -} 81 | inFeetPerSecond : Speed -> Float 82 | inFeetPerSecond speed = 83 | inMetersPerSecond speed / Constants.foot 84 | 85 | 86 | {-| Construct a speed from a number of kilometers per hour. 87 | -} 88 | kilometersPerHour : Float -> Speed 89 | kilometersPerHour numKilometersPerHour = 90 | metersPerSecond (numKilometersPerHour * 1000 / Constants.hour) 91 | 92 | 93 | {-| Convert a speed to a number of kilometers per hour. 94 | -} 95 | inKilometersPerHour : Speed -> Float 96 | inKilometersPerHour speed = 97 | Constants.hour * inMetersPerSecond speed * 0.001 98 | 99 | 100 | {-| Construct a speed from a number of miles per hour. 101 | -} 102 | milesPerHour : Float -> Speed 103 | milesPerHour numMilesPerHour = 104 | metersPerSecond (numMilesPerHour * Constants.mile / Constants.hour) 105 | 106 | 107 | {-| Convert a speed to a number of miles per hour. 108 | -} 109 | inMilesPerHour : Speed -> Float 110 | inMilesPerHour speed = 111 | (Constants.hour / Constants.mile) * inMetersPerSecond speed 112 | -------------------------------------------------------------------------------- /src/Force.elm: -------------------------------------------------------------------------------- 1 | module Force exposing 2 | ( Force, Newtons 3 | , newtons, inNewtons, kilonewtons, inKilonewtons, meganewtons, inMeganewtons 4 | , pounds, inPounds, kips, inKips 5 | ) 6 | 7 | {-| A `Force` value represents a force in newtons, pounds force etc. It is 8 | stored as a number of newtons. 9 | 10 | Note that since `Newtons` is defined as `Product Kilograms 11 | MetersPerSecondSquared`, you can compute force directly as a product of mass and 12 | acceleration: 13 | 14 | mass = 15 | Mass.kilograms 10 16 | 17 | acceleration = 18 | Acceleration.metersPerSecondSquared 2 19 | 20 | mass |> Quantity.times acceleration 21 | --> Force.newtons 20 22 | 23 | @docs Force, Newtons 24 | 25 | 26 | ## Metric 27 | 28 | @docs newtons, inNewtons, kilonewtons, inKilonewtons, meganewtons, inMeganewtons 29 | 30 | 31 | ## Imperial 32 | 33 | @docs pounds, inPounds, kips, inKips 34 | 35 | -} 36 | 37 | import Acceleration exposing (MetersPerSecondSquared) 38 | import Constants 39 | import Mass exposing (Kilograms) 40 | import Quantity exposing (Product, Quantity(..)) 41 | 42 | 43 | {-| -} 44 | type alias Newtons = 45 | Product Kilograms MetersPerSecondSquared 46 | 47 | 48 | {-| -} 49 | type alias Force = 50 | Quantity Float Newtons 51 | 52 | 53 | {-| Construct a force value from a number of newtons. 54 | -} 55 | newtons : Float -> Force 56 | newtons numNewtons = 57 | Quantity numNewtons 58 | 59 | 60 | {-| Convert a force value to a number of newtons. 61 | -} 62 | inNewtons : Force -> Float 63 | inNewtons (Quantity numNewtons) = 64 | numNewtons 65 | 66 | 67 | {-| Construct a force value from a number of kilonewtons. 68 | -} 69 | kilonewtons : Float -> Force 70 | kilonewtons numKilonewtons = 71 | newtons (1000 * numKilonewtons) 72 | 73 | 74 | {-| Convert a force value to a number of kilonewtons. 75 | -} 76 | inKilonewtons : Force -> Float 77 | inKilonewtons force = 78 | inNewtons force / 1000 79 | 80 | 81 | {-| Construct a force value from a number of meganewtons. 82 | -} 83 | meganewtons : Float -> Force 84 | meganewtons numMeganewtons = 85 | newtons (1.0e6 * numMeganewtons) 86 | 87 | 88 | {-| Convert a force value to a number of meganewtons. 89 | -} 90 | inMeganewtons : Force -> Float 91 | inMeganewtons force = 92 | inNewtons force / 1.0e6 93 | 94 | 95 | {-| Construct a force value from a number of pounds force. One pound force is 96 | the force required to accelerate one [pound mass][1] at a rate of [one gee][2]. 97 | 98 | [1]: Mass#pounds 99 | [2]: Acceleration#gees 100 | 101 | -} 102 | pounds : Float -> Force 103 | pounds numPounds = 104 | newtons (Constants.poundForce * numPounds) 105 | 106 | 107 | {-| Convert a force value to a number of pounds force. 108 | -} 109 | inPounds : Force -> Float 110 | inPounds force = 111 | inNewtons force / Constants.poundForce 112 | 113 | 114 | {-| Construct a force value from a number of kips (kilopounds force). 115 | 116 | Force.kips 2 117 | --> Force.pounds 2000 118 | 119 | -} 120 | kips : Float -> Force 121 | kips numKips = 122 | pounds (1000 * numKips) 123 | 124 | 125 | {-| Convert a force value to a number of kips. 126 | -} 127 | inKips : Force -> Float 128 | inKips force = 129 | inPounds force / 1000 130 | -------------------------------------------------------------------------------- /src/AngularAcceleration.elm: -------------------------------------------------------------------------------- 1 | module AngularAcceleration exposing 2 | ( AngularAcceleration, RadiansPerSecondSquared 3 | , radiansPerSecondSquared, inRadiansPerSecondSquared, degreesPerSecondSquared, inDegreesPerSecondSquared 4 | , turnsPerSecondSquared, inTurnsPerSecondSquared 5 | ) 6 | 7 | {-| An `AngularAcceleration` represents an angular acceleration in radians per 8 | second squared, degrees per second squared, and turns per second squared. It is 9 | stored as a number of radians per second squared. 10 | 11 | Note that since `RadiansPerSecondSquared` is defined as `Rate RadiansPerSecond 12 | Seconds` (change in angular speed per unit time), you can construct an 13 | `AngularAcceleration` value using `Quantity.per`: 14 | 15 | angularAcceleration = 16 | changeInAngularSpeed |> Quantity.per duration 17 | 18 | You can also do rate-related calculations with `AngularAcceleration` values to 19 | compute `AngularSpeed` or `Duration`: 20 | 21 | changeInAngularSpeed = 22 | angularAcceleration |> Quantity.for duration 23 | 24 | alsoChangeInAngularSpeed = 25 | duration |> Quantity.at angularAcceleration 26 | 27 | duration = 28 | changeInAngularSpeed |> Quantity.at_ angularAcceleration 29 | 30 | @docs AngularAcceleration, RadiansPerSecondSquared 31 | 32 | 33 | ## Conversions 34 | 35 | @docs radiansPerSecondSquared, inRadiansPerSecondSquared, degreesPerSecondSquared, inDegreesPerSecondSquared 36 | @docs turnsPerSecondSquared, inTurnsPerSecondSquared 37 | 38 | -} 39 | 40 | import Angle exposing (Radians) 41 | import AngularSpeed exposing (RadiansPerSecond) 42 | import Duration exposing (Seconds) 43 | import Quantity exposing (Quantity(..), Rate) 44 | 45 | 46 | {-| -} 47 | type alias RadiansPerSecondSquared = 48 | Rate RadiansPerSecond Seconds 49 | 50 | 51 | {-| -} 52 | type alias AngularAcceleration = 53 | Quantity Float RadiansPerSecondSquared 54 | 55 | 56 | {-| Construct an angular acceleration from a number of radians per second squared. 57 | -} 58 | radiansPerSecondSquared : Float -> AngularAcceleration 59 | radiansPerSecondSquared numRadiansPerSecondSquared = 60 | Quantity numRadiansPerSecondSquared 61 | 62 | 63 | {-| Convert an angular acceleration to a number of radians per second squared. 64 | -} 65 | inRadiansPerSecondSquared : AngularAcceleration -> Float 66 | inRadiansPerSecondSquared (Quantity numRadiansPerSecondSquared) = 67 | numRadiansPerSecondSquared 68 | 69 | 70 | {-| Construct an angular acceleration from a number of degrees per second squared. 71 | -} 72 | degreesPerSecondSquared : Float -> AngularAcceleration 73 | degreesPerSecondSquared numDegreesPerSecondSquared = 74 | radiansPerSecondSquared (pi / 180 * numDegreesPerSecondSquared) 75 | 76 | 77 | {-| Convert an angular acceleration to a number of degrees per second squared. 78 | -} 79 | inDegreesPerSecondSquared : AngularAcceleration -> Float 80 | inDegreesPerSecondSquared angularAcceleration = 81 | inRadiansPerSecondSquared angularAcceleration / (pi / 180) 82 | 83 | 84 | {-| Construct an angular acceleration from a number of turns per second squared. 85 | -} 86 | turnsPerSecondSquared : Float -> AngularAcceleration 87 | turnsPerSecondSquared numTurnsPerSecondSquared = 88 | radiansPerSecondSquared (2 * pi * numTurnsPerSecondSquared) 89 | 90 | 91 | {-| Convert an angular acceleration to a number of turns per second squared. 92 | -} 93 | inTurnsPerSecondSquared : AngularAcceleration -> Float 94 | inTurnsPerSecondSquared angularAcceleration = 95 | inRadiansPerSecondSquared angularAcceleration / (2 * pi) 96 | -------------------------------------------------------------------------------- /src/Acceleration.elm: -------------------------------------------------------------------------------- 1 | module Acceleration exposing 2 | ( Acceleration, MetersPerSecondSquared 3 | , metersPerSecondSquared, inMetersPerSecondSquared 4 | , feetPerSecondSquared, inFeetPerSecondSquared 5 | , gees, inGees 6 | ) 7 | 8 | {-| An `Acceleration` represents an acceleration in meters per second squared, 9 | feet per second squared or [gees][1]. It is stored as a number of meters per 10 | second squared. 11 | 12 | Note that since `MetersPerSecondSquared` is defined as `Rate MetersPerSecond 13 | Seconds` (change in speed per unit time), you can construct an `Acceleration` 14 | value using `Quantity.per`: 15 | 16 | acceleration = 17 | changeInSpeed |> Quantity.per duration 18 | 19 | You can also do rate-related calculations with `Acceleration` values to compute 20 | `Speed` or `Duration`: 21 | 22 | changeInSpeed = 23 | acceleration |> Quantity.for duration 24 | 25 | alsoChangeInSpeed = 26 | duration |> Quantity.at acceleration 27 | 28 | duration = 29 | changeInSpeed |> Quantity.at_ acceleration 30 | 31 | [1]: https://en.wikipedia.org/wiki/G-force#Unit_and_measurement 32 | 33 | @docs Acceleration, MetersPerSecondSquared 34 | 35 | 36 | ## Metric 37 | 38 | @docs metersPerSecondSquared, inMetersPerSecondSquared 39 | 40 | 41 | ## Imperial 42 | 43 | @docs feetPerSecondSquared, inFeetPerSecondSquared 44 | 45 | 46 | ## Gravitational 47 | 48 | @docs gees, inGees 49 | 50 | -} 51 | 52 | import Constants 53 | import Duration exposing (Seconds) 54 | import Length exposing (Meters) 55 | import Quantity exposing (Quantity(..), Rate) 56 | import Speed exposing (MetersPerSecond) 57 | 58 | 59 | {-| -} 60 | type alias MetersPerSecondSquared = 61 | Rate MetersPerSecond Seconds 62 | 63 | 64 | {-| -} 65 | type alias Acceleration = 66 | Quantity Float MetersPerSecondSquared 67 | 68 | 69 | {-| Construct an acceleration from a number of meters per second squared. 70 | -} 71 | metersPerSecondSquared : Float -> Acceleration 72 | metersPerSecondSquared numMetersPerSecondSquared = 73 | Quantity numMetersPerSecondSquared 74 | 75 | 76 | {-| Convert an acceleration to a number of meters per second squared. 77 | -} 78 | inMetersPerSecondSquared : Acceleration -> Float 79 | inMetersPerSecondSquared (Quantity numMetersPerSecondSquared) = 80 | numMetersPerSecondSquared 81 | 82 | 83 | {-| Construct an acceleration from a number of feet per second squared. 84 | -} 85 | feetPerSecondSquared : Float -> Acceleration 86 | feetPerSecondSquared numFeetPerSecondSquared = 87 | metersPerSecondSquared (Constants.foot * numFeetPerSecondSquared) 88 | 89 | 90 | {-| Convert an acceleration to a number of feet per second squared. 91 | -} 92 | inFeetPerSecondSquared : Acceleration -> Float 93 | inFeetPerSecondSquared acceleration = 94 | inMetersPerSecondSquared acceleration / Constants.foot 95 | 96 | 97 | {-| Construct an acceleration from a number of [gees][1]. One gee is equal to 98 | 9.80665 meters per second squared (the standard acceleration due to gravity). 99 | 100 | Acceleration.gees 1 101 | --> Acceleration.metersPerSecondSquared 9.80665 102 | 103 | [1]: https://en.wikipedia.org/wiki/G-force#Unit_and_measurement 104 | 105 | -} 106 | gees : Float -> Acceleration 107 | gees numGees = 108 | metersPerSecondSquared (Constants.gee * numGees) 109 | 110 | 111 | {-| Convert an acceleration to a number of gees. 112 | -} 113 | inGees : Acceleration -> Float 114 | inGees acceleration = 115 | inMetersPerSecondSquared acceleration / Constants.gee 116 | -------------------------------------------------------------------------------- /src/Pressure.elm: -------------------------------------------------------------------------------- 1 | module Pressure exposing 2 | ( Pressure, Pascals 3 | , pascals, inPascals, kilopascals, inKilopascals, megapascals, inMegapascals 4 | , poundsPerSquareInch, inPoundsPerSquareInch 5 | , atmospheres, inAtmospheres 6 | ) 7 | 8 | {-| A `Pressure` value represents a pressure in kilopascals, pounds per square 9 | inch, [atmospheres][1] etc. It is stored as a number of pascals. 10 | 11 | Note that since `Pascals` is defined as `Rate Newtons SquareMeters` (force per 12 | unit area), you can construct a `Pressure` value using `Quantity.per`: 13 | 14 | pressure = 15 | force |> Quantity.per area 16 | 17 | You can also do rate-related calculations with `Pressure` values to compute 18 | `Force` or `Area`: 19 | 20 | force = 21 | area |> Quantity.at pressure 22 | 23 | area = 24 | force |> Quantity.at_ pressure 25 | 26 | [1]: https://en.wikipedia.org/wiki/Atmosphere_(unit) 27 | 28 | @docs Pressure, Pascals 29 | 30 | 31 | ## Metric 32 | 33 | @docs pascals, inPascals, kilopascals, inKilopascals, megapascals, inMegapascals 34 | 35 | 36 | ## Imperial 37 | 38 | @docs poundsPerSquareInch, inPoundsPerSquareInch 39 | 40 | 41 | ## Atmospheric 42 | 43 | @docs atmospheres, inAtmospheres 44 | 45 | -} 46 | 47 | import Area exposing (SquareMeters) 48 | import Constants 49 | import Force exposing (Newtons) 50 | import Quantity exposing (Quantity(..), Rate) 51 | 52 | 53 | {-| -} 54 | type alias Pascals = 55 | Rate Newtons SquareMeters 56 | 57 | 58 | {-| -} 59 | type alias Pressure = 60 | Quantity Float Pascals 61 | 62 | 63 | {-| Construct a pressure from a number of pascals. 64 | -} 65 | pascals : Float -> Pressure 66 | pascals numPascals = 67 | Quantity numPascals 68 | 69 | 70 | {-| Convert a pressure to a number of pascals. 71 | -} 72 | inPascals : Pressure -> Float 73 | inPascals (Quantity numPascals) = 74 | numPascals 75 | 76 | 77 | {-| Construct a pressure from a number of kilopascals. 78 | -} 79 | kilopascals : Float -> Pressure 80 | kilopascals numKilopascals = 81 | pascals (1000 * numKilopascals) 82 | 83 | 84 | {-| Convert a pressure to a number of kilopascals. 85 | -} 86 | inKilopascals : Pressure -> Float 87 | inKilopascals pressure = 88 | inPascals pressure / 1000 89 | 90 | 91 | {-| Construct a pressure from a number of megapascals. 92 | -} 93 | megapascals : Float -> Pressure 94 | megapascals numMegapascals = 95 | pascals (1.0e6 * numMegapascals) 96 | 97 | 98 | {-| Convert a pressure to a number of megapascals. 99 | -} 100 | inMegapascals : Pressure -> Float 101 | inMegapascals pressure = 102 | inPascals pressure / 1.0e6 103 | 104 | 105 | {-| Construct a pressure from a number of pounds per square inch. 106 | -} 107 | poundsPerSquareInch : Float -> Pressure 108 | poundsPerSquareInch value = 109 | Force.pounds value |> Quantity.per (Area.squareInches 1) 110 | 111 | 112 | {-| Convert a pressure to a number of pounds per square inch. 113 | -} 114 | inPoundsPerSquareInch : Pressure -> Float 115 | inPoundsPerSquareInch pressure = 116 | Area.squareInches 1 |> Quantity.at pressure |> Force.inPounds 117 | 118 | 119 | {-| Construct a pressure from a number of [atmospheres][1]. 120 | 121 | [1]: https://en.wikipedia.org/wiki/Atmosphere_(unit) 122 | 123 | -} 124 | atmospheres : Float -> Pressure 125 | atmospheres numAtmospheres = 126 | pascals (Constants.atmosphere * numAtmospheres) 127 | 128 | 129 | {-| Convert a pressure to a number of atmospheres. 130 | -} 131 | inAtmospheres : Pressure -> Float 132 | inAtmospheres pressure = 133 | inPascals pressure / Constants.atmosphere 134 | -------------------------------------------------------------------------------- /src/Density.elm: -------------------------------------------------------------------------------- 1 | module Density exposing 2 | ( Density, KilogramsPerCubicMeter 3 | , kilogramsPerCubicMeter, inKilogramsPerCubicMeter, gramsPerCubicCentimeter, inGramsPerCubicCentimeter 4 | , poundsPerCubicInch, inPoundsPerCubicInch, poundsPerCubicFoot, inPoundsPerCubicFoot 5 | ) 6 | 7 | {-| A `Density` value represents a density in grams per cubic centimeter, pounds 8 | per cubic inch, etc. It is stored as a number of kilograms per cubic meter. 9 | 10 | Note that since `KilogramsPerCubicMeter` is defined as `Rate Kilograms 11 | CubicMeters` (mass per unit volume), you can construct a `Density` value using 12 | `Quantity.per`: 13 | 14 | density = 15 | mass |> Quantity.per volume 16 | 17 | You can also do rate-related calculations with `Density` values to compute 18 | `Mass` or `Volume`: 19 | 20 | mass = 21 | volume |> Quantity.at density 22 | 23 | volume = 24 | mass |> Quantity.at_ density 25 | 26 | @docs Density, KilogramsPerCubicMeter 27 | 28 | 29 | ## Metric 30 | 31 | @docs kilogramsPerCubicMeter, inKilogramsPerCubicMeter, gramsPerCubicCentimeter, inGramsPerCubicCentimeter 32 | 33 | 34 | ## Imperial 35 | 36 | @docs poundsPerCubicInch, inPoundsPerCubicInch, poundsPerCubicFoot, inPoundsPerCubicFoot 37 | 38 | -} 39 | 40 | import Constants 41 | import Mass exposing (Kilograms) 42 | import Quantity exposing (Quantity(..), Rate) 43 | import Volume exposing (CubicMeters) 44 | 45 | 46 | {-| -} 47 | type alias KilogramsPerCubicMeter = 48 | Rate Kilograms CubicMeters 49 | 50 | 51 | {-| -} 52 | type alias Density = 53 | Quantity Float KilogramsPerCubicMeter 54 | 55 | 56 | {-| Construct a density from a number of kilograms per cubic meter. 57 | -} 58 | kilogramsPerCubicMeter : Float -> Density 59 | kilogramsPerCubicMeter numKilogramsPerCubicMeter = 60 | Quantity numKilogramsPerCubicMeter 61 | 62 | 63 | {-| Convert a density to a number of kilograms per cubic meter. 64 | -} 65 | inKilogramsPerCubicMeter : Density -> Float 66 | inKilogramsPerCubicMeter (Quantity numKilogramsPerCubicMeter) = 67 | numKilogramsPerCubicMeter 68 | 69 | 70 | {-| Construct a density from a number of grams per cubic centimeter. 71 | -} 72 | gramsPerCubicCentimeter : Float -> Density 73 | gramsPerCubicCentimeter numGramsPerCubicCentimeter = 74 | kilogramsPerCubicMeter (1000 * numGramsPerCubicCentimeter) 75 | 76 | 77 | {-| Convert a density to a number of grams per cubic centimeter. 78 | -} 79 | inGramsPerCubicCentimeter : Density -> Float 80 | inGramsPerCubicCentimeter density = 81 | inKilogramsPerCubicMeter density / 1000 82 | 83 | 84 | {-| Construct a density from a number of pounds per cubic inch. 85 | -} 86 | poundsPerCubicInch : Float -> Density 87 | poundsPerCubicInch numPoundsPerCubicInch = 88 | kilogramsPerCubicMeter (Constants.pound / Constants.cubicInch * numPoundsPerCubicInch) 89 | 90 | 91 | {-| Convert a density to a number of pounds per cubic inch. 92 | -} 93 | inPoundsPerCubicInch : Density -> Float 94 | inPoundsPerCubicInch density = 95 | inKilogramsPerCubicMeter density / (Constants.pound / Constants.cubicInch) 96 | 97 | 98 | {-| Construct a density from a number of pounds per cubic foot. 99 | -} 100 | poundsPerCubicFoot : Float -> Density 101 | poundsPerCubicFoot numPoundsPerCubicFoot = 102 | kilogramsPerCubicMeter (Constants.pound / Constants.cubicFoot * numPoundsPerCubicFoot) 103 | 104 | 105 | {-| Convert a density to a number of pounds per cubic foot. 106 | -} 107 | inPoundsPerCubicFoot : Density -> Float 108 | inPoundsPerCubicFoot density = 109 | inKilogramsPerCubicMeter density / (Constants.pound / Constants.cubicFoot) 110 | -------------------------------------------------------------------------------- /src/AngularSpeed.elm: -------------------------------------------------------------------------------- 1 | module AngularSpeed exposing 2 | ( AngularSpeed, RadiansPerSecond 3 | , radiansPerSecond, inRadiansPerSecond, degreesPerSecond, inDegreesPerSecond 4 | , turnsPerSecond, inTurnsPerSecond, turnsPerMinute, inTurnsPerMinute 5 | , revolutionsPerSecond, inRevolutionsPerSecond, revolutionsPerMinute, inRevolutionsPerMinute 6 | ) 7 | 8 | {-| An `AngularSpeed` represents a rotation rate in radians per second, degrees 9 | per second, turns (revolutions) per second or turns (revolutions) per minute. 10 | It is stored as a number of radians per second. 11 | 12 | Note that since `RadiansPerSecond` is defined as `Rate Radians Seconds` (angle 13 | per unit time), you can construct an `AngularSpeed` value using `Quantity.per`: 14 | 15 | angularSpeed = 16 | angle |> Quantity.per duration 17 | 18 | You can also do rate-related calculations with `AngularSpeed` values to compute 19 | `Angle` or `Duration`: 20 | 21 | angle = 22 | angularSpeed |> Quantity.for duration 23 | 24 | alsoAngle = 25 | duration |> Quantity.at angularSpeed 26 | 27 | duration = 28 | angle |> Quantity.at_ angularSpeed 29 | 30 | @docs AngularSpeed, RadiansPerSecond 31 | 32 | 33 | ## Conversions 34 | 35 | @docs radiansPerSecond, inRadiansPerSecond, degreesPerSecond, inDegreesPerSecond 36 | @docs turnsPerSecond, inTurnsPerSecond, turnsPerMinute, inTurnsPerMinute 37 | 38 | 39 | ## Aliases for `turns` as `revolutions` 40 | 41 | The Elm core `Basics` module uses `turns` in its [Angle Conversions](https://package.elm-lang.org/packages/elm-lang/core/latest/Basics#angle-conversions). 42 | To be consistent, this module also uses `turns`, however since 'revolutions per 43 | minute' (RPM) is in common usage, several aliases are provided. 44 | 45 | @docs revolutionsPerSecond, inRevolutionsPerSecond, revolutionsPerMinute, inRevolutionsPerMinute 46 | 47 | -} 48 | 49 | import Angle exposing (Radians) 50 | import Duration exposing (Seconds) 51 | import Quantity exposing (Quantity(..), Rate) 52 | 53 | 54 | {-| -} 55 | type alias RadiansPerSecond = 56 | Rate Radians Seconds 57 | 58 | 59 | {-| -} 60 | type alias AngularSpeed = 61 | Quantity Float RadiansPerSecond 62 | 63 | 64 | {-| Construct an angular speed from a number of radians per second. 65 | -} 66 | radiansPerSecond : Float -> AngularSpeed 67 | radiansPerSecond numRadiansPerSecond = 68 | Quantity numRadiansPerSecond 69 | 70 | 71 | {-| Convert an angular speed to a number of radians per second. 72 | -} 73 | inRadiansPerSecond : AngularSpeed -> Float 74 | inRadiansPerSecond (Quantity numRadiansPerSecond) = 75 | numRadiansPerSecond 76 | 77 | 78 | {-| Construct an angular speed from a number of degrees per second. 79 | -} 80 | degreesPerSecond : Float -> AngularSpeed 81 | degreesPerSecond numDegreesPerSecond = 82 | radiansPerSecond (pi / 180 * numDegreesPerSecond) 83 | 84 | 85 | {-| Convert an angular speed to a number of degrees per second. 86 | -} 87 | inDegreesPerSecond : AngularSpeed -> Float 88 | inDegreesPerSecond angularSpeed = 89 | inRadiansPerSecond angularSpeed / (pi / 180) 90 | 91 | 92 | {-| Construct an angular speed from a number of turns per second. 93 | -} 94 | turnsPerSecond : Float -> AngularSpeed 95 | turnsPerSecond numTurnsPerSecond = 96 | radiansPerSecond (2 * pi * numTurnsPerSecond) 97 | 98 | 99 | {-| Convert an angular speed to a number of turns per second. 100 | -} 101 | inTurnsPerSecond : AngularSpeed -> Float 102 | inTurnsPerSecond angularSpeed = 103 | inRadiansPerSecond angularSpeed / (2 * pi) 104 | 105 | 106 | {-| Construct an angular speed from a number of turns per minute. 107 | -} 108 | turnsPerMinute : Float -> AngularSpeed 109 | turnsPerMinute numTurnsPerMinute = 110 | radiansPerSecond ((2 * pi * numTurnsPerMinute) / 60) 111 | 112 | 113 | {-| Convert an angular speed to a number of turns per minute. 114 | -} 115 | inTurnsPerMinute : AngularSpeed -> Float 116 | inTurnsPerMinute angularSpeed = 117 | inRadiansPerSecond angularSpeed / ((2 * pi) / 60) 118 | 119 | 120 | 121 | ---------- FUNCTION ALIASES ---------- 122 | 123 | 124 | {-| Alias for `turnsPerSecond`. 125 | -} 126 | revolutionsPerSecond : Float -> AngularSpeed 127 | revolutionsPerSecond = 128 | turnsPerSecond 129 | 130 | 131 | {-| Alias for `inTurnsPerSecond`. 132 | -} 133 | inRevolutionsPerSecond : AngularSpeed -> Float 134 | inRevolutionsPerSecond = 135 | inTurnsPerSecond 136 | 137 | 138 | {-| Alias for `turnsPerMinute`. 139 | -} 140 | revolutionsPerMinute : Float -> AngularSpeed 141 | revolutionsPerMinute = 142 | turnsPerMinute 143 | 144 | 145 | {-| Alias for `inTurnsPerMinute`. 146 | -} 147 | inRevolutionsPerMinute : AngularSpeed -> Float 148 | inRevolutionsPerMinute = 149 | inTurnsPerMinute 150 | -------------------------------------------------------------------------------- /src/Power.elm: -------------------------------------------------------------------------------- 1 | module Power exposing 2 | ( Power, Watts 3 | , watts, inWatts, kilowatts, inKilowatts, megawatts, inMegawatts 4 | , metricHorsepower, inMetricHorsepower, mechanicalHorsepower, inMechanicalHorsepower, electricalHorsepower, inElectricalHorsepower 5 | ) 6 | 7 | {-| A `Power` value represents power in watts or horsepower. It is stored as a 8 | number of watts. 9 | 10 | Note that since `Watts` is defined as `Rate Joules Seconds` (energy per unit 11 | time), you can construct a `Power` value using `Quantity.per`: 12 | 13 | power = 14 | energy |> Quantity.per duration 15 | 16 | You can also do rate-related calculations with `Power` values to compute 17 | `Energy` or `Duration`: 18 | 19 | energy = 20 | power |> Quantity.for duration 21 | 22 | alsoEnergy = 23 | duration |> Quantity.at power 24 | 25 | duration = 26 | energy |> Quantity.at_ power 27 | 28 | [1]: https://en.wikipedia.org/wiki/Horsepower#Definitions 29 | 30 | @docs Power, Watts 31 | 32 | 33 | ## Metric 34 | 35 | @docs watts, inWatts, kilowatts, inKilowatts, megawatts, inMegawatts 36 | 37 | 38 | ## Horsepower 39 | 40 | Who knew that there were not one, not two, but _three_ possible interpretations 41 | of "one horsepower"? (Actually there are more than that, but these three 42 | seemed the most reasonable.) 43 | 44 | @docs metricHorsepower, inMetricHorsepower, mechanicalHorsepower, inMechanicalHorsepower, electricalHorsepower, inElectricalHorsepower 45 | 46 | -} 47 | 48 | import Constants 49 | import Duration exposing (Seconds) 50 | import Energy exposing (Joules) 51 | import Quantity exposing (Quantity(..), Rate) 52 | 53 | 54 | {-| -} 55 | type alias Watts = 56 | Rate Joules Seconds 57 | 58 | 59 | {-| -} 60 | type alias Power = 61 | Quantity Float Watts 62 | 63 | 64 | {-| Construct a `Power` value from a number of watts. 65 | -} 66 | watts : Float -> Power 67 | watts numWatts = 68 | Quantity numWatts 69 | 70 | 71 | {-| Convert a `Power` value to a number of watts. 72 | -} 73 | inWatts : Power -> Float 74 | inWatts (Quantity numWatts) = 75 | numWatts 76 | 77 | 78 | {-| Construct a `Power` value from a number of kilowatts. 79 | -} 80 | kilowatts : Float -> Power 81 | kilowatts numKilowatts = 82 | watts (1000 * numKilowatts) 83 | 84 | 85 | {-| Convert a `Power` value to a number of kilowatts. 86 | -} 87 | inKilowatts : Power -> Float 88 | inKilowatts power = 89 | inWatts power / 1000 90 | 91 | 92 | {-| Construct a `Power` value from a number of megawatts. 93 | -} 94 | megawatts : Float -> Power 95 | megawatts numMegawatts = 96 | watts (1.0e6 * numMegawatts) 97 | 98 | 99 | {-| Convert a `Power` value to a number of megawatts. 100 | -} 101 | inMegawatts : Power -> Float 102 | inMegawatts power = 103 | inWatts power / 1.0e6 104 | 105 | 106 | {-| Construct a `Power` value from an number of [metric horsepower][1]. 107 | 108 | Power.metricHorsepower 1 109 | --> Power.watts 735.49875 110 | 111 | [1]: https://en.wikipedia.org/wiki/Horsepower#Metric_horsepower 112 | 113 | -} 114 | metricHorsepower : Float -> Power 115 | metricHorsepower numMetricHorsepower = 116 | watts (Constants.metricHorsepower * numMetricHorsepower) 117 | 118 | 119 | {-| Convert a `Power` value to a number of metric horsepower. 120 | -} 121 | inMetricHorsepower : Power -> Float 122 | inMetricHorsepower power = 123 | inWatts power / Constants.metricHorsepower 124 | 125 | 126 | {-| Construct a `Power` value from an number of [mechanical horsepower][1]. 127 | 128 | Power.mechanicalHorsepower 1 129 | --> Power.watts 745.6998715822702 130 | 131 | [1]: https://en.wikipedia.org/wiki/Horsepower#Mechanical_horsepower 132 | 133 | -} 134 | mechanicalHorsepower : Float -> Power 135 | mechanicalHorsepower numMechanicalHorsepower = 136 | watts (numMechanicalHorsepower * Constants.mechanicalHorsepower) 137 | 138 | 139 | {-| Convert a `Power` value to a number of mechanical horsepower. 140 | -} 141 | inMechanicalHorsepower : Power -> Float 142 | inMechanicalHorsepower power = 143 | inWatts power / Constants.mechanicalHorsepower 144 | 145 | 146 | {-| Construct a `Power` value from an number of [electrical horsepower][1]. 147 | 148 | Power.electricalHorsepower 1 149 | --> Power.watts 746 150 | 151 | [1]: https://en.wikipedia.org/wiki/Horsepower#Electrical_horsepower 152 | 153 | -} 154 | electricalHorsepower : Float -> Power 155 | electricalHorsepower numElectricalHorsepower = 156 | watts (Constants.electricalHorsepower * numElectricalHorsepower) 157 | 158 | 159 | {-| Convert a `Power` value to a number of electrical horsepower. 160 | -} 161 | inElectricalHorsepower : Power -> Float 162 | inElectricalHorsepower power = 163 | inWatts power / Constants.electricalHorsepower 164 | -------------------------------------------------------------------------------- /src/Mass.elm: -------------------------------------------------------------------------------- 1 | module Mass exposing 2 | ( Mass, Kilograms 3 | , kilograms, inKilograms, grams, inGrams, metricTons, inMetricTons 4 | , pounds, inPounds, ounces, inOunces, longTons, inLongTons 5 | , shortTons, inShortTons 6 | , kilogram, gram, metricTon, pound, ounce, longTon, shortTon 7 | ) 8 | 9 | {-| A `Mass` represents a mass in kilograms, pounds, metric or imperial tons 10 | etc. It is stored as a number of kilograms. 11 | 12 | @docs Mass, Kilograms 13 | 14 | 15 | ## Metric 16 | 17 | @docs kilograms, inKilograms, grams, inGrams, metricTons, inMetricTons 18 | 19 | 20 | ## Imperial 21 | 22 | @docs pounds, inPounds, ounces, inOunces, longTons, inLongTons 23 | 24 | 25 | ## U.S. customary 26 | 27 | @docs shortTons, inShortTons 28 | 29 | 30 | ## Constants 31 | 32 | Shorthand for `Mass.kilograms 1`, `Mass.shortTons 1` etc. Can be convenient to 33 | use with [`Quantity.per`](Quantity#per). 34 | 35 | @docs kilogram, gram, metricTon, pound, ounce, longTon, shortTon 36 | 37 | -} 38 | 39 | import Constants 40 | import Quantity exposing (Quantity(..), Rate) 41 | 42 | 43 | {-| -} 44 | type Kilograms 45 | = Kilograms 46 | 47 | 48 | {-| -} 49 | type alias Mass = 50 | Quantity Float Kilograms 51 | 52 | 53 | {-| Construct a mass from a number of kilograms. 54 | -} 55 | kilograms : Float -> Mass 56 | kilograms numKilograms = 57 | Quantity numKilograms 58 | 59 | 60 | {-| Convert a mass to a number of kilograms. 61 | -} 62 | inKilograms : Mass -> Float 63 | inKilograms (Quantity numKilograms) = 64 | numKilograms 65 | 66 | 67 | {-| Construct a mass from a number of grams. 68 | -} 69 | grams : Float -> Mass 70 | grams numGrams = 71 | kilograms (0.001 * numGrams) 72 | 73 | 74 | {-| Convert a mass to a number of grams. 75 | -} 76 | inGrams : Mass -> Float 77 | inGrams mass = 78 | 1000 * inKilograms mass 79 | 80 | 81 | {-| Construct a mass from a number of pounds. 82 | -} 83 | pounds : Float -> Mass 84 | pounds numPounds = 85 | kilograms (Constants.pound * numPounds) 86 | 87 | 88 | {-| Convert a mass to a number of pounds. 89 | -} 90 | inPounds : Mass -> Float 91 | inPounds mass = 92 | inKilograms mass / Constants.pound 93 | 94 | 95 | {-| Construct a mass from a number of ounces. 96 | -} 97 | ounces : Float -> Mass 98 | ounces numOunces = 99 | kilograms (Constants.ounce * numOunces) 100 | 101 | 102 | {-| Convert a mass to a number of ounces. 103 | 104 | Mass.pounds 1 |> Mass.inOunces 105 | --> 16 106 | 107 | -} 108 | inOunces : Mass -> Float 109 | inOunces mass = 110 | inKilograms mass / Constants.ounce 111 | 112 | 113 | {-| Construct a mass from a number of [metric tons][1]. 114 | 115 | Mass.metricTons 1 116 | --> Mass.kilograms 1000 117 | 118 | [1]: https://en.wikipedia.org/wiki/Tonne 119 | 120 | -} 121 | metricTons : Float -> Mass 122 | metricTons numTonnes = 123 | kilograms (1000 * numTonnes) 124 | 125 | 126 | {-| Convert a mass to a number of metric tons. 127 | -} 128 | inMetricTons : Mass -> Float 129 | inMetricTons mass = 130 | 0.001 * inKilograms mass 131 | 132 | 133 | {-| Construct a mass from a number of [short tons][1]. This is the 'ton' 134 | commonly used in the United States. 135 | 136 | Mass.shortTons 1 137 | --> Mass.pounds 2000 138 | 139 | [1]: https://en.wikipedia.org/wiki/Short_ton 140 | 141 | -} 142 | shortTons : Float -> Mass 143 | shortTons numShortTons = 144 | kilograms (Constants.shortTon * numShortTons) 145 | 146 | 147 | {-| Convert a mass to a number of short tons. 148 | -} 149 | inShortTons : Mass -> Float 150 | inShortTons mass = 151 | inKilograms mass / Constants.shortTon 152 | 153 | 154 | {-| Construct a mass from a number of [long tons][1]. This is the 'ton' commonly 155 | used in the United Kingdom and British Commonwealth. 156 | 157 | Mass.longTons 1 158 | --> Mass.pounds 2240 159 | 160 | [1]: https://en.wikipedia.org/wiki/Long_ton 161 | 162 | -} 163 | longTons : Float -> Mass 164 | longTons numLongTons = 165 | kilograms (Constants.longTon * numLongTons) 166 | 167 | 168 | {-| Convert a mass to a number of long tons. 169 | -} 170 | inLongTons : Mass -> Float 171 | inLongTons mass = 172 | inKilograms mass / Constants.longTon 173 | 174 | 175 | {-| -} 176 | kilogram : Mass 177 | kilogram = 178 | kilograms 1 179 | 180 | 181 | {-| -} 182 | gram : Mass 183 | gram = 184 | grams 1 185 | 186 | 187 | {-| -} 188 | metricTon : Mass 189 | metricTon = 190 | metricTons 1 191 | 192 | 193 | {-| -} 194 | pound : Mass 195 | pound = 196 | pounds 1 197 | 198 | 199 | {-| -} 200 | ounce : Mass 201 | ounce = 202 | ounces 1 203 | 204 | 205 | {-| -} 206 | longTon : Mass 207 | longTon = 208 | longTons 1 209 | 210 | 211 | {-| -} 212 | shortTon : Mass 213 | shortTon = 214 | shortTons 1 215 | -------------------------------------------------------------------------------- /src/SubstanceAmount.elm: -------------------------------------------------------------------------------- 1 | module SubstanceAmount exposing 2 | ( SubstanceAmount, Moles 3 | , moles, inMoles, picomoles, inPicomoles, nanomoles, inNanomoles 4 | , micromoles, inMicromoles, millimoles, inMillimoles 5 | , centimoles, inCentimoles, decimoles, inDecimoles 6 | , kilomoles, inKilomoles, megamoles, inMegamoles, gigamoles, inGigamoles 7 | ) 8 | 9 | {-| A `SubstanceAmount` value represents a substance amount in [moles][1]. 10 | 11 | [1]: https://en.wikipedia.org/wiki/Mole_(unit) 12 | 13 | @docs SubstanceAmount, Moles 14 | 15 | 16 | ## Conversions 17 | 18 | @docs moles, inMoles, picomoles, inPicomoles, nanomoles, inNanomoles 19 | @docs micromoles, inMicromoles, millimoles, inMillimoles 20 | @docs centimoles, inCentimoles, decimoles, inDecimoles 21 | @docs kilomoles, inKilomoles, megamoles, inMegamoles, gigamoles, inGigamoles 22 | 23 | -} 24 | 25 | import Quantity exposing (Quantity(..)) 26 | 27 | 28 | {-| -} 29 | type Moles 30 | = Moles 31 | 32 | 33 | {-| -} 34 | type alias SubstanceAmount = 35 | Quantity Float Moles 36 | 37 | 38 | {-| Construct a substance amount from a number of moles. 39 | -} 40 | moles : Float -> SubstanceAmount 41 | moles numMoles = 42 | Quantity numMoles 43 | 44 | 45 | {-| Convert a substance amount to a number of moles. 46 | -} 47 | inMoles : SubstanceAmount -> Float 48 | inMoles (Quantity numMoles) = 49 | numMoles 50 | 51 | 52 | {-| Construct a substance amount from a number of picomoles. 53 | -} 54 | picomoles : Float -> SubstanceAmount 55 | picomoles numPicomoles = 56 | moles (numPicomoles * 1.0e-12) 57 | 58 | 59 | {-| Convert a substance amount to a number of picomoles. 60 | -} 61 | inPicomoles : SubstanceAmount -> Float 62 | inPicomoles substanceAmount = 63 | inMoles substanceAmount / 1.0e-12 64 | 65 | 66 | {-| Construct a substance amount from a number of nanomoles. 67 | -} 68 | nanomoles : Float -> SubstanceAmount 69 | nanomoles numNanomoles = 70 | moles (numNanomoles * 1.0e-9) 71 | 72 | 73 | {-| Convert a substance amount to a number of nanomoles. 74 | -} 75 | inNanomoles : SubstanceAmount -> Float 76 | inNanomoles substanceAmount = 77 | inMoles substanceAmount / 1.0e-9 78 | 79 | 80 | {-| Construct a substance amount from a number of micromoles. 81 | -} 82 | micromoles : Float -> SubstanceAmount 83 | micromoles numMicromoles = 84 | moles (numMicromoles * 1.0e-6) 85 | 86 | 87 | {-| Convert a substance amount to a number of micromoles. 88 | -} 89 | inMicromoles : SubstanceAmount -> Float 90 | inMicromoles substanceAmount = 91 | inMoles substanceAmount / 1.0e-6 92 | 93 | 94 | {-| Construct a substance amount from a number of millimoles. 95 | -} 96 | millimoles : Float -> SubstanceAmount 97 | millimoles numMillimoles = 98 | moles (numMillimoles * 1.0e-3) 99 | 100 | 101 | {-| Convert a substance amount to a number of millimoles. 102 | -} 103 | inMillimoles : SubstanceAmount -> Float 104 | inMillimoles substanceAmount = 105 | inMoles substanceAmount / 1.0e-3 106 | 107 | 108 | {-| Construct a substance amount from a number of centimoles. 109 | -} 110 | centimoles : Float -> SubstanceAmount 111 | centimoles numCentimoles = 112 | moles (numCentimoles * 1.0e-2) 113 | 114 | 115 | {-| Convert a substance amount to a number of centimoles. 116 | -} 117 | inCentimoles : SubstanceAmount -> Float 118 | inCentimoles substanceAmount = 119 | inMoles substanceAmount / 1.0e-2 120 | 121 | 122 | {-| Construct a substance amount from a number of decimoles. 123 | -} 124 | decimoles : Float -> SubstanceAmount 125 | decimoles numDecimoles = 126 | moles (numDecimoles * 1.0e-1) 127 | 128 | 129 | {-| Convert a substance amount to a number of decimoles. 130 | -} 131 | inDecimoles : SubstanceAmount -> Float 132 | inDecimoles substanceAmount = 133 | inMoles substanceAmount / 1.0e-1 134 | 135 | 136 | {-| Construct a substance amount from a number of kilomoles. 137 | -} 138 | kilomoles : Float -> SubstanceAmount 139 | kilomoles numKilomoles = 140 | moles (numKilomoles * 1.0e3) 141 | 142 | 143 | {-| Convert a substance amount to a number of kilomoles. 144 | -} 145 | inKilomoles : SubstanceAmount -> Float 146 | inKilomoles substanceAmount = 147 | inMoles substanceAmount / 1.0e3 148 | 149 | 150 | {-| Construct a substance amount from a number of megamoles. 151 | -} 152 | megamoles : Float -> SubstanceAmount 153 | megamoles numMegamoles = 154 | moles (numMegamoles * 1.0e6) 155 | 156 | 157 | {-| Convert a substance amount to a number of megamoles. 158 | -} 159 | inMegamoles : SubstanceAmount -> Float 160 | inMegamoles substanceAmount = 161 | inMoles substanceAmount / 1.0e6 162 | 163 | 164 | {-| Construct a substance amount from a number of gigamoles. 165 | -} 166 | gigamoles : Float -> SubstanceAmount 167 | gigamoles numGigamoles = 168 | moles (numGigamoles * 1.0e9) 169 | 170 | 171 | {-| Convert a substance amount to a number of gigamoles. 172 | -} 173 | inGigamoles : SubstanceAmount -> Float 174 | inGigamoles substanceAmount = 175 | inMoles substanceAmount / 1.0e9 176 | -------------------------------------------------------------------------------- /src/Molarity.elm: -------------------------------------------------------------------------------- 1 | module Molarity exposing 2 | ( Molarity, MolesPerCubicMeter 3 | , molesPerCubicMeter, inMolesPerCubicMeter 4 | , molesPerLiter, inMolesPerLiter 5 | , decimolesPerLiter, inDecimolesPerLiter 6 | , centimolesPerLiter, inCentimolesPerLiter 7 | , millimolesPerLiter, inMillimolesPerLiter 8 | , micromolesPerLiter, inMicromolesPerLiter 9 | ) 10 | 11 | {-| A `Molarity` value represents a concentration of substance in moles per 12 | cubic meter, moles per liter, millimoles per liter etc. It is stored as a number 13 | of moles per cubic meter. 14 | 15 | Note that the [NIST Guide to the 16 | SI](https://www.nist.gov/pml/special-publication-811/nist-guide-si-chapter-8) 17 | states that the term "molarity" is considered obsolete, but it appears to still 18 | be in common use and is far less verbose than the alternative NIST suggestion of 19 | "amount-of-substance concentration". 20 | 21 | Since the units of `Molarity` are defined to be `Rate Moles CubicMeters` (amount 22 | of substance per unit volume), you can construct a `Molarity` value using 23 | `Quantity.per`: 24 | 25 | molarity = 26 | substanceAmount |> Quantity.per volume 27 | 28 | You can also do rate-related calculations with `Molarity` values to compute 29 | `SubstanceAmount` or `Volume`: 30 | 31 | substanceAmount = 32 | volume |> Quantity.at molarity 33 | 34 | volume = 35 | substanceAmount |> Quantity.at_ molarity 36 | 37 | @docs Molarity, MolesPerCubicMeter 38 | @docs molesPerCubicMeter, inMolesPerCubicMeter 39 | @docs molesPerLiter, inMolesPerLiter 40 | @docs decimolesPerLiter, inDecimolesPerLiter 41 | @docs centimolesPerLiter, inCentimolesPerLiter 42 | @docs millimolesPerLiter, inMillimolesPerLiter 43 | @docs micromolesPerLiter, inMicromolesPerLiter 44 | 45 | -} 46 | 47 | import Constants 48 | import Quantity exposing (Quantity(..), Rate) 49 | import SubstanceAmount exposing (Moles) 50 | import Volume exposing (CubicMeters) 51 | 52 | 53 | {-| -} 54 | type alias MolesPerCubicMeter = 55 | Rate Moles CubicMeters 56 | 57 | 58 | {-| -} 59 | type alias Molarity = 60 | Quantity Float MolesPerCubicMeter 61 | 62 | 63 | 64 | ---------- CONSTANTS ---------- 65 | 66 | 67 | {-| One mole per liter, in moles per cubic meter 68 | -} 69 | oneMolePerLiter : Float 70 | oneMolePerLiter = 71 | Constants.mole / Constants.liter 72 | 73 | 74 | {-| One decimole per liter, in moles per cubic meter 75 | -} 76 | oneDecimolePerLiter : Float 77 | oneDecimolePerLiter = 78 | 0.1 * Constants.mole / Constants.liter 79 | 80 | 81 | 82 | ---------- FUNCTIONS ---------- 83 | 84 | 85 | {-| Construct a molarity from a number of moles per cubic meter. 86 | -} 87 | molesPerCubicMeter : Float -> Molarity 88 | molesPerCubicMeter numMolesPerCubicMeter = 89 | Quantity numMolesPerCubicMeter 90 | 91 | 92 | {-| Convert a molarity to a number of moles per cubic meter. 93 | -} 94 | inMolesPerCubicMeter : Molarity -> Float 95 | inMolesPerCubicMeter (Quantity numMolesPerCubicMeter) = 96 | numMolesPerCubicMeter 97 | 98 | 99 | {-| Construct a molarity from a number of moles per liter. 100 | -} 101 | molesPerLiter : Float -> Molarity 102 | molesPerLiter numMolesPerLiter = 103 | molesPerCubicMeter (numMolesPerLiter * oneMolePerLiter) 104 | 105 | 106 | {-| Convert a molarity to a number of moles per liter. 107 | -} 108 | inMolesPerLiter : Molarity -> Float 109 | inMolesPerLiter molarity = 110 | inMolesPerCubicMeter molarity / oneMolePerLiter 111 | 112 | 113 | {-| Construct a molarity from a number of decimoles per liter. 114 | -} 115 | decimolesPerLiter : Float -> Molarity 116 | decimolesPerLiter numDecimolesPerLiter = 117 | molesPerCubicMeter (numDecimolesPerLiter * oneDecimolePerLiter) 118 | 119 | 120 | {-| Convert a molarity to a number of decimoles per liter. 121 | -} 122 | inDecimolesPerLiter : Molarity -> Float 123 | inDecimolesPerLiter molarity = 124 | inMolesPerCubicMeter molarity / oneDecimolePerLiter 125 | 126 | 127 | {-| Construct a molarity from a number of centimoles per liter. 128 | -} 129 | centimolesPerLiter : Float -> Molarity 130 | centimolesPerLiter numCentimolesPerLiter = 131 | decimolesPerLiter (10 * numCentimolesPerLiter) 132 | 133 | 134 | {-| Convert a molarity to a number of centimoles per liter. 135 | -} 136 | inCentimolesPerLiter : Molarity -> Float 137 | inCentimolesPerLiter molar = 138 | inDecimolesPerLiter molar / 10 139 | 140 | 141 | {-| Construct a molarity from a number of millimoles per liter. 142 | -} 143 | millimolesPerLiter : Float -> Molarity 144 | millimolesPerLiter numMillimolesPerLiter = 145 | decimolesPerLiter (100 * numMillimolesPerLiter) 146 | 147 | 148 | {-| Convert a molarity to a number of millimoles per liter. 149 | -} 150 | inMillimolesPerLiter : Molarity -> Float 151 | inMillimolesPerLiter molar = 152 | inDecimolesPerLiter molar / 100 153 | 154 | 155 | {-| Construct a molarity from a number of micromoles per liter. 156 | -} 157 | micromolesPerLiter : Float -> Molarity 158 | micromolesPerLiter numMicromolesPerLiter = 159 | decimolesPerLiter (1000 * numMicromolesPerLiter) 160 | 161 | 162 | {-| Convert a molarity to a number of micromoles per liter. 163 | -} 164 | inMicromolesPerLiter : Molarity -> Float 165 | inMicromolesPerLiter molar = 166 | inDecimolesPerLiter molar / 1000 167 | -------------------------------------------------------------------------------- /src/Pixels.elm: -------------------------------------------------------------------------------- 1 | module Pixels exposing 2 | ( Pixels 3 | , int, float, toInt, toFloat, pixels, inPixels, pixel 4 | , PixelsPerSecond, PixelsPerSecondSquared 5 | , pixelsPerSecond, inPixelsPerSecond 6 | , pixelsPerSecondSquared, inPixelsPerSecondSquared 7 | , SquarePixels 8 | , squarePixels, inSquarePixels 9 | ) 10 | 11 | {-| Although most of the focus of `elm-units` is on physical/scientific units, 12 | it's often useful to be able to safely convert back and forth between (for 13 | example) [`Length`](Length) values in the real world and on-screen lengths in 14 | pixels. 15 | 16 | This module provides a standard `Pixels` units type and basic functions for 17 | constructing/converting values of type `Quantity Int Pixels` or 18 | `Quantity Float Pixels`, which allows you to do things like represent 19 | conversions between real-world and on-screen lengths as [rates of change][1]. 20 | This in turn means that all the normal [`Quantity`](Quantity) functions can be 21 | used to convert between pixels and other units, or even do type-safe math 22 | directly on pixel values. 23 | 24 | [1]: Quantity#working-with-rates 25 | 26 | @docs Pixels 27 | 28 | @docs int, float, toInt, toFloat, pixels, inPixels, pixel 29 | 30 | 31 | ## Rates 32 | 33 | @docs PixelsPerSecond, PixelsPerSecondSquared 34 | 35 | @docs pixelsPerSecond, inPixelsPerSecond 36 | 37 | @docs pixelsPerSecondSquared, inPixelsPerSecondSquared 38 | 39 | 40 | ## Areas 41 | 42 | @docs SquarePixels 43 | 44 | @docs squarePixels, inSquarePixels 45 | 46 | -} 47 | 48 | import Duration exposing (Seconds) 49 | import Quantity exposing (Quantity(..), Rate, Squared) 50 | 51 | 52 | {-| Units type representing one on-screen pixel. 53 | -} 54 | type Pixels 55 | = Pixels 56 | 57 | 58 | {-| Units type representing an on-screen speed of one pixel per second. 59 | -} 60 | type alias PixelsPerSecond = 61 | Rate Pixels Seconds 62 | 63 | 64 | {-| Units type representing an on-screen acceleration of one pixel per second 65 | squared. 66 | -} 67 | type alias PixelsPerSecondSquared = 68 | Rate PixelsPerSecond Seconds 69 | 70 | 71 | {-| Units type representing an on-screen area of one square pixel. For example, 72 | a 32x32 image has an area of 1024 square pixels. 73 | -} 74 | type alias SquarePixels = 75 | Squared Pixels 76 | 77 | 78 | {-| Construct a quantity representing an integer number of on-screen pixels: 79 | 80 | screenWidth = 81 | Pixels.int 1920 82 | 83 | -} 84 | int : Int -> Quantity Int Pixels 85 | int numPixels = 86 | Quantity numPixels 87 | 88 | 89 | {-| Construct a quantity representing a floating-point number of on-screen 90 | pixels: 91 | 92 | lineWeight = 93 | Pixels.float 1.5 94 | 95 | -} 96 | float : Float -> Quantity Float Pixels 97 | float numPixels = 98 | Quantity numPixels 99 | 100 | 101 | {-| Convert an integer number of pixels back into a plain `Int`: 102 | 103 | Pixels.int 1920 104 | |> Quantity.multiplyBy 2 105 | |> Pixels.toInt 106 | --> 3840 107 | 108 | -} 109 | toInt : Quantity Int Pixels -> Int 110 | toInt (Quantity numPixels) = 111 | numPixels 112 | 113 | 114 | {-| Convert a floating-point number of pixels back into a plain `Float`: 115 | 116 | pixelDensity = 117 | Pixels.float 96 |> Quantity.per (Length.inches 1) 118 | 119 | Length.centimeters 1 120 | |> Quantity.at pixelDensity 121 | |> Pixels.toFloat 122 | --> 37.795275590551185 123 | 124 | -} 125 | toFloat : Quantity Float Pixels -> Float 126 | toFloat (Quantity numPixels) = 127 | numPixels 128 | 129 | 130 | {-| Generic version of `Pixels.int`/`Pixels.float`, for consistency with other 131 | modules like `Length`. Note that passing an `Int` will give you a 132 | 133 | Quantity Int Pixels 134 | 135 | while passing a `Float` will give you a 136 | 137 | Quantity Float Pixels 138 | 139 | If you pass a _literal_ integer like `1920`, you will get a generic `Quantity 140 | number Pixels` which can be used as either an `Int` _or_ `Float` number of 141 | pixels. 142 | 143 | -} 144 | pixels : number -> Quantity number Pixels 145 | pixels numPixels = 146 | Quantity numPixels 147 | 148 | 149 | {-| Convert a `Pixels` value to a plain number of pixels. This is a generic 150 | version of `Pixels.toInt`/`Pixels.toFloat`. 151 | -} 152 | inPixels : Quantity number Pixels -> number 153 | inPixels (Quantity numPixels) = 154 | numPixels 155 | 156 | 157 | {-| Shorthand for `Pixels.pixels 1`. Can be convenient to use with 158 | [`Quantity.per`](Quantity#per). 159 | -} 160 | pixel : Quantity number Pixels 161 | pixel = 162 | pixels 1 163 | 164 | 165 | {-| Construct an on-screen speed from a number of pixels per second. 166 | -} 167 | pixelsPerSecond : Float -> Quantity Float PixelsPerSecond 168 | pixelsPerSecond numPixelsPerSecond = 169 | Quantity numPixelsPerSecond 170 | 171 | 172 | {-| Convert an on-screen speed to a number of pixels per second. 173 | 174 | elapsedTime = 175 | Duration.milliseconds 16 176 | 177 | dragDistance = 178 | Pixels.float 2 179 | 180 | dragSpeed = 181 | dragDistance |> Quantity.per elapsedTime 182 | 183 | dragSpeed |> Pixels.inPixelsPerSecond 184 | --> 125 185 | 186 | -} 187 | inPixelsPerSecond : Quantity Float PixelsPerSecond -> Float 188 | inPixelsPerSecond (Quantity numPixelsPerSecond) = 189 | numPixelsPerSecond 190 | 191 | 192 | {-| Construct an on-screen acceleration from a number of pixels per second 193 | squared. 194 | -} 195 | pixelsPerSecondSquared : Float -> Quantity Float PixelsPerSecondSquared 196 | pixelsPerSecondSquared numPixelsPerSecondSquared = 197 | Quantity numPixelsPerSecondSquared 198 | 199 | 200 | {-| Convert an on-screen acceleration to a number of pixels per second squared. 201 | -} 202 | inPixelsPerSecondSquared : Quantity Float PixelsPerSecondSquared -> Float 203 | inPixelsPerSecondSquared (Quantity numPixelsPerSecondSquared) = 204 | numPixelsPerSecondSquared 205 | 206 | 207 | {-| Construct an on-screen area from a number of square pixels. 208 | -} 209 | squarePixels : number -> Quantity number SquarePixels 210 | squarePixels numSquarePixels = 211 | Quantity numSquarePixels 212 | 213 | 214 | {-| Convert an on-screen area to a number of square pixels. 215 | 216 | area = 217 | Pixels.int 1928 |> Quantity.times (Pixels.int 1080) 218 | 219 | area |> Pixels.inSquarePixels 220 | --> 2073600 221 | 222 | -} 223 | inSquarePixels : Quantity number SquarePixels -> number 224 | inSquarePixels (Quantity numSquarePixels) = 225 | numSquarePixels 226 | -------------------------------------------------------------------------------- /src/SolidAngle.elm: -------------------------------------------------------------------------------- 1 | module SolidAngle exposing 2 | ( SolidAngle, Steradians 3 | , steradians, inSteradians, spats, inSpats, squareDegrees, inSquareDegrees 4 | , conical, pyramidal 5 | ) 6 | 7 | {-| [Solid angle](https://en.wikipedia.org/wiki/Solid_angle) is a tricky concept 8 | to explain, but roughly speaking solid angle is for 3D what angle is for 2D. It 9 | can be used to measure three-dimensional field of view and is stored in 10 | [steradians](https://en.wikipedia.org/wiki/Steradian). 11 | 12 | 2D angle can be thought of as how much circumference of the unit circle is 13 | covered. The unit circle (circle of radius 1) has a circumference of 2π, and an 14 | angle in radians corresponds to the corresponding amount of circumference 15 | covered. So an angle of 2π radians covers the entire circumference of the 16 | circle, π radians covers half the circle, π/2 radians covers a quarter, etc. 17 | 18 | Similarly, 3D solid angle can be thought of as how much surface area of the unit 19 | sphere is covered. The unit sphere has surface area of 4π, and a solid angle in 20 | steradians corresponds to the corresponding amount of surface area covered. So a 21 | solid angle of 4π steradians covers the entire sphere, 2π steradians covers half 22 | the sphere (one hemisphere), etc. 23 | 24 | @docs SolidAngle, Steradians 25 | 26 | 27 | ## Conversions 28 | 29 | @docs steradians, inSteradians, spats, inSpats, squareDegrees, inSquareDegrees 30 | 31 | 32 | ## Computation 33 | 34 | @docs conical, pyramidal 35 | 36 | -} 37 | 38 | import Angle exposing (Angle) 39 | import Quantity exposing (Quantity(..), zero) 40 | 41 | 42 | {-| -} 43 | type Steradians 44 | = Steradians 45 | 46 | 47 | {-| -} 48 | type alias SolidAngle = 49 | Quantity Float Steradians 50 | 51 | 52 | {-| Construct a solid angle from a number of steradians. 53 | -} 54 | steradians : Float -> SolidAngle 55 | steradians numSteradians = 56 | Quantity numSteradians 57 | 58 | 59 | {-| Convert a solid angle to a number of steradians. 60 | -} 61 | inSteradians : SolidAngle -> Float 62 | inSteradians (Quantity numSteradians) = 63 | numSteradians 64 | 65 | 66 | {-| Construct a solid angle from a number of [spats][1]. One spat is the 3D 67 | equivalent of one full turn; in the same way that one turn is the angle that 68 | covers an entire circle, one spat is the solid angle that covers an entire 69 | sphere. It's rare to have solid angles more than one spat, since solid angles 70 | are usually used to measure what angular fraction of a full sphere something 71 | covers. 72 | 73 | SolidAngle.spats 1 74 | --> SolidAngle.steradians (4 * pi) 75 | 76 | [1]: https://en.wikipedia.org/wiki/Spat_(unit) 77 | 78 | -} 79 | spats : Float -> SolidAngle 80 | spats numSpats = 81 | steradians (4 * pi * numSpats) 82 | 83 | 84 | {-| Convert a solid angle to a number of spats. 85 | 86 | SolidAngle.steradians (2 * pi) |> SolidAngle.inSpats 87 | --> 0.5 88 | 89 | -} 90 | inSpats : SolidAngle -> Float 91 | inSpats solidAngle = 92 | inSteradians solidAngle / (4 * pi) 93 | 94 | 95 | {-| Construct a solid angle from a number of [square 96 | degrees](https://en.wikipedia.org/wiki/Square_degree). One square degree is, 97 | roughly speaking, the solid angle of a square on the surface of a sphere where 98 | the square is one degree wide and one degree tall as viewed from the center of 99 | the sphere. 100 | 101 | SolidAngle.squareDegrees 100 102 | -> SolidAngle.steradians 0.03046 103 | 104 | -} 105 | squareDegrees : Float -> SolidAngle 106 | squareDegrees numSquareDegrees = 107 | steradians (numSquareDegrees * (pi / 180) ^ 2) 108 | 109 | 110 | {-| Convert a solid angle to a number of square degrees. 111 | 112 | SolidAngle.spats 1 |> SolidAngle.inSquareDegrees 113 | --> 41252.96125 114 | 115 | -} 116 | inSquareDegrees : SolidAngle -> Float 117 | inSquareDegrees solidAngle = 118 | inSteradians solidAngle / ((pi / 180) ^ 2) 119 | 120 | 121 | {-| Find the solid angle of a cone with a given tip angle (the angle between two 122 | opposite sides of the cone, _not_ the half-angle from the axis of the cone to 123 | its side). A 1 degree cone has a solid angle of approximately π/4 square 124 | degrees, similar to how a circle of diameter 1 has an area of π/4: 125 | 126 | SolidAngle.conical (Angle.degrees 1) 127 | |> SolidAngle.inSquareDegrees 128 | --> 0.78539318 129 | 130 | pi / 4 131 | --> 0.78539816 132 | 133 | A cone with a tip angle of 180 degrees is just a hemisphere: 134 | 135 | SolidAngle.conical (Angle.degrees 180) 136 | --> SolidAngle.spats 0.5 137 | 138 | "Inside out" cones are also supported, up to 360 degrees (a full sphere): 139 | 140 | SolidAngle.conical (Angle.degrees 270) 141 | --> SolidAngle.spats 0.85355 142 | 143 | SolidAngle.conical (Angle.degrees 360) 144 | --> SolidAngle.spats 1 145 | 146 | -} 147 | conical : Angle -> SolidAngle 148 | conical angle = 149 | let 150 | halfAngle = 151 | Quantity.half angle 152 | in 153 | steradians (2 * pi * (1 - Angle.cos halfAngle)) 154 | 155 | 156 | {-| Find the solid angle of a rectangular pyramid given the angles between the 157 | two pairs of sides. A 1 degree by 1 degree pyramid has a solid angle of almost 158 | exactly 1 square degree: 159 | 160 | SolidAngle.pyramidal 161 | (Angle.degrees 1) 162 | (Angle.degrees 1) 163 | --> SolidAngle.squareDegrees 0.9999746 164 | 165 | In general, the solid angle of a pyramid that is _n_ degrees wide by _m_ degrees 166 | tall is (for relatively small values of _n_ and _m_) approximately _nm_ square 167 | degrees: 168 | 169 | SolidAngle.pyramidal 170 | (Angle.degrees 10) 171 | (Angle.degrees 10) 172 | --> SolidAngle.squareDegrees 99.7474 173 | 174 | SolidAngle.pyramidal 175 | (Angle.degrees 60) 176 | (Angle.degrees 30) 177 | --> SolidAngle.squareDegrees 1704.08 178 | 179 | A pyramid that is 180 degrees by 180 degrees covers an entire hemisphere: 180 | 181 | SolidAngle.pyramidal 182 | (Angle.degrees 180) 183 | (Angle.degrees 180) 184 | --> SolidAngle.spats 0.5 185 | 186 | "Inside out" pyramids greater than 180 degrees are not supported and will be 187 | treated as the corresponding "normal" pyramid (an angle of 240 degrees will be 188 | treated as 120 degrees, an angle of 330 degrees will be treated as 30 degrees, 189 | etc.). 190 | 191 | -} 192 | pyramidal : Angle -> Angle -> SolidAngle 193 | pyramidal theta phi = 194 | let 195 | halfTheta = 196 | Quantity.half theta 197 | 198 | halfPhi = 199 | Quantity.half phi 200 | in 201 | steradians (4 * asin (Angle.sin halfTheta * Angle.sin halfPhi)) 202 | -------------------------------------------------------------------------------- /src/Area.elm: -------------------------------------------------------------------------------- 1 | module Area exposing 2 | ( Area, SquareMeters 3 | , squareMeters, inSquareMeters 4 | , squareMillimeters, inSquareMillimeters, squareCentimeters, inSquareCentimeters, hectares, inHectares, squareKilometers, inSquareKilometers 5 | , squareInches, inSquareInches, squareFeet, inSquareFeet, squareYards, inSquareYards, acres, inAcres, squareMiles, inSquareMiles 6 | , squareMeter, squareMillimeter, squareCentimeter, hectare, squareKilometer 7 | , squareInch, squareFoot, squareYard, acre, squareMile 8 | ) 9 | 10 | {-| An `Area` represents an area in square meters, square feet, acres, hectares 11 | etc. It is stored as a number of square meters. 12 | 13 | Note that you can construct an `Area` value directly using the functions in this 14 | module, but it also works to call [`Quantity.squared`](Quantity#squared) on a 15 | `Length` or [`Quantity.times`](Quantity#times) on a pair of `Length`s. The 16 | following are all equivalent: 17 | 18 | Area.squareFeet 100 19 | 20 | Quantity.squared (Length.feet 10) 21 | 22 | Length.feet 25 |> Quantity.times (Length.feet 4) 23 | 24 | @docs Area, SquareMeters 25 | 26 | 27 | ## Metric 28 | 29 | @docs squareMeters, inSquareMeters 30 | @docs squareMillimeters, inSquareMillimeters, squareCentimeters, inSquareCentimeters, hectares, inHectares, squareKilometers, inSquareKilometers 31 | 32 | 33 | ## Imperial 34 | 35 | @docs squareInches, inSquareInches, squareFeet, inSquareFeet, squareYards, inSquareYards, acres, inAcres, squareMiles, inSquareMiles 36 | 37 | 38 | ## Constants 39 | 40 | Shorthand for `Area.squareMeters 1`, `Area.acres 1` etc. Can be convenient to 41 | use with [`Quantity.per`](Quantity#per). 42 | 43 | @docs squareMeter, squareMillimeter, squareCentimeter, hectare, squareKilometer 44 | @docs squareInch, squareFoot, squareYard, acre, squareMile 45 | 46 | -} 47 | 48 | import Constants 49 | import Length exposing (Meters) 50 | import Quantity exposing (Quantity(..), Squared) 51 | 52 | 53 | {-| -} 54 | type alias SquareMeters = 55 | Squared Meters 56 | 57 | 58 | {-| -} 59 | type alias Area = 60 | Quantity Float SquareMeters 61 | 62 | 63 | {-| Construct an area from a number of square meters. 64 | -} 65 | squareMeters : Float -> Area 66 | squareMeters numSquareMeters = 67 | Quantity numSquareMeters 68 | 69 | 70 | {-| Convert an area to a number of square meters. 71 | -} 72 | inSquareMeters : Area -> Float 73 | inSquareMeters (Quantity numSquareMeters) = 74 | numSquareMeters 75 | 76 | 77 | {-| Construct an area from a number of square millimeters. 78 | -} 79 | squareMillimeters : Float -> Area 80 | squareMillimeters numSquareMillimeters = 81 | squareMeters (1.0e-6 * numSquareMillimeters) 82 | 83 | 84 | {-| Convert an area to a number of square millimeters. 85 | -} 86 | inSquareMillimeters : Area -> Float 87 | inSquareMillimeters area = 88 | 1.0e6 * inSquareMeters area 89 | 90 | 91 | {-| Construct an area from a number of square inches. 92 | -} 93 | squareInches : Float -> Area 94 | squareInches numSquareInches = 95 | squareMeters (Constants.squareInch * numSquareInches) 96 | 97 | 98 | {-| Convert an area to a number of square inches. 99 | -} 100 | inSquareInches : Area -> Float 101 | inSquareInches area = 102 | inSquareMeters area / Constants.squareInch 103 | 104 | 105 | {-| Construct an area from a number of square centimeters. 106 | -} 107 | squareCentimeters : Float -> Area 108 | squareCentimeters numSquareCentimeters = 109 | squareMeters (1.0e-4 * numSquareCentimeters) 110 | 111 | 112 | {-| Convert an area to a number of square centimeters. 113 | -} 114 | inSquareCentimeters : Area -> Float 115 | inSquareCentimeters area = 116 | 1.0e4 * inSquareMeters area 117 | 118 | 119 | {-| Construct an area from a number of square feet. 120 | -} 121 | squareFeet : Float -> Area 122 | squareFeet numSquareFeet = 123 | squareMeters (Constants.squareFoot * numSquareFeet) 124 | 125 | 126 | {-| Convert an area to a number of square feet. 127 | -} 128 | inSquareFeet : Area -> Float 129 | inSquareFeet area = 130 | inSquareMeters area / Constants.squareFoot 131 | 132 | 133 | {-| Construct an area from a number of square yards. 134 | -} 135 | squareYards : Float -> Area 136 | squareYards numSquareYards = 137 | squareMeters (Constants.squareYard * numSquareYards) 138 | 139 | 140 | {-| Convert an area to a number of square yards. 141 | -} 142 | inSquareYards : Area -> Float 143 | inSquareYards area = 144 | inSquareMeters area / Constants.squareYard 145 | 146 | 147 | {-| Construct an area from a number of hectares. 148 | -} 149 | hectares : Float -> Area 150 | hectares numHectares = 151 | squareMeters (1.0e4 * numHectares) 152 | 153 | 154 | {-| Convert an area to a number of hectares. 155 | -} 156 | inHectares : Area -> Float 157 | inHectares area = 158 | 1.0e-4 * inSquareMeters area 159 | 160 | 161 | {-| Construct an area from a number of square kilometers. 162 | -} 163 | squareKilometers : Float -> Area 164 | squareKilometers numSquareKilometers = 165 | squareMeters (1.0e6 * numSquareKilometers) 166 | 167 | 168 | {-| Convert an area to a number of square kilometers. 169 | -} 170 | inSquareKilometers : Area -> Float 171 | inSquareKilometers area = 172 | 1.0e-6 * inSquareMeters area 173 | 174 | 175 | {-| Construct an area from a number of acres. 176 | -} 177 | acres : Float -> Area 178 | acres numAcres = 179 | squareMeters (Constants.acre * numAcres) 180 | 181 | 182 | {-| Convert an area to a number of acres. 183 | -} 184 | inAcres : Area -> Float 185 | inAcres area = 186 | inSquareMeters area / Constants.acre 187 | 188 | 189 | {-| Construct an area from a number of square miles. 190 | -} 191 | squareMiles : Float -> Area 192 | squareMiles numSquareMiles = 193 | squareMeters (Constants.squareMile * numSquareMiles) 194 | 195 | 196 | {-| Convert an area to a number of square miles. 197 | -} 198 | inSquareMiles : Area -> Float 199 | inSquareMiles area = 200 | inSquareMeters area / Constants.squareMile 201 | 202 | 203 | {-| -} 204 | squareMeter : Area 205 | squareMeter = 206 | squareMeters 1 207 | 208 | 209 | {-| -} 210 | squareMillimeter : Area 211 | squareMillimeter = 212 | squareMillimeters 1 213 | 214 | 215 | {-| -} 216 | squareCentimeter : Area 217 | squareCentimeter = 218 | squareCentimeters 1 219 | 220 | 221 | {-| -} 222 | hectare : Area 223 | hectare = 224 | hectares 1 225 | 226 | 227 | {-| -} 228 | squareKilometer : Area 229 | squareKilometer = 230 | squareKilometers 1 231 | 232 | 233 | {-| -} 234 | squareInch : Area 235 | squareInch = 236 | squareInches 1 237 | 238 | 239 | {-| -} 240 | squareFoot : Area 241 | squareFoot = 242 | squareFeet 1 243 | 244 | 245 | {-| -} 246 | squareYard : Area 247 | squareYard = 248 | squareYards 1 249 | 250 | 251 | {-| -} 252 | acre : Area 253 | acre = 254 | acres 1 255 | 256 | 257 | {-| -} 258 | squareMile : Area 259 | squareMile = 260 | squareMiles 1 261 | -------------------------------------------------------------------------------- /src/Constants.elm: -------------------------------------------------------------------------------- 1 | module Constants exposing 2 | ( acre 3 | , astronomicalUnit 4 | , atmosphere 5 | , bushel 6 | , cssPixel 7 | , cubicFoot 8 | , cubicInch 9 | , cubicMeter 10 | , cubicYard 11 | , day 12 | , electricalHorsepower 13 | , foot 14 | , gee 15 | , hour 16 | , imperialFluidOunce 17 | , imperialGallon 18 | , imperialPint 19 | , imperialQuart 20 | , inch 21 | , julianYear 22 | , lightYear 23 | , liter 24 | , longTon 25 | , mechanicalHorsepower 26 | , meter 27 | , metricHorsepower 28 | , mile 29 | , mole 30 | , newton 31 | , newtonMeter 32 | , ounce 33 | , parsec 34 | , pascal 35 | , peck 36 | , pica 37 | , point 38 | , pound 39 | , poundFoot 40 | , poundForce 41 | , shortTon 42 | , squareFoot 43 | , squareInch 44 | , squareMile 45 | , squareYard 46 | , usDryGallon 47 | , usDryPint 48 | , usDryQuart 49 | , usFluidOunce 50 | , usLiquidGallon 51 | , usLiquidPint 52 | , usLiquidQuart 53 | , watt 54 | , week 55 | , yard 56 | ) 57 | 58 | {-| All conversion factors sourced from [National Institute of Standards and Technology (NIST)][1] 59 | unless otherwise specified. 60 | 61 | [1]: https://www.nist.gov/pml/weights-and-measures/publications/nist-handbooks/handbook-44 62 | 63 | -} 64 | 65 | ---------- UNITS OF LENGTH (in meters) ---------- 66 | 67 | 68 | astronomicalUnit : Float 69 | astronomicalUnit = 70 | 149597870700 * meter 71 | 72 | 73 | lightYear : Float 74 | lightYear = 75 | 9460730472580800 * meter 76 | 77 | 78 | parsec : Float 79 | parsec = 80 | (648000 / pi) * astronomicalUnit 81 | 82 | 83 | meter : Float 84 | meter = 85 | 1.0 86 | 87 | 88 | inch : Float 89 | inch = 90 | 0.0254 * meter 91 | 92 | 93 | foot : Float 94 | foot = 95 | 12 * inch 96 | 97 | 98 | yard : Float 99 | yard = 100 | 3 * foot 101 | 102 | 103 | mile : Float 104 | mile = 105 | 5280 * foot 106 | 107 | 108 | cssPixel : Float 109 | cssPixel = 110 | inch / 96 111 | 112 | 113 | point : Float 114 | point = 115 | inch / 72 116 | 117 | 118 | pica : Float 119 | pica = 120 | inch / 6 121 | 122 | 123 | 124 | ---------- UNITS OF AREA (in squared meters) ---------- 125 | 126 | 127 | squareInch : Float 128 | squareInch = 129 | inch * inch 130 | 131 | 132 | squareFoot : Float 133 | squareFoot = 134 | foot * foot 135 | 136 | 137 | squareYard : Float 138 | squareYard = 139 | yard * yard 140 | 141 | 142 | squareMile : Float 143 | squareMile = 144 | mile * mile 145 | 146 | 147 | acre : Float 148 | acre = 149 | 43560 * squareFoot 150 | 151 | 152 | 153 | ---------- UNITS OF VOLUME (in cubic meters) ---------- 154 | 155 | 156 | cubicMeter : Float 157 | cubicMeter = 158 | meter * meter * meter 159 | 160 | 161 | liter : Float 162 | liter = 163 | 0.001 * cubicMeter 164 | 165 | 166 | {-| Sourced from [UK Weights and Measures Act][1]. One imperial gallon is equal to 167 | 4.54609 cubic decimeters (formerly defined as the volume of one kilogram 168 | of pure water under standard conditions, now equal to 1 liter). 169 | 170 | [1]: https://www.legislation.gov.uk/ukpga/1985/72#tgp10-tbl10-tbd1-tr22 171 | 172 | -} 173 | imperialGallon : Float 174 | imperialGallon = 175 | 4.54609 * liter 176 | 177 | 178 | imperialQuart : Float 179 | imperialQuart = 180 | imperialGallon / 4 181 | 182 | 183 | imperialPint : Float 184 | imperialPint = 185 | imperialQuart / 2 186 | 187 | 188 | imperialFluidOunce : Float 189 | imperialFluidOunce = 190 | imperialPint / 20 191 | 192 | 193 | cubicInch : Float 194 | cubicInch = 195 | inch * inch * inch 196 | 197 | 198 | cubicFoot : Float 199 | cubicFoot = 200 | foot * foot * foot 201 | 202 | 203 | cubicYard : Float 204 | cubicYard = 205 | yard * yard * yard 206 | 207 | 208 | usLiquidGallon : Float 209 | usLiquidGallon = 210 | 231 * cubicInch 211 | 212 | 213 | usLiquidQuart : Float 214 | usLiquidQuart = 215 | usLiquidGallon / 4 216 | 217 | 218 | usLiquidPint : Float 219 | usLiquidPint = 220 | usLiquidQuart / 2 221 | 222 | 223 | usFluidOunce : Float 224 | usFluidOunce = 225 | usLiquidPint / 16 226 | 227 | 228 | bushel : Float 229 | bushel = 230 | 2150.42 * cubicInch 231 | 232 | 233 | peck : Float 234 | peck = 235 | bushel / 4 236 | 237 | 238 | usDryGallon : Float 239 | usDryGallon = 240 | peck / 2 241 | 242 | 243 | usDryQuart : Float 244 | usDryQuart = 245 | 67.200625 * cubicInch 246 | 247 | 248 | usDryPint : Float 249 | usDryPint = 250 | usDryQuart / 2 251 | 252 | 253 | 254 | ---------- UNITS OF MASS (in kilograms) ---------- 255 | 256 | 257 | longTon : Float 258 | longTon = 259 | 2240 * pound 260 | 261 | 262 | shortTon : Float 263 | shortTon = 264 | 2000 * pound 265 | 266 | 267 | kilogram : Float 268 | kilogram = 269 | 1 270 | 271 | 272 | pound : Float 273 | pound = 274 | 0.45359237 * kilogram 275 | 276 | 277 | ounce : Float 278 | ounce = 279 | pound / 16 280 | 281 | 282 | 283 | ---------- UNITS OF DURATION (in seconds) ---------- 284 | 285 | 286 | second : Float 287 | second = 288 | 1 289 | 290 | 291 | minute : Float 292 | minute = 293 | 60 * second 294 | 295 | 296 | hour : Float 297 | hour = 298 | 60 * minute 299 | 300 | 301 | day : Float 302 | day = 303 | 24 * hour 304 | 305 | 306 | week : Float 307 | week = 308 | 7 * day 309 | 310 | 311 | julianYear : Float 312 | julianYear = 313 | 365.25 * day 314 | 315 | 316 | 317 | ---------- UNITS OF SUBSTANCE AMOUNT (in moles) ---------- 318 | 319 | 320 | mole : Float 321 | mole = 322 | 1 323 | 324 | 325 | 326 | ---------- UNITS OF ACELERATION (in meters per second squared) ---------- 327 | 328 | 329 | gee : Float 330 | gee = 331 | 9.80665 * meter / (second * second) 332 | 333 | 334 | 335 | ---------- UNITS OF FORCE (in newtons) ---------- 336 | 337 | 338 | poundForce : Float 339 | poundForce = 340 | 4.4482216152605 * newton 341 | 342 | 343 | newton : Float 344 | newton = 345 | kilogram * meter / (second * second) 346 | 347 | 348 | 349 | ---------- UNITS OF POWER (in watts) ---------- 350 | 351 | 352 | electricalHorsepower : Float 353 | electricalHorsepower = 354 | 746 * watt 355 | 356 | 357 | mechanicalHorsepower : Float 358 | mechanicalHorsepower = 359 | 33000 * foot * poundForce / minute 360 | 361 | 362 | metricHorsepower : Float 363 | metricHorsepower = 364 | 75 * kilogram * gee * meter / second 365 | 366 | 367 | watt : Float 368 | watt = 369 | newton * meter / second 370 | 371 | 372 | 373 | ---------- UNITS OF PRESSURE (in pascals) ---------- 374 | 375 | 376 | atmosphere : Float 377 | atmosphere = 378 | 101325 * pascal 379 | 380 | 381 | pascal : Float 382 | pascal = 383 | newton / (meter * meter) 384 | 385 | 386 | 387 | -------- UNITS OF TORQUE (in newton-meters) --------- 388 | 389 | 390 | newtonMeter : Float 391 | newtonMeter = 392 | newton * meter 393 | 394 | 395 | poundFoot : Float 396 | poundFoot = 397 | poundForce * foot 398 | -------------------------------------------------------------------------------- /src/Duration.elm: -------------------------------------------------------------------------------- 1 | module Duration exposing 2 | ( Duration, Seconds 3 | , seconds, inSeconds, milliseconds, inMilliseconds, minutes, inMinutes, hours, inHours, days, inDays, weeks, inWeeks, julianYears, inJulianYears 4 | , from 5 | , addTo, subtractFrom 6 | , second, millisecond, minute, hour, day, week, julianYear 7 | ) 8 | 9 | {-| A `Duration` refers to an elapsed time in seconds, milliseconds, hours etc., 10 | as opposed to a specific instant in time (which would generally be represented 11 | by a [`Posix`](https://package.elm-lang.org/packages/elm/time/latest/Time#Posix) 12 | value). It is stored as a number of seconds. 13 | 14 | @docs Duration, Seconds 15 | 16 | 17 | ## Conversions 18 | 19 | @docs seconds, inSeconds, milliseconds, inMilliseconds, minutes, inMinutes, hours, inHours, days, inDays, weeks, inWeeks, julianYears, inJulianYears 20 | 21 | 22 | ## Working with `Time.Posix` values 23 | 24 | @docs from 25 | 26 | 27 | ### Offsetting 28 | 29 | `addTo` and `subtractFrom` can be used to offset a [`Time.Posix`](https://package.elm-lang.org/packages/elm/time/latest/Time#Posix) 30 | value by a given `Duration`. However, note that `Time.Posix` values are stored 31 | as an integer number of milliseconds, so the offset amount will be rounded to 32 | the nearest number of milliseconds. 33 | 34 | @docs addTo, subtractFrom 35 | 36 | 37 | ## Constants 38 | 39 | Shorthand for `Duration.seconds 1`, `Duration.hours 1` etc. Can be convenient to 40 | use with [`Quantity.per`](Quantity#per). 41 | 42 | @docs second, millisecond, minute, hour, day, week, julianYear 43 | 44 | -} 45 | 46 | import Constants 47 | import Quantity exposing (Quantity(..)) 48 | import Time 49 | 50 | 51 | {-| -} 52 | type Seconds 53 | = Seconds 54 | 55 | 56 | {-| -} 57 | type alias Duration = 58 | Quantity Float Seconds 59 | 60 | 61 | {-| Find the elapsed time from a start time to an end time. For example, 62 | assuming that `nineAM` and `fivePM` are two [`Time.Posix`](https://package.elm-lang.org/packages/elm/time/latest/Time#Posix) 63 | values on the same day: 64 | 65 | Duration.from nineAM fivePM 66 | --> Duration.hours 8 67 | 68 | -} 69 | from : Time.Posix -> Time.Posix -> Duration 70 | from startTime endTime = 71 | let 72 | numMilliseconds = 73 | Time.posixToMillis endTime - Time.posixToMillis startTime 74 | in 75 | milliseconds (toFloat numMilliseconds) 76 | 77 | 78 | {-| Construct a `Duration` from a given number of seconds. 79 | -} 80 | seconds : Float -> Duration 81 | seconds numSeconds = 82 | Quantity numSeconds 83 | 84 | 85 | {-| Convert a `Duration` to a value in seconds. 86 | 87 | Duration.milliseconds 10 |> Duration.inSeconds 88 | --> 0.01 89 | 90 | -} 91 | inSeconds : Duration -> Float 92 | inSeconds (Quantity numSeconds) = 93 | numSeconds 94 | 95 | 96 | {-| Construct a `Duration` from a given number of milliseconds. 97 | 98 | Duration.milliseconds 5000 99 | --> Duration.seconds 5 100 | 101 | -} 102 | milliseconds : Float -> Duration 103 | milliseconds numMilliseconds = 104 | seconds (0.001 * numMilliseconds) 105 | 106 | 107 | {-| Convert a `Duration` to a value in milliseconds. 108 | 109 | Duration.seconds 0.5 |> Duration.inMilliseconds 110 | --> 500 111 | 112 | -} 113 | inMilliseconds : Duration -> Float 114 | inMilliseconds duration = 115 | inSeconds duration * 1000 116 | 117 | 118 | {-| Construct a `Duration` from a given number of minutes. 119 | 120 | Duration.minutes 3 121 | --> Duration.seconds 180 122 | 123 | -} 124 | minutes : Float -> Duration 125 | minutes numMinutes = 126 | seconds (60 * numMinutes) 127 | 128 | 129 | {-| Convert a `Duration` to a value in minutes. 130 | 131 | Duration.seconds 90 |> Duration.inMinutes 132 | --> 1.5 133 | 134 | -} 135 | inMinutes : Duration -> Float 136 | inMinutes duration = 137 | inSeconds duration / 60 138 | 139 | 140 | {-| Construct a `Duration` from a given number of hours. 141 | 142 | Duration.hours 1 143 | --> Duration.seconds 3600 144 | 145 | -} 146 | hours : Float -> Duration 147 | hours numHours = 148 | seconds (Constants.hour * numHours) 149 | 150 | 151 | {-| Convert a `Duration` to a value in hours. 152 | 153 | Duration.minutes 120 |> Duration.inHours 154 | --> 2 155 | 156 | -} 157 | inHours : Duration -> Float 158 | inHours duration = 159 | inSeconds duration / Constants.hour 160 | 161 | 162 | {-| Construct a `Duration` from a given number of days. A day is defined as 163 | exactly 24 hours or 86400 seconds. Therefore, it is only equal to the length of 164 | a given calendar day if that calendar day does not include either a leap second 165 | or any added/removed daylight savings hours. 166 | 167 | Duration.days 1 168 | --> Duration.hours 24 169 | 170 | -} 171 | days : Float -> Duration 172 | days numDays = 173 | seconds (Constants.day * numDays) 174 | 175 | 176 | {-| Convert a `Duration` to a value in days. 177 | 178 | Duration.hours 72 |> Duration.inDays 179 | --> 3 180 | 181 | -} 182 | inDays : Duration -> Float 183 | inDays duration = 184 | inSeconds duration / Constants.day 185 | 186 | 187 | {-| Construct a `Duration` from a given number of weeks. 188 | 189 | Duration.weeks 1 190 | --> Duration.days 7 191 | 192 | -} 193 | weeks : Float -> Duration 194 | weeks numWeeks = 195 | seconds (Constants.week * numWeeks) 196 | 197 | 198 | {-| Convert a `Duration` to a value in weeks. 199 | 200 | Duration.days 28 |> Duration.inWeeks 201 | --> 4 202 | 203 | -} 204 | inWeeks : Duration -> Float 205 | inWeeks duration = 206 | inSeconds duration / Constants.week 207 | 208 | 209 | {-| Construct a `Duration` from a given number of [Julian years][julian_year]. 210 | A Julian year is defined as exactly 365.25 days, the average length of a year in 211 | the historical Julian calendar. This is 10 minutes and 48 seconds longer than 212 | a Gregorian year (365.2425 days), which is the average length of a year in the 213 | modern Gregorian calendar, but the Julian year is a bit easier to remember and 214 | reason about and has the virtue of being the 'year' value used in the definition 215 | of a [light year](Length#lightYears). 216 | 217 | Duration.julianYears 1 218 | --> Duration.days 365.25 219 | 220 | [julian_year]: https://en.wikipedia.org/wiki/Julian_year_(astronomy) "Julian year" 221 | 222 | -} 223 | julianYears : Float -> Duration 224 | julianYears numJulianYears = 225 | seconds (Constants.julianYear * numJulianYears) 226 | 227 | 228 | {-| Convert a `Duration` to a value in Julian years. 229 | 230 | Duration.hours 10000 |> Duration.inJulianYears 231 | --> 1.1407711613050422 232 | 233 | -} 234 | inJulianYears : Duration -> Float 235 | inJulianYears duration = 236 | inSeconds duration / Constants.julianYear 237 | 238 | 239 | {-| Offset a [`Time.Posix`](https://package.elm-lang.org/packages/elm/time/latest/Time#Posix) 240 | value forwards in time by a given `Duration`: 241 | 242 | -- Assuming that 'now' is a Time.Posix value obtained 243 | -- from Time.now 244 | threeHoursFromNow = 245 | Duration.addTo now (Duration.hours 3) 246 | 247 | -} 248 | addTo : Time.Posix -> Duration -> Time.Posix 249 | addTo time duration = 250 | Time.millisToPosix 251 | (Time.posixToMillis time + round (inMilliseconds duration)) 252 | 253 | 254 | {-| Offset a [`Time.Posix`](https://package.elm-lang.org/packages/elm/time/latest/Time#Posix) 255 | value backwards in time by a given `Duration`: 256 | 257 | -- Assuming that 'now' is a Time.Posix value obtained 258 | -- from Time.now 259 | fiveMinutesAgo = 260 | Duration.subtractFrom now (Duration.minutes 5) 261 | 262 | `Duration.subtractFrom time duration` is equivalent to `Duration.addTo time 263 | (Quantity.negate duration)`. 264 | 265 | -} 266 | subtractFrom : Time.Posix -> Duration -> Time.Posix 267 | subtractFrom time duration = 268 | Time.millisToPosix 269 | (Time.posixToMillis time - round (inMilliseconds duration)) 270 | 271 | 272 | {-| -} 273 | second : Duration 274 | second = 275 | seconds 1 276 | 277 | 278 | {-| -} 279 | millisecond : Duration 280 | millisecond = 281 | milliseconds 1 282 | 283 | 284 | {-| -} 285 | minute : Duration 286 | minute = 287 | minutes 1 288 | 289 | 290 | {-| -} 291 | hour : Duration 292 | hour = 293 | hours 1 294 | 295 | 296 | {-| -} 297 | day : Duration 298 | day = 299 | days 1 300 | 301 | 302 | {-| -} 303 | week : Duration 304 | week = 305 | weeks 1 306 | 307 | 308 | {-| -} 309 | julianYear : Duration 310 | julianYear = 311 | julianYears 1 312 | -------------------------------------------------------------------------------- /src/Angle.elm: -------------------------------------------------------------------------------- 1 | module Angle exposing 2 | ( Angle, Radians 3 | , radians, inRadians, degrees, inDegrees, turns, inTurns 4 | , minutes, inMinutes, seconds, inSeconds 5 | , Sign(..), fromDms, toDms 6 | , sin, cos, tan, asin, acos, atan, atan2 7 | , normalize 8 | , radian, degree, turn, minute, second 9 | ) 10 | 11 | {-| An `Angle` represents an angle in degrees, radians, or turns. It is stored 12 | as a number of radians. 13 | 14 | @docs Angle, Radians 15 | 16 | 17 | ## Common units 18 | 19 | @docs radians, inRadians, degrees, inDegrees, turns, inTurns 20 | 21 | 22 | ## Minutes and seconds 23 | 24 | Angles are sometimes measured in degrees, minutes, and seconds, where 1 minute = 25 | 1/60th of a degree and 1 second = 1/60th of a minute. 26 | 27 | @docs minutes, inMinutes, seconds, inSeconds 28 | 29 | Degrees, minutes and seconds are often used together, so a couple of special 30 | functions are provided to convert to and from combinations of those units. 31 | 32 | @docs Sign, fromDms, toDms 33 | 34 | 35 | ## Trigonometry 36 | 37 | If you're using `Angle` values instead of plain `Float`s, you'll need to use 38 | these functions instead of [the corresponding ones in core][1]. 39 | 40 | [1]: https://package.elm-lang.org/packages/elm/core/latest/Basics#trigonometry 41 | 42 | @docs sin, cos, tan, asin, acos, atan, atan2 43 | 44 | 45 | ## Normalization 46 | 47 | @docs normalize 48 | 49 | 50 | ## Constants 51 | 52 | Shorthand for `Angle.radians 1`, `Angle.degrees 1` etc. Can be convenient to use 53 | with [`Quantity.per`](Quantity#per). 54 | 55 | @docs radian, degree, turn, minute, second 56 | 57 | -} 58 | 59 | import Quantity exposing (Quantity(..), zero) 60 | 61 | 62 | {-| -} 63 | type Radians 64 | = Radians 65 | 66 | 67 | {-| -} 68 | type alias Angle = 69 | Quantity Float Radians 70 | 71 | 72 | {-| Construct an angle from a number of radians. 73 | -} 74 | radians : Float -> Angle 75 | radians numRadians = 76 | Quantity numRadians 77 | 78 | 79 | {-| Convert an angle to a number of radians. 80 | -} 81 | inRadians : Angle -> Float 82 | inRadians (Quantity numRadians) = 83 | numRadians 84 | 85 | 86 | {-| Construct an angle from a number of degrees. 87 | 88 | Angle.degrees 180 89 | --> Angle.radians pi 90 | 91 | -} 92 | degrees : Float -> Angle 93 | degrees numDegrees = 94 | radians (pi * (numDegrees / 180)) 95 | 96 | 97 | {-| Convert an angle to a number of degrees. 98 | 99 | Angle.turns 2 |> Angle.inDegrees 100 | --> 720 101 | 102 | -} 103 | inDegrees : Angle -> Float 104 | inDegrees angle = 105 | 180 * (inRadians angle / pi) 106 | 107 | 108 | {-| Construct an angle from a number of turns. 109 | 110 | Angle.turns -0.25 111 | --> Angle.degrees -90 112 | 113 | -} 114 | turns : Float -> Angle 115 | turns numTurns = 116 | radians (2 * pi * numTurns) 117 | 118 | 119 | {-| Convert an angle to a number of turns. 120 | 121 | Angle.radians pi |> Angle.inTurns 122 | --> 0.5 123 | 124 | -} 125 | inTurns : Angle -> Float 126 | inTurns angle = 127 | inRadians angle / (2 * pi) 128 | 129 | 130 | {-| Construct an angle from a number of minutes. 131 | 132 | Angle.minutes 30 133 | --> Angle.degrees 0.5 134 | 135 | -} 136 | minutes : Float -> Angle 137 | minutes numMinutes = 138 | degrees (numMinutes / 60) 139 | 140 | 141 | {-| Convert an angle to a number of minutes. 142 | 143 | Angle.degrees 2 |> Angle.inMinutes 144 | --> 120 145 | 146 | -} 147 | inMinutes : Angle -> Float 148 | inMinutes angle = 149 | 60 * inDegrees angle 150 | 151 | 152 | {-| Construct an angle from a number of seconds. 153 | 154 | Angle.seconds 120 155 | --> Angle.minutes 2 156 | 157 | -} 158 | seconds : Float -> Angle 159 | seconds numSeconds = 160 | degrees (numSeconds / 3600) 161 | 162 | 163 | {-| Convert an angle to a number of seconds. 164 | 165 | Angle.degrees 0.1 |> Angle.inSeconds 166 | --> 360 167 | 168 | -} 169 | inSeconds : Angle -> Float 170 | inSeconds angle = 171 | 3600 * inDegrees angle 172 | 173 | 174 | {-| The sign of an angle given in degrees, minutes and seconds. 175 | -} 176 | type Sign 177 | = Positive 178 | | Negative 179 | 180 | 181 | {-| Construct an angle given its sign and its degree, minute and second 182 | components. The signs of `degrees`, `minutes` and `seconds` will be ignored 183 | (their absolute values will be used). Note that only `seconds` may be 184 | fractional! In general `minutes` and `seconds` should each be less than 60, but 185 | this is not enforced. 186 | 187 | Angle.fromDms 188 | { sign = Angle.Positive 189 | , degrees = 45 190 | , minutes = 30 191 | , seconds = 36 192 | } 193 | --> Angle.degrees 45.51 194 | 195 | Angle.fromDms 196 | { sign = Angle.Negative 197 | , degrees = 2 198 | , minutes = 15 199 | , seconds = 0 200 | } 201 | --> Angle.degrees -2.25 202 | 203 | -} 204 | fromDms : { sign : Sign, degrees : Int, minutes : Int, seconds : Float } -> Angle 205 | fromDms given = 206 | let 207 | absDegrees = 208 | toFloat (abs given.degrees) 209 | + (toFloat (abs given.minutes) / 60) 210 | + (abs given.seconds / 3600) 211 | in 212 | case given.sign of 213 | Positive -> 214 | degrees absDegrees 215 | 216 | Negative -> 217 | degrees -absDegrees 218 | 219 | 220 | {-| Convert an angle to a number of degrees, minutes and seconds, along with its 221 | sign. The `degrees`, `minutes` and `seconds` values will all be non-negative, 222 | and both `minutes` and `seconds` will be less than 60. 223 | 224 | Angle.toDms (Angle.degrees 1.5) 225 | --> { sign = Angle.Positive 226 | --> , degrees = 1 227 | --> , minutes = 30 228 | --> , seconds = 0 229 | --> } 230 | 231 | Angle.toDms (Angle.degrees -0.751) 232 | --> { sign = Angle.Negative 233 | --> , degrees = 0 234 | --> , minutes = 45 235 | --> , seconds = 3.6 236 | --> } 237 | 238 | You could use this to write a string-conversion function for angles, something 239 | like: 240 | 241 | angleString angle = 242 | let 243 | { sign, degrees, minutes, seconds } = 244 | Angle.toDms angle 245 | 246 | signString = 247 | case sign of 248 | Angle.Positive -> 249 | "" 250 | 251 | Angle.Negative -> 252 | "-" 253 | in 254 | String.concat 255 | [ signString 256 | , String.fromInt degrees 257 | , "° " 258 | , String.fromInt minutes 259 | , "′ " 260 | , Round.round 3 seconds 261 | , "″" 262 | ] 263 | 264 | (Here we're using the 265 | [myrho/elm-round](https://package.elm-lang.org/packages/myrho/elm-round/latest/) 266 | package to control the number of decimal places used when displaying the number 267 | of seconds.) 268 | 269 | -} 270 | toDms : Angle -> { sign : Sign, degrees : Int, minutes : Int, seconds : Float } 271 | toDms angle = 272 | let 273 | signedDegrees = 274 | inDegrees angle 275 | 276 | sign = 277 | if signedDegrees >= 0 then 278 | Positive 279 | 280 | else 281 | Negative 282 | 283 | numDegrees = 284 | abs signedDegrees 285 | 286 | integerDegrees = 287 | floor numDegrees 288 | 289 | fractionalDegrees = 290 | numDegrees - toFloat integerDegrees 291 | 292 | numMinutes = 293 | fractionalDegrees * 60 294 | 295 | integerMinutes = 296 | floor numMinutes 297 | 298 | fractionalMinutes = 299 | numMinutes - toFloat integerMinutes 300 | 301 | numSeconds = 302 | fractionalMinutes * 60 303 | in 304 | { sign = sign 305 | , degrees = integerDegrees 306 | , minutes = integerMinutes 307 | , seconds = numSeconds 308 | } 309 | 310 | 311 | {-| -} 312 | sin : Angle -> Float 313 | sin (Quantity angle) = 314 | Basics.sin angle 315 | 316 | 317 | {-| -} 318 | cos : Angle -> Float 319 | cos (Quantity angle) = 320 | Basics.cos angle 321 | 322 | 323 | {-| -} 324 | tan : Angle -> Float 325 | tan (Quantity angle) = 326 | Basics.tan angle 327 | 328 | 329 | {-| -} 330 | asin : Float -> Angle 331 | asin x = 332 | Quantity (Basics.asin x) 333 | 334 | 335 | {-| -} 336 | acos : Float -> Angle 337 | acos x = 338 | Quantity (Basics.acos x) 339 | 340 | 341 | {-| -} 342 | atan : Float -> Angle 343 | atan x = 344 | Quantity (Basics.atan x) 345 | 346 | 347 | {-| -} 348 | atan2 : Quantity Float units -> Quantity Float units -> Angle 349 | atan2 (Quantity y) (Quantity x) = 350 | Quantity (Basics.atan2 y x) 351 | 352 | 353 | {-| Convert an arbitrary angle to the equivalent angle in the range -180 to 180 354 | degrees (-π to π radians), by adding or subtracting some multiple of 360 355 | degrees (2π radians) if necessary. 356 | 357 | Angle.normalize (Angle.degrees 45) 358 | --> Angle.degrees 45 359 | 360 | Angle.normalize (Angle.degrees 270) 361 | --> Angle.degrees -90 362 | 363 | Angle.normalize (Angle.degrees 370) 364 | --> Angle.degrees 10 365 | 366 | Angle.normalize (Angle.degrees 181) 367 | --> Angle.degrees -179 368 | 369 | -} 370 | normalize : Angle -> Angle 371 | normalize (Quantity angle) = 372 | Quantity <| 373 | clamp -pi pi <| 374 | (angle - 2 * pi * toFloat (round (angle / (2 * pi)))) 375 | 376 | 377 | {-| -} 378 | radian : Angle 379 | radian = 380 | radians 1 381 | 382 | 383 | {-| -} 384 | degree : Angle 385 | degree = 386 | degrees 1 387 | 388 | 389 | {-| -} 390 | turn : Angle 391 | turn = 392 | turns 1 393 | 394 | 395 | {-| -} 396 | minute : Angle 397 | minute = 398 | minutes 1 399 | 400 | 401 | {-| -} 402 | second : Angle 403 | second = 404 | seconds 1 405 | -------------------------------------------------------------------------------- /src/Length.elm: -------------------------------------------------------------------------------- 1 | module Length exposing 2 | ( Length, Meters 3 | , meters, inMeters 4 | , angstroms, inAngstroms, nanometers, inNanometers, microns, inMicrons, millimeters, inMillimeters, centimeters, inCentimeters, kilometers, inKilometers 5 | , thou, inThou, inches, inInches, feet, inFeet, yards, inYards, miles, inMiles 6 | , cssPixels, inCssPixels, points, inPoints, picas, inPicas 7 | , astronomicalUnits, inAstronomicalUnits, parsecs, inParsecs, lightYears, inLightYears 8 | , meter, angstrom, nanometer, micron, millimeter, centimeter, kilometer 9 | , inch, foot, yard, mile 10 | , astronomicalUnit, parsec, lightYear 11 | ) 12 | 13 | {-| A `Length` represents a length in meters, feet, centimeters, miles etc. It 14 | is stored as a number of meters. 15 | 16 | @docs Length, Meters 17 | 18 | 19 | ## Metric 20 | 21 | @docs meters, inMeters 22 | @docs angstroms, inAngstroms, nanometers, inNanometers, microns, inMicrons, millimeters, inMillimeters, centimeters, inCentimeters, kilometers, inKilometers 23 | 24 | 25 | ## Imperial 26 | 27 | @docs thou, inThou, inches, inInches, feet, inFeet, yards, inYards, miles, inMiles 28 | 29 | 30 | ## CSS and typography 31 | 32 | @docs cssPixels, inCssPixels, points, inPoints, picas, inPicas 33 | 34 | 35 | ## Astronomical 36 | 37 | @docs astronomicalUnits, inAstronomicalUnits, parsecs, inParsecs, lightYears, inLightYears 38 | 39 | 40 | ## Constants 41 | 42 | Shorthand for `Length.meters 1`, `Length.feet 1` etc. Can be convenient to use 43 | with [`Quantity.per`](Quantity#per). 44 | 45 | Note that `thou` is omitted since it doesn't have separate singular and plural 46 | forms. 47 | 48 | @docs meter, angstrom, nanometer, micron, millimeter, centimeter, kilometer 49 | @docs inch, foot, yard, mile 50 | @docs astronomicalUnit, parsec, lightYear 51 | 52 | -} 53 | 54 | import Constants 55 | import Quantity exposing (Quantity(..)) 56 | 57 | 58 | {-| -} 59 | type Meters 60 | = Meters 61 | 62 | 63 | {-| -} 64 | type alias Length = 65 | Quantity Float Meters 66 | 67 | 68 | {-| Construct a length from a number of meters. 69 | 70 | height : Length 71 | height = 72 | Length.meters 2 73 | 74 | -} 75 | meters : Float -> Length 76 | meters numMeters = 77 | Quantity numMeters 78 | 79 | 80 | {-| Convert a length to a number of meters. 81 | 82 | Length.feet 1 |> Length.inMeters 83 | --> 0.3048 84 | 85 | -} 86 | inMeters : Length -> Float 87 | inMeters (Quantity numMeters) = 88 | numMeters 89 | 90 | 91 | {-| Construct a length from a number of angstroms. 92 | 93 | Length.angstroms 1 94 | --> Length.meters 1e-10 95 | 96 | -} 97 | angstroms : Float -> Length 98 | angstroms numAngstroms = 99 | meters (1.0e-10 * numAngstroms) 100 | 101 | 102 | {-| Convert a length to a number of angstroms. 103 | 104 | Length.nanometers 1 |> Length.inAngstroms 105 | --> 10 106 | 107 | -} 108 | inAngstroms : Length -> Float 109 | inAngstroms length = 110 | 1.0e10 * inMeters length 111 | 112 | 113 | {-| Construct a length from a number of nanometers. 114 | 115 | Length.nanometers 1 116 | --> Length.meters 1e-9 117 | 118 | -} 119 | nanometers : Float -> Length 120 | nanometers numNanometers = 121 | meters (1.0e-9 * numNanometers) 122 | 123 | 124 | {-| Convert a length to a number of nanometers. 125 | 126 | Length.microns 1 |> Length.inNanometers 127 | --> 1000 128 | 129 | -} 130 | inNanometers : Length -> Float 131 | inNanometers length = 132 | 1.0e9 * inMeters length 133 | 134 | 135 | {-| Construct a length from a number of microns (micrometers). 136 | 137 | Length.microns 1 138 | --> Length.meters 1e-6 139 | 140 | -} 141 | microns : Float -> Length 142 | microns numMicrons = 143 | meters (1.0e-6 * numMicrons) 144 | 145 | 146 | {-| Convert a length to a number of microns (micrometers). 147 | 148 | Length.millimeters 1 |> Length.inMicrons 149 | --> 1000 150 | 151 | -} 152 | inMicrons : Length -> Float 153 | inMicrons length = 154 | 1.0e6 * inMeters length 155 | 156 | 157 | {-| Construct a length from number of millimeters. 158 | -} 159 | millimeters : Float -> Length 160 | millimeters numMillimeters = 161 | meters (0.001 * numMillimeters) 162 | 163 | 164 | {-| Convert a length to number of millimeters. 165 | -} 166 | inMillimeters : Length -> Float 167 | inMillimeters length = 168 | 1000 * inMeters length 169 | 170 | 171 | {-| Construct a length from a number of thou (thousandths of an inch). 172 | 173 | Length.thou 5 174 | --> Length.inches 0.005 175 | 176 | -} 177 | thou : Float -> Length 178 | thou numThou = 179 | meters (Constants.inch * 0.001 * numThou) 180 | 181 | 182 | {-| Convert a length to a number of thou (thousandths of an inch). 183 | 184 | Length.millimeters 1 |> Length.inThou 185 | --> 39.37007874015748 186 | 187 | -} 188 | inThou : Length -> Float 189 | inThou length = 190 | inMeters length / (Constants.inch * 0.001) 191 | 192 | 193 | {-| Construct a length from a number of inches. 194 | -} 195 | inches : Float -> Length 196 | inches numInches = 197 | meters (Constants.inch * numInches) 198 | 199 | 200 | {-| Convert a length to a number of inches. 201 | -} 202 | inInches : Length -> Float 203 | inInches length = 204 | inMeters length / Constants.inch 205 | 206 | 207 | {-| Construct a length from a number of centimeters. 208 | -} 209 | centimeters : Float -> Length 210 | centimeters numCentimeters = 211 | meters (0.01 * numCentimeters) 212 | 213 | 214 | {-| Convert a length to a number of centimeters. 215 | -} 216 | inCentimeters : Length -> Float 217 | inCentimeters length = 218 | 100 * inMeters length 219 | 220 | 221 | {-| Construct a length from a number of feet. 222 | -} 223 | feet : Float -> Length 224 | feet numFeet = 225 | meters (Constants.foot * numFeet) 226 | 227 | 228 | {-| Convert a length to a number of feet. 229 | -} 230 | inFeet : Length -> Float 231 | inFeet length = 232 | inMeters length / Constants.foot 233 | 234 | 235 | {-| Construct a length from a number of yards. 236 | -} 237 | yards : Float -> Length 238 | yards numYards = 239 | meters (Constants.yard * numYards) 240 | 241 | 242 | {-| Convert a length to a number of yards. 243 | -} 244 | inYards : Length -> Float 245 | inYards length = 246 | inMeters length / Constants.yard 247 | 248 | 249 | {-| Construct a length from a number of kilometers. 250 | -} 251 | kilometers : Float -> Length 252 | kilometers numKilometers = 253 | meters (1000 * numKilometers) 254 | 255 | 256 | {-| Convert a length to a number of kilometers. 257 | -} 258 | inKilometers : Length -> Float 259 | inKilometers length = 260 | 0.001 * inMeters length 261 | 262 | 263 | {-| Construct a length from a number of miles. 264 | -} 265 | miles : Float -> Length 266 | miles numMiles = 267 | meters (Constants.mile * numMiles) 268 | 269 | 270 | {-| Convert a length to a number of miles. 271 | -} 272 | inMiles : Length -> Float 273 | inMiles length = 274 | inMeters length / Constants.mile 275 | 276 | 277 | {-| Construct a length from a number of [CSS pixels](https://drafts.csswg.org/css-values-3/#absolute-lengths), 278 | defined as 1/96 of an inch. 279 | 280 | Note the difference between this function and [`Pixels.pixels`](Pixels#pixels). 281 | `Length.cssPixels 1` is equivalent to `Length.inches (1 / 96)` or 282 | approximately `Length.millimeters 0.264583`; it returns a length in _real world_ 283 | units equal to the (nominal) physical size of one CSS pixel. 284 | 285 | In contrast, `Pixels.pixels 1` simply returns an abstract "1 pixel" value. You 286 | can think of `Length.cssPixels 1` as a shorthand for 287 | 288 | Pixels.pixels 1 289 | |> Quantity.at_ 290 | (Pixels.pixels 96 291 | |> Quantity.per (Length.inches 1) 292 | ) 293 | 294 | That is, `Length.cssPixels 1` is the size of 1 pixel at a resolution of 96 DPI. 295 | 296 | -} 297 | cssPixels : Float -> Length 298 | cssPixels numCssPixels = 299 | meters (Constants.cssPixel * numCssPixels) 300 | 301 | 302 | {-| Convert a length to a number of CSS pixels. 303 | -} 304 | inCssPixels : Length -> Float 305 | inCssPixels length = 306 | inMeters length / Constants.cssPixel 307 | 308 | 309 | {-| Construct a length from a number of [points](https://en.wikipedia.org/wiki/Point_%28typography%29), 310 | defined as 1/72 of an inch. 311 | -} 312 | points : Float -> Length 313 | points numPoints = 314 | meters (Constants.point * numPoints) 315 | 316 | 317 | {-| Convert a length to a number of points. 318 | -} 319 | inPoints : Length -> Float 320 | inPoints length = 321 | inMeters length / Constants.point 322 | 323 | 324 | {-| Construct a length from a number of [picas](https://en.wikipedia.org/wiki/Pica_%28typography%29), 325 | defined as 1/6 of an inch. 326 | -} 327 | picas : Float -> Length 328 | picas numPicas = 329 | meters (Constants.pica * numPicas) 330 | 331 | 332 | {-| Convert a length to a number of picas. 333 | -} 334 | inPicas : Length -> Float 335 | inPicas length = 336 | inMeters length / Constants.pica 337 | 338 | 339 | {-| Construct a length from a number of [astronomical units][au] (AU). One AU is 340 | approximately equal to the average distance of the Earth from the Sun. 341 | 342 | [au]: https://en.wikipedia.org/wiki/Astronomical_unit "Astronomical unit" 343 | 344 | -} 345 | astronomicalUnits : Float -> Length 346 | astronomicalUnits numAstronomicalUnits = 347 | meters (Constants.astronomicalUnit * numAstronomicalUnits) 348 | 349 | 350 | {-| Convert a length to a number of astronomical units. 351 | -} 352 | inAstronomicalUnits : Length -> Float 353 | inAstronomicalUnits length = 354 | inMeters length / Constants.astronomicalUnit 355 | 356 | 357 | {-| Construct a length from a number of [parsecs][parsec]. 358 | 359 | [parsec]: https://en.wikipedia.org/wiki/Parsec "Parsec" 360 | 361 | -} 362 | parsecs : Float -> Length 363 | parsecs numParsecs = 364 | meters (numParsecs * Constants.parsec) 365 | 366 | 367 | {-| Convert a length to a number of parsecs. 368 | -} 369 | inParsecs : Length -> Float 370 | inParsecs length = 371 | inMeters length / Constants.parsec 372 | 373 | 374 | {-| Construct a length from a number of light years. One light year is the 375 | distance traveled when moving at the speed of light for one [Julian year](Duration#julianYear). 376 | -} 377 | lightYears : Float -> Length 378 | lightYears numLightYears = 379 | meters (Constants.lightYear * numLightYears) 380 | 381 | 382 | {-| Convert a length to a number of light years. 383 | -} 384 | inLightYears : Length -> Float 385 | inLightYears length = 386 | inMeters length / Constants.lightYear 387 | 388 | 389 | {-| -} 390 | meter : Length 391 | meter = 392 | meters 1 393 | 394 | 395 | {-| -} 396 | angstrom : Length 397 | angstrom = 398 | angstroms 1 399 | 400 | 401 | {-| -} 402 | nanometer : Length 403 | nanometer = 404 | nanometers 1 405 | 406 | 407 | {-| -} 408 | micron : Length 409 | micron = 410 | microns 1 411 | 412 | 413 | {-| -} 414 | millimeter : Length 415 | millimeter = 416 | millimeters 1 417 | 418 | 419 | {-| -} 420 | centimeter : Length 421 | centimeter = 422 | centimeters 1 423 | 424 | 425 | {-| -} 426 | kilometer : Length 427 | kilometer = 428 | kilometers 1 429 | 430 | 431 | {-| -} 432 | inch : Length 433 | inch = 434 | inches 1 435 | 436 | 437 | {-| -} 438 | foot : Length 439 | foot = 440 | feet 1 441 | 442 | 443 | {-| -} 444 | yard : Length 445 | yard = 446 | yards 1 447 | 448 | 449 | {-| -} 450 | mile : Length 451 | mile = 452 | miles 1 453 | 454 | 455 | {-| -} 456 | astronomicalUnit : Length 457 | astronomicalUnit = 458 | astronomicalUnits 1 459 | 460 | 461 | {-| -} 462 | parsec : Length 463 | parsec = 464 | parsecs 1 465 | 466 | 467 | {-| -} 468 | lightYear : Length 469 | lightYear = 470 | lightYears 1 471 | -------------------------------------------------------------------------------- /src/Volume.elm: -------------------------------------------------------------------------------- 1 | module Volume exposing 2 | ( Volume, CubicMeters 3 | , cubicMeters, inCubicMeters 4 | , milliliters, inMilliliters, liters, inLiters, cubicCentimeters, inCubicCentimeters 5 | , cubicInches, inCubicInches, cubicFeet, inCubicFeet, cubicYards, inCubicYards 6 | , usLiquidGallons, inUsLiquidGallons, usDryGallons, inUsDryGallons, imperialGallons, inImperialGallons 7 | , usLiquidQuarts, inUsLiquidQuarts, usDryQuarts, inUsDryQuarts, imperialQuarts, inImperialQuarts 8 | , usLiquidPints, inUsLiquidPints, usDryPints, inUsDryPints, imperialPints, inImperialPints 9 | , usFluidOunces, inUsFluidOunces, imperialFluidOunces, inImperialFluidOunces 10 | , cubicMeter, milliliter, liter, cubicCentimeter 11 | , cubicInch, cubicFoot, cubicYard 12 | , usLiquidGallon, usDryGallon, imperialGallon 13 | , usLiquidQuart, usDryQuart, imperialQuart 14 | , usLiquidPint, usDryPint, imperialPint 15 | , usFluidOunce, imperialFluidOunce 16 | ) 17 | 18 | {-| A `Volume` represents a volume in cubic meters, cubic feet, liters, US 19 | liquid gallons, imperial fluid ounces etc. It is stored as a number of cubic 20 | meters. 21 | 22 | @docs Volume, CubicMeters 23 | 24 | 25 | ## Metric 26 | 27 | @docs cubicMeters, inCubicMeters 28 | @docs milliliters, inMilliliters, liters, inLiters, cubicCentimeters, inCubicCentimeters 29 | 30 | 31 | ## Imperial 32 | 33 | @docs cubicInches, inCubicInches, cubicFeet, inCubicFeet, cubicYards, inCubicYards 34 | @docs usLiquidGallons, inUsLiquidGallons, usDryGallons, inUsDryGallons, imperialGallons, inImperialGallons 35 | @docs usLiquidQuarts, inUsLiquidQuarts, usDryQuarts, inUsDryQuarts, imperialQuarts, inImperialQuarts 36 | @docs usLiquidPints, inUsLiquidPints, usDryPints, inUsDryPints, imperialPints, inImperialPints 37 | @docs usFluidOunces, inUsFluidOunces, imperialFluidOunces, inImperialFluidOunces 38 | 39 | 40 | ## Constants 41 | 42 | Shorthand for `Volume.cubicMeters 1`, `Volume.imperialGallons 1` etc. Can be 43 | convenient to use with [`Quantity.per`](Quantity#per). 44 | 45 | @docs cubicMeter, milliliter, liter, cubicCentimeter 46 | @docs cubicInch, cubicFoot, cubicYard 47 | @docs usLiquidGallon, usDryGallon, imperialGallon 48 | @docs usLiquidQuart, usDryQuart, imperialQuart 49 | @docs usLiquidPint, usDryPint, imperialPint 50 | @docs usFluidOunce, imperialFluidOunce 51 | 52 | -} 53 | 54 | import Constants 55 | import Length exposing (Meters) 56 | import Quantity exposing (Cubed, Quantity(..)) 57 | 58 | 59 | {-| -} 60 | type alias CubicMeters = 61 | Cubed Meters 62 | 63 | 64 | {-| -} 65 | type alias Volume = 66 | Quantity Float CubicMeters 67 | 68 | 69 | {-| Construct a volume from a number of cubic meters. 70 | -} 71 | cubicMeters : Float -> Volume 72 | cubicMeters numCubicMeters = 73 | Quantity numCubicMeters 74 | 75 | 76 | {-| Convert a volume to a number of cubic meters. 77 | -} 78 | inCubicMeters : Volume -> Float 79 | inCubicMeters (Quantity numCubicMeters) = 80 | numCubicMeters 81 | 82 | 83 | {-| Construct a volume from a number of cubic inches. 84 | -} 85 | cubicInches : Float -> Volume 86 | cubicInches numCubicInches = 87 | cubicMeters (Constants.cubicInch * numCubicInches) 88 | 89 | 90 | {-| Convert a volume to a number of cubic inches. 91 | -} 92 | inCubicInches : Volume -> Float 93 | inCubicInches volume = 94 | inCubicMeters volume / Constants.cubicInch 95 | 96 | 97 | {-| Construct a volume from a number of cubic feet. 98 | -} 99 | cubicFeet : Float -> Volume 100 | cubicFeet numCubicFeet = 101 | cubicMeters (Constants.cubicFoot * numCubicFeet) 102 | 103 | 104 | {-| Convert a volume to a number of cubic feet. 105 | -} 106 | inCubicFeet : Volume -> Float 107 | inCubicFeet volume = 108 | inCubicMeters volume / Constants.cubicFoot 109 | 110 | 111 | {-| Construct a volume from a number of cubic yards. 112 | -} 113 | cubicYards : Float -> Volume 114 | cubicYards numCubicYards = 115 | cubicMeters (Constants.cubicYard * numCubicYards) 116 | 117 | 118 | {-| Convert a volume to a number of cubic yards. 119 | -} 120 | inCubicYards : Volume -> Float 121 | inCubicYards volume = 122 | inCubicMeters volume / Constants.cubicYard 123 | 124 | 125 | {-| Construct a volume from a number of milliliters. 126 | -} 127 | milliliters : Float -> Volume 128 | milliliters numMilliliters = 129 | cubicMeters (1.0e-6 * numMilliliters) 130 | 131 | 132 | {-| Convert a volume to a number of milliliters. 133 | -} 134 | inMilliliters : Volume -> Float 135 | inMilliliters volume = 136 | 1.0e6 * inCubicMeters volume 137 | 138 | 139 | {-| Construct a volume from a number of cubic centimeters. 140 | Alias for `milliliters`. 141 | -} 142 | cubicCentimeters : Float -> Volume 143 | cubicCentimeters numCubicCentimeters = 144 | milliliters numCubicCentimeters 145 | 146 | 147 | {-| Convert a volume to a number of cubic centimeters. 148 | Alias for `inMilliliters`. 149 | -} 150 | inCubicCentimeters : Volume -> Float 151 | inCubicCentimeters volume = 152 | inMilliliters volume 153 | 154 | 155 | {-| Construct a volume from a number of liters. 156 | -} 157 | liters : Float -> Volume 158 | liters numLiters = 159 | cubicMeters (0.001 * numLiters) 160 | 161 | 162 | {-| Convert a volume to a number of liters. 163 | -} 164 | inLiters : Volume -> Float 165 | inLiters volume = 166 | 1000 * inCubicMeters volume 167 | 168 | 169 | {-| Construct a volume from a number of U.S. liquid gallon. 170 | -} 171 | usLiquidGallons : Float -> Volume 172 | usLiquidGallons numUsLiquidGallons = 173 | cubicMeters (numUsLiquidGallons * Constants.usLiquidGallon) 174 | 175 | 176 | {-| Convert a volume to a number of U.S. liquid gallons. 177 | -} 178 | inUsLiquidGallons : Volume -> Float 179 | inUsLiquidGallons volume = 180 | inCubicMeters volume / Constants.usLiquidGallon 181 | 182 | 183 | {-| Construct a volume from a number of U.S. dry gallons. 184 | -} 185 | usDryGallons : Float -> Volume 186 | usDryGallons numUsDryGallons = 187 | cubicMeters (numUsDryGallons * Constants.usDryGallon) 188 | 189 | 190 | {-| Convert a volume to a number of U.S. dry gallons. 191 | -} 192 | inUsDryGallons : Volume -> Float 193 | inUsDryGallons volume = 194 | inCubicMeters volume / Constants.usDryGallon 195 | 196 | 197 | {-| Construct a volume from a number of imperial gallons. 198 | -} 199 | imperialGallons : Float -> Volume 200 | imperialGallons numImperialGallons = 201 | cubicMeters (numImperialGallons * Constants.imperialGallon) 202 | 203 | 204 | {-| Convert a volume to a number of imperial gallons. 205 | -} 206 | inImperialGallons : Volume -> Float 207 | inImperialGallons volume = 208 | inCubicMeters volume / Constants.imperialGallon 209 | 210 | 211 | {-| Construct a volume from a number of U.S. liquid quarts. 212 | -} 213 | usLiquidQuarts : Float -> Volume 214 | usLiquidQuarts numUsLiquidQuarts = 215 | cubicMeters (numUsLiquidQuarts * Constants.usLiquidQuart) 216 | 217 | 218 | {-| Convert a volume to a number of U.S. liquid quarts. 219 | -} 220 | inUsLiquidQuarts : Volume -> Float 221 | inUsLiquidQuarts volume = 222 | inCubicMeters volume / Constants.usLiquidQuart 223 | 224 | 225 | {-| Construct a volume from a number of U.S. dry quarts. 226 | -} 227 | usDryQuarts : Float -> Volume 228 | usDryQuarts numUsDryQuarts = 229 | cubicMeters (numUsDryQuarts * Constants.usDryQuart) 230 | 231 | 232 | {-| Convert a volume to a number of U.S. dry quarts. 233 | -} 234 | inUsDryQuarts : Volume -> Float 235 | inUsDryQuarts volume = 236 | inCubicMeters volume / Constants.usDryQuart 237 | 238 | 239 | {-| Construct a volume from a number of imperial quarts. 240 | -} 241 | imperialQuarts : Float -> Volume 242 | imperialQuarts numImperialQuarts = 243 | cubicMeters (numImperialQuarts * Constants.imperialQuart) 244 | 245 | 246 | {-| Convert a volume to a number of imperial quarts. 247 | -} 248 | inImperialQuarts : Volume -> Float 249 | inImperialQuarts volume = 250 | inCubicMeters volume / Constants.imperialQuart 251 | 252 | 253 | {-| Construct a volume from a number of U.S. liquid pints. 254 | -} 255 | usLiquidPints : Float -> Volume 256 | usLiquidPints numUsLiquidPints = 257 | cubicMeters (numUsLiquidPints * Constants.usLiquidPint) 258 | 259 | 260 | {-| Convert a volume to a number of U.S. liquid pints. 261 | -} 262 | inUsLiquidPints : Volume -> Float 263 | inUsLiquidPints volume = 264 | inCubicMeters volume / Constants.usLiquidPint 265 | 266 | 267 | {-| Construct a volume from a number of U.S. dry pints. 268 | -} 269 | usDryPints : Float -> Volume 270 | usDryPints numUsDryPints = 271 | cubicMeters (numUsDryPints * Constants.usDryPint) 272 | 273 | 274 | {-| Convert a volume to a number of U.S. dry pints. 275 | -} 276 | inUsDryPints : Volume -> Float 277 | inUsDryPints volume = 278 | inCubicMeters volume / Constants.usDryPint 279 | 280 | 281 | {-| Construct a volume from a number of imperial pints. 282 | -} 283 | imperialPints : Float -> Volume 284 | imperialPints numImperialPints = 285 | cubicMeters (numImperialPints * Constants.imperialPint) 286 | 287 | 288 | {-| Convert a volume to a number of imperial pints. 289 | -} 290 | inImperialPints : Volume -> Float 291 | inImperialPints volume = 292 | inCubicMeters volume / Constants.imperialPint 293 | 294 | 295 | {-| Construct a volume from a number of U.S. fluid ounces. 296 | -} 297 | usFluidOunces : Float -> Volume 298 | usFluidOunces numUsFluidOunces = 299 | cubicMeters (numUsFluidOunces * Constants.usFluidOunce) 300 | 301 | 302 | {-| Convert a volume to a number of U.S. fluid ounces. 303 | -} 304 | inUsFluidOunces : Volume -> Float 305 | inUsFluidOunces volume = 306 | inCubicMeters volume / Constants.usFluidOunce 307 | 308 | 309 | {-| Construct a volume from a number of imperial fluid ounces. 310 | -} 311 | imperialFluidOunces : Float -> Volume 312 | imperialFluidOunces numImperialFluidOunces = 313 | cubicMeters (numImperialFluidOunces * Constants.imperialFluidOunce) 314 | 315 | 316 | {-| Convert a volume to a number of imperial fluid ounces. 317 | -} 318 | inImperialFluidOunces : Volume -> Float 319 | inImperialFluidOunces volume = 320 | inCubicMeters volume / Constants.imperialFluidOunce 321 | 322 | 323 | {-| -} 324 | cubicMeter : Volume 325 | cubicMeter = 326 | cubicMeters 1 327 | 328 | 329 | {-| -} 330 | milliliter : Volume 331 | milliliter = 332 | milliliters 1 333 | 334 | 335 | {-| -} 336 | cubicCentimeter : Volume 337 | cubicCentimeter = 338 | milliliters 1 339 | 340 | 341 | {-| -} 342 | liter : Volume 343 | liter = 344 | liters 1 345 | 346 | 347 | {-| -} 348 | cubicInch : Volume 349 | cubicInch = 350 | cubicInches 1 351 | 352 | 353 | {-| -} 354 | cubicFoot : Volume 355 | cubicFoot = 356 | cubicFeet 1 357 | 358 | 359 | {-| -} 360 | cubicYard : Volume 361 | cubicYard = 362 | cubicYards 1 363 | 364 | 365 | {-| -} 366 | usLiquidGallon : Volume 367 | usLiquidGallon = 368 | usLiquidGallons 1 369 | 370 | 371 | {-| -} 372 | usDryGallon : Volume 373 | usDryGallon = 374 | usDryGallons 1 375 | 376 | 377 | {-| -} 378 | imperialGallon : Volume 379 | imperialGallon = 380 | imperialGallons 1 381 | 382 | 383 | {-| -} 384 | usLiquidQuart : Volume 385 | usLiquidQuart = 386 | usLiquidQuarts 1 387 | 388 | 389 | {-| -} 390 | usDryQuart : Volume 391 | usDryQuart = 392 | usDryQuarts 1 393 | 394 | 395 | {-| -} 396 | imperialQuart : Volume 397 | imperialQuart = 398 | imperialQuarts 1 399 | 400 | 401 | {-| -} 402 | usLiquidPint : Volume 403 | usLiquidPint = 404 | usLiquidPints 1 405 | 406 | 407 | {-| -} 408 | usDryPint : Volume 409 | usDryPint = 410 | usDryPints 1 411 | 412 | 413 | {-| -} 414 | imperialPint : Volume 415 | imperialPint = 416 | imperialPints 1 417 | 418 | 419 | {-| -} 420 | usFluidOunce : Volume 421 | usFluidOunce = 422 | usFluidOunces 1 423 | 424 | 425 | {-| -} 426 | imperialFluidOunce : Volume 427 | imperialFluidOunce = 428 | imperialFluidOunces 1 429 | -------------------------------------------------------------------------------- /src/Temperature.elm: -------------------------------------------------------------------------------- 1 | module Temperature exposing 2 | ( Temperature, Delta, CelsiusDegrees 3 | , degreesCelsius, inDegreesCelsius, degreesFahrenheit, inDegreesFahrenheit, kelvins, inKelvins, absoluteZero 4 | , celsiusDegrees, inCelsiusDegrees, fahrenheitDegrees, inFahrenheitDegrees 5 | , celsiusDegree, fahrenheitDegree 6 | , lessThan, greaterThan, lessThanOrEqualTo, greaterThanOrEqualTo, compare, equalWithin, min, max 7 | , plus, minus, clamp 8 | , minimum, maximum, sort, sortBy 9 | ) 10 | 11 | {-| Unlike other modules in `elm-units`, this module contains two different 12 | primary types: 13 | 14 | - `Temperature`, which is not actually a `Quantity` since temperatures don't 15 | really act like normal quantities. For example, it doesn't make sense to 16 | add two temperatures or find the ratio between them. 17 | - `Delta`, which represents the difference between two temperatures. A `Delta` 18 | _is_ a `Quantity` since it does make sense to add two deltas to get a net 19 | delta, find the ratio between two deltas (one rise in temperature might be 20 | twice as much as another rise in temperature), etc. 21 | 22 | Since a `Temperature` value is not a `Quantity`, this module exposes specialized 23 | functions for doing the operations on `Temperature` values that _do_ make sense, 24 | such as comparing two temperatures or sorting a list of temperatures. It's also 25 | possible to find the delta from one temperature to another using [`minus`](Temperature#minus), 26 | and then add a `Delta` to a `Temperature` using [`plus`](Temperature#plus). 27 | 28 | @docs Temperature, Delta, CelsiusDegrees 29 | 30 | 31 | # Temperatures 32 | 33 | @docs degreesCelsius, inDegreesCelsius, degreesFahrenheit, inDegreesFahrenheit, kelvins, inKelvins, absoluteZero 34 | 35 | 36 | # Deltas 37 | 38 | Following the suggestion mentioned [here](https://en.wikipedia.org/wiki/Celsius#Temperatures_and_intervals), 39 | this module uses (for example) `celsiusDegrees` to indicate a temperature delta 40 | (change in temperature), in contrast to `degreesCelsius` which indicates an 41 | actual temperature. 42 | 43 | @docs celsiusDegrees, inCelsiusDegrees, fahrenheitDegrees, inFahrenheitDegrees 44 | 45 | 46 | ## Constants 47 | 48 | Shorthand for `Temperature.celsiusDegrees 1` and `Temperature.fahrenheitDegrees 49 | 1`. Can be convenient to use with [`Quantity.per`](Quantity#per). 50 | 51 | @docs celsiusDegree, fahrenheitDegree 52 | 53 | 54 | # Comparison 55 | 56 | @docs lessThan, greaterThan, lessThanOrEqualTo, greaterThanOrEqualTo, compare, equalWithin, min, max 57 | 58 | 59 | # Arithmetic 60 | 61 | @docs plus, minus, clamp 62 | 63 | 64 | # List functions 65 | 66 | @docs minimum, maximum, sort, sortBy 67 | 68 | -} 69 | 70 | import Quantity exposing (Quantity(..)) 71 | 72 | 73 | {-| A temperature such as 25 degrees Celsius or 80 degrees Fahrenheit. 74 | -} 75 | type Temperature 76 | = Temperature Float 77 | 78 | 79 | {-| A `Delta` represents the difference between two temperatures. 80 | -} 81 | type alias Delta = 82 | Quantity Float CelsiusDegrees 83 | 84 | 85 | {-| Tempereature deltas are stored as a number of Celsius degrees. 86 | -} 87 | type CelsiusDegrees 88 | = CelsiusDegrees 89 | 90 | 91 | 92 | -- Temperatures 93 | 94 | 95 | {-| Construct a temperature from a number of degrees Celsius. 96 | -} 97 | degreesCelsius : Float -> Temperature 98 | degreesCelsius numDegreesCelsius = 99 | kelvins (273.15 + numDegreesCelsius) 100 | 101 | 102 | {-| Convert a temperature to a number of degrees Celsius. 103 | -} 104 | inDegreesCelsius : Temperature -> Float 105 | inDegreesCelsius temperature = 106 | inKelvins temperature - 273.15 107 | 108 | 109 | {-| Construct a temperature from a number of degrees Fahrenheit. 110 | -} 111 | degreesFahrenheit : Float -> Temperature 112 | degreesFahrenheit numDegreesFahrenheit = 113 | degreesCelsius ((numDegreesFahrenheit - 32) / 1.8) 114 | 115 | 116 | {-| Convert a temperature to a number of degrees Fahrenheit. 117 | -} 118 | inDegreesFahrenheit : Temperature -> Float 119 | inDegreesFahrenheit temperature = 120 | 32 + 1.8 * inDegreesCelsius temperature 121 | 122 | 123 | {-| Construct a temperature from a number of [kelvins][kelvin]. 124 | 125 | Temperature.kelvins 300 126 | --> Temperature.degreesCelsius 26.85 127 | 128 | [kelvin]: https://en.wikipedia.org/wiki/Kelvin "Kelvin" 129 | 130 | -} 131 | kelvins : Float -> Temperature 132 | kelvins numKelvins = 133 | Temperature numKelvins 134 | 135 | 136 | {-| Convert a temperature to a number of kelvins. 137 | 138 | Temperature.degreesCelsius 0 139 | |> Temperature.inKelvins 140 | --> 273.15 141 | 142 | -} 143 | inKelvins : Temperature -> Float 144 | inKelvins (Temperature numKelvins) = 145 | numKelvins 146 | 147 | 148 | {-| [Absolute zero](https://en.wikipedia.org/wiki/Absolute_zero), equal to zero 149 | kelvins or -273.15 degrees Celsius. 150 | 151 | Temperature.absoluteZero 152 | --> Temperature.degreesCelsius -273.15 153 | 154 | -} 155 | absoluteZero : Temperature 156 | absoluteZero = 157 | kelvins 0 158 | 159 | 160 | 161 | -- Deltas 162 | 163 | 164 | {-| Construct a temperature delta from a number of Celsius degrees. 165 | -} 166 | celsiusDegrees : Float -> Delta 167 | celsiusDegrees numCelsiusDegrees = 168 | Quantity numCelsiusDegrees 169 | 170 | 171 | {-| Convert a temperature delta to a number of Celsius degrees. 172 | -} 173 | inCelsiusDegrees : Delta -> Float 174 | inCelsiusDegrees (Quantity numCelsiusDegrees) = 175 | numCelsiusDegrees 176 | 177 | 178 | {-| Construct a temperature delta from a number of Fahrenheit degrees. 179 | 180 | Temperature.fahrenheitDegrees 36 181 | --> Temperature.celsiusDegrees 20 182 | 183 | -} 184 | fahrenheitDegrees : Float -> Delta 185 | fahrenheitDegrees numFahrenheitDegrees = 186 | celsiusDegrees (numFahrenheitDegrees / 1.8) 187 | 188 | 189 | {-| Convert a temperature delta to a number of Fahrenheit degrees. 190 | 191 | Temperature.celsiusDegrees 10 192 | |> Temperature.inFahrenheitDegrees 193 | --> 18 194 | 195 | -} 196 | inFahrenheitDegrees : Delta -> Float 197 | inFahrenheitDegrees quantity = 198 | inCelsiusDegrees quantity * 1.8 199 | 200 | 201 | {-| -} 202 | celsiusDegree : Delta 203 | celsiusDegree = 204 | celsiusDegrees 1 205 | 206 | 207 | {-| -} 208 | fahrenheitDegree : Delta 209 | fahrenheitDegree = 210 | fahrenheitDegrees 1 211 | 212 | 213 | 214 | -- Comparison 215 | 216 | 217 | {-| Check if one temperature is less than another. Note the [argument order](/#argument-order)! 218 | 219 | roomTemperature = 220 | Temperature.degreesCelsius 21 221 | 222 | Temperature.degreesFahrenheit 50 223 | |> Temperature.lessThan roomTemperature 224 | --> True 225 | 226 | -- Same as: 227 | Temperature.lessThan roomTemperature 228 | (Temperature.degreesFahrenheit 50) 229 | --> True 230 | 231 | -} 232 | lessThan : Temperature -> Temperature -> Bool 233 | lessThan (Temperature y) (Temperature x) = 234 | x < y 235 | 236 | 237 | {-| Check if one temperature is greater than another. Note the [argument order](/#argument-order)! 238 | 239 | roomTemperature = 240 | Temperature.degreesCelsius 21 241 | 242 | Temperature.degreesFahrenheit 50 243 | |> Temperature.greaterThan roomTemperature 244 | --> False 245 | 246 | -- Same as: 247 | Temperature.greaterThan roomTemperature 248 | (Temperature.degreesFahrenheit 50) 249 | --> False 250 | 251 | -} 252 | greaterThan : Temperature -> Temperature -> Bool 253 | greaterThan (Temperature y) (Temperature x) = 254 | x > y 255 | 256 | 257 | {-| Check if one temperature is less than or equal to another. Note the 258 | [argument order](/#argument-order)! 259 | -} 260 | lessThanOrEqualTo : Temperature -> Temperature -> Bool 261 | lessThanOrEqualTo (Temperature y) (Temperature x) = 262 | x <= y 263 | 264 | 265 | {-| Check if one temperature is greater than or equal to another. Note the 266 | [argument order](/#argument-order)! 267 | -} 268 | greaterThanOrEqualTo : Temperature -> Temperature -> Bool 269 | greaterThanOrEqualTo (Temperature y) (Temperature x) = 270 | x >= y 271 | 272 | 273 | {-| Compare two temperatures, returning an [`Order`](https://package.elm-lang.org/packages/elm/core/latest/Basics#Order) 274 | value indicating whether the first is less than, equal to or greater than the 275 | second. 276 | 277 | Temperature.compare 278 | (Temperature.degreesCelsius 25) 279 | (Temperature.degreesFahrenheit 75) 280 | --> GT 281 | 282 | Temperature.compare 283 | (Temperature.degreesCelsius 25) 284 | (Temperature.degreesFahrenheit 77) 285 | --> EQ 286 | 287 | (Note that due to floating-point roundoff, you generally shouldn't rely on 288 | temperatures comparing as exactly equal.) 289 | 290 | -} 291 | compare : Temperature -> Temperature -> Order 292 | compare (Temperature x) (Temperature y) = 293 | Basics.compare x y 294 | 295 | 296 | {-| Check if two temperatures are equal within a given delta tolerance. The 297 | tolerance must be greater than or equal to zero - if it is negative, then the 298 | result will always be false. 299 | 300 | Temperature.equalWithin (Temperature.fahrenheitDegrees 1) 301 | (Temperature.degreesCelsius 25) 302 | (Temperature.degreesFahrenheit 75) 303 | --> False 304 | 305 | Temperature.equalWithin (Temperature.fahrenheitDegrees 3) 306 | (Temperature.degreesCelsius 25) 307 | (Temperature.degreesFahrenheit 75) 308 | --> True 309 | 310 | -} 311 | equalWithin : Delta -> Temperature -> Temperature -> Bool 312 | equalWithin (Quantity tolerance) (Temperature x) (Temperature y) = 313 | Basics.abs (x - y) <= tolerance 314 | 315 | 316 | {-| Find the minimum of two temperatures. 317 | 318 | Temperature.min 319 | (Temperature.degreesCelsius 25) 320 | (Temperature.degreesFahrenheit 75) 321 | --> Temperature.degreesFahrenheit 75 322 | 323 | -} 324 | min : Temperature -> Temperature -> Temperature 325 | min (Temperature x) (Temperature y) = 326 | Temperature (Basics.min x y) 327 | 328 | 329 | {-| Find the maximum of two temperatures. 330 | 331 | Temperature.max 332 | (Temperature.degreesCelsius 25) 333 | (Temperature.degreesFahrenheit 75) 334 | --> Temperature.degreesCelsius 25 335 | 336 | -} 337 | max : Temperature -> Temperature -> Temperature 338 | max (Temperature x) (Temperature y) = 339 | Temperature (Basics.max x y) 340 | 341 | 342 | 343 | -- Arithmetic 344 | 345 | 346 | {-| Add a `Delta` to a `Temperature` to get a new `Temperature`. 347 | 348 | Temperature.degreesCelsius 25 349 | |> Temperature.plus 350 | (Temperature.celsiusDegrees 7) 351 | --> Temperature.degreesCelsius 32 352 | 353 | If you want to _subtract_ a `Delta` from a `Temperature`, you can [`negate`](Quantity#negate) 354 | the delta first and then call `plus`. 355 | 356 | -} 357 | plus : Delta -> Temperature -> Temperature 358 | plus (Quantity delta) (Temperature temperature) = 359 | Temperature (temperature + delta) 360 | 361 | 362 | {-| Subtract one `Temperature` from another to get a `Delta`. Note the [argument 363 | order](/#argument-order)! 364 | 365 | -- 25 degrees Celsius is 77 degrees Fahrenheit 366 | start = 367 | Temperature.degreesCelsius 25 368 | 369 | end = 370 | Temperature.degreesFahrenheit 80 371 | 372 | end |> Temperature.minus start 373 | --> Temperature.fahrenheitDegrees 3 374 | 375 | start |> Temperature.minus end 376 | --> Temperature.fahrenheitDegrees -3 377 | 378 | -} 379 | minus : Temperature -> Temperature -> Delta 380 | minus (Temperature y) (Temperature x) = 381 | Quantity (x - y) 382 | 383 | 384 | {-| Given a lower and upper bound, clamp a given temperature to within those 385 | bounds. Say you wanted to clamp a temperature to be between 18 and 22 degrees 386 | Celsius: 387 | 388 | lowerBound = 389 | Temperature.degreesCelsius 18 390 | 391 | upperBound = 392 | Temperature.degreesCelsius 22 393 | 394 | Temperature.degreesCelsius 25 395 | |> Temperature.clamp lowerBound upperBound 396 | --> Temperature.degreesCelsius 22 397 | 398 | Temperature.degreesFahrenheit 67 -- approx 19.4 °C 399 | |> Temperature.clamp lowerBound upperBound 400 | --> Temperature.degreesFahrenheit 67 401 | 402 | Temperature.absoluteZero 403 | |> Temperature.clamp lowerBound upperBound 404 | --> Temperature.degreesCelsius 18 405 | 406 | -} 407 | clamp : Temperature -> Temperature -> Temperature -> Temperature 408 | clamp (Temperature lower) (Temperature upper) (Temperature temperature) = 409 | Temperature (Basics.clamp lower upper temperature) 410 | 411 | 412 | 413 | -- List functions 414 | 415 | 416 | {-| Find the minimum of a list of temperatures. Returns `Nothing` if the list 417 | is empty. 418 | 419 | Temperature.minimum 420 | [ Temperature.degreesCelsius 20 421 | , Temperature.kelvins 300 422 | , Temperature.degreesFahrenheit 74 423 | ] 424 | --> Just (Temperature.degreesCelsius 20) 425 | 426 | -} 427 | minimum : List Temperature -> Maybe Temperature 428 | minimum temperatures = 429 | case temperatures of 430 | first :: rest -> 431 | Just (List.foldl min first rest) 432 | 433 | [] -> 434 | Nothing 435 | 436 | 437 | {-| Find the maximum of a list of temperatures. Returns `Nothing` if the list 438 | is empty. 439 | 440 | Temperature.maximum 441 | [ Temperature.degreesCelsius 20 442 | , Temperature.kelvins 300 443 | , Temperature.degreesFahrenheit 74 444 | ] 445 | --> Just (Temperature.kelvins 300) 446 | 447 | -} 448 | maximum : List Temperature -> Maybe Temperature 449 | maximum temperatures = 450 | case temperatures of 451 | first :: rest -> 452 | Just (List.foldl max first rest) 453 | 454 | [] -> 455 | Nothing 456 | 457 | 458 | {-| Sort a list of temperatures from lowest to highest. 459 | 460 | Temperature.sort 461 | [ Temperature.degreesCelsius 20 462 | , Temperature.kelvins 300 463 | , Temperature.degreesFahrenheit 74 464 | ] 465 | --> [ Temperature.degreesCelsius 20 466 | --> , Temperature.degreesFahrenheit 74 467 | --> , Temperature.kelvins 300 468 | --> ] 469 | 470 | -} 471 | sort : List Temperature -> List Temperature 472 | sort temperatures = 473 | List.sortBy inKelvins temperatures 474 | 475 | 476 | {-| Sort an arbitrary list of values by a derived `Temperature`. If you had 477 | 478 | rooms = 479 | [ ( "Lobby", Temperature.degreesCelsius 21 ) 480 | , ( "Locker room", Temperature.degreesCelsius 17 ) 481 | , ( "Rink", Temperature.degreesCelsius -4 ) 482 | , ( "Sauna", Temperature.degreesCelsius 82 ) 483 | ] 484 | 485 | then you could sort by temperature with 486 | 487 | Temperature.sortBy Tuple.second rooms 488 | --> [ ( "Rink", Temperature.degreesCelsius -4 ) 489 | --> , ( "Locker room", Temperature.degreesCelsius 17 ) 490 | --> , ( "Lobby", Temperature.degreesCelsius 21 ) 491 | --> , ( "Sauna", Temperature.degreesCelsius 82 ) 492 | --> ] 493 | 494 | -} 495 | sortBy : (a -> Temperature) -> List a -> List a 496 | sortBy toTemperature list = 497 | let 498 | comparator first second = 499 | compare (toTemperature first) (toTemperature second) 500 | in 501 | List.sortWith comparator list 502 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-units 2 | 3 | _Release notes for 2.0 are [here](https://github.com/ianmackenzie/elm-units/releases/tag/2.0.0)._ 4 | 5 | `elm-units` is useful if you want to store, pass around, convert between, 6 | compare, or do arithmetic on: 7 | 8 | - Durations (seconds, milliseconds, hours...) 9 | - Angles (degrees, radians, turns...) 10 | - Lengths (meters, feet, inches, miles, light years...) 11 | - Temperatures (Celsius, Fahrenheit, kelvins) 12 | - Pixels (whole or partial) 13 | - Speeds (pixels per second, miles per hour...) or any other rate of change 14 | - Any of the other built-in quantity types: areas, accelerations, masses, 15 | forces, pressures, currents, voltages... 16 | - Or even values in your own custom units, such as 'number of tiles' in a 17 | tile-based game 18 | 19 | It is aimed especially at engineering/scientific/technical applications but is 20 | designed to be generic enough to work well for other fields such as games and 21 | finance. The core of the package consists of a generic `Quantity` type and 22 | many concrete types such as `Length`, `Angle`, `Duration`, `Temperature`, and 23 | `Speed`, which you can use to add some nice type safety to data types and 24 | function signatures: 25 | 26 | ```elm 27 | type alias Camera = 28 | { fieldOfView : Angle 29 | , shutterSpeed : Duration 30 | , minimumOperatingTemperature : Temperature 31 | } 32 | 33 | canOperateAt : Temperature -> Camera -> Bool 34 | canOperateAt temperature camera = 35 | temperature 36 | |> Temperature.greaterThan 37 | camera.minimumOperatingTemperature 38 | ``` 39 | 40 | You can construct values of these types from any units you want, using provided 41 | functions such as: 42 | 43 | ```elm 44 | Length.feet : Float -> Length 45 | Length.meters : Float -> Length 46 | Duration.seconds : Float -> Duration 47 | Angle.degrees : Float -> Angle 48 | Temperature.degreesFahrenheit : Float -> Temperature 49 | ``` 50 | 51 | You can later convert back to plain numeric values, also in any units you want 52 | (which do not have to be the same units used when initially constructing the 53 | value!): 54 | 55 | ```elm 56 | Length.inCentimeters : Length -> Float 57 | Length.inMiles : Length -> Float 58 | Duration.inHours : Duration -> Float 59 | Angle.inRadians : Angle -> Float 60 | Temperature.inDegreesCelsius : Temperature -> Float 61 | ``` 62 | 63 | This means that (among other things!) you can use these functions to do simple 64 | unit conversions: 65 | 66 | ```elm 67 | Duration.hours 3 |> Duration.inSeconds 68 | --> 10800 69 | 70 | Length.feet 10 |> Length.inMeters 71 | --> 3.048 72 | 73 | Speed.milesPerHour 60 |> Speed.inMetersPerSecond 74 | --> 26.8224 75 | 76 | Temperature.degreesCelsius 30 77 | |> Temperature.inDegreesFahrenheit 78 | --> 86 79 | ``` 80 | 81 | Additionally, types like `Length` are actually type aliases of the form 82 | `Quantity number units` (`Length` is `Quantity Float Meters`, for example, 83 | meaning that it is internally stored as a number of meters), and there are 84 | many generic functions which let you work directly with any kind of `Quantity` 85 | values: 86 | 87 | ```elm 88 | Length.feet 3 89 | |> Quantity.lessThan (Length.meters 1) 90 | --> True 91 | 92 | Duration.hours 2 93 | |> Quantity.plus (Duration.minutes 30) 94 | |> Duration.inSeconds 95 | --> 9000 96 | 97 | -- Some functions can actually convert between units! 98 | -- Multiplying two Length values gives you an Area 99 | Length.centimeters 60 100 | |> Quantity.times 101 | (Length.centimeters 80) 102 | --> Area.squareMeters 0.48 103 | 104 | Quantity.sort 105 | [ Angle.radians 1 106 | , Angle.degrees 10 107 | , Angle.turns 0.5 108 | ] 109 | --> [ Angle.degrees 10 , Angle.radians 1 , Angle.turns 0.5 ] 110 | ``` 111 | 112 | Ultimately, what this does is let you pass around and manipulate `Length`, 113 | `Duration` or `Temperature` etc. values without having to worry about units. 114 | When you initially construct a `Length`, you need to specify what units you're 115 | using, but once that is done you can: 116 | 117 | - Store the length inside a data structure 118 | - Pass it around between different functions 119 | - Compare it to other lengths 120 | - Add and subtract it to other lengths 121 | - Multiply it by another length to get an area, or divide by a duration to 122 | get a speed 123 | 124 | ...and much more, all without having to care about units at all. All 125 | calculations will be done in an internally consistent way, and when you finally 126 | need to actually display a value on screen or encode to JSON, you can extract 127 | the final result in whatever units you want. 128 | 129 | ## Table of contents 130 | 131 | - [Installation](#installation) 132 | - [Usage](#usage) 133 | - [Fundamentals](#fundamentals) 134 | - [The `Quantity` type](#the-quantity-type) 135 | - [Basic arithmetic and comparison](#basic-arithmetic-and-comparison) 136 | - [Multiplication and division](#multiplication-and-division) 137 | - [Argument order](#argument-order) 138 | - [Custom functions](#custom-functions) 139 | - [Custom units](#custom-units) 140 | - [Understanding quantity types](#understanding-quantity-types) 141 | - [Getting help](#getting-help) 142 | - [API](#api) 143 | - [Climate action](#climate-action) 144 | - [Contributing](#contributing) 145 | - [License](#license) 146 | 147 | ## Installation 148 | 149 | Assuming you have [installed Elm](https://guide.elm-lang.org/install.html) and 150 | started a new project, you can install `elm-units` by running 151 | 152 | ``` 153 | elm install ianmackenzie/elm-units 154 | ``` 155 | 156 | in a command prompt inside your project directory. 157 | 158 | ## Usage 159 | 160 | ### Fundamentals 161 | 162 | To take code that currently uses raw `Float` values and convert it to using 163 | `elm-units` types, there are three basic steps: 164 | 165 | - Wherever you store a `Float`, such as in your model or in a message, switch 166 | to storing a `Duration` or `Angle` or `Temperature` etc. value instead. 167 | - Whenever you _have_ a `Float` (from an external package, JSON decoder etc.), 168 | use a function such as `Duration.seconds`, `Angle.degrees` or 169 | `Temperature.degreesFahrenheit` to turn it into a type-safe value. 170 | - Whenever you _need_ a `Float` (to pass to an external package, encode as 171 | JSON etc.), use a function such as `Duration.inMillliseconds`, 172 | `Angle.inRadians` or `Temperature.inDegreesCelsius` to extract the value in 173 | whatever units you want. 174 | - Where you do math with `Float` values, switch to using `Quantity` functions 175 | like `Quantity.plus` or `Quantity.greaterThan`. If this becomes impractical, 176 | there are [other approaches](#custom-functions). 177 | 178 | ### The `Quantity` type 179 | 180 | All values produced by this package (with the exception of `Temperature`, which 181 | is a bit of a special case) are actually values of type `Quantity`, defined as 182 | 183 | ```elm 184 | type Quantity number units 185 | = Quantity number 186 | ``` 187 | 188 | For example, `Length` is defined as 189 | 190 | ```elm 191 | type alias Length = 192 | Quantity Float Meters 193 | ``` 194 | 195 | This means that a `Length` is internally stored as a `Float` number of `Meters`, 196 | but the choice of internal units can mostly be treated as an implementation 197 | detail. 198 | 199 | Having a common `Quantity` type means that it is possible to define generic 200 | arithmetic and comparison operations that work on any kind of quantity; read on! 201 | 202 | ### Basic arithmetic and comparison 203 | 204 | You can do basic math with `Quantity` values: 205 | 206 | ```elm 207 | -- 6 feet 3 inches, converted to meters 208 | Length.feet 6 209 | |> Quantity.plus (Length.inches 3) 210 | |> Length.inMeters 211 | --> 1.9050000000000002 212 | 213 | Duration.hours 1 214 | |> Quantity.minus (Duration.minutes 15) 215 | |> Duration.inMinutes 216 | --> 45 217 | 218 | -- pi radians plus 45 degrees is 5/8 of a full turn 219 | Quantity.sum [ Angle.radians pi, Angle.degrees 45 ] 220 | |> Angle.inTurns 221 | --> 0.625 222 | ``` 223 | 224 | `Quantity` values can be compared/sorted: 225 | 226 | ```elm 227 | Length.meters 1 |> Quantity.greaterThan (Length.feet 3) 228 | --> True 229 | 230 | Quantity.compare (Length.meters 1) (Length.feet 3) 231 | --> GT 232 | 233 | Quantity.max (Length.meters 1) (Length.feet 3) 234 | --> Length.meters 1 235 | 236 | Quantity.maximum [ Length.meters 1, Length.feet 3 ] 237 | --> Just (Length.meters 1) 238 | 239 | Quantity.sort [ Length.meters 1, Length.feet 3 ] 240 | --> [ Length.feet 3, Length.meters 1 ] 241 | ``` 242 | 243 | ### Multiplication and division 244 | 245 | There are actually three different 'families' of multiplication and division 246 | functions in the `Quantity` module, used in different contexts: 247 | 248 | - `multiplyBy` and `divideBy` are used to multiply (scale) or divide a 249 | `Quantity` by a plain `Int` or `Float`, with `twice` and `half` for the common 250 | cases of multiplying or dividing by 2 251 | - `product`, `times`, `over` and `over_` are used to work with quantities that 252 | are products of other quantities: 253 | - multiply a `Length` by another `Length` to get an `Area` 254 | - multiply an `Area` by a `Length` to get a `Volume` 255 | - multiply a `Mass` by an `Acceleration` to get a `Force` 256 | - divide a `Volume` by an `Area` to get a `Length` 257 | - divide a `Force` by a `Mass` to get an `Acceleration` 258 | - `rate`, `per`, `at`, `at_` and `for` are used to work with rates of change: 259 | - divide `Length` by `Duration` to get `Speed` 260 | - multiply `Speed` by `Duration` to get `Length` 261 | - divide `Length` by `Speed` to get `Duration` 262 | - And one bonus fourth function: `ratio`, used to divide two quantities with 263 | the same units to get a plain `Float` value 264 | 265 | For example, to calculate the area of a triangle: 266 | 267 | ```elm 268 | -- Area of a triangle with base of 2 feet and 269 | -- height of 8 inches 270 | base = 271 | Length.feet 2 272 | 273 | height = 274 | Length.inches 8 275 | 276 | Quantity.half (Quantity.product base height) 277 | |> Area.inSquareInches 278 | --> 96 279 | ``` 280 | 281 | Comprehensive support is provided for working with rates of change: 282 | 283 | ```elm 284 | -- How fast are we going if we travel 30 meters in 285 | -- 2 seconds? 286 | speed = 287 | Length.meters 30 |> Quantity.per (Duration.seconds 2) 288 | 289 | -- How far do we go if we travel for 2 minutes 290 | -- at that speed? 291 | Duration.minutes 2 -- duration 292 | |> Quantity.at speed -- length per duration 293 | |> Length.inKilometers -- gives us a length! 294 | --> 1.8 295 | 296 | -- How long will it take to travel 20 km 297 | -- if we're driving at 60 mph? 298 | Length.kilometers 20 299 | |> Quantity.at_ (Speed.milesPerHour 60) 300 | |> Duration.inMinutes 301 | --> 12.427423844746679 302 | 303 | -- How fast is "a mile a minute", in kilometers per hour? 304 | Length.miles 1 305 | |> Quantity.per (Duration.minutes 1) 306 | |> Speed.inKilometersPerHour 307 | --> 96.56064 308 | 309 | -- Reverse engineer the speed of light from defined 310 | -- lengths/durations (the speed of light is 'one light 311 | -- year per year') 312 | speedOfLight = 313 | Length.lightYears 1 314 | |> Quantity.per (Duration.julianYears 1) 315 | 316 | speedOfLight |> Speed.inMetersPerSecond 317 | --> 299792458 318 | 319 | -- One astronomical unit is the (average) distance from the 320 | -- Sun to the Earth. Roughly how long does it take light to 321 | -- reach the Earth from the Sun? 322 | Length.astronomicalUnits 1 323 | |> Quantity.at_ speedOfLight 324 | |> Duration.inMinutes 325 | --> 8.316746397269274 326 | ``` 327 | 328 | Note that the various functions above are not restricted to speed (length per 329 | unit time) - any units work: 330 | 331 | ```elm 332 | pixelDensity = 333 | Pixels.pixels 96 |> Quantity.per (Length.inches 1) 334 | 335 | Length.centimeters 3 -- length 336 | |> Quantity.at pixelDensity -- pixels per length 337 | |> Pixels.inPixels -- gives us pixels! 338 | --> 113.38582677165354 339 | ``` 340 | 341 | ### Argument order 342 | 343 | Note that several functions like `Quantity.minus` and `Quantity.lessThan` (and 344 | their `Temperature` equivalents) that mimic binary operators like `-` and `<` 345 | "take the second argument first"; for example, 346 | 347 | ```elm 348 | Quantity.lessThan x y 349 | ``` 350 | 351 | means `y < x`, _not_ `x < y`. This is done for a couple of reasons. First, so 352 | that use with `|>` works naturally; for example, 353 | 354 | ```elm 355 | x |> Quantity.lessThan y 356 | ``` 357 | 358 | _does_ mean `x < y`. The 'reversed' argument order also means that things like 359 | 360 | ```elm 361 | List.map (Quantity.minus x) [ a, b, c ] 362 | ``` 363 | 364 | will work as expected - it will result in 365 | 366 | ```elm 367 | [ a - x, b - x, c - x ] 368 | ``` 369 | 370 | instead of 371 | 372 | ```elm 373 | [ x - a, x - b, x - c ] 374 | ``` 375 | 376 | which is what you would get if `Quantity.minus` took arguments in the 'normal' 377 | order. 378 | 379 | There are, however, several functions that take arguments in 'normal' order, for 380 | example: 381 | 382 | - `Quantity.difference` (compare to `minus`) 383 | - `Quantity.product` (compare to `times`) 384 | - `Quantity.rate` (compare to `per`) 385 | - `Quantity.ratio` 386 | - `Quantity.compare` 387 | 388 | In general the function names try to match how you would use them in English; 389 | you would say "the difference of `a` and `b`" (and so `Quantity.difference a b`) 390 | but "`a` minus `b`" (and so `a |> Quantity.minus b`). 391 | 392 | ### Custom Functions 393 | 394 | Some calculations cannot be expressed using the built-in `Quantity` functions. 395 | Take kinetic energy `E_k = 1/2 * m * v^2`, for example - the `elm-units` type 396 | system is not sophisticated enough to work out the units properly. Instead, 397 | you'd need to create a custom function like 398 | 399 | ```elm 400 | kineticEnergy : Mass -> Speed -> Energy 401 | kineticEnergy (Quantity m) (Quantity v) = 402 | Quantity (0.5 * m * v^2) 403 | ``` 404 | 405 | In the _implementation_ of `kineticEnergy`, you're working with raw `Float` 406 | values so you need to be careful to make sure the units actually do work out. 407 | (The values will be in [SI units][6] - meters, seconds etc.) Once the function 408 | has been implemented, though, it can be used in a completely type-safe way - 409 | callers can supply arguments using whatever units they have, and extract results 410 | in whatever units they want: 411 | 412 | ```elm 413 | kineticEnergy (Mass.tonnes 1.5) (Speed.milesPerHour 60) 414 | |> Energy.inKilowattHours 415 | --> 0.14988357119999998 416 | ``` 417 | 418 | ### Custom Units 419 | 420 | `elm-units` defines many standard unit types, but you can easily define your 421 | own! See [CustomUnits][1] for an example. 422 | 423 | ### Understanding quantity types 424 | 425 | The same quantity type can often be expressed in multiple different ways. Take 426 | the `Volume` type as an example. It is an alias for 427 | 428 | ```elm 429 | Quantity Float CubicMeters 430 | ``` 431 | 432 | but expanding the `CubicMeters` type alias, this is equivalent to 433 | 434 | ```elm 435 | Quantity Float (Cubed Meters) 436 | ``` 437 | 438 | which expands further to 439 | 440 | ```elm 441 | Quantity Float (Product (Product Meters Meters) Meters) 442 | ``` 443 | 444 | which could also be written as 445 | 446 | ```elm 447 | Quantity Float (Product (Squared Meters) Meters) 448 | ``` 449 | 450 | or even 451 | 452 | ```elm 453 | Quantity Float (Product SquareMeters Meters) 454 | ``` 455 | 456 | and you may see any one of these forms pop up in compiler error messages. 457 | 458 | ## Getting Help 459 | 460 | For general questions about using `elm-units`, try asking in the [Elm Slack][3] 461 | or posting on the [Elm Discourse forums][4] or the [Elm subreddit][5]. I'm 462 | **@ianmackenzie** on all three =) 463 | 464 | ## API 465 | 466 | [Full API documentation][10] is available. 467 | 468 | ## Climate action 469 | 470 | I would like for the projects I work on to be as helpful as possible in 471 | addressing the climate crisis. If 472 | 473 | - you are working on a project that helps address the climate crisis (clean 474 | energy, public transit, reforestation, sustainable agriculture etc.) either as 475 | an individual, as part of an non-profit organization or even as part of a 476 | for-profit company, and 477 | - there is a new feature you would find helpful for that work (or a bug you need 478 | fixed) in any of my open-source projects, then 479 | 480 | please [open a new issue](https://github.com/ianmackenzie/elm-units/issues), 481 | describe briefly what you're working on and I will treat that issue as high 482 | priority. 483 | 484 | ## Contributing 485 | 486 | Yes please! One of the best ways to contribute is to add a module for a new 487 | quantity type; see [issue #6][7] for details. I'll add a proper CONTRIBUTING.md 488 | at some point, but some brief guidelines in the meantime: 489 | 490 | - Open a pull request by forking this repository, creating a new branch in 491 | your fork, making all changes in that branch, then opening a pull request 492 | from that branch. 493 | - Format code with [`elm-format`][8] 0.8.1. 494 | - Git commit messages should follow [the seven rules of a great Git commit 495 | message][9], although I'm not strict about the 50 or 72 character rules. 496 | 497 | ## License 498 | 499 | [BSD-3-Clause © Ian Mackenzie][2] 500 | 501 | [1]: https://github.com/ianmackenzie/elm-units/blob/master/doc/CustomUnits.md 502 | [2]: https://github.com/ianmackenzie/elm-units/blob/master/LICENSE 503 | [3]: http://elmlang.herokuapp.com/ 504 | [4]: https://discourse.elm-lang.org/ 505 | [5]: https://www.reddit.com/r/elm/ 506 | [6]: https://en.wikipedia.org/wiki/International_System_of_Units 507 | [7]: https://github.com/ianmackenzie/elm-units/issues/6 508 | [8]: https://github.com/avh4/elm-format 509 | [9]: https://chris.beams.io/posts/git-commit/#seven-rules 510 | [10]: https://package.elm-lang.org/packages/ianmackenzie/elm-units/latest 511 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing 2 | ( angleNormalization 3 | , angularAccelerations 4 | , angularSpeeds 5 | , conversionsToQuantityAndBack 6 | , densities 7 | , durations 8 | , fractionalModBy 9 | , fractionalRemainderBy 10 | , fromDmsNegative 11 | , fromDmsPositive 12 | , illuminances 13 | , inductance 14 | , lengths 15 | , luminances 16 | , masses 17 | , maximumBy 18 | , minimumBy 19 | , negativeInfinity 20 | , over 21 | , positiveInfinity 22 | , powers 23 | , pressures 24 | , solidAngles 25 | , speeds 26 | , substanceAmount 27 | , temperatureDeltas 28 | , temperatures 29 | , timeOffset 30 | , times 31 | , toDmsProducesValidValues 32 | , toDmsReconstructsAngle 33 | , torques 34 | , volumes 35 | ) 36 | 37 | import Acceleration exposing (..) 38 | import Angle 39 | import AngularAcceleration exposing (..) 40 | import AngularSpeed exposing (..) 41 | import Area exposing (..) 42 | import Capacitance exposing (..) 43 | import Charge exposing (..) 44 | import Current exposing (..) 45 | import Density exposing (..) 46 | import Duration exposing (..) 47 | import Energy exposing (..) 48 | import Expect exposing (Expectation) 49 | import Force exposing (..) 50 | import Fuzz exposing (Fuzzer) 51 | import Illuminance 52 | import Inductance exposing (..) 53 | import Length exposing (..) 54 | import Luminance 55 | import LuminousFlux 56 | import LuminousIntensity 57 | import Mass exposing (..) 58 | import Molarity exposing (..) 59 | import Pixels 60 | import Power exposing (..) 61 | import Pressure exposing (..) 62 | import Quantity exposing (Quantity(..), at, at_, minus, per, plus, times) 63 | import Random 64 | import Resistance exposing (..) 65 | import SolidAngle 66 | import Speed exposing (..) 67 | import SubstanceAmount exposing (..) 68 | import Temperature exposing (Temperature) 69 | import Test exposing (Test) 70 | import Time 71 | import Torque exposing (newtonMeters, poundFeet) 72 | import Voltage exposing (..) 73 | import Volume exposing (..) 74 | 75 | 76 | equalityTest : String -> String -> ( Quantity Float units, Quantity Float units ) -> Test 77 | equalityTest title unit ( Quantity x, Quantity y ) = 78 | let 79 | description = 80 | String.join " " 81 | [ title 82 | , String.fromFloat x ++ unit 83 | , "and" 84 | , String.fromFloat y ++ unit 85 | , "should be equal" 86 | ] 87 | in 88 | Test.test description (\() -> Expect.within (Expect.Absolute 1.0e-12) x y) 89 | 90 | 91 | equalPairs : String -> String -> List ( Quantity Float units, Quantity Float units ) -> Test 92 | equalPairs title unit pairs = 93 | Test.describe title (List.map (equalityTest title unit) pairs) 94 | 95 | 96 | fuzzFloatToQuantityAndBack : String -> (Float -> Quantity Float unit) -> (Quantity Float unit -> Float) -> Test 97 | fuzzFloatToQuantityAndBack testName toQuantity fromQuantity = 98 | Test.fuzz (Fuzz.floatRange -10 10) testName <| 99 | \randomFloat -> 100 | Expect.within (Expect.Absolute 1.0e-12) randomFloat (randomFloat |> toQuantity |> fromQuantity) 101 | 102 | 103 | inductance : Test 104 | inductance = 105 | equalPairs 106 | "Inductance" 107 | "H" 108 | [ ( henries 1000 109 | , kilohenries 1 110 | ) 111 | , ( nanohenries 1000 112 | , microhenries 1 113 | ) 114 | , ( kilohenries 10 115 | , millihenries 10000000 116 | ) 117 | ] 118 | 119 | 120 | lengths : Test 121 | lengths = 122 | equalPairs 123 | "Lengths" 124 | "m" 125 | [ ( inches 1 126 | , centimeters 2.54 127 | ) 128 | , ( feet 3 129 | , yards 1 130 | ) 131 | , ( miles 1 132 | , feet 5280 133 | ) 134 | , ( meters 1 135 | , microns 1.0e6 136 | ) 137 | , ( angstroms 2.0e10 138 | , meters 2 139 | ) 140 | , ( nanometers 1 141 | , angstroms 10 142 | ) 143 | , ( cssPixels 1 144 | , inches (1 / 96) 145 | ) 146 | , ( points 1 147 | , inches (1 / 72) 148 | ) 149 | , ( picas 1 150 | , inches (1 / 6) 151 | ) 152 | ] 153 | 154 | 155 | speeds : Test 156 | speeds = 157 | equalPairs 158 | "Speeds" 159 | "m/s" 160 | [ ( metersPerSecond 2.5 161 | , meters 5 |> per (seconds 2) 162 | ) 163 | , ( milesPerHour 60 164 | , miles 1 |> per (minutes 1) 165 | ) 166 | , ( lightYears 1 |> per (julianYears 1) 167 | , metersPerSecond 299792458 168 | ) 169 | ] 170 | 171 | 172 | angularAccelerations : Test 173 | angularAccelerations = 174 | equalPairs 175 | "Angular Accelerations" 176 | "rad/s" 177 | [ ( degreesPerSecondSquared 360 178 | , turnsPerSecondSquared 1 179 | ) 180 | ] 181 | 182 | 183 | angularSpeeds : Test 184 | angularSpeeds = 185 | equalPairs 186 | "Angular Speeds" 187 | "rad/s" 188 | [ ( turnsPerSecond 1 189 | , turnsPerMinute 60 190 | ) 191 | ] 192 | 193 | 194 | powers : Test 195 | powers = 196 | equalPairs 197 | "Powers" 198 | "W" 199 | [ ( watts 50 200 | , amperes 5 |> at (amperes 5 |> at (ohms 2)) 201 | ) 202 | ] 203 | 204 | 205 | pressures : Test 206 | pressures = 207 | equalPairs 208 | "Pressures" 209 | "Pa" 210 | [ ( atmospheres 1 211 | , kilopascals 101.325 212 | ) 213 | ] 214 | 215 | 216 | masses : Test 217 | masses = 218 | equalPairs 219 | "Masses" 220 | "kg" 221 | [ ( grams 1 222 | , kilograms 0.001 223 | ) 224 | , ( ounces 1 225 | , Mass.pounds 0.0625 226 | ) 227 | , ( Mass.pounds 1 228 | , kilograms 0.45359237 229 | ) 230 | ] 231 | 232 | 233 | densities : Test 234 | densities = 235 | equalPairs 236 | "Densities" 237 | "kg/m^3" 238 | [ ( gramsPerCubicCentimeter 1 239 | , kilogramsPerCubicMeter 1000 240 | ) 241 | ] 242 | 243 | 244 | durations : Test 245 | durations = 246 | equalPairs 247 | "Durations" 248 | "s" 249 | [ ( julianYears 1 250 | , days 365.25 251 | ) 252 | ] 253 | 254 | 255 | substanceAmount : Test 256 | substanceAmount = 257 | equalPairs 258 | "SubstanceAmounts" 259 | "ν" 260 | [ ( nanomoles 20 261 | , picomoles 20000 262 | ) 263 | , ( nanomoles 7000 264 | , micromoles 7 265 | ) 266 | , ( millimoles 3 267 | , micromoles 3000 268 | ) 269 | , ( nanomoles 1000000 270 | , millimoles 1 271 | ) 272 | , ( centimoles 600 273 | , decimoles 60 274 | ) 275 | , ( moles 1 276 | , millimoles 1000 277 | ) 278 | , ( moles 4 279 | , centimoles 400 280 | ) 281 | , ( moles 2 282 | , decimoles 20 283 | ) 284 | , ( moles 2000 285 | , kilomoles 2 286 | ) 287 | , ( kilomoles 1000 288 | , megamoles 1 289 | ) 290 | , ( megamoles 1000 291 | , gigamoles 1 292 | ) 293 | ] 294 | 295 | 296 | temperatureEqualityTest : ( Temperature, Temperature ) -> Test 297 | temperatureEqualityTest ( x, y ) = 298 | let 299 | xInCelsius = 300 | Temperature.inDegreesCelsius x 301 | 302 | yInCelsius = 303 | Temperature.inDegreesCelsius y 304 | 305 | description = 306 | String.join " " 307 | [ "Temperatures" 308 | , String.fromFloat xInCelsius ++ "C" 309 | , "and" 310 | , String.fromFloat yInCelsius ++ "C" 311 | , "should be equal" 312 | ] 313 | in 314 | Test.test description 315 | (\() -> Expect.within (Expect.Absolute 1.0e-12) xInCelsius yInCelsius) 316 | 317 | 318 | temperatures : Test 319 | temperatures = 320 | Test.describe "Temperatures" <| 321 | List.map temperatureEqualityTest 322 | [ ( Temperature.degreesCelsius -40 323 | , Temperature.degreesFahrenheit -40 324 | ) 325 | , ( Temperature.degreesCelsius 0 326 | , Temperature.degreesFahrenheit 32 327 | ) 328 | , ( Temperature.degreesCelsius 25 329 | |> Temperature.plus (Temperature.fahrenheitDegrees 3) 330 | , Temperature.degreesFahrenheit 80 331 | ) 332 | , ( Temperature.absoluteZero 333 | , Temperature.degreesCelsius -273.15 334 | ) 335 | ] 336 | 337 | 338 | temperatureDeltas : Test 339 | temperatureDeltas = 340 | equalPairs 341 | "Temperature deltas" 342 | "K" 343 | [ ( Quantity.sum 344 | [ Temperature.celsiusDegrees 10 345 | , Temperature.fahrenheitDegrees 5 346 | ] 347 | , Temperature.fahrenheitDegrees 23 348 | ) 349 | ] 350 | 351 | 352 | torques : Test 353 | torques = 354 | equalPairs "Torques" 355 | "Nm" 356 | [ ( newtonMeters 1, poundFeet 0.7375621492772654 ) ] 357 | 358 | 359 | volumes : Test 360 | volumes = 361 | equalPairs 362 | "Volumes" 363 | "m^3" 364 | [ ( cubicMeters 2 365 | , usLiquidGallons (2 * 264.17205235814845) 366 | ) 367 | , ( imperialGallons 219.96924829908778 368 | , usDryGallons 227.02074606721396 369 | ) 370 | , ( cubicInches (36 * 36 * 36) 371 | , cubicYards 1 372 | ) 373 | , ( usLiquidGallons 1 374 | , usLiquidQuarts 4 375 | ) 376 | , ( usDryGallons 1 377 | , usDryQuarts 4 378 | ) 379 | , ( imperialGallons 1 380 | , imperialQuarts 4 381 | ) 382 | , ( usLiquidQuarts 1 383 | , usLiquidPints 2 384 | ) 385 | , ( usDryQuarts 1 386 | , usDryPints 2 387 | ) 388 | , ( imperialQuarts 1 389 | , imperialPints 2 390 | ) 391 | , ( usLiquidPints 1 392 | , usFluidOunces 16 393 | ) 394 | , ( imperialPints 1 395 | , imperialFluidOunces 20 396 | ) 397 | , ( cubicCentimeters 1 398 | , milliliters 1 399 | ) 400 | ] 401 | 402 | 403 | times : Test 404 | times = 405 | Test.describe "times" 406 | [ equalPairs 407 | "Areas" 408 | "m^2" 409 | [ ( Length.feet 66 |> Quantity.times (Length.feet 660) 410 | , Area.acres 1 411 | ) 412 | ] 413 | , equalPairs 414 | "Volumes" 415 | "m^3" 416 | [ ( Area.squareMeters 1 |> Quantity.times (Length.centimeters 1) 417 | , Volume.liters 10 418 | ) 419 | ] 420 | , equalPairs 421 | "Forces" 422 | "N" 423 | [ ( Mass.kilograms 10 |> Quantity.times (Acceleration.gees 1) 424 | , Force.newtons 98.0665 425 | ) 426 | ] 427 | , equalPairs 428 | "Energies" 429 | "J" 430 | [ ( Force.newtons 5 |> Quantity.times (Length.meters 4) 431 | , Energy.joules 20 432 | ) 433 | , ( Mass.kilograms 1 434 | |> Quantity.times (Acceleration.metersPerSecondSquared 2) 435 | |> Quantity.times (Length.meters 3) 436 | , Energy.joules 6 437 | ) 438 | ] 439 | ] 440 | 441 | 442 | over : Test 443 | over = 444 | Test.describe "over" 445 | [ equalPairs 446 | "Area / length" 447 | "m" 448 | [ ( Area.squareKilometers 1 |> Quantity.over (Length.meters 100) 449 | , Length.kilometers 10 450 | ) 451 | ] 452 | , equalPairs 453 | "Volume / length" 454 | "m^2" 455 | [ ( Volume.liters 10 |> Quantity.over_ (Length.centimeters 10) 456 | , Area.squareCentimeters 1000 457 | ) 458 | ] 459 | , equalPairs 460 | "Volume / area" 461 | "m" 462 | [ ( Volume.cubicMeters 1.0e7 463 | |> Quantity.over 464 | (Area.squareKilometers 1) 465 | , Length.meters 10 466 | ) 467 | ] 468 | ] 469 | 470 | 471 | conversionsToQuantityAndBack : Test 472 | conversionsToQuantityAndBack = 473 | Test.describe "Conversion to Quantity and back is (almost) identity" <| 474 | [ Test.describe "Acceleration" <| 475 | [ fuzzFloatToQuantityAndBack "metersPerSecondSquared" Acceleration.metersPerSecondSquared Acceleration.inMetersPerSecondSquared 476 | , fuzzFloatToQuantityAndBack "feetPerSecondSquared" Acceleration.feetPerSecondSquared Acceleration.inFeetPerSecondSquared 477 | , fuzzFloatToQuantityAndBack "gees" Acceleration.gees Acceleration.inGees 478 | ] 479 | , Test.describe "Angle" <| 480 | [ fuzzFloatToQuantityAndBack "radians" Angle.radians Angle.inRadians 481 | , fuzzFloatToQuantityAndBack "degrees" Angle.degrees Angle.inDegrees 482 | , fuzzFloatToQuantityAndBack "turns" Angle.turns Angle.inTurns 483 | ] 484 | , Test.describe "AngularAcceleration" <| 485 | [ fuzzFloatToQuantityAndBack "radiansPerSecondSquared" AngularAcceleration.radiansPerSecondSquared AngularAcceleration.inRadiansPerSecondSquared 486 | , fuzzFloatToQuantityAndBack "degreesPerSecondSquared" AngularAcceleration.degreesPerSecondSquared AngularAcceleration.inDegreesPerSecondSquared 487 | , fuzzFloatToQuantityAndBack "turnsPerSecondSquared" AngularAcceleration.turnsPerSecondSquared AngularAcceleration.inTurnsPerSecondSquared 488 | ] 489 | , Test.describe "AngularSpeed" <| 490 | [ fuzzFloatToQuantityAndBack "radiansPerSecond" AngularSpeed.radiansPerSecond AngularSpeed.inRadiansPerSecond 491 | , fuzzFloatToQuantityAndBack "degreesPerSecond" AngularSpeed.degreesPerSecond AngularSpeed.inDegreesPerSecond 492 | , fuzzFloatToQuantityAndBack "turnsPerSecond" AngularSpeed.turnsPerSecond AngularSpeed.inTurnsPerSecond 493 | , fuzzFloatToQuantityAndBack "turnsPerMinute" AngularSpeed.turnsPerMinute AngularSpeed.inTurnsPerMinute 494 | ] 495 | , Test.describe "Area" <| 496 | [ fuzzFloatToQuantityAndBack "squareMeters" Area.squareMeters Area.inSquareMeters 497 | , fuzzFloatToQuantityAndBack "squareMillimeters" Area.squareMillimeters Area.inSquareMillimeters 498 | , fuzzFloatToQuantityAndBack "squareInches" Area.squareInches Area.inSquareInches 499 | , fuzzFloatToQuantityAndBack "squareCentimeters" Area.squareCentimeters Area.inSquareCentimeters 500 | , fuzzFloatToQuantityAndBack "squareFeet" Area.squareFeet Area.inSquareFeet 501 | , fuzzFloatToQuantityAndBack "squareYards" Area.squareYards Area.inSquareYards 502 | , fuzzFloatToQuantityAndBack "hectares" Area.hectares Area.inHectares 503 | , fuzzFloatToQuantityAndBack "squareKilometers" Area.squareKilometers Area.inSquareKilometers 504 | , fuzzFloatToQuantityAndBack "acres" Area.acres Area.inAcres 505 | , fuzzFloatToQuantityAndBack "squareMiles" Area.squareMiles Area.inSquareMiles 506 | ] 507 | , Test.describe "Capacitance" <| 508 | [ fuzzFloatToQuantityAndBack "farads" Capacitance.farads Capacitance.inFarads 509 | , fuzzFloatToQuantityAndBack "microfarads" Capacitance.microfarads Capacitance.inMicrofarads 510 | , fuzzFloatToQuantityAndBack "nanofarads" Capacitance.nanofarads Capacitance.inNanofarads 511 | , fuzzFloatToQuantityAndBack "picofarads" Capacitance.picofarads Capacitance.inPicofarads 512 | ] 513 | , Test.describe "Charge" <| 514 | [ fuzzFloatToQuantityAndBack "coulombs" Charge.coulombs Charge.inCoulombs 515 | , fuzzFloatToQuantityAndBack "ampereHours" Charge.ampereHours Charge.inAmpereHours 516 | , fuzzFloatToQuantityAndBack "milliampereHours" Charge.milliampereHours Charge.inMilliampereHours 517 | ] 518 | , Test.describe "Current" <| 519 | [ fuzzFloatToQuantityAndBack "ampere" Current.amperes Current.inAmperes 520 | , fuzzFloatToQuantityAndBack "milliampere" Current.milliamperes Current.inMilliamperes 521 | ] 522 | , Test.describe "Density" <| 523 | [ fuzzFloatToQuantityAndBack "kilogramsPerCubicMeter" Density.kilogramsPerCubicMeter Density.inKilogramsPerCubicMeter 524 | , fuzzFloatToQuantityAndBack "gramsPerCubicCentimeter" Density.gramsPerCubicCentimeter Density.inGramsPerCubicCentimeter 525 | , fuzzFloatToQuantityAndBack "poundsPerCubicInch" Density.poundsPerCubicInch Density.inPoundsPerCubicInch 526 | , fuzzFloatToQuantityAndBack "poundsPerCubicFoot" Density.poundsPerCubicFoot Density.inPoundsPerCubicFoot 527 | ] 528 | , Test.describe "Duration" <| 529 | [ fuzzFloatToQuantityAndBack "seconds" Duration.seconds Duration.inSeconds 530 | , fuzzFloatToQuantityAndBack "milliseconds" Duration.milliseconds Duration.inMilliseconds 531 | , fuzzFloatToQuantityAndBack "minutes" Duration.minutes Duration.inMinutes 532 | , fuzzFloatToQuantityAndBack "hours" Duration.hours Duration.inHours 533 | , fuzzFloatToQuantityAndBack "days" Duration.days Duration.inDays 534 | , fuzzFloatToQuantityAndBack "weeks" Duration.weeks Duration.inWeeks 535 | , fuzzFloatToQuantityAndBack "julianYears" Duration.julianYears Duration.inJulianYears 536 | ] 537 | , Test.describe "Energy" <| 538 | [ fuzzFloatToQuantityAndBack "joules" Energy.joules Energy.inJoules 539 | , fuzzFloatToQuantityAndBack "kilojoules" Energy.kilojoules Energy.inKilojoules 540 | , fuzzFloatToQuantityAndBack "megajoules" Energy.megajoules Energy.inMegajoules 541 | , fuzzFloatToQuantityAndBack "kilowattHours" Energy.kilowattHours Energy.inKilowattHours 542 | ] 543 | , Test.describe "Force" <| 544 | [ fuzzFloatToQuantityAndBack "newtons" Force.newtons Force.inNewtons 545 | , fuzzFloatToQuantityAndBack "kilonewtons" Force.kilonewtons Force.inKilonewtons 546 | , fuzzFloatToQuantityAndBack "meganewtons" Force.meganewtons Force.inMeganewtons 547 | , fuzzFloatToQuantityAndBack "pounds" Force.pounds Force.inPounds 548 | , fuzzFloatToQuantityAndBack "kips" Force.kips Force.inKips 549 | ] 550 | , Test.describe "Inductance" <| 551 | [ fuzzFloatToQuantityAndBack "henries" Inductance.henries Inductance.inHenries 552 | , fuzzFloatToQuantityAndBack "millihenries" Inductance.millihenries Inductance.inMillihenries 553 | , fuzzFloatToQuantityAndBack "microhenries" Inductance.microhenries Inductance.inMicrohenries 554 | , fuzzFloatToQuantityAndBack "nanohenries" Inductance.nanohenries Inductance.inNanohenries 555 | , fuzzFloatToQuantityAndBack "kilohenries" Inductance.kilohenries Inductance.inKilohenries 556 | ] 557 | , Test.describe "Length" <| 558 | [ fuzzFloatToQuantityAndBack "meters" Length.meters Length.inMeters 559 | , fuzzFloatToQuantityAndBack "microns" Length.microns Length.inMicrons 560 | , fuzzFloatToQuantityAndBack "millimeters" Length.millimeters Length.inMillimeters 561 | , fuzzFloatToQuantityAndBack "thou" Length.thou Length.inThou 562 | , fuzzFloatToQuantityAndBack "inches" Length.inches Length.inInches 563 | , fuzzFloatToQuantityAndBack "centimeters" Length.centimeters Length.inCentimeters 564 | , fuzzFloatToQuantityAndBack "feet" Length.feet Length.inFeet 565 | , fuzzFloatToQuantityAndBack "yards" Length.yards Length.inYards 566 | , fuzzFloatToQuantityAndBack "kilometers" Length.kilometers Length.inKilometers 567 | , fuzzFloatToQuantityAndBack "miles" Length.miles Length.inMiles 568 | , fuzzFloatToQuantityAndBack "astronomicalUnits" Length.astronomicalUnits Length.inAstronomicalUnits 569 | , fuzzFloatToQuantityAndBack "parsecs" Length.parsecs Length.inParsecs 570 | , fuzzFloatToQuantityAndBack "lightYears" Length.lightYears Length.inLightYears 571 | , fuzzFloatToQuantityAndBack "cssPixels" Length.cssPixels Length.inCssPixels 572 | , fuzzFloatToQuantityAndBack "points" Length.points Length.inPoints 573 | , fuzzFloatToQuantityAndBack "picas" Length.picas Length.inPicas 574 | ] 575 | , Test.describe "Mass" <| 576 | [ fuzzFloatToQuantityAndBack "kilograms" Mass.kilograms Mass.inKilograms 577 | , fuzzFloatToQuantityAndBack "grams" Mass.grams Mass.inGrams 578 | , fuzzFloatToQuantityAndBack "pounds" Mass.pounds Mass.inPounds 579 | , fuzzFloatToQuantityAndBack "ounces" Mass.ounces Mass.inOunces 580 | , fuzzFloatToQuantityAndBack "metricTons" Mass.metricTons Mass.inMetricTons 581 | , fuzzFloatToQuantityAndBack "shortTons" Mass.shortTons Mass.inShortTons 582 | , fuzzFloatToQuantityAndBack "longTons" Mass.longTons Mass.inLongTons 583 | ] 584 | , Test.describe "Molarity" <| 585 | [ fuzzFloatToQuantityAndBack "molesPerCubicMeter" Molarity.molesPerCubicMeter Molarity.inMolesPerCubicMeter 586 | , fuzzFloatToQuantityAndBack "decimolesPerLiter" Molarity.decimolesPerLiter Molarity.inDecimolesPerLiter 587 | , fuzzFloatToQuantityAndBack "centimolesPerLiter" Molarity.centimolesPerLiter Molarity.inCentimolesPerLiter 588 | , fuzzFloatToQuantityAndBack "millimolesPerLiter" Molarity.millimolesPerLiter Molarity.inMillimolesPerLiter 589 | , fuzzFloatToQuantityAndBack "micromolesPerLiter" Molarity.micromolesPerLiter Molarity.inMicromolesPerLiter 590 | ] 591 | , Test.describe "Pixels" <| 592 | [ fuzzFloatToQuantityAndBack "pixels" Pixels.pixels Pixels.inPixels 593 | , fuzzFloatToQuantityAndBack "pixelsPerSecond" Pixels.pixelsPerSecond Pixels.inPixelsPerSecond 594 | , fuzzFloatToQuantityAndBack "pixelsPerSecondSquared" Pixels.pixelsPerSecondSquared Pixels.inPixelsPerSecondSquared 595 | , fuzzFloatToQuantityAndBack "squarePixels" Pixels.squarePixels Pixels.inSquarePixels 596 | ] 597 | , Test.describe "Power" <| 598 | [ fuzzFloatToQuantityAndBack "watts" Power.watts Power.inWatts 599 | , fuzzFloatToQuantityAndBack "kilowatts" Power.kilowatts Power.inKilowatts 600 | , fuzzFloatToQuantityAndBack "megawatts" Power.megawatts Power.inMegawatts 601 | , fuzzFloatToQuantityAndBack "metricHorsepower" Power.metricHorsepower Power.inMetricHorsepower 602 | , fuzzFloatToQuantityAndBack "mechanicalHorsepower" Power.mechanicalHorsepower Power.inMechanicalHorsepower 603 | , fuzzFloatToQuantityAndBack "electricalHorsepower" Power.electricalHorsepower Power.inElectricalHorsepower 604 | ] 605 | , Test.describe "Pressure" <| 606 | [ fuzzFloatToQuantityAndBack "pascals" Pressure.pascals Pressure.inPascals 607 | , fuzzFloatToQuantityAndBack "kilopascals" Pressure.kilopascals Pressure.inKilopascals 608 | , fuzzFloatToQuantityAndBack "megapascals" Pressure.megapascals Pressure.inMegapascals 609 | , fuzzFloatToQuantityAndBack "poundsPerSquareInch" Pressure.poundsPerSquareInch Pressure.inPoundsPerSquareInch 610 | , fuzzFloatToQuantityAndBack "atmospheres" Pressure.atmospheres Pressure.inAtmospheres 611 | ] 612 | , Test.describe "Resistance" <| 613 | [ fuzzFloatToQuantityAndBack "ohms" Resistance.ohms Resistance.inOhms 614 | ] 615 | , Test.describe "Speed" <| 616 | [ fuzzFloatToQuantityAndBack "metersPerSecond" Speed.metersPerSecond Speed.inMetersPerSecond 617 | , fuzzFloatToQuantityAndBack "feetPerSecond" Speed.feetPerSecond Speed.inFeetPerSecond 618 | , fuzzFloatToQuantityAndBack "kilometersPerHour" Speed.kilometersPerHour Speed.inKilometersPerHour 619 | , fuzzFloatToQuantityAndBack "milesPerHour" Speed.milesPerHour Speed.inMilesPerHour 620 | ] 621 | , Test.describe "SubstanceAmount" <| 622 | [ fuzzFloatToQuantityAndBack "moles" SubstanceAmount.moles SubstanceAmount.inMoles 623 | , fuzzFloatToQuantityAndBack "picomoles" SubstanceAmount.picomoles SubstanceAmount.inPicomoles 624 | , fuzzFloatToQuantityAndBack "nanomoles" SubstanceAmount.nanomoles SubstanceAmount.inNanomoles 625 | , fuzzFloatToQuantityAndBack "micromoles" SubstanceAmount.micromoles SubstanceAmount.inMicromoles 626 | , fuzzFloatToQuantityAndBack "millimoles" SubstanceAmount.millimoles SubstanceAmount.inMillimoles 627 | , fuzzFloatToQuantityAndBack "centimoles" SubstanceAmount.centimoles SubstanceAmount.inCentimoles 628 | , fuzzFloatToQuantityAndBack "decimoles" SubstanceAmount.decimoles SubstanceAmount.inDecimoles 629 | , fuzzFloatToQuantityAndBack "kilomoles" SubstanceAmount.kilomoles SubstanceAmount.inKilomoles 630 | , fuzzFloatToQuantityAndBack "megamoles" SubstanceAmount.megamoles SubstanceAmount.inMegamoles 631 | , fuzzFloatToQuantityAndBack "gigamoles" SubstanceAmount.gigamoles SubstanceAmount.inGigamoles 632 | ] 633 | , Test.describe "Temperature" <| 634 | [ fuzzFloatToQuantityAndBack "celsiusDegrees" Temperature.celsiusDegrees Temperature.inCelsiusDegrees 635 | , fuzzFloatToQuantityAndBack "fahrenheitDegrees" Temperature.fahrenheitDegrees Temperature.inFahrenheitDegrees 636 | ] 637 | , Test.describe "Voltage" <| 638 | [ fuzzFloatToQuantityAndBack "volts" Voltage.volts Voltage.inVolts 639 | ] 640 | , Test.describe "Volume" <| 641 | [ fuzzFloatToQuantityAndBack "cubicMeters" Volume.cubicMeters Volume.inCubicMeters 642 | , fuzzFloatToQuantityAndBack "cubicCentimeters" Volume.cubicCentimeters Volume.inCubicCentimeters 643 | , fuzzFloatToQuantityAndBack "cubicInches" Volume.cubicInches Volume.inCubicInches 644 | , fuzzFloatToQuantityAndBack "cubicFeet" Volume.cubicFeet Volume.inCubicFeet 645 | , fuzzFloatToQuantityAndBack "cubicYards" Volume.cubicYards Volume.inCubicYards 646 | , fuzzFloatToQuantityAndBack "milliliters" Volume.milliliters Volume.inMilliliters 647 | , fuzzFloatToQuantityAndBack "liters" Volume.liters Volume.inLiters 648 | , fuzzFloatToQuantityAndBack "usLiquidGallons" Volume.usLiquidGallons Volume.inUsLiquidGallons 649 | , fuzzFloatToQuantityAndBack "usDryGallons" Volume.usDryGallons Volume.inUsDryGallons 650 | , fuzzFloatToQuantityAndBack "imperialGallons" Volume.imperialGallons Volume.inImperialGallons 651 | , fuzzFloatToQuantityAndBack "usLiquidQuarts" Volume.usLiquidQuarts Volume.inUsLiquidQuarts 652 | , fuzzFloatToQuantityAndBack "usDryQuarts" Volume.usDryQuarts Volume.inUsDryQuarts 653 | , fuzzFloatToQuantityAndBack "imperialQuarts" Volume.imperialQuarts Volume.inImperialQuarts 654 | , fuzzFloatToQuantityAndBack "usLiquidPints" Volume.usLiquidPints Volume.inUsLiquidPints 655 | , fuzzFloatToQuantityAndBack "usDryPints" Volume.usDryPints Volume.inUsDryPints 656 | , fuzzFloatToQuantityAndBack "imperialPints" Volume.imperialPints Volume.inImperialPints 657 | , fuzzFloatToQuantityAndBack "usFluidOunces" Volume.usFluidOunces Volume.inUsFluidOunces 658 | , fuzzFloatToQuantityAndBack "imperialFluidOunces" Volume.imperialFluidOunces Volume.inImperialFluidOunces 659 | ] 660 | , Test.describe "SolidAngle" <| 661 | [ fuzzFloatToQuantityAndBack "steradians" SolidAngle.steradians SolidAngle.inSteradians 662 | , fuzzFloatToQuantityAndBack "spats" SolidAngle.spats SolidAngle.inSpats 663 | , fuzzFloatToQuantityAndBack "squareDegrees" SolidAngle.squareDegrees SolidAngle.inSquareDegrees 664 | ] 665 | , Test.describe "LuminousFlux" <| 666 | [ fuzzFloatToQuantityAndBack "lumens" LuminousFlux.lumens LuminousFlux.inLumens 667 | ] 668 | , Test.describe "Illuminance" <| 669 | [ fuzzFloatToQuantityAndBack "lux" Illuminance.lux Illuminance.inLux 670 | , fuzzFloatToQuantityAndBack "footCandles" Illuminance.footCandles Illuminance.inFootCandles 671 | ] 672 | , Test.describe "LuminousIntensity" <| 673 | [ fuzzFloatToQuantityAndBack "candelas" LuminousIntensity.candelas LuminousIntensity.inCandelas 674 | ] 675 | , Test.describe "Luminance" <| 676 | [ fuzzFloatToQuantityAndBack "nits" Luminance.nits Luminance.inNits 677 | , fuzzFloatToQuantityAndBack "footLamberts" Luminance.footLamberts Luminance.inFootLamberts 678 | ] 679 | ] 680 | 681 | 682 | fromDmsPositive : Test 683 | fromDmsPositive = 684 | Test.fuzz3 685 | (Fuzz.intRange 0 1000) 686 | (Fuzz.intRange 0 59) 687 | (Fuzz.floatRange 0 60) 688 | "toDms works for positive angles" 689 | (\numDegrees numMinutes numSeconds -> 690 | let 691 | angle1 = 692 | Angle.fromDms 693 | { sign = Angle.Positive 694 | , degrees = numDegrees 695 | , minutes = numMinutes 696 | , seconds = numSeconds 697 | } 698 | 699 | angle2 = 700 | Quantity.sum 701 | [ Angle.degrees (toFloat numDegrees) 702 | , Angle.minutes (toFloat numMinutes) 703 | , Angle.seconds numSeconds 704 | ] 705 | in 706 | Expect.within (Expect.Absolute 1.0e-12) 707 | (Angle.inRadians angle1) 708 | (Angle.inRadians angle2) 709 | ) 710 | 711 | 712 | fromDmsNegative : Test 713 | fromDmsNegative = 714 | Test.fuzz3 715 | (Fuzz.intRange 0 1000) 716 | (Fuzz.intRange 0 59) 717 | (Fuzz.floatRange 0 60) 718 | "toDms works for negative angles" 719 | (\numDegrees numMinutes numSeconds -> 720 | let 721 | angle1 = 722 | Angle.fromDms 723 | { sign = Angle.Negative 724 | , degrees = numDegrees 725 | , minutes = numMinutes 726 | , seconds = numSeconds 727 | } 728 | 729 | angle2 = 730 | Quantity.negate <| 731 | Quantity.sum 732 | [ Angle.degrees (toFloat numDegrees) 733 | , Angle.minutes (toFloat numMinutes) 734 | , Angle.seconds numSeconds 735 | ] 736 | in 737 | Expect.within (Expect.Absolute 1.0e-12) 738 | (Angle.inRadians angle1) 739 | (Angle.inRadians angle2) 740 | ) 741 | 742 | 743 | toDmsReconstructsAngle : Test 744 | toDmsReconstructsAngle = 745 | Test.fuzz (Fuzz.map Angle.degrees (Fuzz.floatRange 0 1000)) 746 | "toDms reconstructs angles properly" 747 | (\originalAngle -> 748 | let 749 | { degrees, minutes, seconds } = 750 | Angle.toDms originalAngle 751 | 752 | reconstructedAngle = 753 | Quantity.sum 754 | [ Angle.degrees (toFloat degrees) 755 | , Angle.minutes (toFloat minutes) 756 | , Angle.seconds seconds 757 | ] 758 | in 759 | Expect.within (Expect.Absolute 1.0e-12) 760 | (Angle.inRadians originalAngle) 761 | (Angle.inRadians reconstructedAngle) 762 | ) 763 | 764 | 765 | toDmsProducesValidValues : Test 766 | toDmsProducesValidValues = 767 | Test.fuzz (Fuzz.map Angle.degrees (Fuzz.floatRange 0 1000)) 768 | "toDms only produces values in the valid range" 769 | (\angle -> 770 | let 771 | dms = 772 | Angle.toDms angle 773 | in 774 | dms 775 | |> Expect.all 776 | [ .degrees >> Expect.atLeast 0 777 | , .minutes >> Expect.atLeast 0 778 | , .minutes >> Expect.lessThan 60 779 | , .seconds >> Expect.atLeast 0 780 | , .seconds >> Expect.lessThan 60 781 | ] 782 | ) 783 | 784 | 785 | solidAngles : Test 786 | solidAngles = 787 | equalPairs 788 | "SolidAngles" 789 | "sr" 790 | [ ( SolidAngle.spats 1 791 | , SolidAngle.steradians (4 * pi) 792 | ) 793 | , ( SolidAngle.squareDegrees 1 794 | , SolidAngle.steradians ((pi / 180) ^ 2) 795 | ) 796 | ] 797 | 798 | 799 | luminances : Test 800 | luminances = 801 | equalPairs 802 | "Luminance" 803 | "nt" 804 | [ ( Luminance.footLamberts 1 805 | , LuminousIntensity.candelas (1 / pi) 806 | |> Quantity.per (Area.squareFeet 1) 807 | ) 808 | , ( Luminance.nits 1 809 | , LuminousIntensity.candelas 1 |> Quantity.per (Area.squareMeters 1) 810 | ) 811 | ] 812 | 813 | 814 | illuminances : Test 815 | illuminances = 816 | equalPairs 817 | "Illuminance" 818 | "lx" 819 | [ ( Illuminance.footCandles 1 820 | , LuminousFlux.lumens 1 |> Quantity.per (Area.squareFeet 1) 821 | ) 822 | ] 823 | 824 | 825 | quantityPairs : List { length : Length, duration : Duration } 826 | quantityPairs = 827 | [ { length = Length.feet 1, duration = Duration.seconds 1 } 828 | , { length = Length.meters 1, duration = Duration.hours 1 } 829 | , { length = Length.centimeters 1, duration = Duration.minutes 1 } 830 | ] 831 | 832 | 833 | minimumBy : Test 834 | minimumBy = 835 | Test.describe "minimumBy" 836 | [ Test.test "Minimum by length" <| 837 | \() -> 838 | Quantity.minimumBy .length quantityPairs 839 | |> Expect.equal 840 | (Just { length = Length.centimeters 1, duration = Duration.minutes 1 }) 841 | , Test.test "Minimum by duration" <| 842 | \() -> 843 | Quantity.minimumBy .duration quantityPairs 844 | |> Expect.equal 845 | (Just { length = Length.feet 1, duration = Duration.seconds 1 }) 846 | ] 847 | 848 | 849 | maximumBy : Test 850 | maximumBy = 851 | Test.describe "maximumBy" 852 | [ Test.test "Maximum by length" <| 853 | \() -> 854 | Quantity.maximumBy .length quantityPairs 855 | |> Expect.equal 856 | (Just { length = Length.meters 1, duration = Duration.hours 1 }) 857 | , Test.test "Maximum by duration" <| 858 | \() -> 859 | Quantity.maximumBy .duration quantityPairs 860 | |> Expect.equal 861 | (Just { length = Length.meters 1, duration = Duration.hours 1 }) 862 | ] 863 | 864 | 865 | timeFuzzer : Fuzzer Time.Posix 866 | timeFuzzer = 867 | Fuzz.map (abs >> Time.millisToPosix) Fuzz.int 868 | 869 | 870 | durationFuzzer : Fuzzer Duration 871 | durationFuzzer = 872 | Fuzz.map Duration.seconds Fuzz.float 873 | 874 | 875 | timeOffset : Test 876 | timeOffset = 877 | Test.describe "timeOffset" 878 | [ Test.fuzz2 879 | timeFuzzer 880 | durationFuzzer 881 | "addTo" 882 | (\time offset -> 883 | let 884 | offsetTime = 885 | Duration.addTo time offset 886 | in 887 | Duration.from time offsetTime 888 | |> Duration.inMilliseconds 889 | |> Expect.within (Expect.Absolute 1) (Duration.inMilliseconds offset) 890 | ) 891 | , Test.fuzz2 892 | timeFuzzer 893 | durationFuzzer 894 | "subtractFrom" 895 | (\time offset -> 896 | let 897 | offsetTime = 898 | Duration.subtractFrom time offset 899 | in 900 | Duration.from offsetTime time 901 | |> Duration.inMilliseconds 902 | |> Expect.within (Expect.Absolute 1) (Duration.inMilliseconds offset) 903 | ) 904 | ] 905 | 906 | 907 | modulusFuzzer : Fuzzer Int 908 | modulusFuzzer = 909 | Fuzz.oneOf 910 | [ Fuzz.intRange 1 Random.maxInt 911 | , Fuzz.intRange (Random.minInt + 1) -1 912 | ] 913 | 914 | 915 | valueFuzzer : Fuzzer Int 916 | valueFuzzer = 917 | Fuzz.intRange (Random.minInt + 1) Random.maxInt 918 | 919 | 920 | fractionalModBy : Test 921 | fractionalModBy = 922 | Test.fuzz2 923 | modulusFuzzer 924 | valueFuzzer 925 | "fractionalModBy" 926 | (\modulus value -> 927 | let 928 | (Quantity result) = 929 | Quantity.fractionalModBy (Quantity (toFloat modulus)) 930 | (Quantity (toFloat value)) 931 | in 932 | result |> Expect.within (Expect.Absolute 0.0) (toFloat (modBy modulus value)) 933 | ) 934 | 935 | 936 | fractionalRemainderBy : Test 937 | fractionalRemainderBy = 938 | Test.fuzz2 939 | modulusFuzzer 940 | valueFuzzer 941 | "fractionalRemainderBy" 942 | (\modulus value -> 943 | let 944 | (Quantity result) = 945 | Quantity.fractionalRemainderBy (Quantity (toFloat modulus)) 946 | (Quantity (toFloat value)) 947 | in 948 | result |> Expect.within (Expect.Absolute 0.0) (toFloat (remainderBy modulus value)) 949 | ) 950 | 951 | 952 | angleNormalization : Test 953 | angleNormalization = 954 | let 955 | angleFuzzer = 956 | Fuzz.map Angle.degrees (Fuzz.floatRange -10000 10000) 957 | in 958 | Test.describe "Angle.normalize" 959 | [ Test.fuzz angleFuzzer "Returns value in the correct range" <| 960 | \angle -> 961 | Angle.normalize angle 962 | |> Quantity.abs 963 | |> Angle.inRadians 964 | |> Expect.atMost pi 965 | , Test.fuzz angleFuzzer "Cosine is equal to the original angle" <| 966 | \angle -> 967 | Angle.cos (Angle.normalize angle) 968 | |> Expect.within (Expect.Absolute 1.0e-12) (Angle.cos angle) 969 | , Test.fuzz angleFuzzer "Sine is equal to the original angle" <| 970 | \angle -> 971 | Angle.sin (Angle.normalize angle) 972 | |> Expect.within (Expect.Absolute 1.0e-12) (Angle.sin angle) 973 | ] 974 | 975 | 976 | positiveInfinity : Test 977 | positiveInfinity = 978 | Test.fuzz Fuzz.float "Quantity.positiveInfinity" <| 979 | \value -> 980 | Quantity.positiveInfinity 981 | |> Quantity.greaterThan (Quantity value) 982 | |> Expect.true "positiveInfinity must be greater than any finite value" 983 | 984 | 985 | negativeInfinity : Test 986 | negativeInfinity = 987 | Test.fuzz Fuzz.float "Quantity.negativeInfinity" <| 988 | \value -> 989 | Quantity.negativeInfinity 990 | |> Quantity.lessThan (Quantity value) 991 | |> Expect.true "negativeInfinity must be less than any finite value" 992 | --------------------------------------------------------------------------------