ianmackenzie / elm-geometry

2D/3D geometry package for Elm
Mozilla Public License 2.0
183 stars 26 forks source link

Add Ellipsoid3d module #135

Closed ianmackenzie closed 4 years ago

ianmackenzie commented 4 years ago

3D version of Ellipse2d; would be useful for 3D rendering. Should mostly be able to follow the pattern established by Ellipse2d and Sphere3d.

ianmackenzie commented 4 years ago

See https://en.wikipedia.org/wiki/Ellipsoid for volume/surface area calculations.

g-belmonte commented 4 years ago

I came up with this "API" while organizing my thoughts. @ianmackenzie Let me know your thoughts :smile:

Constructors

throughPoints :
    Point3d units coordinates
    -> Point3d units coordinates
    -> Point3d units coordinates
    -> Point3d units coordinates
    -> Maybe (Ellipsoid3d units coordinates)

Properties

centerPoint : Ellipsoid3d units coordinates -> Point3d units coordinates

(something to get a, b and c - the points where the ellipsoid intersect the axis of its own coordinate system)

volume : Ellipsoid3d units coordinates -> Quantity Float (Cubed units)
surfaceArea : Ellipsoid3d units coordinates -> Quantity Float (Squared units)
boundingBox : Ellipsoid3d units coordinates -> BoundingBox3d units coordinates

Queries

contains : Point3d units coordinates -> Sphere3d units coordinates -> Bool

Transformations

scaleAbout :
    Point3d units coordinates
    -> Float
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates
rotateAround :
    Axis3d units coordinates
    -> Angle
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates
translateBy :
    Vector3d units coordinates
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates
translateIn :
    Direction3d coordinates
    -> Quantity Float units
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates
mirrorAcross :
    Plane3d units coordinates
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

(will an ellipsoid projection always be an ellipsis? - that considering that a circle is a special case of an ellipsis)

projectOnto :
    Plane3d units coordinates
    -> Ellipsoid3d units coordinates
    -> Ellipsis2d units coordinates
projectInto :
    SketchPlane3d units coordinates3d { defines : coordinates2d }
    -> Ellipsoid3d units coordinates3d
    -> Ellipsis2d units coordinates2d

Unit conversions

at :
    Quantity Float (Rate units2 units1)
    -> Ellipsoid3d units1 coordinates
    -> Ellipsoid3d units2 coordinates
at_ :
    Quantity Float (Rate units1 units2)
    -> Ellipsoid3d units1 coordinates
    -> Ellipsoid3d units2 coordinates

Coordinate conversions

relativeTo :
    Frame3d units globalCoordinates { defines : localCoordinates }
    -> Ellipsoid3d units globalCoordinates
    -> Ellipsoid3d units localCoordinates
placeIn :
    Frame3d units globalCoordinates { defines : localCoordinates }
    -> Ellipsoid3d units localCoordinates
    -> Ellipsoid3d units globalCoordinates
ianmackenzie commented 4 years ago

