Clipping with Planes: ClipPlane
, ConvexClipPlaneSet
, UnionOfConvexClipPlaneSets
, ClipPrimitive
, ClipVector
These ClipXXX
types use combinations of infinite 3d planes to execute complex geometry clipping.
These types support methods that classify whether single point is inside some space region, and to split curves, polygons, and meshes into "inside" and "outside" parts.
The following table summarizes the progression of complexity of the successively more complex classes:
Class | Represents |
ClipPlane |
One half of all space as split by a single plane. This is always unbounded. |
ConvexClipPlaneSet |
Intersection of many ClipPlane clips, hence a convex polygon, possibly unbounded |
UnionOfConvexClipPlaneSets |
Union of many ConvexClipPlaneSets , hence able to assemble convex polygons to a non-convex overall shape |
ClipVector |
Intersection of multiple UnionOfConvexClipPlaneSets , with each one optionally inverted so its outer part is considered "inside" the ClipVector result. In common use, one UnionOfConvexClipPlaneSets is a (swept) outer boundary and the others are (with inversion flag set) holes whose outside (due to the inversion flag) is inside the outer polygon. |
ClipPlane
A ClipPlane
is a single plane. All xyz is split into two parts by the plane.
The plane is defined by an (inward) normal vector and a distance from origin. Various construction methods (ClipPlane.createXXX...
) compute the normal and distance from other data.
A ClipPlane
is intrinsically a full 3D plane. It splits infinite xyz space into an "inside" part and an "outside" part. Its property data has a (inward) normal vector and distance from origin, but no bounds or preferred vector directions within its plane.
The ClipPlane
class is used as the leaf-level node of successively larger clipping tree structures ConvexClipPlaneSet
, UnionOfConvexClipPlaneSets
.
THe key operation in clipping is to classify a single point as "above" , "on", or "below" a single ClipPlane
. This is done based on the (signed) distance from the plane to a point. The methods to evaluate this are
clipPlane.altitudeXYZ(x,y,z)
returns altitude at coordinate x,y,zclipPlane.altitude(point: Point3d)
is equivalent toclipPlane.altitude(point.x, point.y, point.z)
The ClipPlane
considers its normal vector to be pointing inward.
See the ClipPlane
class documentation for how altitude is computed using the input x,y,z coordinates and the plane's normal vector and distance from the origin.
Note that there are two other classes for planes with different defining data:
Plane3dByOriginAndUnitNormal
carries (a) a specific point on the plane as origin and (b) a unit normal.- This provides immediate definition of above and below but does not indicate preferred directions for coordinates within the plane.
- Convert this to a ClipPlane with
ClipPlane.createPlane (planeByOriginAndUnitNormal...)
Plane3dByOriginAndVectors
carries an (a) an origin and (b) two vectors that are within the plane.- The two in-plane vectors define directions define "local" uv coordinates.
- The plane normal for "above" and "below" classification is the cross product of the two vectors.
- Convert this to a ClipPlane with `ClipPlane.createNormalAndPoint(planeByVectors.unitNormal (), planeByVectors.origin);
ConvexClipPlaneSet
- A
ConvexClipPlaneSet
is an collection (array) ofClipPlane
. - The inside of the set is the intersection of the insides of all the planes.
- That is, a point in space is considered "inside" the convex set if it is to the inside direction from all of the individual planes.
- The inside of a rectangle in the xy plane is defined by 4
ClipPlane
s.- Note that those 4 planes perpendicular to the xy plane extend to infinity in the z direction, so this
ConvexClipPlaneSet
that we discuss for a rectangle is actually a rectangular prism extending to infinity in both z directions. - Adding additional planes with normals in the positive and negative z direction restricts that infinite prism.
- Note that those 4 planes perpendicular to the xy plane extend to infinity in the z direction, so this
- A
ConvexClipPlaneSet
can be unbounded. For example, ifConvexClipPlaneSet
has two plane which are the xz and yz planes, the interior of the set is (depending on direction of the normals) one of the unbounded 4 quadrants: - A
ConvexClipPlaneSet
can be empty if the normals are aligned to cancel. This is not commonly useful. If it occurs it is probably an error in directions of normals.
A grid of points, with 3 example planes showing points classified as inside (plus) and outside (-) | |
The same grid, clipped to the convex set inside one, then two and then all three of the planes |
Hence three clip planes can give a complete boundary for a triangle.
ConvexClipPlaneSet
can be unbounded
Observe that (in planar examples0)
The inside region for a single clip plane is always unbounded
The inside region for two intersecting clip planes is always unbounded
The inside region for three clip planes can be bounded or unbounded depending on where they intersect. The example below shows 3 planes with an unbounded "inside" region.
Visualizing it
Because of the potentially unbounded interior of things bounded by ClipPlane
or combinations of them, it is not possible to produce a typical piece of geometry that "is" the entire interior. The best that can be done is to create something bounded, clip it to the clip set.
The central snip of the 3x3 grid below shows xy axes and an octagon (). Each of the other 8 have the octagon clipped by a clip plane set with 1 or 2 planes, using the method
const work = new GrowableXYZArray();
clipper.clipConvexPolygonInPlace(polygonToClip, work);
The left column clips all have a "negative Y" half space. Right column has "positive Y". Lower and upper have (respectively) "negative X" and "positive X" half spaces in their ConvexClipPlaneSet
UnionOfConvexClipPlaneSets
If a polygon is non-convex, no single ConvexClipPlaneSet can exactly cover its interior. It has to be broken into convex pieces, and those in turn bundled into a UnionOfConvexClipPlaneSets
.
The snip below shows
- (left) a non-convex polygon
- (middle) 6 ConvexClipPlaneSets, each clipping a triangle from the interior.
- (right) 3 ConvexClipPlaneSets, each clipping a quad from the interior.
Display-Time Clip Usage
The display subsystem uses clipping that is a limit tree of boolean operations:
- The overall clipper (root of boolean tree) is a
ClipVector
instance. - The root
ClipVector
is an array ofClipPrimitive
.- The boolean in the root
ClipVector
is an intersection among theClipPrimitives
in the array
- The boolean in the root
- Each
ClipPrimitive
has (just) oneUnionOfConvexCipPlaneSets
. AClipVector
requests theUnionOfConvexClipPlaneSets
from theClipPrimitive
.- The clipping abilities of each
ClipPrimitive
are thus no more complex than itsUnionOfComplexClipPlaneSets
.- Some
ClipPrimitives
are "nothing but" carriers for theirUnionOfComplexClipPlaneSets
- A
ClipShape
carries a defining polygon to be swept and optionally capped by front and back planes.
- Some
- The clipping abilities of each
(As noted in a previous paragraph, the ConvexClipPlaneSets cannot be directly "drawn". The displayed shapes are the result of using the clipper to clip a larger polygon.)
Clipper
interfaces for clip requests
The Clipper
interface defines methods for clip operations. Note that these are fairly low-level. The are expected to be called by intermediate API methods that fit their detail operations into larger scale API.
The point, line, and arc methods are required.
The polygon method is optional. This allows a curved-surface clipper (e.g. an ellipsoid) to implement point and curve clipping but not polygon clipping (which cannot produce exact match clip curves as polygonal output).
Interface | method | Remarks |
---|---|---|
Clipper | isPointOnOrInside(point: Point3d, tolerance?: number): boolean; |
Test if point is inside or on |
Clipper | announceClippedSegmentIntervals(f0: number, f1: number, pointA: Point3d, pointB: Point3d, announce?: AnnounceNumberNumber): boolean; |
compare a line segment to the clipper. Issue function calls with fractional intervals that are "in" |
Clipper | announceClippedArcIntervals(arc: Arc3d, announce?: AnnounceNumberNumberCurvePrimitive): boolean; |
compare an arc to the clipper. Announce intervals that are in. |
PolygonClipper (Optional in Clipper) | appendPolygonClip(xyz: GrowableXYZArray, insideFragments: GrowableXYZArray[], outsideFragments: GrowableXYZArray[], arrayCache: GrowableXYZArrayCache): void; |
Clip a single polygon, emitting inside and outside pieces into indicated arrays. |
BooleanClipNode
A BooleanClipNode
contains an array of objects that implement the Clipper
interface, and implements the Clipper
interface itself. Hence arbitrary trees of booleans can be described.
This allows completely general boolean trees to be constructed (strictly within typescript, apart from the limited display system clipping) with instances of the abstract class BooleanClipNode
as interior nodes.
The tree structure is created by statics in the BooleanClipFactory
class:
method | boolean expression |
---|---|
createCaptureUnion (children) | union over (array of) children |
createCaptureIntersection (children) | intersection among (array of) children |
createCaptureParity (children) | parity among (array of) children |
createCaptureDifference (primary, secondary) | inside primary and outside secondary |
createCaptureOutside (clipper) | negation of clipper |
There are 3 concrete classes:
- BooleanClipNodeUnion -- union (boolean OR) operation among its members, with optional inversion of the result (boolean NOR)
- BooleanClipNodeIntersection -- intersection (boolean AND) operation among its members, with optional inversion of the result (boolean NAND)
- BooleanClipNodeParity -- parity (boolean XOR) among its members, with optional inversion of the result.
Last Updated: 15 May, 2024