elm-geometry

2D/3D geometry package for Elm

MPL-2.0 License

Stars
183
Committers
15

Bot releases are visible (Hide)

elm-geometry - 4.0.0 Latest Release

Published by ianmackenzie about 1 year 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
elm-geometry - 3.11.0

Published by ianmackenzie about 1 year 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.).

elm-geometry - 3.10.0

Published by ianmackenzie over 1 year 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)
elm-geometry - 3.9.1

Published by ianmackenzie about 3 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!

elm-geometry - 3.9.0

Published by ianmackenzie almost 4 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.)

elm-geometry - 3.8.0

Published by ianmackenzie almost 4 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).

elm-geometry - 3.7.0

Published by ianmackenzie almost 4 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
elm-geometry - 3.6.0

Published by ianmackenzie over 4 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!

elm-geometry - 3.5.0

Published by ianmackenzie over 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!)

elm-geometry - 3.4.0

Published by ianmackenzie over 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
elm-geometry - 3.3.0

Published by ianmackenzie over 4 years ago

This release of elm-geometry adds a new Rectangle3d module and type. That's it, that's the release =)

elm-geometry - 3.2.0

Published by ianmackenzie over 4 years ago

elm-geometry 3.2 is out! This release brings a handful of small but useful new features requested (and in many cases contributed) by the community:

  • Arc2d.midpoint, Arc3d.midpoint and EllipticalArc2d.midpoint for getting midpoints of circular and elliptical arcs (see #115, thanks @w0rm!)
  • Polygon2d.centroid for getting the centroid (center of mass) of a polygon (see #117, thanks @w0rm!)
  • Axis2d.throughPoints and Axis3d.throughPoints for constructing axes that pass through two given points (thanks @MartinSStewart for the request)
  • Several functions for getting point coordinates and vector/direction components as tuples, bringing back some feature parity with previous versions of elm-geometry (thanks @MartinSStewart for the request):
    • Point2d.coordinates, Point2d.coordinatesIn
    • Point3d.coordinates, Point3d.coordinatesIn
    • Vector2d.components, Vector3d.components
    • Direction2d.components, Direction3d.components
  • Several functions for doing more sophisticated and/or convenient type-safe arithmetic on vectors:
    • Vector2d.half, Vector2d.twice as convenient shortand for Vector2d.scaleBy
    • Vector2d.sum to get the sum of a list of vectors
    • Vector2d.product and Vector2d.times to multiply a vector by a scalar Quantity (analogous to Quantity.times)
    • Vector2d.over and Vector2d.over_ to divide a vector by a scalar Quantity (analogous to Quantity.over and Quantity.over_)
    • plus all the same functions for Vector3d
elm-geometry - 3.1.0

Published by ianmackenzie almost 5 years ago

This release adds a new Cylinder3d type and corresponding module, for representing/manipulating/querying 3D cylinders.

elm-geometry - 3.0.0

Published by ianmackenzie almost 5 years ago

elm-geometry 3.0 is out! This release brings some small but breaking changes from the interim 2.0 release, and should hopefully now be stable for the foreseeable future.

Block3d

elm-geometry now has a Block3d type which is basically a 3D version of Rectangle2d, useful for defining rectangular blocks in 3D. Unlike a BoundingBox3d, a Block3d does not have to be axis-aligned - it can be arbitrarily rotated, mirrored and otherwise transformed).

Bounding box constructor changes

A few bounding box related functions have been moved around/renamed for clarity/discoverability, with the end result that they're actually a bit closer to where they used to be in elm-geometry 1.x. Specifically:

  • Most of the existing bounding box hull functions have been renamed to aggregate; for example BoundingBox2d.hull3 is now BoundingBox2d.aggregate3.
  • The existing BoundingBox2d.hull2 and BoundingBox3d.hull2 functions have been renamed to union (not strictly the correct term mathematically speaking, but convenient).
  • Most of the existing point hull functions have been moved to the corresponding bounding box modules; for example Point3d.hull is now BoundingBox3d.hull (with the same signature).
  • The existing Point2d.hull2 and BoundingBox3d.hull2 have been moved/renamed to BoundingBox2d.from and BoundingBox3d.from (matching how things worked in 1.x).

Basically, all functions that construct bounding boxes are now in bounding box modules. In addition, hullOfN and aggregateOfN functions have been added that basically act as combinations of hullOf/hullN and aggregateOf/aggregateN respectively.

See #107 for relevant discussion - thanks @MartinSStewart!

Miscellaneous

  • The Arc.SweptAngle has been renamed to simply SweptAngle for simplicity (didn't seem like that was likely to cause too many module name conflicts).
  • There is now an Axis3d.intersectionWithPlane function to find the intersection of an axis and a plane in 3D.
  • The Circle2d and Sphere3d modules now have some convenient atPoint and atOrigin constructors.
  • The LineSegment2d and LineSegment3d modules now have fromPointAndVector constructors as suggested in #110.
  • The type signatures of Frame2d.atOrigin and Frame3d.atOrigin have been loosened slightly to match Frame2d.atPoint Point2d.origin and Frame3d.atPoint Point3d.atOrigin; this was a case where the initial design was a bit too strict about coordinate system tracking and prevented some perfectly valid code from compiling. Thanks @unsoundscapes for some useful feedback here!
  • Rectangle2d.withAxes has been renamed back to Rectangle2d.centeredOn; I've gone back and forth on this one but I think centeredOn is more descriptive and readable.

Full diff

I think all the changes in 3.0 have been discussed above, but I may have missed something! Here's what elm diff reported for elm-geometry 3.0 relative to 2.0:

---- ADDED MODULES - MINOR ----

    Block3d
    SweptAngle


---- REMOVED MODULES - MAJOR ----

    Arc.SweptAngle


---- Axis3d - MINOR ----

    Added:
        intersectionWithPlane :
            Geometry.Types.Plane3d units coordinates
            -> Axis3d.Axis3d units coordinates
            -> Maybe.Maybe (Point3d.Point3d units coordinates)


---- BoundingBox2d - MAJOR ----

    Added:
        aggregate :
            BoundingBox2d.BoundingBox2d units coordinates
            -> List.List (BoundingBox2d.BoundingBox2d units coordinates)
            -> BoundingBox2d.BoundingBox2d units coordinates
        aggregate3 :
            BoundingBox2d.BoundingBox2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates
        aggregateN :
            List.List (BoundingBox2d.BoundingBox2d units coordinates)
            -> Maybe.Maybe (BoundingBox2d.BoundingBox2d units coordinates)
        aggregateOf :
            (a -> BoundingBox2d.BoundingBox2d units coordinates)
            -> a
            -> List.List a
            -> BoundingBox2d.BoundingBox2d units coordinates
        aggregateOfN :
            (a -> BoundingBox2d.BoundingBox2d units coordinates)
            -> List.List a
            -> Maybe.Maybe (BoundingBox2d.BoundingBox2d units coordinates)
        from :
            Point2d.Point2d units coordinates
            -> Point2d.Point2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates
        hullOfN :
            (a -> Point2d.Point2d units coordinates)
            -> List.List a
            -> Maybe.Maybe (BoundingBox2d.BoundingBox2d units coordinates)
        union :
            BoundingBox2d.BoundingBox2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates

    Removed:
        hull2 :
            BoundingBox2d units coordinates
            -> BoundingBox2d units coordinates
            -> BoundingBox2d units coordinates

    Changed:
      - hull :
            BoundingBox2d units coordinates
            -> List (BoundingBox2d units coordinates)
            -> BoundingBox2d units coordinates
      + hull :
            Point2d.Point2d units coordinates
            -> List.List (Point2d.Point2d units coordinates)
            -> BoundingBox2d.BoundingBox2d units coordinates

      - hull3 :
            BoundingBox2d units coordinates
            -> BoundingBox2d units coordinates
            -> BoundingBox2d units coordinates
            -> BoundingBox2d units coordinates
      + hull3 :
            Point2d.Point2d units coordinates
            -> Point2d.Point2d units coordinates
            -> Point2d.Point2d units coordinates
            -> BoundingBox2d.BoundingBox2d units coordinates

      - hullN :
            List (BoundingBox2d units coordinates)
            -> Maybe (BoundingBox2d units coordinates)
      + hullN :
            List.List (Point2d.Point2d units coordinates)
            -> Maybe.Maybe (BoundingBox2d.BoundingBox2d units coordinates)

      - hullOf :
            (a -> BoundingBox2d units coordinates)
            -> a
            -> List a
            -> BoundingBox2d units coordinates
      + hullOf :
            (a -> Point2d.Point2d units coordinates)
            -> a
            -> List.List a
            -> BoundingBox2d.BoundingBox2d units coordinates



---- BoundingBox3d - MAJOR ----

    Added:
        aggregate :
            BoundingBox3d.BoundingBox3d units coordinates
            -> List.List (BoundingBox3d.BoundingBox3d units coordinates)
            -> BoundingBox3d.BoundingBox3d units coordinates
        aggregate3 :
            BoundingBox3d.BoundingBox3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates
        aggregateN :
            List.List (BoundingBox3d.BoundingBox3d units coordinates)
            -> Maybe.Maybe (BoundingBox3d.BoundingBox3d units coordinates)
        aggregateOf :
            (a -> BoundingBox3d.BoundingBox3d units coordinates)
            -> a
            -> List.List a
            -> BoundingBox3d.BoundingBox3d units coordinates
        aggregateOfN :
            (a -> BoundingBox3d.BoundingBox3d units coordinates)
            -> List.List a
            -> Maybe.Maybe (BoundingBox3d.BoundingBox3d units coordinates)
        from :
            Point3d.Point3d units coordinates
            -> Point3d.Point3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates
        hullOfN :
            (a -> Point3d.Point3d units coordinates)
            -> List.List a
            -> Maybe.Maybe (BoundingBox3d.BoundingBox3d units coordinates)
        union :
            BoundingBox3d.BoundingBox3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates

    Removed:
        hull2 :
            BoundingBox3d units coordinates
            -> BoundingBox3d units coordinates
            -> BoundingBox3d units coordinates

    Changed:
      - hull :
            BoundingBox3d units coordinates
            -> List (BoundingBox3d units coordinates)
            -> BoundingBox3d units coordinates
      + hull :
            Point3d.Point3d units coordinates
            -> List.List (Point3d.Point3d units coordinates)
            -> BoundingBox3d.BoundingBox3d units coordinates

      - hull3 :
            BoundingBox3d units coordinates
            -> BoundingBox3d units coordinates
            -> BoundingBox3d units coordinates
            -> BoundingBox3d units coordinates
      + hull3 :
            Point3d.Point3d units coordinates
            -> Point3d.Point3d units coordinates
            -> Point3d.Point3d units coordinates
            -> BoundingBox3d.BoundingBox3d units coordinates

      - hullN :
            List (BoundingBox3d units coordinates)
            -> Maybe (BoundingBox3d units coordinates)
      + hullN :
            List.List (Point3d.Point3d units coordinates)
            -> Maybe.Maybe (BoundingBox3d.BoundingBox3d units coordinates)

      - hullOf :
            (a -> BoundingBox3d units coordinates)
            -> a
            -> List a
            -> BoundingBox3d units coordinates
      + hullOf :
            (a -> Point3d.Point3d units coordinates)
            -> a
            -> List.List a
            -> BoundingBox3d.BoundingBox3d units coordinates



---- Circle2d - MINOR ----

    Added:
        atOrigin :
            Quantity.Quantity Basics.Float units
            -> Circle2d.Circle2d units coordinates
        atPoint :
            Point2d.Point2d units coordinates
            -> Quantity.Quantity Basics.Float units
            -> Circle2d.Circle2d units coordinates


---- Frame2d - MAJOR ----

    Changed:
      - atOrigin : Frame2d units coordinates { defines : coordinates }
      + atOrigin : Frame2d.Frame2d units coordinates defines



---- Frame3d - MAJOR ----

    Changed:
      - atOrigin : Frame3d units coordinates { defines : coordinates }
      + atOrigin : Frame3d.Frame3d units coordinates defines



---- LineSegment2d - MINOR ----

    Added:
        fromPointAndVector :
            Point2d.Point2d units coordinates
            -> Vector2d.Vector2d units coordinates
            -> LineSegment2d.LineSegment2d units coordinates


---- LineSegment3d - MINOR ----

    Added:
        fromPointAndVector :
            Point3d.Point3d units coordinates
            -> Vector3d.Vector3d units coordinates
            -> LineSegment3d.LineSegment3d units coordinates


---- Point2d - MAJOR ----

    Removed:
        hull :
            Point2d units coordinates
            -> List (Point2d units coordinates)
            -> BoundingBox2d units coordinates
        hull2 :
            Point2d units coordinates
            -> Point2d units coordinates
            -> BoundingBox2d units coordinates
        hull3 :
            Point2d units coordinates
            -> Point2d units coordinates
            -> Point2d units coordinates
            -> BoundingBox2d units coordinates
        hullN :
            List (Point2d units coordinates)
            -> Maybe (BoundingBox2d units coordinates)
        hullOf :
            (a -> Point2d units coordinates)
            -> a
            -> List a
            -> BoundingBox2d units coordinates


---- Point3d - MAJOR ----

    Removed:
        hull :
            Point3d units coordinates
            -> List (Point3d units coordinates)
            -> BoundingBox3d units coordinates
        hull2 :
            Point3d units coordinates
            -> Point3d units coordinates
            -> BoundingBox3d units coordinates
        hull3 :
            Point3d units coordinates
            -> Point3d units coordinates
            -> Point3d units coordinates
            -> BoundingBox3d units coordinates
        hullN :
            List (Point3d units coordinates)
            -> Maybe (BoundingBox3d units coordinates)
        hullOf :
            (a -> Point3d units coordinates)
            -> a
            -> List a
            -> BoundingBox3d units coordinates


---- Rectangle2d - MAJOR ----

    Added:
        centeredOn :
            Frame2d.Frame2d units coordinates defines
            -> ( Quantity.Quantity Basics.Float units
               , Quantity.Quantity Basics.Float units
               )
            -> Rectangle2d.Rectangle2d units coordinates

    Removed:
        withAxes :
            Frame2d units coordinates defines
            -> ( Quantity Float units, Quantity Float units )
            -> Rectangle2d units coordinates


---- Sphere3d - MINOR ----

    Added:
        atOrigin :
            Quantity.Quantity Basics.Float units
            -> Sphere3d.Sphere3d units coordinates
        atPoint :
            Point3d.Point3d units coordinates
            -> Quantity.Quantity Basics.Float units
            -> Sphere3d.Sphere3d units coordinates
elm-geometry - 2.0.0

Published by ianmackenzie about 5 years ago

elm-geometry 2.0 is out! For now this is a bit of a preview release; it's feature-complete and I'm pretty happy with the API, but the docs are not yet as complete as they should be. Feel free to try out the new version, but please let me (@ianmackenzie) know on Slack if you find any gaps or errors in the documentation.

Units and coordinate systems

The main new feature in this release (that will break all your code) is the addition of type-level tracking of the units and coordinate systems associated with different values. Most types now have two type parameters; where in elm-geometry 1.x you'd have a plain

Point3d

in elm-geometry 2.x you'll have a

Point3d units coordinates

instead. Individual coordinate/component values are now Quantity values from elm-units, so you might construct points using

Point2d.xy (Pixels.pixels 100) (Pixels.pixels 200)
Point3d.xyz (Length.meters 2) (Length.meters 3) (Length.meters 1)

or, to be more concise (especially when prototyping/experimenting),

Point2d.pixels 100 200
Point3d.meters 2 3 1

Unlike units, which can generally be inferred to be a concrete type from usage, the coordinates type variable is largely "optional": you can choose to annotate particular values as being in particular coordinate systems (and then the compiler can check whether those annotations are consistent with each other), but you can also often ignore it entirely. The only time a coordinates type is actually required is if you need to add a type annotation to a top level value; and in the simple case, where everything is in one coordinate system, you can do something like

type World
    = World

and then annotate points as (for example)

myPoint : Point3d Meters World
myPoint =
    Point3d.meters 5 10 20

If different values have different units and coordinate systems, we of course need ways to convert between them! The Frame2d, Frame3d and SketchPlane3d types now both exist in a particular coordinate system and define their own coordinate system; this means that functions like placeIn and relativeTo now let you convert between different coordinate systems. (They always did, really, now it's just official at the type level.) For units, there are new at and at_ functions in each module that act just like the corresponding functions at and at_ functions in the Quantity module.

Streamlined primitive construction

In addition to the new units/coordinate system, point/vector/direction construction has been streamlined and made significantly more flexible. For example, here are several ways of defining the same point:

-- Most flexible, useful with Json.Decode.map3 etc.
Point3d.xyz (Length.meters 2) (Length.meters 3) (Length.meters 1)

-- Very succinct, especially handy for experimentation/REPL use
Point3d.meters 2 3 1

-- Flexible conversion from plain Elm data types
Point3d.fromRecord Length.meters { x = 2, y = 3, z = 1 }
Point3d.fromTuple Length.meters ( 2, 3, 1 )
Point3d.fromRecord Length.centimeters { x = 200, y = 300, z = 100 }
Point3d.fromTuple Length.centimeters ( 200, 300, 100 )

-- Zero-overhead construction from records that happen
-- to be in 'base' units (meters or pixels)
Point3d.fromMeters { x = 2, y = 3, z = 1 }

It's also much easier to construct directions:

Direction2d.fromAngle (Angle.degrees 30) -- like before
Direction2d.degrees 30 -- handy shorthand

Direction3d.xy (Angle.degrees 45) -- an angle on the XY plane
Direction3d.yz (Angle.degrees 60) -- an angle on the YZ plane

Direction3d.xyZ (Angle.degrees 45) -- azimuth in XY...
    (Angle.degrees 30) -- ...and elevation towards +Z


Direction3d.zxY (Angle.degrees 45) -- azimuth in ZX...
    (Angle.degrees 30) -- ...and elevation towards +Y

Miscellaneous additions

While I was working on units and coordinate systems in the coordinate-systems branch, several new features were contributed by various members of the community; these have all now been merged into 2.0:

  • expandBy and offsetBy functions for BoundingBox2d and BoundingBox3d to allow expanding or contracting bounding boxes. (The former only ever expands, and returns a plain bounding box; the latter also supports contraction but returns a Maybe to handle the case where the bounding box contracts to nothing.) Thanks @MrL1605!
  • midpoint functions for QuadraticSpline2d, QuadraticSpline3d, CubicSpline2d and CubicSpline3d as a convenient way to find the point half way along a spline by arc length (not parameter value). Thanks @MrL1605!
  • centroid functions for Polyline2d and Polyline3d to find the center of mass of a polyline. Thanks @davcamer!
  • Polygon2d.contains to check whether a given point is contained within a given polygon. Thanks @gampleman!
  • Circle2d.intersectsBoundingBox to check whether a circle touches a bounding box. Thanks @SebastianKG!

More efficient/type-safe centroid and hull functions

I took the opportunity to fix one thing that's been bothering me a bit: functions that compute the centroid or hull (bounding box) of a list of points have to return a Maybe so that they can return Nothing if given an empty list. There are now a few different versions of those functions for different use cases, for example:

-- If you definitely have at least one point (no Maybe!)
Point3d.centroid : 
    Point3d units coordinates -- first point
    -> List (Point3d units coordinates) -- rest
    -> Point3d units coordinates

-- If you have exactly 3 points (efficient - no list allocation/manipulation)
Point3d.centroid3 :
    Point3d units coordinates
    -> Point3d units coordinates
    -> Point3d units coordinates
    -> Point3d units coordinates

-- If you have an unknown number of points (as before)
Point3d.centroidN :
    List (Point3d units coordinates)
    -> Maybe (Point3d units coordinates)

This means that if you've pattern-matched a list and already have it in head/tail form, then you don't have to deal with the (impossible) Nothing case.

As part of this change a few hull-type functions were renamed/moved around; for example BoundingBox2d.from is now Point2d.hull2 and BoundingBox2d.containingPoints is now Point2d.hullN.

Simplified bounding box overlap/separation checks

The existing BoundingBox#d.overlappingBy and BoundingBox#d.separatedBy functions are very flexible but difficult to describe, inefficient to implement and generally a bit confusing. They've now been simplified to what are hopefully the most common/useful cases:

BoundingBox2d.overlappingByAtLeast :
    Quantity Float units
    -> BoundingBox2d units coordinates
    -> BoundingBox2d units coordinates
    -> Bool

BoundingBox2d.separatedByAtLeast:
    Quantity Float units
    -> BoundingBox2d units coordinates
    -> BoundingBox2d units coordinates
    -> Bool

(Plus similar for BoundingBox3d.)

There are several more smaller changes and probably some I'm forgetting; check out the API docs and reach out if you're confused/curious about anything!

elm-geometry - 1.3.0

Published by ianmackenzie over 5 years ago

elm-geometry 1.3.0 is out! This is a minor release with a few nice additions, all contributed by the community:

  • expandBy and offsetBy functions for BoundingBox2d and BoundingBox3d to allow expanding or contracting bounding boxes. (The former only ever expands, and returns a plain bounding box; the latter also supports contraction but returns a Maybe to handle the case where the bounding box contracts to nothing.) Thanks @MrL1605!
  • midpoint functions for QuadraticSpline2d, QuadraticSpline3d, CubicSpline2d and CubicSpline3d as a convenient way to find the point half way along a spline by arc length (not parameter value). Thanks @MrL1605!
  • centroid functions for Polyline2d and Polyline3d to find the center of mass of a polyline. Thanks @davcamer!
  • Polygon2d.contains to check whether a given point is contained within a given polygon. Thanks @gampleman!
elm-geometry - 1.2.1

Published by ianmackenzie about 6 years ago

Add images to Voronoi/Delaunay docs

elm-geometry - 1.2.0

Published by ianmackenzie about 6 years ago

elm-geometry 1.2 is out! The major feature in this release is support for generation of Delaunay triangulations and Voronoi diagrams:

Voronoi diagram

Huge thanks to @folkertdev for all his work on this in #58!

Additionally, this release adds a Rectangle2d module for directly creating and manipulating rectangles (which, unlike the similar BoundingBox2d, can be rotated/mirrored etc.).

Last (and least, really), a few functions have been renamed - the old names have been kept for now but will likely be removed in the next major release:

1.1 1.2
BoundingBox2d.centroid BoundingBox2d.centerPoint
BoundingBox3d.centroid BoundingBox3d.centerPoint
Frame2d.xy Frame2d.atOrigin
Frame3d.xyz Frame3d.atOrigin

Finally, shoutout to @dmy for the excellent elm-doc-preview tool. I used it quite a bit when finishing up the documentation for this release, and it made things really pleasant. Highly recommended to other package authors to let you quickly iterate on your documentation before release!

elm-geometry - 1.1.0

Published by ianmackenzie about 6 years ago

First release for Elm 0.19! This is almost identical to the last published release for Elm 0.18 (1.0.2), but contains a handful of new functions:

  • LineSegment2d.intersectionWithAxis : Axis2d -> LineSegment2d -> Maybe Point2d
  • Point2d.centroid : List Point2d -> Maybe Point2d
  • Point3d.centroid : List Point3d -> Maybe Point3d
elm-geometry - 1.0.0

Published by ianmackenzie over 6 years ago

elm-geometry 1.0 is out! This is effectively version 3.0 of opensolid/geometry, with quite a few changes since opensolid/geometry 2.1.0:

  • Renamed all modules to remove OpenSolid. prefix
  • Removed JSON encoding/decoding modules and split Interval and Scalar modules out into their own packages
  • Added new ParameterValue and SweptAngle modules
  • Refactored curve evaluation functions such as pointOn
  • Improved Polygon2d functionality significantly
  • Streamlined constructors for many geometric types
  • Updated spline constructors
  • Added several new useful functions
  • Renamed several functions
  • Removed a few deprecated functions

In general, to update code written for opensolid/geometry to use elm-geometry, first remove the OpenSolid. prefix from all imports. Then, follow the compiler error messages and look within these release notes to see how individual function calls should be updated (for example, search for Vector3d.with to see that it has been replaced by Vector3d.withLength). That said, these release notes are not exhaustive, so if you run into anything not covered here, come ask in the #geometry channel on the Elm Slack!

Renamed modules

Modules no longer include an OpenSolid. prefix, so imports are now more succinct:

-- opensolid/geometry
import OpenSolid.Point3d as Point3d exposing (Point3d)

-- elm-geometry
import Point3d exposing (Point3d)

The module names in elm-geometry have been chosen to try to avoid conflicts with other published Elm packages, but if you do find a conflict, please file an issue.

Removed modules

opensolid/geometry included built-in support for JSON encoding and decoding, but after some discussion on Discourse and Slack it was decided to remove this functionality from elm-geometry. It may reappear later, but likely as a separate package.

A couple of modules have been moved out into their own packages:

New modules

Curve.ParameterValue

Evaluating points on curves now requires you to pass a ParameterValue instead of a plain Float. A ParameterValue is effectively a Float that is guaranteed to be between 0 and 1, which adds some extra type safety to curve evaluation. The ParameterValue module includes functions for constructing ParameterValues from plain Floats, and several convenient functions for constructing ranges of evenly-spaced parameter values.

Arc.SweptAngle

The Arc.SweptAngle module replaces the duplicate SweptAngle types and values that existed the Arc2d and EllipticalArc2d modules:

-- opensolid/geometry
Arc2d.smallPositive : Arc2d.SweptAngle
EllipticalArc2d.smallPositive : EllipticalArc2d.SweptAngle

-- elm-geometry
SweptAngle.smallPositive : SweptAngle.SweptAngle

Refactored curve evaluation

A very common and important operation in elm-geometry is evaluating positions and tangent directions at various points along a curve (such as an arc or a cubic Bezier spline). A few related changes have been made in elm-geometry to make this more type-safe and explicit. First of all, as mentioned above, curve evaluation functions now generally take ParameterValue arguments instead of plain Float ones:

-- opensolid/geometry
CubicSpline3d.pointOn : CubicSpline3d -> Float -> Point3d

-- elm-geometry
CubicSpline3d.pointOn : CubicSpline3d -> ParameterValue -> Point3d

You can construct a ParameterValue using ParameterValue.clamped, but for the common case of evaluating many points at once there are also functions for directly generating a list of parameter values and then evaluating curves at those values. For example, to get 11 points on a cubic spline (including the start and end points), you could use

pointsOnSpline =
    cubicSpline |> CubicSpline3d.pointsAt (ParameterValue.steps 10)

Note that if you take 1 step along a curve you get 2 points (start and end), if you take 2 steps along a curve you get 3 points (start, middle and end), if you take 10 steps along a curve you get 11 points, etc.

Curves now also support evaluating tangent directions and 'samples' (point/tangent direction pairs), but only if the curve is nondegenerate. If a curve is actually just a single point (e.g. a spline where all control points are equal), then the curve is said to be degenerate and the tangent direction is undefined. All curve types (Arc3d, EllipticalArc2d, CubicSpline3d etc.) now have functions like

CubicSpline3d.nondegenerate : CubicSpline3d -> Result Point3d CubicSpline3d.Nondegenerate

to attempt to convert a curve to its guaranteed-nondegenerate form. If the curve is in fact degenerate (consists of a single point) then you will get an Err with that point instead. Once you have a Nondegenerate value, you can then use it to evaluate tangent directions and samples:

CubicSpline3d.tangentDirection : CubicSpline3d.Nondegenerate -> ParameterValue -> Direction3d
CubicSpline3d.sample : CubicSpline3d.Nondegenerate -> ParameterValue -> ( Point3d, Direction3d )

For example, if you wanted to animate along a cubic spline, you might call CubicSpline3d.nondegenerate first to try to get a nondegenerate curve. If that succeeds, then use the resulting Nondegenerate value to evaluate points and tangent directions along the curve. If it fails, then you should apply special-case logic - perhaps drop that curve entirely from your animation path, or display your animated object in a fixed position (using the point returned in the Err case) with some default orientation.

Polygon2d improvements

The Polygon2d module has been improved significantly in this release. First of all, polygons can now have holes, which necessitated several changes:

  • Polygon2d.fromVertices has been renamed to Polygon2d.singleLoop to emphasize that it constructs a polygon without holes
  • Polygon2d.with has been added to construct polygons with holes
  • Polygon2d.outerLoop and Polygon2d.innerLoops accessors have been added
  • Polygon2d.clockwiseArea, Polygon2d.counterclockwiseArea and Polygon2d.mapVertices have been removed since they could not easily be made to work with the new polygon representation

Polygon2d.convexHull has been added to compute the convex hull of a set of points (thanks @gampleman!):

Convex hull of a set of points

Finally, polygons can now be triangulated, so you can define a polygon just by specifying its outline (and holes, if it has any)

Polygon with hole

then use Polygon2d.triangulate to turn that polygon into a list of triangles:

Polygon triangulation

This is primarily useful for WebGL rendering but has many other applications.

Streamlined constructors

There was a significant push in this release to streamline geometry construction functions. Many functions that previously took a single record argument have been reworked to take multiple arguments instead, with the following goals in mind:

  • Reduce verbosity
  • Support partial application/work well with |>, map, map2 etc.
  • Try not to sacrifice too much clarity/explicitness

For example,

-- opensolid/geometry
Vector2d.with { length = 3, direction = Direction2d.x }

can now be written as

-- elm-geometry
Vector2d.withLength 3 Direction2d.x

Similarly,

-- opensolid/geometry
Axis2d.with { originPoint = point, direction = direction }

can now be written as

-- elm-geometry
Axis2d.through point direction
-- OR
Axis2d.withDirection direction point

Having both versions allow you to do different things with partial application:

-- A list of axes in different directions all passing through the same origin point
List.map (Axis2d.through point) directions

-- A list of parallel axes (all having the same direction) through different points
List.map (Axis2d.withDirection direction) points

Many other constructors have been similarly updated:

opensolid/geometry elm-geometry
Arc2d.fromEndpoints Arc2d.withRadius: Float -> SweptAngle -> Point2d -> Point2d -> Maybe Arc2d
Arc2d.with Arc2d.sweptAround: Point2d -> Float -> Point2d -> Arc2d
Arc3d.around Arc3d.sweptAround: Axis3d -> Float -> Point3d -> Arc3d
Axis3d.with Axis3d.through: Point3d -> Direction3d -> Axis3dAxis3d.withDirection: Direction3d -> Point3d -> Axis3d
Circle2d.with Circle2d.withRadius: Float -> Point2d -> Circle2d
Circle3d.with Circle3d.withRadius: Float -> Direction3d -> Point3d -> Circle3d
Direction3d.with Direction3d.fromAzimuthAndElevation: ( Float, Float ) -> Direction3d
Frame2d.with Frame2d.withXDirection: Direction2d -> Point2d -> Frame2dFrame2d.withYDirection: Direction2d -> Point2d -> Frame2d
Frame3d.with Frame3d.withXDirection: Direction3d -> Point3d -> Frame3dFrame3d.withYDirection: Direction3d -> Point3d -> Frame3dFrame3d.withZDirection: Direction3d -> Point3d -> Frame3d
Plane3d.with Plane3d.through: Point3d -> Direction3d -> Plane3dPlane3d.withNormalDirection: Direction3d -> Point3d -> Plane3d
SketchPlane3d.with SketchPlane3d.withNormalDirection: Direction3d -> Point3d -> SketchPlane3d
Sphere3d.with Sphere3d.withRadius: Float -> Point3d -> Sphere3d
Vector3d.with Vector3d.withLength : Float -> Direction3d -> Vector3d

Updated spline constructors

The quadratic and cubic spline constructors previously known as fromControlPoints have been updated to avoid the use of large tuples, and generally be consistent with constructors in other modules:

-- opensolid/geometry
QuadraticSpline2d.fromControlPoints ( p1, p2, p3 )
CubicSpline3d.fromControlPoints ( p1, p2, p3, p4 )

-- elm-geometry
QuadraticSpline2d.with
    { startPoint = p1
    , controlPoint = p2
    , endPoint = p3
    }
    
CubicSpline3d.with
    { startPoint = p1
    , startControlPoint = p2
    , endControlPoint = p3
    , endPoint = p4
    }

Similarly, CubicSpline#d.hermite has been replaced by fromEndpoints:

-- opensolid/geometry
CubicSpline3d.hermite ( p1, v1 ) ( p2, v2 )

-- elm-geometry
CubicSpline3d.fromEndpoints
    { startPoint = p1
    , startDerivative = v1
    , endPoint = p2
    , endDerivative = v2
    }

New functions

As a small but very useful change, all modules with translateBy functions now also have translateIn functions; for example,

triangle |> Triangle3d.translateIn Direction3d.y 5

means "translate triangle in the Y direction by 5 units".

Arc2d.from has been added as a new and convenient way to construct arcs from a start point, end point and swept angle:

cornerArc =
    Arc2d.from startPoint endPoint (degrees 90)

A new version of Arc2d.with has also been added:

semicircle =
    Arc2d.with
        { centerPoint = Point2d.origin
        , radius = 3
        , startAngle = degrees 0
        , sweptAngle = degrees 180
        }

Circle2d.sweptAround has been added to construct a circle by sweeping one point around a center point:

circle =
    Circle2d.sweptAround centerPoint pointOnCircle

Bounding boxes can now be constructed for splines:

CubicSpline2d.boundingBox : CubicSpline2d -> BoundingBox2d
CubicSpline3d.boundingBox : CubicSpline3d -> BoundingBox3d
QuadraticSpline2d.boundingBox : QuadraticSpline2d -> BoundingBox2d
QuadraticSpline3d.boundingBox : QuadraticSpline3d -> BoundingBox3d

It's now possible to compute the second derivative for cubic splines as well as quadratic ones:

CubicSpline2d.secondDerivative : CubicSpline2d -> ParameterValue -> Vector2d
CubicSpline3d.secondDerivative : CubicSpline3d -> ParameterValue -> Vector3d

If you have an arc length parameterized curve, you can now recover the original curve from it:

CubicSpline2d.fromArcLengthParameterized : CubicSpline2d.ArcLengthParameterized -> CubicSpline2d
CubicSpline3d.fromArcLengthParameterized : CubicSpline3d.ArcLengthParameterized -> CubicSpline3d
EllipticalArc2d.fromArcLengthParameterized : EllipticalArc2d.ArcLengthParameterized -> EllipticalArc2d
QuadraticSpline2d.fromArcLengthParameterized : QuadraticSpline2d.ArcLengthParameterized -> QuadraticSpline2d
QuadraticSpline3d.fromArcLengthParameterized : QuadraticSpline3d.ArcLengthParameterized -> QuadraticSpline3d

Frame2d.atCoordinates has been added as a convenient shorthand for Frame2d.atPoint+Point2d.fromCoordinates, and similar for Frame3d:

Frame2d.atCoordinates : ( Float, Float ) -> Frame2d
Frame3d.atCoordinates : ( Float, Float, Float ) -> Frame3d

Point2d values can now be constructed directly from their polar coordinates with respect to a particular Frame2d:

Point2d.fromPolarCoordinatesIn : Frame2d -> ( Float, Float ) -> Point2d

Finally, like Direction2d, Vector2d values can now also be conveniently (and efficiently) rotated 90 degrees clockwise or counterclockwise:

Vector2d.rotateClockwise : Vector2d -> Vector2d
Vector3d.rotateCounterclockwise : Vector2d -> Vector2d

Renamed functions

Several functions have been renamed to try to be more descriptive, read better in pipelines, reduce circular dependencies between modules, or be more consistent with how other functions are named:

opensolid/geometry elm-geometry
Axis2d.flip Axis2d.reverse
Axis3d.flip Axis3d.reverse
BoundingBox2d.hullOf BoundingBox2d.aggregate
BoundingBox3d.hullOf BoundingBox3d.aggregate
BoundingBox2d.with BoundingBox2d.fromExtrema
BoundingBox3d.with BoundingBox3d.fromExtrema
Circle3d.around Circle3d.sweptAround
Direction2d.angle Direction2d.toAngle
Direction2d.flip Direction2d.reverse
Direction3d.flip Direction3d.reverse
Frame2d.flipX Frame2d.reverseX
Frame2d.flipY Frame2d.reverseY
Frame3d.flipX Frame3d.reverseX
Frame3d.flipY Frame3d.reverseY
Frame3d.flipZ Frame3d.reverseZ
LineSegment2d.normalDirection LineSegment2d.perpendicularDirection
LineSegment3d.normalDirection LineSegment3d.perpendicularDirection
Plane3d.flip Plane3d.reverseNormal
Point2d.hull BoundingBox2d.from
Point3d.hull BoundingBox3d.from
Point2d.hullOf BoundingBox2d.containingPoints
Point3d.hullOf BoundingBox3d.containingPoints
Point2d.in_ Point2d.fromCoordinatesIn
Point3d.in_ Point2d.fromCoordinatesIn
SketchPlane3d.flipX SketchPlane3d.reverseX
SketchPlane3d.flipY SketchPlane3d.reverseY
SketchPlane3d.plane SketchPlane3d.toPlane
Vector2d.flip Vector2d.reverse
Vector3d.flip Vector3d.reverse

Removed functions

A few functions have been removed in this release, but each one was simply a deprecated alias for a better-named replacement:

Removed Replacement
BoundingBox2d.overlaps BoundingBox2d.intersects
BoundingBox3d.overlaps BoundingBox3d.intersects
Point2d.distanceAlong Point2d.signedDistanceAlong
Point3d.distanceAlong Point3d.signedDistanceAlong