File size: 10,645 Bytes
7c72eb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# Anti-Patterns

Common mistakes LLMs make when generating CadQuery code, and how to fix them.

---

## 1. The CSG Reflex β€” Booleans Instead of Feature Operations

The most pervasive anti-pattern: reaching for `.union()` / `.cut()` when a direct
feature operation would be cleaner, faster, and produce better topology.

| Instead of... | Use... |
|--------------|--------|
| `.union(cylinder(...))` for a boss | `.faces(...).workplane().circle(r).extrude(h)` |
| `.cut(cylinder(...))` for a hole | `.faces(...).hole(diameter)` |
| `.cut(box(...))` for a pocket | `.faces(...).workplane().rect(w, h).cutBlind(depth)` |
| `.union(...)` for a chamfered edge | `.edges(...).chamfer(d)` |
| `.union(...)` for a filleted edge | `.edges(...).fillet(r)` |
| `.cut(shell_solid)` to hollow | `.faces(...).shell(thickness)` |

Booleans rebuild the full boundary from scratch. Feature operations extend or modify
the existing BRep directly - they are faster and leave cleaner topology for subsequent
selections.

**Use booleans only when combining genuinely separate solids** or when the geometry
cannot be expressed as a profile operation.

---

## 2. Using `.translate()` on a Workplane Chain

`.translate()` is a method on `Shape` objects, not on `Workplane` objects. Calling it
in a fluent chain after building geometry moves the shape, but does so in global
coordinates β€” bypassing the workplane system entirely.

```python
# Wrong: translate is called on the Workplane, not the Shape
result = cq.Workplane("XY").box(10, 10, 10).translate((5, 0, 0))

# If you need to position geometry, use workplane placement instead
result = cq.Workplane("XY").center(5, 0).box(10, 10, 10)

# Or move the workplane origin explicitly
result = cq.Workplane(cq.Plane(origin=(5, 0, 0))).box(10, 10, 10)

# .translate() is valid when called on a Shape extracted from the chain
shape = cq.Workplane("XY").box(10, 10, 10).val()
moved = shape.translate(cq.Vector(5, 0, 0))
```

---

## 3. Building in Global Coordinates Instead of Using Workplanes

Hardcoding global coordinates creates scripts that are brittle and hard to modify.
Use workplanes and relative positioning instead.

```python
# Wrong: all positions hardcoded in global space
result = (
    cq.Workplane("XY")
    .box(30, 30, 10)
    .union(cq.Workplane("XY").cylinder(5, 8).translate((0, 0, 9)))
    .union(cq.Workplane("XY").cylinder(5, 8).translate((10, 10, 9)))
)

# Right: build relative to existing faces
result = (
    cq.Workplane("XY")
    .box(30, 30, 10)
    .faces(">Z").workplane()
    .circle(5).extrude(8)            # center boss
    .faces(">Z").workplane()
    .center(10, 10).circle(5).extrude(8)   # offset boss
)
```

---

## 4. Forgetting `combine=False` on `.extrude()`

By default, `.extrude()` unions the new solid with whatever is on the context stack.
If you want a separate solid (e.g., to combine later or export independently),
pass `combine=False`.

```python
# Default: extrusion is unioned into the existing solid
result = solid.faces(">Z").workplane().circle(3).extrude(5)

# Separate solid
new_part = cq.Workplane("XY").circle(3).extrude(5, combine=False)
```

The inverse mistake also occurs: forgetting that `combine=True` is the default,
and then trying to union the result again β€” producing a double union.

---

## 5. Misreading `.cutBlind()` Direction

`.cutBlind(depth)` cuts in the direction of the workplane normal. The normal points
**outward** from the selected face by default, so cutting from the top face without
inverting will cut away from the solid.

```python
# Wrong: cuts upward, away from the solid
solid.faces(">Z").workplane().rect(5, 5).cutBlind(3)

# Correct: invert=True flips the normal into the solid
solid.faces(">Z").workplane(invert=True).rect(5, 5).cutBlind(3)
```

Also applies to `.extrude(..., combine="cut")`.

---

## 6. `.val()` on a Multi-Item Stack

`.val()` returns the **first** item on the stack. It does not error if there are
multiple items - it silently discards the rest. Use `.vals()` when you need all items.

```python
solid = cq.Workplane("XY").box(10, 10, 10)

# Returns one Face - the first face in iteration order, not necessarily ">Z"
face = solid.faces().val()

# Returns all 6 faces as a list
faces = solid.faces().vals()

# To get a specific face as a Shape, select first
top_face = solid.faces(">Z").val()
```

---

## 7. Misusing `.each()`

`.each(callback)` calls `callback` on every item in the stack and **replaces the stack**
with the results. The callback must return a `Shape`. It is not for iteration with
side effects.

```python
# Wrong: callback doesn't return a Shape; result is undefined
solid.edges().each(lambda e: print(e.Length()))

# Wrong: trying to accumulate results
results = []
solid.faces().each(lambda f: results.append(f))  # each() ignores the return value None

# Right: use .each() to transform stack items
# e.g., get the center point of each face as a Vertex
centers = solid.faces().each(lambda f: f.Center())

# For iteration/inspection, use .vals() instead
for face in solid.faces().vals():
    print(face.Area())
```

---

## 8. Losing the Solid Context After a Selector

