v0.1-sprint0: Esqueleto completo AR-ShipDesign

- Estructura completa de carpetas (236 módulos stub + implementados)
- pyproject.toml, requirements, .gitignore, LICENSE (propietario)
- core/project.py: serialización .arsd (ZIP con JSON)
- core/units.py: conversiones SI <-> imperial completas
- ui/main_window.py: layout DELFTship-style con todos los paneles
  - Árbol de proyecto (dock izquierda)
  - Tabs de módulos (centro)
  - Panel de propiedades (dock derecha)
  - Panel hidrostáticos en vivo (inferior, fijo)
- ui/i18n: español e inglés
- ui/themes: tema claro y oscuro
- utils/logger.py, settings.py, validation.py
- data/liquids.json: 15 líquidos navales
- data/stability_criteria.json: IMO IS Code 2008, A.749(18), USCG
- tests/test_startup.py: 12 tests, todos PASSED
- Módulo scantling/ ISO 12215 (stubs Sprint 2.5)
- Módulo fabrication/molds/ para moldes FRP (stubs Sprint 13B)
- Módulo fabrication/ para CNC plasma/router/laser (stubs Sprint 13)
This commit is contained in:
2026-05-26 22:10:18 -04:00
commit 0dbc2a4518
266 changed files with 4249 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
{
"permissions": {
"allow": [
"Bash(*)",
"mcp__ccd_session__mark_chapter",
"mcp__ccd_session__spawn_task",
"mcp__Claude_in_Chrome__*",
"mcp__Claude_Preview__*",
"mcp__ccd_session_mgmt__*",
"mcp__ccd_directory__*",
"mcp__scheduled-tasks__*",
"mcp__mcp-registry__*"
],
"deny": [
"Bash(rm -rf /*)",
"Bash(format *)",
"Bash(del /f /s /q C:\\*)",
"Bash(git push --force origin main)",
"Bash(git push --force origin master)",
"Bash(DROP DATABASE*)",
"Bash(mkfs*)"
]
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"permissions": {
"allow": [
"Bash(ls ~/.claude/projects/ | head -20)",
"Bash(awk '{print $2}')",
"Bash(xargs -I{} sh -c 'grep -o \"\\\\\"name\\\\\":\\\\\"Bash\\\\\",\\\\\"input\\\\\":{\\\\\"command\\\\\":\\\\\"[^\\\\\"]*\\\\\"\" \"{}\" 2>/dev/null || true')",
"Bash(awk '{print $1, $2}')",
"Bash(xargs '-I{}' python3 -c ' *)",
"Bash(sort -t/ -k1)",
"Bash(xargs -I{} wc -l {})",
"Bash(mkdir -p \"D:/Proyectos Software/AR-Shipdesign/.claude\")",
"Bash(python create_stubs.py)"
]
}
}
+11
View File
@@ -0,0 +1,11 @@
# AR-ShipDesign — Variables de entorno de ejemplo
# Copiar a .env y ajustar según el entorno local
# Nivel de logging: DEBUG, INFO, WARNING, ERROR
LOG_LEVEL=INFO
# Idioma por defecto: es, en
ARSD_LANG=es
# Tema por defecto: light, dark
ARSD_THEME=light
+49
View File
@@ -0,0 +1,49 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
dist/
*.egg-info/
.eggs/
*.egg
.venv/
venv/
env/
.env
# PyInstaller
*.spec
build/
dist/
# Testing
.pytest_cache/
.coverage
htmlcov/
tests/benchmark_results/
# IDEs
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
desktop.ini
# Logs
logs/
*.log
# Proyecto
*.arsd
*.arsd.bak
# Datos sensibles
.env.local
secrets.json
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
# Changelog — AR-ShipDesign
Todos los cambios notables se documentan en este archivo.
Formato basado en [Keep a Changelog](https://keepachangelog.com/es/1.0.0/).
---
## [0.1.0] — Sprint 0 — 2025-05-26
### Añadido
- Estructura completa de carpetas del proyecto con stubs documentados
- `pyproject.toml`, `requirements.txt`, `.gitignore`, `LICENSE.txt`
- `main.py` — punto de entrada de la aplicación
- `arshipdesign/core/project.py` — clase `Project` con serialización `.arsd`
- `arshipdesign/core/units.py` — conversiones SI ↔ imperial completas
- `arshipdesign/ui/main_window.py` — ventana principal con layout DELFTship-style
- Panel árbol de proyecto (izquierda)
- Vista 3D central (PyVista placeholder)
- Panel de propiedades (derecha)
- Panel hidrostáticos en vivo (inferior, siempre visible)
- Barra de tabs de módulos
- `arshipdesign/ui/i18n/es.json` y `en.json` — internacionalización
- `arshipdesign/ui/themes/light.qss` y `dark.qss` — temas visual
- `arshipdesign/utils/logger.py` — logging rotativo en %APPDATA%
- `arshipdesign/utils/settings.py` — configuración con QSettings
- `data/liquids.json` — base de datos de líquidos navales
- `data/stability_criteria.json` — criterios IMO IS Code 2008
- `tests/conftest.py` + `tests/test_startup.py` — tests básicos
### Arquitectura
- NURBS (geomdl) como motor geométrico principal
- Stubs de todos los módulos futuros con referencia al sprint de implementación
- Módulo `scantling/` para diseño estructural según ISO 12215
- Módulo `fabrication/` para CNC (plasma/router/laser) y moldes FRP
+10
View File
@@ -0,0 +1,10 @@
AR-ShipDesign — Software de Diseño Naval
Copyright (c) 2025 Álvaro Rodríguez. Todos los derechos reservados.
LICENCIA PROPIETARIA
Este software y su código fuente son propiedad exclusiva de Álvaro Rodríguez.
Queda prohibida su copia, distribución, modificación, ingeniería inversa o
uso comercial sin autorización expresa y por escrito del titular.
Se reservan todos los derechos hasta que se defina una licencia de distribución.
+49
View File
@@ -0,0 +1,49 @@
# AR-ShipDesign
Software profesional de diseño naval para Windows.
**Lo mejor de Maxsurf** (rigor técnico, NURBS, suite completa) con **la UX de DELFTship**
(árbol de capas, vista 3D, hidrostáticos siempre visibles, wizards paso a paso).
## Características principales
- Diseño de cascos por superficies NURBS (buques a motor y veleros)
- Hidrostáticos en tiempo real
- Estabilidad intacta y en avería — criterios IMO IS Code 2008
- Escantillado estructural según ISO 12215
- Resistencia: Holtrop & Mennen, Savitsky, DSYHS y más
- VPP completo para veleros (polar diagram estilo ORC)
- Seakeeping por strip theory (Salvesen-Tuck-Faltinsen)
- Sistemas del buque: eléctrico, combustible, FW, CI, HVAC, gobierno
- Fabricación CNC: nesting + G-code (plasma / router / laser)
- Construcción de moldes en fibra de vidrio con schedule de laminado
## Instalación rápida
```bash
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
python main.py
```
## Requisitos
- Windows 10/11 (64-bit)
- Python 3.11+
- 4 GB RAM mínimo, 8 GB recomendado
## Estado del proyecto
| Sprint | Módulo | Estado |
|--------|--------|--------|
| 0 | Esqueleto + UI base + proyecto .arsd | ✅ Completo |
| 1 | Geometría NURBS + offsets + viewer 3D | 🔜 Próximo |
| 2 | Hidrostáticos básicos | ⏳ Pendiente |
| 3 | Equilibrio libre + estabilidad GZ | ⏳ Pendiente |
| ... | ... | ... |
## Licencia
Copyright © 2025 Álvaro Rodríguez. Todos los derechos reservados.
Ver `LICENSE.txt` para detalles.
+9
View File
@@ -0,0 +1,9 @@
"""
AR-ShipDesign — Software profesional de diseño naval.
Copyright (c) 2025 Álvaro Rodríguez. Todos los derechos reservados.
"""
__version__ = "0.1.0"
__author__ = "Álvaro Rodríguez"
__license__ = "Propietario"
+1
View File
@@ -0,0 +1 @@
# arshipdesign/core
+2
View File
@@ -0,0 +1,2 @@
"""Apéndices. Stub — Sprint 1."""
raise NotImplementedError("appendage — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Baterías. Stub — Sprint 7."""
raise NotImplementedError("battery — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Compartimento. Stub — Sprint 4."""
raise NotImplementedError("compartment — Sprint 4")
+2
View File
@@ -0,0 +1,2 @@
"""Carga eléctrica. Stub — Sprint 7."""
raise NotImplementedError("electrical_load — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Motor. Stub — Sprint 5."""
raise NotImplementedError("engine — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Reductora. Stub — Sprint 5."""
raise NotImplementedError("gear — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Genset. Stub — Sprint 7."""
raise NotImplementedError("generator — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Geometría del casco NURBS. Stub — Sprint 1."""
raise NotImplementedError("hull — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Peso en rosca. Stub — Sprint 4."""
raise NotImplementedError("lightship — Sprint 4")
+2
View File
@@ -0,0 +1,2 @@
"""Líquidos. Stub — Sprint 4."""
raise NotImplementedError("liquid — Sprint 4")
+2
View File
@@ -0,0 +1,2 @@
"""Condiciones de carga. Stub — Sprint 4."""
raise NotImplementedError("loadcase — Sprint 4")
+2
View File
@@ -0,0 +1,2 @@
"""Tabla de offsets. Stub — Sprint 1."""
raise NotImplementedError("offsets — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Tubería. Stub — Sprint 7."""
raise NotImplementedError("pipe — Sprint 7")
+273
View File
@@ -0,0 +1,273 @@
"""
Clase Project — raíz del modelo de datos de AR-ShipDesign.
Gestiona la serialización/deserialización del formato .arsd
(ZIP con JSONs + meshes binarios).
El archivo .arsd es un ZIP que contiene:
manifest.json → metadatos del proyecto
ship.json → datos del buque
geometry/ → meshes binarios (.npz)
loadcases/ → condiciones de carga
"""
from __future__ import annotations
import json
import shutil
import zipfile
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any
from arshipdesign.utils.logger import get_logger
logger = get_logger("core.project")
ARSD_EXTENSION = ".arsd"
MANIFEST_FILE = "manifest.json"
SHIP_FILE = "ship.json"
FORMAT_VERSION = "1.0"
@dataclass
class ProjectMetadata:
"""Metadatos del proyecto."""
name: str = "Proyecto sin título"
description: str = ""
author: str = ""
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
modified_at: str = field(default_factory=lambda: datetime.now().isoformat())
format_version: str = FORMAT_VERSION
arshipdesign_version: str = "0.1.0"
def to_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"description": self.description,
"author": self.author,
"created_at": self.created_at,
"modified_at": self.modified_at,
"format_version": self.format_version,
"arshipdesign_version": self.arshipdesign_version,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> "ProjectMetadata":
return cls(
name=data.get("name", "Sin título"),
description=data.get("description", ""),
author=data.get("author", ""),
created_at=data.get("created_at", datetime.now().isoformat()),
modified_at=data.get("modified_at", datetime.now().isoformat()),
format_version=data.get("format_version", FORMAT_VERSION),
arshipdesign_version=data.get("arshipdesign_version", "0.1.0"),
)
class Project:
"""
Raíz del modelo de datos de AR-ShipDesign.
Gestiona el ciclo de vida del proyecto: nuevo, abrir, guardar, guardar como.
El formato .arsd es un archivo ZIP con estructura interna definida.
Parameters
----------
path : Path | None
Ruta al archivo .arsd. None para proyecto nuevo sin guardar.
Examples
--------
>>> project = Project.new("Mi velero")
>>> project.save(Path("D:/proyectos/mi_velero.arsd"))
>>> project2 = Project.load(Path("D:/proyectos/mi_velero.arsd"))
"""
def __init__(self, path: Path | None = None) -> None:
self.path: Path | None = path
self.metadata: ProjectMetadata = ProjectMetadata()
self.ship_data: dict[str, Any] = {}
self._is_modified: bool = False
# ──────────────────────────────────────────────
# CONSTRUCTORES
# ──────────────────────────────────────────────
@classmethod
def new(cls, name: str = "Proyecto sin título", author: str = "") -> "Project":
"""Crea un proyecto nuevo vacío."""
project = cls(path=None)
project.metadata = ProjectMetadata(name=name, author=author)
project.ship_data = _default_ship_data()
project._is_modified = True
logger.info("Nuevo proyecto creado: %s", name)
return project
@classmethod
def load(cls, path: Path) -> "Project":
"""
Carga un proyecto desde un archivo .arsd.
Parameters
----------
path : Path
Ruta al archivo .arsd.
Raises
------
FileNotFoundError
Si el archivo no existe.
ValueError
Si el archivo no es un .arsd válido.
"""
path = Path(path)
if not path.exists():
raise FileNotFoundError(f"Archivo no encontrado: {path}")
if path.suffix.lower() != ARSD_EXTENSION:
raise ValueError(f"El archivo no tiene extensión {ARSD_EXTENSION}: {path}")
logger.info("Cargando proyecto: %s", path)
try:
with zipfile.ZipFile(path, "r") as zf:
names = zf.namelist()
if MANIFEST_FILE not in names:
raise ValueError("Archivo .arsd corrupto: falta manifest.json")
manifest_raw = zf.read(MANIFEST_FILE).decode("utf-8")
manifest = json.loads(manifest_raw)
metadata = ProjectMetadata.from_dict(manifest)
ship_data: dict[str, Any] = {}
if SHIP_FILE in names:
ship_raw = zf.read(SHIP_FILE).decode("utf-8")
ship_data = json.loads(ship_raw)
except zipfile.BadZipFile as e:
raise ValueError(f"Archivo .arsd inválido o corrupto: {e}") from e
project = cls(path=path)
project.metadata = metadata
project.ship_data = ship_data
project._is_modified = False
logger.info("Proyecto cargado: %s (v%s)", metadata.name, metadata.format_version)
return project
# ──────────────────────────────────────────────
# GUARDAR
# ──────────────────────────────────────────────
def save(self, path: Path | None = None) -> None:
"""
Guarda el proyecto en un archivo .arsd.
Parameters
----------
path : Path | None
Si se especifica, guarda en esa ruta (guardar como).
Si es None, guarda en self.path.
Raises
------
ValueError
Si no hay ruta definida.
"""
save_path = path or self.path
if save_path is None:
raise ValueError("No hay ruta definida. Use save(path) para guardar.")
save_path = Path(save_path)
if save_path.suffix.lower() != ARSD_EXTENSION:
save_path = save_path.with_suffix(ARSD_EXTENSION)
# Actualizar fecha de modificación
self.metadata.modified_at = datetime.now().isoformat()
# Guardar en un temporal primero (escritura atómica)
tmp_path = save_path.with_suffix(".arsd.tmp")
try:
with zipfile.ZipFile(tmp_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
# manifest.json
zf.writestr(
MANIFEST_FILE,
json.dumps(self.metadata.to_dict(), indent=2, ensure_ascii=False),
)
# ship.json
zf.writestr(
SHIP_FILE,
json.dumps(self.ship_data, indent=2, ensure_ascii=False),
)
# Reemplazar el archivo final
shutil.move(str(tmp_path), str(save_path))
except Exception:
if tmp_path.exists():
tmp_path.unlink()
raise
self.path = save_path
self._is_modified = False
logger.info("Proyecto guardado: %s", save_path)
def save_as(self, path: Path) -> None:
"""Guarda el proyecto en una nueva ruta."""
self.save(path)
# ──────────────────────────────────────────────
# PROPIEDADES
# ──────────────────────────────────────────────
@property
def name(self) -> str:
return self.metadata.name
@name.setter
def name(self, value: str) -> None:
self.metadata.name = value
self._is_modified = True
@property
def is_modified(self) -> bool:
return self._is_modified
@property
def display_name(self) -> str:
"""Nombre para mostrar en la barra de título."""
base = self.metadata.name
if self.path:
base = self.path.stem
return f"{base}{'*' if self._is_modified else ''}"
def mark_modified(self) -> None:
"""Marca el proyecto como modificado."""
self._is_modified = True
def __repr__(self) -> str:
return f"Project(name={self.name!r}, path={self.path})"
# ──────────────────────────────────────────────
# HELPERS PRIVADOS
# ──────────────────────────────────────────────
def _default_ship_data() -> dict[str, Any]:
"""Estructura de datos inicial de un buque vacío."""
return {
"type": "motor", # motor | sailing_mono | sailing_cat | planing
"hull": {},
"appendages": [],
"superstructure": {},
"tanks": [],
"compartments": [],
"loadcases": [],
"rig": None,
"propulsion": {},
"systems": {},
"scantling": {},
}
+2
View File
@@ -0,0 +1,2 @@
"""Hélice. Stub — Sprint 5."""
raise NotImplementedError("propeller — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Bomba. Stub — Sprint 7."""
raise NotImplementedError("pump — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Aparejo velero. Stub — Sprint 6."""
raise NotImplementedError("rig — Sprint 6")
+2
View File
@@ -0,0 +1,2 @@
"""Velas. Stub — Sprint 6."""
raise NotImplementedError("sail — Sprint 6")
+2
View File
@@ -0,0 +1,2 @@
"""Sección transversal. Stub — Sprint 1."""
raise NotImplementedError("section — Sprint 1")
+3
View File
@@ -0,0 +1,3 @@
"""Clase Ship. Stub — Sprint 1."""
class Ship:
def __init__(self): raise NotImplementedError("Ship — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Superestructura. Stub — Sprint 1."""
raise NotImplementedError("superstructure — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Sistema del buque. Stub — Sprint 7."""
raise NotImplementedError("system — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Tanque. Stub — Sprint 4."""
raise NotImplementedError("tank — Sprint 4")
+285
View File
@@ -0,0 +1,285 @@
"""
Conversiones de unidades SI ↔ Imperial para AR-ShipDesign.
Internamente todo se almacena en SI (m, kg, s, N, Pa, W).
Este módulo convierte para visualización en UI cuando el usuario
selecciona unidades imperiales.
Unidades SI base:
Longitud → metro (m)
Masa → kilogramo (kg)
Fuerza → Newton (N)
Presión → Pascal (Pa)
Potencia → Watt (W)
Velocidad → metro/segundo (m/s)
Volumen → metro cúbico (m³)
Área → metro cuadrado (m²)
Temperatura → Celsius (°C)
"""
from __future__ import annotations
from enum import Enum
class UnitSystem(Enum):
SI = "si"
IMPERIAL = "imperial"
# ──────────────────────────────────────────────
# LONGITUD
# ──────────────────────────────────────────────
def m_to_ft(m: float) -> float:
"""Metros → Pies."""
return m * 3.280839895
def ft_to_m(ft: float) -> float:
"""Pies → Metros."""
return ft / 3.280839895
def m_to_in(m: float) -> float:
"""Metros → Pulgadas."""
return m * 39.3700787
def in_to_m(inches: float) -> float:
"""Pulgadas → Metros."""
return inches / 39.3700787
def mm_to_in(mm: float) -> float:
"""Milímetros → Pulgadas."""
return mm / 25.4
def in_to_mm(inches: float) -> float:
"""Pulgadas → Milímetros."""
return inches * 25.4
def nm_to_km(nm: float) -> float:
"""Millas náuticas → Kilómetros."""
return nm * 1.852
def km_to_nm(km: float) -> float:
"""Kilómetros → Millas náuticas."""
return km / 1.852
# ──────────────────────────────────────────────
# VELOCIDAD
# ──────────────────────────────────────────────
def ms_to_kn(ms: float) -> float:
"""m/s → Nudos."""
return ms * 1.943844
def kn_to_ms(kn: float) -> float:
"""Nudos → m/s."""
return kn / 1.943844
def kn_to_kmh(kn: float) -> float:
"""Nudos → km/h."""
return kn * 1.852
def kmh_to_kn(kmh: float) -> float:
"""km/h → Nudos."""
return kmh / 1.852
# ──────────────────────────────────────────────
# MASA Y FUERZA
# ──────────────────────────────────────────────
def kg_to_lb(kg: float) -> float:
"""Kilogramos → Libras."""
return kg * 2.204622622
def lb_to_kg(lb: float) -> float:
"""Libras → Kilogramos."""
return lb / 2.204622622
def kg_to_ton_metric(kg: float) -> float:
"""Kilogramos → Toneladas métricas."""
return kg / 1000.0
def ton_metric_to_kg(t: float) -> float:
"""Toneladas métricas → Kilogramos."""
return t * 1000.0
def kg_to_ton_long(kg: float) -> float:
"""Kilogramos → Toneladas largas (UK)."""
return kg / 1016.0469088
def ton_long_to_kg(t: float) -> float:
"""Toneladas largas → Kilogramos."""
return t * 1016.0469088
def n_to_lbf(n: float) -> float:
"""Newton → Libra-fuerza."""
return n * 0.224808943
def lbf_to_n(lbf: float) -> float:
"""Libra-fuerza → Newton."""
return lbf / 0.224808943
# ──────────────────────────────────────────────
# PRESIÓN
# ──────────────────────────────────────────────
def pa_to_psi(pa: float) -> float:
"""Pascal → PSI."""
return pa * 0.000145038
def psi_to_pa(psi: float) -> float:
"""PSI → Pascal."""
return psi / 0.000145038
def pa_to_bar(pa: float) -> float:
"""Pascal → Bar."""
return pa / 100000.0
def bar_to_pa(bar: float) -> float:
"""Bar → Pascal."""
return bar * 100000.0
def pa_to_kpa(pa: float) -> float:
"""Pascal → Kilopascal."""
return pa / 1000.0
def kpa_to_pa(kpa: float) -> float:
"""Kilopascal → Pascal."""
return kpa * 1000.0
# ──────────────────────────────────────────────
# POTENCIA
# ──────────────────────────────────────────────
def w_to_hp(w: float) -> float:
"""Watt → Caballos de vapor (metric HP)."""
return w / 735.49875
def hp_to_w(hp: float) -> float:
"""HP métrico → Watt."""
return hp * 735.49875
def w_to_bhp(w: float) -> float:
"""Watt → BHP (brake horsepower, SAE)."""
return w / 745.69987
def bhp_to_w(bhp: float) -> float:
"""BHP → Watt."""
return bhp * 745.69987
def w_to_kw(w: float) -> float:
"""Watt → Kilowatt."""
return w / 1000.0
def kw_to_w(kw: float) -> float:
"""Kilowatt → Watt."""
return kw * 1000.0
# ──────────────────────────────────────────────
# VOLUMEN
# ──────────────────────────────────────────────
def m3_to_ft3(m3: float) -> float:
"""m³ → ft³."""
return m3 * 35.3146667
def ft3_to_m3(ft3: float) -> float:
"""ft³ → m³."""
return ft3 / 35.3146667
def m3_to_liters(m3: float) -> float:
"""m³ → Litros."""
return m3 * 1000.0
def liters_to_m3(liters: float) -> float:
"""Litros → m³."""
return liters / 1000.0
def m3_to_usgal(m3: float) -> float:
"""m³ → Galones US."""
return m3 * 264.172052
def usgal_to_m3(gal: float) -> float:
"""Galones US → m³."""
return gal / 264.172052
# ──────────────────────────────────────────────
# ÁREA
# ──────────────────────────────────────────────
def m2_to_ft2(m2: float) -> float:
"""m² → ft²."""
return m2 * 10.7639104
def ft2_to_m2(ft2: float) -> float:
"""ft² → m²."""
return ft2 / 10.7639104
# ──────────────────────────────────────────────
# TEMPERATURA
# ──────────────────────────────────────────────
def c_to_f(celsius: float) -> float:
"""Celsius → Fahrenheit."""
return celsius * 9 / 5 + 32
def f_to_c(fahrenheit: float) -> float:
"""Fahrenheit → Celsius."""
return (fahrenheit - 32) * 5 / 9
# ──────────────────────────────────────────────
# DENSIDAD
# ──────────────────────────────────────────────
def kg_m3_to_lb_ft3(rho: float) -> float:
"""kg/m³ → lb/ft³."""
return rho * 0.062427961
def lb_ft3_to_kg_m3(rho: float) -> float:
"""lb/ft³ → kg/m³."""
return rho / 0.062427961
# ──────────────────────────────────────────────
# MOMENTO
# ──────────────────────────────────────────────
def nm_to_ft_lbf(nm: float) -> float:
"""Newton·metro → ft·lbf."""
return nm * 0.737562149
def ft_lbf_to_nm(ft_lbf: float) -> float:
"""ft·lbf → Newton·metro."""
return ft_lbf / 0.737562149
# ──────────────────────────────────────────────
# HELPER GENÉRICO para la UI
# ──────────────────────────────────────────────
UNIT_LABELS: dict[str, dict[str, str]] = {
"length": {"si": "m", "imperial": "ft"},
"length_small": {"si": "mm", "imperial": "in"},
"speed": {"si": "m/s", "imperial": "kn"},
"speed_nav": {"si": "kn", "imperial": "kn"},
"mass": {"si": "kg", "imperial": "lb"},
"displacement": {"si": "t", "imperial": "LT"},
"power": {"si": "kW", "imperial": "BHP"},
"pressure": {"si": "kPa", "imperial": "psi"},
"volume": {"si": "", "imperial": "ft³"},
"area": {"si": "", "imperial": "ft²"},
"temperature": {"si": "°C", "imperial": "°F"},
"density": {"si": "kg/m³", "imperial": "lb/ft³"},
}
def unit_label(quantity: str, system: str = "si") -> str:
"""Retorna la etiqueta de unidad para una cantidad dada."""
return UNIT_LABELS.get(quantity, {}).get(system, "")
+2
View File
@@ -0,0 +1,2 @@
"""Válvula. Stub — Sprint 7."""
raise NotImplementedError("valve — Sprint 7")
+2
View File
@@ -0,0 +1,2 @@
"""Plano de flotación. Stub — Sprint 2."""
raise NotImplementedError("waterplane — Sprint 2")
+2
View File
@@ -0,0 +1,2 @@
"""Peso puntual. Stub — Sprint 4."""
raise NotImplementedError("weight_item — Sprint 4")
+1
View File
@@ -0,0 +1 @@
# arshipdesign/engines
+2
View File
@@ -0,0 +1,2 @@
"""Curva BHP RPM. Stub — Sprint 5."""
raise NotImplementedError("engine_curve — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Catálogo motores. Stub — Sprint 5."""
raise NotImplementedError("engine_db — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Selección motor. Stub — Sprint 5."""
raise NotImplementedError("engine_selection — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Consumo combustible. Stub — Sprint 5."""
raise NotImplementedError("fuel_consumption — Sprint 5")
+2
View File
@@ -0,0 +1,2 @@
"""Emisiones NOx IMO. Stub — Sprint 5."""
raise NotImplementedError("nox_emissions — Sprint 5")
+1
View File
@@ -0,0 +1 @@
# arshipdesign/fabrication
+2
View File
@@ -0,0 +1,2 @@
"""BOM lista de materiales. Stub — Sprint 13."""
raise NotImplementedError("bom — Sprint 13")
@@ -0,0 +1,2 @@
"""Secuencia de montaje. Stub — Sprint 13."""
raise NotImplementedError("construction_sequence — Sprint 13")
@@ -0,0 +1 @@
# fabrication/details
@@ -0,0 +1,2 @@
"""Marcas alineación. Stub — Sprint 13."""
raise NotImplementedError("alignment_marks — Sprint 13")
@@ -0,0 +1,2 @@
"""Dog-bones CNC. Stub — Sprint 13."""
raise NotImplementedError("dogbone — Sprint 13")
@@ -0,0 +1,2 @@
"""Dirección fibra plywood. Stub — Sprint 13."""
raise NotImplementedError("grain_direction — Sprint 13")
@@ -0,0 +1,2 @@
"""Vaciados aligeramiento. Stub — Sprint 13."""
raise NotImplementedError("lightening_holes — Sprint 13")
@@ -0,0 +1,2 @@
"""Agujeros achique. Stub — Sprint 13."""
raise NotImplementedError("limber_holes — Sprint 13")
@@ -0,0 +1,2 @@
"""Slots y tabs. Stub — Sprint 13."""
raise NotImplementedError("slot_tab — Sprint 13")
@@ -0,0 +1,2 @@
"""Reporte fabricación. Stub — Sprint 13."""
raise NotImplementedError("fabrication_report — Sprint 13")
@@ -0,0 +1,2 @@
"""G-code CNC plasma/router/laser. Stub — Sprint 13."""
raise NotImplementedError("gcode_generator — Sprint 13")
@@ -0,0 +1 @@
# fabrication/joints
@@ -0,0 +1,2 @@
"""Butt joint. Stub — Sprint 13."""
raise NotImplementedError("butt_joint — Sprint 13")
@@ -0,0 +1,2 @@
"""Unión epóxica. Stub — Sprint 13."""
raise NotImplementedError("epoxy_bond — Sprint 13")
@@ -0,0 +1,2 @@
"""Scarf joint. Stub — Sprint 13."""
raise NotImplementedError("scarf_joint — Sprint 13")
@@ -0,0 +1,2 @@
"""Preparación soldadura. Stub — Sprint 13."""
raise NotImplementedError("weld_prep — Sprint 13")
@@ -0,0 +1,2 @@
"""Estimación de material. Stub — Sprint 13."""
raise NotImplementedError("material_estimator — Sprint 13")
@@ -0,0 +1 @@
# fabrication/materials
@@ -0,0 +1 @@
# fabrication/molds — Moldes FRP
@@ -0,0 +1,2 @@
"""Schedule laminado casco FRP. Stub — Sprint 13B."""
raise NotImplementedError("hull_laminate — Sprint 13B")
@@ -0,0 +1,2 @@
"""Schedule laminado molde. Stub — Sprint 13B."""
raise NotImplementedError("laminate_schedule — Sprint 13B")
@@ -0,0 +1,2 @@
"""Lofting tabla offsets. Stub — Sprint 13B."""
raise NotImplementedError("lofting — Sprint 13B")
@@ -0,0 +1,2 @@
"""Molde hembra. Stub — Sprint 13B."""
raise NotImplementedError("mold_female — Sprint 13B")
@@ -0,0 +1,2 @@
"""Reporte molde FRP. Stub — Sprint 13B."""
raise NotImplementedError("mold_report — Sprint 13B")
@@ -0,0 +1,2 @@
"""Estructura del molde. Stub — Sprint 13B."""
raise NotImplementedError("mold_structure — Sprint 13B")
@@ -0,0 +1,2 @@
"""Línea partición. Stub — Sprint 13B."""
raise NotImplementedError("parting_line — Sprint 13B")
+2
View File
@@ -0,0 +1,2 @@
"""Plug macho FRP. Stub — Sprint 13B."""
raise NotImplementedError("plug — Sprint 13B")
@@ -0,0 +1,2 @@
"""Cálculo materiales FRP. Stub — Sprint 13B."""
raise NotImplementedError("resin_calculator — Sprint 13B")
@@ -0,0 +1,2 @@
"""Cuadernas del molde con offsets. Stub — Sprint 13B."""
raise NotImplementedError("station_molds — Sprint 13B")
+2
View File
@@ -0,0 +1,2 @@
"""Nesting 2D. Stub — Sprint 13."""
raise NotImplementedError("nesting — Sprint 13")
@@ -0,0 +1,2 @@
"""División de piezas. Stub — Sprint 13."""
raise NotImplementedError("part_splitter — Sprint 13")
+2
View File
@@ -0,0 +1,2 @@
"""Lámina de material. Stub — Sprint 13."""
raise NotImplementedError("sheet — Sprint 13")
@@ -0,0 +1 @@
# fabrication/standards
@@ -0,0 +1,2 @@
"""ABYC. Stub — Sprint 13B."""
raise NotImplementedError("abyc — Sprint 13B")
@@ -0,0 +1,2 @@
"""ISO 12215 FRP construcción. Stub — Sprint 13B."""
raise NotImplementedError("iso_12215 fabrication — Sprint 13B")
+1
View File
@@ -0,0 +1 @@
# arshipdesign/geometry
+2
View File
@@ -0,0 +1,2 @@
"""Booleanas trimesh. Stub — Sprint 1."""
raise NotImplementedError("boolean — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Análisis curvatura. Stub — Sprint 1."""
raise NotImplementedError("fairing — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Ajuste NURBS. Stub — Sprint 1."""
raise NotImplementedError("fitting — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Intersecciones. Stub — Sprint 1."""
raise NotImplementedError("intersection — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Malla triangular. Stub — Sprint 1."""
raise NotImplementedError("mesh — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Curva NURBS. Stub — Sprint 1."""
raise NotImplementedError("nurbs_curve — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Superficie NURBS geomdl. Stub — Sprint 1."""
raise NotImplementedError("nurbs_surface — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Corte por planos. Stub — Sprint 1."""
raise NotImplementedError("slicer — Sprint 1")
+1
View File
@@ -0,0 +1 @@
# arshipdesign/hydrostatics
+2
View File
@@ -0,0 +1,2 @@
"""Curvas Bonjean. Stub — Sprint 2."""
raise NotImplementedError("bonjean — Sprint 2")
@@ -0,0 +1,2 @@
"""Coeficientes de forma. Stub — Sprint 2."""
raise NotImplementedError("coefficients — Sprint 2")
@@ -0,0 +1,2 @@
"""Curvas hidrostáticas. Stub — Sprint 2."""
raise NotImplementedError("curves_of_form — Sprint 2")
@@ -0,0 +1,2 @@
"""Equilibrio libre 3DOF. Stub — Sprint 3."""
raise NotImplementedError("free_floating — Sprint 3")
+2
View File
@@ -0,0 +1,2 @@
"""Equilibrio escorado. Stub — Sprint 3."""
raise NotImplementedError("heeled — Sprint 3")
+2
View File
@@ -0,0 +1,2 @@
"""Integradores Simpson. Stub — Sprint 2."""
raise NotImplementedError("integrator — Sprint 2")
+2
View File
@@ -0,0 +1,2 @@
"""Equilibrio asiento. Stub — Sprint 3."""
raise NotImplementedError("trim — Sprint 3")
+2
View File
@@ -0,0 +1,2 @@
"""Hidrostáticos vertical. Stub — Sprint 2."""
raise NotImplementedError("upright — Sprint 2")
+1
View File
@@ -0,0 +1 @@
# arshipdesign/io
+2
View File
@@ -0,0 +1,2 @@
"""Lectura DXF. Stub — Sprint 1."""
raise NotImplementedError("dxf_reader — Sprint 1")
+2
View File
@@ -0,0 +1,2 @@
"""Escritura DXF. Stub — Sprint 10."""
raise NotImplementedError("dxf_writer — Sprint 10")

Some files were not shown because too many files have changed in this diff Show More