Elm Geometry Versions Save

2D/3D geometry package for Elm

4.0.0

7 months ago

Despite the major version bump, this is really just a clean-up release with three main changes.

Changing 3D vector/direction rotation functions to take a Direction3d instead of an Axis3d

Previously, Vector3d.rotateAround and Direction3d.rotateAround took an Axis3d as an argument even though the origin point of the axis wasn't used, only the axis direction. This was to be consistent with all other 3D rotateAround functions, but can be confusing and in some cases could force you to create an axis with a dummy origin point, which seems messy. These two functions now accept a Direction3d instead, so you may need to change some code from (e.g.)

Direction3d.rotateAround someAxis someDirection

to

Direction3d.rotateAround (Axis3d.direction someAxis) someDirection

Thanks @MartinSStewart for bringing this up!

Renaming ArcLengthParameterization to ArcLength

I originally used the longer name for this module since it's the only module in elm-geometry that doesn't have a 2d or 3d suffix, and I wanted to add something to make the name less likely to conflict with modules in other packages. However, several years later the Elm package repository still doesn't have any other packages that mention arc length, so it looks like the simpler name is probably OK.

No functionality has been changed, but all the names have:

3.x 4.0
ArcLengthParameterization.ArcLengthParameterization ArcLength.Parameterization
ArcLengthParameterization.build ArcLength.parameterization
ArcLengthParameterization.totalArcLength ArcLength.total
ArcLengthParameterization.arcLengthToParameterValue ArcLength.toParameterValue
ArcLengthParameterization.parameterValueToArcLength ArcLength.fromParameterValue

I personally now like using the Parameterization type qualified; instead of

import ArcLengthParameterization exposing (ArcLengthParameterization)
import Length exposing (Meters)

type alias Model =
    { parameterization : ArcLengthParameterization Meters
    , -- other stuff
    }

I now tend to write

import ArcLength
import Length exposing (Meters)

type alias Model =
    { parameterization : ArcLength.Parameterization Meters
    , -- other stuff
    }

Relaxing vector construction/transformation type signatures

This one is subtle and shouldn't actually be a breaking change for any code, since the only change is making some type signatures more permissive (all existing code should still type-check just fine, and the behaviour is unchanged). The short version is that a few vector-related functions were requiring units types to match when they didn't actually need to, and those type signatures have now been relaxed to be more permissive (and more correct!).

For example, in elm-geometry 3.x, the Vector3d.mirrorAcross function had the following signature:

Plane3d units coordinates -> Vector3d units coordinates -> Vector3d units coordinates

The units type parameter here will most commonly be Meters, meaning that:

  • The plane's origin point has coordinates which are Length (a.k.a. Quantity Float Meters) values; that is, the coordinates are measured in meters, centimeters, feet, inches etc.
  • The vector's components are also Length values; that is, the vector is a displacement (a vector between two points).

However, it's not actually necessary for those two units types to match! Since mirroring a vector across a plane doesn't involve the plane's origin point (just the plane's normal direction, which is unitless) it's not actually necessary that the units of the vector match the units of the plane's origin point. For example, it's totally valid (and sometimes useful) to mirror a velocity vector (with units of MetersPerSecond) across a plane with units of Meters. As a result, Vector3d.mirrorAcross now has the signature

Plane3d planeUnits coordinates -> Vector3d units coordinates -> Vector3d units coordinates

That is, the plane and vector are still enforced to be defined in the same coordinate system, and the units of the mirrored vector are enforced to be the same as the original vector, but the units of the plane and vector are allowed be different.

Of course, the units don't have to be different, so any existing code that (for example) mirrors a Vector3d Meters WorldCoordinates across a Plane3d Meters WorldCoordinates will still compile and run exactly the same as before. The Elm compiler considers this change a breaking one, but all it actually does is allow certain kinds of code that weren't allowed previously.

It's not just transformation functions like mirrorAcross that have been updated - several vector construction functions have had similar changes. For example, it is now possible to do things like construct a velocity vector with units of MetersPerSecond by providing its components within a sketch plane with units of Meters:

-- elm-geometry 3.x
Vector3d.xyOn :
    SketchPlane3d units coordinates3d { defines : coordinates2d }
    -> Quantity Float units
    -> Quantity Float units
    -> Vector3d units coordinates3d