After `.faces(...)` or `.edges(...)`, the stack contains only the selected entities β€”
not the full solid. Calling `.workplane()` on a face selection is correct;
calling feature operations directly on the selection without a workplane can
silently use the wrong context.

```python
# Wrong: after .faces(), the solid is not the active context
solid.faces(">Z").box(5, 5, 3)   # adds a new box to the face selection context, not the solid

# Correct: use .workplane() to re-establish context for sketching
solid.faces(">Z").workplane().rect(5, 5).extrude(3)

# Correct: use .end() to return to the solid context
solid.faces(">Z").tag("top").end().faces("<Z").workplane().hole(3)
```

---

## 9. Assuming `centerOption` Default

The default `centerOption="ProjectedOrigin"` projects the parent workplane's origin
onto the new face - which is often not the face center and can place the origin
well outside the face bounds on irregular geometry.

```python
# Fragile: origin placement depends on face shape
solid.faces(">Z").workplane().circle(3).extrude(5)

# Explicit and predictable
solid.faces(">Z").workplane(centerOption="CenterOfBoundBox").circle(3).extrude(5)
```

---

## 10. Index Selector Fragility

Selectors like `">>Z[1]"` depend on a sorted count of all matching entities.
Adding fillets, chamfers, holes, or shells changes the entity count and can
silently shift which entity is selected.

```python
# Fragile: index may shift after any feature is added
solid.faces(">>Z[1]").workplane().hole(3)

# Robust: tag the face before adding features
solid = (
    cq.Workplane("XY")
    .box(20, 20, 20)
    .faces(">>Z[1]").tag("target")
    .end()
    .edges(">Z").fillet(1)          # this would shift the index above
    .faces(tag="target").workplane().hole(3)
)
```

---

## 11. Mixing Fluent and Free Function APIs Unintentionally

The two APIs are not interchangeable mid-script. A `Workplane` object is not
a `Shape`, and Free Function operations do not accept `Workplane` objects.

```python
from cadquery.func import *
import cadquery as cq

# Wrong: passing a Workplane to a free function
b = box(10, 10, 10)
wp = cq.Workplane("XY").box(5, 5, 5)
result = b + wp   # TypeError β€” wp is not a Shape

# Correct: extract the Shape from the Workplane first
wp_shape = cq.Workplane("XY").box(5, 5, 5).val()
result = b + wp_shape
```

When mixing is intentional, always extract with `.val()` or `.vals()` before
passing to Free Function API operations.

---

## 12. Unclosed Wires

When building a profile with `.lineTo()`, `.spline()`, `.threePointArc()`, etc.,
the wire must be closed before `.extrude()` or `.revolve()`. An unclosed wire
produces an error or unexpected open shell.

```python
# Wrong: missing .close()
result = (
    cq.Workplane("XY")
    .moveTo(0, 0)
    .lineTo(10, 0)
    .lineTo(10, 5)
    .lineTo(0, 5)
    .extrude(3)   # error - wire is not closed
)

# Correct
result = (
    cq.Workplane("XY")
    .moveTo(0, 0)
    .lineTo(10, 0)
    .lineTo(10, 5)
    .lineTo(0, 5)
    .close()
    .extrude(3)
)
```

---

## 13. Free Function API: Boolean in a Loop

Boolean operations in the Free Function API (and the fluent API) are expensive
because they recompute the full boundary. Never perform them in a loop.

```python
from cadquery.func import *

holes = [cylinder(0.5, 5).moved(x=i*2) for i in range(10)]

# Wrong: 10 sequential boolean operations
result = box(20, 5, 5)
for h in holes:
    result = result - h

# Correct: combine into a compound first, then subtract once
result = box(20, 5, 5) - compound(holes)
```

---

## 14. `BREP_API command not done` Errors

This error comes from the OpenCASCADE kernel and means a BRep operation could not be
completed geometrically. It is not a CadQuery bug β€” it means the requested geometry
is invalid or degenerate. The error message does not identify which operation failed.

**Common causes:**

### Fillet or chamfer radius too large

A fillet fails when its radius is larger than the shortest adjacent edge, or when
adjacent fillets overlap each other.

```python
# Fails if the box edge is shorter than radius=5, or if adjacent fillets intersect
solid.edges("|Z").fillet(5)   # BREP_API command not done
```

Fixes:
- Reduce the radius
- Fillet edges in separate calls, smallest features first
- Fillet different edge groups separately rather than all at once

```python
# Apply smaller fillets to shorter edges first
result = (
    cq.Workplane("XY")
    .box(20, 20, 5)
    .edges("|Z").fillet(1)      # vertical edges first
    .edges(">Z").fillet(0.5)    # then top perimeter with smaller radius
)
```

### Self-intersecting profiles

A profile (wire or sketch) that crosses itself cannot be extruded, revolved, or swept.
This includes profiles where an arc or spline curves back through the outline.

Check that:
- All `lineTo()` / `spline()` sequences form a simple (non-self-intersecting) closed loop
- Revolve profiles do not cross the axis of revolution
- Sweep profiles are small enough to navigate tight path curvature without self-intersection

### Self-intersecting sweep

A sweep fails when the profile is too large relative to the path curvature β€” the
extruded solid folds back on itself at tight bends.

Reduce the profile size or increase the path radius.

### Shell thickness too large

`.shell()` fails when the thickness is larger than the smallest local radius of
curvature β€” the offset surface self-intersects.

Reduce the shell thickness or simplify the geometry before shelling.