Un generatore di IKEA Billy con OpenSCAD


Prima delle quote, mi interessa il tipo di oggetto. Nel furniture design molti mobili si possono ricondurre all’archetipo del cabinet, o carcassa: una struttura che contiene un volume e ne mantiene la forma. I suoi elementi ricorrenti sono fianchi, piani, base, schienale e compartimenti o ripiani. Ante e cassetti sono opzionali; anche la base può assumere la forma di uno zoccolo, un telaio o semplici piedini.

La voce Cabinetry di Wikipedia è un buon promemoria: non esiste una distinta universale del mobile, esiste una famiglia di relazioni che ricompare con soluzioni costruttive diverse. La carcassa è il sistema strutturale; ripiani, ferramenta, ante e finiture si appoggiano a quel sistema.

Una Billy è un esempio quasi disarmante: niente ante da parametrizzare, ma abbastanza elementi da rendere visibili queste relazioni. Il modello che ho scritto non vuole essere un disegno costruttivo ufficiale IKEA. È un esercizio sul passaggio dal volume finito ai pannelli, e soprattutto sul modo di non perdere quel legame quando si cambia una scelta progettuale.

Il generatore parametrico segue questa sequenza: descrive il volume, lo scompone in pannelli con woodworkers-lib, costruisce la vista del mobile e prepara una BOM (bill of materials, distinta dei pannelli) per cutplanner. Il piano di taglio arriva dopo: aiuta a fabbricare, ma non prende decisioni di furniture design.

Descrivere il volume

La prima scelta è partire dalle dimensioni esterne e non dai pezzi. Nel codice width_l, depth_w e height_h descrivono rispettivamente larghezza, profondità e altezza; wood_thickness e back_thickness descrivono i materiali. Il punto importante non sono i valori iniziali, ma le dipendenze:

width_l = 800;
depth_w = 280;
height_h = 1900;
wood_thickness = 18;
back_thickness = 6;
has_back = true;
back_style = "nailed";
toekick_height = 70;

TH = wood_thickness;
carcass_h = height_h - toekick_height;
carcass_depth =
    (has_back && back_style == "nailed") ? depth_w - back_thickness : depth_w;
carcass = [width_l, carcass_depth, carcass_h];

La profondità della carcassa cambia in base a come si monta lo schienale. Applicato dietro, lo schienale occupa parte della profondità finita; inserito in scanalatura, resta dentro il volume e modifica il modo in cui si incontrano fianchi, fondo e cappello. È una relazione progettuale generale, non un dettaglio della Billy.

Lo stesso vale per un pannello inserito tra due fianchi: la sua larghezza deriva dalla luce interna, quindi dalla larghezza esterna meno i due spessori. Se cambio wood_thickness, la relazione continua a valere. Non devo rincorrere la quota del ripiano con una correzione manuale.

Pensare per facce e pannelli

La libreria di fxdave propone una metafora che trovo utile: prima immagino una scatola, poi sostituisco le sue facce con pannelli. planeLeft() e planeRight() sono i fianchi; planeBottom() e planeTop() i piani; planeBack() lo schienale. I moduli generano la geometria e lasciano una traccia delle quote nella console.

Per esempio, il fondo deve sapere che si trova tra i fianchi:

include <woodworkers/std.scad>

box = [800, 280, 1900];
th = 18;

planeLeft(box, thick = th);
planeRight(box, thick = th);
planeBottom(box, l = -1, r = -1, thick = th);

Qui l = -1 e r = -1 esprimono la sottrazione di uno spessore per lato. Non è una quota da ricordare: è una relazione leggibile. Le correzioni assolute come ff e BB servono invece quando il pannello deve arretrare rispetto al fronte o al retro, per esempio per lasciare spazio allo schienale.

Nel sorgente del progetto i moduli sono incorporati, quindi quel file è autonomo. L’esempio ridotto qui sopra usa invece l’installazione separata descritta nel README; sono due modi diversi di provare la stessa idea.

Una scelta costruttiva attraversa tutto il modello

Nel generatore i pannelli sono riusati per costruire la carcassa:

