A BoundingBox2d
is a rectangular box in 2D defined by its minimum and
maximum X and Y values. It is possible to generate bounding boxes for most
geometric objects; for example, Triangle2d.boundingBox
takes a Triangle2d
and returns a BoundingBox2d
that contains that triangle.
There are several use cases where it is more efficient to deal with the bounding
box of an object than the object itself, such as:
Geometry.Types.BoundingBox2d units coordinates
from : Point2d units coordinates -> Point2d units coordinates -> BoundingBox2d units coordinates
Construct a bounding box with the two given points as two of its corners. The points can be given in any order and don't have to represent the 'primary' diagonal of the bounding box.
BoundingBox2d.from
(Point2d.meters 2 3)
(Point2d.meters -1 5)
--> BoundingBox2d.fromExtrema
--> { minX = Length.meters -1
--> , maxX = Length.meters 2
--> , minY = Length.meters 3
--> , maxY = Length.meters 5
--> }
fromExtrema : { minX : Quantity Basics.Float units, maxX : Quantity Basics.Float units, minY : Quantity Basics.Float units, maxY : Quantity Basics.Float units } -> BoundingBox2d units coordinates
Construct a bounding box from its minimum and maximum X and Y values:
exampleBox =
BoundingBox2d.fromExtrema
{ minX = Length.meters 3
, maxX = Length.meters 8
, minY = Length.meters 2
, maxY = Length.meters 6
}
If the minimum and maximum values are provided in the wrong order (for example
if minX
is greater than maxX
), then they will be swapped so that the
resulting bounding box is valid.
withDimensions : ( Quantity Basics.Float units, Quantity Basics.Float units ) -> Point2d units coordinates -> BoundingBox2d units coordinates
Construct a bounding box given its overall dimensions (width and height) and center point.
singleton : Point2d units coordinates -> BoundingBox2d units coordinates
Construct a zero-width bounding box containing a single point.
xy : Quantity.Interval.Interval Basics.Float units -> Quantity.Interval.Interval Basics.Float units -> BoundingBox2d units coordinates
Construct a bounding box from separate X and Y intervals.
fromIntervals : ( Quantity.Interval.Interval Basics.Float units, Quantity.Interval.Interval Basics.Float units ) -> BoundingBox2d units coordinates
Construct a bounding box from a pair of X and Y intervals.
union : BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Build a bounding box that contains both given bounding boxes.
firstBox =
BoundingBox2d.from
(Point2d.meters 1 2)
(Point2d.meters 4 3)
secondBox =
BoundingBox2d.from
(Point2d.meters -2 4)
(Point2d.meters 2 5)
BoundingBox2d.union firstBox secondBox
--> BoundingBox2d.from
--> (Point2d.meters -2 2)
--> (Point2d.meters 4 5)
(Note that this is not strictly speaking a 'union' in the precise mathematical sense.)
intersection : BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> Maybe (BoundingBox2d units coordinates)
Attempt to build a bounding box that contains all points common to both
given bounding boxes. If the given boxes do not intersect, returns Nothing
.
firstBox =
BoundingBox2d.from
(Point2d.meters 1 2)
(Point2d.meters 4 3)
secondBox =
BoundingBox2d.from
(Point2d.meters 2 1)
(Point2d.meters 5 4)
BoundingBox2d.intersection firstBox secondBox
--> Just <|
--> BoundingBox2d.from
--> (Point2d.meters 2 2)
--> (Point2d.meters 4 3)
If two boxes just touch along an edge or at a corner, they are still considered to have an intersection, even though that intersection will have zero area (at least one of its dimensions will be zero):
firstBox =
BoundingBox2d.from
(Point2d.meters 0 0)
(Point2d.meters 1 2)
secondBox =
BoundingBox2d.from
(Point2d.meters 1 1)
(Point2d.meters 2 3)
BoundingBox2d.intersection firstBox secondBox
--> Just <|
--> BoundingBox2d.from
--> (Point2d.meters 1 1)
--> (Point2d.meters 1 2)
Functions for building bounding boxes containing several points.
hull : Point2d units coordinates -> List (Point2d units coordinates) -> BoundingBox2d units coordinates
Find the bounding box containing one or more input points:
BoundingBox2d.hull p1 [ p2, p3, p4 ]
Often ends up being used within a case
expression:
case points of
[] ->
-- some default behavior
first :: rest ->
let
boundingBox =
BoundingBox2d.hull first rest
in
-- normal behavior using 'boundingBox'
See also hullN
.
hull3 : Point2d units coordinates -> Point2d units coordinates -> Point2d units coordinates -> BoundingBox2d units coordinates
Build a bounding box that contains all three of the given points;
BoundingBox2d.hull3 p1 p2 p3
is equivalent to
BoundingBox2d.hull p1 [ p2, p3 ]
but is more efficient.
hullN : List (Point2d units coordinates) -> Maybe (BoundingBox2d units coordinates)
Construct a bounding box containing all N points in the given list. If the
list is empty, returns Nothing
. If you know you have at least one point, you
can use hull
instead.
hullOf : (a -> Point2d units coordinates) -> a -> List a -> BoundingBox2d units coordinates
Like hull
, but lets you work on any kind of item as long as a
point can be extracted from it. For example, if you had
type alias Vertex =
{ id : Int
, position : Point2d Meters WorldCoordinates
, color : Color
}
then you could get the bounding box around several vertices using
BoundingBox2d.hullOf .position
firstVertex
[ secondVertex
, thirdVertex
, fourthVertex
]
hullOfN : (a -> Point2d units coordinates) -> List a -> Maybe (BoundingBox2d units coordinates)
Combination of hullOf
and hullN
.
Functions for combining several bounding boxes into one bounding box that contains all of the input boxes.
aggregate : BoundingBox2d units coordinates -> List (BoundingBox2d units coordinates) -> BoundingBox2d units coordinates
Find the bounding box containing one or more input boxes; works much like
hull
. See also aggregateN
.
aggregate3 : BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Build a bounding box that contains all three of the given bounding boxes;
BoundingBox2d.aggregate3 b1 b2 b3
is equivalent to
BoundingBox2d.aggregate b1 [ b2, b3 ]
but is more efficient.
aggregateN : List (BoundingBox2d units coordinates) -> Maybe (BoundingBox2d units coordinates)
Construct a bounding box containing all bounding boxes in the given list. If
the list is empty, returns Nothing
. If you know you have at least one bounding
box, you can use aggregate
instead.
aggregateOf : (a -> BoundingBox2d units coordinates) -> a -> List a -> BoundingBox2d units coordinates
Like aggregate
, but lets you work on any kind of item as
long as a bounding box can be extracted from it. For example, to get the
bounding box around four triangles:
BoundingBox2d.aggregateOf Triangle2d.boundingBox
firstTriangle
[ secondTriangle
, thirdTriangle
, fourthTriangle
]
aggregateOfN : (a -> BoundingBox2d units coordinates) -> List a -> Maybe (BoundingBox2d units coordinates)
Combination of aggregateOf
and aggregateN
.
extrema : BoundingBox2d units coordinates -> { minX : Quantity Basics.Float units, maxX : Quantity Basics.Float units, minY : Quantity Basics.Float units, maxY : Quantity Basics.Float units }
Get the minimum and maximum X and Y values of a bounding box in a single record.
BoundingBox2d.extrema exampleBox
--> { minX = Length.meters 3
--> , maxX = Length.meters 8
--> , minY = Length.meters 2
--> , maxY = Length.meters 6
--> }
Can be useful when combined with record destructuring, e.g.
{ minX, maxX, minY, maxY } =
BoundingBox2d.extrema exampleBox
minX : BoundingBox2d units coordinates -> Quantity Basics.Float units
maxX : BoundingBox2d units coordinates -> Quantity Basics.Float units
minY : BoundingBox2d units coordinates -> Quantity Basics.Float units
maxY : BoundingBox2d units coordinates -> Quantity Basics.Float units
dimensions : BoundingBox2d units coordinates -> ( Quantity Basics.Float units, Quantity Basics.Float units )
Get the X and Y dimensions (width and height) of a bounding box.
( width, height ) =
BoundingBox2d.dimensions exampleBox
midX : BoundingBox2d units coordinates -> Quantity Basics.Float units
Get the median (central) X value of a bounding box.
midY : BoundingBox2d units coordinates -> Quantity Basics.Float units
Get the median (central) Y value of a bounding box.
xInterval : BoundingBox2d units coordinates -> Quantity.Interval.Interval Basics.Float units
Get the range of X values contained by a bounding box.
yInterval : BoundingBox2d units coordinates -> Quantity.Interval.Interval Basics.Float units
Get the range of Y values contained by a bounding box.
intervals : BoundingBox2d units coordinates -> ( Quantity.Interval.Interval Basics.Float units, Quantity.Interval.Interval Basics.Float units )
Convert a bounding box to a pair of X and Y intervals.
contains : Point2d units coordinates -> BoundingBox2d units coordinates -> Basics.Bool
Check if a bounding box contains a particular point.
isContainedIn : BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> Basics.Bool
Test if the second given bounding box is fully contained within the first (is a subset of it).
outerBox =
BoundingBox2d.from
(Point2d.meters 0 0)
(Point2d.meters 10 10)
innerBox =
BoundingBox2d.from
(Point2d.meters 1 3)
(Point2d.meters 5 9)
overlappingBox =
BoundingBox2d.from
(Point2d.meters 1 3)
(Point2d.meters 5 12)
innerBox |> BoundingBox2d.isContainedIn outerBox
--> True
overlappingBox |> BoundingBox2d.isContainedIn outerBox
--> False
intersects : BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> Basics.Bool
Test if two boxes touch or overlap at all (have any points in common);
BoundingBox2d.intersects firstBox secondBox
is equivalent to
BoundingBox2d.intersection firstBox secondBox
/= Nothing
but is more efficient.
overlappingByAtLeast : Quantity Basics.Float units -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> Basics.Bool
Check two boxes overlap by at least the given amount. For example, you could implement a tolerant collision check (one that only returns true if the boxes overlap by at least a millimeter, and ignores boxes that just barely touch each other) as
boxesCollide firstBox secondBox =
BoundingBox2d.overlappingByAtLeast
(Length.millimeters 1)
firstBox
secondBox
Overlap is defined as the minimum distance one box would have to move so that it did not touch the other. Boxes that just touch are considered to have an overlap of zero, so
BoundingBox2d.overlappingByAtLeast Quantity.zero
firstBox
secondBox
will return true even if the two boxes just touch each other.
separatedByAtLeast : Quantity Basics.Float units -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates -> Basics.Bool
Check if two boxes are separated by at least the given amount. For example,
to perform clash detection between some objects, you could use separatedBy
on
those objects' bounding boxes as a quick check to see if the objects had a gap
of at least 1 cm between them:
safelySeparated firstBox secondBox =
BoundingBox2d.separatedByAtLeast
(Length.centimeters 1)
firstBox
secondBox
Separation is defined as the minimum distance one box would have to move so that it touched the other. (Note that this may be a diagonal distance between corners.) Boxes that just touch are considered to have a separation of zero, so
BoundingBox2d.separatedByAtLeast Quantity.zero
firstBox
secondBox
will return true even if the two boxes just touch each other.
interpolate : BoundingBox2d units coordinates -> Basics.Float -> Basics.Float -> Point2d units coordinates
Interpolate within a bounding box based on parameter values which range from 0 to 1.
scaleAbout : Point2d units coordinates -> Basics.Float -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Scale a bounding box about a given point by a given scale.
translateBy : Vector2d units coordinates -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Translate a bounding box by a given displacement.
translateIn : Direction2d coordinates -> Quantity Basics.Float units -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Translate a bounding box in a given direction by a given distance.
expandBy : Quantity Basics.Float units -> BoundingBox2d units coordinates -> BoundingBox2d units coordinates
Expand the given bounding box in all directions by the given offset:
BoundingBox2d.expandBy (Length.meters 3) exampleBox
--> BoundingBox2d.fromExtrema
--> { minX = Length.meters 0
--> , maxX = Length.meters 11
--> , minY = Length.meters -1
--> , maxY = Length.meters 9
--> }
Negative offsets will be treated as positive (the absolute value will be used),
so the resulting box will always be at least as large as the original. If you
need to be able to contract a bounding box, use
offsetBy
instead.
offsetBy : Quantity Basics.Float units -> BoundingBox2d units coordinates -> Maybe (BoundingBox2d units coordinates)
Expand or shrink the given bounding box in all the directions by the given distance. A positive offset will cause the bounding box to expand and a negative value will cause it to shrink.
BoundingBox2d.offsetBy (Length.meters -1) exampleBox
--> Just <|
--> BoundingBox2d.fromExtrema
--> { minX = Length.meters 4
--> , maxX = Length.meters 7
--> , minY = Length.meters 3
--> , maxY = Length.meters 5
--> }
Returns Nothing
if the offset is negative and large enough to cause the
bounding box to vanish (that is, if the offset is larger than half the height or
half the width of the bounding box, whichever is less):
BoundingBox2d.offsetBy (Length.meters -3) exampleBox
--> Nothing
If you only want to expand a bounding box, you can use
expandBy
instead (which does not return a Maybe
).
at : Quantity Basics.Float (Quantity.Rate units2 units1) -> BoundingBox2d units1 coordinates -> BoundingBox2d units2 coordinates
Convert a bounding box from one units type to another, by providing a conversion factor given as a rate of change of destination units with respect to source units.
at_ : Quantity Basics.Float (Quantity.Rate units1 units2) -> BoundingBox2d units1 coordinates -> BoundingBox2d units2 coordinates
Convert a bounding box from one units type to another, by providing an 'inverse' conversion factor given as a rate of change of source units with respect to destination units.
randomPoint : BoundingBox2d units coordinates -> Random.Generator (Point2d units coordinates)
Create a random generator for points within a given bounding box.