Files
AR-Shipdesign/arshipdesign/parametric/series60.py
T
alro65 002c00aff3 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>
2026-05-27 07:52:46 -04:00

111 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Generador paramétrico — Buque mercante / Supply full (inspirado en Serie 60).
Genera cascos para buques de carga, supply vessels y embarcaciones de trabajo
con coeficientes de bloque altos:
- Fondo casi plano en la zona central
- Cuadernas rectangulares con pantoque redondeado
- Proa de bulbo (simplificada) o proa recta
- Popa transom o popa de crucero
Parámetros típicos:
Cb: 0.60 0.80
Froude: 0.12 0.22
Autor: Álvaro Romero | Sprint 2A — AR-ShipDesign
"""
from __future__ import annotations
import numpy as np
from arshipdesign.core.hull import Hull
from arshipdesign.core.offsets import OffsetsTable
def make_merchant_hull(
name: str = "Buque Mercante / Supply",
lpp: float = 20.0,
beam: float = 6.0,
draft: float = 2.40,
depth: float = 3.20,
cb: float = 0.70,
lcb_frac: float = 0.50,
cm: float = 0.96, # sección maestra casi rectangular
bilge_radius_frac: float = 0.08,
flat_bottom_frac: float = 0.90,
n_stations: int = 21,
n_waterlines: int = 11,
) -> Hull:
"""Genera un casco tipo Serie 60 / buque mercante.
Parámetros
----------
cb : float
Coeficiente de bloque objetivo (0.600.80).
cm : float
Coeficiente de cuaderna maestra (0.930.98).
bilge_radius_frac : float
Radio del pantoque / calado (0.060.14).
flat_bottom_frac : float
Ancho del fondo plano / manga (0.850.94).
"""
x_sta = np.linspace(0.0, lpp, n_stations)
z_wl = np.linspace(0.0, draft, n_waterlines)
xi = (x_sta / lpp - 0.5) * 2.0
lcb_shift = 2.0 * (lcb_frac - 0.5)
f_plan = _merchant_plan_form(xi, cb, lcb_shift)
r_bilge = draft * bilge_radius_frac
y_flat_b = (beam / 2.0) * flat_bottom_frac
data = np.zeros((n_stations, n_waterlines))
for i in range(n_stations):
y_wl = (beam / 2.0) * f_plan[i]
scale = f_plan[i]
y_flat_i = y_flat_b * scale
r_bilge_i = r_bilge * max(scale, 0.25)
for j, z in enumerate(z_wl):
if z <= r_bilge_i:
dz = r_bilge_i - z
inner = max(0.0, r_bilge_i**2 - dz**2)
y = y_flat_i - r_bilge_i + np.sqrt(inner)
else:
# Costados casi verticales (Cm alto)
t = (z - r_bilge_i) / max(draft - r_bilge_i, 1e-6)
# Cm ajusta plenitud de la zona de costados
flare_side = (1.0 - cm) * 0.15
y = y_flat_i + (y_wl - y_flat_i) * (t + flare_side * t * (1 - t))
y = min(y, y_wl)
data[i, j] = max(0.0, y)
offsets = OffsetsTable(
x_stations=x_sta,
z_waterlines=z_wl,
data=data,
station_labels=[f"S{i}" for i in range(n_stations)],
lpp=lpp, beam=beam, draft=draft,
)
return Hull(
name=name, lpp=lpp, beam=beam, depth=depth, draft=draft, offsets=offsets
)
def _merchant_plan_form(
xi: np.ndarray, cb: float, lcb_shift: float
) -> np.ndarray:
"""Plan form mercante: central muy llena, extremos redondeados."""
n = _merchant_plan_exponent(cb)
xi_s = np.clip(xi - lcb_shift, -1.0, 1.0)
return np.maximum(0.0, 1.0 - np.abs(xi_s) ** n)
def _merchant_plan_exponent(cb: float) -> float:
cb_vals = [0.58, 0.65, 0.70, 0.75, 0.82]
n_vals = [3.0, 4.0, 5.0, 6.0, 8.0]
return float(np.interp(cb, cb_vals, n_vals))