ianmackenzie/elm-units-interval/1.1.0

NOTE: the latest version is 3.2.0

Quantity.Interval

Note: most examples assume that you have imported this module as

import Quantity.Interval as Interval

The only exceptions are the examples for sin and cos; see the Trigonometry section for details.

type Interval number units

Represents a finite, closed interval with a minimum and maximum value, for example the interval from 0 degrees to 360 degrees. An Interval number units represents a range of Quantity number units values.

Constructors

singleton : Quantity number units -> Interval number units

Construct a zero-width interval containing a single value.

Interval.singleton (Length.meters 3)
--> Interval.fromEndpoints
-->     ( Length.meters 3
-->     , Length.meters 3
-->     )
( Quantity number units, Quantity number units )
-> Interval number units

Construct an interval from its endpoints (the minimum and maximum values of the interval). For example, the infamous old "allow 4 to 6 weeks for delivery":

deliveryTime =
    Interval.fromEndpoints
        ( Duration.weeks 4
        , Duration.weeks 6
        )

The two values should be given in order but will be swapped if necessary to ensure a valid interval is returned:

Interval.endpoints
    ( Interval.fromEndpoints
        ( Duration.minutes 3
        , Duration.minutes 2
        )
    )
--> ( Duration.minutes 2, Duration.minutes 3 )
Quantity number units
-> Quantity number units
-> Interval number units

Construct an interval with the two given endpoints (which can be provided in either order).

Interval.endpoints <|
    Interval.from
        (Angle.degrees 0)
        (Angle.degrees 180)
--> ( Angle.degrees 0, Angle.degrees 180 )


Interval.endpoints <|
    Interval.from
        (Angle.degrees 180)
        (Angle.degrees 0)
--> ( Angle.degrees 0, Angle.degrees 180 )
containingValues : List (Quantity number units) -> Maybe (Interval number units)

Construct an interval containing all values in the given list. If the list is empty, returns Nothing.

Interval.containingValues
    [ Length.meters 2
    , Length.meters 1
    , Length.meters 3
    ]
--> Just <|
-->     Interval.from
-->         (Length.meters 1)
-->         (Length.meters 3)


Interval.containingValues [ Length.meters -3 ]
--> Just (Interval.singleton (Length.meters -3))


Interval.containingValues []
--> Nothing
aggregate : List (Interval number units) -> Maybe (Interval number units)

Construct an interval containing all of the intervals in the given list. If the list is empty, returns Nothing.

Interval.aggregate
    [ Interval.singleton (Length.feet 2)
    , Interval.from
        (Length.feet 3)
        (Length.feet 4)
    ]
--> Just (Interval.from (Length.feet 2) (Length.feet 4))


Interval.aggregate []
--> Nothing
Interval number units
-> Interval number units
-> Interval number units

Construct an interval containing both of the given intervals.

firstInterval =
    Interval.from
        (Duration.hours 1)
        (Duration.hours 2)


secondInterval =
    Interval.from
        (Duration.hours 3)
        (Duration.hours 6)


Interval.hull firstInterval secondInterval
--> Interval.from
-->     (Duration.hours 1)
-->     (Duration.hours 6)
Interval number units
-> Interval number units
-> Maybe (Interval number units)

Attempt to construct an interval containing all the values common to both given intervals. If the intervals do not intersect, returns Nothing.

Interval.intersection
    (Interval.from (pixels 100) (pixels 300))
    (Interval.from (pixels 200) (pixels 500))
--> Just (Interval.from (pixels 200) (pixels 300))


Interval.intersection
    (Interval.from (pixels 100) (pixels 300))
    (Interval.from (pixels 400) (pixels 700))
--> Nothing

If the two intervals just touch, a singleton interval will be returned:

Interval.intersection
    (Interval.from (pixels 100) (pixels 300))
    (Interval.from (pixels 300) (pixels 500))
--> Just (Interval.singleton (pixels 300))

Properties

Interval number units
-> ( Quantity number units, Quantity number units )

Get the endpoints of an interval (its minimum and maximum values) as a tuple. The first value will always be less than or equal to the second.

( minValue, maxValue ) =
    Interval.endpoints someInterval

For any interval,

Interval.endpoints interval

is equivalent to (but more efficient than)

( Interval.minValue interval
, Interval.maxValue interval
)
minValue : Interval number units -> Quantity number units

Get the minimum value of an interval.

Interval.minValue <|
    Interval.from
        (Length.meters 1)
        (Length.meters 3)
--> Length.meters 1
maxValue : Interval number units -> Quantity number units

Get the maximum value of an interval.

Interval.maxValue <|
    Interval.from
        (Length.meters 1)
        (Length.meters 3)
--> Length.meters 3
midpoint : Interval Float units -> Quantity Float units

Get the midpoint of an interval.

Interval.midpoint <|
    Interval.from
        (Duration.seconds 1)
        (Duration.seconds 4)
--> Duration.seconds 2.5
width : Interval number units -> Quantity number units

Get the width of an interval. This will never be negative.

