002c00aff3
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>
170 lines
5.4 KiB
Python
170 lines
5.4 KiB
Python
"""
|
||
arshipdesign.parametric — Generadores paramétricos de cascos.
|
||
|
||
Exporta la función unificada ``generate_hull`` y los constantes
|
||
de familia usados por el wizard de nuevo proyecto.
|
||
|
||
Uso rápido
|
||
----------
|
||
>>> from arshipdesign.parametric import generate_hull, HullFamily
|
||
>>> hull = generate_hull(
|
||
... family=HullFamily.DISPLACEMENT,
|
||
... lpp=15.0, beam=4.0, draft=1.60, depth=2.30,
|
||
... cb=0.55,
|
||
... )
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from enum import Enum, auto
|
||
from typing import Any
|
||
|
||
from arshipdesign.core.hull import Hull
|
||
|
||
|
||
class HullFamily(str, Enum):
|
||
"""Familia de carena disponible en el wizard."""
|
||
PLANING = "planing" # Planeador — V-fondo, chine dura
|
||
DISPLACEMENT = "displacement" # Desplazamiento — carena redonda
|
||
SEMI_DISP = "semi_disp" # Semi-desplazamiento
|
||
WORKBOAT = "workboat" # Workboat / Supply / Remolcador
|
||
SAILING = "sailing" # Velero monocasco fin keel
|
||
MERCHANT = "merchant" # Buque mercante / Serie 60
|
||
|
||
@property
|
||
def label_es(self) -> str:
|
||
return {
|
||
"planing": "Planeo",
|
||
"displacement":"Desplazamiento",
|
||
"semi_disp": "Semi-desplazamiento",
|
||
"workboat": "Workboat / Supply",
|
||
"sailing": "Velero",
|
||
"merchant": "Mercante / Supply full",
|
||
}[self.value]
|
||
|
||
@property
|
||
def cb_default(self) -> float:
|
||
return {
|
||
"planing": 0.43,
|
||
"displacement":0.55,
|
||
"semi_disp": 0.50,
|
||
"workboat": 0.67,
|
||
"sailing": 0.40,
|
||
"merchant": 0.70,
|
||
}[self.value]
|
||
|
||
@property
|
||
def cb_range(self) -> tuple[float, float]:
|
||
return {
|
||
"planing": (0.38, 0.48),
|
||
"displacement":(0.45, 0.65),
|
||
"semi_disp": (0.46, 0.58),
|
||
"workboat": (0.60, 0.75),
|
||
"sailing": (0.35, 0.46),
|
||
"merchant": (0.60, 0.82),
|
||
}[self.value]
|
||
|
||
@property
|
||
def description_es(self) -> str:
|
||
return {
|
||
"planing":
|
||
"Embarcación rápida con fondo en V y chine dura.\n"
|
||
"Fn > 0.50 — lanchas, patrulleras, RIBs.",
|
||
"displacement":
|
||
"Carena redondeada de velocidad moderada.\n"
|
||
"Fn 0.20–0.35 — cruceros, pesqueros, ferrys.",
|
||
"semi_disp":
|
||
"Compromiso entre planeo y desplazamiento.\n"
|
||
"Fn 0.35–0.55 — yates de motor, patrulleras.",
|
||
"workboat":
|
||
"Sección cajón con pantoque duro.\n"
|
||
"Fn < 0.22 — remolcadores, supply, barcazas.",
|
||
"sailing":
|
||
"Cuerpo fino con quilla de aleta.\n"
|
||
"Veleros de recreo y regata.",
|
||
"merchant":
|
||
"Formas llenas tipo Serie 60.\n"
|
||
"Fn < 0.20 — carga, RORO, buques de trabajo.",
|
||
}[self.value]
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Función unificada
|
||
# ---------------------------------------------------------------------------
|
||
|
||
def generate_hull(
|
||
family: HullFamily | str,
|
||
lpp: float,
|
||
beam: float,
|
||
draft: float,
|
||
depth: float | None = None,
|
||
name: str = "",
|
||
n_stations: int = 21,
|
||
n_waterlines: int = 11,
|
||
**kwargs: Any,
|
||
) -> Hull:
|
||
"""Genera un Hull paramétrico de la familia indicada.
|
||
|
||
Parámetros
|
||
----------
|
||
family : HullFamily
|
||
Tipo de carena (ver HullFamily).
|
||
lpp : float
|
||
Eslora entre perpendiculares [m].
|
||
beam : float
|
||
Manga máxima [m].
|
||
draft : float
|
||
Calado de diseño [m].
|
||
depth : float, optional
|
||
Puntal de trazado [m]. Si es None usa draft * 1.45.
|
||
name : str
|
||
Nombre del proyecto/casco.
|
||
n_stations : int
|
||
Número de estaciones transversales (≥ 7, default 21).
|
||
n_waterlines : int
|
||
Número de líneas de agua (≥ 5, default 11).
|
||
**kwargs
|
||
Parámetros adicionales específicos de cada familia
|
||
(p.ej. deadrise_mid para planing, cb para displacement, etc.)
|
||
|
||
Retorna
|
||
-------
|
||
Hull
|
||
"""
|
||
fam = HullFamily(family) if isinstance(family, str) else family
|
||
if depth is None:
|
||
depth = draft * 1.45
|
||
|
||
if not name:
|
||
name = f"{fam.label_es} {lpp:.0f}m"
|
||
|
||
common = dict(
|
||
name=name, lpp=lpp, beam=beam, draft=draft, depth=depth,
|
||
n_stations=n_stations, n_waterlines=n_waterlines,
|
||
)
|
||
common.update(kwargs)
|
||
|
||
if fam == HullFamily.PLANING:
|
||
from arshipdesign.parametric.wizard_planing import make_planing_hull
|
||
return make_planing_hull(**common)
|
||
|
||
elif fam in (HullFamily.DISPLACEMENT, HullFamily.SEMI_DISP):
|
||
from arshipdesign.parametric.wizard_cruiser import make_displacement_hull
|
||
if fam == HullFamily.SEMI_DISP and "cb" not in kwargs:
|
||
common.setdefault("cb", 0.50)
|
||
return make_displacement_hull(**common)
|
||
|
||
elif fam == HullFamily.WORKBOAT:
|
||
from arshipdesign.parametric.wizard_workboat import make_workboat_hull
|
||
return make_workboat_hull(**common)
|
||
|
||
elif fam == HullFamily.SAILING:
|
||
from arshipdesign.parametric.wizard_sailing_mono import make_sailing_hull
|
||
return make_sailing_hull(**common)
|
||
|
||
elif fam == HullFamily.MERCHANT:
|
||
from arshipdesign.parametric.series60 import make_merchant_hull
|
||
return make_merchant_hull(**common)
|
||
|
||
else:
|
||
raise ValueError(f"Familia de carena desconocida: {fam!r}")
|