Files
willtools-demo/docs/interference.md
T
will 52d94303bc
ci / test (push) Has been cancelled
WillTools: precast shop-ticket QC and panel handling tools
Public demo snapshot of an in-house pyRevit extension for precast concrete
shop-ticket QC (37 checks / 46 checklist rows) and a standalone panel
handling solver (PCI Chapter 8).

- No client, job, plant or product data: knowledge layers ship as
  structural templates only.
- Ticket QC's model-read escalation targets a LAN model server endpoint
  (configurable), not a cloud provider.
- Built from the private development repo via tools/make_dist.py.
2026-09-03 16:33:47 -05:00

12 KiB
Raw Blame History

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.