# Elm-geometry 3: Now with units and coordinate systems

`elm-geometry` 3 is out! This release is the result of many months of work (and tons of useful feedback and contributions from the community) updating `elm-geometry` to keep track of both units and coordinate systems at compile time. This builds on `elm-units` to keep track of (for example) whether a given `Point2d` is in on-screen units (pixels) or real-world units (meters), or even some other kind of custom unit like tiles in a tile-based 2D game. Keeping track of coordinate systems is similar but unique to `elm-geometry`; this lets you keep track of things like whether a given point is defined in (for example) ālocalā or āglobalā coordinates, and is useful especially in more complex applications. Functions are also provided to convert between different units, and between different coordinate systems, in a rigorous and type-safe way.

These changes will break any code that uses `elm-geometry` 1.x, and may require a fair bit of work to update. For example, where before you might have written

``````import Point2d

point =
Point2d.fromCoordinates ( 200, 300 )
``````

``````import Point2d
import Pixels exposing (pixels)

point =
Point2d.xy (pixels 200) (pixels 300)
``````

or perhaps just

``````import Point2d

point =
Point2d.pixels 200 300
``````

In addition, most functions that returned a `Float` now generally return a `Quantity Float units`, which means you will need to either convert those values to a `Float` using functions like `Length.inMeters`, or work directly with the values using functions from the `Quantity` module. For example, where before you might have written

``````if Point2d.distanceFrom p1 p2 > 10 then ...
``````

you might now write something like

``````if Point2d.distanceFrom p1 p2 |> Quantity.greaterThan (Length.meters 10) then ...
``````

As a result, while I certainly recommend using version 3 for all new projects, it may not be worth the effort to update existing apps - `elm-geometry` 1.x still works fine and has no known bugs, and Iām happy to support it for the foreseeable future. That said, if you have a published package that currently uses `elm-geometry` 1.x (or the temporary, never-officially-published 2.x) then it would be great if you could update it to use 3.x. Iām hoping that `elm-geometry` 3.x can be an āLTSā release that can reliably be used as a base for other packages, so it would be great to get all published packages using it to avoid dependency conflicts/ecosystem fragmentation. Iām happy to help with this process by answering questions or submitting PRs; please reach out to me (@ianmackenzie) on Slack!

Iām currently working on some high-level documentation for `elm-geometry` (based on the excellent `elm-pages`) that will complement the API reference documentation and discuss units, coordinate systems and other topics like 2D and 3D transformations in more detail; in the meantime, check out the README for a brief discussion of units and coordinate systems, and the release notes to get a sense of whatās changed since 1.x (youāll have to look at the release notes for both 2.0.0 and 3.0.0 to get the full picture).

Happy to answer any questions either here or in the #geometry channel on the Elm Slack!

25 Likes

This is great, and itās obvious that this is an absolute labour of love. I was especially delighted to play around with the Voronoi tesselation.

This made me think however that there might be space for an `svg-simple` or `geometry-simple` package building on top of this and `elm-geometry-svg`. The way I imagine it:

• as a quick-start, give a simple, visible canvas backdrop (I tried to do something here)
• let you import only one or two modules, so that you have lines, polygons, splines, etc all in one place, along with their drawing functions
• it wouldnāt directly expose the units
• It would let you draw lines, polygons, etc just based on lists of (x, y) coordinates, wrapping `Point2d` without directly expsing it.

The idea is basically to make it really fast and easy to go from nothing to drawing stuff and manipulating shapes. That way it would be a progressive gateway drug to this more full-fledged package.

What do you think, would this be fun to use, or is the above all covered by the raw svg package?

Hey @2mol, thanks! Iām actually working on a high level 2D drawing package almost like what you describe, although it does expose the units/`Point2d` type etc. Take a look at the examples to get a sense of what using the package would look like, although keep in mind that things may change before release.

I should also mention that evancz/elm-playground seems like a pretty good match for what you described, although thatās entirely independent of `elm-geometry`.

Oh yeah, thanks for reminding me of `elm-playground`! Iāll look at it in some more detail, but at first glance I think my ideal library would indeed be based on yours. Especially for transformations, BĆ©zier curves, and leveraging some of your more advanced functionality like Voronoi.

I managed to create a working puzzle by the way: https://ellie-app.com/7qc2yL5hPpca1

The examples you linked are also useful to learn how to use your library in a more elegant way, thanks!

Has adding units had much of an impact on performance? Not that I am up against any perf limits with SVG that I am using - just curious.

@2mol that puzzle looks awesome, great work!

@rupert I confess I havenāt done detailed performance comparison yet. Iām confident that it should be possible to get equivalent performance even with the addition of units, since in an `--optimize` build the `Quantity Float units` values will just be `Float` values at runtime (a compiler optimization introduced in Elm 0.19). But to get that maximum performance, itās necessary to manually unwrap and re-wrap `Quantity` values and do all computations on the raw `Float` values instead of using the corresponding type-safe `Quantity` functions; for example in

``````sum =
a |> Quantity.plus b
``````

the use of the `Quantity.plus` function will likely introduce some function call overhead compared to

``````sum =
let
(Quantity aValue) =
a

(Quantity bValue) =
b
in
Quantity (aValue + bValue)
``````

In most low-level performance-critical code in `elm-geometry` code Iāve already done the above kind of transformation, but I havenāt done it everywhere.

On a related note, though, thereās one big performance improvement in `elm-geometry` 3 Iām excited about: effectively zero-overhead interop with code that uses plain records like `{ x : Float, y : Float }` to represent points or vectors. Even though the public `elm-geometry` API uses units everywhere, the internal representation of a `Point2d` is actually just a `{ x : Float, y : Float }`. This means that functions like `Point2d.fromPixels` or `Point3d.toMeters` are basically no-ops - they donāt incur any actual conversion or memory allocation overhead (at least in an `--optimize` build), they just ācastā the given value to a different type. This should make it very efficient to have code that uses a mix of `elm-geometry` and plain record types (as long as youāre working in ābaseā units like meters or pixels, but you should be doing that anyways =)).

2 Likes

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.