WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s

History-free snapshot of the private development repository at 77a82eb.
Code, docs and the public catalogue only: no plant or job knowledge
layers, no client, job or producer names (tools/scrub.py clean against
the widened term list; a second word-level grep clean). The intro deck is
left out because its slide images cannot be checked for job drawings.
This commit is contained in:
2026-09-09 08:35:15 -05:00
commit 25592fa6c2
406 changed files with 223435 additions and 0 deletions
+246
View File
@@ -0,0 +1,246 @@
# Interference checking, and what it took to make it usable
Written down because every wrong version of this check looked right, and two
of them reported "no interference" while being incapable of finding any.
The code is `check_member_interference` in
`WillTools.extension/lib/vibe/ticketchecks.py`, with the geometry in
`ticketqc.py`. This is the reasoning, not the API.
## The criterion
Two bodies interfere when the material they share is **more than 1/8" thick**.
For the *filtering* stages, which work on boxes, that is read as "the boxes
overlap by more than the tolerance on every axis" — the same statement a
detailer makes as "more than 1/8" of interference shows in each of the three
views". A view is a dropped coordinate, so each view shows two axes;
"overlaps in all three views" expands to `(0∧2) ∧ (0∧1) ∧ (1∧2)`, which is
`0 ∧ 1 ∧ 2`. There is nothing to gain by writing it as three 2D tests; the
gain is in not forming the pair at all.
For the *verdict*, boxes are not good enough, and neither is volume.
**Volume is a trap.** A bar grazing the face of a plate over a wide area has
volume while being thousandths deep; one bar catching the corner of another
has depth with almost no volume.
**A bounding box of the intersection is a worse trap**, and this one survived
several rounds of looking right. When two round bars graze, the material they
share is a *curved lens*, and a lens is no better described by its box than a
bent bar is — measured on a real piece, these intersections fill **10 to 15
percent** of their own boxes. Boxing them reported **0.51" of interference for
a sliver of 0.0166 cubic inches**: a shape a thousandth of an inch thick would
have measured exactly the same. The box was not even mis-oriented; the
intersection's own box was already axis-aligned. It is simply the wrong tool
for a curved shape, twice over.
So the verdict is a **mean thickness**. For a thin body, `Volume = Area × t`
and `SurfaceArea = 2 × Area`, hence
```
t = 2 × Volume / SurfaceArea
```
which does not care how the sliver is oriented or how curved it is. On the
piece where boxes claimed 44 interferences, every one measured **0.059″ to
0.066″** thick — under half the tolerance, and the count went to zero.
Known bias: mean thickness **under-reports a chunky intersection**, giving
`side/3` for a cube and `diameter/3` for two bars crossing dead centre. It
errs towards calling an interference shallower than it is, so the tolerance it
is compared against should be set knowing that.
## The pipeline, and why each stage exists
Revit's own assembly clash tests every pair and solves geometry for all of it.
Five stages instead, each one cheap enough to earn the one after it:
| stage | rejects | cost |
| --- | --- | --- |
| sweep and prune along the length | pairs that never share a station | one sort |
| **top in form**, then front, then right | pairs clear in the drawing plane | six comparisons |
| one box per **solid** | air inside multi-part embeds | solves geometry |
| boolean **intersection** | air inside bent bars | one boolean |
| exemptions | what is deliberate | table lookup |
Measured on three real pieces:
```
all pairs -> sweep -> top in form -> front -> solid boxes -> real
12090 2656 1120 644 320 27
8385 2246 980 555 187 47
6670 1839 956 506 150 10
```
Under a second per piece.
### The first view is fixed, not chosen
Top in form — the full 299" × 143" face, where members are spread out and most
pairs separate at once. The other two views look along a long axis at an 8"
thickness, where nearly everything overlaps nearly everything.
Which view is worth looking at first is a fact about how a wall panel is
built. It does not change piece by piece, so it is not inferred from the data.
Read off the model rather than assumed: the view named `Elevation Top` on a
real ticket looks along the panel's local thickness axis, dot product ±1.000
on every piece checked. The sign varies because pieces are mirrored; the axis
does not.
**The third view never rejects anything and cannot.** Top-in-form establishes
length and width, front adds thickness, and right asks only about thickness
and width — both already known. It passed 755 of 755, 596 of 596, 537 of 537.
Kept because the code should read like the three drawings it describes; not
load-bearing, and nobody should think it is.
## Three ways to build this wrong
### 1. Sampling edge vertices for a bounding box
A cylinder's edges are **circles**, and a circle's two "end" points are the
same point on the seam. Sampling them collapses a strand's box to a
zero-thickness sliver in two axes — and a sliver overlaps nothing.
The first version of this check reported **no interference anywhere**, on
every piece, and looked entirely healthy doing it. Use
`Solid.GetBoundingBox()`, and remember its box is stated in the solid's own
coordinates, so its `Transform` applies before the panel's inverse.
### 2. One box per element
An embed drawn as a plate at one end and anchor bars at the other is **nine
solids**, and one box around all of them claims the air between as occupied.
Measured fill: **3%** steel by box volume. Per-solid boxes cut the reported
count from 265 to 223 before any boolean ran.
### 3. Trusting boxes at all, once bars are bent
A box is the product of three intervals, so a **bent bar's box is the whole U
including the hole**, and anything passing cleanly through that hole reads as
an interference. How much of each box is actually steel, measured per family
on one piece:
```
3.05% a 9-solid embed 6.06% a bent bar, 1 solid
3.2% a 3-solid embed 10.16% a bent bar, 1 solid
12.85% a bent bar, 1 solid
--------------------------------------------------------------------
46.32% a straight bar 50.16% a strand
```
The straight bar and the strand are close to what a cylinder in its own box
should be. Everything above them is mostly air — and one of those bent bars
is a 0.63" bar occupying a 13.63" × 6.06" box.
Boxes claimed 265 interferences on that piece; **86 were real**. Two thirds
was air. The boolean pass that establishes this costs about 0.1 s.
This also produced a wrong conclusion that stood for a while: transverse bars
were said to cross every bottom-row strand as a drafting convention, 70 such
pairs on one piece. **Not one was real** — the bars and the strands miss each
other through the thickness.
Boxing the **intersection** is sound where boxing the inputs was not. A bent
bar's own box is mostly the hole; the intersection is the small piece of steel
actually shared, and its box describes that piece fairly.
## What counts as deliberate
Left alone, the check reports several hundred per piece and nobody reads it.
Everything below is configuration, not code, and lives in the knowledge layers.
| | where | why |
| --- | --- | --- |
| `same_family_ok_categories` | plant | Some hardware is placed as several overlapping instances of one family **on purpose** — the intersection is what makes Revit pick them up together when the assembly is created. |
| `within_category_only` | plant | Reinforcing meeting an embed, or a bar running into a ledge, is how the piece goes together. Two things of one **kind** in one place is the defect. Blunt, and it does hide real cross-category problems. |
| `insert_bars` | plant | A lifting insert is set out with a bar to either side. Those belong to the **insert**, not the reinforcing schedule — the plant places them and may move them freely, so there is nothing to hold them to. |
| `component_groups` | either | Hardware that arrives as several families but is **one part** — see below. |
| `exempt_families` | job | Hardware whose modelled solid sits inside other things and always will. |
| `ignore_family_pairs` | either | A named pair, one at a time. The last resort, not the first. |
### Component groups
A connection is not one family. A top-of-panel-to-column connection is a
plate, its rebar, its sleeve and a good deal else; an embed and the bar welded
to it are one part installed together. They are modelled touching **because
they are one thing**.
Revit records none of this. Checked on a real model: every component's family
name is just its own part number, and no parameter, nested family or shared
parameter points at the assembly it belongs to. So the ownership has to be
stated, and `component_groups` is where.
Two strengths, and reaching for the wrong one goes blind:
- `interferes: within_group` — the components may overlap **each other**
freely, but one of them hitting something **outside** the group is still
reported. Reach for this first. It explains the overlaps that are by design
without hiding the ones that are not, and on a real piece it kept a
`P76 × P106` finding while dismissing `P76 × its own bar`.
- `interferes: false` — nothing in the group is reported at all. For hardware
set out as a unit that simply is not the detailer's to check.
### The insert-bar rule: found by symmetry, not by distance
An insert's own bars cannot be told apart from ordinary reinforcing by
anything local. Same family, same size, same direction, same layer.
**Distance alone gets it backwards.** The first version of this rule exempted
bars within 1.5" of a lifter, and on a real piece that exempted the ordinary
#4s which happen to pass near one while leaving the actual lifter bars — #5s,
sitting 4" away — fully checkable. It was wrong in both directions at once and
the reported counts barely moved, so nothing gave it away.
What does distinguish them is **symmetry**. Reinforcing comes in layers
through the thickness and the layers mirror each other, so an ordinary bar has
a counterpart at the same station in another layer. An insert's bar does not.
Measured on one piece:
```
layer at 6.0" 20 x #4 at stations 2 4 14 32 50 68 86 104 122 140 ...
layer at 2.5" 20 x #4 at exactly the same stations <- mirrored
10 x #5 at 26 34 | 116 124 | 175 183 | 187 195 | 265 273
<- unpaired
lifter stations 30 | 120 | 179 | 191 | 269
```
Five pairs, 8" apart, each centred **±4" on a lifter station**. Nothing else
sets those ten apart.
So: a bar with no counterpart, within reach of an insert, belongs to it. A bar
with no counterpart and **no** insert nearby is reported (`BAR-UNPAIRED`) —
the pattern does not explain it and neither does a lifter.
**The mirror is never assumed to be at `thickness - depth`.** The rule looks
for a counterpart in *some* other layer rather than at a computed depth, so a
piece whose symmetry is broken — extra clear cover on one face, say — costs
nothing and no layer pairing has to be worked out.
A measurement note that cost an afternoon: layer depths must be reported
**raw**. Binning them to the nearest half inch turned 1.75 / 6.25 into
2.0 / 6.5 and 2.25 / 5.75 into 2.5 / 6.0, which made a perfectly symmetric
piece look like it had half an inch of extra cover on one face. The panel was
symmetric to 0.000" all along, in every layer except the insert bars — which
is what a wall panel should be, since the only thing that breaks the mirror is
a lifter, and a lifter is not symmetric either.
## Open
- Every bar in `bar_families` is assumed to be layered reinforcing. Ledge bars
are not, and they surface as `BAR-UNPAIRED` — correct as stated, but it is
a category the rule does not really have.
- If a lifter has no bars at all, nothing notices. Whether every face lifter
must carry one either side is a *completeness* question, and no check asks
it yet.
- Exempting an insert's bars from **everything** is what the plant asked for,
since it may move them freely — but it does suppress real geometry. On one
piece it removed the deepest interference in the list, a #5 buried a full
diameter into an embed.
- Cross-category interference is not looked at when `within_category_only` is
on, and some of it is certainly real.
- AABBs remain a **conservative** approximation everywhere they survive into a
result: they can invent an overlap but never hide one. That is the right
direction for a filter and the wrong one for an exemption, which is why the
insert-bar test refines geometry before measuring.