RasmahRasmah

Half-Space Slicing and Polyhedral BReps

using Rasmah
using LinearAlgebra

Overview

Boolean operations answer “what is the union/intersection/difference of two solids?”; a half-space slice answers the simpler, cheaper question “what is the part of this solid on one side of a plane?” — the question behind the drawing office’s section views: cut a part with a plane and look at the cross-section. clip_half_space does this exactly for polyhedral (planar-faced) BReps — no tessellation — and convex_polyhedron_sdf turns the result back into a fast, exact signed field.

This tutorial draws a standard machined part — a 60 × 40 × 30 mm spacer block with a 3 × 3 chamfer along one edge — entirely by clipping, and takes its section views. Dimensions are millimetres.

The chamfer: a half-space cut

A chamfer is the classic half-space feature: the machinist sets the block at 45° and cuts off the corner. clip_half_space(b, n, offset) keeps the part of b where n·x ≤ offset (the normal is normalised for you), so the plane x + z = 42 trims the top-right corner of the block to a 3 mm chamfer:

block = brep(box(60.0, 40.0, 30.0))

chamfered = clip_half_space(block, [1.0, 0.0, 1.0], 42.0 / √2)   # x + z ≤ 42

println("faces: ", length(chamfered.faces), ", watertight: ", is_watertight(chamfered))
faces: 7, watertight: true
vtk(chamfered)

The cut removes a triangular prism of cross-section 3 × 3 / 2 along the whole 40 mm edge — and the clipped BRep volume is that number, exactly:

vm = volume(surface_mesh(chamfered; n = 64))
println("clipped volume = ", round(vm; digits = 1), " mm³")
println("analytic       = ", round(60.0 * 40.0 * 30.0 - 3.0 * 3.0 / 2 * 40.0; digits = 1), " mm³")
clipped volume = 71820.0 m^3 mm³
analytic       = 71820.0 mm³

The section view

A drawing shows the inside of a part by cutting it. The vertical plane x = 0 splits the block down its middle — keep one half:

sec = clip_half_space(chamfered, [1.0, 0.0, 0.0], 0.0)          # keep x ≤ 0
println("section faces: ", length(sec.faces), ", watertight: ", is_watertight(sec))
println("section volume = ", round(volume(surface_mesh(sec; n = 64)); digits = 1),
        " mm³   (half of 72000; the chamfer lies on the removed side)")
section faces: 6, watertight: true
section volume = 36000.0 m^3 mm³   (half of 72000; the chamfer lies on the removed side)
vtk(sec)

Slicing is repeatable: cut the half again with the horizontal plane z = 0 to isolate the bottom quarter:

quarter = clip_half_space(sec, [0.0, 0.0, 1.0], 0.0)            # keep z ≤ 0
println("quarter volume = ", round(volume(surface_mesh(quarter; n = 64)); digits = 1), " mm³")
quarter volume = 18000.0 m^3 mm³

Every cut stitches a planar cap onto the slicing plane, so each result stays a closed, consistently-oriented solid — the exact polyhedron a drawing’s section view depicts.

The fast polyhedral field

convex_polyhedron_sdf builds a PlanarBRepField — an ImplicitGeometry that evaluates each shell as the intersection of its face half-spaces (max of the signed plane distances) and the whole solid as the union of its shells (min across shells). It carries finite bounds, so translate/rotate/dilate, field, and marching cubes all work on it without meshing the BRep:

g = convex_polyhedron_sdf(sec)

println("inside the section:   ", g([-10.0, 0.0, 10.0]))    # negative
println("outside the section:  ", g([10.0, 0.0, 10.0]))     # positive
println("on the section plane: ", g([0.0, 0.0, 10.0]))      # ≈ 0
println("bounds:               ", bounds(g))
inside the section:   -5.0
outside the section:  10.0
on the section plane: 0.0
bounds:               ([-30.0, -20.0, -15.0], [0.0, 20.0, 15.0])

Because the field is exact, the SDF-sampled surface matches the exact BRep (to the marching resolution) — the cheap, gradient-friendly proxy the assembly optimizer uses for the fragmented solid:

m = surface_mesh(g; n = 64)
println("marched volume: ", round(volume(m); digits = 1))   # → 36000 as n grows
marched volume: 35998.5 m^3

Faceted BRep: mesh → BRep

The reverse conversion — a triangle mesh back into an exact (planar-faceted) BRep — is faceted_brep. It welds coincident vertices and shared edges, so a closed, oriented surface mesh becomes a watertight, consistently-oriented solid that flows through the rest of the kernel (STEP export, booleans, is_watertight):

fb = faceted_brep(sphere(1.0); n = 24)      # also brep(::TriangleMesh)

println("faceted faces:     ", length(fb.faces))
println("faceted watertight: ", is_watertight(fb))
faceted faces:     1280
faceted watertight: true

Summary

  • clip_half_space slices a polyhedral BRep exactly (no tessellation), sewing a planar cap onto the cut — the section-view and chamfer primitive.
  • convex_polyhedron_sdf turns a planar BRep into a fast exact signed field with finite bounds.
  • faceted_brep turns any triangle mesh back into a watertight planar BRep.

Together they close the loop between exact polyhedra and the SDF/mesh pipeline.

Next steps