ianmackenzie/elm-1d-parameter

Generate evenly spaced interpolated values


License
MPL-2.0
Install
elm-package install ianmackenzie/elm-1d-parameter 1.0.1

Documentation

elm-1d-parameter

elm-1d-parameter is an Elm package to help with generating evenly spaced values such as (but not limited to) Floats, Colors, Quantitys, or Point2ds.

The core idea is that there are many functions that let you interpolate between two values based on a parameter value that ranges from 0 to 1; 0 gives you the first value, 1 gives you the second value, 0.5 gives you a value half way in between, etc. Some examples from elm-float-extra, elm-color-extra, elm-units and elm-geometry that work this way:

Float.Extra.interpolateFrom :
    Float -- first value
    -> Float -- second value
    -> Float -- interpolation parameter
    -> Float

Color.Interpolate.interpolate :
    Space -- what color space to interpolate in (e.g. HSL)
    -> Color -- first color
    -> Color -- second color
    -> Float -- interpolation parameter
    -> Color

Quantity.interpolateFrom :
    Quantity Float units
    -> Quantity Float units
    -> Float
    -> Quantity Float units

Point2d.interpolateFrom :
    Point2d
    -> Point2d
    -> Float
    -> Point2d

Note that by partially applying the above functions, we can turn them all into the form Float -> a:

-- Float -> Float
Float.Extra.interpolateFrom 5 10

-- Float -> Color
Color.Interpolate.interpolate
    Color.Interpolate.HSL
    Color.red
    Color.blue

-- Float -> Length
Quantity.interpolateFrom (meters 10) (meters 20)

-- Float -> Point2d
Point2d.interpolateFrom startPoint endPoint

The functions in this package all take a function of the form Float -> a, evaluate it at a bunch of evenly-spaced parameter values between 0 and 1, and return the results as a List a or Array a:

Parameter1d.steps 5 (Float.Extra.interpolateFrom 2 3)
--> [ 2, 2.2, 2.4, 2.6, 2.8, 3 ]

The results do not themselves have to be Floats:

Parameter1d.steps 10 <|
    Color.Interpolate.interpolate
        Color.Interpolate.HSL
        Color.red
        Color.blue

HSL-interpolated colors

Parameter1d.steps 10 <|
    Color.Interpolate.interpolate
        Color.Interpolate.RGB
        Color.red
        Color.blue

RGB-interpolated colors

Parameter1d.steps 20 <|
    Point2d.interpolateFrom
        (Point2d.fromCoordinates ( 20, 20 ))
        (Point2d.fromCoordinates ( 280, 130 ))

Interpolated points

Various different functions are provided that let you control what parameter values get used (and therefore what values get returned):

import Float.Extra as Float

Parameter1d.steps 4 (Float.interpolateFrom 2 3)
--> [ 2, 2.25, 2.5, 2.75, 3 ]

Parameter1d.leading 4 (Float.interpolateFrom 2 3)
--> [ 2, 2.25, 2.5, 2.75 ]

Parameter1d.trailing 4 (Float.interpolateFrom 2 3)
--> [ 2.25, 2.5, 2.75, 3 ]

Parameter1d.inBetween 4 (Float.interpolateFrom 2 3)
--> [ 2.25, 2.5, 2.75 ]

Parameter1d.midpoints 4 (Float.interpolateFrom 2 3)
--> [ 2.125, 2.375, 2.625, 2.875 ]

Note that if you just want the actual parameter values, you can pass the identity function as the last argument:

Parameter1d.steps 4 identity
--> [ 0, 0.25, 0.5, 0.75, 1 ]

Why?

Instead of using this package and writing

Parameter1d.steps 1000 (Float.interpolateFrom 100 200)

you could use List.range and List.map to get the same result:

List.range 0 1000
    |> List.map
        (\i ->
            let
                parameterValue =
                    toFloat i / 1000
            in
            Float.interpolateFrom 100 200 parameterValue
        )

Alternatively, you could use List.Extra.initialize:

List.Extra.initialize 1001
    (\i ->
        let
            parameterValue =
                toFloat i / 1000
        in
        Float.interpolateFrom 100 200 parameterValue
    )

On my machine, using the built-in List functions is slowest on both Chrome and Firefox; List.Extra.initialize is fastest in Firefox but Parameter1d.steps is far, far faster in Chrome. Runs per second of the above code in both browsers (more is better):

Method                | Chrome        | Firefox
----------------------|---------------|--------------
List.range/List.map   | 10,022        | 10,255
List.Extra.initialize | 16,976 (1.7x) | 26,689 (2.6x)
Parameter1d.steps     | 87,213 (8.7x) | 17,628 (1.7x)

The Parameter1d.Array functions are about the same in speed to calling Array.initialize yourself (perhaps even slightly slower), but are still a bit more convenient to use, for example

Parameter1d.Array.steps 4 (Float.interpolateFrom 100 200)

vs.

Array.initialize 5
    (\i -> Float.interpolateFrom 100 200 (toFloat i / 4))