Road: sistema de alineamiento profesional. Alignment (eje+estaciones), Road multicapa con cubicación contra terreno
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# /**********************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2026 Javier Braña <javier.branagutierrez@gmail.com> *
|
||||
# * *
|
||||
# * Sistema de Alineamiento Horizontal y Vertical para carreteras *
|
||||
# * *
|
||||
# ***********************************************************************
|
||||
|
||||
import FreeCAD
|
||||
import Part
|
||||
import math
|
||||
import numpy as np
|
||||
|
||||
def make_alignment_from_wire(wire, name="Alignment"):
|
||||
"""Crea un objeto Alignment a partir de un wire de FreeCAD."""
|
||||
if FreeCAD.ActiveDocument is None:
|
||||
return None
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
Alignment(obj)
|
||||
_ViewProviderAlignment(obj.ViewObject)
|
||||
obj.SourceWire = wire
|
||||
obj.Label = name
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
return obj
|
||||
|
||||
|
||||
class Alignment:
|
||||
"""
|
||||
Alineamiento horizontal + vertical.
|
||||
|
||||
Propiedades:
|
||||
- SourceWire: Polilínea base (2D o 3D)
|
||||
- Stations: Lista de estaciones (progresivas)
|
||||
- HorizontalCurves: Curvas circulares en el plano
|
||||
- VerticalProfile: Perfil longitudinal (pendientes + curvas verticales)
|
||||
- CrossSections: Secciones transversales asociadas
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
obj.Proxy = self
|
||||
self.setProperties(obj)
|
||||
self._cached_chainage = None
|
||||
self._cached_points = None
|
||||
|
||||
def setProperties(self, obj):
|
||||
pl = obj.PropertiesList
|
||||
if "SourceWire" not in pl:
|
||||
obj.addProperty("App::PropertyLink",
|
||||
"SourceWire",
|
||||
"Alignment",
|
||||
"Polilínea base del eje").SourceWire = None
|
||||
if "Stations" not in pl:
|
||||
obj.addProperty("App::PropertyFloatList",
|
||||
"Stations",
|
||||
"Alignment",
|
||||
"Progresivas (estaciones) del eje")
|
||||
if "StationInterval" not in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"StationInterval",
|
||||
"Alignment",
|
||||
"Intervalo entre estaciones").StationInterval = 20000 # 20m
|
||||
if "NumberOfStations" not in pl:
|
||||
obj.addProperty("App::PropertyInteger",
|
||||
"NumberOfStations",
|
||||
"Alignment",
|
||||
"Número de estaciones").NumberOfStations = 0
|
||||
obj.setEditorMode("NumberOfStations", 1) # read-only
|
||||
if "TotalLength" not in pl:
|
||||
obj.addProperty("App::PropertyLength",
|
||||
"TotalLength",
|
||||
"Alignment",
|
||||
"Longitud total del eje").TotalLength = 0
|
||||
obj.setEditorMode("TotalLength", 1)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
self.setProperties(obj)
|
||||
|
||||
def execute(self, obj):
|
||||
"""Calcula estaciones y geometría del alignment."""
|
||||
if not obj.SourceWire or not obj.SourceWire.Shape:
|
||||
return
|
||||
|
||||
wire = obj.SourceWire.Shape
|
||||
total_len = wire.Length
|
||||
obj.TotalLength = total_len
|
||||
|
||||
interval = obj.StationInterval.Value if hasattr(obj.StationInterval, 'Value') else obj.StationInterval
|
||||
if interval <= 0:
|
||||
interval = 20000
|
||||
|
||||
n_stations = max(2, int(total_len / interval) + 1)
|
||||
obj.NumberOfStations = n_stations
|
||||
|
||||
# Generar puntos de estación a lo largo del wire
|
||||
stations = []
|
||||
for i in range(n_stations):
|
||||
param = i / (n_stations - 1)
|
||||
stations.append(param * total_len)
|
||||
|
||||
obj.Stations = stations
|
||||
self._cached_chainage = stations
|
||||
self._cached_points = None
|
||||
|
||||
def get_station_point(self, obj, distance):
|
||||
"""Devuelve el punto en el eje a una distancia (progresiva) dada."""
|
||||
if not obj.SourceWire:
|
||||
return None
|
||||
try:
|
||||
return obj.SourceWire.Shape.valueAt(
|
||||
obj.SourceWire.Shape.getParameterByLength(distance / obj.TotalLength)
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_tangent_at(self, obj, distance):
|
||||
"""Devuelve el vector tangente en una progresiva."""
|
||||
if not obj.SourceWire:
|
||||
return None
|
||||
try:
|
||||
return obj.SourceWire.Shape.tangentAt(
|
||||
obj.SourceWire.Shape.getParameterByLength(distance / obj.TotalLength)
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
|
||||
|
||||
class _ViewProviderAlignment:
|
||||
def __init__(self, vobj):
|
||||
vobj.Proxy = self
|
||||
|
||||
def getIcon(self):
|
||||
return ""
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
Reference in New Issue
Block a user