module Sides() {
    planeLeft(carcass, bb = toekick_height, thick = TH);
    planeRight(carcass, bb = toekick_height, thick = TH);
}

module Shelves() {
    for (z = shelf_positions)
        translate([0, 0, z])
            planeBottom(carcass, l = -1, r = -1,
                        ff = -shelf_recess_front, BB = shelf_bb, thick = TH);
}

Il for non descrive un nuovo tipo di pezzo: ripete lo stesso ripiano in posizioni diverse. Per questo una modifica a shelf_count deve riflettersi sia nella vista sia nella distinta.

Lo schienale rende evidente il vantaggio di questo approccio. Con back_style = "nailed" riduce la profondità della cassa; con back_style = "slot" resta nella cassa e introduce scanalature, arretramenti e giochi di montaggio. La geometria solida costruttiva (CSG) entra in gioco quando difference() sottrae le scanalature ai pannelli. La vista 3D, la distinta e le lavorazioni devono restare coerenti: un cambio di assemblaggio non è solo un cambio di colore nella preview.

Dalla geometria alla distinta

La console è un primo controllo, ma non è ancora una distinta affidabile. Per portare i pezzi fuori dalla preview il generatore costruisce una seconda rappresentazione: ogni voce di bom contiene nome, due lati della faccia e spessore.

bom = concat(
    [["Fianco Sx", carcass_depth, side_h, TH],
     ["Fianco Dx", carcass_depth, side_h, TH],
     ["Fondo cassa", width_l - 2 * TH, carcass_depth, TH]],
    [for (i = [0 : 1 : len(shelf_positions) - 1])
        [str("Ripiano ", i + 1), width_l - 2 * TH,
         carcass_depth - shelf_recess_front + shelf_bb, TH]]
);

for (p = bom)
    echo(str("{\"name\":\"", p[0],
             "\",\"length\":", max(p[1], p[2]),
             ",\"width\":", min(p[1], p[2]),
             ",\"thickness\":", p[3], "}"));

Questi record sono stringhe JSON, una per pannello, riconoscibili da cutplanner. La struttura JSON separa nome, lato maggiore, lato minore e spessore; cutplanner documenta questi campi. La ferramenta, le scanalature e i fori restano informazioni diverse: non diventano rettangoli aggiuntivi nel piano di taglio.

Il piano di taglio come controllo della pipeline

Una BOM dice cosa serve; un ottimizzatore prova a disporlo sui fogli disponibili. Il file inventory.yaml descrive i materiali disponibili e il loro spessore. Non è un dettaglio secondario: un pannello sottile non deve finire fra quelli strutturali.

La documentazione di cutplanner riporta i comandi necessari. La prova reale del progetto è stata:

uvx --from git+https://github.com/uberbruns/cutplanner cutplanner serve parametric-billy-bookcase.scad inventory.yaml --kerf 3

write-bom ha trovato 12 pannelli. Il layout prodotto con l’inventario del progetto e --kerf 3 ha collocato tutti i pannelli su fogli compatibili; non sono rimasti pannelli fuori. È una verifica della pipeline, non un certificato di progetto esecutivo: venatura, bordatura, rifili, lavorazioni e sicurezza del mobile richiedono ancora decisioni e controlli reali.

La regola che resta

Provare un altro valore di width_l, wood_thickness o shelf_count nel Customizer di OpenSCAD serve a osservare la qualità del modello: cambiano insieme carcassa, ripiani e BOM. La relazione più semplice è anche la più istruttiva: se i fianchi diventano più spessi, la luce interna si riduce del loro spessore complessivo.

È questa, per me, la parte interessante del CAD parametrico applicato al legno. Non disegnare una Billy una volta, ma descrivere le regole di una famiglia di mobili. woodworkers-lib rende quelle regole leggibili attraverso facce e pannelli; echo() le porta dalla preview alla distinta; cutplanner permette di controllare un passaggio ulteriore. Il modello non decide come costruire un mobile, ma evita che una scelta cambi in un punto soltanto e venga dimenticata negli altri.