-- elm-geometry 4.0
Vector3d.xyOn :
    SketchPlane3d sketchPlaneUnits coordinates3d { defines : coordinates2d }
    -> Quantity Float units
    -> Quantity Float units
    -> Vector3d units coordinates3d

(Note how the vector units are now allowed to be different from the sketch plane units.)

Full list of functions with updated signatures:

Vector2d

  • xyIn
  • rThetaIn
  • relativeTo
  • placeIn
  • projectOnto

(Note that Vector2d.mirrorAcross was the one function that actually had the correct type signature already, so didn't need to be updated.)

Vector3d

  • xyzIn
  • on
  • xyOn
  • rThetaOn
  • relativeTo
  • placeIn
  • rotateAround
  • mirrorAcross
  • projectOnto
  • projectInto

3.11.0

7 months ago

This release adds Spline2d and Spline3d modules, which are generalizations of the existing QuadraticSpline2d/CubicSpline2d and QuadraticSpline3d/CubicSpline3d modules.

In general you should prefer to use the existing modules (they're much more efficient), but the new ones are useful if you need support for fancy higher-degree splines (quartic, quintic etc.) or if you're doing something like making a graphics editor where users can choose how many points to use to define a spline curve (two for a straight line, three for a quadratic spline, four for a cubic spline, etc.).

3.10.0

11 months ago

This (long-delayed!) release adds several small new functions:

  • Polygon2d.regular for constructing regular polygons (triangles, squares, pentagons, hexagons etc.) (thanks @gampleman in #148)
  • Axis2d.intersectionPoint for finding the intersection point between two 2D axes
  • Axis3d.intersectionWithTriangle, and Axis3d.intersectionWithRectangle (thanks @w0rm in #146)
  • BoundingBox2d.interpolate and BoundingBox3d.interpolate for interpolating within bounding boxes
  • LineSegment2d.axis and LineSegment3d.axis for getting the axis that a given line segment lies on
  • Several functions for multiplying and dividing Vector2d and Vector3d by Float or Quantity Float Unitless values (functionally the same thing, but different in the eyes of the type system): multiplyBy, divideBy, timesUnitless and overUnitless for both Vector2d and Vector3d
  • RationalQuadraticSpline3d.approximate and RationalCubicSpline3d.approximate for generating polyline approximations to rational splines in 3D (useful for 3D rendering)

Adding the rational spline approximate functions (the last bullet point above) also required adding a bunch of lower-level functionality that is less likely to be useful outside of elm-geometry internals:

  • New VectorBoundingBox2d and VectorBoundingBox3d types/modules, equivalent to BoundingBox2d and BoundingBox3d but for bounds on vectors instead of points
  • Several functions for computing derivatives and bounding boxes on derivatives of different spline types (also added to elliptical arcs, for completeness)

3.9.1

2 years ago

This release fixes a typo in the implementation of Vector3d.perpendicularTo which would cause it would give an incorrect (non-perpendicular) result in some cases. Please update!

3.9.0

3 years ago

NURBS curves

This release extends the work on spline functionality from the last release and adds support for rational quadratic and cubic splines in the RationalQuadraticSpline2d, RationalQuadraticSpline3d, RationalCubicSpline2d and RationalCubicSpline3d modules. This means it is now possible to create quadratic or cubic NURBS curves in 2D or 3D using elm-geometry! For example, to create a NURBS curve representing a perfect circle (consisting of four rational quadratic spline segments), you could write

splineSegments =
    RationalQuadraticSpline2d.bSplineSegments
        [ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 ]
        [ ( Point2d.pixels 300 0, 1 )
        , ( Point2d.pixels 300 300, 1 / sqrt 2 )
        , ( Point2d.pixels 0 300, 1 )
        , ( Point2d.pixels -300 300, 1 / sqrt 2 )
        , ( Point2d.pixels -300 0, 1 )
        , ( Point2d.pixels -300 -300, 1 / sqrt 2 )
        , ( Point2d.pixels 0 -300, 1 )
        , ( Point2d.pixels 300 -300, 1 / sqrt 2 )
        , ( Point2d.pixels 300 0, 1 )
        ]

See Wikipedia for some explanation, but note that:

  • Only the ratios between knot values matter, so the 0..4 knot sequence here is equivalent to the 0..2π knot sequence on Wikipedia.
  • elm-geometry uses a different knot convention that avoids the 'dummy' first and last knots, so note that the first and last knots are only repeated twice here instead of three times.

Ellipsoids

This release also adds an Ellipsoid3d type and module for representing ellipsoids. Thanks to @g-belmonte for contributing this in #135! (Eventually elm-3d-scene will get support for rendering ellipsoids.)

3.8.0

3 years ago

This is a small release that primarily brings some basic B-spline functionality to the QuadraticSpline2d, QuadraticSpline3d, CubicSpline2d and CubicSpline3d modules, for example:

-- Construct a B-spline and get its individual cubic spline segments
CubicSpline2d.bSplineSegments : 
    List Float -- knot values
    -> List (Point2d units coordinates) -- control points
    -> List (CubicSpline2d units coordinates) -- resulting curve segments

-- Find the knot intervals corresponding to those curve segments
CubicSpline2d.bSplineIntervals : List Float -> List (Interval Float)

In addition, Circle3d now has a toArc function for converting a 360 degree arc (with unspecified start/end point).

3.7.0

3 years ago

This release is the result of a steady accumulation of contributions over the last several months - not a very cohesive story to tell here, but lots of useful new features!

New modules

This release adds new Ellipse3d and EllipticalArc3d modules, which are fairly straightforward 3D versions of Ellipse2d and EllipticalArc2d having many of the same operations.

Random value generation

A few modules now have basic functions for generating random values in 2D and 3D. You can generate random points within bounding boxes and rectangles:

BoundingBox2d.randomPoint : BoundingBox2d units coordinates -> Generator (Point2d units coordinates)
BoundingBox3d.randomPoint : BoundingBox3d units coordinates -> Generator (Point3d units coordinates)
Rectangle2d.randomPoint : Rectangle2d units coordinates -> Generator (Point2d units coordinates)
Rectangle3d.randomPoint : Rectangle3d units coordinates -> Generator (Point3d units coordinates)

You can also generate random 2D and 3D directions:

Direction2d.random : Generator (Direction2d coordinates)
Direction3d.random : Generator (Direction3d coordinates)

Future releases will likely add more random generators such as 'point within circle' , 'point within triangle' etc.

Curve approximation

This release brings some more flexibility in how to approximate curves (arcs, splines) etc. by things like polylines. The Arc2d, Arc3d, QuadraticSpline2d, QuadraticSpline3d, CubicSpline2d, CubicSpline3d and EllipticalArc2d modules now all have new approximate, segments and numApproximationSegments functions, for example:

Arc2d.approximate : Quantity Float units -> Arc2d units coordinates -> Polyline2d units coordinates
Arc2d.segments : Int -> Arc2d units coordinates -> Polyline2d units coordinates
Arc2d.numApproximationSegments : Quantity Float units -> Arc2d units coordinates -> Int
  • The approximate function replaces toPolyline, which is now deprecated since there are now multiple ways to convert a curve to a polyline. Like toPolyline, approximate takes a tolerance and returns a polyline that approximates the arc to within that tolerance.
  • The segments function is a simpler version of approximate that takes as an argument the number of segments to use.
  • Finally, numApproximationSegments is a lower-level function that tells you how many segments would be needed for a polyline to approximate the given arc to within the given accuracy. You can then use this to determine how many times to call functions like pointOn or sample.

Polygon triangulation customization

The existing Polygon2d.triangulate turns a Polygon2d into a triangular mesh by adding edges between existing polygon vertices. Sometimes, however, you want some extra control over the resulting triangulation - for example, you might want to ensure that all triangles are smaller than some given size. There is now a Polygon2d.triangulateWith function and some functions for setting up some 'rules' for the triangulation:

Polygon2d.triangulateWith :
    TriangulationRule units coordinates
    -> Polygon2d units coordinates
    -> TriangularMesh (Point2d units coordinates)

Polygon2d.maxEdgeLength : Quantity Float units -> TriangulationRule units coordinates
Polygon2d.maxTriangleDimensions : Quantity Float units -> Quantity Float units -> TriangulationRule units coordinates

Vector scaling

Vectors now have a new scaleTo function thanks to @g-belmonte in #137:

Vector2d.scaleTo : Quantity Float units2 -> Vector2d units1 coordinates -> Vector2d units2 coordinates
Vector3d.scaleTo : Quantity Float units2 -> Vector3d units1 coordinates -> Vector3d units2 coordinates

These functions will return a vector in the same direction as the original, but scaled to the given length (or left as zero if they are zero to begin with). Note that this is capable of changing the units of the vector if you want!

Bounding box improvements

The Arc2d, Arc3d, Ellipse2d and EllipticalArc2d modules now all have boundingBox functions. The BoundingBox2d and BoundingBox3d modules themselves now also have new functionality to convert between bounding boxes and X/Y/Z intervals, for example

BoundingBox2d.xy : Interval Float units -> Interval Float units -> BoundingBox2d units coordinates
BoundingBox2d.fromIntervals : ( Interval Float units, Interval Float units ) -> BoundingBox2d units coordinates
BoundingBox2d.intervals : BoundingBox2d units coordinates -> ( Interval Float units, Interval Float units )
BoundingBox2d.xInterval : BoundingBox2d units coordinates -> Interval Float units
BoundingBox2d.yInterval : BoundingBox2d units coordinates -> Interval Float units

and similar for BoundingBox3d.

Physics-related vector constructors

The Vector2d and Vector3d modules got several new functions for constructing speed, acceleration and force vectors from their X/Y/Z components. For example, the Vector2d module now has several functions such as:

Vector2d.metersPerSecond : Float -> Float -> Vector2d MetersPerSecond coordinates
Vector2d.feetPerSecondSquared : Float -> Float -> Vector2d MetersPerSecondSquared coordinates
Vector2d.kilonewtons : Float -> Float -> Vector2d Newtons coordinates

Thanks to @g-belmonte in #139 for adding these!

Miscellaneous

Other new functions in this release:

Arc3d.projectOnto : Plane3d units coordinates -> Arc3d units coordinates -> EllipticalArc3d units coordinates

-- Reverse the orientation (axial direction) of a circle
Circle3d.flip : Circle3d units coordinates -> Circle3d units coordinates

-- Alias for Plane3d.reverseNormal, for consistency with Circle3d.flip
Plane3d.flip : Plane3d units coordinates -> Plane3d units coordinates

-- Find the minimum/maximum distances of ellipses and elliptical arcs along arbitrary axes
Ellipse2d.signedDistanceAlong : Axis2d units coordinates -> Ellipse2d units coordinates -> Interval Float units
EllipticalArc2d.signedDistanceAlong : Axis2d units coordinates -> EllipticalArc2d units coordinates -> Interval Float units

3.6.0

3 years ago

This release brings a few useful new functions and some performance improvements. First, there's Axis3d.intersectionWithSphere, contributed by @SuzzzWood in #128:

Axis3d.intersectionWithSphere : 
    Sphere3d units coordinates
    -> Axis3d units coordinates 
    -> Maybe ( Point3d units coordinates, Point3d units coordinates )

Second, a handful of functions for measuring intervals of distances of line segments relative to axes and planes in 2D and 3D, contributed by @w0rm in #123:

LineSegment2d.signedDistanceAlong :
    Axis2d units coordinates 
    -> LineSegment2d units coordinates 
    -> Interval Float units

LineSegment2d.signedDistanceFrom :
    Axis2d units coordinates 
    -> LineSegment2d units coordinates 
    -> Interval Float units

LineSegment3d.signedDistanceAlong :
    Axis3d units coordinates 
    -> LineSegment3d units coordinates 
    -> Interval Float units

LineSegment3d.signedDistanceFrom :
    Plane3d units coordinates 
    -> LineSegment3d units coordinates 
    -> Interval Float units

Finally, the performance of arc length parameterization has been improved (particularly for 2D/3D cubic and quadratic splines). Please open an issue or reach out to @ianmackenzie on the Elm Slack if you are encountering any other performance issues with elm-geometry!

3.5.0

4 years ago

This release brings a new Cone3d module for representing/manipulating 3D cones, contributed by @w0rm in #131. (See also Andrey's corresponding PR to add support for rendering cones in elm-3d-scene!)

3.4.0

4 years ago

This is a fairly small release that brings a few new bounding box related functions, all suggested by @MartinSStewart in #129 and #130:

BoundingBox2d.withDimensions : 
    ( Quantity Float units, Quantity Float units )
    -> Point2d units coordinates 
    -> BoundingBox2d units coordinates

BoundingBox3d.withDimensions : 
    ( Quantity Float units, Quantity Float units, Quantity Float units )
    -> Point3d units coordinates 
    -> BoundingBox3d units coordinates

Rectangle2d.fromBoundingBox :
    BoundingBox2d units coordinates
    -> Rectangle2d units coordinates

Block3d.fromBoundingBox :
    BoundingBox3d units coordinates
    -> Block3d units coordinates