Interval.width <|
    Interval.from
        (Duration.seconds 1)
        (Duration.seconds 4)
--> Duration.seconds 3

Queries

contains : Quantity number units -> Interval number units -> Bool

Check if an interval contains a given value.

durationInterval =
    Interval.from
        (Duration.minutes 30)
        (Duration.minutes 90)


durationInterval
    |> Interval.contains
        (Duration.hours 1)
--> True




durationInterval
    |> Interval.contains
        (Duration.hours 2)
--> False

The minimum and maximum values of an interval are considered to be contained in the interval:

durationInterval
    |> Interval.contains
        (Duration.hours 0.5)
--> True
intersects : Interval number units -> Interval number units -> Bool

Check if two intervals touch or overlap (have any values in common).

distanceInterval =
    Interval.from
        (Length.kilometers 5)
        (Length.kilometers 10)


distanceInterval
    |> Interval.intersects
        (Interval.from
            (Length.kilometers 8)
            (Length.kilometers 12)
        )
--> True


distanceInterval
    |> Interval.intersects
        (Interval.from
            (Length.kilometers 12)
            (Length.kilometers 15)
        )
--> False

Intervals that just touch each other are considered to intersect (this is consistent with intersection which will return a zero-width interval for the intersection of two just-touching intervals):

distanceInterval
    |> Interval.intersects
        (Interval.from
            (Length.kilometers 10)
            (Length.kilometers 15)
        )
--> True
isContainedIn : Interval number units -> Interval number units -> Bool

Check if the second interval is fully contained in the first.

angleInterval =
    Interval.from
        (Angle.degrees -30)
        (Angle.degrees 30)


Interval.from (Angle.degrees -5) (Angle.degrees 15)
    |> Interval.isContainedIn angleInterval
--> True


Interval.from (Angle.degrees 15) (Angle.degrees 45)
    |> Interval.isContainedIn angleInterval
--> False

Be careful with the argument order! If not using the |> operator, the first example would be written as:

Interval.isContainedIn angleInterval
    (Interval.from
        (Angle.degrees -5)
        (Angle.degrees 15)
    )
--> True
isSingleton : Interval number units -> Bool

Check if the interval is a singleton (the minimum and maximum values are the same).

Interval.isSingleton
    (Interval.from (Length.meters 2) (Length.meters 2))
--> True


Interval.isSingleton
    (Interval.from (Length.meters 2) (Length.meters 3))
--> False

Interpolation

interpolate : Interval Float units -> Float -> Quantity Float units

Interpolate between an interval's endpoints; a value of 0.0 corresponds to the minimum value of the interval, a value of 0.5 corresponds to its midpoint and a value of 1.0 corresponds to its maximum value. Values less than 0.0 or greater than 1.0 can be used to extrapolate.

oneTurn =
    Interval.from (Angle.degrees 0) (Angle.degrees 360)


Interval.interpolate oneTurn 0
--> Angle.degrees 0


Interval.interpolate oneTurn 0.75
--> Angle.degrees 270


Interval.interpolate oneTurn -0.5
--> Angle.degrees -180

Note that the interpolation is in fact from the minimum value to the maximum, not "from the first Interval.from argument to the second":

Interval.interpolate
    (Interval.from
        (Angle.degrees 0)
        (Angle.degrees 180)
    )
    0.25
--> Angle.degrees 45


Interval.interpolate
    (Interval.from
        (Angle.degrees 180)
        (Angle.degrees 0)
    )
    0.25
--> Angle.degrees 45 -- not 135!

Trigonometry

Note that in the same way that Angle.sin and Angle.cos from the elm-units package take an Angle (a Quantity Float Radians) value and return a plain Float, sin and cos in this package take an angle Interval from this package (a Quantity.Interval.Interval Float Radians) and return a plain Interval Float from the elm-interval package (an Interval.Interval Float).

As a result, the examples for sin and cos below use fully qualified module names to avoid ambiguity, so Interval refers to the Interval module from elm-interval and not the Quantity.Interval module from this package.

sin : Interval Float Radians -> Interval Float

For a given range of angle values, find the corresponding range of sine values. Takes an Interval from this module and returns an Interval from the elm-interval package.

Quantity.Interval.sin <|
    Quantity.Interval.from
        (Angle.degrees 0)
        (Angle.degrees 45)
--> Interval.from 0 0.7071


Quantity.Interval.sin <|
    Quantity.Interval.from
        (Angle.degrees 0)
        (Angle.degrees 180)
--> Interval.from 0 1
cos : Interval Float Radians -> Interval Float

For a given range of angle values, find the corresponding range of sine values. Takes an Interval from this module and returns an Interval from the elm-interval package.

Quantity.Interval.cos <|
    Quantity.Interval.from
        (Angle.degrees 0)
        (Angle.degrees 45)
--> Interval.from 0.7071 1


Quantity.Interval.cos <|
    Quantity.Interval.from
        (Angle.degrees 0)
        (Angle.degrees 180)
--> Interval.from -1 1