"""Generazione di una pagina HTML per ruotare il modello nel browser. La pagina e' autonoma: mesh, stile e codice sono dentro il file. Nessuna libreria esterna, nessuna connessione. Si apre con un doppio clic, anche da una cartella di rete, e funziona su qualunque browser recente. Il disegno usa una canvas 2D con ordinamento per profondita' invece di WebGL, che su alcune postazioni aziendali e' disattivato: per la mesh di un componente meccanico e' abbondante. Un pezzo meccanico ombreggiato e basta risulta illeggibile, perche' facce complanari hanno tutte lo stesso colore. Qui sopra alle facce disegniamo: - gli **spigoli vivi**, dove due facce formano un angolo netto; - la **silhouette**, dove una faccia visibile ne incontra una nascosta. Sono le stesse linee che un disegnatore traccerebbe a mano, e sono cio' che rende riconoscibile la forma. """ from __future__ import annotations import json from xml.sax.saxutils import escape TEMPLATE = """ %(title)s

%(title)s

%(subtitle)s
""" def build(mesh, title: str, subtitle: str = "") -> str: """Pagina HTML autonoma con il modello incorporato.""" vertices, faces, edges = mesh.indexed() compact = lambda data: json.dumps(data, separators=(",", ":")) return TEMPLATE % { "title": escape(title), "subtitle": escape(subtitle), "vertices": compact(vertices), "faces": compact(faces), "edges": compact(edges), } def describe(mesh, unit: str = "mm") -> str: """Riga di riepilogo con ingombro e numero di triangoli.""" w, d, h = mesh.size return ("%d triangoli · ingombro %g × %g × %g %s" % (len(mesh), w, d, h, unit))