bspy.solid
A portion of the boundary of a solid.
Parameters
- manifold (
Manifold
): The differentiable function whose range is one dimension higher than its domain that defines the range of the boundary. - domain (
Solid
, optional): The region of the domain of the manifold that's within the boundary. The default is the full domain of the manifold.
See also
Solid
: A region that separates space into an inside and outside, defined by a collection of boundaries.
Manifold.full_domain
: Return a solid that represents the full domain of the manifold.
Return an arbitrary point on the boundary.
Returns
- point (
numpy.array
): A point on the boundary.
See Also
Solid.any_point
: Return an arbitrary point on the solid.
Notes
The point is computed by evaluating the boundary manifold by an arbitrary point in the domain of the boundary.
A region that separates space into an inside and outside, defined by a collection of boundaries.
Parameters
- dimension (
int
): The dimension of the solid (non-negative). - containsInfinity (
bool
): Indicates whether or not the solid contains infinity. - metadata (
dict
, optional): A dictionary of ancillary data to store with the solid. Default is {}.
See also
Boundary
: A portion of the boundary of a solid.
Notes
Solids also contain a list
of boundaries
. That list may be empty.
Solids can be of zero dimension, typically acting as the domain of boundary endpoints. Zero-dimension solids have no boundaries, they only contain infinity or not.
Adds a boundary to a solid, recomputing the solid's bounds.
Parameters
- boundary (
Boundary
): A boundary to add to the solid.
Notes
While you could just append the boundary to the solid's collection of boundaries, this method also recomputes the solid's bounds for faster intersection and containment operations. Adding the boundary directly to the solid's boundaries collection may result in faulty operations.
Return an arbitrary point on the solid.
Returns
- point (
numpy.array
): A point on the solid.
See Also
Boundary.any_point
: Return an arbitrary point on the boundary.
Notes
The point is computed by calling Boundary.any_point
on the solid's first boundary.
If the solid has no boundaries but contains infinity, any_point
returns the origin.
If the solid has no boundaries and doesn't contain infinity, any_point
returns None
.
Return the complement of the solid: whatever was inside is outside and vice-versa.
Returns
- solid (
Solid
): The complement of the solid.
See Also
intersection
: Intersect two solids.
union
: Union two solids.
difference
: Subtract one solid from another.
Test if a point lies within the solid.
Parameters
- point (array-like): A point that may lie within the solid.
Returns
- containment (
bool
):True
ifpoint
lies within the solid.False
otherwise.
See Also
winding_number
: Compute the winding number for a point relative to the solid.
Notes
A point is considered contained if it's on the boundary of the solid or it's winding number is greater than 0.5.
Returns whether or not bounds1 and bounds2 are disjoint.
Parameters
- bounds1 (array-like or
None
): An array with shape (dimension, 2) of lower and upper and lower bounds on each dimension. If bounds1 isNone
then then there are no bounds. - bounds2 (array-like or
None
): An array with shape (dimension, 2) of lower and upper and lower bounds on each dimension. If bounds2 isNone
then then there are no bounds.
Returns
- disjoint (
bool
): Value is true if the bounds are disjoint. Value is false if either bounds isNone
.
Intersect two solids.
Parameters
- other (
Solid
): TheSolid
intersecting self. - cache (
dict
, optional): A dictionary to cacheManifold
intersections, speeding computation. If no dictionary is passed, one is created.
Returns
See Also
slice
: Slice a solid by a manifold.
union
: Union two solids.
difference
: Subtract one solid from another.
Notes
To intersect two solids, we slice each solid with the boundaries of the other solid. The slices are the region of the domain that intersect the solid. We then intersect the domain of each boundary with its slice of the other solid. Thus, the intersection of two solids becomes a set of intersections within the domains of their boundaries. This recursion continues until we are intersecting points whose domains have no boundaries.
The only subtlety is when two boundaries are coincident. To avoid overlapping the coincident region, we keep that region for one slice and trim it away for the other. We use a manifold intersection cache to keep track of these pairs, as well as to reduce computation.
Test if the solid is empty.
Returns
- isEmpty (
bool
):True
if the solid is empty,False
otherwise.
Notes
Casting the solid to bool
returns not is_empty
.
Load solids and/or manifolds in json format from the specified filename (full path).
Parameters
- fileName (
string
): The full path to the file containing the solids and/or manifolds. Can be a relative path.
Returns
- solidsAndManifolds (list of
Solid
and/orManifold
): The loaded solids and/or manifolds.
See Also
save
: Save a solids and/or manifolds in json format to the specified filename (full path).
Returns whether or not point is outside bounds.
Parameters
- point (array-like): A point whose dimension matches the bounds.
- bounds (array-like or
None
): An array with shape (dimension, 2) of lower and upper and lower bounds on each dimension. If bounds isNone
then then there are no bounds.
Returns
- within (
bool
): Value is true if the point is outside bounds or bounds isNone
.
Save a solids and/or manifolds in json format to the specified filename (full path).
Parameters
- fileName (
string
): The full path to the file containing the solids and/or manifolds. Can be a relative path. - *solids_or_manifolds (
Solid
orManifold
): Solids and/or manifolds to save in the same file.
See Also
load
: Load solids and/or manifolds in json format from the specified filename (full path).
Slice the solid by a manifold.
Parameters
- manifold (
Manifold
): TheManifold
used to slice the solid. - cache (
dict
, optional): A dictionary to cacheManifold
intersections, speeding computation. - trimTwin :
bool
, default (False): Trim coincident boundary twins on subsequent calls to slice (avoids duplication of overlapping regions). Trimming twins is typically only used in conjunction withintersection
.
Returns
- slice (
Solid
): A region in the domain ofmanifold
that intersects with the solid. The region may contain infinity.
See Also
intersection
: Intersect two solids.
Manifold.intersect
: Intersect two manifolds.
Manifold.cached_intersect
: Intersect two manifolds, caching the result for twins.
Manifold.complete_slice
: Add any missing inherent (implicit) boundaries of this manifold's domain to the given slice.
Notes
The dimension of the slice is always one less than the dimension of the solid, since the slice is a region in the domain of the manifold slicing the solid.
To compute the slice of a manifold intersecting the solid, we intersect the manifold with each boundary of the solid. There may be multiple intersections between the manifold and the boundary. Each is either a crossing or a coincident region.
Crossings result in two intersection manifolds: one in the domain of the manifold and one in the domain of the boundary. By construction, both intersection manifolds have the same domain and the same range of the manifold and boundary (the crossing itself). The intersection manifold in the domain of the manifold becomes a boundary of the slice, but we must determine the intersection's domain. For that, we slice the boundary's intersection manifold with the boundary's domain. This recursion continues until the slice is just a point with no domain.
Coincident regions appear in the domains of the manifold and the boundary. We intersect the boundary's coincident region with the domain of the boundary and then map it to the domain of the manifold. If the coincident regions have normals in opposite directions, they cancel each other out, so we subtract them from the slice by inverting the region and intersecting it with the slice. We use this same technique for removing overlapping coincident regions. If the coincident regions have normals in the same direction, we union them with the slice.
Compute the surface integral of a vector field on the boundary of the solid.
Parameters
- **f : python function
f(point: numpy.array, normal: numpy.array, args** (user-defined) -> numpy.array
): The vector field to be integrated on the boundary of the solid. It's passed a point on the boundary and its corresponding outward-pointing unit normal, as well as any optional user-defined arguments. - args (tuple, optional):
Extra arguments to pass to
f
. - *quadArgs (Quadrature arguments passed to
scipy.integrate.quad
.):
Returns
- sum (scalar value): The value of the surface integral.
See Also
volume_integral
: Compute the volume integral of a function within the solid.
scipy.integrate.quad
: Integrate func from a to b (possibly infinite interval) using a technique from the Fortran library QUADPACK.
Notes
To compute the surface integral of a scalar function on the boundary, have f
return the product of the normal
times the scalar function for the point
.
surface_integral
sums the volume_integral
over the domain of the solid's boundaries, using the integrand: numpy.dot(f(point, normal), normal)
,
where normal
is the cross-product of the boundary tangents (the normal before normalization).
Transform the range of the solid.
Parameters
- matrix (
numpy.array
): A square matrix transformation. - matrixInverseTranspose (
numpy.array
, optional): The inverse transpose of matrix (computed if not provided).
Returns
- solid (
Solid
): The transformed solid.
Translate the range of the solid.
Parameters
- delta (
numpy.array
): A 1D array translation.
Returns
- solid (
Solid
): The translated solid.
Union two solids.
Parameters
Returns
See Also
intersection
: Intersect two solids.
difference
: Subtract one solid from another.
Compute the volume integral of a function within the solid.
Parameters
- **f : python function
f(point: numpy.array, args** (user-defined) -> scalar value
): The function to be integrated within the solid. It's passed a point within the solid, as well as any optional user-defined arguments. - args (tuple, optional):
Extra arguments to pass to
f
. - *quadArgs (Quadrature arguments passed to
scipy.integrate.quad
.):
Returns
- sum (scalar value): The value of the volume integral.
See Also
surface_integral
: Compute the surface integral of a vector field on the boundary of the solid.
scipy.integrate.quad
: Integrate func from a to b (possibly infinite interval) using a technique from the Fortran library QUADPACK.
Notes
The volume integral is computed by recursive application of the divergence theorem: volume_integral(divergence(F)) = surface_integral(dot(F, n))
,
where F
is a vector field and n
is the outward boundary unit normal.
Let F = [Integral(f) from x0 to x holding other coordinates fixed, 0..0]
. divergence(F) = f
by construction, and dot(F, n) = Integral(f) * n[0]
.
Note that the choice of x0
is arbitrary as long as it's in the domain of f and doesn't change across all surface integral boundaries.
Thus, we have volume_integral(f) = surface_integral(Integral(f) * n[0])
.
The outward boundary unit normal, n
, is the cross product of the boundary manifold's tangent space divided by its length.
The surface differential, dS
, is the length of cross product of the boundary manifold's tangent space times the differentials of the manifold's domain variables.
The length of the cross product appears in the numerator and denominator of the surface integral and cancels.
What's left multiplying Integral(f)
is the first coordinate of the cross product plus the domain differentials (volume integral).
The first coordinate of the cross product of the boundary manifold's tangent space is the first cofactor of the tangent space.
And so, surface_integral(Integral(f) * n[0]) = volume_integral(Integral(f) * first cofactor)
over each boundary manifold's domain.
So, we have volume_integral(f) = volume_integral(Integral(f) * first cofactor)
over each boundary manifold's domain.
To compute the volume integral we sum volume_integral
over the domain of the solid's boundaries, using the integrand:
scipy.integrate.quad(f, x0, x [other coordinates fixed]) * first cofactor
.
This recursion continues until the boundaries are only points, where we can just sum the integrand.
Compute the winding number for a point relative to the solid.
Parameters
- point (array-like): A point that may lie within the solid.
Returns
- windingNumber (scalar value):
The
windingNumber
is 0 if the point is outside the solid, 1 if it's inside. Other values indicate issues:- A point on the boundary leads to an undefined (random) winding number;
- Boundaries with gaps or overlaps lead to fractional winding numbers;
- Interior-pointing normals lead to negative winding numbers;
- Nested shells lead to winding numbers with absolute value 2 or greater.
- onBoundaryNormal (
numpy.array
): The boundary normal if the point lies on a boundary,None
otherwise.
See Also
contains_point
: Test if a point lies within the solid.
Notes
If onBoundaryNormal
is not None
, windingNumber
is undefined and should be ignored.
winding_number
uses two different implementations:
- A simple fast implementation if the solid is a number line (dimension <= 1). This is the default for dimension <= 1.
- A surface integral with integrand:
(x - point) / norm(x - point)**dimension
.