Nella prima parte abbiamo visto la struttura generale del codice e le operazioni fondamentali di OpenSCAD. Ora scendiamo nel dettaglio del componente più interessante del passacavo, e che ci consentirà di scendere più nel dettaglio dei pattern geometrici più utilizzati nella progettazione: l’innesto a baionetta.
Un innesto a baionetta è un meccanismo di fissaggio, tipico degli obiettivi fotografici, che unisce due pezzi tramite una combinazione di inserimento assiale e rotazione. Lo troviamo negli obiettivi fotografici, nei tappi dei radiatori e — nel nostro caso — in un passacavo da scrivania che deve restare saldo senza viti, colla o incastri a pressione. Lo so, lo so, per la destinazione d’uso, non c’era alcun motivo di realizzare questa componente. Ma mi è sembrato un ottimo approfondimento per gli scopi prettamente divulgativi di questa serie.
Quindi, appurata la necessità “divulgativa” di avere un elemento inferiore nell’assieme, la prima domanda è…
Perché una baionetta e non una filettatura
La scelta è puramente pratica e dettata dai vincoli della stampa 3D FDM. Una filettatura su un pezzo di 60 mm di diametro con pareti di 3 mm avrebbe questi problemi:
- Supporti inevitabili. Il profilo di un filetto crea sporgenze che richiederebbero supporti, peraltro di quelli difficilmente rimovibili. E io odio i supporti difficilmente rimovibili (come tutti).
- Tolleranze critiche. Una filettatura stampata in PLA è molto sensibile alle tolleranze: troppo stretta si grippa, troppo larga balla.
- Orientamento di stampa. I tre pezzi del passacavo vengono stampati in orientamenti diversi. Una filettatura sul manicotto sarebbe stampata in verticale (ok), ma sull’anello sarebbe orizzontale (pessimo). La resistenza alla delaminazione, infatti, dipende da come è orientato l’oggetto e, quindi, dall’asse di carico. Di nuovo, in questo caso parliamo di quasi totale assenza di carico, ma teniamolo come principio generale che male non farà.
L’innesto a baionetta risolve tutti e tre i problemi: i pin e gli slot sono geometrie semplici, stampabili senza supporti o con supporti minimi e le tolleranze si controllano con un unico parametro a prescindere dall’orientamento sul piano di stampa dei vari componenti dell’assieme. E poi, è anche bello da vedere!
Descrizione estesa
A sinistra: sezione trasversale di un accoppiamento filettato tra manicotto (azzurro) e vite (grigio). I denti trapezoidali della filettatura creano sporgenze superiori a 45° (evidenziate) che richiedono supporti di stampa. La zona di tolleranza critica è indicata da frecce in arancione. A destra: sviluppo piano della parete interna del manicotto con slot a L composto da ingresso verticale (1), corridoio orizzontale (2) e tacca di ritenzione (3). Il pin (verde) compie un percorso a baionetta: inserimento assiale, rotazione, scatto nella tacca.L’anatomia della baionetta
Il meccanismo si compone di due elementi:
- Pin radiali sul collare dell’anello inferiore (
bottom_part). Sono denti che sporgono verso l’esterno. - Slot a L scavati nella parete interna del manicotto superiore (
top_part). Ogni slot ha tre zone: un ingresso verticale, un corridoio orizzontale e una tacca di ritenzione.
L’assemblaggio funziona così: si allineano i pin con gli ingressi verticali, si spinge l’anello fino in fondo, si ruota in senso orario. I pin percorrono il corridoio orizzontale e scattano nella tacca di ritenzione, dove restano bloccati.
Il pin: un dente radiale
Il pin è un piccolo settore di anello, generato dalla primitiva ring_sector che abbiamo introdotto nella parte 1:
module bayonet_pin(r_inner, h, w_deg, t) {
rotate([0, 0, -w_deg/2])
ring_sector(r_inner, r_inner + t, h, w_deg);
}
I parametri del pin sono:
r_inner: il raggio interno a cui inizia il dente (tipicamente il raggio esterno del collare)h(bayo_pin_h = 2.4): l’altezza assialew_deg(bayo_pin_w_deg = 14): la larghezza angolare in gradit(bayo_pin_t = 1.4): la sporgenza radiale
La rotate([0, 0, -w_deg/2]) centra il pin sull’angolo zero, a quel punto serve solo un ciclo for per definire il pattern circolare:
for (i = [0 : bayo_pins-1]) {
rotate([0, 0, i * 360/bayo_pins])
translate([0, 0, pin_z_on_collar])
bayonet_pin(
r_inner = collar_od/2,
h = bayo_pin_h,
w_deg = bayo_pin_w_deg,
t = bayo_pin_t
);
}
Tre pin (bayo_pins = 3) equidistanti a 120°. Tre è il numero ottimale: due potrebbero basculare, quattro sarebbero sovrabbondanti e ridurrebbero la superficie di contatto. Tre è il numero perfetto, tanto per cambiare.
Il ciclo for: iterazione geometrica
Il posizionamento dei tre pin introduce una feature di OpenSCAD che non avevamo ancora incontrato: il ciclo for. Diversamente dai linguaggi imperativi, in OpenSCAD for non esegue istruzioni ripetutamente: genera copie multiple dell’oggetto che segue, ciascuna con un valore diverso della variabile di iterazione.
for (i = [0 : bayo_pins-1]) {
rotate([0, 0, i * 360/bayo_pins])
translate([0, 0, pin_z_on_collar])
bayonet_pin(...);
}
La sintassi [start : end] produce un range: con bayo_pins = 3, la sequenza è [0, 1, 2]. Si può usare anche [start : step : end] per controllare l’incremento. Il pattern i * 360 / N distribuisce N oggetti in cerchio: ogni copia viene ruotata di un angolo proporzionale al suo indice.
Differenza chiave: in un linguaggio imperativo, un for esegue il corpo N volte in sequenza. In OpenSCAD, il for produce N istanze geometriche che vengono automaticamente unite (è implicita una union()). Questo ha due conseguenze:
- Non potete creare o modificare una variabile dentro il ciclo — le variabili sono immutabili, e comunque ogni iterazione è un contesto indipendente.
- Il risultato è sempre un singolo solido (l’unione di tutte le copie), non N solidi separati. Se volete solidi indipendenti, dovete usare costrutti diversi (ad esempio moduli separati per ogni pezzo dell’assieme, come già fatto con
top_part(),bottom_part()ecap_part()).
intersection_for: quando serve l’intersezione
La union() implicita del for standard è pratica, ma rende impossibili altri tipi di combinazioni booleana. Il caso classico: volete l’intersezione di N copie di un oggetto, ciascuna ruotata progressivamente.
// Questo NON funziona come ci si aspetta:
intersection() {
for (n = [1 : 6]) {
rotate([0, 0, n * 60])
translate([5, 0, 0])
sphere(r = 12);
}
}

Il problema è che il for interno raggruppa tutte le sfere in una union() prima ancora che intersection() le veda. L’intersezione viene calcolata tra… l’unione di tutte le sfere e nient’altro. Risultato: l’unionee.
intersection_for risolve esattamente questo: sintassi identica al for, ma l’unione implicita è sostituita dall’intersezione:
// Questo SÌ che funziona:
intersection_for(n = [1 : 6]) {
rotate([0, 0, n * 60])
translate([5, 0, 0])
sphere(r = 12);
}

Lo slot a L: tre settori sovrapposti
Lo slot è più articolato del pin. Deve permettere l’inserimento assiale, la rotazione e infine il bloccaggio. La logica, però è la stessa, in quanto si realizza con tre ring_sector sovrapposti. Sono tutti concetti che abbiamo già visto:
module bayonet_slot_L(r_inner_wall, axial_in, h_pin, w_pin_deg, twist_deg, drop_extra) {
eps = 0.05;
r1 = r_inner_wall - eps;
r2 = r_inner_wall + slot_depth_r + eps;
rotate([0, 0, -w_pin_deg/2]) {
// 1) Vertical entry slot (open at the bottom edge)
translate([0, 0, -eps])
ring_sector(r1, r2, axial_in + eps, w_pin_deg);
// 2) Horizontal travel
translate([0, 0, axial_in - h_pin - eps])
ring_sector(r1, r2, h_pin + 2*eps, w_pin_deg + twist_deg);
// 3) Retention notch at end of travel
rotate([0, 0, twist_deg])
translate([0, 0, axial_in - h_pin - eps])
ring_sector(r1, r2, h_pin + drop_extra + 2*eps, w_pin_deg);
}
}

Analizziamo i tre settori:
-
Ingresso verticale (settore 1). Parte dal bordo inferiore del manicotto (
z = -eps) e sale peraxial_inmillimetri. Ha larghezzaw_pin_deg: quanto basta per far passare il pin. -
Corridoio orizzontale (settore 2). Si estende per
w_pin_deg + twist_deggradi. Qui il pin può ruotare dopo l’inserimento. La larghezza extra (+ twist_deg) è lo spazio di manovra. -
Tacca di ritenzione (settore 3). Alla fine del corridoio, un piccolo gradino (
drop_extra = 0.5mm) in cui il pin “cade” e si blocca. Questo è il segreto per cui la baionetta non si svita da sola: la gravità (o una leggera pressione della molla del tappo) tiene il pin nella tacca.
Il pattern eps: sovrapposizione anti-artefatti
I tre settori sono intenzionalmente sovrapposti (eps = 0.05 mm) per evitare che il motore CSG di OpenSCAD generi facce orfane (non-manifold) nelle zone di confine tra settori adiacenti o perfettamente complanari. Senza questa abbondanza, l’operazione di difference() produrrebbe artefatti visibili e potenziali errori di esportazione STL.
Questo pattern è un accorgimento ricorrente nella modellazione CSG e merita un approfondimento.
Il problema. Quando due primitive condividono esattamente una faccia (sono complanari), il motore CSG non sa decidere se i punti su quella faccia appartengano a un solido, all’altro, o a nessuno dei due. Il risultato sono micro-fratture nella mesh, normali invertite, o il rifiuto dell’esportazione STL con errori del tipo “Object may not be a valid 2-manifold”.
Ma perché succede? La causa è una proprietà intrinseca dell’aritmetica a virgola mobile con cui lavorano tutti i computer. Tre meccanismi cospirano:
- Numeri con la virgola. I computer rappresentano i numeri reali con precisione finita (tipicamente 15-17 cifre significative).
1/3non è0.333...all’infinito ma0.3333333333333333. È un’approssimazione talmente buona da essere indistinguibile nella stampa, ma sufficiente a confondere il CSG. - Rotazioni e trigonometria. Ogni
rotate([0, 0, 45])chiamasin(45°)ecos(45°)che valgono√2/2 ≈ 0.7071067811865476. La radice di 2 è un numero irrazionale — ha infinite cifre decimali. Il computer la tronca. Dopo una rotazione, le coordinate dei vertici non sono mai esattamente dove la geometria “pura” le vorrebbe. - Catena di operazioni. Un
rotate()seguito datranslate(), poidifference(), poi un altrorotate()… ogni passaggio accumula errori di arrotondamento. Due facce che dovrebbero essere az = 10.0potrebbero trovarsi una a9.999999999999996e l’altra a10.000000000000002. Per noi sono10. Per il CSG sono due piani diversi.
La conseguenza pratica: ogni union(), esplicita o implicita, richiede che le facce da fondere non siano coincidenti. Se lo sono (o meglio: se il computer pensa nell’incercezza della rappresentazione a virgola mobile che lo siano), il comportamento è indefinito: porzioni di mesh possono sparire dal render, apparire rovesciate, o lampeggiare nella preview. Non è un bug: è il prezzo della rappresentazione discreta dei numeri.
La soluzione. Si sommano o si sottraggono piccole quantità (eps, che in effetti sta per epsilon) alle dimensioni degli oggetti, in modo che le superfici adiacenti compenetrino leggermente invece di toccarsi. Nel nostro slot:
r1 = r_inner_wall - epser2 = r_inner_wall + slot_depth_r + eps: i settori partono prima della parete e arrivano oltre lo scavo.translate([0, 0, -eps])sul settore 1: l’ingresso inizia appena sotto il bordo inferiore del manicotto, garantendo che non ci sia una linea di giunzione esattamente az = 0.h_pin + 2*epsnei settori 2 e 3: l’altezza è leggermente maggiorata per sfondare oltre i confini.
Quanto piccolo? 0.05 mm è un valore empirico che funziona bene con la risoluzione standard delle stampanti FDM (0.4 mm di ugello, 0.2 mm di layer). È abbastanza grande da risolvere le ambiguità del CSG, ma abbastanza piccolo da non produrre alterazioni visibili nella stampa finale. Se usate $fn molto alti (>200), potete ridurlo a 0.01; se lavorate con mesh a bassa risoluzione, potrebbe servirvi 0.1.
Il compromesso. L’eps modifica leggermente le dimensioni reali del modello. Per un passacavo non è un problema (0.05 mm su 60 mm di diametro è lo 0.08%), ma in applicazioni di alta precisione — ingranaggi, accoppiamenti meccanici stretti — va considerato nel calcolo delle tolleranze, aggiungendolo ai parametri di gioco come bayo_slot_play.
So che questo introduce tanta frizione nella modellazione, pazienza.
Tolleranze: benvenuti nel mondo reale
La differenza tra un incastro che funziona e uno che si inceppa sta nei parametri di tolleranza. Nel passacavo sono tutti espliciti:
bayo_slot_play = 0.4; // Radial and axial clearance inside the slots
bayo_drop_extra = 0.5; // Extra axial drop for retention snap
bayo_slot_play aggiunge 0.4 mm di gioco in tutte le direzioni all’interno dello slot. È il parametro che regola la “morbidezza” dell’incastro: aumentatelo se la vostra stampante tende a sovra-estrudere, diminuitelo se l’incastro balla.
La profondità radiale dello slot è bayo_pin_t + bayo_slot_play: la sporgenza del pin più il gioco. L’altezza del pin nello slot è bayo_pin_h + 2*bayo_slot_play: altezza pin più gioco assiale sopra e sotto.
Questa disciplina — ogni tolleranza è una variabile con nome — è ciò che trasforma un modello “può funzionare” in un modello “funziona sulla tua stampante” e sotto tutte le condizioni di lavoro per cui è progettato.
Noterete anche un altro dettaglio: ho più di una tolleranza nel mio progetto. È uno scenario molto comune se lavorate su assiemi. Alcuni elementi del progetto probabilmente avranno, infatti, bisogno di una tolleranza maggiore rispetto agli altri: non dipende, quindi, solo dalla tua stampante, ma anche dal tipo di accoppiamento, dalla frizione meccanica, dal materiale, eccetera.
Posizionamento e rotazione di lock
Un dettaglio importante: nella vista esplosa (e nell’assieme montato), i pin dell’anello inferiore appaiono già ruotati nella posizione di bloccaggio:
module bottom_part_placed(extra_drop = 0) {
translate([0, 0, -extra_drop])
rotate([0, 0, -bayo_twist_deg])
bottom_part();
}
La rotate([0, 0, -bayo_twist_deg]) pre-ruota l’intero anello in modo che, nella visualizzazione, i pin appaiano già nella tacca di ritenzione — esattamente come sarebbero dopo l’assemblaggio reale. È solo una mia convenzione visiva. In futuro conto di lavorare a delle piccole animazioni degli assiemi esplosi in OpenSCAD generate programmaticamente: aver creato questo modulo mi tornerà utile quel giorno.
Riepilogo: cosa abbiamo imparato
- Una baionetta è preferibile a una filettatura per pezzi stampati in FDM: niente supporti, tolleranze controllabili, geometria semplice.
- Il
fordi OpenSCAD non è un ciclo imperativo: genera copie geometriche multiple, implicitamente unite in un unico solido. Il patterni * 360 / Nè il modo standard per distribuire oggetti in cerchio. - Il pin è un settore di anello, generato con
ring_sector. - Lo slot a L è composto da tre settori sovrapposti: ingresso, corsa, ritenzione.
- Le tolleranze (
bayo_slot_play,bayo_drop_extra) sono parametri espliciti. - La costante
epspreviene artefatti CSG con 0.05 mm di sovrapposizione intenzionale tra primitive adiacenti: è un accorgimento universale per evitare geometrie non-manifold.
Nella terza e ultima parte della serie realizzeremo il tappo superiore e vedremo come i CAD parametrici possono sfruttare la math library molto di più dei CAD tradizionali. Poi ci concentreremo su un picclo sistema di visualizzazione (mode) che permette di passare dalla vista d’assieme alla vista esplosa, al layout di stampa e all’esportazione STL/3MF.
Passacavi da scrivania: geometrie interne e innesto a baionetta — Download
Desk cable management, 60 mm hole
- Materiale consigliato
- PLA, PETG
- Dimensioni
- 76 × 76 × 28 mm
- Stampante
- Nozzle 0.4 mm, layer 0.2 mm, infill 15% gyroid
Sorgente OpenSCAD: customizable-cable-grommet.scad