Hey @g-belmonte! I think that mostly looks good except that I don't think throughPoints could be implemented in any kind of useful way. Four points uniquely defines a sphere (unless all four points lie in the same plane), but an ellipsoid has several more degrees of freedom (three different radii, axis orientation) so four points wouldn't be anywhere near enough to uniquely identify an ellipsoid. (And I would guess that constructing an ellipsoid through particular points is not something you'd want to do frequently anyways.)

I suspect that the projection of an ellipsoid onto a plane would in fact always be an ellipse, but the math could get pretty tricky! Probably OK to leave that one out for now.

It looks like you've based this mostly off of Sphere3d - I think there are several functions that could also be adapted from Ellipse2d:

with :
    { axes : Frame3d units coordinates defines
    , xRadius : Quantity Float units
    , yRadius : Quantity Float units
    , zRadius : Quantity Float units
    }
    -> Ellipsoid3d units coordinates

axes : Ellipsoid3d units coordinates -> Frame3d units coordinates defines

xAxis : Ellipsoid3d units coordinates -> Axis3d units coordinates
-- plus yAxis, zAxis

xDirection : Ellipsoid3d units coordinates -> Direction3d coordinates
-- plus yDirection, zDirection

xRadius : Ellipsoid3d units coordinates -> Quantity Float units
-- plus yRadius, zRadius

signedDistanceAlong : Axis3d units coordinates -> Ellipsoid3d units coordinates -> Interval Float units

With those functions in place, I'm not sure you'd really need accessors for the points where the ellipsoid intersects its own axes - if necessary you could get them pretty easily with

Point3d.along (Ellipsoid3d.xAxis ellipsoid) (Ellipsoid3d.xRadius ellipsoid)
Point3d.along (Ellipsoid3d.xAxis ellipsoid) (Quantity.negate (Ellipsoid3d.xRadius ellipsoid))

etc.

g-belmonte commented 4 years ago

Hey there, @ianmackenzie ! Sorry for the delay, this week was crazy :sweat_smile: When I thought about the throughPoints function, I was actually thinking in giving the x, y and z radii relative to a local coordinate system. And yes, I did everything based on the Sphere3d. About the signedDistanceAlong, is it the distance from the axis' zero up to the projection of the center of the ellipsoid on that axis, or is it from the zero up to the surface of the ellipsoid?

Here's another sketch:

Constructors

with :
    { axes : Frame3d units coordinates defines
    , xRadius : Quantity Float units
    , yRadius : Quantity Float units
    , zRadius : Quantity Float units
    }
    -> Ellipsoid3d units coordinates

Properties

centerPoint : Ellipsoid3d units coordinates -> Point3d units coordinates

axes : Ellipsoid3d units coordinates -> Frame3d units coordinates defines

xAxis : Ellipsoid3d units coordinates -> Axis3d units coordinates
yAxis : Ellipsoid3d units coordinates -> Axis3d units coordinates
zAxis : Ellipsoid3d units coordinates -> Axis3d units coordinates

xDirection : Ellipsoid3d units coordinates -> Direction3d coordinates
yDirection : Ellipsoid3d units coordinates -> Direction3d coordinates
zDirection : Ellipsoid3d units coordinates -> Direction3d coordinates

xRadius : Ellipsoid3d units coordinates -> Quantity Float units
yRadius : Ellipsoid3d units coordinates -> Quantity Float units
zRadius : Ellipsoid3d units coordinates -> Quantity Float units

volume : Ellipsoid3d units coordinates -> Quantity Float (Cubed units)

surfaceArea : Ellipsoid3d units coordinates -> Quantity Float (Squared units)

boundingBox : Ellipsoid3d units coordinates -> BoundingBox3d units coordinates

Queries

contains : Point3d units coordinates -> Sphere3d units coordinates -> Bool

Measurement

signedDistanceAlong : Axis3d units coordinates -> Ellipsoid3d units coordinates -> Interval Float units

Transformations

scaleAbout :
    Point3d units coordinates
    -> Float
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

rotateAround :
    Axis3d units coordinates
    -> Angle
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

translateBy :
    Vector3d units coordinates
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

translateIn :
    Direction3d coordinates
    -> Quantity Float units
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

mirrorAcross :
    Plane3d units coordinates
    -> Ellipsoid3d units coordinates
    -> Ellipsoid3d units coordinates

Unit conversions

at :
    Quantity Float (Rate units2 units1)
    -> Ellipsoid3d units1 coordinates
    -> Ellipsoid3d units2 coordinates

at_ :
    Quantity Float (Rate units1 units2)
    -> Ellipsoid3d units1 coordinates
    -> Ellipsoid3d units2 coordinates

Coordinate conversions

relativeTo :
    Frame3d units globalCoordinates { defines : localCoordinates }
    -> Ellipsoid3d units globalCoordinates
    -> Ellipsoid3d units localCoordinates

placeIn :
    Frame3d units globalCoordinates { defines : localCoordinates }
    -> Ellipsoid3d units localCoordinates
    -> Ellipsoid3d units globalCoordinates
ianmackenzie commented 4 years ago

I think that all looks good!

The signedDistanceAlong function is kind of the 'shadow' or projection of the ellipsoid along a particular axis. If you fire up elm reactor within the sandbox directory and then open src/Ellipse2dDistanceAlongAxis.elm you can see how this works in 2D - the blue line is an axis and the red and green lines indicate the min and max distances of a given ellipse along that axis.

I don't think it's a function that most people will use much, but it's a necessary low-level function for a few other functions in elm-geometry. It's also a mathematically pretty tricky calculation, but I think you should basically be able to copy Ellipse2d's implementation and extend it to also include Z anywhere it currently uses X and Y. Does that make sense?

g-belmonte commented 4 years ago

Yes, it does make sense! Later today I'll open a WIP pull request, so that you can follow up :)

g-belmonte commented 4 years ago

@ianmackenzie I think this issue may be closed :)

ianmackenzie commented 4 years ago

Right you are! Implemented in #142.