# VierkanteKoker — A Worked Example, Explained

**File:** `VierkanteKoker.py`
**Subject:** A hollow square steel tube with mounting holes, generated at
three levels of detail from a single parametric script.
**Why it is here:** the square tube is the workhorse profile of the
Q Framebuilder research frame. This script is small enough to read in five
minutes and demonstrates every CadQuery operation Framebuilder will need to
emit per profile: extrusion, edge rounding, shelling, face selection,
patterned holes, and parametric level-of-detail switching.

---

## 1. What the script does, in one sentence

It builds a 300 mm long, 50 mm square steel tube — solid at the lowest
level of detail, a chamfered hollow tube at the middle level, and a fully
filleted hollow profile with two mounting holes at the highest level — and
assigns the result to a single variable that Q CadLab will pick up and
render.

---

## 2. The parameters

The top of the script declares every dimension as a named variable. Nothing
else in the script touches a literal number. This is the single most
important habit in CadQuery: **never bury a dimension inside an
operation**. If a value is interesting enough to type, it is interesting
enough to name.

| Variable          | Meaning                                                  | Value     |
|-------------------|----------------------------------------------------------|-----------|
| `Profiel_Z`       | Outside width and height of the square section, in mm    | `50`      |
| `Profiel_s`       | Wall thickness, in mm                                    | `2.9`     |
| `Profiel_r`       | Outside corner radius, in mm                             | `4.35`    |
| `ProfielLengte`   | Length of the tube, in mm                                | `300`     |
| `Boringposities`  | List of (x, y) positions for the mounting holes          | two pairs |
| `Boringdiameter`  | Diameter of each mounting hole, in mm                    | `20`      |
| `LOD`             | Level of detail: `0` simplest, `2` highest               | `2`       |

The named hole positions deserve a closer look:

```
Boringposities = [(0, 100), (0, -100)]
```

These coordinates are **in the local coordinate system of the workplane
that will be selected later**, not in world coordinates. That distinction
is one of the most common sources of confusion for new CadQuery users —
see §5 on face selection below.

---

## 3. The level-of-detail concept

The `LOD` variable selects what gets built. Three values are honoured:

- **`LOD = 0` — Virtual reality.** The tube is a plain 50 × 50 × 300 mm
  solid block. No fillets, no chamfers, no hollow, no holes. This is the
  cheapest representation possible: six triangles per face, twelve
  triangles for the whole tube. Useful when you have to fit thirty of
  these on screen in a stand-alone VR headset.

- **`LOD = 1` — Virtual reality (PCVR).** Two chamfers replace the outside
  corners (a real chamfer at `Profiel_r / 2`, then a finer one at
  `Profiel_r / 4` to give a faceted impression of a rounded corner), and
  the top and bottom faces are shelled out at the wall thickness. The
  tube is now hollow and visually rounded, but still cheap to render.
  Tethered PCVR can afford this.

- **`LOD = 2` — Photorealistic rendering.** The corners become real round
  fillets at the full `Profiel_r` radius; the top and bottom faces are
  shelled at the wall thickness; the tube acquires two through-holes on
  the side face for clamps or fasteners. This is the geometry the
  workshop sees and the renderer photographs.

Crucially, the script does not write three separate models. It writes one
model that **progressively elaborates** itself depending on the value of
`LOD`. The `if (LOD == 1)` and `if (LOD >= 2)` blocks each take the
existing `Profiel` and refine it. That is exactly the pattern Q Framebuilder
will use: one logical profile in the design, rendered at the appropriate
fidelity for the device that is currently viewing it.

---

## 4. The base block

```python
Profiel = (
  cq.Workplane("XY")
  .box(Profiel_Z, Profiel_Z, ProfielLengte)
)
```

Two ideas in five lines.

