The rules become parameters
Draft, wall thickness and rib proportion stop being habits and become variables at the top of the file. Change the wall and every rib that depends on it follows.
CadQuery is a Python library for building exact solid models. It runs on the same OpenCascade geometry kernel as FreeCAD, and it exports STEP, the file a mold shop starts from. Because the part is code, the rules that make it moldable can be named parameters and checks that run on every build, not things someone has to remember.
Draft, wall thickness and rib proportion stop being habits and become variables at the top of the file. Change the wall and every rib that depends on it follows.
Fourteen lines of Python can flag a wall with no draft each time the model regenerates, long before a toolmaker has to point it out.
Assistants write CadQuery well, because it is ordinary Python. What they cannot do is know whether the part will mold. A check that fails loudly gives the assistant something to correct against, the same way a failing test does for software.
A small open housing, pulled straight out of the mold along Z. Each molding rule is a parameter, and each parameter is used exactly where it applies. The rib is drawn the obvious way, with no draft, which is the mistake the check below is there to catch.
import cadquery as cq
# Design intent: every molding rule is a named parameter.
L, W, H = 60.0, 40.0, 18.0 # outside size, mm
WALL = 2.0 # nominal wall
DRAFT = 1.5 # degrees, on every wall that runs along the pull
R = 4.0 # outside corner radius in plan
RIB = 0.6 * WALL # rule of thumb: a rib about 60% of the wall it stands on
outline = cq.Sketch().rect(L, W).vertices().fillet(R)
body = (
cq.Workplane("XY")
.placeSketch(outline)
.extrude(H, taper=DRAFT) # the taper is the draft
.faces(">Z")
.shell(-WALL) # open at the top, WALL thick everywhere else
)
# A stiffening rib across the floor, drawn the obvious way: no draft.
rib = (
cq.Workplane("XY")
.workplane(offset=WALL)
.rect(L - WALL, RIB) # long enough to run into both walls
.extrude(H * 0.6)
)
part = body.union(rib)Every wall that runs along the direction the mold opens needs a slight taper, or the part drags on the steel as it is ejected. In CadQuery that is the taper argument of extrude, in degrees. Around one degree is a common starting point on a polished wall; a textured wall needs more, and the texture supplier states how much.
Plastic shrinks as it cools, and a thick section cools last and shrinks most, which is where sink marks and warp come from. Shelling the solid gives one nominal wall everywhere, set by one parameter.
A rib thicker than the wall it stands on leaves a sink mark on the far face. Writing the rib as a fraction of WALL, about 60% by the usual rule of thumb, keeps the two in step when the wall changes.
Filleting the outline before extruding rounds the corners in plan, which helps the plastic flow and the steel last. Rounding the sketch rather than the solid also keeps the fillet from fighting the taper.
The check looks at every face and measures how far it leans away from the pull direction. A wall that runs exactly along the pull has zero draft; the floor and the rim, square to the pull, read ninety degrees and pass.
import math
import cadquery as cq
def draft_report(shape, pull=(0, 0, 1), min_draft=1.0):
"""Faces that sit closer to parallel with the pull direction than min_draft degrees."""
pull = cq.Vector(*pull).normalized()
flagged = []
for face in shape.Faces():
normal = face.normalAt() # the normal at the face's centre
draft = 90.0 - math.degrees(normal.getAngle(pull))
if abs(draft) < min_draft:
flagged.append((face, abs(draft)))
return flaggedRun it on the part:
for face, draft in draft_report(part.val(), min_draft=1.0):
c = face.Center()
print(f"{draft:.2f}° area {face.Area():.0f} mm² at ({c.x:.1f}, {c.y:.1f}, {c.z:.1f})")0.00° area 601 mm² at (-0.0, 0.6, 7.4)
0.00° area 601 mm² at (0.0, -0.6, 7.4)Two faces, both on the rib: its long sides. Its ends run into the walls and merge with them, so there is no free end to flag. The drafted walls pass, and so do the rounded corners. The fix is one argument.
.extrude(H * 0.6, taper=DRAFT) # the rib gets the same draft as the wallsRun it again and the report is empty. The rib now thins slightly toward its top edge, which is what a drafted rib does, and one more reason to keep it in proportion to the wall. Then export the file a mold shop works from:
cq.exporters.export(part, "part.step")Marve is our part-reading assistant. Upload the STEP file and it reads the draft, the wall thickness and any undercuts, works out where the mold would split, and gives you a quote. That makes it the natural next step after the script, whether you wrote the CadQuery yourself or an assistant wrote it for you. A tooling engineer still reviews the design before any steel is cut.
build123d, a newer Python CAD library, runs on the same OpenCascade kernel and exports the same STEP. The check carries over with its own method names for face normals and angles.
More on what decides a molded part: sink marks, tolerances, and the calculators in The Lab.