feat(sprint2): wizard 'Nuevo Proyecto' + 5 generadores paramétricos de casco

Generadores paramétricos (arshipdesign/parametric/):
  - wizard_planing.py    → V-fondo, chine dura, deadrise variable AP→FP
  - wizard_cruiser.py    → Carena redonda, plan form Lackenby, Cm ajustable
  - wizard_workboat.py   → Sección cajón, pantoque duro, fondo plano
  - wizard_sailing_mono.py → Velero fin keel, sección fina, LCB a popa
  - series60.py          → Serie 60 / mercante full, Cm ~ 0.96
  - __init__.py          → API unificada generate_hull(family, lpp, beam, draft)
                           + HullFamily enum con labels, rangos Cb, descripciones

Wizard UI (arshipdesign/ui/dialogs/wizards.py):
  - NewShipWizard: QDialog 4 pasos con barra de progreso animada
  - _StepDimensions:  nombre, Lpp, B, puntal, calado + ratios L/B y B/T en vivo
  - _StepFamily:      6 FamilyCard con HullThumbnail QPainter (sección maestra)
  - _StepRefine:      sliders Cb y LCB, spinboxes discretización
  - _StepPreview:     tabla hidrostáticos completa (V, D, Cb, LCB, KB, Awp...)
  - Al aceptar → Hull cargado en visor 3D del viewport Perspectiva

MainWindow:
  - _on_new_project() abre NewShipWizard (antes creaba proyecto vacío)
  - Tras accept(): carga hull en Viewer3DWidget si disponible

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 07:52:46 -04:00
parent 503e00bfc9
commit 002c00aff3
8 changed files with 1685 additions and 15 deletions
+15 -2
View File
@@ -1181,9 +1181,22 @@ class MainWindow(QMainWindow):
def _on_new_project(self) -> None:
if self._project and self._project.is_modified and not self._ask_save():
return
self._project = Project.new("Proyecto sin título")
from arshipdesign.ui.dialogs.wizards import NewShipWizard
wiz = NewShipWizard(self)
if wiz.exec() != wiz.DialogCode.Accepted:
return
hull = wiz.result_hull()
self._project = Project.new(hull.name if hull else "Proyecto sin título")
self._on_project_loaded()
self.statusBar().showMessage("Nuevo proyecto creado")
# Cargar geometría en el visor 3D
if hull is not None and self._viewer_3d is not None:
try:
self._viewer_3d.load_hull(hull)
except Exception as exc:
logger.warning("No se pudo cargar hull en visor 3D: %s", exc)
self.statusBar().showMessage(
f"Nuevo proyecto: {self._project.name}"
)
def _on_open_project(self) -> None:
if self._project and self._project.is_modified and not self._ask_save():