-
Notifications
You must be signed in to change notification settings - Fork 26
Philosophy
elm-geometry
is functionally similar to other vector/geometry packages, but
works differently in a few subtle but meaningful ways. In general,
elm-geometry
has a more geometric than mathematical focus. For example,
distinct types are used for points, vectors and directions which many other
packages treat as a single generic vector type.
elm-geometry
uses the concept of a 'direction' where other packages typically
use vectors with unit length. Having separate types helps to keep track of
whether a vector has already been normalized - no more having to guess whether a
function that accepts a vector argument actually needs a unit vector, and if so
whether you're expected to normalize the vector yourself or whether the function
will do that internally.
You can get a direction from a vector with the Vector2d.direction
and
Vector3d.direction
functions, but they actually return Maybe
values since
the zero vector has no direction:
Vector2d.direction (Vector2d.fromComponents ( 3, 0 )) ==
Just Direction2d.x
Vector2d.direction (Vector2d.fromComponents ( -2, 2 )) ==
Just (Direction2d.fromAngle (degrees 135))
Vector2d.direction Vector2d.zero ==
Nothing
This takes advantage of Elm's type system to ensure that all code considers the degenerate zero-vector case. For example, given an eye point and a point to look at, the corresponding view direction could be determined with
Vector3d.direction (Vector3d.from eyePoint lookAtPoint)
This would return a Maybe Direction3d
, with Nothing
corresponding to the
case where the eye point and point to look at are coincident (in which case the
view direction is not well-defined and some special-case logic is needed).
Explicitly working with individual X/Y/Z point coordinates and vector components
is easy to do in elm-geometry
when necessary:
forwardSpeed =
Vector3d.xComponent velocityVector
height =
Point3d.zCoordinate position
point2d =
let
( x, y, z ) =
Point3d.coordinates point3d
in
Point2d.fromCoordinates ( x, z )
In many cases, however, it is equally easy and often advantageous to consider points and vectors as abstract geometric quantities and treat their internal representations by coordinates/components as an implementation detail. For example, the above might be replaced by
forwardSpeed =
Vector3d.componentIn forwardDirection velocityVector
height =
Point3d.signedDistanceFrom groundPlane position
point2d =
Point3d.projectInto sketchPlane point3d
where (perhaps in a Constants.elm
file or similar) you would define
forwardDirection =
Direction3d.x
groundPlane =
Plane3d.xy
sketchPlane =
SketchPlane3d.xz
This approach is less coupled to the particular coordinate system being used and adapts more easily to changes - perhaps the forward direction becomes a configuration setting, or the ground plane becomes dynamic and shifts or tilts over time.