boolean - Boolean cut/intersect/stitch meshes

This modules provide boolean operations for all kind of meshes.

Strictly speaking, boolean operations are operations on mathematically defined sets. In a n-dimensinal space it applies to subsets of point of the same dimension (volumes in 3D, surfaces in 2D). Here, volumes are defined by their exterior envelope (a Mesh) and surfaces by their contour (Web). So the boolean operation consist of intersecting the envelopes and contours and decide which cutted parts to keep.

../_images/boolean-principle.svg

This relies on a new intersection algorithm named syandana. It finds candidates for intersections using a spacial hashing of triangles over a voxel (see madcad.hashing). This is solving the problem of putting triangles in an octree. Also to avoid the increasing complexity of the operation with flat planes divided in multiple parallel triangles, the algorithm is implemented with a detection of ngons.

The syandana algorithm achieves intersections of meshes in nearly O(n) where usual methods are O(n**2)

After intersection, the selection of surface sides to keep or not is done through a propagation.

Note

Web boolean operations theoretically means something only in a 2d space, and it takes place in a 3d space here, causing many situations where a web portion can be both inside and outside the contour. The algorithm here works even with meshes that are not planar at all. but it always assumes that the web you give it can be processed consistently as if it was.

Note

Mesh boolean operation is relying on the surface outer normal to decide what is exterior and interior. It’s always a local decision (ie. a decision made at the intersection), So the meshes must be well oriented.

Web boolean operations on the other hand CANNOT be local due to the 3d space it’s laying in. (a higher dimension than the surface it’s theoreticall meant to outline). So it may sometimes not behave the way you expect it. To solve ambiguities of interior and exterior, the most outer part of first mesh argument is always considered to belong to the exterior. And the information is propagated through the web.

Most common

pierce(m, ref, side=False, prec=None)[source]

Cut a web/mesh and remove its parts considered inside the ref shape

Overloads:

pierce(Mesh, Mesh) -> Mesh
pierce(Web, Web) -> Web
pierce(Web, Mesh) -> Web

side decides which part of each mesh to keep

  • False keeps the exterior part (part exclusive to the other mesh)
  • True keeps the interior part (the common part)

Between Web (The result is the white part, the green part is the ref parameter)

rect = web(
    wire([vec3(-w, -h, 0), vec3(w, -h, 0), vec3(w, h, 0), vec3(-w, h, 0)])
    .close()
    .segmented()
)
hole = web(Circle((O, Z), 1.5))

result = pierce(rect, hole)
../_images/pierce-web-before.png
../_images/pierce-web.png

Between Web and Mesh:

rect = web(
    wire([vec3(-w, -h, 0), vec3(w, -h, 0), vec3(w, h, 0), vec3(-w, h, 0)])
    .close()
    .segmented()
)
hole = extrusion(4 * Z, Circle((O, Z), 1.5), alignment=0.5)

result = pierce(rect, hole)
../_images/pierce-web-mesh-before.png
../_images/pierce-web-mesh.png

Between Mesh:

rect = flatsurface(
    wire([vec3(-w, -h, 0), vec3(w, -h, 0), vec3(w, h, 0), vec3(-w, h, 0)])
)
rect = rect.transform(Z) + rect.transform(-Z).flip()
hole = extrusion(4 * Z, flatsurface(Circle((O, Z), 1.5)).flip(), alignment=0.5)

result = pierce(rect, hole)
../_images/pierce-mesh-before.png
../_images/pierce-mesh.png
boolean(a, b, sides=(False, True), prec=None)[source]

Cut two web/mesh and keep its interior or exterior parts

Overloads:

boolean(Mesh, Mesh) -> Mesh
boolean(Web, Web) -> Web

sides decides which part of each mesh to keep

  • False keeps the exterior part (part exclusive to the other mesh)
  • True keeps the common part

Between Web:

w, h = 2, 1
a = web(
    wire([vec3(-w, -h, 0), vec3(w, -h, 0), vec3(w, h, 0), vec3(-w, h, 0)])
    .close()
    .segmented()
)
b = web(Circle((O, Z), 1.5))

result = boolean(a, b, (False, False))
../_images/boolean-boolean-web-before.png
../_images/boolean-boolean-web.png

Between Mesh:

w, h = 3, 2
rect = flatsurface(
    wire([vec3(-w, -h, 0), vec3(w, -h, 0), vec3(w, h, 0), vec3(-w, h, 0)])
)
a = rect.transform(Z) + rect.transform(-Z).flip()
b = extrusion(4 * Z, flatsurface(Circle((O, Z), 1.5)).flip(), alignment=0.5)

result = boolean(a, b, (False, False))
../_images/boolean-boolean-mesh-before.png
../_images/boolean-boolean-mesh.png

Those are shortcuts for boolean:

union(a, b) Mesh[source]

Return a mesh for the union of the volumes. It is a boolean with selector (False,False)

../_images/boolean-union.png
difference(a, b) Mesh[source]

Return a mesh for the volume of a less the common volume with b It is a boolean with selector (False, True)

../_images/boolean-difference.png
intersection(a, b) Mesh[source]

Return a mesh for the common volume. It is a boolean with selector (True, True)

../_images/boolean-intersection.png

More advanced

cut_web(w1: Web, ref: Web, prec=None)[source]

Cut the web edges at their intersectsions with the ref web

Returns:(cutted, frontier)
cutted(Web):is w1 with the edges cutted, but representing the same lines as before
frontier(Wire):is a Web where edges are the intersection edges, and whose groups are the causing faces in ref
../_images/boolean-cut-web-before.png
../_images/boolean-cut-web.png
cut_web_mesh(w1: Web, ref: Mesh, prec=None)[source]

Cut the web inplace at its intersections with the ref web.

Returns:(cutted, frontier)
../_images/boolean-cut-web-mesh-before.png
../_images/boolean-cut-web-mesh.png
cut_mesh(m1, m2, prec=None)[source]

Cut m1 faces at their intersections with m2.

Returns:(cutted, frontier)
cutted(Mesh):is m1 with the faces cutted, but representing the same surface as before
frontier(Web):is a Web where edges are the intersection edges, and whose groups are the causing faces in m2

Returning the intersection edges in m1 and associated m2 faces. The algorithm is using ngon intersections and retriangulation, in order to avoid infinite loops and intermediate triangles.

../_images/boolean-cut-mesh-before.png ../_images/boolean-cut-mesh.png