Documenta la decodifica dei .prt: albero delle feature, quote e geometria

Reverse engineering di test.prt.1, 9252000507.prt e 9239901907.prt contro
il model tree di Creo: contenitore e sezioni, ricostruzione dell'albero
(nomi per contatore, gruppi, sezioni), numeri compatti, quote, foro, smusso
e vincoli dei piani, con la tabella di cosa resta da decodificare.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-21 15:58:15 +02:00
1 parent d5fb20a34e
commit 5b11b3e8ca
4 files changed
+434

No files matched your search

+5
View File
@@ -7,6 +7,11 @@ aderisce a [Semantic Versioning](https://semver.org/lang/it/).
## [Non rilasciato]
### Aggiunto
- `docs/formato-prt-feature.md`: decodifica (da reverse engineering) dell'albero delle feature di un `.prt`.
- `docs/formato-prt-geometria.md`: numeri, quote e geometria degli schizzi di un `.prt`, con la tabella di cosa resta da decodificare.
## [1.1.0] - 2026-10-07
### Aggiunto
+4
View File
@@ -20,6 +20,8 @@ e cambia solo quando cambia il programma.
- [`db-cad-index.md`](db-cad-index.md) — schema, vista e query di `cad_index.db`.
- [`formato-asm-record.md`](formato-asm-record.md) — parser esatto dei record `.asm`, casi limite (§7).
- [`formato-asm.md`](formato-asm.md) — euristica testuale di fallback.
- [`formato-prt-feature.md`](formato-prt-feature.md) — come leggere l'albero delle feature di un `.prt` (reverse engineering, non ancora nel codice).
- [`formato-prt-geometria.md`](formato-prt-geometria.md) — numeri, quote e geometria degli schizzi di un `.prt`, con la tabella di cosa manca per determinarlo per intero.
- [`glossario.md`](glossario.md) — i termini usati nei doc e nell'app.
- [`../CHANGELOG.md`](../CHANGELOG.md) — storico delle versioni.
@@ -37,6 +39,8 @@ Ogni informazione vive in **un** posto; altrove si mette un link, non una copia.
| Versione e nome prodotto | `PrepStep/PrepStep.csproj` (`<Version>`, `<Product>`) |
| Schema e vista del DB | [`db-cad-index.md`](db-cad-index.md) |
| Formato dei record `.asm` e casi limite | [`formato-asm-record.md`](formato-asm-record.md) |
| Albero delle feature di un `.prt` | [`formato-prt-feature.md`](formato-prt-feature.md) |
| Quote, coordinate e geometria degli schizzi di un `.prt` | [`formato-prt-geometria.md`](formato-prt-geometria.md) |
| Processo delle 5 fasi, mappa dei file | [`architettura.md`](architettura.md) |
| Passi dell'utente | [`guida-utente.md`](guida-utente.md) |
| Build, test, regole sui doc | [`sviluppo.md`](sviluppo.md) |
Binary file not shown.
+425
View File
@@ -0,0 +1,425 @@
# Decodifica del `.prt` Creo: numeri, quote e geometria degli schizzi
Seguito di [`formato-prt-feature.md`](formato-prt-feature.md), che spiega come trovare le sezioni
del file e leggere l'**albero** delle feature (ordine, tipo, nome). Qui: come leggere i **numeri**
(quote, coordinate) e come ricostruire da quelli la forma di una feature.
**Stato, da leggere prima di fidarsi.** Questo doc contiene *tutto quello che è stato decodificato
e verificato*, e dice per ogni cosa quanto è provata. **Non basta ancora a determinare in modo
univoco com'è fatto un `.prt`**: manca il posizionamento delle feature nello spazio e il
significato certo di alcuni codici (§7 elenca cosa manca, dove sta nel file, e come finire il
lavoro). Nessun codice del progetto lo usa: è reverse engineering fatto con script di prova.
Livelli di prova, come negli altri doc:
1. **Contro Creo** (schermate del modello aperto): `9252000507.prt.11`, feature *Rivoluzione 1*
(§5). Quote e coordinate dello schizzo coincidono con quelle mostrate da Creo.
2. **Coerenza e plausibilità**: `test.prt.1` e le altre feature di `9252000507.prt.11`. I valori
sono numeri "tondi" e coerenti fra sezioni diverse, ma nessuno li ha confrontati con Creo.
## 1. Come sono scritti i numeri reali
Nelle sezioni a marcatori ([`formato-asm-record.md`](formato-asm-record.md) §2) i `double` sono
scritti in forma compatta. Si distinguono per il primo byte, `tag`:
| Tag (hex) | Byte dopo il tag | Valore | Prova |
|---|---|---|---|
| `18` | 0 | `0.0` | in ogni sezione |
| `0f` | 0 | `1.0` | diagonale delle matrici identità |
| `0e` | 0 | `0.5` | **livello 1**: la quota di *Smusso 1* (D = 0.500) è il solo byte `0e` |
| `10` | 0 | `-1.0` | ipotesi dal contesto |
| `2f` | 2 | `40 b1 b2 00 00 00 00 00` | **livello 1** (12,5; 69,5; 100; 139) e 2 (8, 20, 25, 75) |
| `2e` | 2 | `40 b1 b2 b2 b2 b2 b2 b2` (l'ultimo byte si ripete) | **livello 1**: `2e 1b 33` = 6,8, il diametro della punta mostrato da Creo |
| `2d` | 7 | `40 b1 .. b7` (precisione piena) | livello 2: `2d 39 7f ff ff ff ff ff` = 25,5 |
| `2a` | 2 | `3f b1 b2 00...` | livello 2: `2a f4 00` = 1,25, il passo di M8x1.25 |
| `48` | 2 | `c0 b1 b2 00...` (negativo) | **livello 1** (−12,5; −69,5) |
| `47` / `46` | 2 / 7 | stessa famiglia di `48`, con ripetizione / precisione piena | non isolati |
| `ed`, `32`... | 8-9 | altri byte alti | non decodificato |
**La regola, ricavata dai dati** (non da una specifica): a ogni *byte alto* `T` del `double`
corrispondono **tre tag consecutivi**, e la base si sposta di 5 per ogni passo di `T`:
```
tag_zero(T) = 0x2f + 5 * (T - 0x40) per i positivi (negativi: 0x48 + 5 * (T - 0xc0))
tag_zero -> T, b1, b2 e 5 byte a zero (valori "tondi")
tag_zero - 1 -> T, b1, b2 e b2 ripetuto 5 volte (es. 6,8 = 40 1b 33 33 33 33 33 33)
tag_zero - 2 -> T e 7 byte pieni (precisione piena)
```
Verifica: `T = 0x40` dà `2f`, `2e`, `2d`; `T = 0x3f` dà `2a` (1,25 con `f4 00`); `T = 0xc0` dà
`48`. Funziona solo finché i tag non si sovrappongono: i byte alti `0x41`-`0x43` e `0xb8`-`0xbf`
non li ho incontrati e non so se la regola regge (i tag di `0x45` collidono con quelli dei
negativi). Per i valori "non tondi" con ripetizione (come 6,8 o 3,4) serve il tag `2e`: senza
quello si leggerebbe `6.7998`.
Come si ricostruisce un valore a 3 byte: si prende il `double` big-endian `40 b1 b2 00...` (per
`2f`) oppure `c0 b1 b2 00...` (per `48`). Esempi verificati (`struct.unpack('>d', ...)`):
| Byte | Valore | Dove |
|---|---|---|
| `2f 59 00` | `100.0` | quota |
| `2f 52 c0` | `75.0` | quota |
| `2f 51 60` | `69.5` | coordinata y dello schizzo di Rivoluzione 1 (= 139 / 2) |
| `2f 29 00` | `12.5` | coordinata x (= 25 / 2) |
| `48 29 00` | `-12.5` | idem, lato opposto all'asse |
| `48 51 60` | `-69.5` | idem |
| `2f 61 60` | `139.0` | quota (lunghezza) |
| `48 59 00` | `-100.0` | estremo della linea d'asse |
Le unità non sono scritte nel record. Nei campioni sono millimetri, ma il file ha una sezione
`UnitSystemDef` (nel contenitore compresso, §1.2 di `formato-prt-feature.md`) che non ho letto:
l'unità va presa da lì prima di dare un numero come "mm".
## 2. Le quote: `dim_array` in `BasFullData`
Ogni quota (dimensione guida) di ogni feature e di ogni schizzo è un record della sezione
`BasFullData`, array `dim_array`. Campi, nell'ordine in cui compaiono:
```
\xe1 nome della quota (qui sempre nullo: il nome "d12" si calcola altrove)
\x08 tipo del record
<dim_type> 1 byte: classe della quota (tabella sotto)
<feat_id> intero (formato asm-record §2.1): la feature a cui appartiene
<attributi> \xf8 \x02 ... flag
\xe3 <valore> <valore_di_backup> \xe3 ... due numeri uguali: value e bck_value
\xe3 \x09 \x01 ... <tolleranza> ... blocco della tolleranza (nominal_value, bound...)
```
Per leggere solo valore e feature basta ancorarsi a `\xe1 \x08 <dim_type> <feat_id>` e leggere il
primo numero che segue (§1). Il primo record dell'array è scritto con i nomi di campo per esteso
(layout diverso: non l'ho agganciato con lo stesso pattern, quindi **la prima quota dell'array può
mancare**).
`feat_id` è lo stesso id di `AllFeatur` e dello storico (`formato-prt-feature.md` §2 e §5): è il
modo per attribuire la quota a "Foro 1" o a "Estrusione 2".
### 2.1 Le quote lette sui due campioni
`test.prt.1` (id → feature come in `formato-prt-feature.md` §6):
| Feature | `dim_type` | Valore | Interpretazione |
|---|---|---|---|
| Estrusione 1 (79) | 62 | 75 | profondità (ipotesi) |
| Estrusione 1 (79) | 4 | 100 | diametro del cerchio dello schizzo: nello schizzo il raggio è 50 (`entity(circle)`, `radius`) |
| Foro 1 (100) | 4 | 19 | diametro (ipotesi) |
| Foro 1 (100) | 62 | 40 | profondità (ipotesi) |
| Arrotondamento 1 (125) | 3 | 3,5 | raggio (ipotesi) |
| Estrusione 2 (149) | 62 / 1 / 1 | 11 / 25 / 53 | profondità e due quote dello schizzo (ipotesi) |
| Smusso 1 (188) | 9 | 6 | distanza dello smusso (ipotesi) |
`9252000507.prt.11`:
| Feature | `dim_type` | Valore | Interpretazione |
|---|---|---|---|
| Rivoluzione 1 (73) | 2 | **25** | **diametro, confermato da Creo** (Ø25 h7) |
| Rivoluzione 1 (73) | 1 | **139** | **lunghezza, confermata da Creo** |
| Estrusione 1 / taglio (118) | 62 / 14 / 5 / 20 | 4 / 45 / 8 / 3 | non verificate |
| Foro 1 (157) | 4 / 9 / 2 / 10 / 2 | 8 / 20 / 6,8 / 118 / 25 | **confermate da Creo** (§6.4): 8 = diametro nominale, 20 = profondità filetto, 118 = angolo punta, 25 = profondità foro |
**Cosa è provato e cosa no.** I valori `25` e `139` di *Rivoluzione 1* sono confermati da Creo
(§5). Per tutte le altre righe il *numero* è decodificato in modo plausibile ma il *significato*
(profondità? diametro?) è dedotto dal codice `dim_type`, da cui non ho una tabella certa. I codici
visti: `1` e `2` quote lineari/diametrali di schizzo, `3` raggio, `4` diametro, `9` distanza
(smusso), `62` profondità/estensione, altri (`5`, `10`, `14`, `37`) non interpretati. Per
distinguere con certezza serve un confronto riga per riga con un modello noto, o leggere il
nome della quota (`d0`, `d1`... del Creo).
La *tolleranza* (es. `h7` di Ø25) sta nello stesso record, ma non l'ho decodificata: nel campo
`tol_class`/`tol_table_index` compaiono indici, non la stringa `h7`.
## 3. La geometria di uno schizzo: `p_saved_result`
Ogni schizzo (sezione) ha, dentro `BasFullData`, un blocco:
```
gsec_ptr ... name "Sezione 1" <- nome mostrato nel tree
gsec2d_ptr name "S2D0001"
segtab_ptr / dimtab_ptr / dim_ref <- segmenti, quote e a cosa si riferiscono
...
p_saved_result
entity(line) ... <- le linee dello schizzo con le coordinate
entity(circle) ... <- i cerchi (centro, raggio)
local_sys 4x3 <- sistema di coordinate dello schizzo
```
Le coordinate delle entità sono **nel sistema dello schizzo**, non assolute (§6).
Ogni `entity(line)` ha due estremi con tre coordinate ciascuno `(x, y, z)`; con z sempre `0` nello
schizzo. Le coordinate sono numeri come in §1.
### 3.1 Esempio verificato: *Sezione 1* di Rivoluzione 1
Decodificando i numeri del blocco `entity(line)` di `9252000507.prt.11` si ottengono quattro
linee chiuse e una linea d'asse:
| Linea | Da `(x, y)` | A `(x, y)` |
|---|---|---|
| 1 | (0, 69,5) | (−12,5, 69,5) |
| 2 | (−12,5, 69,5) | (−12,5, −69,5) |
| 3 | (−12,5, −69,5) | (0, −69,5) |
| 4 | (0, −69,5) | (0, 69,5) |
| asse | (0, −100) | (0, 100) |
È il rettangolo di larghezza 12,5 (= raggio) e altezza 139, centrato in y sul piano di
riferimento, con il lato destro sull'asse x = 0, e una linea d'asse più lunga della figura.
**Coincide con quello che Creo mostra** nella vista dello schizzo (rettangolo di 139 di altezza,
quotato 25 come diametro rispetto all'asse tratteggiato, simmetrico rispetto a `TOP`).
Nota: nel file la quota `25` è un **diametro** (Ø25), anche se in geometria il lato è 12,5. Per
questo non si può dedurre la forma dalle sole quote: servono anche le linee.
## 4. Da schizzo + quote alla forma: la rivoluzione
Con le due cose sopra, *Rivoluzione 1* è determinata così:
```
profilo (nel piano dello schizzo): x in [-12,5 ; 0], y in [-69,5 ; 69,5]
asse di rotazione: la linea x = 0 (la linea d'asse dello schizzo)
angolo: 360 (non decodificato, vedi §7)
risultato: cilindro Ø25 x 139, centrato sull'origine lungo y dello schizzo
```
Nell'albero di Creo il cilindro risulta con il fondo sul piano `DTM1` e l'asse come `A_1`: la
posizione assoluta (quale piano contiene lo schizzo, come è orientato `y`) **non è nello schizzo**
ma nella definizione della feature (§7).
## 5. Verifica di *Rivoluzione 1* contro Creo
`9252000507.prt` aperto in Creo (schermate del model tree e dello schizzo):
| Cosa | Creo | Letto dal file |
|---|---|---|
| Lunghezza dello schizzo | 139.000 | quota `dim_type 1` = 139 |
| Diametro | Ø25 (h7) | quota `dim_type 2` = 25 |
| Rettangolo, simmetrico rispetto a `TOP` | sì | y da −69,5 a 69,5 |
| Rettangolo a sinistra dell'asse | sì | x da −12,5 a 0 |
| Asse (linea di costruzione) | più lungo della figura | da y = −100 a 100 |
| Assi A_3, A_4, A_5, punto PNT0 | presenti nel tree | `AllFeatur` + storico (`formato-prt-feature.md`) |
## 6. Posizione nello spazio
### 6.1 Matrici 4×3
Piani di riferimento, assi, sistemi di coordinate e schizzi hanno un `local_sys`: array `f9 04 03`,
cioè 4 righe per 3 colonne = 12 numeri (3 versori degli assi + l'origine). Lo schizzo sul piano
base ha la matrice identità, scritta `0f 18 e5 0f 18 e5 0f 18 e5`: tre gruppi uguali, ciascuno
`1, 0, 0, 0`, cioè 12 numeri `1,0,0,0, 1,0,0,0, 1,0,0,0`. Da qui l'ipotesi che `e4`-`e7` dopo uno
zero valgano "altri K−3 zeri".
**Questa lettura è un'ipotesi, non verificata.** L'identità non permette di distinguere
"per righe" da "per colonne", né di sapere quale gruppo è l'origine. Servono matrici *non*
identità (un piano ruotato, un'origine fuori dallo zero) con il valore atteso da Creo. Finché non
c'è, rotazione e origine di piani e schizzi sono **non decodificate**.
### 6.2 Dove stanno
| Cosa | Dove nel file |
|---|---|
| Piani di riferimento (RIGHT, TOP, FRONT, DTM1) | `ActDatums` (`srf_prim_ptr(plane)` con `local_sys`), `feat_id` = id della feature; vincoli di posizionamento in `FeatDefsDtm` (§6.3) |
| Sistema di coordinate (`PRT_CSYS_DEF`) | `MdlRefInfo` (`ent_list(coord_sys)`) e `GeomDepen` |
| Assi, punti (A_3, PNT0...) | `ent_list(arc_z)`, `ent_list(point)` in `MdlRefInfo` |
| Piano che contiene lo schizzo | `gsec3d_ptr` -> `plane_id`, `ref_planes`; il piano è identificato da un id di geometria |
| Orientamento dello schizzo | `sec_orient_data`, `place_instruction_ptrs` (nella definizione di feature `feat_defs_<tipo>`) |
| Offset di un piano da un altro (DTM1) | quota di tipo "offset" nel `dim_array` della feature, se esiste |
### 6.3 Come è posizionato un piano: i vincoli di `FeatDefsDtm`
La finestra "Piano di riferimento → Posizionamento" di Creo (riferimenti con il loro vincolo,
es. *Tangente* a una superficie, *Parallelo* a un piano) **si legge dal file**. Sta nella sezione
`FeatDefsDtm`, record `feat_defs_923` (un record per piano; `feat_defs_926` assi, `feat_defs_931`
punti, `feat_defs_979` sistemi di assi). Ogni record ha:
```
id id della feature (come in AllFeatur)
local_sys matrice 4x3 del piano risultante (§6.1)
place_instruction_ptrs una istruzione per ogni riferimento della finestra:
type codice del vincolo (intero a 2 byte)
offset double, offset del vincolo
dim_id, ref_id quota dell'offset e riferimento, se esistono
geometry1_id id della geometria a cui ci si riferisce
geometry2_id id della geometria che si sta creando (il piano stesso)
```
Gli `id` di geometria (`geom_id`) si risolvono nelle sezioni `ActDatums` (piani e assi di
riferimento), `VisibGeom` e `NovisGeom` (superfici del solido, visibili e no). Ogni voce di
`srf_array` ha `geom_id`, `geom_type` (`"`, 0x22 = piano; `$`, 0x24 = cilindro) e **`feat_id`**: è il
legame con la feature che l'ha generata.
**Verifica sul caso che hai mostrato**: *DTM1* di `9252000507.prt` (feature 116), finestra di Creo
"Supfce:F5(RIVOLUZIONE_1) Tangente / FRONT:F3 Parallelo". Dal file:
| Istruzione | `type` | `geometry1_id` | Si risolve in | Corrisponde a |
|---|---|---|---|---|
| 1 | `81 fb` (507) | 88 | `VisibGeom`: cilindro (`$`), `feat_id` 73 = Rivoluzione 1 | "Supfce:F5(RIVOLUZIONE_1)", **Tangente** |
| 2 | `81 f9` (505) | 6 | `ActDatums`: piano (`"`), `feat_id` 5 = FRONT | "FRONT:F3", **Parallelo** |
`geometry2_id` è 117 in entrambe: `ActDatums` lo dà come piano con `feat_id` 116 = DTM1 (il piano
risultante). Nella sua matrice compare il valore `12.5` (`2f 29 00`): è il raggio del cilindro,
come deve essere per un piano tangente ad esso. Il campo `Traslazione` della finestra era vuoto.
Quindi **quali sono i riferimenti** (quale feature/superficie e quale piano) si legge con certezza.
Resta da fissare:
- **Il significato di `type`.** 507 ↔ Tangente e 505 ↔ Parallelo è dedotto da un solo esempio e
dall'ordine delle righe della finestra: non ho una tabella dei codici. Gli altri visti
(`82 01`, `82 02`, `82 03`, `81 fd`, `81 ff`) sono quelli dei piani di default e degli altri
piani del campione, non interpretati.
- **`Feature number` di Creo** (`F3`, `F5`): non è l'`id` ma la **posizione** nel tree
(RIGHT = F1, TOP = F2, FRONT = F3, PRT_CSYS_DEF = F4, Rivoluzione 1 = F5). I piani interni non
contano. Per tradurre un `id` in `F<n>` si numerano le feature visibili in ordine
(`formato-prt-feature.md` §3).
- **L'offset.** Se un vincolo ha un offset (quota di tipo offset) il valore sta nel `dim_array`
della feature (§2) con il `dim_id` dell'istruzione; non ho un esempio con offset non nullo.
### 6.4 Il foro: `feat_defs_911`
Riferimento: *Foro 1* di `9252000507.prt.11` (feature 157), confrontato con le schermate di Creo.
Creo mostra: tipo **Standard**, profilo **Diritto** con **Maschiato**, filetto ISO **M8x1.25**,
**profondità 25**, **Ø6,8**, **profondità del filetto 20**, **angolo della punta 118°**,
"Includi la superficie del filetto" attivo, posizionamento **Coassiale** con riferimenti
`Supfce:F5(RIVOLUZIONE_1)` e `A_1(ASSE):F5(RIVOLUZIONE_1)`.
La definizione del foro sta nella sezione `FeatDefs`, record `feat_defs_911`. Ha due parti.
**a) I parametri del filetto, in chiaro** (`data(ddt_plinfo_hole_fstn_param)`):
| Campo | Valore letto | In Creo |
|---|---|---|
| `name`, `series` | `ISO`, `ISO` | Tipo filetto: ISO |
| `scrsize` | `M8x1.25` | Dimensione vite: M8x1.25 |
| `scrsize_val` | `0x1d` (29) | indice di M8x1.25 nella tabella delle viti di Creo (nel segnaposto `M1x.25` vale 0) |
| `hole_class` | `H` | (classe del foro) |
| `drillnum` | `6.8` | Ø6,8 |
**b) Le istruzioni** (`place_instruction_ptrs`): una per ogni parametro numerico, nella forma
`type`, `offset` (un `double`, §1), `dim_id`, `ref_id`, `geometry1_id`, `geometry2_id`. Quelle
del foro filettato, con il valore atteso da Creo:
| `type` | `offset` letto | In Creo | Prova |
|---|---|---|---|
| 5104 (`0x13f0`) | 6,8 | Ø6,8 (anche nel foro semplice, vedi sotto) | **livello 1** |
| 5528 (`0x1598`) | 118 | angolo della punta | **livello 1** |
| 5538 (`0x15a2`) | 25 | profondità del foro | **livello 1** |
| 5536 (`0x15a0`) | 1,25 | passo del filetto (M8**x1.25**) | livello 2 |
| 6617 (`0x19d9`) | 11 | non corrisponde a nulla di mostrato | non interpretato |
| 5542, 10103, 5533, 5534 | 0 | non mostrati | non interpretati |
Il codice `type` identifica il parametro; il valore è `offset`. La profondità del filetto (20) e il
diametro nominale (8) **non** sono istruzioni ma quote del `dim_array` (§2), con `dim_type` 9 e 4
e `feat_id` 157. In tutto il `dim_array` ha per il foro cinque quote: 8 (`dim_type` 4), 20 (9),
Ø6,8 (2, scritto con il tag `2e`), 118 (10) e 25 (2). Tre di queste (6,8, 118, 25) compaiono
quindi due volte, come quota e come parametro di un'istruzione.
**Foro semplice e foro standard.** `test.prt.1` ha un foro **semplice**: il suo `feat_defs_911`
è lungo 862 byte e ha solo due istruzioni (`5104` con `dim_id` 7, e `1853`); i parametri del
filetto ci sono comunque, ma con i valori segnaposto di Creo (`M1x.25`, `drillnum 0.75`,
`scrsize_val 0`): **quindi le stringhe `scrsize`/`drillnum` da sole non dicono se il foro è
filettato**. Quello di Fata ha 3212 byte e 12 istruzioni, valori diversi dal segnaposto. Il segno
affidabile oggi è: **foro standard = presenza delle istruzioni 5536 (passo) e 5528 (angolo punta)**,
e il foro è filettato quando anche i valori del filetto non sono quelli segnaposto.
**Non individuato nel file** (per ognuno servono due salvataggi che differiscono solo per
quell'opzione):
- *Maschiato / Trapanato / Gioco*, *Diritto / Rastremato*: non so quale istruzione o flag le
codifichi. `plin_info_hole_flag.flags` vale `2, 2` in entrambi i fori.
- *Includi la superficie del filetto*: idem.
- Il tipo *Standard*/*Semplice* come campo esplicito: oggi si deduce solo dal numero di istruzioni.
**Posizionamento Coassiale.** Gli ingredienti ci sono: un blocco `data(plin_info_ref)` per ogni
riferimento, con gli id di geometria. Il piano del riferimento `Supfce:F5` dovrebbe essere la
faccia piana di *Rivoluzione 1* (feature 73, in `VisibGeom` con `feat_id` 73) e il secondo
riferimento l'asse `A_1`: non ho agganciato quegli id perché le coppie `id_type1`/`id_type2` non le
ho decifrate. La geometria generata dal foro (cilindro, cono, piani con `feat_id` 157) è invece
in `VisibGeom` e `NovisGeom`, e `geometry2_id` 175 delle istruzioni è un suo piano.
### 6.5 Lo smusso: `feat_defs_914`
Riferimento: *Smusso 1* di `9252000507.prt.11` (feature 252), in modifica in Creo: modalità
**Insiemi**, schema quote **D x D** con **D = 0.500**, un insieme ("Insieme 1") con **due
riferimenti**, entrambi `Spigolo:F5(RIVOLUZIONE_1)`, e l'opzione "Superfici con offset".
| Cosa | Dove | Letto | Prova |
|---|---|---|---|
| Valore D | `dim_array`, `feat_id` 252, `dim_type` 9 | `0e` = **0,5** | **livello 1** |
| Valore in `test.prt.1` | `dim_array`, feat 188, `dim_type` 9 | `2f 18 00` = 6,0 | livello 2 |
| Superfici del bordo | `feat_defs_914`, istruzioni di tipo 4530 | `geometry1_id` = 88, 86, 106, 90 | vedi sotto |
| Superfici generate dallo smusso | `VisibGeom`, `feat_id` 252 | superfici coniche (`geom_id` 253, 258, 261, 266) | presenti |
Il `dim_type` 9 è lo stesso del foro (profondità del filetto): dice la *classe* della quota, non
cosa misura. Per lo smusso vale D, ma la stessa classe in un foro vale 20.
**I riferimenti.** Le istruzioni di tipo `4530` hanno nel campo `geometry1_id` gli id 88, 86, 106 e
90, ripetuti a coppie. Risolti con `VisibGeom`: **88** e **106** sono superfici cilindriche con
`feat_id` 73 (cioè *Rivoluzione 1*, F5), **86** è il piano di estremità della stessa rivoluzione
(visto in §3.1 e §6.3). Quindi i riferimenti `Spigolo:F5(RIVOLUZIONE_1)` sono spigoli ricavati dalle
superfici di Rivoluzione 1, come mostra Creo. Non so ancora quale coppia di id formi quale
spigolo, né a quale dei due "Spigolo" della lista corrisponda (le istruzioni non sono in un ordine
che io abbia decifrato), e **90** non l'ho risolto.
**Non decodificato:**
- lo schema quote (D x D, D1 x D2, angolo x D...): non ho trovato dove sta; compaiono vari tipi di
istruzione (`7350`, `7360`, `4549`, `4552`, `4557`, `4558`, `1125`, `7265`, `14741`) senza
corrispondenza con le voci mostrate;
- "Insieme 1" e la sua composizione: compare la struttura (`data(plin_info_chut_info)`, array di
riferimenti) ma non la ho letta;
- "Superfici con offset" e il tipo di transizione;
- l'istruzione `14741`, con un valore `11,476` in precisione piena: non corrisponde a niente di
mostrato.
## 7. Cosa manca per determinare un `.prt` in modo univoco
Tabella di verità: ogni informazione necessaria, se l'ho decodificata e quanto è provata.
| Informazione | Stato | Dove si trova |
|---|---|---|
| Elenco e ordine delle feature | **fatto**, provato (`test.prt.1`, `9252000507.prt.11` contro Creo) | `AllFeatur` |
| Tipo di ogni feature | **fatto**; estrusione vs rivoluzione solo con lo storico | `AllFeatur`, storico |
| Nome di ogni feature | **fatto**, ma solo se esiste lo storico (10 file su 31 leggibili) | `MdlStatus` |
| Quote (valori numerici) | **fatto per i valori interi/semplici**, tag `2f`/`48` | `dim_array` |
| Significato di ogni quota | **parziale**: provato solo per Rivoluzione 1 | `dim_type` (§2.1) |
| Geometria dello schizzo (linee) | **fatto** per le linee, provato su Rivoluzione 1 | `p_saved_result` |
| Cerchi, archi, spline dello schizzo | non verificato (cerchio visto in `test.prt.1`, raggio 50) | `entity(circle)`, `entity(arc_z)`, `entity(spline)` |
| Valori non "tondi" | **non decodificato** (tag diversi da `2f`/`48`) | ovunque |
| Piano e orientamento dello schizzo | **non decodificato** | `gsec3d_ptr`, `sec_orient_data` |
| Posizione/rotazione di piani, assi, punti | **non decodificato** (§6.1) | `ActDatums`, `MdlRefInfo` |
| Vincoli e riferimenti di un piano (come DTM1) | **fatto per DTM1 di `9252000507`** (riferimenti sì, nome del vincolo da un esempio) | `FeatDefsDtm`, §6.3 |
| Offset di un piano da un altro | **non decodificato**: mai visto un offset non nullo | quota in `dim_array` (`dim_id`) |
| Direzione/lato e angolo di una rivoluzione, profondità e direzione di un'estrusione | **non decodificato** | `feat_defs_917`/`916` (`place_instruction_ptrs`, `plin_info_arr`) |
| Parametri del foro (misura, punta, passo, profondità, angolo) | **fatto**, provato contro Creo su `9252000507` | `feat_defs_911`, §6.4 |
| Opzioni del foro (maschiato, diritto, superficie del filetto) | **non individuato** | §6.4 |
| Posizione del foro (riferimenti e offset) | **non decodificato**: le istruzioni di posizionamento ci sono (`plin_info_arr`), come per DTM1 | `feat_defs_911` |
| Valore di raccordi e smussi | **fatto**, provato contro Creo su `9252000507` (D = 0,5) | `dim_array` |
| Spigoli a cui si applicano raccordi/smussi | **parziale**: si risolvono le superfici (feature di origine), non lo spigolo né l'insieme | `feat_defs_914`, §6.5 |
| Schema quote e insiemi dello smusso | **non decodificato** | `feat_defs_914` |
| Tolleranze (h7...) | **non decodificato** | blocco `tol_ptr` del `dim_array` |
| Unità di misura | **non letto** | `UnitSystemDef` (sezione compressa) |
| Feature nascoste / soppresse | **non decodificato** | non individuato nel file |
| Parametri e materiale | parziale, via `ND:0:Model_*` (XML) | sezione compressa `.Z` |
| File Wildfire (generazioni 1322/1375/1376) | **illeggibile** (sezioni di tipo `11`) | `formato-prt-feature.md` §1.3 |
**Conclusione onesta:** oggi si ricava con affidabilità *quali* feature ci sono, *che tipo* sono,
*come si chiamano* (se c'è lo storico), *quanto misurano* le quote semplici e *che forma ha lo
schizzo* se fatto di linee con numeri semplici. Non si ricava ancora *dove* stanno nello spazio né
*come* sono orientate, né si può ricostruire il solido completo.
## 8. Come finire il lavoro
Per ogni riga "non decodificato" della §7 il metodo è lo stesso, ed è quello che ha dato Rivoluzione
1:
1. Scegliere un `.prt` piccolo con **una sola feature** del tipo da decodificare, e annotare da
Creo i valori veri (quote, posizione dei piani, angolo, direzione).
2. Estrarre da `BasFullData`/`ActDatums` i record di quella feature e cercarvi quei numeri con il
decodificatore di §1: i valori noti ancorano i byte.
3. Variare **una cosa alla volta** nel modello (spostare il piano, cambiare l'angolo), risalvare e
confrontare i due file: i byte che cambiano sono il campo cercato.
4. Registrare in questo doc il campo, con il file di prova e il valore atteso, come in §5.
Tre modelli di prova che chiuderebbero la maggior parte della §7: un piano `DTM1` a offset non nullo
dal piano `TOP` (e con un vincolo *Attraverso*, *Normale*, *Offset*...); una estrusione di profondità nota su un piano ruotato; una rivoluzione di 90°.
Una volta chiusa la §7 il passo successivo è un `PrtModelReader` in C# accanto ad `AsmRecordReader`,
con le stesse regole: se un valore non si decodifica si solleva `InvalidDataException`, non si
restituisce un modello parziale in silenzio.