9.7 KiB
Note operative — produttore GLB (Fusion → Blender)
Questo file è la sponda produttore del FUSION_GLB_CONTRACT.md.
Tutte le risposte ai problemi [OPEN] del contratto vivono qui finché non vengono spostate nello stato [RESOLVED] del contratto stesso.
Risposte ai problemi aperti del contratto §8
1) Motion link con joint1: null
Stato: è un limite dell'API Fusion. Quando l'utente crea il motion link tramite la finestra "Collegamento movimento", Fusion lo memorizza con un riferimento interno (entity-token) ma il proxy motionLink.entityOne può ritornare None se il joint è stato creato/rinominato dopo il link. L'add-in non può inventarsi il nome.
Workaround consigliato a chi consuma:
- I 5 driver (
A,X,Y,PEN,Z) NON sono mainullsujoint2: i link conjoint2 == driversono usabili anche senzajoint1(basta non propagare nulla). - Per i link con
joint2: nullANDjoint1: null(es."Collegamento movimento 32","Collegamento movimento 4"): sono rumore, ignorabili. - Per i link con
joint1: nullmajoint2valorizzato (es."Collegamento movimento 14"→Motore asse X,"Collegamento movimento 28"→Rivoluzione 26,"Collegamento movimento 38"→Asse Penna):Motore asse Xè il driver, non ha bisogno di slave esterni — la traslazione del carrello X è già descritta dal rigid groupGruppo rigido 6(end stop X PIastrina:1 | binario SX:1 | Cinghia T5:1).Asse Pennaidem: lo slider muove direttamenteguida lineare:1, gli altri pezzi seguono via rigid groups.
→ TL;DR per il viewer: ignora tranquillamente i link con joint1: null. Tutta la propagazione necessaria passa dai rigid groups + i 5 driver diretti.
2) Motore A esiste in asBuiltJoints?
Confermato. Verificato sul file corrente: presente con nome esatto "Motore A", tipo revolute, child = "6627T331_Stepper Motor (1) (1):1", axis ≈ [0, 0, 1].
L'eventuale mismatch lato viewer dipende dal child name: nel JSON è esattamente "6627T331_Stepper Motor (1) (1):1" (due " (1)" di seguito), che nasce dal fatto che in Fusion il componente è stato copiato/incollato due volte. Se il viewer non lo trova, il bug è nel matching, non nell'export.
3) Convenzione multi-macchina
Da concordare quando servirà. Proposta minimale:
exports/
plotter/
plotter.glb
plotter.joints.json
<altra_macchina>/
<altra_macchina>.glb
<altra_macchina>.joints.json
Lato viewer un file machines.json indicizza nome → coppia (glb, json, DRIVERS map).
Stato attuale dell'export (rispetto alla checklist §6 del contratto)
| Voce checklist | Stato | Note |
|---|---|---|
| GLB in metri | ✅ | build_glb_from_fusion_export.py applica scaleFactor=0.01 al root, mesh OBJ importate in cm sono rimpicciolite di 100× → metri |
| Gerarchia Empty 1:1 | ✅ | Pass 1 crea un Empty per ogni node del hierarchy.json |
fusion_name e fusion_path per ogni Empty |
✅ (dal 2026-06-09) | Aggiunti come custom properties; coincidono byte-per-byte con Joint.child / Joint.childFullPath |
| Mesh figlie degli Empty, non joinate | ✅ | OBJ importati e re-parentati all'Empty corrispondente |
| Stato salvato = posa "tutti i driver a 0" | ⚠️ | Posa "as built" di Fusion al momento dell'export. Se l'utente esporta con slideValue ≠ 0 o rotationValue ≠ 0 la posa neutra slitta. Convenzione: in Fusion riportare i 5 driver a zero prima di lanciare lo script. |
Manifest embedded scene["fusion"] |
✅ | Il GLB contiene già il JSON completo in scene.extras.fusion |
Comandi operativi
Rigenera GLB dopo modifica codice:
.\build_glb.bat "C:\Users\croce\OneDrive\Desktop\export"
Riallinea script Fusion (da fare dopo ogni edit di ExportKinematicGraph.py):
python -c "import py_compile; py_compile.compile(r'C:\Users\croce\OneDrive\Desktop\export grafo fusion\ExportKinematicGraph.py', doraise=True); print('OK')"; if($LASTEXITCODE -eq 0){ Copy-Item -Force 'C:\Users\croce\OneDrive\Desktop\export grafo fusion\ExportKinematicGraph.py' (Join-Path $env:APPDATA 'Autodesk\Autodesk Fusion 360\API\Scripts\ExportKinematicGraph'); Write-Host "Sync OK" }
Sanity-check sui driver nel JSON:
$j = Get-Content 'C:\Users\croce\OneDrive\Desktop\export\joints.json' -Raw | ConvertFrom-Json
$names = @('Motore asse X','Motore asse Y','Asse Penna ','asse Z pneumatico M5?')
foreach($n in $names){ $j.joints | Where-Object name -eq $n | Select-Object name,type,child,axis,@{n='limits';e={ if($_.slideLimits){'slide '+$_.slideLimits.minimumValue+'..'+$_.slideLimits.maximumValue}elseif($_.rotationLimits){'rot '+$_.rotationLimits.minimumValue+'..'+$_.rotationLimits.maximumValue} }} | Format-List }
$j.asBuiltJoints | Where-Object name -eq 'Motore A' | Select-Object name,type,child,axis | Format-List
Findings dal viewer (2026-06-09)
Lato consumer (automation_kriz) ho aggiunto una pagina di ispezione live a /lab/graph che carica hierarchy.json + joints.json direttamente dal server e ne mostra: l'albero completo con conteggi joint/rigid per nodo, i 5 driver con stato OK/rotto, la lista di tutti i 329 joint + 28 asBuilt con filtro per tipo, i 9 motion link (validi vs rotti) e i 20 rigid group. Serve per fare debug del JSON senza dover aprire Fusion. Dato che è una pagina autenticata e legge /api/fusion/{joints,hierarchy}/ lato Django, basta sostituire i file in /home/marco/automation_kriz/joints.json + hierarchy.json e ricaricare.
Ispezionando l'export attuale (329 joints, 28 asBuiltJoints, 9 motionLinks, 20 rigidGroups) ho trovato tre problemi che impediscono al viewer di propagare il movimento sull'asse X (e probabilmente anche Y e PEN dipendono dagli stessi pattern). Servono fix lato ExportKinematicGraph.py.
BUG 1 — rigidGroups[].occurrences è SEMPRE vuoto FALSO ALLARME
rigidGroups[].occurrences è SEMPRE vuotoRitiro questo punto. Era un errore nel mio script Python di diagnostica a terminale (avevo scritto rg.get('occurrences', []) invece di rg.get('occurrenceNames', [])), poi ho copiato la conclusione qui sopra senza ricontrollarla. Verifica fatta:
Keys di un rigid group: ['name', 'occurrenceNames', 'occurrencePaths', 'isSuppressed', 'isLightBulbOn', 'includeChildren']
Gruppo rigido 6 contenuto: ['end stop X PIastrina:1', 'binario SX:1', 'Cinghia T5:1']
Il JSON è conforme al contratto §2.3 (occurrenceNames + occurrencePaths). Anche FusionRig.js e KinGraphPage.jsx lato viewer leggono già correttamente g.occurrenceNames. Quindi BUG 1 non esiste, scusa il rumore.
BUG 2 — Collegamento movimento 14 (driver X) ha joint1: null
{"name": "Collegamento movimento 14", "joint1": null, "joint2": "Motore asse X", "ratio": null}
Motore asse X è un giunto revolute (il motore stepper gira), joint2 = Motore asse X significa "questo è il driver". Senza joint1 (lo slider del carrello che dovrebbe seguire) il viewer non può propagare la rotazione del motore in traslazione del carrello.
Stessa cosa per Collegamento movimento 38 (joint2 = "Asse Penna ") e Collegamento movimento 28 (joint2 = "Rivoluzione 26").
Nel BRIDGE_NOTES precedente è scritto "ignorabili, propagazione passa dai rigid groups" → ma siccome i rigid groups sono vuoti (BUG 1), questi motion link diventano cruciali. Bisogna almeno provare a recuperare joint1 quando è None: il pattern API è motionLink.entityOne (proxy) vs motionLink.entityOneNative o usare motionLink.bRepEdge.assemblyContext. Se proprio l'API ritorna None, scrivere nel JSON il componentToken o l'entityToken come stringa hex così almeno il consumer può fare matching manuale.
BUG 3 — Motore asse X ha parent/child invertiti (convenzione)
{
"name": "Motore asse X",
"type": "revolute",
"parent": "Puleggia_HTD5M_z15:1",
"child": "6627T281_Stepper Motor:1",
"axis": [-1, 0, 0]
}
Fisicamente è il motore stepper che è fissato al telaio e fa girare la puleggia. Nel JSON è il contrario. Il viewer applica sempre la rotazione al child mantenendo fermo il parent, quindi vede il motore ruotare nel vuoto e la puleggia (che dovrebbe seguire) sta ferma.
Risposta alla domanda "il viewer applica al child?": sì, lo applica al child (più tutti i compagni del/dei rigid group che contengono il child). Codice in FusionRig.js#_applyJoint + _worldDelta:
// d.origin viene dal joint, d.axis dal joint, d.child dal joint
const T1 = new THREE.Matrix4().makeTranslation(d.origin.x, d.origin.y, d.origin.z);
const R = new THREE.Matrix4().makeRotationAxis(d.axis, value);
const T2 = new THREE.Matrix4().makeTranslation(-d.origin.x, -d.origin.y, -d.origin.z);
// la matrice ottenuta viene poi applicata a `child` e a tutti gli oggetti in `rigidComponent`
Quindi sì, serve premere Switch in Fusion. Stessa verifica da fare per Motore asse Y, Asse Penna , asse Z pneumatico M5? e Motore A (asBuilt): controllare per ognuno che child = il pezzo che si muove rispetto al telaio del livello precedente, parent = il pezzo solidale al livello precedente.
In ogni caso il fix è lato Fusion, non lato codice viewer.
Patch temporanea lato viewer (in valutazione, non ancora applicata)
Override Saltato. Visto che BUG 1 non esiste e BUG 2 non è bloccante per i driver puri (la propagazione passa dai rigid groups, che ora il viewer legge correttamente), aspetto solo il fix Fusion del BUG 3 e proviamo subito.viewerOverrides.json / viewerRigidGroups.json / viewerMotionLinks.json.
Per gli altri driver
Stessa analisi va fatta per Motore asse Y, Asse Penna e asse Z pneumatico M5?. Possibili problemi simmetrici: rigid groups vuoti, motion link joint1=null, parent/child invertiti. Quando rilancerai un export pulito segnalalo qui e rifaccio il giro su /lab/graph.