14. Workflow Architecture & Data-Flow Connections¶
This chapter is cross-cutting. The preceding chapters classify individual
algorithm families; this one maps how they connect into end-to-end
workflows. The repository is not a bag of components. It is one data-flow
spine, ingest to fabricate, with a small number of subsystem branches that
re-enter that spine at well-defined seams. The 28 worked examples in
examples/ are the evidence: each is a Grasshopper definition that wires a
slice of the spine. Reading the 24 example READMEs and the four
README-less canvases together recovers the architecture as built, not as
imagined.
Three structural facts govern the whole repository and recur in every section below.
- Rhino-free Core. The algorithms live in
Frahan.StonePack.Core(net48, no RhinoCommon reference). The Grasshopper layerFrahan.StonePack.GHis a thin facade: it marshals canvas geometry to Core types, calls one engine, and marshals back. This is why headless harness numbers exist at all, and why AGENTS.md §1 can distinguish "measured" (Core, harness) from "validated" (seen in Rhino). - Native bridges behind a C ABI. Heavy geometry (booleans, remesh,
reconstruction, convex decomposition, exact NFP) is done in native DLLs
wrapped by P/Invoke shims (
native/). The managed side owns only the marshalling. AGENTS.md §3 routes the crash-prone ones out of process. - Facade-over-primitives. The big single-box components (HeteroExt, the Unified sheet filler, the hole-aware nester) compose published Core primitives. They add convenience, not new mathematics, and the composed-equivalent canvas exists alongside them.
14.1 The data-flow spine¶
Every workflow is a path through one acyclic spine: a site or a form is ingested, reconstructed to clean geometry, segmented into regions, then packed or cut into fabricable pieces, stabilised (settling for piles, a limit-state certificate for assemblies), and emitted as a cut plan or an install order.
flowchart LR
subgraph INGEST
A1[Read LAS Cloud<br/>.las/.laz laszip]
A2[Load E57 / PLY Cloud<br/>out-of-proc worker]
A3[GPR File Loader<br/>.rd3/.DT GprFileReader]
A4[Vector Fractures Loader<br/>.shp NetTopologySuite]
end
subgraph RECONSTRUCT
B1[Estimate Cloud Normals]
B2[Scan Reconstruct<br/>Poisson/AdvFront/Alpha]
B3[Sanitize / Remesh<br/>geogram FillHoles+Remesh]
end
subgraph SEGMENT
C1[Mesh Segmentation by Angle<br/>CGAL]
C2[GPR Fracture Extract<br/>Stolt f-k + Hilbert]
C3[Bench From Mesh / offset]
C4[Statue grid x CGAL boolean]
end
subgraph PACK_CUT
D1[Block Pack Tree<br/>DLBF guillotine]
D2[Fracture Block Pack<br/>keep-out aware]
D3[Sheet Nest Hole-Aware<br/>exact NFP-BLF]
D4[Trencadis Catalog Pack<br/>CVD-Lloyd + Hungarian]
D5[Ashlar Pack / Polygonal Seq]
D6[BlockCutOpt / guillotine seq]
end
subgraph STABILISE
E1[Rubble Wall Settle<br/>PCA + dimple drop]
E2[Settle 3D Physics<br/>Bullet]
E3[Masonry Stability RBE<br/>Kao 2022 CRA]
end
subgraph FABRICATE
F1[Guillotine cut planes]
F2[Planes to Robot / KUKA PRC]
F3[Wire-saw toolpath / G-code]
F4[IFC terminal xBIM]
end
A1 --> B1 --> B2 --> B3 --> C3
A2 --> B2
A3 --> C2 --> D2
A4 --> C2
B3 --> C1 --> D4
B3 --> C4 --> D1
C3 --> D1
C2 --> D2 --> D6 --> F1
D1 --> E2
D3 --> F3
D4 --> F2
D5 --> E3 --> F4
E1 --> E3
D1 --> F1 --> F2
F1 --> F3
The spine has four ingest mouths (point cloud, GPR radargram, vector shapefile, and an in-canvas designed form), one reconstruction lane, three segmentation modes (dihedral surfaces, GPR fracture surfaces, grid-boolean blocks), six pack-or-cut engines, three stabilisers, and four fabrication terminals. Each example below is one route through this graph.
14.2 Per-example pipeline graphs¶
Each line is the component chain the example wires, read left to right.
Bracketed names are Grasshopper components; arrows are canvas connections.
Folders without a README ship a .gh + .3dm only; their chain is read
from the canvas and the figure catalogue.
| # | Example | Pipeline chain (component graph) |
|---|---|---|
| 01 | Quarry to wall | Scan/Block source -> Block Pack -> Masonry layout -> Wall (full spine, definition only) |
| 02 | Masonry assembly | Blocks -> Polygonal Masonry Sequence -> colour-by-install-order preview |
| 03 | GPR fracture (granite) | GPR File Loader (.rd3) -> RadargramProcessor (f-k migrate) -> GPR Fracture Extract -> GPR Fracture Surfaces 3D -> report |
| 03b | Quarry to slabs | Block source -> Fracture Block Pack -> Slab Cut By Fractures -> slabs (canvas only) |
| 04 | Scan to bench | Read LAS Cloud (.laz) -> Estimate Cloud Normals -> Scan Reconstruct (Poisson/Geogram) -> Bench From Mesh + offset -> packable volume |
| 05 | Artist pointing machine | Scan mesh (decimated) -> Carving Stages -> staged meshes + pointing coords |
| 07 | Scan ingest (full) | Load E57 / .ply / .laz -> outlier-clean + crop -> Scan Reconstruct (Advancing-Front, out-of-proc) -> Clean Scan Mesh -> hand-off |
| 08 | GPR marble + layout | GPR File Loader (.DT) -> GPR Fracture Extract -> 3 dipping beds -> net + W*volume block pack -> oblique guillotine sequence |
| 09 | Uncertainty-safe yield | GPR Fracture Surfaces 3D -> Fracture Clearance = sigma -> Fracture Block Pack -> per-zone yield |
| 10 | 2D nest | part curves + sheet + holes -> Sheet Pack (Unified) V506 / Freeform Sheet Nest (Exact NFP) -> packed curves + util_stock |
| 11 | 3D block pack | element boxes + container -> Block Pack (Tree) / DLBF -> placed boxes (12/12) + report |
| 12 | Trencadis mosaic | shard catalog + sheet -> Trencadis Catalog Pack (CVD-Lloyd + Hungarian) -> placed pieces + grout |
| 13 | Surface mapping | twisted mesh -> Mesh Segmentation by Angle (CGAL) -> 6 regions -> Surface Chart (BFF) -> Pack On Surface -> 176-shard mosaic |
| 14 | Kintsugi reassembly | Load BB Sample (.bin) -> Frahan Kintsugi (Port mode) -> assembled fragments + verifier score |
| 15 | Statue to blocks | Read PLY -> Geogram FillHoles/Remesh/FillHoles -> 0.5 m grid x CGAL intersect -> 113 real-face blocks -> Branch A gangsaw / Branch B rubble match |
| 16 | Rubble masonry | 40 ETH1100 stones -> Rubble Wall Settle (PCA + dimple drop) -> staggered wall, 36/40 stable |
| 17 | Ashlar masonry | 60-block inventory -> Ashlar Pack (running bond) -> Pack Preview -> coursed wall (45 placed) |
| 18 | Pack + settle (Bullet) | 12 stones + container -> Settle 3D (Physics, Bullet) -> dense non-interpenetrating pile |
| 19 | Rubble evolved fit | blocks + stones -> Rubble Evolved Fit (24 seeds + (1+8)-ES) -> 10/10 enclosed, one block per stone |
| 20 | Rubble multi-bin | blocks + stones -> Rubble Multi-Bin Pack (voxel-occupancy FFD) -> 17/20 across 6 bins |
| 21 | Stereotomy arch | Arch Voussoirs (D5F10012) -> cells -> evolve-match to ETH stone -> CgalMeshBoolean.Intersection -> 11/11 carved voussoirs |
| 22 | Pendentive vault | Pendentive Vault Voussoirs (D5F10013) -> 36 cells -> evolve-match -> CGAL trim -> 36/36 carved |
| 23 | Quarry to slab | fracture-prone block -> Fracture Block Pack (voxel-dlbf-multi) -> intact blocks -> gangsaw slab cut |
| 24 | Guillotine sequence | packed block -> rip(perp-X) -> cross(perp-Y) -> cross(perp-Z) -> 19 saw planes as meshes |
| 25 | Marble gangsaw cost | fractured bench -> Fracture Block Pack -> net + W*volume sweep (Pareto) -> balanced -> guillotine planes |
| 26 | Loviisa surface fractures | Vector Fractures Loader (.shp, F2D00BEC) -> FractureTraceCollection -> strike map (708 traces) |
| 27 | Polygonal masonry | chains/cells + wall -> Polygonal Masonry Sequence (B4E07A3C) / ...3D (C5F18B4D) -> install order DAG -> colour-by-order |
| 28 | Hole nest | parts + sheet + holes -> Sheet Nest (Hole-Aware) D5F10019 / ContactNfpHoleNester -> nested around holes (canvas only) |
The table makes the spine visible as routes. The geologist spine is 03 -> 08 -> 09 (GPR to uncertainty-safe yield). The engineer spine is 04 -> 11 -> 23 (scan to bench to slab). The artist spine is 05 -> 15 (carving and statue decomposition). The masonry spine is 16/17 -> 27 -> 02 (settle or course, then order, then assemble). Examples 21/22 are the top-down stereotomy bridge, and 13 is the surface-cladding bridge.
14.3 Architectural seams¶
Core vs GH facade¶
The deciding seam is the assembly boundary. Frahan.StonePack.Core
references no RhinoCommon; the engines ContactNfpHoleNester
(src/Frahan.StonePack.Core/Packing/TwoD/ContactNfpHoleNester.cs),
BlockCutOptSolver, Dlbf3dMixedSizePacker, RubbleWallSettle, and
EquilibriumMatrixBuilder are all pure. The GH component is the facade.
For example, BlockPackTreeComponent
(src/Frahan.StonePack.GH/Packing/BlockPackTreeComponent.cs:37, GUID
C2D3E4F5-3001-4F5E-A6B7-C8D9E0F12345) marshals canvas boxes to the
Core packer and back; the [Algorithm] attribute at line 30 cites the
engine, not the wrapper. This is the single most important structural
choice in the repository: it makes the Core independently testable and
keeps the truth criterion honest.
Native bridges¶
Three classes of native bridge cross the managed boundary, all behind a C
ABI (native/README.md is the authority).
frahan_cgal.dll(cgal_shim) wraps CGAL Polygon Mesh Processing: corefinement booleans (used in example 15 grid-intersect and 21/22 voussoir trim), decimation, OBB, straight skeleton, SDF/angle segmentation (example 13), alpha shape, advancing front, Poisson. The CGAL packages used are GPL in that distribution.frahan_geogram.dll(geogram_shim) wraps Bruno Levy's Geogram: remesh, hole-fill, voxel downsample, CVT/Lloyd, and the KazhdanGEO::PoissonReconstructionthat Geogram bundles (example 04/15). Geogram core is BSD-3; bundled PoissonRecon is MIT.nfp_kernel.dllwraps Clipper2 (BSL-1.0, no copyleft) for the batched Minkowski-sum NFP lane thatContactNfpHoleNestercalls (examples 10, 28).frahan_coacd.dllwraps CoACD (MIT, SIGGRAPH 2022) for the convex decomposition in example 15 Branch C and the Bullet settle.
The GH wrappers over these (Scan Reconstruct, Mesh Remesh/Decimate, the
CGAL cut/test components, the CoACD test) carry no managed algorithm; they
marshal to the shim. Crash-prone in-process booleans are routed through
OutOfProcessReconstructor + recon_worker (example 07), per AGENTS.md
§3.
Facade-over-primitives pattern¶
The pattern is explicit in source. FrahanHeterogeneousExtractionComponent
(src/Frahan.StonePack.GH/BlockCutOptHeterogeneousComponents.cs:179)
composes BlockCutOptSolver + Dlbf3dMixedSizePacker + the monument
packer; its [Algorithm] note at line 169 reads "Composes Elkarmoty 2020
(BlockCutOpt) and Chehrazad 2025 (DLBF), both interpreted and reimplemented
in managed code for this plugin; the composition and the heterogeneity
model are the contribution." That is the canonical facade: every internal
step resolves to an in-repo primitive that also ships standalone. The
Unified sheet filler dispatches V506 and the obsolete V1/V2/V3 wrappers;
Sheet Nest (Hole-Aware) D5F10019 is the facade over
ContactNfpHoleNester; Pack On Surface
(src/Frahan.StonePack.GH/SurfacePacking/PackOnSurfaceComponent.cs:41-42)
composes the exact NFP-BLF placement on a BFF chart with a classical
triangle barycentric lift back to 3D (attributed to the mean-value-coordinate
family, after Floater 2003; the shipped lift is plain barycentric, not MVC).
Top-down vs bottom-up design flows¶
The repository tags every workflow by design direction (the
[DesignApplication(..., DesignFlow.TopDown|BottomUp)] attribute, e.g.
GprFractureExtractComponent.cs:46). The two flows are first-class.
- Top-down (form-first). A designed form drives the search for stone. Examples 21/22 generate voussoir cells, then find and trim rubble to fit them. Example 15 takes a sculpted bunny and cuts it into a brick grid. Example 17 fills a wall envelope with dressed ashlar. The form is imposed; the stone is negotiated to it.
- Bottom-up (material-first). Found stone drives the form. Example 16 settles real ETH1100 scans into whatever staggered wall emerges. Example 12/13 lay offcut shards into a Trencadis mosaic. The form is the residue of the material.
The same Core engines serve both: Rubble Evolved Fit (example 19) is the
substrate for the top-down voussoir match (21) and the bottom-up rubble
lot match (15 Branch B).
14.4 Cross-subsystem couplings¶
The architectural interest is in the seams where one subsystem's output is another's typed input. Four couplings carry the workflow weight.
GPR defect -> defect-aware nesting¶
The GPR fracture chain emits 3D fracture surfaces; the packers consume them
as keep-out geometry. GprFractureExtractComponent
(src/Frahan.StonePack.GH/Quarry/GprFractureExtractComponent.cs:43, GUID
A7E0B0F1-...) runs Stolt f-k migration and Hilbert energy; its surfaces
flow into FractureBlockPackComponent
(...Quarry/FractureBlockPackComponent.cs:37, GUID
A7E0B0F3-0C0F-4A16-9E3D-0FACE0FACE04) which packs blocks only in intact
rock. Example 09 closes the loop by wiring the GPR position uncertainty
\(\sigma\) as the inward clearance, so no block sits within the measured
error of a fracture. The 2D analogue is the same idea one dimension down:
a vein or defect becomes a sheet hole, and ContactNfpHoleNester
(example 28) nests parts around it. The coupling is one concept, defect as
keep-out, expressed as a 3D surface margin and a 2D hole.
The packed-yield objective itself is a swept scalar. Examples 08 and 25 both maximise
where \(W\) is a volume credit in dollars per cubic metre. Sweeping \(W\) from \(0\) to \(\infty\) walks the Pareto front from pure profit to pure throughput. At \(W=0\) the loss-making block (negative net) is never placed; as \(W \to \infty\) every cell fills. This single knob is what makes the cost/volume/balanced triptych in both examples one workflow, not three.
Statue -> blocks -> CRA¶
Example 15 decomposes a statue into real-face blocks by intersecting a \(0.5\,\text{m}\) grid with the closed solid. The recovered-volume identity is the correctness certificate:
a complete partition (statue \(5.4009\,\text{m}^3\) = sum of blocks). The blocks then flow two ways: Branch A packs them for a gangsaw, Branch B matches each to a rubble stone. Where those blocks become a built wall (example 27 install order, then assembly), the assembly is handed to the limit-state check below. The chain statue -> blocks -> order -> stability is the full top-down fabrication route.
H-model coupling: RBE accepts / CRA rejects¶
The masonry stability subsystem is a coupled two-stage certificate, and
the coupling is a regression test, not just a workflow. The Rigid-Block
Equilibrium (RBE) stage solves a convex QP for a statically admissible
contact force field. Build the equilibrium matrix \(\mathbf{A}_{eq}\)
(EquilibriumMatrixBuilder.Build), the friction cone
\(\mathbf{A}_{fr}\) (FrictionConeBuilder.Build), and solve
The \(f_n \ge 0\) constraint (no tension at a joint) is the sign that the
audit flagged. The shipped path now calls
RbeQpFormulation.BuildPhysicsCorrected
(src/Frahan.StonePack.GH/Masonry/MasonryStabilityRbeComponent.cs:305),
which flips the sign so \(f_n \ge 0\) means compression, not the inverted
convention of the older Build. RBE feasibility is necessary but not
sufficient: it allows a force field that no realisable rigid motion would
sustain. The CRA (Coupled Rigid-Block Analysis) stage adds the kinematic
side, an alternating-convex soundness certificate that can reject an
assembly RBE accepts. The H-model counterexample, an assembly where RBE
reports feasible and CRA reports unstable, is kept as a regression test:
it is the proof that the two stages are not redundant. The component cites
Kao et al. 2022 at
MasonryStabilityRbeComponent.cs:69 (GUID
F6BAC3D4-4E5F-4071-BC3D-5E6F7A8B9CAD).
Vector + GPR -> shared fracture model¶
Examples 26 (shapefile traces) and 08 (GPR depth) both produce a fracture
model the packers consume. The shapefile gives surface trace strike and
spacing in plan; GPR gives depth. VectorFracturesLoaderComponent
(src/Frahan.StonePack.GH/VectorFracturesLoaderComponent.cs:53, GUID
F2D00BEC-2026-4522-B0B0-1ABE15A0DEAD) returns a FractureTraceCollection
that feeds Slab Cut By Fractures and the fracture-aware packers, the same
sink the GPR surfaces reach. Two ingest mouths, one defect model, one set
of packers.
14.5 Originality call-outs (workflow components)¶
The classification is per the seven-class framework. Evidence is
file:line, the [Algorithm] attribute, or the native shim entry.
- ContactNfpHoleNester / Sheet Nest (Hole-Aware) D5F10019 —
clean-room, with an evolved-fork increment. The BLF and Minkowski-sum NFP
math is cited (Burke et al. 2006; Bennell and Oliveira 2009) at
TwoD/HoleNestComponent.cs:25-34; the Int64 Minkowski lane runs in the vendored Clipper2nfp_kernel. The contact-adaptive rotation set and the part-in-part-hole inner-fit polygon are the increment over plain BLF. - Block Pack (Tree) / DLBF — clean-room from Kim 2025
(
Packing/BlockPackTreeComponent.cs:30, Doi 10.3390/computation13090211), with three Frahan extensions (deterministic seed, saw kerf, per-container forbidden boxes) noted in the README, closing Kim 8.2. - HeteroExt (FrahanHeterogeneousExtraction) —
facade-over-primitives. Composes
BlockCutOptSolver+Dlbf3dMixedSizePacker+ monument packer (BlockCutOptHeterogeneousComponents.cs:169-179). - Masonry Stability (RBE) + CRA — RBE clean-room from Kao et al. (2022) / Whiting
(
MasonryStabilityRbeComponent.cs:69-71, Kao et al. 2022 cited); the CRA alternating-convex certificate is the A-candidate (Kao 2022 solves a nonconvex NLP via IPOPT; ours is a managed soundness certificate). - Polygonal Masonry Sequence (B4E07A3C) + 3D (C5F18B4D) — clean-room
from Kim 2024 (
PolygonalMasonrySequenceComponent.cs:34, DETC2024-142563): Kahn toposort + reversed depth search on the install DAG. - GPR Fracture Extract (A7E0B0F1) — clean-room (tier B). Stolt 1978
f-k migration, Taner 1979 Hilbert attributes, USGS continuity, all cited
at
Quarry/GprFractureExtractComponent.cs:43-46. - Rubble Wall Settle (6514A1BB) — clean-room, Frahan-original settle
with Heyman 1966 COM-over-support stability
(
Masonry/RubbleWallSettleComponent.cs:35-36). - Trencadis Catalog Pack (F2D00007) — clean-room: CVD-Lloyd partition
(Lloyd 1982) + Hungarian assignment (Kuhn 1955)
(
Pack2DTrencadisCatalogComponent.cs:37-38); the slab-partitioned catalog placement is the Frahan extension. - Arch Voussoirs (D5F10012) / Pendentive Vault Voussoirs (D5F10013) —
clean-room from Frezier/Monge stereotomy
(
Voussoir/ArchVoussoirsComponent.cs:31-34); radial bed-joint cells. - Frahan Kintsugi Port — direct-port of PuzzleFusion++ (Wang, Chen,
Furukawa, ICLR 2025, arXiv:2406.00259), parity-verified on Breaking Bad
(
src/Frahan.Kintsugi.Port/README.md:3-4). LICENCE-CRITICAL: GPL-3.0 (Frahan.Kintsugi.Port/LICENSE.txt); the geometric path (Frahan.EdgeMatching.Core, Port mode off) carries no GPL. - Vector Fractures Loader (F2D00BEC) — wrapper / vendored-library over
NetTopologySuite.IO.Esri (
VectorFracturesLoaderComponent.cs:38). - Pack On Surface — facade-over-primitives: exact NFP-BLF on a BFF
chart + a classical triangle barycentric lift (attributed to the
mean-value-coordinate family, after Floater 2003; the shipped lift is plain
barycentric, not MVC) (
SurfacePacking/PackOnSurfaceComponent.cs:41-42). - Ashlar Pack (F1A2B3C4) — clean-room Frahan-original grid stacking
with a Gramazio/Kohler/Eichenhofer 2017 running-bond reference
(
Masonry/AshlarPackComponent.cs:31-32).
14.6 Status and what is left¶
The spine is built and the example routes are validated, but four architectural debts remain.
- RBE/CRA in the shipped facade. The component now calls
BuildPhysicsCorrected(the sign fix), but the invertedBuildis still present inRbeQpFormulation. Until it is removed, a caller can wire the wrong overload. Medium severity, scheduled. - RecoveryCascade has no GH consumer. The validated Core cascade
(
Masonry/Quarry/BlockCutOpt/RecoveryCascade.cs) is not reachable from any canvas;FractureBlockPackships its own duplicate recovery engine that calls neither RecoveryCascade nor BlockCutOptSolver. This is a silent-disagreement risk; resolution is facade-not-fork. - Duplicated kernels. Three Horn/Kabsch absolute-orientation copies and two Hungarian implementations should unify on one MathNet-SVD kernel. Low severity, but a real architectural seam.
- Four README-less canvases. Examples 01, 02, 03b, 28 ship
.gh+.3dmwith no README and no rendered PNG; their pipeline graphs in §14.2 are read from the canvas, not from a validated capture. Figure renders are pending. Low severity.
The honesty infrastructure that holds the architecture together is worth
stating: the KB registry with measured reproductions, the 0-overlap gating
with the util_stock = placed area / (sheet - holes) methodology, the
"REPORTED not gated" comments, the H-model counterexample as a regression
test, and [Algorithm] citations on 138 source files. The last battery state
was 1034 PASS / 0 FAIL / 147 SKIP (2026-06-14).
Figures¶









