2D or Not 2D
January 28, 2026Tonight I shipped cylinder booleans. Subtracting cylinders from boxes to make holes. Classic CAD operation.
The problem: a cylinder intersects a cube's face along a circle. Circles in 3D are annoying. No start, no end, curving through space. Splitting a planar face along one means handling the inner disk separately from the outer ring.
The answer is UV space.
parameter space
Every surface in CAD has a 2D coordinate system that maps to points on the 3D surface. We call the coordinates and because , , are taken.
A cylinder is just a rectangle that got rolled up:
- goes around the circumference ( to )
- goes up the height
The surface equation:
Plug in any , get the exact 3D point. No triangles, no approximation.
circles become lines
When a horizontal plane cuts through a vertical cylinder, the intersection is a circle in 3D. In UV space? A horizontal line.
The circle sits at constant height . Height is the coordinate. So the circle becomes .
A 3D circle became a 1D line. That's the trick.
splitting a cylinder
When you subtract a cylinder from a cube, you keep only the part inside the cube. That means splitting at the top and bottom faces.
Say the cube spans z=0 to z=20. The cylinder runs from z=-5 to z=25, poking out both ends. Intersection circles at z=0 and z=20.
In UV space:
- Original cylinder: rectangle
- Split at and
- Three strips:
- Bottom [-5, 0]: outside cube, discard
- Middle [0, 20]: inside cube, keep for hole wall
- Top [20, 25]: outside cube, discard
"Split a curved 3D surface along two circles" became "draw two horizontal lines on a rectangle."
uv domains
Every curved surface has one:
| Surface | domain | domain |
|---|---|---|
| Cylinder | angle | height |
| Sphere | longitude | latitude |
| Cone | angle | distance from apex |
| Torus | major | minor |
The domain shape tells you about topology. A cylinder's UV domain is an open rectangle, infinite in . A torus is closed, both and wrap around.
why bother
When building a CAD kernel, you have two choices:
- Work in 3D: intersect surfaces, trace curves through space, handle every edge case
- Work in UV: map to 2D, solve easy problems, map back
Option 2 wins.
Surface-surface intersection? Sample the curve, convert to UV, get a 2D curve on each surface. Trim curves? 2D paths. Face splitting? 2D polygon operations.
Complex 3D geometry becomes simple 2D geometry.
the code
Here's cylinder splitting in vcad:
pub fn split_cylindrical_face_by_circle(
brep: &mut BRepSolid,
face_id: FaceId,
circle: &Circle3d,
) -> SplitResult {
let cyl = get_cylinder_surface(brep, face_id);
// Convert circle to UV: just compute v coordinate
let v_split = (circle.center - cyl.center).dot(&cyl.axis);
let (v_min, v_max) = get_face_v_bounds(brep, face_id);
// Lower: [0, 2π] × [v_min, v_split]
// Upper: [0, 2π] × [v_split, v_max]
create_split_faces(brep, face_id, v_split, v_min, v_max)
}Fifteen lines. No 3D circle math. Project to v, split the rectangle.
the seam
One gotcha: cylinders have a seam. The UV rectangle's left and right edges ( and ) are the same line in 3D.
When a split curve crosses the seam, it wraps around. Phase 2 handles this. For perpendicular cuts like drilling holes, the intersection is a complete circle that doesn't cross the seam, so we're fine.
what's next
Cylinder booleans unlocked:
- Drilling vertical holes through horizontal plates (done)
- Drilling angled holes (elliptical intersections)
- Cylinder-cylinder intersections (pipe joints)
- General curved surface booleans
Same pattern every time: map to UV, solve in 2D, map back.
Once you see surfaces as 2D domains, everything clicks.
Related:
