Skip to content

Faces & Edges

Every body in llmcad has named faces and accessible edges. This is the core feature that makes llmcad LLM-friendly --- geometry is referenced by name, not by index or coordinate.

FaceRef

A FaceRef is a named reference to a face on a body. It carries:

  • A name ("top", "front", "wall", ...)
  • A local coordinate system (x_dir, y_dir, normal)
  • Access to edges, corners, and geometric properties

Accessing faces

Standard faces are available as properties:

box = Box(100, 60, 10)

box.top       # +Z face
box.bottom    # -Z face
box.front     # -Y face
box.back      # +Y face
box.right     # +X face
box.left      # -X face

Custom faces are accessed through the faces collection:

box.faces["top"]        # same as box.top
box.faces["_end"]       # end cap of an extrusion
box.faces["wall"]       # cylindrical face

Face properties

face = box.top

face.name       # "top"
face.normal     # Vec3(0, 0, 1) — outward normal
face.center     # Position at face center
face.width      # extent along local x_dir
face.height     # extent along local y_dir
face.area       # surface area in mm^2

Face edges

Every face provides edge access:

face.edges         # all edges of this face
face.left_edge     # leftmost edge (in face-local coords)
face.right_edge    # rightmost edge
face.top_edge      # topmost edge
face.bottom_edge   # bottommost edge
face.long_edges    # the longest edges

These directional edges use the face's local coordinate system, so left_edge is always "left on this face" regardless of the face's orientation in 3D space.

Face corners

Rectangular faces have named corners:

face.corners.TL   # top-left Position
face.corners.TR   # top-right Position
face.corners.BL   # bottom-left Position
face.corners.BR   # bottom-right Position

Corners are Position objects, so they support offsets and insets:

# Place a hole near the top-right corner
pos = box.top.corners.TR.inset(dx=10, dy=10)
hole = extrude(Circle(5).place_on(box.top, at=pos), through=True)

You can iterate over all corners:

for corner in box.top.corners:
    pos = corner.inset(dx=8, dy=8)
    hole = extrude(Circle(4).place_on(box.top, at=pos), through=True)
    box = box - hole

EdgeRef

An EdgeRef wraps a single edge with convenient accessors.

Edge properties

edge = box.top.left_edge

edge.midpoint   # Position at the edge center
edge.length     # edge length in mm
edge.start      # Position at start of edge
edge.end        # Position at end of edge

Combining edges

Edges can be combined with + for passing to fillet() or chamfer():

edges = box.top.left_edge + box.top.right_edge
box = fillet(edges, radius=3)

Automatic face naming

Box faces

Boxes automatically get top, bottom, front, back, left, right based on face normals.

Cylinder faces

Cylinders get top, bottom, wall.

Sphere faces

Spheres get surface.

Extrusion faces

After extrude():

  • _start --- face at the extrusion origin
  • _end --- face at the extrusion tip
  • wall --- cylindrical wall (for circular sketches)
  • Standard faces (top, bottom, etc.) are assigned if they match cardinal normals

After booleans

Face names are automatically transferred through boolean operations using geometric matching. The algorithm matches faces by center position and normal direction.

FaceCollection

body.faces returns a FaceCollection with dict-like access:

body.faces["top"]          # by name
"top" in body.faces        # membership test
body.faces.start           # shortcut for body.faces["_start"]
body.faces.end             # shortcut for body.faces["_end"]

# Filter faces by normal direction
z_faces = body.faces.filter(normal="Z")  # faces with normal along Z axis

EdgeCollection

body.edges returns an EdgeCollection:

all_edges = body.edges.all()             # list of all EdgeRef
z_edges = body.edges.filter(axis="Z")    # edges parallel to Z axis
x_edges = body.edges.filter(axis="X")    # edges parallel to X axis

Finding shared edges

# Edge between two faces
edge = body.edge_between(body.top, body.front)

# Edges where two bodies meet (after union)
plate = plate + boss
edges = plate.edges_between(boss)