From 470a31dbcc9281208f259fea2407ffe7a07c8f62 Mon Sep 17 00:00:00 2001 From: Andrey Kuzmin Date: Sat, 2 Jan 2021 15:03:59 +0100 Subject: [PATCH] Implement Axis3d.intersectionWithRectangle --- src/Axis3d.elm | 45 ++++++++++++++++++++++++++++--- tests/Geometry/Fuzz.elm | 12 +++++++-- tests/Tests/Axis3d.elm | 60 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/src/Axis3d.elm b/src/Axis3d.elm index 01368d9a..1b3a1640 100644 --- a/src/Axis3d.elm +++ b/src/Axis3d.elm @@ -12,7 +12,7 @@ module Axis3d exposing , x, y, z , through, withDirection, throughPoints, on , originPoint, direction - , intersectionWithPlane, intersectionWithSphere + , intersectionWithPlane, intersectionWithRectangle, intersectionWithSphere , reverse, moveTo, rotateAround, translateBy, translateIn, mirrorAcross, projectOnto , at, at_ , relativeTo, placeIn, projectInto @@ -46,7 +46,7 @@ by an origin point and direction. Axes have several uses, such as: # Intersection -@docs intersectionWithPlane, intersectionWithSphere +@docs intersectionWithPlane, intersectionWithRectangle, intersectionWithSphere # Transformations @@ -68,9 +68,10 @@ by an origin point and direction. Axes have several uses, such as: import Angle exposing (Angle) import Axis2d exposing (Axis2d) import Direction3d exposing (Direction3d) -import Geometry.Types as Types exposing (Frame3d, Plane3d, SketchPlane3d, Sphere3d, Triangle3d) +import Geometry.Types as Types exposing (Frame3d, LineSegment3d, Plane3d, Rectangle3d, SketchPlane3d, Sphere3d, Triangle3d) import Point3d exposing (Point3d) import Quantity exposing (Quantity(..), Rate) +import Unsafe.Direction3d as Direction3d import Vector3d exposing (Vector3d) @@ -308,6 +309,44 @@ intersectionWithTriangle triangle axis = |> Just +{-| Try to find the unique intersection point of an axis with a rectangle. If +the axis does not intersect the rectangle, or if it is coplanar with it (lying +perfectly in the plane of the rectangle), returns `Nothing`. +-} +intersectionWithRectangle : Rectangle3d units coordinates -> Axis3d units coordinates -> Maybe (Point3d units coordinates) +intersectionWithRectangle (Types.Rectangle3d { axes, dimensions }) axis = + let + (Types.SketchPlane3d sketchPlane) = + axes + + plane = + Types.Plane3d + { normalDirection = + Direction3d.unsafeCrossProduct + sketchPlane.xDirection + sketchPlane.yDirection + , originPoint = sketchPlane.originPoint + } + in + case intersectionWithPlane plane axis of + (Just point) as result -> + let + ( Quantity width, Quantity height ) = + dimensions + + (Types.Point2d p) = + Point3d.projectInto (Types.SketchPlane3d sketchPlane) point + in + if abs p.x / width <= 0.5 && abs p.y / height <= 0.5 then + result + + else + Nothing + + Nothing -> + Nothing + + {-| Attempt to find the intersection of an axis with a sphere. The two points will be in order of signed distance along the axis. Returns `Nothing` if there is no intersection. diff --git a/tests/Geometry/Fuzz.elm b/tests/Geometry/Fuzz.elm index 9522f780..0e87c69e 100644 --- a/tests/Geometry/Fuzz.elm +++ b/tests/Geometry/Fuzz.elm @@ -3,7 +3,7 @@ module Geometry.Fuzz exposing , length, positiveLength, angle, quantityRange , point2d, point3d, vector2d, vector3d, direction2d, direction3d, boundingBox2d, boundingBox3d , axis2d, axis3d, frame2d, frame3d, plane3d, sketchPlane3d - , lineSegment2d, lineSegment3d, triangle2d, triangle3d, rectangle2d, block3d, polyline2d, polyline3d, polygon2d + , lineSegment2d, lineSegment3d, triangle2d, triangle3d, rectangle2d, rectangle3d, block3d, polyline2d, polyline3d, polygon2d , arc2d, arc3d, circle2d, circle3d, cubicSpline2d, cubicSpline3d, cylinder3d, cone3d, ellipse2d, ellipticalArc2d, ellipticalArc3d, ellipsoid3d, quadraticSpline2d, quadraticSpline3d, rationalCubicSpline2d, rationalCubicSpline3d, rationalQuadraticSpline2d, rationalQuadraticSpline3d, sphere3d ) @@ -47,7 +47,7 @@ running into any naming conflicts. # Simple geometry -@docs lineSegment2d, lineSegment3d, triangle2d, triangle3d, rectangle2d, block3d, polyline2d, polyline3d, polygon2d +@docs lineSegment2d, lineSegment3d, triangle2d, triangle3d, rectangle2d, rectangle3d, block3d, polyline2d, polyline3d, polygon2d # Complex geometry @@ -97,6 +97,7 @@ import RationalCubicSpline3d exposing (RationalCubicSpline3d) import RationalQuadraticSpline2d exposing (RationalQuadraticSpline2d) import RationalQuadraticSpline3d exposing (RationalQuadraticSpline3d) import Rectangle2d exposing (Rectangle2d) +import Rectangle3d exposing (Rectangle3d) import Shrink import SketchPlane3d exposing (SketchPlane3d) import Sphere3d exposing (Sphere3d) @@ -571,6 +572,13 @@ rectangle2d = Fuzz.map3 rectangle frame2d positiveLength positiveLength +{-| Generate a random `Rectangle3d`. +-} +rectangle3d : Fuzzer (Rectangle3d Meters coordinates) +rectangle3d = + Fuzz.map2 Rectangle3d.on sketchPlane3d rectangle2d + + {-| Generate a random `Block3d`. -} block3d : Fuzzer (Block3d Meters coordinates) diff --git a/tests/Tests/Axis3d.elm b/tests/Tests/Axis3d.elm index 91cb26f8..f860dc01 100644 --- a/tests/Tests/Axis3d.elm +++ b/tests/Tests/Axis3d.elm @@ -1,6 +1,7 @@ module Tests.Axis3d exposing ( directionExample , intersectionWithPlane + , intersectionWithRectangle , intersectionWithSphere , intersectionWithTriangle , onExamples @@ -26,6 +27,8 @@ import Plane3d import Point2d import Point3d import Quantity +import Rectangle2d exposing (Rectangle2d) +import Rectangle3d import SketchPlane3d import Sphere3d import Test exposing (Test) @@ -205,6 +208,63 @@ intersectionWithTriangle = ) +intersectionWithRectangle : Test +intersectionWithRectangle = + Test.fuzz3 + Fuzz.rectangle3d + Fuzz.direction3d + (ElmFuzz.tuple3 + ( ElmFuzz.floatRange -0.5 1.5 + , ElmFuzz.floatRange -0.5 1.5 + , ElmFuzz.floatRange -10 10 + ) + ) + "intersectionWithRectangle works properly" + (\rectangle axisDirection parameters -> + let + s = + Rectangle3d.axes rectangle + |> SketchPlane3d.normalDirection + |> Direction3d.componentIn axisDirection + in + if abs s < 1.0e-3 then + Expect.pass + + else + let + ( u, v, t ) = + parameters + + planeIntersection = + Rectangle3d.interpolate rectangle u v + + direction = + Vector3d.withLength (Length.meters 1) axisDirection + + axisOrigin = + planeIntersection + |> Point3d.translateBy (Vector3d.scaleBy -t direction) + + axis = + Axis3d.through axisOrigin axisDirection + in + case Axis3d.intersectionWithRectangle rectangle axis of + Nothing -> + if u < 0 || v < 0 || u > 1 || v > 1 then + Expect.pass + + else + Expect.fail "Expected an intersection" + + Just point -> + if u < 0 || v < 0 || u > 1 || v > 1 then + Expect.fail "Expected no intersection" + + else + Expect.point3d planeIntersection point + ) + + intersectionWithSphere : Test intersectionWithSphere = Test.describe "intersectionWithSphere"