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:
@@ -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*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/core
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Apéndices. Stub — Sprint 1."""
|
||||
raise NotImplementedError("appendage — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Baterías. Stub — Sprint 7."""
|
||||
raise NotImplementedError("battery — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Compartimento. Stub — Sprint 4."""
|
||||
raise NotImplementedError("compartment — Sprint 4")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Carga eléctrica. Stub — Sprint 7."""
|
||||
raise NotImplementedError("electrical_load — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Motor. Stub — Sprint 5."""
|
||||
raise NotImplementedError("engine — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Reductora. Stub — Sprint 5."""
|
||||
raise NotImplementedError("gear — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Genset. Stub — Sprint 7."""
|
||||
raise NotImplementedError("generator — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Geometría del casco NURBS. Stub — Sprint 1."""
|
||||
raise NotImplementedError("hull — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Peso en rosca. Stub — Sprint 4."""
|
||||
raise NotImplementedError("lightship — Sprint 4")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Líquidos. Stub — Sprint 4."""
|
||||
raise NotImplementedError("liquid — Sprint 4")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Condiciones de carga. Stub — Sprint 4."""
|
||||
raise NotImplementedError("loadcase — Sprint 4")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Tabla de offsets. Stub — Sprint 1."""
|
||||
raise NotImplementedError("offsets — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Tubería. Stub — Sprint 7."""
|
||||
raise NotImplementedError("pipe — Sprint 7")
|
||||
@@ -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": {},
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Hélice. Stub — Sprint 5."""
|
||||
raise NotImplementedError("propeller — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Bomba. Stub — Sprint 7."""
|
||||
raise NotImplementedError("pump — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Aparejo velero. Stub — Sprint 6."""
|
||||
raise NotImplementedError("rig — Sprint 6")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Velas. Stub — Sprint 6."""
|
||||
raise NotImplementedError("sail — Sprint 6")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Sección transversal. Stub — Sprint 1."""
|
||||
raise NotImplementedError("section — Sprint 1")
|
||||
@@ -0,0 +1,3 @@
|
||||
"""Clase Ship. Stub — Sprint 1."""
|
||||
class Ship:
|
||||
def __init__(self): raise NotImplementedError("Ship — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Superestructura. Stub — Sprint 1."""
|
||||
raise NotImplementedError("superstructure — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Sistema del buque. Stub — Sprint 7."""
|
||||
raise NotImplementedError("system — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Tanque. Stub — Sprint 4."""
|
||||
raise NotImplementedError("tank — Sprint 4")
|
||||
@@ -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": "m³", "imperial": "ft³"},
|
||||
"area": {"si": "m²", "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, "—")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Válvula. Stub — Sprint 7."""
|
||||
raise NotImplementedError("valve — Sprint 7")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Plano de flotación. Stub — Sprint 2."""
|
||||
raise NotImplementedError("waterplane — Sprint 2")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Peso puntual. Stub — Sprint 4."""
|
||||
raise NotImplementedError("weight_item — Sprint 4")
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/engines
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Curva BHP RPM. Stub — Sprint 5."""
|
||||
raise NotImplementedError("engine_curve — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Catálogo motores. Stub — Sprint 5."""
|
||||
raise NotImplementedError("engine_db — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Selección motor. Stub — Sprint 5."""
|
||||
raise NotImplementedError("engine_selection — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Consumo combustible. Stub — Sprint 5."""
|
||||
raise NotImplementedError("fuel_consumption — Sprint 5")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Emisiones NOx IMO. Stub — Sprint 5."""
|
||||
raise NotImplementedError("nox_emissions — Sprint 5")
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/fabrication
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/geometry
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Booleanas trimesh. Stub — Sprint 1."""
|
||||
raise NotImplementedError("boolean — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Análisis curvatura. Stub — Sprint 1."""
|
||||
raise NotImplementedError("fairing — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Ajuste NURBS. Stub — Sprint 1."""
|
||||
raise NotImplementedError("fitting — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Intersecciones. Stub — Sprint 1."""
|
||||
raise NotImplementedError("intersection — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Malla triangular. Stub — Sprint 1."""
|
||||
raise NotImplementedError("mesh — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Curva NURBS. Stub — Sprint 1."""
|
||||
raise NotImplementedError("nurbs_curve — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Superficie NURBS geomdl. Stub — Sprint 1."""
|
||||
raise NotImplementedError("nurbs_surface — Sprint 1")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Corte por planos. Stub — Sprint 1."""
|
||||
raise NotImplementedError("slicer — Sprint 1")
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/hydrostatics
|
||||
@@ -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")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Equilibrio escorado. Stub — Sprint 3."""
|
||||
raise NotImplementedError("heeled — Sprint 3")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Integradores Simpson. Stub — Sprint 2."""
|
||||
raise NotImplementedError("integrator — Sprint 2")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Equilibrio asiento. Stub — Sprint 3."""
|
||||
raise NotImplementedError("trim — Sprint 3")
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Hidrostáticos vertical. Stub — Sprint 2."""
|
||||
raise NotImplementedError("upright — Sprint 2")
|
||||
@@ -0,0 +1 @@
|
||||
# arshipdesign/io
|
||||
@@ -0,0 +1,2 @@
|
||||
"""Lectura DXF. Stub — Sprint 1."""
|
||||
raise NotImplementedError("dxf_reader — Sprint 1")
|
||||
@@ -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
Reference in New Issue
Block a user