Estructura inicial del proyecto
Contrato entre motor y aplicación en motor/api.py, motor simulado con CUBE01, pruebas del contrato, prototipo de la prueba técnica, documentación de trabajo y plantillas de Gitea.
This commit is contained in:
commit
4503df96bd
|
|
@ -0,0 +1,10 @@
|
|||
* text=auto eol=lf
|
||||
|
||||
# Planos y modelos: Git no debe tocar sus finales de línea ni mostrarlos como diff
|
||||
*.dxf binary
|
||||
*.dwg binary
|
||||
*.pdf binary
|
||||
*.step binary
|
||||
*.stp binary
|
||||
*.stl binary
|
||||
*.png binary
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
name: "Error"
|
||||
about: "Algo no funciona como debería"
|
||||
---
|
||||
|
||||
## Qué pasa
|
||||
|
||||
## Qué debería pasar
|
||||
|
||||
## Cómo reproducirlo
|
||||
|
||||
- Plano usado (adjuntar o ruta en `pruebas/catalogo/`):
|
||||
- Pasos:
|
||||
|
||||
## Gravedad
|
||||
|
||||
bloqueante / alta / media / baja
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
name: "Tarea"
|
||||
about: "Trabajo planificado del backlog"
|
||||
---
|
||||
|
||||
## Objetivo
|
||||
|
||||
## Criterio de terminado
|
||||
|
||||
- [ ]
|
||||
|
||||
## Área
|
||||
|
||||
motor / app / pruebas / docs
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
## Qué cambia
|
||||
|
||||
## Cómo se ha probado
|
||||
|
||||
- [ ] `uv run pytest` pasa
|
||||
- [ ] Probado a mano (explicar):
|
||||
|
||||
## Issue
|
||||
|
||||
Closes #
|
||||
|
||||
## ¿Cambia el contrato `motor/api.py`?
|
||||
|
||||
- [ ] No
|
||||
- [ ] Sí, acordado y anotado en `docs/decisiones.md`
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.venv/
|
||||
.pytest_cache/
|
||||
|
||||
# Empaquetado
|
||||
build/
|
||||
dist/
|
||||
*.spec
|
||||
|
||||
# Resultados generados al probar (el catálogo sí se versiona)
|
||||
salidas/
|
||||
*_informe.md
|
||||
*_3d.png
|
||||
|
||||
# Sistema y editores
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.vscode/
|
||||
.idea/
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# Cómo trabajamos
|
||||
|
||||
## Ramas
|
||||
|
||||
- `main` siempre funciona: nadie sube directamente a ella. Todo entra mediante *pull request* revisado por la otra persona.
|
||||
- Una rama por tarea, creada desde `main` actualizada, con el prefijo del área:
|
||||
- `motor/...`: procesado (p. ej. `motor/lectura-dwg`)
|
||||
- `app/...`: interfaz y visor (p. ej. `app/visor-ejes`)
|
||||
- `pruebas/...`, `docs/...`
|
||||
- Ramas cortas: mejor varios PR pequeños que uno enorme. Antes de abrir el PR, actualizar la rama con `main`.
|
||||
|
||||
```bash
|
||||
git switch main
|
||||
git pull
|
||||
git switch -c app/visor-ejes
|
||||
# ... trabajo y commits ...
|
||||
git push -u origin app/visor-ejes
|
||||
```
|
||||
|
||||
## Commits
|
||||
|
||||
En español, en imperativo y diciendo qué cambia: `Añade lectura de capas ocultas`, `Corrige escala en planos sin unidades`. Un commit por cambio con sentido propio.
|
||||
|
||||
## Issues y tablero
|
||||
|
||||
- Cada tarea es un *issue* con etiqueta de área (`motor`, `app`, `pruebas`, `docs`) y asignado a quien la hace.
|
||||
- El tablero del proyecto en Gitea tiene las columnas **Pendiente · En curso · En revisión · Hecho**.
|
||||
- Los errores se registran como *issue* con la plantilla de error: son evidencia para el informe de calidad.
|
||||
|
||||
## Pull requests
|
||||
|
||||
- La descripción dice qué cambia, cómo se ha probado y qué *issue* cierra (`Closes #12`).
|
||||
- `uv run pytest` debe pasar antes de pedir revisión.
|
||||
- La otra persona revisa y aprueba. La revisión cruzada es obligatoria: los dos tenemos que entender todo el código para la defensa.
|
||||
|
||||
## Contrato motor/aplicación
|
||||
|
||||
`motor/api.py` es lo único que comparten las dos partes. Cualquier cambio en él se acuerda antes, se anota en `docs/decisiones.md` y va en un PR propio.
|
||||
|
||||
## Documentación continua
|
||||
|
||||
- **Decisiones** (`docs/decisiones.md`): cada decisión de diseño o de alcance, con motivo.
|
||||
- **Bitácora** (`docs/bitacora.md`): qué ha hecho cada uno cada semana.
|
||||
- **Actas** (`docs/actas/`): una por reunión semanal.
|
||||
- **Uso de IA** (`docs/uso-ia.md`): cada uso relevante de herramientas de IA.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Equipo Plano3D
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# Plano3D
|
||||
|
||||
Aplicación de escritorio que reconstruye automáticamente una pieza mecánica 3D (STEP/STL) a partir de su plano técnico 2D con alzado, planta y perfil (DXF, DWG o PDF vectorial), y verifica el plano: escala, cotas, coherencia entre vistas y ambigüedades.
|
||||
|
||||
Proyecto intermodular de 2.º DAM, IES La Mar.
|
||||
|
||||
## Estructura
|
||||
|
||||
| Carpeta | Contenido | Responsable |
|
||||
| --- | --- | --- |
|
||||
| `motor/` | Procesado: lectura del plano, vistas, reconstrucción, verificación, exportación | Motor |
|
||||
| `motor/api.py` | **Contrato** entre motor y aplicación: lo único que la aplicación importa | Los dos |
|
||||
| `app/` | Aplicación: interfaz Qt, visor 3D, historial, instalador | Aplicación |
|
||||
| `pruebas/` | Pruebas automáticas (`pytest`) y catálogo de piezas con su resultado esperado | Los dos |
|
||||
| `prototipo/` | Prueba técnica inicial (no se modifica; solo de referencia) | Los dos |
|
||||
| `docs/` | Guía técnica, registro de decisiones, bitácora, actas y declaración de uso de IA | Los dos |
|
||||
|
||||
## Puesta en marcha
|
||||
|
||||
Requisitos: [uv](https://docs.astral.sh/uv/) y Git. CadQuery necesita Python 3.12 o 3.13; uv lo descarga solo.
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
## Formas de trabajo
|
||||
|
||||
Ver [CONTRIBUTING.md](CONTRIBUTING.md): ramas, *pull requests*, revisiones e *issues*.
|
||||
|
||||
## Licencia
|
||||
|
||||
MIT. Ver [LICENSE](LICENSE).
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
"""Aplicación de escritorio de Plano3D: interfaz (PySide6), visor 3D (VTK), historial e instalador.
|
||||
|
||||
Solo usa el motor a través de motor.api (procesar, exportar y las clases del contrato).
|
||||
"""
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Acta de reunión AAAA-MM-DD
|
||||
|
||||
- **Asistentes:**
|
||||
- **Modera / toma notas:** (rotatorio)
|
||||
- **Duración:**
|
||||
|
||||
## Revisión de la semana anterior
|
||||
|
||||
| Tarea (issue) | Responsable | Estado |
|
||||
| --- | --- | --- |
|
||||
| | | |
|
||||
|
||||
## Temas tratados
|
||||
|
||||
-
|
||||
|
||||
## Acuerdos
|
||||
|
||||
-
|
||||
|
||||
## Tareas para la próxima semana
|
||||
|
||||
| Tarea | Responsable | Issue |
|
||||
| --- | --- | --- |
|
||||
| | | |
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Bitácora
|
||||
|
||||
Una entrada por semana. Cada persona escribe lo suyo.
|
||||
|
||||
## Semana del AAAA-MM-DD
|
||||
|
||||
### Joel
|
||||
- Hecho:
|
||||
- Problemas:
|
||||
- Próxima semana:
|
||||
- Horas (clase / autónomas):
|
||||
|
||||
### (compañera)
|
||||
- Hecho:
|
||||
- Problemas:
|
||||
- Próxima semana:
|
||||
- Horas (clase / autónomas):
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Registro de decisiones
|
||||
|
||||
Cada decisión de alcance o de diseño, con su motivo. La más reciente, arriba.
|
||||
|
||||
| Fecha | Decisión | Motivo |
|
||||
| --- | --- | --- |
|
||||
| 2026-10-22 | Reparto: Joel, aplicación (interfaz y visor 3D); compañera, motor de procesado. Contrato común en `motor/api.py` | Partes separables que pueden avanzar en paralelo con un motor simulado |
|
||||
| 2026-10-22 | Repositorio en la plataforma Gitea del instituto, con ramas por tarea y PR revisados | Requisito del módulo; trazabilidad de la contribución individual |
|
||||
| 2026-10-14 | Hitos H0 (fin de SA2) y H1 (fin de SA3), con plan B de piezas planas | Gestionar el riesgo técnico de la reconstrucción |
|
||||
| 2026-10-14 | Aplicación de escritorio, no web | Cálculo pesado con OpenCASCADE; privacidad de los planos; sin servidor ni costes |
|
||||
| 2026-10-14 | Alcance por prioridades; roscas como agujero + nota; revolución como importante | Equilibrio entre ambición y las horas del módulo |
|
||||
| 2026-10-14 | Lo no soportado se detecta y se avisa, nunca se reconstruye mal en silencio | Fiabilidad: una pieza errónea sin aviso es peor que ninguna |
|
||||
| 2026-10-14 | Reconstrucción automática desde 3 vistas, con verificación del plano | Es lo que Fusion 360 no hace; la versión "DXF + espesor" no aportaba valor |
|
||||
| 2026-10-14 | Prueba técnica con CUBE01.dxf: reconstrucción correcta y detección de proyección mixta | Comprobar la viabilidad antes de comprometer el alcance |
|
||||
| 2026-10-08 | Piezas mecánicas (no arquitectura); entradas DXF, DWG y PDF | Propuesta inicial del equipo |
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# Guía técnica
|
||||
|
||||
Resumen de las decisiones técnicas vigentes y de las reglas del código. El detalle y la fecha de cada decisión están en [decisiones.md](decisiones.md).
|
||||
|
||||
## Producto
|
||||
|
||||
Aplicación de escritorio que reconstruye piezas mecánicas 3D a partir de planos de tres vistas y verifica el plano. Todo se procesa en el equipo del usuario: no hay servidor ni cuentas.
|
||||
|
||||
## Tecnologías
|
||||
|
||||
| Tecnología | Uso |
|
||||
| --- | --- |
|
||||
| Python 3.12 | Todo el proyecto |
|
||||
| CadQuery (OpenCASCADE) | Sólidos 3D y exportación STEP/STL |
|
||||
| ezdxf | Lectura de DXF |
|
||||
| shapely | Geometría 2D de las vistas |
|
||||
| OR-Tools CP-SAT | Búsqueda de la pieza que reproduce el plano |
|
||||
| PySide6 y VTK | Interfaz y visor 3D |
|
||||
| SQLite | Historial local de conversiones |
|
||||
| PyInstaller | Instalador |
|
||||
| pytest | Pruebas automáticas |
|
||||
|
||||
## Alcance
|
||||
|
||||
- Entradas: DXF (imprescindible), DWG mediante conversor externo, PDF vectorial (importante).
|
||||
- Geometría imprescindible: tres vistas, caras planas, chaflanes, agujeros pasantes y roscas (agujero más nota).
|
||||
- Geometría importante: agujeros ciegos, avellanados y cajeados; piezas de revolución.
|
||||
- Sistemas de proyección europeo (ISO-E) y americano (ISO-A); se avisa si un plano los mezcla.
|
||||
- Lo que no se soporta se detecta y se avisa. Nunca se entrega una pieza que no reproduce el plano sin advertirlo.
|
||||
|
||||
## Reglas del código
|
||||
|
||||
- `motor/api.py` es el contrato entre motor y aplicación. La aplicación solo importa desde ahí, y cualquier cambio se acuerda entre los dos.
|
||||
- `motor/simulado.py` devuelve la pieza CUBE01 mientras el motor real no esté terminado.
|
||||
- `prototipo/reconstruir.py` es la prueba técnica original. Se conserva como referencia y no se modifica.
|
||||
- Cada pieza del catálogo tiene su carpeta en `pruebas/catalogo/` con `plano.dxf`, `esperado.step` e `info.md`.
|
||||
- Ramas, commits y revisiones según [CONTRIBUTING.md](../CONTRIBUTING.md).
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Declaración de uso de herramientas de IA
|
||||
|
||||
Registro de los usos relevantes de IA en el proyecto, para la declaración de la memoria final.
|
||||
|
||||
| Fecha | Herramienta | Quién | Para qué | Cómo se revisó |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 2026-10-08 | Claude (Claude Code) | Joel | Análisis de viabilidad de la idea y primer anteproyecto | Discutido y corregido en equipo |
|
||||
| 2026-10-14 | Claude (Claude Code) | Joel | Prototipo de reconstrucción (`prototipo/reconstruir.py`) a partir de CUBE01.dxf | Resultado abierto y comprobado en Fusion 360 |
|
||||
| 2026-10-14 | Claude (Claude Code) | Joel | Revisión de decisiones técnicas y redacción de la propuesta | Revisado por el equipo antes de entregarla |
|
||||
| 2026-10-22 | Claude (Claude Code) | Joel | Estructura del repositorio, contrato `motor/api.py`, motor simulado y plantillas | Revisado por el equipo |
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
"""Motor de procesado de Plano3D: de plano 2D (DXF, DWG, PDF) a pieza 3D con informe de verificación.
|
||||
|
||||
La aplicación solo debe importar desde motor.api.
|
||||
"""
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
"""Contrato entre el motor de procesado y la aplicación.
|
||||
|
||||
Este módulo es lo único que la aplicación (interfaz y visor) conoce del motor.
|
||||
Si hay que cambiar algo de aquí, se acuerda entre los dos y se anota en docs/decisiones.md,
|
||||
porque rompe el trabajo del otro.
|
||||
|
||||
Uso desde la aplicación:
|
||||
|
||||
from motor.api import procesar, exportar, Opciones
|
||||
|
||||
resultado = procesar("pieza.dxf", Opciones())
|
||||
if resultado.solido is not None:
|
||||
exportar(resultado.solido, "pieza.step")
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
|
||||
import cadquery as cq
|
||||
|
||||
|
||||
class Estado(Enum):
|
||||
OK = "ok" # pieza reconstruida y sin problemas
|
||||
CON_AVISOS = "con_avisos" # pieza reconstruida, pero hay cosas que revisar
|
||||
IMPOSIBLE = "imposible" # no se ha podido reconstruir; ver los avisos bloqueantes
|
||||
|
||||
|
||||
class Gravedad(Enum):
|
||||
INFO = "info" # dato informativo (p. ej. escala detectada)
|
||||
AVISO = "aviso" # algo no cuadra, pero hay pieza
|
||||
BLOQUEANTE = "bloqueante" # impide reconstruir; puede requerir un dato del usuario
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Segmento2D:
|
||||
"""Línea de una vista, en coordenadas reales de la pieza (mm)."""
|
||||
x1: float
|
||||
y1: float
|
||||
x2: float
|
||||
y2: float
|
||||
oculta: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class Vista:
|
||||
"""Geometría 2D de una vista, para mostrarla junto al modelo 3D."""
|
||||
nombre: str # "alzado", "planta" o "perfil"
|
||||
segmentos: list[Segmento2D] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Referencia:
|
||||
"""A qué parte del plano o de la pieza se refiere un aviso, para que el visor la resalte."""
|
||||
vista: str | None = None # vista afectada, si la hay
|
||||
segmento: Segmento2D | None = None # línea del plano afectada
|
||||
punto: tuple[float, float, float] | None = None # punto de la pieza 3D (mm)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Aviso:
|
||||
codigo: str # identificador estable, p. ej. "SIN_COTAS", "PROYECCION_MIXTA"
|
||||
mensaje: str # texto para el usuario, en español
|
||||
gravedad: Gravedad
|
||||
referencia: Referencia | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Opciones:
|
||||
"""Datos que el usuario puede aportar cuando el motor los pide."""
|
||||
escala: float | None = None # factor real/dibujo si el plano no trae escala (1:2 -> 2.0)
|
||||
tolerancia: float = 1.0 # en unidades de dibujo
|
||||
alternativa: int | None = None # si el plano es ambiguo, cuál de las piezas posibles se elige
|
||||
|
||||
|
||||
@dataclass
|
||||
class Resultado:
|
||||
estado: Estado
|
||||
solido: cq.Shape | None # pieza 3D en mm; None si el estado es IMPOSIBLE
|
||||
avisos: list[Aviso] = field(default_factory=list)
|
||||
vistas: dict[str, Vista] = field(default_factory=dict)
|
||||
escala: float = 1.0 # factor aplicado (real/dibujo)
|
||||
alternativas: int = 1 # número de piezas compatibles con el plano
|
||||
|
||||
|
||||
def procesar(ruta_plano: str | Path, opciones: Opciones | None = None) -> Resultado:
|
||||
"""Punto de entrada único del motor.
|
||||
|
||||
Mientras el motor real no esté listo devuelve un resultado simulado (motor/simulado.py),
|
||||
para que la aplicación se pueda desarrollar en paralelo.
|
||||
"""
|
||||
from motor import simulado
|
||||
return simulado.procesar(Path(ruta_plano), opciones or Opciones())
|
||||
|
||||
|
||||
def exportar(solido: cq.Shape, ruta_salida: str | Path) -> None:
|
||||
"""Guarda la pieza en STEP o STL según la extensión del archivo."""
|
||||
ruta_salida = Path(ruta_salida)
|
||||
if ruta_salida.suffix.lower() not in (".step", ".stp", ".stl"):
|
||||
raise ValueError(f"Formato no admitido: {ruta_salida.suffix} (usa .step, .stp o .stl)")
|
||||
cq.exporters.export(cq.Workplane().add(solido), str(ruta_salida))
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
"""Motor simulado: devuelve siempre la pieza de CUBE01 con sus avisos reales.
|
||||
|
||||
Sirve para desarrollar la aplicación (interfaz y visor) antes de que exista el motor real.
|
||||
Las líneas del plano sí se leen del archivo recibido, para poder dibujarlas en 2D.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import cadquery as cq
|
||||
import ezdxf
|
||||
|
||||
from motor.api import Aviso, Estado, Gravedad, Opciones, Referencia, Resultado, Segmento2D, Vista
|
||||
|
||||
RAIZ = Path(__file__).resolve().parent.parent
|
||||
PIEZA_CUBE01 = RAIZ / "pruebas" / "catalogo" / "CUBE01" / "esperado.step"
|
||||
|
||||
|
||||
def _leer_lineas(ruta: Path) -> list[Segmento2D]:
|
||||
if ruta.suffix.lower() != ".dxf":
|
||||
return []
|
||||
doc = ezdxf.readfile(ruta)
|
||||
segmentos = []
|
||||
for e in doc.modelspace().query("LINE"):
|
||||
tipo = e.dxf.get("linetype", "ByLayer")
|
||||
if tipo.lower() == "bylayer":
|
||||
tipo = doc.layers.get(e.dxf.layer).dxf.linetype
|
||||
oculta = "HIDDEN" in tipo.upper() or "HIDDEN" in e.dxf.layer.upper()
|
||||
segmentos.append(Segmento2D(e.dxf.start.x, e.dxf.start.y, e.dxf.end.x, e.dxf.end.y, oculta))
|
||||
return segmentos
|
||||
|
||||
|
||||
def procesar(ruta: Path, opciones: Opciones) -> Resultado:
|
||||
solido = cq.importers.importStep(str(PIEZA_CUBE01)).val()
|
||||
avisos = [
|
||||
Aviso("ESCALA_DETECTADA", "Escala 1:2 leída del cajetín.", Gravedad.INFO),
|
||||
Aviso("SIN_COTAS", "El plano no tiene cotas: las medidas se deducen de la geometría y la escala.",
|
||||
Gravedad.AVISO),
|
||||
Aviso("PROYECCION_MIXTA",
|
||||
"Sistemas de proyección mezclados: la planta sigue el europeo (ISO-E) y el perfil el americano (ISO-A).",
|
||||
Gravedad.AVISO, Referencia(vista="perfil")),
|
||||
]
|
||||
# Simulado: todas las líneas van en una sola "vista"; el motor real las separará en alzado, planta y perfil.
|
||||
vistas = {"plano": Vista("plano", _leer_lineas(ruta))}
|
||||
return Resultado(Estado.CON_AVISOS, solido, avisos, vistas, escala=opciones.escala or 2.0)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Prototipo (prueba técnica)
|
||||
|
||||
`reconstruir.py` es la prueba técnica que demostró la viabilidad del proyecto con CUBE01.dxf: reconstrucción por descomposición en celdas, búsqueda por fuerza bruta y detección del sistema de proyección.
|
||||
|
||||
Se conserva como referencia y no se modifica. El motor definitivo se desarrolla en `motor/`.
|
||||
|
||||
```bash
|
||||
uv run python prototipo/reconstruir.py pruebas/catalogo/CUBE01/plano.dxf
|
||||
```
|
||||
|
||||
Limitaciones conocidas: solo líneas rectas (sin arcos ni círculos) y búsqueda exhaustiva que no escala a más de unas 18 celdas.
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
"""Prueba técnica: reconstrucción automática de una pieza 3D a partir de un plano DXF
|
||||
con tres vistas (alzado, planta y perfil).
|
||||
|
||||
Método (descomposición en celdas):
|
||||
1. Leer las líneas visibles y ocultas; ignorar marco, cajetín, textos y cotas.
|
||||
2. Buscar la escala en los textos del plano ("ESCALA 1:2").
|
||||
3. Agrupar las líneas en vistas e identificar alzado, planta y perfil por su alineación.
|
||||
4. Para cada convención de proyección posible (europea/americana en planta y perfil):
|
||||
a. dividir el volumen en celdas con todos los planos que sugieren las vistas
|
||||
(incluidos los planos inclinados de las líneas diagonales);
|
||||
b. descartar celdas que se salen de la silueta de alguna vista;
|
||||
c. probar combinaciones de celdas cuya silueta coincide con las tres vistas;
|
||||
d. proyectar las aristas del sólido (visibles y ocultas) y compararlas con el plano.
|
||||
5. Quedarse con el sólido que reproduce el plano y exportarlo a STEP y STL.
|
||||
|
||||
Uso: python reconstruir.py CUBE01.dxf
|
||||
"""
|
||||
import itertools
|
||||
import math
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import cadquery as cq
|
||||
import ezdxf
|
||||
import numpy as np
|
||||
from OCP.BRepClass3d import BRepClass3d_SolidClassifier
|
||||
from OCP.BRepIntCurveSurface import BRepIntCurveSurface_Inter
|
||||
from OCP.gp import gp_Dir, gp_Lin, gp_Pnt
|
||||
from OCP.TopAbs import TopAbs_IN
|
||||
from shapely.geometry import LineString, MultiPoint
|
||||
from shapely.ops import polygonize, unary_union
|
||||
|
||||
TOL = 1.0 # tolerancia en unidades de dibujo (las vistas no están perfectamente alineadas)
|
||||
BIG = 1e4
|
||||
MAX_CELDAS = 18 # límite de la búsqueda exhaustiva (2^18 combinaciones)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 1. lectura
|
||||
def leer_dxf(ruta):
|
||||
doc = ezdxf.readfile(ruta)
|
||||
msp = doc.modelspace()
|
||||
avisos = []
|
||||
|
||||
def es_oculta(e):
|
||||
lt = e.dxf.get("linetype", "ByLayer")
|
||||
if lt.lower() in ("bylayer", "byblock"):
|
||||
lt = doc.layers.get(e.dxf.layer).dxf.linetype
|
||||
nombre = (lt + " " + e.dxf.layer).upper()
|
||||
return any(k in nombre for k in ("HIDDEN", "DASH", "OCULT", "ISO02"))
|
||||
|
||||
segs = []
|
||||
for e in msp:
|
||||
if e.dxftype() == "LINE":
|
||||
partes = [(e.dxf.start, e.dxf.end)]
|
||||
elif e.dxftype() == "LWPOLYLINE":
|
||||
pts = [p[:2] for p in e.get_points()]
|
||||
if e.closed:
|
||||
pts.append(pts[0])
|
||||
partes = list(zip(pts, pts[1:]))
|
||||
else:
|
||||
continue # marco y cajetín (INSERT), textos, cotas, etc.
|
||||
for a, b in partes:
|
||||
segs.append((float(a[0]), float(a[1]), float(b[0]), float(b[1]), es_oculta(e)))
|
||||
|
||||
textos = [t.dxf.text for t in msp.query("TEXT")] + [t.text for t in msp.query("MTEXT")]
|
||||
for ins in msp.query("INSERT"):
|
||||
textos += [a.dxf.text for a in ins.attribs]
|
||||
escala = None
|
||||
for t in textos:
|
||||
m = re.search(r"(?:ESCALA|SCALE)\s*:?\s*(\d+(?:[.,]\d+)?)\s*[:/]\s*(\d+(?:[.,]\d+)?)", t, re.I)
|
||||
if m:
|
||||
a, b = (float(x.replace(",", ".")) for x in m.groups())
|
||||
escala = (a, b)
|
||||
break
|
||||
if escala is None:
|
||||
avisos.append("No se ha encontrado la escala en el plano: se supone 1:1.")
|
||||
escala = (1.0, 1.0)
|
||||
|
||||
n_cotas = len(msp.query("DIMENSION"))
|
||||
if n_cotas == 0:
|
||||
avisos.append("El plano no tiene cotas: las medidas se deducen de la geometría y la escala.")
|
||||
unidades = {0: "sin unidades", 1: "pulgadas", 4: "mm", 5: "cm", 6: "m"}.get(doc.header.get("$INSUNITS", 0), "otras")
|
||||
return segs, escala, n_cotas, unidades, avisos
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 2. vistas
|
||||
def agrupar_vistas(segs):
|
||||
n = len(segs)
|
||||
padre = list(range(n))
|
||||
|
||||
def raiz(i):
|
||||
while padre[i] != i:
|
||||
padre[i] = padre[padre[i]]
|
||||
i = padre[i]
|
||||
return i
|
||||
|
||||
cajas = [(min(s[0], s[2]) - TOL, min(s[1], s[3]) - TOL, max(s[0], s[2]) + TOL, max(s[1], s[3]) + TOL) for s in segs]
|
||||
for i in range(n):
|
||||
for j in range(i + 1, n):
|
||||
a, b = cajas[i], cajas[j]
|
||||
if a[0] <= b[2] and b[0] <= a[2] and a[1] <= b[3] and b[1] <= a[3]:
|
||||
padre[raiz(i)] = raiz(j)
|
||||
grupos = {}
|
||||
for i in range(n):
|
||||
grupos.setdefault(raiz(i), []).append(segs[i])
|
||||
vistas = []
|
||||
for g in grupos.values():
|
||||
xs = [c for s in g for c in (s[0], s[2])]
|
||||
ys = [c for s in g for c in (s[1], s[3])]
|
||||
vistas.append({"segs": g, "u0": min(xs), "u1": max(xs), "v0": min(ys), "v1": max(ys)})
|
||||
return vistas
|
||||
|
||||
|
||||
def identificar_vistas(vistas):
|
||||
def mismo_ancho(a, b):
|
||||
return abs(a["u0"] - b["u0"]) < 2 * TOL and abs(a["u1"] - b["u1"]) < 2 * TOL
|
||||
|
||||
def mismo_alto(a, b):
|
||||
return abs(a["v0"] - b["v0"]) < 2 * TOL and abs(a["v1"] - b["v1"]) < 2 * TOL
|
||||
|
||||
for alz in vistas:
|
||||
plantas = [v for v in vistas if v is not alz and mismo_ancho(alz, v)]
|
||||
perfiles = [v for v in vistas if v is not alz and mismo_alto(alz, v)]
|
||||
if plantas and perfiles:
|
||||
pl, pf = plantas[0], perfiles[0]
|
||||
return alz, pl, pf, ("debajo" if pl["v1"] < alz["v0"] else "encima"), ("derecha" if pf["u0"] > alz["u1"] else "izquierda")
|
||||
raise SystemExit("No se han podido identificar alzado, planta y perfil alineados.")
|
||||
|
||||
|
||||
def a_coordenadas(alz, pl, pf, pos_pl, pos_pf, conv_pl, conv_pf):
|
||||
"""Pasa cada vista a coordenadas de la pieza: alzado (X,Z), planta (X,Y), perfil (Y,Z).
|
||||
Y = 0 es la cara delantera. Devuelve también desde dónde mira el observador de cada vista."""
|
||||
D = pl["v1"] - pl["v0"]
|
||||
|
||||
def map_pl(u, v):
|
||||
d = (pl["v1"] - v) if pos_pl == "debajo" else (v - pl["v0"]) # distancia al borde junto al alzado
|
||||
return (u - pl["u0"], D - d if conv_pl == "E" else d)
|
||||
|
||||
def map_pf(u, v):
|
||||
d = (u - pf["u0"]) if pos_pf == "derecha" else (pf["u1"] - u)
|
||||
return (D - d if conv_pf == "E" else d, v - pf["v0"])
|
||||
|
||||
def map_alz(u, v):
|
||||
return (u - alz["u0"], v - alz["v0"])
|
||||
|
||||
out = {}
|
||||
for nombre, vista, f in (("alzado", alz, map_alz), ("planta", pl, map_pl), ("perfil", pf, map_pf)):
|
||||
out[nombre] = [(*f(s[0], s[1]), *f(s[2], s[3]), s[4]) for s in vista["segs"]]
|
||||
|
||||
# Observador: en el sistema europeo la vista de debajo es la superior; en el americano, al revés.
|
||||
desde_arriba = (pos_pl == "debajo") == (conv_pl == "E")
|
||||
desde_izquierda = (pos_pf == "derecha") == (conv_pf == "E")
|
||||
observador = {
|
||||
"alzado": (0, -1, 0),
|
||||
"planta": (0, 0, 1) if desde_arriba else (0, 0, -1),
|
||||
"perfil": (-1, 0, 0) if desde_izquierda else (1, 0, 0),
|
||||
}
|
||||
return out, observador, D
|
||||
|
||||
|
||||
PROY = {"alzado": (0, 2), "planta": (0, 1), "perfil": (1, 2)} # ejes 3D que se ven en cada vista
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 3. celdas
|
||||
def agrupar_valores(vals):
|
||||
vals = sorted(vals)
|
||||
out = []
|
||||
for v in vals:
|
||||
if not out or v - out[-1] > TOL:
|
||||
out.append(v)
|
||||
return out
|
||||
|
||||
|
||||
def region(segs):
|
||||
lineas = [LineString([(s[0], s[1]), (s[2], s[3])]) for s in segs if not s[4]]
|
||||
return unary_union(list(polygonize(unary_union(lineas))))
|
||||
|
||||
|
||||
def semiespacio(p, n):
|
||||
n = cq.Vector(*n).normalized()
|
||||
aux = cq.Vector(1, 0, 0) if abs(n.x) < 0.9 else cq.Vector(0, 1, 0)
|
||||
plano = cq.Plane(origin=cq.Vector(*p), xDir=aux.cross(n).normalized(), normal=n)
|
||||
return cq.Workplane(plano).rect(BIG, BIG).extrude(BIG).val()
|
||||
|
||||
|
||||
def crear_celdas(coords):
|
||||
rejilla = {0: [], 1: [], 2: []}
|
||||
inclinados = []
|
||||
for nombre, segs in coords.items():
|
||||
a, b = PROY[nombre]
|
||||
for x1, y1, x2, y2, _ in segs:
|
||||
rejilla[a] += [x1, x2]
|
||||
rejilla[b] += [y1, y2]
|
||||
if abs(x1 - x2) > TOL and abs(y1 - y2) > TOL: # línea diagonal -> plano inclinado
|
||||
n = [0.0, 0.0, 0.0]
|
||||
n[a], n[b] = -(y2 - y1), (x2 - x1)
|
||||
p = [0.0, 0.0, 0.0]
|
||||
p[a], p[b] = x1, y1
|
||||
inclinados.append((tuple(p), tuple(n)))
|
||||
ejes = [agrupar_valores(rejilla[i]) for i in range(3)]
|
||||
|
||||
celdas = []
|
||||
for i, j, k in itertools.product(range(len(ejes[0]) - 1), range(len(ejes[1]) - 1), range(len(ejes[2]) - 1)):
|
||||
x0, x1 = ejes[0][i], ejes[0][i + 1]
|
||||
y0, y1 = ejes[1][j], ejes[1][j + 1]
|
||||
z0, z1 = ejes[2][k], ejes[2][k + 1]
|
||||
trozos = [cq.Solid.makeBox(x1 - x0, y1 - y0, z1 - z0, pnt=cq.Vector(x0, y0, z0))]
|
||||
esquinas = np.array(list(itertools.product((x0, x1), (y0, y1), (z0, z1))))
|
||||
for p, n in inclinados:
|
||||
dist = (esquinas - np.array(p)) @ np.array(n)
|
||||
if dist.min() < -1e-6 and dist.max() > 1e-6: # el plano atraviesa la caja
|
||||
hs = semiespacio(p, n)
|
||||
nuevos = []
|
||||
for t in trozos:
|
||||
for parte in (t.intersect(hs), t.cut(hs)):
|
||||
nuevos += [s for s in parte.Solids() if s.Volume() > 1e-6]
|
||||
trozos = nuevos
|
||||
celdas += trozos
|
||||
return celdas, ejes, inclinados
|
||||
|
||||
|
||||
def proyeccion(solido, nombre):
|
||||
a, b = PROY[nombre]
|
||||
pts = [(v.toTuple()[a], v.toTuple()[b]) for v in solido.Vertices()]
|
||||
return MultiPoint(pts).convex_hull
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 4. comparación
|
||||
def oculto(forma, p, d):
|
||||
inter = BRepIntCurveSurface_Inter()
|
||||
inter.Init(forma.wrapped, gp_Lin(gp_Pnt(*p), gp_Dir(*d)), 1e-7)
|
||||
ws = []
|
||||
while inter.More():
|
||||
if inter.W() > 1e-5:
|
||||
ws.append(inter.W())
|
||||
inter.Next()
|
||||
params = [0.0] + sorted(set(round(w, 6) for w in ws))
|
||||
for t0, t1 in zip(params, params[1:]):
|
||||
q = np.array(p) + np.array(d) * (t0 + t1) / 2
|
||||
if BRepClass3d_SolidClassifier(forma.wrapped, gp_Pnt(*q), 1e-6).State() == TopAbs_IN:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def aristas_proyectadas(forma, nombre, obs):
|
||||
a, b = PROY[nombre]
|
||||
vis, ocu = [], []
|
||||
for e in forma.Edges():
|
||||
p0, p1 = np.array(e.startPoint().toTuple()), np.array(e.endPoint().toTuple())
|
||||
if math.dist((p0[a], p0[b]), (p1[a], p1[b])) < TOL / 2:
|
||||
continue # arista perpendicular a la vista
|
||||
n = 5
|
||||
for i in range(n):
|
||||
q0, q1 = p0 + (p1 - p0) * i / n, p0 + (p1 - p0) * (i + 1) / n
|
||||
seg = (q0[a], q0[b], q1[a], q1[b])
|
||||
(ocu if oculto(forma, (q0 + q1) / 2, obs) else vis).append(seg)
|
||||
return vis, ocu
|
||||
|
||||
|
||||
def muestras(segs, paso=TOL / 2):
|
||||
pts = []
|
||||
for x1, y1, x2, y2 in segs:
|
||||
n = max(1, int(math.dist((x1, y1), (x2, y2)) / paso))
|
||||
for i in range(n):
|
||||
t = (i + 0.5) / n
|
||||
pts.append((x1 + (x2 - x1) * t, y1 + (y2 - y1) * t))
|
||||
return np.array(pts).reshape(-1, 2)
|
||||
|
||||
|
||||
def distancia(pts, segs):
|
||||
if len(pts) == 0:
|
||||
return np.zeros(0)
|
||||
if not segs:
|
||||
return np.full(len(pts), np.inf)
|
||||
s = np.array(segs, dtype=float)
|
||||
a, b = s[:, :2], s[:, 2:]
|
||||
ab = b - a
|
||||
t = np.clip(((pts[:, None, :] - a) * ab).sum(-1) / np.maximum((ab * ab).sum(-1), 1e-12), 0, 1)
|
||||
proj = a + t[..., None] * ab
|
||||
return np.linalg.norm(pts[:, None, :] - proj, axis=-1).min(axis=1)
|
||||
|
||||
|
||||
def errores_vista(forma, nombre, obs, dibujo):
|
||||
cvis, cocu = aristas_proyectadas(forma, nombre, obs)
|
||||
dvis = [s[:4] for s in dibujo if not s[4]]
|
||||
docu = [s[:4] for s in dibujo if s[4]]
|
||||
err = 0
|
||||
err += int((distancia(muestras(cvis), dvis) > TOL).sum()) # aristas que el plano no tiene
|
||||
m = muestras(cocu)
|
||||
m = m[distancia(m, cvis) > TOL] # lo visible tapa lo oculto
|
||||
err += int((distancia(m, docu) > TOL).sum())
|
||||
err += int((distancia(muestras(dvis), cvis) > TOL).sum()) # líneas del plano que faltan
|
||||
err += int((distancia(muestras(docu), cocu) > TOL).sum())
|
||||
return err
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 5. búsqueda
|
||||
def reconstruir(coords, observador):
|
||||
regiones = {n: region(s) for n, s in coords.items()}
|
||||
celdas, ejes, inclinados = crear_celdas(coords)
|
||||
proy = [{n: proyeccion(c, n) for n in coords} for c in celdas]
|
||||
|
||||
# Una celda que se sale de la silueta de alguna vista no puede formar parte de la pieza.
|
||||
validas = [i for i in range(len(celdas))
|
||||
if all(proy[i][n].difference(regiones[n].buffer(TOL / 2)).area < 1e-3 for n in coords)]
|
||||
if len(validas) > MAX_CELDAS:
|
||||
raise SystemExit(f"Demasiadas celdas ({len(validas)}) para la búsqueda exhaustiva de la prueba.")
|
||||
|
||||
candidatos = []
|
||||
for r in range(1, len(validas) + 1):
|
||||
for combo in itertools.combinations(validas, r):
|
||||
ok = all(unary_union([proy[i][n] for i in combo]).symmetric_difference(regiones[n]).area
|
||||
< 0.005 * regiones[n].area for n in coords)
|
||||
if ok:
|
||||
candidatos.append(combo)
|
||||
|
||||
resultados = []
|
||||
for combo in candidatos:
|
||||
forma = celdas[combo[0]]
|
||||
if len(combo) > 1:
|
||||
forma = forma.fuse(*[celdas[i] for i in combo[1:]])
|
||||
forma = forma.clean()
|
||||
if len(forma.Solids()) != 1:
|
||||
continue # piezas sueltas: no es una pieza única
|
||||
forma = forma.Solids()[0]
|
||||
err = sum(errores_vista(forma, n, observador[n], coords[n]) for n in coords)
|
||||
resultados.append((err, combo, forma))
|
||||
if err == 0 and len([r for r in resultados if r[0] == 0]) > 3:
|
||||
break
|
||||
resultados.sort(key=lambda r: r[0])
|
||||
return resultados, len(celdas), len(validas), len(candidatos), ejes, inclinados
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- salida
|
||||
def vista_previa(forma, ruta_png):
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
||||
|
||||
verts, tris = forma.tessellate(0.1)
|
||||
v = np.array([p.toTuple() for p in verts])
|
||||
caras = v[np.array(tris)]
|
||||
normales = np.cross(caras[:, 1] - caras[:, 0], caras[:, 2] - caras[:, 0])
|
||||
normales /= np.linalg.norm(normales, axis=1, keepdims=True) + 1e-12
|
||||
luz = np.clip(normales @ np.array([0.4, -0.6, 0.7]), 0.15, 1)
|
||||
fig = plt.figure(figsize=(6, 6))
|
||||
ax = fig.add_subplot(projection="3d")
|
||||
ax.add_collection3d(Poly3DCollection(caras, facecolors=plt.cm.Blues(0.35 + 0.55 * luz), edgecolor="none"))
|
||||
for e in forma.Edges():
|
||||
a, b = e.startPoint().toTuple(), e.endPoint().toTuple()
|
||||
ax.plot(*zip(a, b), color="k", lw=0.8)
|
||||
lo, hi = v.min(0), v.max(0)
|
||||
ax.set_xlim(lo[0], hi[0]); ax.set_ylim(lo[1], hi[1]); ax.set_zlim(lo[2], hi[2])
|
||||
ax.set_box_aspect(hi - lo)
|
||||
ax.set_xlabel("X (mm)"); ax.set_ylabel("Y (mm, 0 = frente)"); ax.set_zlabel("Z (mm)")
|
||||
ax.view_init(elev=25, azim=-60)
|
||||
plt.tight_layout()
|
||||
plt.savefig(ruta_png, dpi=110)
|
||||
|
||||
|
||||
def main(ruta):
|
||||
ruta = Path(ruta)
|
||||
segs, (ea, eb), n_cotas, unidades, avisos = leer_dxf(ruta)
|
||||
factor = eb / ea
|
||||
vistas = agrupar_vistas(segs)
|
||||
alz, pl, pf, pos_pl, pos_pf = identificar_vistas(vistas)
|
||||
|
||||
inf = [f"# Informe de reconstrucción: {ruta.name}", "",
|
||||
f"- Líneas leídas: {len(segs)} ({sum(s[4] for s in segs)} ocultas); unidades del archivo: {unidades}",
|
||||
f"- Escala: {ea:g}:{eb:g} -> factor x{factor:g}; cotas en el plano: {n_cotas}",
|
||||
f"- Vistas encontradas: {len(vistas)}; planta {pos_pl} del alzado, perfil a la {pos_pf}",
|
||||
f"- Desalineación planta/alzado: {abs(pl['u0'] - alz['u0']):.2f}; perfil/alzado: {abs(pf['v0'] - alz['v0']):.2f} (unidades de dibujo)"]
|
||||
D_pf = pf["u1"] - pf["u0"]
|
||||
D_pl = pl["v1"] - pl["v0"]
|
||||
if abs(D_pf - D_pl) > 2 * TOL:
|
||||
avisos.append(f"La profundidad no coincide entre planta ({D_pl:.1f}) y perfil ({D_pf:.1f}).")
|
||||
|
||||
nombres = {"E": "europeo (ISO-E)", "A": "americano (ISO-A)"}
|
||||
mejor = None
|
||||
inf += ["", "## Hipótesis de proyección", "", "| Planta | Perfil | Celdas (válidas) | Candidatos por silueta | Error mínimo |", "|---|---|---|---|---|"]
|
||||
for conv_pl, conv_pf in itertools.product("EA", repeat=2):
|
||||
coords, obs, D = a_coordenadas(alz, pl, pf, pos_pl, pos_pf, conv_pl, conv_pf)
|
||||
res, n_c, n_v, n_cand, ejes, incl = reconstruir(coords, obs)
|
||||
e_min = res[0][0] if res else "-"
|
||||
inf.append(f"| {nombres[conv_pl]} | {nombres[conv_pf]} | {n_c} ({n_v}) | {n_cand} | {e_min} |")
|
||||
print(f"planta {conv_pl} / perfil {conv_pf}: celdas {n_c}, validas {n_v}, candidatos {n_cand}, error {e_min}")
|
||||
if res and (mejor is None or res[0][0] < mejor[0][0][0]):
|
||||
mejor = (res, conv_pl, conv_pf)
|
||||
|
||||
if mejor is None:
|
||||
raise SystemExit("No se ha encontrado ninguna pieza compatible con las vistas.")
|
||||
res, conv_pl, conv_pf = mejor
|
||||
err, combo, forma = res[0]
|
||||
exactas = [r for r in res if r[0] == 0]
|
||||
if conv_pl != conv_pf:
|
||||
avisos.append(f"Sistemas de proyección mezclados: la planta sigue el sistema {nombres[conv_pl]} "
|
||||
f"y el perfil el {nombres[conv_pf]}.")
|
||||
if err > 0:
|
||||
avisos.append(f"Ninguna pieza reproduce exactamente el plano (error mínimo {err}): revisar manualmente.")
|
||||
if len(exactas) > 1:
|
||||
avisos.append(f"Hay {len(exactas)} piezas distintas que encajan con las vistas: el plano es ambiguo.")
|
||||
|
||||
forma = forma.scale(factor)
|
||||
bb = forma.BoundingBox()
|
||||
inf += ["", "## Resultado", "",
|
||||
f"- Convención elegida: planta {nombres[conv_pl]}, perfil {nombres[conv_pf]}",
|
||||
f"- Celdas usadas: {len(combo)}; error de reproducción del plano: {err}",
|
||||
f"- Tamaño real: {bb.xlen:.1f} x {bb.ylen:.1f} x {bb.zlen:.1f} mm; volumen {forma.Volume():.0f} mm³",
|
||||
f"- Caras: {len(forma.Faces())}, aristas: {len(forma.Edges())}",
|
||||
"", "## Avisos", ""] + [f"- {a}" for a in avisos]
|
||||
|
||||
base = ruta.with_suffix("")
|
||||
cq.exporters.export(cq.Workplane().add(forma), str(base) + ".step")
|
||||
cq.exporters.export(cq.Workplane().add(forma), str(base) + ".stl")
|
||||
vista_previa(forma, str(base) + "_3d.png")
|
||||
Path(str(base) + "_informe.md").write_text("\n".join(inf) + "\n", encoding="utf-8")
|
||||
print("\n".join(inf))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1] if len(sys.argv) > 1 else "CUBE01.dxf")
|
||||
|
|
@ -0,0 +1,721 @@
|
|||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('Open CASCADE Model'),'2;1');
|
||||
FILE_NAME('Open CASCADE Shape Model','2026-10-08T19:20:47',('Author'),(
|
||||
'Open CASCADE'),'Open CASCADE STEP processor 7.9','Open CASCADE 7.9'
|
||||
,'Unknown');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
#1 = APPLICATION_PROTOCOL_DEFINITION('international standard',
|
||||
'automotive_design',2000,#2);
|
||||
#2 = APPLICATION_CONTEXT(
|
||||
'core data for automotive mechanical design processes');
|
||||
#3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10);
|
||||
#4 = PRODUCT_DEFINITION_SHAPE('','',#5);
|
||||
#5 = PRODUCT_DEFINITION('design','',#6,#9);
|
||||
#6 = PRODUCT_DEFINITION_FORMATION('','',#7);
|
||||
#7 = PRODUCT('Open CASCADE STEP translator 7.9 1',
|
||||
'Open CASCADE STEP translator 7.9 1','',(#8));
|
||||
#8 = PRODUCT_CONTEXT('',#2,'mechanical');
|
||||
#9 = PRODUCT_DEFINITION_CONTEXT('part definition',#2,'design');
|
||||
#10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#609);
|
||||
#11 = AXIS2_PLACEMENT_3D('',#12,#13,#14);
|
||||
#12 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#13 = DIRECTION('',(0.,0.,1.));
|
||||
#14 = DIRECTION('',(1.,0.,-0.));
|
||||
#15 = MANIFOLD_SOLID_BREP('',#16);
|
||||
#16 = CLOSED_SHELL('',(#17,#193,#269,#322,#398,#492,#519,#547,#574,#601)
|
||||
);
|
||||
#17 = ADVANCED_FACE('',(#18),#32,.F.);
|
||||
#18 = FACE_BOUND('',#19,.F.);
|
||||
#19 = EDGE_LOOP('',(#20,#55,#83,#111,#139,#167));
|
||||
#20 = ORIENTED_EDGE('',*,*,#21,.F.);
|
||||
#21 = EDGE_CURVE('',#22,#24,#26,.T.);
|
||||
#22 = VERTEX_POINT('',#23);
|
||||
#23 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#24 = VERTEX_POINT('',#25);
|
||||
#25 = CARTESIAN_POINT('',(0.,0.,100.));
|
||||
#26 = SURFACE_CURVE('',#27,(#31,#43),.PCURVE_S1.);
|
||||
#27 = LINE('',#28,#29);
|
||||
#28 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#29 = VECTOR('',#30,1.);
|
||||
#30 = DIRECTION('',(0.,0.,1.));
|
||||
#31 = PCURVE('',#32,#37);
|
||||
#32 = PLANE('',#33);
|
||||
#33 = AXIS2_PLACEMENT_3D('',#34,#35,#36);
|
||||
#34 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#35 = DIRECTION('',(1.,0.,-0.));
|
||||
#36 = DIRECTION('',(0.,0.,1.));
|
||||
#37 = DEFINITIONAL_REPRESENTATION('',(#38),#42);
|
||||
#38 = LINE('',#39,#40);
|
||||
#39 = CARTESIAN_POINT('',(0.,0.));
|
||||
#40 = VECTOR('',#41,1.);
|
||||
#41 = DIRECTION('',(1.,0.));
|
||||
#42 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#43 = PCURVE('',#44,#49);
|
||||
#44 = PLANE('',#45);
|
||||
#45 = AXIS2_PLACEMENT_3D('',#46,#47,#48);
|
||||
#46 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#47 = DIRECTION('',(-0.,1.,0.));
|
||||
#48 = DIRECTION('',(0.,0.,1.));
|
||||
#49 = DEFINITIONAL_REPRESENTATION('',(#50),#54);
|
||||
#50 = LINE('',#51,#52);
|
||||
#51 = CARTESIAN_POINT('',(0.,0.));
|
||||
#52 = VECTOR('',#53,1.);
|
||||
#53 = DIRECTION('',(1.,0.));
|
||||
#54 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#55 = ORIENTED_EDGE('',*,*,#56,.F.);
|
||||
#56 = EDGE_CURVE('',#57,#22,#59,.T.);
|
||||
#57 = VERTEX_POINT('',#58);
|
||||
#58 = CARTESIAN_POINT('',(0.,200.,0.));
|
||||
#59 = SURFACE_CURVE('',#60,(#64,#71),.PCURVE_S1.);
|
||||
#60 = LINE('',#61,#62);
|
||||
#61 = CARTESIAN_POINT('',(0.,200.,0.));
|
||||
#62 = VECTOR('',#63,1.);
|
||||
#63 = DIRECTION('',(0.,-1.,0.));
|
||||
#64 = PCURVE('',#32,#65);
|
||||
#65 = DEFINITIONAL_REPRESENTATION('',(#66),#70);
|
||||
#66 = LINE('',#67,#68);
|
||||
#67 = CARTESIAN_POINT('',(0.,-200.));
|
||||
#68 = VECTOR('',#69,1.);
|
||||
#69 = DIRECTION('',(0.,1.));
|
||||
#70 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#71 = PCURVE('',#72,#77);
|
||||
#72 = PLANE('',#73);
|
||||
#73 = AXIS2_PLACEMENT_3D('',#74,#75,#76);
|
||||
#74 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#75 = DIRECTION('',(0.,0.,1.));
|
||||
#76 = DIRECTION('',(1.,0.,-0.));
|
||||
#77 = DEFINITIONAL_REPRESENTATION('',(#78),#82);
|
||||
#78 = LINE('',#79,#80);
|
||||
#79 = CARTESIAN_POINT('',(0.,200.));
|
||||
#80 = VECTOR('',#81,1.);
|
||||
#81 = DIRECTION('',(0.,-1.));
|
||||
#82 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#83 = ORIENTED_EDGE('',*,*,#84,.F.);
|
||||
#84 = EDGE_CURVE('',#85,#57,#87,.T.);
|
||||
#85 = VERTEX_POINT('',#86);
|
||||
#86 = CARTESIAN_POINT('',(0.,200.,200.));
|
||||
#87 = SURFACE_CURVE('',#88,(#92,#99),.PCURVE_S1.);
|
||||
#88 = LINE('',#89,#90);
|
||||
#89 = CARTESIAN_POINT('',(0.,200.,200.));
|
||||
#90 = VECTOR('',#91,1.);
|
||||
#91 = DIRECTION('',(0.,0.,-1.));
|
||||
#92 = PCURVE('',#32,#93);
|
||||
#93 = DEFINITIONAL_REPRESENTATION('',(#94),#98);
|
||||
#94 = LINE('',#95,#96);
|
||||
#95 = CARTESIAN_POINT('',(200.,-200.));
|
||||
#96 = VECTOR('',#97,1.);
|
||||
#97 = DIRECTION('',(-1.,0.));
|
||||
#98 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#99 = PCURVE('',#100,#105);
|
||||
#100 = PLANE('',#101);
|
||||
#101 = AXIS2_PLACEMENT_3D('',#102,#103,#104);
|
||||
#102 = CARTESIAN_POINT('',(0.,200.,0.));
|
||||
#103 = DIRECTION('',(-0.,1.,0.));
|
||||
#104 = DIRECTION('',(0.,0.,1.));
|
||||
#105 = DEFINITIONAL_REPRESENTATION('',(#106),#110);
|
||||
#106 = LINE('',#107,#108);
|
||||
#107 = CARTESIAN_POINT('',(200.,0.));
|
||||
#108 = VECTOR('',#109,1.);
|
||||
#109 = DIRECTION('',(-1.,0.));
|
||||
#110 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#111 = ORIENTED_EDGE('',*,*,#112,.F.);
|
||||
#112 = EDGE_CURVE('',#113,#85,#115,.T.);
|
||||
#113 = VERTEX_POINT('',#114);
|
||||
#114 = CARTESIAN_POINT('',(0.,100.,200.));
|
||||
#115 = SURFACE_CURVE('',#116,(#120,#127),.PCURVE_S1.);
|
||||
#116 = LINE('',#117,#118);
|
||||
#117 = CARTESIAN_POINT('',(0.,100.,200.));
|
||||
#118 = VECTOR('',#119,1.);
|
||||
#119 = DIRECTION('',(-0.,1.,0.));
|
||||
#120 = PCURVE('',#32,#121);
|
||||
#121 = DEFINITIONAL_REPRESENTATION('',(#122),#126);
|
||||
#122 = LINE('',#123,#124);
|
||||
#123 = CARTESIAN_POINT('',(200.,-100.));
|
||||
#124 = VECTOR('',#125,1.);
|
||||
#125 = DIRECTION('',(0.,-1.));
|
||||
#126 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#127 = PCURVE('',#128,#133);
|
||||
#128 = PLANE('',#129);
|
||||
#129 = AXIS2_PLACEMENT_3D('',#130,#131,#132);
|
||||
#130 = CARTESIAN_POINT('',(0.,100.,200.));
|
||||
#131 = DIRECTION('',(0.,0.,1.));
|
||||
#132 = DIRECTION('',(1.,0.,-0.));
|
||||
#133 = DEFINITIONAL_REPRESENTATION('',(#134),#138);
|
||||
#134 = LINE('',#135,#136);
|
||||
#135 = CARTESIAN_POINT('',(0.,0.));
|
||||
#136 = VECTOR('',#137,1.);
|
||||
#137 = DIRECTION('',(0.,1.));
|
||||
#138 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#139 = ORIENTED_EDGE('',*,*,#140,.F.);
|
||||
#140 = EDGE_CURVE('',#141,#113,#143,.T.);
|
||||
#141 = VERTEX_POINT('',#142);
|
||||
#142 = CARTESIAN_POINT('',(0.,100.,100.));
|
||||
#143 = SURFACE_CURVE('',#144,(#148,#155),.PCURVE_S1.);
|
||||
#144 = LINE('',#145,#146);
|
||||
#145 = CARTESIAN_POINT('',(0.,100.,100.));
|
||||
#146 = VECTOR('',#147,1.);
|
||||
#147 = DIRECTION('',(0.,0.,1.));
|
||||
#148 = PCURVE('',#32,#149);
|
||||
#149 = DEFINITIONAL_REPRESENTATION('',(#150),#154);
|
||||
#150 = LINE('',#151,#152);
|
||||
#151 = CARTESIAN_POINT('',(100.,-100.));
|
||||
#152 = VECTOR('',#153,1.);
|
||||
#153 = DIRECTION('',(1.,0.));
|
||||
#154 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#155 = PCURVE('',#156,#161);
|
||||
#156 = PLANE('',#157);
|
||||
#157 = AXIS2_PLACEMENT_3D('',#158,#159,#160);
|
||||
#158 = CARTESIAN_POINT('',(0.,100.,100.));
|
||||
#159 = DIRECTION('',(-0.,1.,0.));
|
||||
#160 = DIRECTION('',(0.,0.,1.));
|
||||
#161 = DEFINITIONAL_REPRESENTATION('',(#162),#166);
|
||||
#162 = LINE('',#163,#164);
|
||||
#163 = CARTESIAN_POINT('',(0.,0.));
|
||||
#164 = VECTOR('',#165,1.);
|
||||
#165 = DIRECTION('',(1.,0.));
|
||||
#166 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#167 = ORIENTED_EDGE('',*,*,#168,.F.);
|
||||
#168 = EDGE_CURVE('',#24,#141,#169,.T.);
|
||||
#169 = SURFACE_CURVE('',#170,(#174,#181),.PCURVE_S1.);
|
||||
#170 = LINE('',#171,#172);
|
||||
#171 = CARTESIAN_POINT('',(0.,0.,100.));
|
||||
#172 = VECTOR('',#173,1.);
|
||||
#173 = DIRECTION('',(-0.,1.,0.));
|
||||
#174 = PCURVE('',#32,#175);
|
||||
#175 = DEFINITIONAL_REPRESENTATION('',(#176),#180);
|
||||
#176 = LINE('',#177,#178);
|
||||
#177 = CARTESIAN_POINT('',(100.,0.));
|
||||
#178 = VECTOR('',#179,1.);
|
||||
#179 = DIRECTION('',(0.,-1.));
|
||||
#180 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#181 = PCURVE('',#182,#187);
|
||||
#182 = PLANE('',#183);
|
||||
#183 = AXIS2_PLACEMENT_3D('',#184,#185,#186);
|
||||
#184 = CARTESIAN_POINT('',(0.,0.,100.));
|
||||
#185 = DIRECTION('',(0.,0.,1.));
|
||||
#186 = DIRECTION('',(1.,0.,-0.));
|
||||
#187 = DEFINITIONAL_REPRESENTATION('',(#188),#192);
|
||||
#188 = LINE('',#189,#190);
|
||||
#189 = CARTESIAN_POINT('',(0.,0.));
|
||||
#190 = VECTOR('',#191,1.);
|
||||
#191 = DIRECTION('',(0.,1.));
|
||||
#192 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#193 = ADVANCED_FACE('',(#194),#182,.T.);
|
||||
#194 = FACE_BOUND('',#195,.T.);
|
||||
#195 = EDGE_LOOP('',(#196,#197,#220,#248));
|
||||
#196 = ORIENTED_EDGE('',*,*,#168,.F.);
|
||||
#197 = ORIENTED_EDGE('',*,*,#198,.T.);
|
||||
#198 = EDGE_CURVE('',#24,#199,#201,.T.);
|
||||
#199 = VERTEX_POINT('',#200);
|
||||
#200 = CARTESIAN_POINT('',(100.,0.,100.));
|
||||
#201 = SURFACE_CURVE('',#202,(#206,#213),.PCURVE_S1.);
|
||||
#202 = LINE('',#203,#204);
|
||||
#203 = CARTESIAN_POINT('',(0.,0.,100.));
|
||||
#204 = VECTOR('',#205,1.);
|
||||
#205 = DIRECTION('',(1.,0.,-0.));
|
||||
#206 = PCURVE('',#182,#207);
|
||||
#207 = DEFINITIONAL_REPRESENTATION('',(#208),#212);
|
||||
#208 = LINE('',#209,#210);
|
||||
#209 = CARTESIAN_POINT('',(0.,0.));
|
||||
#210 = VECTOR('',#211,1.);
|
||||
#211 = DIRECTION('',(1.,0.));
|
||||
#212 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#213 = PCURVE('',#44,#214);
|
||||
#214 = DEFINITIONAL_REPRESENTATION('',(#215),#219);
|
||||
#215 = LINE('',#216,#217);
|
||||
#216 = CARTESIAN_POINT('',(100.,0.));
|
||||
#217 = VECTOR('',#218,1.);
|
||||
#218 = DIRECTION('',(0.,1.));
|
||||
#219 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#220 = ORIENTED_EDGE('',*,*,#221,.T.);
|
||||
#221 = EDGE_CURVE('',#199,#222,#224,.T.);
|
||||
#222 = VERTEX_POINT('',#223);
|
||||
#223 = CARTESIAN_POINT('',(100.,100.,100.));
|
||||
#224 = SURFACE_CURVE('',#225,(#229,#236),.PCURVE_S1.);
|
||||
#225 = LINE('',#226,#227);
|
||||
#226 = CARTESIAN_POINT('',(100.,0.,100.));
|
||||
#227 = VECTOR('',#228,1.);
|
||||
#228 = DIRECTION('',(-0.,1.,0.));
|
||||
#229 = PCURVE('',#182,#230);
|
||||
#230 = DEFINITIONAL_REPRESENTATION('',(#231),#235);
|
||||
#231 = LINE('',#232,#233);
|
||||
#232 = CARTESIAN_POINT('',(100.,0.));
|
||||
#233 = VECTOR('',#234,1.);
|
||||
#234 = DIRECTION('',(0.,1.));
|
||||
#235 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#236 = PCURVE('',#237,#242);
|
||||
#237 = PLANE('',#238);
|
||||
#238 = AXIS2_PLACEMENT_3D('',#239,#240,#241);
|
||||
#239 = CARTESIAN_POINT('',(100.,0.,100.));
|
||||
#240 = DIRECTION('',(1.,0.,-0.));
|
||||
#241 = DIRECTION('',(0.,0.,1.));
|
||||
#242 = DEFINITIONAL_REPRESENTATION('',(#243),#247);
|
||||
#243 = LINE('',#244,#245);
|
||||
#244 = CARTESIAN_POINT('',(0.,0.));
|
||||
#245 = VECTOR('',#246,1.);
|
||||
#246 = DIRECTION('',(0.,-1.));
|
||||
#247 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#248 = ORIENTED_EDGE('',*,*,#249,.F.);
|
||||
#249 = EDGE_CURVE('',#141,#222,#250,.T.);
|
||||
#250 = SURFACE_CURVE('',#251,(#255,#262),.PCURVE_S1.);
|
||||
#251 = LINE('',#252,#253);
|
||||
#252 = CARTESIAN_POINT('',(0.,100.,100.));
|
||||
#253 = VECTOR('',#254,1.);
|
||||
#254 = DIRECTION('',(1.,0.,-0.));
|
||||
#255 = PCURVE('',#182,#256);
|
||||
#256 = DEFINITIONAL_REPRESENTATION('',(#257),#261);
|
||||
#257 = LINE('',#258,#259);
|
||||
#258 = CARTESIAN_POINT('',(0.,100.));
|
||||
#259 = VECTOR('',#260,1.);
|
||||
#260 = DIRECTION('',(1.,0.));
|
||||
#261 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#262 = PCURVE('',#156,#263);
|
||||
#263 = DEFINITIONAL_REPRESENTATION('',(#264),#268);
|
||||
#264 = LINE('',#265,#266);
|
||||
#265 = CARTESIAN_POINT('',(0.,0.));
|
||||
#266 = VECTOR('',#267,1.);
|
||||
#267 = DIRECTION('',(0.,1.));
|
||||
#268 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#269 = ADVANCED_FACE('',(#270),#237,.F.);
|
||||
#270 = FACE_BOUND('',#271,.F.);
|
||||
#271 = EDGE_LOOP('',(#272,#300,#301));
|
||||
#272 = ORIENTED_EDGE('',*,*,#273,.F.);
|
||||
#273 = EDGE_CURVE('',#199,#274,#276,.T.);
|
||||
#274 = VERTEX_POINT('',#275);
|
||||
#275 = CARTESIAN_POINT('',(100.,100.,200.));
|
||||
#276 = SURFACE_CURVE('',#277,(#281,#288),.PCURVE_S1.);
|
||||
#277 = LINE('',#278,#279);
|
||||
#278 = CARTESIAN_POINT('',(100.,50.,150.));
|
||||
#279 = VECTOR('',#280,1.);
|
||||
#280 = DIRECTION('',(0.,0.707106781187,0.707106781187));
|
||||
#281 = PCURVE('',#237,#282);
|
||||
#282 = DEFINITIONAL_REPRESENTATION('',(#283),#287);
|
||||
#283 = LINE('',#284,#285);
|
||||
#284 = CARTESIAN_POINT('',(50.,-50.));
|
||||
#285 = VECTOR('',#286,1.);
|
||||
#286 = DIRECTION('',(0.707106781187,-0.707106781187));
|
||||
#287 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#288 = PCURVE('',#289,#294);
|
||||
#289 = PLANE('',#290);
|
||||
#290 = AXIS2_PLACEMENT_3D('',#291,#292,#293);
|
||||
#291 = CARTESIAN_POINT('',(0.,100.,200.));
|
||||
#292 = DIRECTION('',(0.,0.707106781187,-0.707106781187));
|
||||
#293 = DIRECTION('',(0.,0.707106781187,0.707106781187));
|
||||
#294 = DEFINITIONAL_REPRESENTATION('',(#295),#299);
|
||||
#295 = LINE('',#296,#297);
|
||||
#296 = CARTESIAN_POINT('',(-70.71067811865,100.));
|
||||
#297 = VECTOR('',#298,1.);
|
||||
#298 = DIRECTION('',(1.,0.));
|
||||
#299 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#300 = ORIENTED_EDGE('',*,*,#221,.T.);
|
||||
#301 = ORIENTED_EDGE('',*,*,#302,.T.);
|
||||
#302 = EDGE_CURVE('',#222,#274,#303,.T.);
|
||||
#303 = SURFACE_CURVE('',#304,(#308,#315),.PCURVE_S1.);
|
||||
#304 = LINE('',#305,#306);
|
||||
#305 = CARTESIAN_POINT('',(100.,100.,100.));
|
||||
#306 = VECTOR('',#307,1.);
|
||||
#307 = DIRECTION('',(0.,0.,1.));
|
||||
#308 = PCURVE('',#237,#309);
|
||||
#309 = DEFINITIONAL_REPRESENTATION('',(#310),#314);
|
||||
#310 = LINE('',#311,#312);
|
||||
#311 = CARTESIAN_POINT('',(0.,-100.));
|
||||
#312 = VECTOR('',#313,1.);
|
||||
#313 = DIRECTION('',(1.,0.));
|
||||
#314 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#315 = PCURVE('',#156,#316);
|
||||
#316 = DEFINITIONAL_REPRESENTATION('',(#317),#321);
|
||||
#317 = LINE('',#318,#319);
|
||||
#318 = CARTESIAN_POINT('',(0.,100.));
|
||||
#319 = VECTOR('',#320,1.);
|
||||
#320 = DIRECTION('',(1.,0.));
|
||||
#321 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#322 = ADVANCED_FACE('',(#323),#289,.F.);
|
||||
#323 = FACE_BOUND('',#324,.F.);
|
||||
#324 = EDGE_LOOP('',(#325,#355,#376,#377));
|
||||
#325 = ORIENTED_EDGE('',*,*,#326,.F.);
|
||||
#326 = EDGE_CURVE('',#327,#329,#331,.T.);
|
||||
#327 = VERTEX_POINT('',#328);
|
||||
#328 = CARTESIAN_POINT('',(200.,0.,100.));
|
||||
#329 = VERTEX_POINT('',#330);
|
||||
#330 = CARTESIAN_POINT('',(200.,100.,200.));
|
||||
#331 = SURFACE_CURVE('',#332,(#336,#343),.PCURVE_S1.);
|
||||
#332 = LINE('',#333,#334);
|
||||
#333 = CARTESIAN_POINT('',(200.,50.,150.));
|
||||
#334 = VECTOR('',#335,1.);
|
||||
#335 = DIRECTION('',(0.,0.707106781187,0.707106781187));
|
||||
#336 = PCURVE('',#289,#337);
|
||||
#337 = DEFINITIONAL_REPRESENTATION('',(#338),#342);
|
||||
#338 = LINE('',#339,#340);
|
||||
#339 = CARTESIAN_POINT('',(-70.71067811865,200.));
|
||||
#340 = VECTOR('',#341,1.);
|
||||
#341 = DIRECTION('',(1.,0.));
|
||||
#342 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#343 = PCURVE('',#344,#349);
|
||||
#344 = PLANE('',#345);
|
||||
#345 = AXIS2_PLACEMENT_3D('',#346,#347,#348);
|
||||
#346 = CARTESIAN_POINT('',(200.,0.,0.));
|
||||
#347 = DIRECTION('',(1.,0.,-0.));
|
||||
#348 = DIRECTION('',(0.,0.,1.));
|
||||
#349 = DEFINITIONAL_REPRESENTATION('',(#350),#354);
|
||||
#350 = LINE('',#351,#352);
|
||||
#351 = CARTESIAN_POINT('',(150.,-50.));
|
||||
#352 = VECTOR('',#353,1.);
|
||||
#353 = DIRECTION('',(0.707106781187,-0.707106781187));
|
||||
#354 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#355 = ORIENTED_EDGE('',*,*,#356,.F.);
|
||||
#356 = EDGE_CURVE('',#199,#327,#357,.T.);
|
||||
#357 = SURFACE_CURVE('',#358,(#362,#369),.PCURVE_S1.);
|
||||
#358 = LINE('',#359,#360);
|
||||
#359 = CARTESIAN_POINT('',(100.,0.,100.));
|
||||
#360 = VECTOR('',#361,1.);
|
||||
#361 = DIRECTION('',(1.,0.,-0.));
|
||||
#362 = PCURVE('',#289,#363);
|
||||
#363 = DEFINITIONAL_REPRESENTATION('',(#364),#368);
|
||||
#364 = LINE('',#365,#366);
|
||||
#365 = CARTESIAN_POINT('',(-141.4213562373,100.));
|
||||
#366 = VECTOR('',#367,1.);
|
||||
#367 = DIRECTION('',(0.,1.));
|
||||
#368 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#369 = PCURVE('',#44,#370);
|
||||
#370 = DEFINITIONAL_REPRESENTATION('',(#371),#375);
|
||||
#371 = LINE('',#372,#373);
|
||||
#372 = CARTESIAN_POINT('',(100.,100.));
|
||||
#373 = VECTOR('',#374,1.);
|
||||
#374 = DIRECTION('',(0.,1.));
|
||||
#375 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#376 = ORIENTED_EDGE('',*,*,#273,.T.);
|
||||
#377 = ORIENTED_EDGE('',*,*,#378,.T.);
|
||||
#378 = EDGE_CURVE('',#274,#329,#379,.T.);
|
||||
#379 = SURFACE_CURVE('',#380,(#384,#391),.PCURVE_S1.);
|
||||
#380 = LINE('',#381,#382);
|
||||
#381 = CARTESIAN_POINT('',(100.,100.,200.));
|
||||
#382 = VECTOR('',#383,1.);
|
||||
#383 = DIRECTION('',(1.,0.,-0.));
|
||||
#384 = PCURVE('',#289,#385);
|
||||
#385 = DEFINITIONAL_REPRESENTATION('',(#386),#390);
|
||||
#386 = LINE('',#387,#388);
|
||||
#387 = CARTESIAN_POINT('',(0.,100.));
|
||||
#388 = VECTOR('',#389,1.);
|
||||
#389 = DIRECTION('',(0.,1.));
|
||||
#390 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#391 = PCURVE('',#128,#392);
|
||||
#392 = DEFINITIONAL_REPRESENTATION('',(#393),#397);
|
||||
#393 = LINE('',#394,#395);
|
||||
#394 = CARTESIAN_POINT('',(100.,0.));
|
||||
#395 = VECTOR('',#396,1.);
|
||||
#396 = DIRECTION('',(1.,0.));
|
||||
#397 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#398 = ADVANCED_FACE('',(#399),#344,.T.);
|
||||
#399 = FACE_BOUND('',#400,.T.);
|
||||
#400 = EDGE_LOOP('',(#401,#424,#447,#470,#491));
|
||||
#401 = ORIENTED_EDGE('',*,*,#402,.F.);
|
||||
#402 = EDGE_CURVE('',#403,#327,#405,.T.);
|
||||
#403 = VERTEX_POINT('',#404);
|
||||
#404 = CARTESIAN_POINT('',(200.,0.,0.));
|
||||
#405 = SURFACE_CURVE('',#406,(#410,#417),.PCURVE_S1.);
|
||||
#406 = LINE('',#407,#408);
|
||||
#407 = CARTESIAN_POINT('',(200.,0.,0.));
|
||||
#408 = VECTOR('',#409,1.);
|
||||
#409 = DIRECTION('',(0.,0.,1.));
|
||||
#410 = PCURVE('',#344,#411);
|
||||
#411 = DEFINITIONAL_REPRESENTATION('',(#412),#416);
|
||||
#412 = LINE('',#413,#414);
|
||||
#413 = CARTESIAN_POINT('',(0.,0.));
|
||||
#414 = VECTOR('',#415,1.);
|
||||
#415 = DIRECTION('',(1.,0.));
|
||||
#416 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#417 = PCURVE('',#44,#418);
|
||||
#418 = DEFINITIONAL_REPRESENTATION('',(#419),#423);
|
||||
#419 = LINE('',#420,#421);
|
||||
#420 = CARTESIAN_POINT('',(0.,200.));
|
||||
#421 = VECTOR('',#422,1.);
|
||||
#422 = DIRECTION('',(1.,0.));
|
||||
#423 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#424 = ORIENTED_EDGE('',*,*,#425,.F.);
|
||||
#425 = EDGE_CURVE('',#426,#403,#428,.T.);
|
||||
#426 = VERTEX_POINT('',#427);
|
||||
#427 = CARTESIAN_POINT('',(200.,200.,0.));
|
||||
#428 = SURFACE_CURVE('',#429,(#433,#440),.PCURVE_S1.);
|
||||
#429 = LINE('',#430,#431);
|
||||
#430 = CARTESIAN_POINT('',(200.,200.,0.));
|
||||
#431 = VECTOR('',#432,1.);
|
||||
#432 = DIRECTION('',(0.,-1.,0.));
|
||||
#433 = PCURVE('',#344,#434);
|
||||
#434 = DEFINITIONAL_REPRESENTATION('',(#435),#439);
|
||||
#435 = LINE('',#436,#437);
|
||||
#436 = CARTESIAN_POINT('',(0.,-200.));
|
||||
#437 = VECTOR('',#438,1.);
|
||||
#438 = DIRECTION('',(0.,1.));
|
||||
#439 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#440 = PCURVE('',#72,#441);
|
||||
#441 = DEFINITIONAL_REPRESENTATION('',(#442),#446);
|
||||
#442 = LINE('',#443,#444);
|
||||
#443 = CARTESIAN_POINT('',(200.,200.));
|
||||
#444 = VECTOR('',#445,1.);
|
||||
#445 = DIRECTION('',(0.,-1.));
|
||||
#446 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#447 = ORIENTED_EDGE('',*,*,#448,.F.);
|
||||
#448 = EDGE_CURVE('',#449,#426,#451,.T.);
|
||||
#449 = VERTEX_POINT('',#450);
|
||||
#450 = CARTESIAN_POINT('',(200.,200.,200.));
|
||||
#451 = SURFACE_CURVE('',#452,(#456,#463),.PCURVE_S1.);
|
||||
#452 = LINE('',#453,#454);
|
||||
#453 = CARTESIAN_POINT('',(200.,200.,200.));
|
||||
#454 = VECTOR('',#455,1.);
|
||||
#455 = DIRECTION('',(0.,0.,-1.));
|
||||
#456 = PCURVE('',#344,#457);
|
||||
#457 = DEFINITIONAL_REPRESENTATION('',(#458),#462);
|
||||
#458 = LINE('',#459,#460);
|
||||
#459 = CARTESIAN_POINT('',(200.,-200.));
|
||||
#460 = VECTOR('',#461,1.);
|
||||
#461 = DIRECTION('',(-1.,0.));
|
||||
#462 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#463 = PCURVE('',#100,#464);
|
||||
#464 = DEFINITIONAL_REPRESENTATION('',(#465),#469);
|
||||
#465 = LINE('',#466,#467);
|
||||
#466 = CARTESIAN_POINT('',(200.,200.));
|
||||
#467 = VECTOR('',#468,1.);
|
||||
#468 = DIRECTION('',(-1.,0.));
|
||||
#469 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#470 = ORIENTED_EDGE('',*,*,#471,.F.);
|
||||
#471 = EDGE_CURVE('',#329,#449,#472,.T.);
|
||||
#472 = SURFACE_CURVE('',#473,(#477,#484),.PCURVE_S1.);
|
||||
#473 = LINE('',#474,#475);
|
||||
#474 = CARTESIAN_POINT('',(200.,100.,200.));
|
||||
#475 = VECTOR('',#476,1.);
|
||||
#476 = DIRECTION('',(-0.,1.,0.));
|
||||
#477 = PCURVE('',#344,#478);
|
||||
#478 = DEFINITIONAL_REPRESENTATION('',(#479),#483);
|
||||
#479 = LINE('',#480,#481);
|
||||
#480 = CARTESIAN_POINT('',(200.,-100.));
|
||||
#481 = VECTOR('',#482,1.);
|
||||
#482 = DIRECTION('',(0.,-1.));
|
||||
#483 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#484 = PCURVE('',#128,#485);
|
||||
#485 = DEFINITIONAL_REPRESENTATION('',(#486),#490);
|
||||
#486 = LINE('',#487,#488);
|
||||
#487 = CARTESIAN_POINT('',(200.,0.));
|
||||
#488 = VECTOR('',#489,1.);
|
||||
#489 = DIRECTION('',(0.,1.));
|
||||
#490 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#491 = ORIENTED_EDGE('',*,*,#326,.F.);
|
||||
#492 = ADVANCED_FACE('',(#493),#156,.F.);
|
||||
#493 = FACE_BOUND('',#494,.F.);
|
||||
#494 = EDGE_LOOP('',(#495,#496,#497,#518));
|
||||
#495 = ORIENTED_EDGE('',*,*,#249,.F.);
|
||||
#496 = ORIENTED_EDGE('',*,*,#140,.T.);
|
||||
#497 = ORIENTED_EDGE('',*,*,#498,.T.);
|
||||
#498 = EDGE_CURVE('',#113,#274,#499,.T.);
|
||||
#499 = SURFACE_CURVE('',#500,(#504,#511),.PCURVE_S1.);
|
||||
#500 = LINE('',#501,#502);
|
||||
#501 = CARTESIAN_POINT('',(0.,100.,200.));
|
||||
#502 = VECTOR('',#503,1.);
|
||||
#503 = DIRECTION('',(1.,0.,-0.));
|
||||
#504 = PCURVE('',#156,#505);
|
||||
#505 = DEFINITIONAL_REPRESENTATION('',(#506),#510);
|
||||
#506 = LINE('',#507,#508);
|
||||
#507 = CARTESIAN_POINT('',(100.,0.));
|
||||
#508 = VECTOR('',#509,1.);
|
||||
#509 = DIRECTION('',(0.,1.));
|
||||
#510 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#511 = PCURVE('',#128,#512);
|
||||
#512 = DEFINITIONAL_REPRESENTATION('',(#513),#517);
|
||||
#513 = LINE('',#514,#515);
|
||||
#514 = CARTESIAN_POINT('',(0.,0.));
|
||||
#515 = VECTOR('',#516,1.);
|
||||
#516 = DIRECTION('',(1.,0.));
|
||||
#517 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#518 = ORIENTED_EDGE('',*,*,#302,.F.);
|
||||
#519 = ADVANCED_FACE('',(#520),#128,.T.);
|
||||
#520 = FACE_BOUND('',#521,.T.);
|
||||
#521 = EDGE_LOOP('',(#522,#523,#524,#525,#526));
|
||||
#522 = ORIENTED_EDGE('',*,*,#112,.F.);
|
||||
#523 = ORIENTED_EDGE('',*,*,#498,.T.);
|
||||
#524 = ORIENTED_EDGE('',*,*,#378,.T.);
|
||||
#525 = ORIENTED_EDGE('',*,*,#471,.T.);
|
||||
#526 = ORIENTED_EDGE('',*,*,#527,.F.);
|
||||
#527 = EDGE_CURVE('',#85,#449,#528,.T.);
|
||||
#528 = SURFACE_CURVE('',#529,(#533,#540),.PCURVE_S1.);
|
||||
#529 = LINE('',#530,#531);
|
||||
#530 = CARTESIAN_POINT('',(0.,200.,200.));
|
||||
#531 = VECTOR('',#532,1.);
|
||||
#532 = DIRECTION('',(1.,0.,0.));
|
||||
#533 = PCURVE('',#128,#534);
|
||||
#534 = DEFINITIONAL_REPRESENTATION('',(#535),#539);
|
||||
#535 = LINE('',#536,#537);
|
||||
#536 = CARTESIAN_POINT('',(0.,100.));
|
||||
#537 = VECTOR('',#538,1.);
|
||||
#538 = DIRECTION('',(1.,0.));
|
||||
#539 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#540 = PCURVE('',#100,#541);
|
||||
#541 = DEFINITIONAL_REPRESENTATION('',(#542),#546);
|
||||
#542 = LINE('',#543,#544);
|
||||
#543 = CARTESIAN_POINT('',(200.,0.));
|
||||
#544 = VECTOR('',#545,1.);
|
||||
#545 = DIRECTION('',(0.,1.));
|
||||
#546 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#547 = ADVANCED_FACE('',(#548),#100,.T.);
|
||||
#548 = FACE_BOUND('',#549,.T.);
|
||||
#549 = EDGE_LOOP('',(#550,#571,#572,#573));
|
||||
#550 = ORIENTED_EDGE('',*,*,#551,.F.);
|
||||
#551 = EDGE_CURVE('',#57,#426,#552,.T.);
|
||||
#552 = SURFACE_CURVE('',#553,(#557,#564),.PCURVE_S1.);
|
||||
#553 = LINE('',#554,#555);
|
||||
#554 = CARTESIAN_POINT('',(0.,200.,0.));
|
||||
#555 = VECTOR('',#556,1.);
|
||||
#556 = DIRECTION('',(1.,0.,0.));
|
||||
#557 = PCURVE('',#100,#558);
|
||||
#558 = DEFINITIONAL_REPRESENTATION('',(#559),#563);
|
||||
#559 = LINE('',#560,#561);
|
||||
#560 = CARTESIAN_POINT('',(0.,0.));
|
||||
#561 = VECTOR('',#562,1.);
|
||||
#562 = DIRECTION('',(0.,1.));
|
||||
#563 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#564 = PCURVE('',#72,#565);
|
||||
#565 = DEFINITIONAL_REPRESENTATION('',(#566),#570);
|
||||
#566 = LINE('',#567,#568);
|
||||
#567 = CARTESIAN_POINT('',(0.,200.));
|
||||
#568 = VECTOR('',#569,1.);
|
||||
#569 = DIRECTION('',(1.,0.));
|
||||
#570 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#571 = ORIENTED_EDGE('',*,*,#84,.F.);
|
||||
#572 = ORIENTED_EDGE('',*,*,#527,.T.);
|
||||
#573 = ORIENTED_EDGE('',*,*,#448,.T.);
|
||||
#574 = ADVANCED_FACE('',(#575),#72,.F.);
|
||||
#575 = FACE_BOUND('',#576,.F.);
|
||||
#576 = EDGE_LOOP('',(#577,#598,#599,#600));
|
||||
#577 = ORIENTED_EDGE('',*,*,#578,.T.);
|
||||
#578 = EDGE_CURVE('',#22,#403,#579,.T.);
|
||||
#579 = SURFACE_CURVE('',#580,(#584,#591),.PCURVE_S1.);
|
||||
#580 = LINE('',#581,#582);
|
||||
#581 = CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#582 = VECTOR('',#583,1.);
|
||||
#583 = DIRECTION('',(1.,0.,0.));
|
||||
#584 = PCURVE('',#72,#585);
|
||||
#585 = DEFINITIONAL_REPRESENTATION('',(#586),#590);
|
||||
#586 = LINE('',#587,#588);
|
||||
#587 = CARTESIAN_POINT('',(0.,0.));
|
||||
#588 = VECTOR('',#589,1.);
|
||||
#589 = DIRECTION('',(1.,0.));
|
||||
#590 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#591 = PCURVE('',#44,#592);
|
||||
#592 = DEFINITIONAL_REPRESENTATION('',(#593),#597);
|
||||
#593 = LINE('',#594,#595);
|
||||
#594 = CARTESIAN_POINT('',(0.,0.));
|
||||
#595 = VECTOR('',#596,1.);
|
||||
#596 = DIRECTION('',(0.,1.));
|
||||
#597 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2)
|
||||
PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE',''
|
||||
) );
|
||||
#598 = ORIENTED_EDGE('',*,*,#425,.F.);
|
||||
#599 = ORIENTED_EDGE('',*,*,#551,.F.);
|
||||
#600 = ORIENTED_EDGE('',*,*,#56,.T.);
|
||||
#601 = ADVANCED_FACE('',(#602),#44,.F.);
|
||||
#602 = FACE_BOUND('',#603,.F.);
|
||||
#603 = EDGE_LOOP('',(#604,#605,#606,#607,#608));
|
||||
#604 = ORIENTED_EDGE('',*,*,#578,.F.);
|
||||
#605 = ORIENTED_EDGE('',*,*,#21,.T.);
|
||||
#606 = ORIENTED_EDGE('',*,*,#198,.T.);
|
||||
#607 = ORIENTED_EDGE('',*,*,#356,.T.);
|
||||
#608 = ORIENTED_EDGE('',*,*,#402,.F.);
|
||||
#609 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3)
|
||||
GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#613)) GLOBAL_UNIT_ASSIGNED_CONTEXT
|
||||
((#610,#611,#612)) REPRESENTATION_CONTEXT('Context #1',
|
||||
'3D Context with UNIT and UNCERTAINTY') );
|
||||
#610 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) );
|
||||
#611 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) );
|
||||
#612 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() );
|
||||
#613 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(2.E-07),#610,
|
||||
'distance_accuracy_value','confusion accuracy');
|
||||
#614 = PRODUCT_RELATED_PRODUCT_CATEGORY('part',$,(#7));
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# CUBE01
|
||||
|
||||
Caso de prueba del profesorado. Cubo de 200 mm con un escalón en una mitad y un chaflán a 45° en la otra.
|
||||
|
||||
- Escala: 1:2 (en el cajetín, campo *Title*)
|
||||
- Cotas: ninguna
|
||||
- Particularidad: planta en sistema europeo y perfil en sistema americano (mezcla intencionada)
|
||||
- Volumen esperado: 6 500 000 mm³
|
||||
- Avisos esperados: `SIN_COTAS`, `PROYECCION_MIXTA`
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,30 @@
|
|||
"""Comprueba que motor.api cumple el contrato que usa la aplicación."""
|
||||
from pathlib import Path
|
||||
|
||||
import cadquery as cq
|
||||
|
||||
from motor.api import Estado, Gravedad, Resultado, exportar, procesar
|
||||
|
||||
CATALOGO = Path(__file__).parent / "catalogo"
|
||||
|
||||
|
||||
def test_procesar_devuelve_un_resultado_valido():
|
||||
r = procesar(CATALOGO / "CUBE01" / "plano.dxf")
|
||||
assert isinstance(r, Resultado)
|
||||
assert r.estado in Estado
|
||||
assert all(a.gravedad in Gravedad for a in r.avisos)
|
||||
|
||||
|
||||
def test_cube01_tiene_el_volumen_esperado():
|
||||
r = procesar(CATALOGO / "CUBE01" / "plano.dxf")
|
||||
# Cubo de 200 mm menos el escalón (100^3) y el chaflán (100^3 / 2)
|
||||
assert abs(r.solido.Volume() - 6_500_000) < 1
|
||||
|
||||
|
||||
def test_exportar_step_y_stl(tmp_path):
|
||||
r = procesar(CATALOGO / "CUBE01" / "plano.dxf")
|
||||
for ext in (".step", ".stl"):
|
||||
exportar(r.solido, tmp_path / f"pieza{ext}")
|
||||
assert (tmp_path / f"pieza{ext}").stat().st_size > 0
|
||||
releida = cq.importers.importStep(str(tmp_path / "pieza.step")).val()
|
||||
assert releida.isValid()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
[project]
|
||||
name = "plano3d"
|
||||
version = "0.1.0"
|
||||
description = "Reconstrucción automática de piezas 3D a partir de planos técnicos 2D, con verificación del plano"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
requires-python = ">=3.12,<3.14"
|
||||
dependencies = [
|
||||
"cadquery>=2.8",
|
||||
"ezdxf>=1.4",
|
||||
"shapely>=2.0",
|
||||
"numpy>=2.0",
|
||||
"ortools>=9.10",
|
||||
"PySide6>=6.7",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8",
|
||||
"pyinstaller>=6",
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["pruebas"]
|
||||
pythonpath = ["."]
|
||||
Loading…
Reference in New Issue