**`cq.Workplane("XY")`** opens a new workplane on the XY plane (the floor,
in CadQuery's Z-up convention). A workplane is CadQuery's "current sheet
of paper": the imagined surface on which the next sketch or extrusion is
laid out. Every CadQuery script begins with one.

**`.box(width, depth, height)`** asks CadQuery to put a rectangular solid
on the active workplane. It is centred on the workplane origin by default,
and its third dimension extends along the workplane's normal. Because the
workplane is `"XY"`, the tube runs along the Z axis.

At this point — and only at this point — the script will already render at
`LOD = 0`. The remaining two blocks decorate this base block; if `LOD` is
zero they are skipped entirely.

---

## 5. The PCVR refinement (`LOD = 1`)

```python
if (LOD == 1):
  Profiel = (
    Profiel.edges("|Z")
    .chamfer(Profiel_r / 2)
    .edges("|Z")
    .chamfer(Profiel_r / 4)
    .faces(">Z or <Z")
    .shell(-1 * Profiel_s)
  )
```

Four operations in one fluent chain.

**`.edges("|Z")`** is a *selector*. CadQuery selectors are tiny strings
that pick a subset of the model. The vertical bar `|` means "parallel to";
`Z` is the world Z axis. So `"|Z"` reads as "every edge parallel to Z" —
the four long vertical corners of the tube. CadQuery selectors are one of
the language's most powerful ideas: instead of clicking on geometry, you
describe it.

**`.chamfer(Profiel_r / 2)`** rounds the selected edges with a 45° bevel
of half the corner radius. The chain continues from there. Calling
`.edges("|Z")` a second time picks up the new edges introduced by the
first chamfer (CadQuery re-evaluates selectors after every operation), and
`.chamfer(Profiel_r / 4)` puts a finer bevel on those. The double-chamfer
trick produces a corner that *looks* rounded in VR, without paying the
triangle cost of a real fillet.

**`.faces(">Z or <Z")`** selects the two end faces of the tube. The `>Z`
selector means "the face with the highest Z coordinate" (the top end);
`<Z` means "the lowest" (the bottom end). The `or` combines them. The
selector grammar reads almost like English.

**`.shell(-1 * Profiel_s)`** hollows the body out. Shell removes the
selected faces and offsets the remaining surface inward by the given
thickness (negative number = inward). The result is a hollow square tube
open at both ends. Because the two end faces were removed by the
selection, the inside of the tube is exposed — exactly what a real steel
tube looks like.

---

## 6. The high-detail refinement (`LOD = 2`)

```python
if (LOD >= 2):
  Profiel = (
    Profiel.edges("|Z")
    .fillet(Profiel_r)
    .faces(">Z or <Z")
    .shell(-1 * Profiel_s)
    .faces("<Y")
    .workplane()
    .pushPoints(Boringposities)
    .hole(Boringdiameter)
  )
```

Six operations. The first two are the high-detail twins of the previous
section:

**`.fillet(Profiel_r)`** replaces the double-chamfer with a real
curved fillet at the full corner radius. Where chamfer cuts with a flat
bevel, fillet builds a true cylindrical surface tangent to both faces.
This is the geometry the photoreal renderer wants, and the geometry
Fusion 360 will receive on STEP export.

**`.faces(">Z or <Z").shell(-1 * Profiel_s)`** is identical to the PCVR
case: top and bottom faces are removed, the wall is offset inward.

The remaining four operations are new, and they introduce two of the most
useful patterns in CadQuery.

**`.faces("<Y").workplane()`** is a *change of workplane*. The selector
`"<Y"` picks the face with the lowest Y coordinate — the side of the tube
facing the viewer in CadQuery's default camera orientation.
`.workplane()` then sets a brand-new workplane *on that selected face*.
From this point on, every coordinate the script mentions is **local to
that face**: the origin is at the centre of the face, X runs along the
face's local right, Y runs along the face's local up. This is what makes
`Boringposities = [(0, 100), (0, -100)]` mean "two holes spaced 200 mm
apart along the length of the tube, centred on the side face": the
positions are in the face's local frame, not in world coordinates.

**`.pushPoints(Boringposities)`** is the *patterning* primitive. It puts
two stack points on the active workplane, at the listed (x, y) positions.
Subsequent operations apply to **each** point in turn. CadQuery's stack
is the secret to its expressive power: every operation either consumes or
produces a stack, and patterns become loops without writing a `for` loop.

**`.hole(Boringdiameter)`** drills a through-hole at every stacked point.
Because two points are on the stack, two holes are drilled, with a single
operation. Each hole is bored all the way through the body, perpendicular
to the active workplane (which sits on the side face), in one call.

---

## 7. The closing line

```python
result = Profiel
```

The variable name `result` is one of three things CadLab looks for after
running a script (alongside any objects passed to `show_object(...)` and,
failing both, the last-defined `Workplane`). Assigning the final tube to
`result` is the simplest way to tell CadLab what to render. It will reach
into the `Workplane`, ask its underlying shapes for a tessellation, and
ship the triangle mesh to the browser viewer.

---

## 8. What to try next

Once the script renders at `LOD = 2`, change a value and press *Run*
again. CadLab re-tessellates in a few hundred milliseconds for a part of
this size.

- Change `Profiel_Z` to `80` to see a heftier tube — every dependent
  feature (fillet, wall thickness, hole positions) keeps its meaning
  because nothing was hard-coded.
- Change `ProfielLengte` to `600` and the hole positions to
  `[(0, 200), (0, -200)]` to see the patterning track the new length.
- Add a third hole at `(0, 0)` to see how `pushPoints` makes the third
  hole as easily as the first two.
- Switch `LOD` between `0`, `1` and `2` and watch the tessellation count
  in the console change.
- Press *Export STEP* — the file that comes back opens unchanged in
  Fusion 360, Inventor, SolidWorks or FreeCAD. From there the part is
  ready for the drawing office, the toolpath generator, or the workshop
  floor.

---

## 9. Why this example matters for Q Framebuilder

Every concept the script demonstrates maps directly onto a feature
Framebuilder will need to provide automatically:

- **Named parameters** become the slots a voice command fills in. *"Make
  the tube wall thicker"* becomes a change to one named variable, not a
  search-and-replace in geometry code.
- **Level-of-detail switching** becomes the way Framebuilder hands the
  same logical frame to a stand-alone VR headset, a tethered PCVR
  station, and a Blender render — at the appropriate fidelity for each.
- **Selectors and workplanes** become the way an AI agent expresses
  intent — *"drill two mounting holes on the outer face"* — without
  having to know the exact world coordinates of the face.
- **STEP export** becomes the handoff to the workshop.

A learner who understands `VierkanteKoker.py` understands the spine of
what Framebuilder has to do — and has a working sandbox in which to try
their own variations before any voice or VR is added on top.
