CasaCOB Designer is a parametric design engine for modular cob (monolithic earth-straw construction) roundhouses, enabling architects and builders to explore sustainable, customizable structural and aesthetic configurations through an integrated 3D design interface and geometric validation pipeline.
From a single JSON parameter file it derives every coordinate, angle,
length, volume and quantity of a circular cob house — foundation, walls with
openings, timber posts, ring beam and a reciprocal-frame roof — and produces
2D drawings, a 3D model, CAD/mesh exports (STL/OBJ/STEP/DXF/SVG), a bill of
materials, a timber cut list and a bilingual (IT/EN) construction report
(PDF). No empirical constants in the geometry — every dimension, angle,
length and volume is derived analytically; material properties are cited,
overridable parameters (see docs/formulas.md).
Requires Python 3.13 and uv.
uv syncCLI — build the full artifact pipeline from the bundled example project:
uv run casacob validate examples/roundhouse.json
uv run casacob check examples/roundhouse.json
uv run casacob build examples/roundhouse.json --out out/
uv run casacob report examples/roundhouse.json --out out/validateloads and solves the project and prints key derived figures (net cob wall volume, ridge height, rafter axis length, contact shim/notch).checkruns the full design validator (collisions + geometric invariants) and prints its report; exits 3 on any error, writes nothing.buildruns the same validator first (nothing is written on a validation error, exit 3) and then writes every artifact under--out: mesh/CAD exports (STL/OBJ/STEP/DXF/SVG), dimensioned 2D drawings, a costed bill of materials (CSV+JSON), a 36-piece per-piece cut list (CSV), a coordinate dump, the collision census (collision_report.json) and validator report (validation.json), per-piece shop drawings (shop_drawings.pdf), and the bilingual PDF construction report (report.pdf, with a validation summary, tool/time estimates and an illustrated 15-step assembly sequence).reportwrites only the PDF construction report under--out, gated by the same validator.- See CLI commands below for
export-part,statusandlog.
Example project figures (examples/roundhouse.json, from casacob validate
and casacob build):
| Figure | Value |
|---|---|
| Net cob wall volume | 17.301 m³ |
| Ridge height | 4.928 m |
| Rafter axis length | 3.834 m |
| Contact shim thickness | 1.1 mm |
| Cut-list pieces | 36 |
| Assembly sequence | 15 steps, 24.63 person-days |
| Estimated total material cost | 1957.17 EUR |
UI — open the interactive designer (loads examples/roundhouse.json by
default, or the file passed as an argument):
uv run python -m casacob.main [file.json]v2.0 turns the parametric modeller into a construction engine: every element now knows its own identity, mass, connections, machining operations and site status — not just its geometry.
- Digital twin. Every physical element (wall, foundation, post, ring
beam segment, rafter, opening, joint) is a
Component(core/component.py,core/registry.py) with a deterministic UUID (uuid5of a fixed namespace and the element's graph node id — stable across regenerations and machines, seedocs/formulas.md), mass, world bounding box, centroid, placement transform, connections to its neighbours and machining operations. Site build progress is tracked per component in a JSON sidecar (casacob status), and every parameter save is recorded in a changelog sidecar (casacob log) — the project JSON itself always stays the single source of geometric truth. - Carpentry. Rafters are defined by their three construction points —
P1 (bearing on the ring beam), P2 (support on the previous
rafter), P3 (free eaves end) — with the central roof opening a
derived polygon, never an input (see
docs/formulas.md). The rafter-to-ring-beam bearing is cut as a birdsmouth seat by default (IRC-cited depth cap), the ring beam is an autonomous per-segment component with its own bolt drill operations and optional steel gusset plates (hardware.steel_plates), and any single connection's joint kind can be overridden per project (joints_overrides). - Analysis. A two-stage collision detector (AABB broad phase in pure
core, exact CAD boolean narrow phase) classifies every measured contact
as expected (a joint's own cut accounts for the overlap) or
interference (a real design conflict — e.g. an opening placed over a
post). A blocking validator runs before every export (
build,export-part, and every UI export/report action): on any error nothing is written, the CLI exits 3, andcasacob checkcan be run standalone. - Workshop outputs. A per-piece cut list with every drill/notch/end-cut
called out, illustrated single-part shop drawings collected into
shop_drawings.pdf, a costed bill of materials decomposed into the cob recipe (earth/straw/sand), tool checklists and person-day estimates per phase (core/costing.py,core/labor.py,core/tools.py), and an illustrated assembly sequence with prop/closing notes for the reciprocal roof's closing cycle. Every physical part can be exported individually or all at once (casacob export-part, or the UI's component tree panel).
v2.1 adds an "Anteprime" toolbar to the interactive designer, alongside the existing Apri/Salva JSON/Rigenera/per-format-export toolbar. Seven buttons — one per registered preview — open closable tabs in the centre panel on demand; a second click on the same button focuses the tab already open rather than duplicating it, and each preview tab can be closed individually (unlike the always-open 3D/2D tabs). Every tab shows a live, read-only render of one build output plus its own Esporta button:
| Toolbar button (preview id) | Content shown | Esporta writes |
|---|---|---|
Bill of Materials (bom) |
Table: Categoria, Descrizione, Quantità, Unità, Costo € | bom.csv |
Cut List (cut_list) |
Table: ID, Tipo, Materiale, Sezione, Lungh. m, Massa kg, Taglio in., Taglio fin., Fori, Scassi | cut_list.csv |
Assembly Sequence (assembly) |
Table: N., Fase, Componenti, Utensili, Giorni-persona | assembly.csv (preview-only export — not part of the full bundle below; the same sequence is already embedded in report.pdf) |
Coordinates (coordinates) |
Pretty-printed JSON coordinate dump (read-only) | coordinates.json |
Collisions (collisions) |
Pretty-printed JSON collision census (read-only) | collision_report.json |
Validation (validation) |
Table: Gravità, Regola, Componente, Messaggio | validation.json |
Shop Drawings (shop_drawings) |
Part picker + canvas rendering one piece's dimensioned drawing at a time | Esporta menu has two entries: "Tutte (PDF)" (every piece, shop_drawings.pdf) or "Pezzo mostrato" (only the shown piece, saved as SVG or PDF by extension) |
Opening a preview is never blocked by validation — the Collisions/Validation tabs exist precisely so an author can see why a model fails — but every Esporta action is validation-gated exactly like the existing whole-house exports: on a validation error the red banner reports the error count ("Validazione fallita: N errori — export bloccato") and nothing is written. The per-tab Esporta buttons open the file save dialog first, then validate; the toolbar's Esporta tutto validates before opening any dialog.
Esporta tutto writes the full 20-file build bundle into a chosen
folder, byte-identical to the CLI's build command: the three combined CAD
exports (house.stl/.obj/.step), plan/section DXF+SVG, the costed BOM
(CSV+JSON), the cut list, the coordinate dump, the collision and validation
reports, report.pdf, shop_drawings.pdf, and the illustrated drawings/
set (plan/section/elevation SVG, axonometry/exploded PNG).
Every human-facing string produced by CasaCOB Designer — UI labels and
menus, BOM/cut-list/assembly-sequence descriptions, validation messages,
and the PDF construction report — is drawn from a single catalog
(core/i18n/catalog.py, LABELS: {id: {it, en}}) through a Translator.
Machine identifiers are never translated and stay identical across
languages: JSON keys, the BOM category slug, cut-list
piece_id/kind/material/section, graph node ids (e.g. rafter:3),
JointKind values, validation rule slugs, and the full contents of
coordinates.json/collision_report.json. Only descriptions, headings,
messages and UI chrome change with the active language.
The active language is resolved with this precedence (first non-null value
wins): **CLI --lang / UI "Lingua" menu selection > persisted UI settings
project
report.language> default"it".**
- UI "Lingua / Language" menu — a menubar menu with two checkable
entries ("Italiano"/"English"). Selecting one switches every open
widget's text live (form labels, tabs, table headers, previews) and
persists the choice to
~/.casacob/settings.json({"language": "it"}or{"language": "en"}), so the next UI launch reopens in the same language regardless of which project is loaded. A corrupt or unreadable settings file is reported in the error banner rather than crashing the window; that session falls back to the project'sreport.language(or the default). - CLI
--lang {it,en}— accepted on every subcommand, either before or after it (casacob --lang en build ...andcasacob build ... --lang enboth work); overrides the language for that invocation only and is never persisted. The CLI does not consult~/.casacob/settings.json(that file only backs the UI menu), so for CLI runs the effective order is--lang> projectreport.language>"it". - Project
report.language— the optional"it"/"en"field in the project JSON'sreportobject (see JSON schema below); the project's own default whenever no CLI flag, menu selection or settings-file value applies.
Example — build the same project in both languages:
uv run casacob build examples/roundhouse.json --lang en --out out/produces the identical 20-artifact bundle as an it build — same
category/piece_id/rule slugs, same JSON keys, same
coordinates.json/collision_report.json content — with every
description/message/heading (bom.csv, cut_list.csv,
validation.json, report.pdf) rendered in English instead of Italian.
Every subcommand takes the project JSON path as its first argument;
build/report/export-part additionally require --out. A
ParameterError/GeometryError while loading or solving the project maps
to a printed message and exit code 2; build/report/export-part
additionally run the full validator FIRST and exit 3 (nothing written) on
any validation error.
| Command | What it does |
|---|---|
validate <json> |
Loads and solves the project, prints key derived figures. |
check <json> |
Runs the full validator (collisions + geometric invariants) and prints its report (errors, then warnings, then "OK"); exit 0 clean, exit 3 on any error. Writes nothing. |
build <json> --out DIR |
Writes every artifact (see Quickstart above). |
report <json> --out DIR |
Writes only report.pdf. |
export-part <json> --part <node_id>|all --format step|stl|dxf|svg --out DIR |
Exports one physical part (e.g. rafter:3) or every solid-bearing part (--part all) to its own file. STEP/STL export the built solid with its machining operations applied; DXF/SVG render a dimensioned single-part shop drawing (only available for posts/ring beam segments/rafters — a single wall/foundation DXF/SVG request exits 2; under --part all those two are silently skipped with a printed note). |
status <json> [--set NODE_ID STATUS] |
Prints one row per component (node id, kind, short uuid, status); --set updates one component's status (planned/cut/installed/verified) and saves the <project>.site-status.json sidecar. Orphaned sidecar entries (from a component the project no longer builds) are reported, never silently dropped. |
log <json> |
Prints the project's parameter changelog (<project>.log.json): one block per save, each changed leaf path with its before/after value. |
Examples:
uv run casacob check examples/roundhouse.json
uv run casacob export-part examples/roundhouse.json --part rafter:3 --format step --out out/parts/
uv run casacob export-part examples/roundhouse.json --part all --format stl --out out/parts/
uv run casacob status examples/roundhouse.json
uv run casacob status examples/roundhouse.json --set rafter:3 installed
uv run casacob log examples/roundhouse.jsonAll lengths in metres, all masses in kilograms, angles in the JSON file are
expressed in degrees (converted to radians at load time). Every
constraint below is enforced by the loader (src/casacob/core/loader.py),
which raises a ParameterError naming the JSON path and the violated rule.
| Field | Unit | Constraint |
|---|---|---|
type |
— | informational only, currently ignored (v0.1 supports circular rooms only) |
inner_diameter |
m | > 0 |
wall_thickness |
m | > 0 |
wall_height |
m | > 0 |
Each entry:
| Field | Unit | Constraint |
|---|---|---|
kind |
— | "door" or "window" |
azimuth_deg |
deg | any value (CCW from +X, not restricted to [0, 360)) |
width |
m | > 0 |
height |
m | > 0 |
sill_height |
m | optional, default 0; ≥ 0 |
Cross-field: sill_height + height must be ≤ room.wall_height; the angular
spans of all openings on the wall centerline circle must not overlap.
| Field | Unit | Constraint |
|---|---|---|
depth |
m | > 0 |
width |
m | > 0 |
gravel_depth |
m | > 0, and < foundation.depth |
geotextile_overlap |
m | > 0 |
| Field | Unit | Constraint |
|---|---|---|
count |
— | integer ≥ 3 |
diameter |
m | > 0 |
length |
m | > 0, and ≥ room.wall_height + posts.embedment |
embedment |
m | > 0 |
| Field | Unit | Constraint |
|---|---|---|
type |
— | only "reciprocal" is supported in v0.1 |
rafters |
— | integer ≥ 3 |
slope_deg |
deg | > 0 |
eaves |
m | > 0 |
timber_section |
[width, depth] (m) |
array of exactly 2 numbers, each > 0 |
central_opening_radius |
m | > 0, and central_opening_radius + timber_width/2 < room.centerline_radius |
max_shim |
m | optional, default 0.02; ≥ 0. If the solved rafter-contact shim exceeds it, a GeometryError is raised with the maximum feasible slope |
chamfer |
m | optional, default 0; ≥ 0. When > 0, emits 4 chamfer machining operations per rafter (edge-break of this size); 0 emits none |
Fastener choices shared by every generated joint (core/joints.py).
| Field | Unit | Constraint |
|---|---|---|
bolt_diameter |
m | optional, default 0.012; > 0. Diameter of every bearing/steel-plate drill operation |
steel_plates |
— | optional boolean, default false. When true, each ring beam vertex additionally gets a steel gusset plate (STEEL_PLATE joint) reinforcing the half-lap |
| Field | Unit | Default |
|---|---|---|
cob.density |
kg/m³ | 1700 |
cob.specific_heat |
J/(kg·K) | 1000 |
timber.density |
kg/m³ | 550 |
stone.density |
kg/m³ | 1600 |
cob.recipe.earth |
fraction | 0.5 |
cob.recipe.straw |
fraction | 0.2 |
cob.recipe.sand |
fraction | 0.3 |
cob.recipe (volumetric mix ratios of the wet cob batch, see
docs/formulas.md) is all-or-nothing: when present, earth/straw/sand
are all required, each > 0, and must sum to 1.0 within 1e-6.
An object of {category: unit_price}; every key must already be one of the
categories below (an unknown key is a ParameterError), each value > 0 (the
category's unit is fixed, never overridable). Defaults are indicative
Italian market prices, 2026 — see docs/formulas.md for the full cited
table (earth, sand, straw_bale, rubble_stone, gravel,
geotextile, drain_pipe, timber, bolts, structural_screws,
steel_plates).
An object of {phase: productivity} (units per person-day); every key must
already be one of the phases below (an unknown key is a ParameterError),
each value > 0. Defaults are indicative planning figures — see
docs/formulas.md for the full cited table (excavation, stone_fill,
cob_lift, timber_prep, assembly).
Each entry swaps one connection's default joint kind:
| Field | Unit | Constraint |
|---|---|---|
connection |
— | array of exactly 2 node-id strings, e.g. ["rafter:3", "ringbeam_segment:2"] (order-insensitive; "ringbeam"/"ringbeam_segment" both denote a ring beam segment host) |
joint |
— | one of the JointKind values (half_lap, saddle, mortise, tenon, birdsmouth, plus the hardware-only bolt/structural_screw/steel_plate, which are never meaningful override targets) |
A connection naming a pair with no matching joint raises a GeometryError
(at model-build time, listing how many connections are actually available)
rather than a loader ParameterError, since matching requires the solved
joint census.
| Field | Unit | Constraint |
|---|---|---|
language |
— | optional, default "it"; "it" or "en" |
See examples/roundhouse.json for a complete,
valid project file, docs/formulas.md for every formula
derived from these parameters, and
docs/superpowers/specs/2026-07-19-casacob-v2-design.md
for the v2.0 design spec (docs/superpowers/specs/2026-07-18-casacob-designer-design.md
for the original v0.1 spec).
Clean Architecture, two layers plus composition roots. Dependencies point
inward only: core never imports adapters, ui, cli.py or
main.py; adapters depend on core but not on each other; ui/cli.py
depend on both and wire everything together.
src/casacob/
core/ # PURE domain: numpy/scipy/shapely/networkx only, no I/O, no CAD, no Qt
params.py # frozen parameter dataclasses (validated ProjectParams)
loader.py # JSON -> ProjectParams (validation)
model.py # build_model(params) -> HouseModel (the one entry point)
component.py # digital-twin primitives: Operation, ConnectionRef, Component, UUIDs
registry.py # ComponentRegistry: builds one Component per physical element
joints.py # timber joint library + joint census (birdsmouth, half-lap, steel plate...)
site_status.py # per-component site-status sidecar (planned/cut/installed/verified)
project_log.py # project parameter changelog sidecar
validation.py # blocking pre-export validator (collisions + geometric invariants)
analysis/ # two-stage AABB/CAD collision detection + classification
costing.py # cited unit-price defaults + override resolution
labor.py # cited labour-productivity defaults + override resolution
tools.py # hand/power tool checklists per machining operation / build phase
geometry/ # circle, regular polygon, 3D frame/transform primitives
walls/ # cob wall, openings, posts
foundation/ # trench, rubble fill, drainage
roof/ # reciprocal frame solver (P1/P2/P3 rafters), ring beam
materials/ # literature-sourced material defaults + cob recipe
reports/ # BOM, per-piece cut list, assembly sequence, coordinate dump (pure data)
adapters/ # turn core data into artifacts
cad/ # cadquery: solids, STEP/DXF/SVG export, interference measurement
mesh/ # trimesh: STL/OBJ export, viewport meshes
drawing2d/ # matplotlib: dimensioned plan/section/elevation/axonometry/exploded/parts
pdf/ # reportlab: bilingual (IT/EN) construction report + shop drawings PDF
export_part.py # single-part STEP/STL/DXF/SVG export, shared by the CLI and the UI
ui/ # PySide6 main window (incl. component tree + per-item export),
# parameter form, 3D viewport, 2D tab
cli.py # composition root (headless): validate|check|build|report|export-part|status|log
main.py # composition root (GUI)
JSON --loader.py--> ProjectParams --model.py--> HouseModel --adapters--> artifacts
(core, pure) (+ ComponentRegistry) ^
cli.py / main.py
(composition roots)
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv run pytestAll four must pass clean. Package-wide coverage is enforced at ≥ 90%
(pytest-cov's --cov-fail-under=90, see pyproject.toml); src/casacob/core
itself currently runs at ~98%.
Not in v2.0, but the architecture is designed not to block (unchanged since
v0.1 — see docs/superpowers/specs/2026-07-19-casacob-v2-design.md's
Non-goals):
- Multi-room/corridor layouts (new nodes in the component graph)
- Curved free walls
- Domes and other roof types (new
RoofBuilderstrategies behind the same interface) - Load analysis and assembly simulation
- MEP / PV / water systems (new component families + BOM sections)
- Greenhouse/off-grid modules
- BIM export (the v2.0 component registry — stable UUIDs, mass, connections, operations — is designed as its seed)
- FreeCAD/OpenCascade adapter as an alternative
SolidBackend
MIT — see LICENSE.
CasaCOB Designer è un motore di progettazione parametrica per case circolari in terra cruda (COB) modulare, che consente ad architetti e costruttori di esplorare configurazioni strutturali e estetiche sostenibili e personalizzabili attraverso un'interfaccia di progettazione 3D integrata e una pipeline di validazione geometrica.
A partire da un unico file JSON di parametri, il motore deriva ogni
coordinata, angolo, lunghezza, volume e quantità di una casa circolare in
cob — fondazione, pareti con aperture, pali in legno, cordolo (ring beam) e
tetto a struttura reciproca — e produce disegni 2D, un modello 3D, export
CAD/mesh (STL/OBJ/STEP/DXF/SVG), un computo metrico dei materiali, una
distinta di taglio del legname e una relazione di cantiere bilingue (IT/EN)
in PDF. Nessuna costante empirica nella geometria — ogni dimensione, angolo,
lunghezza e volume è derivato analiticamente; le proprietà dei materiali sono
parametri sovrascrivibili con fonte citata (vedi
docs/formulas.md).
Richiede Python 3.13 e uv.
uv syncCLI — genera l'intera pipeline di output a partire dal progetto di esempio incluso:
uv run casacob validate examples/roundhouse.json
uv run casacob check examples/roundhouse.json
uv run casacob build examples/roundhouse.json --out out/
uv run casacob report examples/roundhouse.json --out out/validatecarica e risolve il progetto stampando le grandezze derivate principali (volume netto della parete in cob, altezza di colmo, lunghezza asse della falda, spessore dello spessoramento/intaglio di contatto).checkesegue il validatore completo (collisioni + invarianti geometrici) e ne stampa il report; esce con codice 3 in presenza di errori, senza scrivere nulla.buildesegue prima lo stesso validatore (nulla viene scritto in caso di errore, uscita 3) e poi scrive tutti gli output in--out: export mesh/CAD (STL/OBJ/STEP/DXF/SVG), disegni 2D quotati, un computo metrico con costi (CSV+JSON), una distinta di taglio per singolo pezzo di 36 elementi (CSV), un dump delle coordinate, il censimento delle collisioni (collision_report.json) e il report di validazione (validation.json), i disegni di officina per singolo pezzo (shop_drawings.pdf) e la relazione PDF bilingue di cantiere (report.pdf, con sommario di validazione, stima di attrezzi/tempi e sequenza di montaggio illustrata in 15 fasi).reportscrive solo la relazione PDF di cantiere in--out, con lo stesso controllo del validatore.- Vedi Comandi CLI più sotto per
export-part,statuselog.
Grandezze del progetto di esempio (examples/roundhouse.json, da
casacob validate e casacob build):
| Grandezza | Valore |
|---|---|
| Volume netto parete in cob | 17,301 m³ |
| Altezza di colmo | 4,928 m |
| Lunghezza asse falda | 3,834 m |
| Spessore spessoramento di contatto | 1,1 mm |
| Pezzi nella distinta di taglio | 36 |
| Sequenza di montaggio | 15 fasi, 24,63 persona-giorno |
| Costo totale stimato dei materiali | 1957,17 EUR |
UI — apre l'interfaccia grafica del designer (carica di default
examples/roundhouse.json, oppure il file passato come argomento):
uv run python -m casacob.main [file.json]La v2.0 trasforma il modellatore parametrico in un motore costruttivo: ogni elemento conosce ora la propria identità, massa, connessioni, lavorazioni e stato di cantiere — non solo la propria geometria.
- Gemello digitale. Ogni elemento fisico (parete, fondazione, palo,
segmento di cordolo, trave, apertura, giunto) è un
Component(core/component.py,core/registry.py) con un UUID deterministico (uuid5di un namespace fisso e dell'id nodo del grafo dell'elemento — stabile tra rigenerazioni e macchine diverse, vedidocs/formulas.md), massa, bounding box mondiale, centroide, trasformazione di posizionamento, connessioni con gli elementi vicini e lavorazioni di officina. L'avanzamento di cantiere per componente è tracciato in un file sidecar JSON (casacob status), e ogni salvataggio dei parametri viene registrato in un changelog sidecar (casacob log) — il file JSON del progetto resta sempre l'unica fonte di verità geometrica. - Carpenteria. Le travi sono definite dai loro tre punti costruttivi —
P1 (appoggio sul cordolo), P2 (appoggio sulla trave precedente),
P3 (estremità libera di gronda) — con l'apertura centrale del tetto
poligono derivato, mai un dato di ingresso (vedi
docs/formulas.md). L'appoggio trave-cordolo è tagliato di default a incastro birdsmouth (profondità massima citata da normativa IRC), il cordolo è un componente autonomo per segmento con proprie forature per bulloni e piastre di acciaio opzionali (hardware.steel_plates), e il tipo di giunto di ogni singola connessione può essere sovrascritto per progetto (joints_overrides). - Analisi. Un rilevatore di collisioni a due stadi (AABB nel core puro,
booleano CAD esatto per la fase stretta) classifica ogni contatto
misurato come atteso (il taglio del giunto ne rende conto) o
interferenza (un vero conflitto di progetto — es. un'apertura posta
sopra un palo). Un validatore bloccante viene eseguito prima di ogni
esportazione (
build,export-part, e ogni azione di esportazione/relazione della UI): in presenza di errori non viene scritto nulla, la CLI esce con codice 3, ecasacob checkpuò essere eseguito da solo. - Output di officina. Una distinta di taglio per singolo pezzo con ogni
foro/intaglio/taglio di testa esplicitato, disegni di officina per
singolo pezzo raccolti in
shop_drawings.pdf, un computo metrico con costi scomposto nella ricetta del cob (terra/paglia/sabbia), checklist degli attrezzi e stime in persona-giorno per fase (core/costing.py,core/labor.py,core/tools.py), e una sequenza di montaggio illustrata con note su puntellamento e chiusura del ciclo reciproco del tetto. Ogni pezzo fisico può essere esportato singolarmente o tutti insieme (casacob export-part, oppure il pannello ad albero dei componenti della UI).
La v2.1 aggiunge alla UI interattiva una barra strumenti "Anteprime", accanto alla barra esistente Apri/Salva JSON/Rigenera/export per formato. Sette pulsanti — uno per ogni anteprima registrata — aprono su richiesta schede chiudibili nel pannello centrale; un secondo clic sullo stesso pulsante porta in primo piano la scheda già aperta invece di duplicarla, e ogni scheda di anteprima può essere chiusa singolarmente (a differenza delle schede sempre aperte 3D/2D). Ogni scheda mostra il rendering di sola lettura di un output di build, con un proprio pulsante Esporta:
| Pulsante in barra (id anteprima) | Contenuto mostrato | Esporta scrive |
|---|---|---|
Bill of Materials (bom) |
Tabella: Categoria, Descrizione, Quantità, Unità, Costo € | bom.csv |
Cut List (cut_list) |
Tabella: ID, Tipo, Materiale, Sezione, Lungh. m, Massa kg, Taglio in., Taglio fin., Fori, Scassi | cut_list.csv |
Assembly Sequence (assembly) |
Tabella: N., Fase, Componenti, Utensili, Giorni-persona | assembly.csv (export solo-anteprima — non fa parte del pacchetto completo sotto; la stessa sequenza è già incorporata in report.pdf) |
Coordinates (coordinates) |
Dump JSON di sola lettura delle coordinate | coordinates.json |
Collisions (collisions) |
Censimento collisioni in JSON di sola lettura | collision_report.json |
Validation (validation) |
Tabella: Gravità, Regola, Componente, Messaggio | validation.json |
Shop Drawings (shop_drawings) |
Selettore pezzo + canvas che disegna, quotato, un pezzo alla volta | Il menu Esporta ha due voci: "Tutte (PDF)" (ogni pezzo, shop_drawings.pdf) oppure "Pezzo mostrato" (solo il pezzo visualizzato, salvato come SVG o PDF in base all'estensione) |
L'apertura di un'anteprima non è mai bloccata dalla validazione — le schede Collisions/Validation esistono proprio perché l'autore possa vedere perché un modello fallisce — ma ogni azione Esporta è vincolata alla validazione esattamente come le esportazioni dell'intera casa già esistenti: in caso di errore di validazione il banner rosso riporta il numero di errori ("Validazione fallita: N errori — export bloccato") e non viene scritto nulla. I pulsanti Esporta per singola scheda aprono prima la finestra di salvataggio file, poi convalidano; il pulsante Esporta tutto della barra strumenti convalida prima di aprire qualsiasi finestra di dialogo.
Esporta tutto scrive l'intero pacchetto di 20 file di build in una
cartella scelta, identico byte-per-byte a quello del comando CLI build: i
tre export CAD combinati (house.stl/.obj/.step), DXF+SVG di
pianta/sezione, il computo metrico con costi (CSV+JSON), la distinta di
taglio, il dump delle coordinate, i report di collisioni e validazione,
report.pdf, shop_drawings.pdf e la serie illustrata drawings/ (SVG di
pianta/sezione/prospetto, PNG di assonometria/esploso).
Ogni stringa rivolta all'utente prodotta da CasaCOB Designer — etichette e
menu della UI, descrizioni di computo metrico/distinta di taglio/sequenza
di montaggio, messaggi di validazione e la relazione PDF di cantiere —
proviene da un unico catalogo (core/i18n/catalog.py,
LABELS: {id: {it, en}}) tramite un Translator. Gli identificatori
macchina non vengono mai tradotti e restano identici in entrambe le
lingue: chiavi JSON, lo slug category del computo metrico,
piece_id/kind/material/section della distinta di taglio, gli id
nodo del grafo (es. rafter:3), i valori JointKind, gli slug rule di
validazione, e l'intero contenuto di
coordinates.json/collision_report.json. Solo descrizioni, intestazioni,
messaggi e testi dell'interfaccia cambiano con la lingua attiva.
La lingua attiva viene risolta con questa precedenza (vince il primo
valore non nullo): selezione da --lang CLI / menu "Lingua" della UI >
impostazioni UI persistite > report.language di progetto > default
"it".
- Menu UI "Lingua / Language" — un menu in barra dei menu con due voci
selezionabili ("Italiano"/"English"). Selezionarne una cambia dal vivo
il testo di ogni widget aperto (etichette del form, schede, intestazioni
di tabella, anteprime) e salva la scelta in
~/.casacob/settings.json({"language": "it"}oppure{"language": "en"}), cosicché il successivo avvio della UI riparta nella stessa lingua indipendentemente dal progetto caricato. Un file impostazioni corrotto o illeggibile viene segnalato nel banner di errore invece di far crashare la finestra; per quella sessione si ricade sureport.languagedel progetto (o sul default). - CLI
--lang {it,en}— accettato su ogni sottocomando, sia prima sia dopo (funzionano siacasacob --lang en build ...siacasacob build ... --lang en); sovrascrive la lingua solo per quella invocazione e non viene mai salvato. La CLI non consulta~/.casacob/settings.json(quel file serve solo al menu della UI), quindi per le esecuzioni CLI l'ordine effettivo è--lang>report.languagedi progetto >"it". report.languagedi progetto — il campo opzionale"it"/"en"nell'oggettoreportdel JSON di progetto (vedi Schema JSON più sotto); il default del progetto stesso quando non si applica alcun valore da flag CLI, selezione menu o file impostazioni.
Esempio — genera lo stesso progetto in entrambe le lingue:
uv run casacob build examples/roundhouse.json --lang en --out out/produce lo stesso pacchetto di 20 file di una build it — identici slug
category/piece_id/rule, identiche chiavi JSON, identico contenuto di
coordinates.json/collision_report.json — con ogni
description/message/intestazione (bom.csv, cut_list.csv,
validation.json, report.pdf) resa in inglese invece che in italiano.
Ogni sottocomando accetta il percorso del JSON di progetto come primo
argomento; build/report/export-part richiedono anche --out. Un
ParameterError/GeometryError durante il caricamento o la risoluzione
del progetto genera un messaggio stampato e il codice di uscita 2;
build/report/export-part eseguono prima il validatore completo ed
escono con codice 3 (nulla viene scritto) in presenza di errori di
validazione.
| Comando | Cosa fa |
|---|---|
validate <json> |
Carica e risolve il progetto, stampa le grandezze derivate principali. |
check <json> |
Esegue il validatore completo (collisioni + invarianti geometrici) e ne stampa il report (errori, poi avvisi, poi "OK"); uscita 0 se pulito, uscita 3 in presenza di errori. Non scrive nulla. |
build <json> --out DIR |
Scrive tutti gli output (vedi Avvio rapido sopra). |
report <json> --out DIR |
Scrive solo report.pdf. |
export-part <json> --part <node_id>|all --format step|stl|dxf|svg --out DIR |
Esporta un singolo pezzo fisico (es. rafter:3) o ogni pezzo dotato di solido (--part all) nel proprio file. STEP/STL esportano il solido costruito con le lavorazioni applicate; DXF/SVG generano un disegno di officina quotato per singolo pezzo (disponibile solo per pali/segmenti di cordolo/travi — una richiesta DXF/SVG singola su wall/foundation esce con codice 2; con --part all questi due vengono saltati silenziosamente con una nota stampata). |
status <json> [--set NODE_ID STATUS] |
Stampa una riga per componente (id nodo, tipo, uuid breve, stato); --set aggiorna lo stato di un componente (planned/cut/installed/verified) e salva il sidecar <progetto>.site-status.json. Le voci orfane del sidecar (di un componente che il progetto non genera più) sono segnalate, mai eliminate silenziosamente. |
log <json> |
Stampa il changelog dei parametri del progetto (<progetto>.log.json): un blocco per ogni salvataggio, con ogni percorso di parametro modificato e il suo valore prima/dopo. |
Esempi:
uv run casacob check examples/roundhouse.json
uv run casacob export-part examples/roundhouse.json --part rafter:3 --format step --out out/parts/
uv run casacob export-part examples/roundhouse.json --part all --format stl --out out/parts/
uv run casacob status examples/roundhouse.json
uv run casacob status examples/roundhouse.json --set rafter:3 installed
uv run casacob log examples/roundhouse.jsonTutte le lunghezze sono in metri, le masse in chilogrammi, gli angoli nel
file JSON sono espressi in gradi (convertiti in radianti al momento del
caricamento). Ogni vincolo elencato di seguito è verificato dal loader
(src/casacob/core/loader.py), che solleva un ParameterError indicando il
percorso JSON e la regola violata.
| Campo | Unità | Vincolo |
|---|---|---|
type |
— | solo informativo, attualmente ignorato (v0.1 supporta solo stanze circolari) |
inner_diameter |
m | > 0 |
wall_thickness |
m | > 0 |
wall_height |
m | > 0 |
Ogni elemento:
| Campo | Unità | Vincolo |
|---|---|---|
kind |
— | "door" o "window" |
azimuth_deg |
gradi | qualsiasi valore (CCW da +X, non limitato a [0, 360)) |
width |
m | > 0 |
height |
m | > 0 |
sill_height |
m | opzionale, default 0; ≥ 0 |
Vincolo incrociato: sill_height + height deve essere ≤ room.wall_height;
gli intervalli angolari di tutte le aperture sulla circonferenza di asse
parete non devono sovrapporsi.
| Campo | Unità | Vincolo |
|---|---|---|
depth |
m | > 0 |
width |
m | > 0 |
gravel_depth |
m | > 0, e < foundation.depth |
geotextile_overlap |
m | > 0 |
| Campo | Unità | Vincolo |
|---|---|---|
count |
— | intero ≥ 3 |
diameter |
m | > 0 |
length |
m | > 0, e ≥ room.wall_height + posts.embedment |
embedment |
m | > 0 |
| Campo | Unità | Vincolo |
|---|---|---|
type |
— | in v0.1 è supportato solo "reciprocal" |
rafters |
— | intero ≥ 3 |
slope_deg |
gradi | > 0 |
eaves |
m | > 0 |
timber_section |
[larghezza, altezza] (m) |
array di esattamente 2 numeri, ciascuno > 0 |
central_opening_radius |
m | > 0, e central_opening_radius + timber_width/2 < room.centerline_radius |
max_shim |
m | opzionale, default 0.02; ≥ 0. Se lo spessoramento di contatto risolto lo supera, viene sollevato un GeometryError con la pendenza massima ammissibile |
chamfer |
m | opzionale, default 0; ≥ 0. Se > 0, genera 4 lavorazioni chamfer per trave (smusso di questa dimensione); 0 non ne genera nessuna |
Scelte di ferramenta condivise da ogni giunto generato (core/joints.py).
| Campo | Unità | Vincolo |
|---|---|---|
bolt_diameter |
m | opzionale, default 0.012; > 0. Diametro di ogni foratura di appoggio/piastra |
steel_plates |
— | opzionale booleano, default false. Se true, ogni vertice del cordolo riceve in aggiunta una piastra di rinforzo in acciaio (giunto STEEL_PLATE) sul mezzo-legno |
| Campo | Unità | Default |
|---|---|---|
cob.density |
kg/m³ | 1700 |
cob.specific_heat |
J/(kg·K) | 1000 |
timber.density |
kg/m³ | 550 |
stone.density |
kg/m³ | 1600 |
cob.recipe.earth |
frazione | 0,5 |
cob.recipe.straw |
frazione | 0,2 |
cob.recipe.sand |
frazione | 0,3 |
cob.recipe (rapporti volumetrici della miscela umida di cob, vedi
docs/formulas.md) è tutto-o-niente: se presente, earth/straw/sand
sono tutti obbligatori, ciascuno > 0, e la loro somma deve fare 1,0 con
tolleranza 1e-6.
Un oggetto {categoria: prezzo_unitario}; ogni chiave deve già essere una
delle categorie sottostanti (una chiave sconosciuta genera un
ParameterError), ogni valore > 0 (l'unità della categoria è fissa, mai
sovrascrivibile). I default sono prezzi indicativi del mercato italiano,
2026 — vedi docs/formulas.md per la tabella completa citata (earth,
sand, straw_bale, rubble_stone, gravel, geotextile, drain_pipe,
timber, bolts, structural_screws, steel_plates).
Un oggetto {fase: produttività} (unità per persona-giorno); ogni chiave
deve già essere una delle fasi sottostanti (una chiave sconosciuta genera
un ParameterError), ogni valore > 0. I default sono cifre di
pianificazione indicative — vedi docs/formulas.md per la tabella completa
citata (excavation, stone_fill, cob_lift, timber_prep, assembly).
Ogni elemento sostituisce il tipo di giunto di default di una connessione:
| Campo | Unità | Vincolo |
|---|---|---|
connection |
— | array di esattamente 2 stringhe id-nodo, es. ["rafter:3", "ringbeam_segment:2"] (non ordinato; "ringbeam"/"ringbeam_segment" denotano entrambi un segmento di cordolo) |
joint |
— | uno dei valori JointKind (half_lap, saddle, mortise, tenon, birdsmouth, più i soli di ferramenta bolt/structural_screw/steel_plate, mai bersagli significativi di sostituzione) |
Una connection che nomina una coppia senza giunto corrispondente solleva
un GeometryError (in fase di costruzione del modello, elencando quante
connessioni sono effettivamente disponibili), non un ParameterError del
loader, poiché la corrispondenza richiede il censimento dei giunti risolto.
| Campo | Unità | Vincolo |
|---|---|---|
language |
— | opzionale, default "it"; "it" o "en" |
Vedi examples/roundhouse.json per un file di
progetto completo e valido, docs/formulas.md per ogni
formula derivata da questi parametri, e
docs/superpowers/specs/2026-07-19-casacob-v2-design.md
per il documento di design v2.0
(docs/superpowers/specs/2026-07-18-casacob-designer-design.md per lo
spec v0.1 originale).
Clean Architecture, due livelli più i composition root. Le dipendenze
puntano solo verso l'interno: core non importa mai adapters, ui,
cli.py né main.py; gli adapter dipendono da core ma non tra loro;
ui/cli.py dipendono da entrambi e li collegano.
src/casacob/
core/ # dominio PURO: solo numpy/scipy/shapely/networkx, nessun I/O, CAD o Qt
params.py # dataclass dei parametri (ProjectParams validati e immutabili)
loader.py # JSON -> ProjectParams (validazione)
model.py # build_model(params) -> HouseModel (l'unico punto di ingresso)
component.py # primitive del gemello digitale: Operation, ConnectionRef, Component, UUID
registry.py # ComponentRegistry: costruisce un Component per ogni elemento fisico
joints.py # libreria giunti in legno + censimento (birdsmouth, mezzo-legno, piastra...)
site_status.py # sidecar stato di cantiere per componente (planned/cut/installed/verified)
project_log.py # sidecar changelog dei parametri di progetto
validation.py # validatore bloccante pre-esportazione (collisioni + invarianti geometrici)
analysis/ # rilevamento collisioni a due stadi AABB/CAD + classificazione
costing.py # valori di default dei prezzi unitari citati + risoluzione override
labor.py # valori di default di produttività citati + risoluzione override
tools.py # checklist attrezzi manuali/elettrici per lavorazione/fase di cantiere
geometry/ # cerchio, poligono regolare, primitive di trasformazione 3D
walls/ # parete in cob, aperture, pali
foundation/ # trincea, riempimento in pietrame, drenaggio
roof/ # solutore della struttura reciproca (travi P1/P2/P3), cordolo
materials/ # valori di default dei materiali (fonti di letteratura) + ricetta cob
reports/ # computo metrico, distinta di taglio per pezzo, sequenza di montaggio, dump coordinate
adapters/ # trasformano i dati del core in output
cad/ # cadquery: solidi, export STEP/DXF/SVG, misura interferenze
mesh/ # trimesh: export STL/OBJ, mesh per il viewport
drawing2d/ # matplotlib: pianta/sezione/prospetto/assonometria/esploso/pezzi quotati
pdf/ # reportlab: relazione di cantiere bilingue (IT/EN) + PDF disegni di officina
export_part.py # export STEP/STL/DXF/SVG di un singolo pezzo, condiviso da CLI e UI
ui/ # finestra principale PySide6 (incl. albero componenti + export per pezzo),
# form parametri, viewport 3D, scheda 2D
cli.py # composition root (headless): validate|check|build|report|export-part|status|log
main.py # composition root (GUI)
JSON --loader.py--> ProjectParams --model.py--> HouseModel --adapter--> output
(core, puro) (+ ComponentRegistry) ^
cli.py / main.py
(composition root)
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv run pytestTutti e quattro i comandi devono passare senza errori. La copertura
dell'intero pacchetto è imposta a ≥ 90% (--cov-fail-under=90 di
pytest-cov, vedi pyproject.toml); src/casacob/core da solo è
attualmente al ~98%.
Non presenti in v2.0, ma l'architettura è pensata per non bloccarle
(invariata rispetto alla v0.1 — vedi le "Non-goals" di
docs/superpowers/specs/2026-07-19-casacob-v2-design.md):
- Configurazioni multi-stanza/corridoio (nuovi nodi nel grafo dei componenti)
- Pareti libere curve
- Cupole e altre tipologie di tetto (nuove strategie
RoofBuilderdietro la stessa interfaccia) - Analisi dei carichi e simulazione del montaggio
- Impianti MEP / fotovoltaico / idrici (nuove famiglie di componenti + sezioni del computo metrico)
- Moduli serra/off-grid
- Export BIM (il registro componenti v2.0 — UUID stabili, massa, connessioni, lavorazioni — è pensato come suo seme)
- Adapter FreeCAD/OpenCascade come
SolidBackendalternativo
MIT — vedi LICENSE.