diff --git a/Civil/Alignment.py b/Civil/Alignment.py deleted file mode 100644 index aa73979..0000000 --- a/Civil/Alignment.py +++ /dev/null @@ -1,144 +0,0 @@ -# /********************************************************************** -# * * -# * Copyright (c) 2026 Javier Braña * -# * * -# * 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 \ No newline at end of file diff --git a/Export/exportKMZ.py b/Export/exportKMZ.py index 8f21acd..666bd9e 100644 --- a/Export/exportKMZ.py +++ b/Export/exportKMZ.py @@ -5,7 +5,7 @@ import zipfile import tempfile import shutil import xml.etree.ElementTree as ET -from PySide2 import QtWidgets, QtCore, QtGui +from PySide import QtWidgets, QtCore, QtGui import FreeCAD import Mesh import Part diff --git a/Importer/importDXF.py b/Importer/importDXF.py index 9b66373..9ea9365 100644 --- a/Importer/importDXF.py +++ b/Importer/importDXF.py @@ -72,11 +72,11 @@ class _PVPlantImportDXF: def openFile(self): ''' ''' - "getOpenFileName(parent: typing.Union[PySide2.QtWidgets.QWidget, NoneType] = None," \ + "getOpenFileName(parent: typing.Union[PySide.QtWidgets.QWidget, NoneType] = None," \ "caption: str = ''," \ "dir: str = ''," \ "filter: str = ''," \ - "options: PySide2.QtWidgets.QFileDialog.Options = Default(QFileDialog.Options)) -> typing.Tuple[str, str]" + "options: PySide.QtWidgets.QFileDialog.Options = Default(QFileDialog.Options)) -> typing.Tuple[str, str]" filename, trash = QtGui.QFileDialog().getOpenFileName(None, 'Select File', os.getcwd(), 'Autocad dxf (*.dxf)') if filename == "": return diff --git a/PVPLantPlacement-old_2022.py b/PVPLantPlacement-old_2022.py deleted file mode 100644 index 563a836..0000000 --- a/PVPLantPlacement-old_2022.py +++ /dev/null @@ -1,554 +0,0 @@ -import ArchComponent -import FreeCAD - -if FreeCAD.GuiUp: - import FreeCADGui - from PySide import QtCore, QtGui - from PySide.QtCore import QT_TRANSLATE_NOOP -else: - # \cond - def translate(ctxt, txt): - return txt - - - def QT_TRANSLATE_NOOP(ctxt, txt): - return txt - # \endcond - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -import threading - - -def makePlacement(baseobj=None, diameter=0, length=0, placement=None, name="Placement"): - "makePipe([baseobj,diamerter,length,placement,name]): creates an pipe object from the given base object" - - if not FreeCAD.ActiveDocument: - FreeCAD.Console.PrintError("No active document. Aborting\n") - return - - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name) - obj.Label = name - _PVPlantPlacement(obj) - - if FreeCAD.GuiUp: - _ViewProviderPVPlantPlacement(obj.ViewObject) - if baseobj: - baseobj.ViewObject.hide() - - return obj - - -class _CommandPVPlantPlacement: - "the Arch Schedule command definition" - - def GetResources(self): - return {'Pixmap': 'Placement', - 'Accel': "P, S", - 'MenuText': QT_TRANSLATE_NOOP("Placement", "Placement"), - 'ToolTip': QT_TRANSLATE_NOOP("Placement", "Crear un campo fotovoltaico")} - - def Activated(self): - taskd = _PVPlantPlacementTaskPanel() - FreeCADGui.Control.showDialog(taskd) - - def IsActive(self): - if FreeCAD.ActiveDocument: - return True - else: - return False - - -class _PVPlantPlacement(ArchComponent.Component): - "the PVPlantPlacement object" - - def __init__(self, obj): - - ArchComponent.Component.__init__(self, obj) - self.setProperties(obj) - # Does a IfcType exist? - # obj.IfcType = "Fence" - obj.MoveWithHost = False - - def setProperties(self, obj): - ArchComponent.Component.setProperties(self, obj) - - pl = obj.PropertiesList - - if not "Section" in pl: - obj.addProperty("App::PropertyLink", "Land", "Placement", QT_TRANSLATE_NOOP( - "App::Property", "A single section of the fence")) - - if not "Post" in pl: - obj.addProperty("App::PropertyLink", "Structure", "Placement", QT_TRANSLATE_NOOP( - "App::Property", "A single fence post")) - - if not "Path" in pl: - obj.addProperty("App::PropertyLink", "Path", "Placement", QT_TRANSLATE_NOOP( - "App::Property", "The Path the fence should follow")) - - if not "NumberOfSections" in pl: - obj.addProperty("App::PropertyInteger", "NumberOfSections", "Count", QT_TRANSLATE_NOOP( - "App::Property", "The number of sections the fence is built of")) - obj.setEditorMode("NumberOfSections", 1) - - if not "NumberOfPosts" in pl: - obj.addProperty("App::PropertyInteger", "NumberOfPosts", "Count", QT_TRANSLATE_NOOP( - "App::Property", "The number of posts used to build the fence")) - obj.setEditorMode("NumberOfPosts", 1) - - self.Type = "Fence" - - def execute(self, obj): - # fills columns A, B and C of the spreadsheet - if not obj.Description: - return - - def __getstate__(self): - return self.Type - - def __setstate__(self, state): - if state: - self.Type = state - - -class _ViewProviderPVPlantPlacement: - "A View Provider for PVPlantPlacement" - - def __init__(self, vobj): - vobj.Proxy = self - - def getIcon(self): - return ":/icons/Arch_Schedule.svg" - - def attach(self, vobj): - self.Object = vobj.Object - - def setEdit(self, vobj, mode): - # taskd = _ArchScheduleTaskPanel(vobj.Object) - # FreeCADGui.Control.showDialog(taskd) - return True - - def doubleClicked(self, vobj): - # taskd = _ArchScheduleTaskPanel(vobj.Object) - # FreeCADGui.Control.showDialog(taskd) - return True - - def unsetEdit(self, vobj, mode): - # FreeCADGui.Control.closeDialog() - return - - def claimChildren(self): - # if hasattr(self,"Object"): - # return [self.Object.Result] - return None - - def __getstate__(self): - return None - - def __setstate__(self, state): - return None - - def getDisplayModes(self, vobj): - return ["Default"] - - def getDefaultDisplayMode(self): - return "Default" - - def setDisplayMode(self, mode): - return mode - - -class _PVPlantPlacementTaskPanel: - '''The editmode TaskPanel for Schedules''' - - def __init__(self, obj=None): - self.Terrain = None - self.Rack = None - self.Gap = 200 - self.Pitch = 4500 - - # form: - self.form = QtGui.QWidget() - self.form.resize(800, 640) - self.form.setWindowTitle("Curvas de nivel") - self.form.setWindowIcon(QtGui.QIcon(":/icons/Arch_Schedule.svg")) - self.grid = QtGui.QGridLayout(self.form) - - # parameters - self.labelTerrain = QtGui.QLabel() - self.labelTerrain.setText("Terreno:") - self.lineTerrain = QtGui.QLineEdit(self.form) - self.lineTerrain.setObjectName(_fromUtf8("lineTerrain")) - self.lineTerrain.readOnly = True - self.grid.addWidget(self.labelTerrain, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.lineTerrain, self.grid.rowCount() - 1, 1, 1, 1) - self.buttonAddTerrain = QtGui.QPushButton('Sel') - self.grid.addWidget(self.buttonAddTerrain, self.grid.rowCount() - 1, 2, 1, 1) - - self.labelRack = QtGui.QLabel() - self.labelRack.setText("Rack:") - self.lineRack = QtGui.QLineEdit(self.form) - self.lineRack.setObjectName(_fromUtf8("lineRack")) - self.lineRack.readOnly = True - self.grid.addWidget(self.labelRack, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.lineRack, self.grid.rowCount() - 1, 1, 1, 1) - self.buttonAddRack = QtGui.QPushButton('Sel') - self.grid.addWidget(self.buttonAddRack, self.grid.rowCount() - 1, 2, 1, 1) - - self.line1 = QtGui.QFrame() - self.line1.setFrameShape(QtGui.QFrame.HLine) - self.line1.setFrameShadow(QtGui.QFrame.Sunken) - self.grid.addWidget(self.line1, self.grid.rowCount(), 0, 1, -1) - - self.labelTypeStructure = QtGui.QLabel() - self.labelTypeStructure.setText("Tipo de estructura:") - self.valueTypeStructure = QtGui.QComboBox() - self.valueTypeStructure.addItems(["Fixed", "Tracker 1 Axis"]) - self.valueTypeStructure.setCurrentIndex(0) - self.grid.addWidget(self.labelTypeStructure, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valueTypeStructure, self.grid.rowCount() - 1, 1, 1, -1) - - self.labelOrientation = QtGui.QLabel() - self.labelOrientation.setText("Orientacion:") - self.valueOrientation = QtGui.QComboBox() - self.valueOrientation.addItems(["Norte-Sur", "Este-Oeste"]) - self.valueOrientation.setCurrentIndex(0) - self.grid.addWidget(self.labelOrientation, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valueOrientation, self.grid.rowCount() - 1, 1, 1, -1) - - self.labelGap = QtGui.QLabel() - self.labelGap.setText("Espacio entre Columnas:") - self.valueGap = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.valueGap.setText(str(self.Gap) + " mm") - self.grid.addWidget(self.labelGap, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valueGap, self.grid.rowCount() - 1, 1, 1, -1) - - self.labelPitch = QtGui.QLabel() - self.labelPitch.setText("Separacion entre Filas:") - self.valuePitch = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.valuePitch.setText(str(self.Pitch) + " mm") - self.grid.addWidget(self.labelPitch, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valuePitch, self.grid.rowCount() - 1, 1, 1, -1) - - self.labelAlign = QtGui.QLabel() - self.labelAlign.setText("Método de alineación:") - self.valueAlign = QtGui.QComboBox() - self.valueAlign.addItems(["Si", "No"]) - self.valueAlign.setCurrentIndex(0) - self.grid.addWidget(self.labelAlign, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valueAlign, self.grid.rowCount() - 1, 1, 1, -1) - - self.line2 = QtGui.QFrame() - self.line2.setFrameShape(QtGui.QFrame.HLine) - self.line2.setFrameShadow(QtGui.QFrame.Sunken) - self.grid.addWidget(self.line2, self.grid.rowCount(), 0, 1, -1) - - self.labelSideSlope = QtGui.QLabel() - self.labelSideSlope.setText("Maxima inclinacion longitudinal:") - self.valueSideSlope = FreeCADGui.UiLoader().createWidget("Gui::InputField") - self.valueSideSlope.setText("15") - self.grid.addWidget(self.labelSideSlope, self.grid.rowCount(), 0, 1, 1) - self.grid.addWidget(self.valueSideSlope, self.grid.rowCount() - 1, 1, 1, -1) - - QtCore.QObject.connect(self.buttonAddTerrain, QtCore.SIGNAL("clicked()"), self.addTerrain) - QtCore.QObject.connect(self.buttonAddRack, QtCore.SIGNAL("clicked()"), self.addRack) - # QtCore.QObject.connect(self.form.buttonDel, QtCore.SIGNAL("clicked()"), self.remove) - # QtCore.QObject.connect(self.form.buttonClear, QtCore.SIGNAL("clicked()"), self.clear) - # QtCore.QObject.connect(self.form.buttonSelect, QtCore.SIGNAL("clicked()"), self.select) - - def addTerrain(self): - sel = FreeCADGui.Selection.getSelection() - if len(sel) > 0: - self.Terrain = sel[0] - self.lineTerrain.setText(self.Terrain.Label) - - def addRack(self): - sel = FreeCADGui.Selection.getSelection() - if len(sel) > 0: - self.Rack = sel[0] - self.lineRack.setText(self.Rack.Label) - - def accept(self): - if self.Terrain is not None and self.Rack is not None: - self.Gap = FreeCAD.Units.Quantity(self.valueGap.text()).Value - self.Pitch = FreeCAD.Units.Quantity(self.valuePitch.text()).Value - self.placement() - return True - - def placement(self): - if self.valueTypeStructure.currentIndex() == 0: # Fixed - print("Rack") - else: - print("Tracker") - if self.Rack.Height < self.Rack.Length: - print("rotar") - aux = self.Rack.Length - self.Rack.Length = self.Rack.Height - self.Rack.Height = aux - - self.Rack.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin - self.Rack.Placement.Base.y = self.Terrain.Shape.BoundBox.YMin - - DistColls = self.Rack.Length.Value + self.Gap - DistRows = self.Rack.Height.Value + self.Pitch - area = self.Rack.Shape.Faces[0].Area # * 0.999999999 - - import Draft - rec = Draft.makeRectangle(length=self.Terrain.Shape.BoundBox.XLength, height=self.Rack.Height, face=True, - support=None) - rec.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin - rec.Placement.Base.y = self.Terrain.Shape.BoundBox.YMin - - try: - while rec.Shape.BoundBox.YMax <= self.Terrain.Shape.BoundBox.YMax: - common = self.Terrain.Shape.common(rec.Shape) - for shape in common.Faces: - if shape.Area >= area: - if False: - minorPoint = FreeCAD.Vector(0, 0, 0) - for spoint in shape.OuterWire.Vertexes: - if minorPoint.y >= spoint.Point.y: - if minorPoint.x >= spoint.x: - minorPoint = spoint - self.Rack.Placement.Base = spoint - else: - # más rápido - self.Rack.Placement.Base.x = shape.BoundBox.XMin - self.Rack.Placement.Base.y = shape.BoundBox.YMin - - while self.Rack.Shape.BoundBox.XMax <= shape.BoundBox.XMax: - verts = [v.Point for v in rackClone.Shape.OuterWire.OrderedVertexes] - inside = True - for vert in verts: - if not shape.isInside(vert, 0, True): - inside = False - break - - if inside: - raise - else: - # ajuste fino hasta encontrar el primer sitio: - rackClone.Placement.Base.x += 100 # un metro - - '''old version - common1 = shape.common(self.Rack.Shape) - if common1.Area >= area: - raise - else: - # ajuste fino hasta encontrar el primer sitio: - self.Rack.Placement.Base.x += 500 # un metro - del common1 - ''' - # ajuste fino hasta encontrar el primer sitio: - rec.Placement.Base.y += 100 - del common - except: - pass - #print("Found") - - FreeCAD.ActiveDocument.removeObject(rec.Name) - - from datetime import datetime - starttime = datetime.now() - - if self.valueOrientation.currentIndex() == 0: - # Código para crear filas: - self.Rack.Placement.Base.x = self.Terrain.Shape.BoundBox.XMin - i = 1 - yy = self.Rack.Placement.Base.y - while yy < self.Terrain.Shape.BoundBox.YMax: - CreateRow1(self.Rack.Placement.Base.x, yy, self.Rack, self.Terrain, DistColls, area, i) - i += 1 - yy += DistRows - elif self.valueOrientation.currentIndex() == 2: - # Código para crear columnas: - while self.Rack.Placement.Base.x > self.Terrain.Shape.BoundBox.XMin: - self.Rack.Placement.Base.x -= DistColls - else: - xx = self.Rack.Placement.Base.x - while xx < self.Terrain.Shape.BoundBox.XMax: - CreateGrid(xx, self.Rack.Placement.Base.y, self.Rack, self.Terrain, DistRows, area) - xx += DistColls - - FreeCAD.activeDocument().recompute() - print("Everything OK (", datetime.now() - starttime, ")") - - -# Alinear solo filas. las columnas donde se pueda -def CreateRow(XX, YY, rack, land, gap, area, rowNumber): - import Draft - rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None) - rackClone.Label = 'rackClone{a}'.format(a=rowNumber) - rackClone.Placement.Base.x = XX - rackClone.Placement.Base.y = YY - - rec = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height, face=True, support=None) - rec.Placement.Base.x = land.Shape.BoundBox.XMin - rec.Placement.Base.y = YY - FreeCAD.activeDocument().recompute() - - common = land.Shape.common(rec.Shape) - for shape in common.Faces: - if shape.Area >= area: - rackClone.Placement.Base.x = shape.BoundBox.XMin - rackClone.Placement.Base.y = shape.BoundBox.YMin - while rackClone.Shape.BoundBox.XMax <= shape.BoundBox.XMax: - common1 = shape.common(rackClone.Shape) - if common1.Area >= area: - tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, placement=rackClone.Placement, - face=True, support=None) - tmp.Label = 'R{:03}-000'.format(rowNumber) - rackClone.Placement.Base.x += gap - else: - # ajuste fino hasta encontrar el primer sitio: - rackClone.Placement.Base.x += 500 # un metro - del common1 - del common - FreeCAD.ActiveDocument.removeObject(rackClone.Name) - FreeCAD.ActiveDocument.removeObject(rec.Name) - - -# Alinear solo filas. las columnas donde se pueda -def CreateRow1(XX, YY, rack, land, gap, area, rowNumber): - import Draft - rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None) - rackClone.Label = 'rackClone{a}'.format(a=rowNumber) - rackClone.Placement.Base.x = XX - rackClone.Placement.Base.y = YY - - rec = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height, face=True, support=None) - rec.Placement.Base.x = land.Shape.BoundBox.XMin - rec.Placement.Base.y = YY - FreeCAD.activeDocument().recompute() - - common = land.Shape.common(rec.Shape) - for shape in common.Faces: - if shape.Area >= area: - if False: - minorPoint = FreeCAD.Vector(0, 0, 0) - for spoint in shape.OuterWire.Vertexes: - if minorPoint.y >= spoint.Point.y: - if minorPoint.x >= spoint.x: - minorPoint = spoint - rackClone.Placement.Base = spoint - else: - # más rápido - rackClone.Placement.Base.x = shape.BoundBox.XMin - rackClone.Placement.Base.y = shape.BoundBox.YMin - - while rackClone.Shape.BoundBox.XMax <= shape.BoundBox.XMax: - verts = [v.Point for v in rackClone.Shape.OuterWire.OrderedVertexes] - inside = True - for vert in verts: - if not shape.isInside(vert, 0, True): - inside = False - break - if inside: - #tmp = rack.Shape.copy() - #tmp.Placement = rack.Placement - tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, placement=rackClone.Placement, - face=True, support=None) - tmp.Label = 'R{:03}-000'.format(rowNumber) - - rackClone.Placement.Base.x += gap - else: - # ajuste fino hasta encontrar el primer sitio: - rackClone.Placement.Base.x += 500 # un metro - del common - FreeCAD.ActiveDocument.removeObject(rackClone.Name) - FreeCAD.ActiveDocument.removeObject(rec.Name) - - -# Alinear columna y fila (grid perfecta) -def CreateGrid(XX, YY, rack, land, gap, area): - print("CreateGrid") - import Draft - rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None) - rackClone.Label = 'rackClone{a}'.format(a=XX) - rackClone.Placement.Base.x = XX - rackClone.Placement.Base.y = YY - - # if False: - while rackClone.Shape.BoundBox.YMax < land.Shape.BoundBox.YMax: - common = land.Shape.common(rackClone.Shape) - - if common.Area >= area: - tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, - placement=rackClone.Placement, face=True, support=None) - tmp.Label = 'rackClone{a}'.format(a=XX) - rackClone.Placement.Base.y += gap - # else: - # # ajuste fino hasta encontrar el primer sitio: - # rackClone.Placement.Base.y += 1000 - FreeCAD.ActiveDocument.removeObject(rackClone.Name) - - -# Alinear solo filas. las columnas donde se pueda -def CreateCol(XX, YY, rack, land, gap, area): - import Draft - rackClone = Draft.makeRectangle(length=rack.Length, height=rack.Height, face=True, support=None) - rackClone.Label = 'rackClone{a}'.format(a=XX) - rackClone.Placement.Base.x = XX - rackClone.Placement.Base.y = YY - - while rackClone.Shape.BoundBox.YMax < land.Shape.BoundBox.YMax: - common = land.Shape.common(rackClone.Shape) - - if common.Area >= area: - tmp = Draft.makeRectangle(length=rack.Length, height=rack.Height, - placement=rackClone.Placement, face=True, support=None) - tmp.Label = 'rackClone{a}'.format(a=XX) - rackClone.Placement.Base.y += gap - else: - # ajuste fino hasta encontrar el primer sitio: - rackClone.Placement.Base.y += 100 - - FreeCAD.ActiveDocument.removeObject(rackClone.Name) - - -# TODO: Probar a usar hilos: -class _CreateCol(threading.Thread): - def __init__(self, args=()): - super().__init__() - self.XX = args[0] - self.YY = args[1] - self.rack = args[2] - self.land = args[3] - self.gap = args[4] - self.area = args[5] - - def run(self): - import Draft - # rackClone = Draft.makeRectangle(length=land.Shape.BoundBox.XLength, height=rack.Height, - # face=True, support=None) - # rackClone = FreeCAD.activeDocument().addObject('Part::Feature') - # rackClone.Shape = self.rack.Shape - - rackClone = Draft.makeRectangle(length=self.rack.Length, height=self.rack.Height, face=True, support=None) - rackClone.Label = 'rackClone{a}'.format(a=self.XX) - rackClone.Placement.Base.x = self.XX - rackClone.Placement.Base.y = self.YY - - # if False: - while rackClone.Shape.BoundBox.YMax < self.land.Shape.BoundBox.YMax: - common = self.land.Shape.common(rackClone.Shape) - - if common.Area >= self.area: - rack = Draft.makeRectangle(length=self.rack.Length, height=self.rack.Height, - placement=rackClone.Placement, face=True, support=None) - rack.Label = 'rackClone{a}'.format(a=self.XX) - rackClone.Placement.Base.y += self.gap - # else: - # # ajuste fino hasta encontrar el primer sitio: - # rackClone.Placement.Base.y += 1000 - - # FreeCAD.ActiveDocument.removeObject(rackClone.Name) - - -if FreeCAD.GuiUp: - FreeCADGui.addCommand('PVPlantPlacement', _CommandPVPlantPlacement()) diff --git a/PVPlantEarthWorks.py b/PVPlantEarthWorks.py deleted file mode 100644 index 04f1880..0000000 --- a/PVPlantEarthWorks.py +++ /dev/null @@ -1,945 +0,0 @@ -import math - -import FreeCAD -import Part -import ArchComponent -from pivy import coin -import numpy as np -import DraftGeomUtils - -if FreeCAD.GuiUp: - import FreeCADGui, os - from PySide import QtCore, QtGui - from PySide.QtCore import QT_TRANSLATE_NOOP -else: - # \cond - def translate(ctxt, txt): - return txt - - def QT_TRANSLATE_NOOP(ctxt, txt): - return txt - # \endcond - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - def _fromUtf8(s): - return s - -import PVPlantResources -from PVPlantResources import DirIcons as DirIcons - -voltype = ["Fill", "Cut"] - - -def makeEarthWorksVolume(vtype = 0): - obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", voltype[vtype]) - EarthWorksVolume(obj) - ViewProviderEarthWorksVolume(obj.ViewObject) - return obj - - -class EarthWorksVolume(ArchComponent.Component): - def __init__(self, obj): - # Definición de Variables: - ArchComponent.Component.__init__(self, obj) - self.obj = obj - self.setProperties(obj) - - def setProperties(self, obj): - # Definicion de Propiedades: - pl = obj.PropertiesList - - if not ("VolumeType" in pl): - obj.addProperty("App::PropertyEnumeration", - "VolumeType", - "Volume", - "Connection").VolumeType = voltype - - if not ("SurfaceSlope" in pl): - obj.addProperty("App::PropertyPercent", - "SurfaceSlope", - "Volume", - "Connection").SurfaceSlope = 2 - - if not ("VolumeMesh" in pl): - obj.addProperty("Mesh::PropertyMeshKernel", - "VolumeMesh", - "Volume", - "Volume") - obj.setEditorMode("VolumeMesh", 2) - - if not ("Volume" in pl): - obj.addProperty("App::PropertyVolume", - "Volume", - "Volume", - "Volume") - obj.setEditorMode("Volume", 1) - - obj.Proxy = self - obj.IfcType = "Civil Element" - obj.setEditorMode("IfcType", 1) - obj.Proxy = self - - def onDocumentRestored(self, obj): - ArchComponent.Component.onDocumentRestored(self, obj) - self.setProperties(obj) - - def onChange(self, obj, prop): - if prop == "VolumeMesh": - if obj.VolumeMesh: - obj.VolumeMesh = obj.VolumeMesh.Volume - - def execute(self, obj): - ''' ''' - pass - - -class ViewProviderEarthWorksVolume: - "A View Provider for the Pipe object" - - def __init__(self, vobj): - ''' Set view properties. ''' - pl = vobj.PropertiesList - - (r, g, b) = (1.0, 0.0, 0.0) if vobj.Object.VolumeType == "Cut" else (0.0, 0.0, 1.0) - - # Triangulation properties. - if not "Transparency" in pl: - vobj.addProperty("App::PropertyIntegerConstraint", - "Transparency", "Surface Style", - "Set triangle face transparency") - vobj.Transparency = (50, 0, 100, 1) - - if not "ShapeColor" in pl: - vobj.addProperty("App::PropertyColor", - "ShapeColor", - "Surface Style", - "Set triangle face color") - vobj.ShapeColor = (r, g, b, vobj.Transparency / 100) - - if not "ShapeMaterial" in pl: - vobj.addProperty("App::PropertyMaterial", - "ShapeMaterial", "Surface Style", - "Triangle face material") - vobj.ShapeMaterial = FreeCAD.Material() - - if not "LineTransparency" in pl: - vobj.addProperty("App::PropertyIntegerConstraint", - "LineTransparency", "Surface Style", - "Set triangle edge transparency") - vobj.LineTransparency = (50, 0, 100, 1) - - if not "LineColor" in pl: - vobj.addProperty("App::PropertyColor", - "LineColor", "Surface Style", - "Set triangle face color") - vobj.LineColor = (0.5, 0.5, 0.5, vobj.LineTransparency / 100) - - '''vobj.addProperty( - "App::PropertyMaterial", "LineMaterial", "Surface Style", - "Triangle face material").LineMaterial = FreeCAD.Material() - - vobj.addProperty( - "App::PropertyFloatConstraint", "LineWidth", "Surface Style", - "Set triangle edge line width").LineWidth = (0.0, 1.0, 20.0, 1.0) - - # Boundary properties. - vobj.addProperty( - "App::PropertyColor", "BoundaryColor", "Boundary Style", - "Set boundary contour color").BoundaryColor = (0.0, 0.75, 1.0, 0.0) - - vobj.addProperty( - "App::PropertyFloatConstraint", "BoundaryWidth", "Boundary Style", - "Set boundary contour line width").BoundaryWidth = (3.0, 1.0, 20.0, 1.0) - - vobj.addProperty( - "App::PropertyEnumeration", "BoundaryPattern", "Boundary Style", - "Set a line pattern for boundary").BoundaryPattern = [*line_patterns] - - vobj.addProperty( - "App::PropertyIntegerConstraint", "PatternScale", "Boundary Style", - "Scale the line pattern").PatternScale = (3, 1, 20, 1) - - # Contour properties. - vobj.addProperty( - "App::PropertyColor", "MajorColor", "Contour Style", - "Set major contour color").MajorColor = (1.0, 0.0, 0.0, 0.0) - - vobj.addProperty( - "App::PropertyFloatConstraint", "MajorWidth", "Contour Style", - "Set major contour line width").MajorWidth = (4.0, 1.0, 20.0, 1.0) - - vobj.addProperty( - "App::PropertyColor", "MinorColor", "Contour Style", - "Set minor contour color").MinorColor = (1.0, 1.0, 0.0, 0.0) - - vobj.addProperty( - "App::PropertyFloatConstraint", "MinorWidth", "Contour Style", - "Set major contour line width").MinorWidth = (2.0, 1.0, 20.0, 1.0) -''' - vobj.Proxy = self - vobj.ShapeMaterial.DiffuseColor = vobj.ShapeColor - - def onChanged(self, vobj, prop): - ''' - Update Object visuals when a view property changed. - ''' - if prop == "ShapeColor" or prop == "Transparency": - if hasattr(vobj, "ShapeColor") and hasattr(vobj, "Transparency"): - color = vobj.getPropertyByName("ShapeColor") - transparency = vobj.getPropertyByName("Transparency") - color = (color[0], color[1], color[2], transparency / 100) - vobj.ShapeMaterial.DiffuseColor = color - - if prop == "ShapeMaterial": - if hasattr(vobj, "ShapeMaterial"): - material = vobj.getPropertyByName("ShapeMaterial") - self.face_material.diffuseColor.setValue(material.DiffuseColor[:3]) - self.face_material.transparency = material.DiffuseColor[3] - - if prop == "LineColor" or prop == "LineTransparency": - if hasattr(vobj, "LineColor") and hasattr(vobj, "LineTransparency"): - color = vobj.getPropertyByName("LineColor") - transparency = vobj.getPropertyByName("LineTransparency") - color = (color[0], color[1], color[2], transparency / 100) - vobj.LineMaterial.DiffuseColor = color - - if prop == "LineMaterial": - material = vobj.getPropertyByName(prop) - self.edge_material.diffuseColor.setValue(material.DiffuseColor[:3]) - self.edge_material.transparency = material.DiffuseColor[3] - - if prop == "LineWidth": - width = vobj.getPropertyByName(prop) - self.edge_style.lineWidth = width - - if prop == "BoundaryColor": - color = vobj.getPropertyByName(prop) - self.boundary_color.rgb = color[:3] - - if prop == "BoundaryWidth": - width = vobj.getPropertyByName(prop) - self.boundary_style.lineWidth = width - - if prop == "BoundaryPattern": - if hasattr(vobj, "BoundaryPattern"): - pattern = vobj.getPropertyByName(prop) - self.boundary_style.linePattern = line_patterns[pattern] - - if prop == "PatternScale": - if hasattr(vobj, "PatternScale"): - scale = vobj.getPropertyByName(prop) - self.boundary_style.linePatternScaleFactor = scale - - if prop == "MajorColor": - color = vobj.getPropertyByName(prop) - self.major_color.rgb = color[:3] - - if prop == "MajorWidth": - width = vobj.getPropertyByName(prop) - self.major_style.lineWidth = width - - if prop == "MinorColor": - color = vobj.getPropertyByName(prop) - self.minor_color.rgb = color[:3] - - if prop == "MinorWidth": - width = vobj.getPropertyByName(prop) - self.minor_style.lineWidth = width - - - def attach(self, vobj): - ''' - Create Object visuals in 3D view. - ''' - # GeoCoords Node. - self.geo_coords = coin.SoGeoCoordinate() - - # Surface features. - self.triangles = coin.SoIndexedFaceSet() - self.face_material = coin.SoMaterial() - self.edge_material = coin.SoMaterial() - self.edge_color = coin.SoBaseColor() - self.edge_style = coin.SoDrawStyle() - self.edge_style.style = coin.SoDrawStyle.LINES - - shape_hints = coin.SoShapeHints() - shape_hints.vertex_ordering = coin.SoShapeHints.COUNTERCLOCKWISE - mat_binding = coin.SoMaterialBinding() - mat_binding.value = coin.SoMaterialBinding.PER_FACE - offset = coin.SoPolygonOffset() - offset.styles = coin.SoPolygonOffset.LINES - offset.factor = -2.0 - - # Boundary features. - self.boundary_color = coin.SoBaseColor() - self.boundary_coords = coin.SoGeoCoordinate() - self.boundary_lines = coin.SoLineSet() - self.boundary_style = coin.SoDrawStyle() - self.boundary_style.style = coin.SoDrawStyle.LINES - - # Boundary root. - boundaries = coin.SoType.fromName('SoFCSelection').createInstance() - boundaries.style = 'EMISSIVE_DIFFUSE' - boundaries.addChild(self.boundary_color) - boundaries.addChild(self.boundary_style) - boundaries.addChild(self.boundary_coords) - boundaries.addChild(self.boundary_lines) - - # Major Contour features. - self.major_color = coin.SoBaseColor() - self.major_coords = coin.SoGeoCoordinate() - self.major_lines = coin.SoLineSet() - self.major_style = coin.SoDrawStyle() - self.major_style.style = coin.SoDrawStyle.LINES - - # Major Contour root. - major_contours = coin.SoSeparator() - major_contours.addChild(self.major_color) - major_contours.addChild(self.major_style) - major_contours.addChild(self.major_coords) - major_contours.addChild(self.major_lines) - - # Minor Contour features. - self.minor_color = coin.SoBaseColor() - self.minor_coords = coin.SoGeoCoordinate() - self.minor_lines = coin.SoLineSet() - self.minor_style = coin.SoDrawStyle() - self.minor_style.style = coin.SoDrawStyle.LINES - - # Minor Contour root. - minor_contours = coin.SoSeparator() - minor_contours.addChild(self.minor_color) - minor_contours.addChild(self.minor_style) - minor_contours.addChild(self.minor_coords) - minor_contours.addChild(self.minor_lines) - - # Highlight for selection. - highlight = coin.SoType.fromName('SoFCSelection').createInstance() - highlight.style = 'EMISSIVE_DIFFUSE' - highlight.addChild(shape_hints) - highlight.addChild(mat_binding) - highlight.addChild(self.geo_coords) - highlight.addChild(self.triangles) - highlight.addChild(boundaries) - - # Face root. - face = coin.SoSeparator() - face.addChild(self.face_material) - face.addChild(highlight) - - # Edge root. - edge = coin.SoSeparator() - edge.addChild(self.edge_material) - edge.addChild(self.edge_style) - edge.addChild(highlight) - - # Surface root. - surface_root = coin.SoSeparator() - surface_root.addChild(face) - surface_root.addChild(offset) - surface_root.addChild(edge) - surface_root.addChild(major_contours) - surface_root.addChild(minor_contours) - vobj.addDisplayMode(surface_root, "Surface") - - # Boundary root. - boundary_root = coin.SoSeparator() - boundary_root.addChild(boundaries) - vobj.addDisplayMode(boundary_root, "Boundary") - - # Elevation/Shaded root. - shaded_root = coin.SoSeparator() - shaded_root.addChild(face) - vobj.addDisplayMode(shaded_root, "Elevation") - vobj.addDisplayMode(shaded_root, "Slope") - vobj.addDisplayMode(shaded_root, "Shaded") - - # Flat Lines root. - flatlines_root = coin.SoSeparator() - flatlines_root.addChild(face) - flatlines_root.addChild(offset) - flatlines_root.addChild(edge) - vobj.addDisplayMode(flatlines_root, "Flat Lines") - - # Wireframe root. - wireframe_root = coin.SoSeparator() - wireframe_root.addChild(edge) - wireframe_root.addChild(major_contours) - wireframe_root.addChild(minor_contours) - vobj.addDisplayMode(wireframe_root, "Wireframe") - - # Take features from properties. - self.onChanged(vobj, "ShapeColor") - self.onChanged(vobj, "LineColor") - self.onChanged(vobj, "LineWidth") - '''self.onChanged(vobj, "BoundaryColor") - self.onChanged(vobj, "BoundaryWidth") - self.onChanged(vobj, "BoundaryPattern") - self.onChanged(vobj, "PatternScale") - self.onChanged(vobj, "MajorColor") - self.onChanged(vobj, "MajorWidth") - self.onChanged(vobj, "MinorColor") - self.onChanged(vobj, "MinorWidth")''' - - - def updateData(self, obj, prop): - ''' - Update Object visuals when a data property changed. - ''' - - # Set System. - geo_system = ["UTM", FreeCAD.ActiveDocument.Site.UtmZone, "FLAT"] - self.geo_coords.geoSystem.setValues(geo_system) - self.boundary_coords.geoSystem.setValues(geo_system) - self.major_coords.geoSystem.setValues(geo_system) - self.minor_coords.geoSystem.setValues(geo_system) - - if prop == "VolumeMesh": - mesh = obj.VolumeMesh - copy_mesh = mesh.copy() - #copy_mesh.Placement.move(origin.Origin) - - triangles = [] - for i in copy_mesh.Topology[1]: - triangles.extend(list(i)) - triangles.append(-1) - - self.geo_coords.point.values = copy_mesh.Topology[0] - self.triangles.coordIndex.values = triangles - del copy_mesh - - '''if prop == "ContourShapes": - contour_shape = obj.getPropertyByName(prop) - - if contour_shape.SubShapes: - major_shape = contour_shape.SubShapes[0] - points, vertices = self.wire_view(major_shape, origin.Origin) - - self.major_coords.point.values = points - self.major_lines.numVertices.values = vertices - - minor_shape = contour_shape.SubShapes[1] - points, vertices = self.wire_view(minor_shape, origin.Origin) - - self.minor_coords.point.values = points - self.minor_lines.numVertices.values = vertices - - if prop == "BoundaryShapes": - boundary_shape = obj.getPropertyByName(prop) - points, vertices = self.wire_view(boundary_shape, origin.Origin, True) - - self.boundary_coords.point.values = points - self.boundary_lines.numVertices.values = vertices - - if prop == "AnalysisType" or prop == "Ranges": - analysis_type = obj.getPropertyByName("AnalysisType") - ranges = obj.getPropertyByName("Ranges") - - if analysis_type == "Default": - if hasattr(obj.ViewObject, "ShapeMaterial"): - material = obj.ViewObject.ShapeMaterial - self.face_material.diffuseColor = material.DiffuseColor[:3] - - if analysis_type == "Elevation": - colorlist = self.elevation_analysis(obj.Mesh, ranges) - self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist) - - elif analysis_type == "Slope": - colorlist = self.slope_analysis(obj.Mesh, ranges) - self.face_material.diffuseColor.setValues(0, len(colorlist), colorlist) - ''' - def getIcon(self): - """ Return the path to the appropriate icon. """ - return str(os.path.join(DirIcons, "solar-fixed.svg")) - - def getDisplayModes(self, vobj): - ''' - Return a list of display modes. - ''' - modes = ["Surface", "Boundary", "Flat Lines", "Shaded", "Wireframe"] - - return modes - - def getDefaultDisplayMode(self): - ''' - Return the name of the default display mode. - ''' - - return "Surface" - - def setDisplayMode(self, mode): - ''' - Map the display mode defined in attach with - those defined in getDisplayModes. - ''' - return mode - - def __getstate__(self): - """ - Save variables to file. - """ - return None - - def __setstate__(self, state): - """ - Get variables from file. - """ - return None - - -class EarthWorksTaskPanel: - def __init__(self): - self.To = None - - # self.form: - self.form = FreeCADGui.PySideUic.loadUi(os.path.join(PVPlantResources.__dir__, "PVPlantEarthworks.ui")) - self.form.setWindowIcon(QtGui.QIcon(os.path.join(PVPlantResources.DirIcons, "convert.svg"))) - - def accept(self): - from datetime import datetime - starttime = datetime.now() - - import MeshPart as mp - land = FreeCAD.ActiveDocument.Terrain.Mesh.copy() - frames = [] - for obj in FreeCADGui.Selection.getSelection(): - if hasattr(obj, "Proxy"): - if obj.Proxy.Type == "Tracker": - if not (obj in frames): - frames.append(obj) - elif obj.Proxy.Type == "FrameArea": - for fr in obj.Frames: - if not (fr in frames): - frames.append(fr) - if len(frames) == 0: - return False - - FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras") - - def calculateEarthWorks(line, extreme=False): - pts = [] - pts1 = [] - line1 = line.copy() - angles = line.Placement.Rotation.toEulerAngles("XYZ") - line1.Placement.Rotation.setEulerAngles("XYZ", 0, 0, angles[2]) - line1.Placement.Base.z = 0 - pro = mp.projectShapeOnMesh(line1, land, FreeCAD.Vector(0, 0, 1)) - flat = [] - for points in pro: - flat.extend(points) - pro = Part.makePolygon(flat) - points = pro.discretize(Distance=500) - - for point in points: - ver = Part.Vertex(point) - dist = ver.distToShape(line) - linepoint = dist[1][0][1] - - if not extreme: - if self.form.groupTolerances.isChecked(): - if linepoint.z > point.z: - if linepoint.sub(point).Length > self.form.editToleranceCut.value(): - pts.append(linepoint) - elif linepoint.z < point.z: - if linepoint.sub(point).Length > self.form.editToleranceFill.value(): - pts1.append(linepoint) - else: - if linepoint.z > point.z: - pts.append(linepoint) - elif linepoint.z < point.z: - pts1.append(linepoint) - #pts.append(linepoint) - else: - if linepoint.z > point.z: - if linepoint.sub(point).Length > 200: - pts.append(linepoint) - - return pts, pts1 - - tools = [[],[]] - ver = 2 - if ver == 0: - frames = sorted(frames, key=lambda x: (x.Placement.Base.x, x.Placement.Base.y)) - for frame in frames: - length = frame.Setup.Length.Value / 2 - p1 = FreeCAD.Vector(-length, 0, 0) - p2 = FreeCAD.Vector(length, 0, 0) - line = Part.LineSegment(p1, p2).toShape() - line.Placement = frame.Placement.copy() - line.Placement.Base.x = frame.Shape.BoundBox.XMin - step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2 - for n in range(3): - ret = calculateEarthWorks(line, n % 2) - tools[0].extend(ret[0]) - tools[1].extend(ret[1]) - line.Placement.Base.x += step - elif ver == 1: - from PVPlantPlacement import getCols - columns = getCols(frames) - for groups in columns: - for group in groups: - first = group[0] - last = group[-1] - for frame in group: - length = frame.Setup.Length.Value / 2 - p1 = FreeCAD.Vector(-(length + (self.form.editOffset.value() if frame == first else -1000)), - 0, 0) - p2 = FreeCAD.Vector(length + (self.form.editOffset.value() if frame == last else -1000), - 0, 0) - line = Part.LineSegment(p1, p2).toShape() - line.Placement = frame.Placement.copy() - line.Placement.Base.x = frame.Shape.BoundBox.XMin - step = (frame.Shape.BoundBox.XMax - frame.Shape.BoundBox.XMin) / 2 - for n in range(3): - ret = calculateEarthWorks(line, n % 2 == 1) - tools[0].extend(ret[0]) - tools[1].extend(ret[1]) - line.Placement.Base.x += step - elif ver == 2: - print("versión 2") - import PVPlantPlacement - rows, columns = PVPlantPlacement.getRows(frames) - if (rows is None) or (columns is None): - print("Nada que procesar") - return False - tools = [] - lofts = [] - for group in rows: - lines = [] - cont = 0 - while cont < len(group): - aw = 0 - if cont > 0: - p0 = FreeCAD.Vector(group[cont - 1].Placement.Base) - p1 = FreeCAD.Vector(group[cont].Placement.Base) - aw = getAngle(p0, p1) - - ae = 0 - if cont < (len(group) - 1): - p1 = FreeCAD.Vector(group[cont].Placement.Base) - p2 = FreeCAD.Vector(group[cont + 1].Placement.Base) - ae = getAngle(p1, p2) - - lng = int(group[cont].Setup.Length / 2) - wdt = int(group[cont].Setup.Width / 2) - line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0), - FreeCAD.Vector(lng, 0, 0)).toShape() - - line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0), - FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape() - - anf = (aw + ae) / 2 - if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value: - anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value - zz = wdt * math.sin(math.radians(anf)) - - li = line.copy() - li.Placement = group[cont].Placement - li.Placement.Rotation = group[cont].Placement.Rotation - li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0)) - li.Placement.Base.z -= zz - lines.append(li) - - ld = line.copy() - ld.Placement = group[cont].Placement - ld.Placement.Rotation = group[cont].Placement.Rotation - ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0)) - ld.Placement.Base.z += zz - lines.append(ld) - tools.append([group[cont], li, ld]) - cont += 1 - loft = Part.makeLoft(lines, False, True, False) - lofts.append(loft) - - for group in rows: - lines = [] - for frame in group: - col, idx = searchFrameInColumns(frame, columns) - tool = searchTool(frame, tools) - if idx == 0: - ''' ''' - - if idx == (len(col) - 1): - ''' ''' - - if (idx + 1) < len(col): - frame1 = col[idx + 1] - tool1 = searchTool(frame1, tools) - line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape() - lines.append(line) - line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape() - lines.append(line) - - if len(lines) > 0: - loft = Part.makeLoft(lines, False, True, False) - lofts.append(loft) - - faces = [] - for loft in lofts: - faces.extend(loft.Faces) - sh = Part.makeShell(faces) - import Utils.PVPlantUtils as utils - import Mesh - pro = utils.getProjected(sh) - pro = utils.simplifyWire(pro) - pts = [ver.Point for ver in pro.Vertexes] - land.trim(pts, 1) - - tmp = [] - shp = Part.Shape() - for face in sh.Faces: - wire = face.Wires[0].copy() - pl = wire.Placement.Base - wire.Placement.Base = wire.Placement.Base - pl - - if DraftGeomUtils.isPlanar(wire): - # Caso simple - wire = wire.makeOffset2D(10000, 0, False, False, True) - wire.Placement.Base.z = wire.Placement.Base.z - 10000 - top = wire.makeOffset2D(1, 0, False, False, True) - loft = Part.makeLoft([top, wire], True, True, False) - tmp.append(loft) - shp = shp.fuse(loft) - else: - # Caso complejo: - vertices = face.Vertexes - # Dividir rectángulo en 2 triángulos - triangles = [ - [vertices[0], vertices[1], vertices[2]], - [vertices[2], vertices[3], vertices[0]] - ] - - for tri in triangles: - # Crear wire triangular - wire = Part.makePolygon([v.Point for v in tri] + [tri[0].Point]) - wire = wire.makeOffset2D(10000, 0, False, False, True) - wire.Placement.Base.z = wire.Placement.Base.z - 10000 - top = wire.makeOffset2D(1, 0, False, False, True) - loft = Part.makeLoft([top, wire], True, True, False) - tmp.append(loft) - shp = shp.fuse(loft) - - final_tool = Part.makeCompound(tmp) - Part.show(final_tool, "tool") - Part.show(shp) - - FreeCAD.ActiveDocument.commitTransaction() - self.closeForm() - return True - - import MeshTools.Triangulation as TriangulateMesh - import MeshTools.MeshGetBoundary as mgb - import Mesh - - for ind, points in enumerate(tools): - mesh = TriangulateMesh.Triangulate(points, MaxlengthLE=3000, MaxAngleLE=math.radians(100)) - if mesh: - for mesh in mesh.getSeparateComponents(): - boundary = mgb.get_boundary(mesh) - Part.show(boundary) - '''if self.form.editOffset.value() != 0: - import Utils.PVPlantUtils as utils - pro = utils.getProjected(boundary) - pro = pro.makeOffset2D(self.form.editOffset.value(), 0, False, False, True) - # TODO: paso intermedio de restar las areas prohibidas - pro = mp.projectShapeOnMesh(pro, land, FreeCAD.Vector(0, 0, 1)) - cnt = 0 - for lp in pro: - cnt += len(lp) - # points.extend(boundary.Wires[0].discretize(Number=cnt)) - points = boundary.Wires[0].discretize(Distance=cnt) - for lp in pro: - points.extend(lp) - mesh1 = TriangulateMesh.Triangulate(points, MaxlengthLE=5000) # , MaxAngleLE=math.pi / 1.334) - import Mesh - Mesh.show(mesh1) - boundary = Part.makeCompound([]) - for section in pro: - if len(section) > 0: - try: - boundary.add(Part.makePolygon(section)) - except Part.OCCError: - pass - Part.show(boundary)''' - #mesh.smooth("Laplace", 3) - #Mesh.show(mesh) - #Part.show(boundary) - vol = makeEarthWorksVolume(ind) - vol.VolumeMesh = mesh.copy() - if ind == 0: - ''' put inside fills group ''' - else: - ''' put inside fills group ''' - - FreeCAD.ActiveDocument.commitTransaction() - self.closeForm() - return True - - def reject(self): - self.closeForm() - return True - - def closeForm(self): - FreeCADGui.Control.closeDialog() - - -def getAngle(vec1, vec2): - dX = vec2.x - vec1.x - dZ = vec2.z - vec1.z - return math.degrees(math.atan2(float(dZ), float(dX))) - - -def searchFrameInColumns(obj, columns): - for colidx, col in enumerate(columns): - for group in col: - if obj in group: - return group, group.index(obj) #groupidx - - -def searchTool(obj, tools): - for tool in tools: - if obj in tool: - return tool - - -'''class _CommandCalculateEarthworks: - - def GetResources(self): - return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "pico.svg")), - 'Accel': "C, E", - 'MenuText': QT_TRANSLATE_NOOP("Placement", "Movimiento de tierras"), - 'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el movimiento de tierras")} - - def Activated(self): - TaskPanel = _EarthWorksTaskPanel() - FreeCADGui.Control.showDialog(TaskPanel) - - def IsActive(self): - active = not (FreeCAD.ActiveDocument is None) - if not (FreeCAD.ActiveDocument.getObject("Terrain") is None): - active = active and not (FreeCAD.ActiveDocument.getObject("Terrain").Mesh is None) - return active - -if FreeCAD.GuiUp: - FreeCADGui.addCommand('PVPlantEarthworks', _CommandCalculateEarthworks())''' - - -def accept(): - import MeshPart as mp - land = FreeCAD.ActiveDocument.Terrain.Mesh - frames = [] - for obj in FreeCADGui.Selection.getSelection(): - if hasattr(obj, "Proxy"): - if obj.Proxy.Type == "Tracker": - if not (obj in frames): - frames.append(obj) - elif obj.Proxy.Type == "FrameArea": - for fr in obj.Frames: - if not (fr in frames): - frames.append(fr) - if len(frames) == 0: - return False - - FreeCAD.ActiveDocument.openTransaction("Calcular movimiento de tierras") - import PVPlantPlacement - rows, columns = PVPlantPlacement.getRows(frames) - if (rows is None) or (columns is None): - print("Nada que procesar") - return False - tools = [] - - for group in rows: - lines = [] - cont = 0 - while cont < len(group): - aw = 0 - if cont > 0: - p0 = FreeCAD.Vector(group[cont - 1].Placement.Base) - p1 = FreeCAD.Vector(group[cont].Placement.Base) - aw = getAngle(p0, p1) - - ae = 0 - if cont < (len(group) - 1): - p1 = FreeCAD.Vector(group[cont].Placement.Base) - p2 = FreeCAD.Vector(group[cont + 1].Placement.Base) - ae = getAngle(p1, p2) - - lng = int(group[cont].Setup.Length / 2) - wdt = int(group[cont].Setup.Width / 2) - line = Part.LineSegment(FreeCAD.Vector(-lng, 0, 0), - FreeCAD.Vector(lng, 0, 0)).toShape() - - line = Part.LineSegment(FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[0].Placement.Base.x, 0, 0), - FreeCAD.Vector(group[cont].Setup.Shape.SubShapes[1].SubShapes[0].SubShapes[-1].Placement.Base.x, 0, 0)).toShape() - - anf = (aw + ae) / 2 - if anf > FreeCAD.ActiveDocument.MaximumWestEastSlope.Value: - anf = FreeCAD.ActiveDocument.MaximumWestEastSlope.Value - zz = wdt * math.sin(math.radians(anf)) - - li = line.copy() - li.Placement = group[cont].Placement - li.Placement.Rotation = group[cont].Placement.Rotation - li.Placement.Base.x -= wdt #+ (3000 if cont == 0 else 0)) - li.Placement.Base.z -= zz - lines.append(li) - - ld = line.copy() - ld.Placement = group[cont].Placement - ld.Placement.Rotation = group[cont].Placement.Rotation - ld.Placement.Base.x += wdt #+ (3000 if cont == len(group) - 1 else 0)) - ld.Placement.Base.z += zz - lines.append(ld) - tools.append([group[cont], li, ld]) - cont += 1 - - loft = Part.makeLoft(lines, False, True, False) - import MeshPart as mp - msh = mp.meshFromShape(Shape=loft) #, MaxLength=1) - #msh = msh.smooth("Laplace", 3) - import Mesh - Mesh.show(msh) - '''intersec = land.section(msh, MinDist=0.01) - import Draft - for sec in intersec: - Draft.makeWire(sec)''' - - for group in rows: - lines = [] - for frame in group: - col, idx = searchFrameInColumns(frame, columns) - tool = searchTool(frame, tools) - if idx == 0: - ''' ''' - if idx == (len(col) - 1): - ''' ''' - - if (idx + 1) < len(col): - frame1 = col[idx + 1] - tool1 = searchTool(frame1, tools) - line = Part.LineSegment(tool[1].Vertexes[1].Point, tool1[1].Vertexes[0].Point).toShape() - Part.show(line) - lines.append(line) - line = Part.LineSegment(tool[2].Vertexes[1].Point, tool1[2].Vertexes[0].Point).toShape() - Part.show(line) - lines.append(line) - - - if len(lines) > 0: - loft = Part.makeLoft(lines, False, True, False) - import MeshPart as mp - msh = mp.meshFromShape(Shape=loft) # , MaxLength=1) - #msh = msh.smooth("Laplace", 3) - import Mesh - Mesh.show(msh) - intersec = land.section(msh, MinDist=0.01) - import Draft - for sec in intersec: - Draft.makeWire(sec) - - FreeCAD.ActiveDocument.commitTransaction() - self.closeForm() - return True - diff --git a/Project/GenerateExternalDocument.py b/Project/GenerateExternalDocument.py index 3511cb4..42190c2 100644 --- a/Project/GenerateExternalDocument.py +++ b/Project/GenerateExternalDocument.py @@ -1,6 +1,6 @@ import FreeCAD import FreeCADGui -from PySide2 import QtWidgets +from PySide import QtWidgets import os if FreeCAD.GuiUp: diff --git a/Resources/Icons/home - copia.svg b/Resources/Icons/home - copia.svg deleted file mode 100644 index bc8f285..0000000 --- a/Resources/Icons/home - copia.svg +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/Icons/stringsetup - copia.svg b/Resources/Icons/stringsetup - copia.svg deleted file mode 100644 index c8b7a90..0000000 --- a/Resources/Icons/stringsetup - copia.svg +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - diff --git a/Resources/Icons/trench_old.svg b/Resources/Icons/trench_old.svg deleted file mode 100644 index 45aec36..0000000 --- a/Resources/Icons/trench_old.svg +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Road/Alignment.py b/Road/Alignment.py new file mode 100644 index 0000000..0c5daa6 --- /dev/null +++ b/Road/Alignment.py @@ -0,0 +1,233 @@ +# /********************************************************************** +# * * +# * Copyright (c) 2026 Javier Braña * +# * * +# * Alignment - Alineamiento horizontal y vertical * +# * * +# * Define el eje de la carretera mediante: * +# * - Alineamiento horizontal: polilínea + curvas circulares * +# * - Alineamiento vertical: rasante con pendientes y curvas verticales* +# * - Estaciones (progresivas) * +# * * +# *********************************************************************** + +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 de una carretera. + + Propiedades principales: + SourceWire : Polilínea base del eje + Stations : Progresivas (lista de distancias) + StationInterval : Intervalo entre estaciones + TotalLength : Longitud total del eje + HorizontalCurves : Curvas circulares (radio, longitud, parámetros) + VerticalPVI : Puntos de intersección vertical (PVI) para la rasante + """ + + def __init__(self, obj): + obj.Proxy = self + self.setProperties(obj) + self._cached_chainage = None + self._cached_station_points = None + self._cached_tangents = None + + def setProperties(self, obj): + pl = obj.PropertiesList + + if "SourceWire" not in pl: + obj.addProperty("App::PropertyLink", + "SourceWire", "Alignment", + "Polilínea base del eje") + + if "Stations" not in pl: + obj.addProperty("App::PropertyFloatList", + "Stations", "Alignment", + "Estaciones (progresivas) en mm") + + if "StationInterval" not in pl: + obj.addProperty("App::PropertyLength", + "StationInterval", "Alignment", + "Intervalo entre estaciones").StationInterval = 20000 + + if "NumberOfStations" not in pl: + obj.addProperty("App::PropertyInteger", + "NumberOfStations", "Alignment", + "Número de estaciones") + obj.setEditorMode("NumberOfStations", 1) + + if "TotalLength" not in pl: + obj.addProperty("App::PropertyLength", + "TotalLength", "Alignment", + "Longitud total del eje") + obj.setEditorMode("TotalLength", 1) + + if "HorizontalCurveRadii" not in pl: + obj.addProperty("App::PropertyFloatList", + "HorizontalCurveRadii", "Alignment", + "Radios de curva en cada vértice (0 = recta)") + + if "ShowStations" not in pl: + obj.addProperty("App::PropertyBool", + "ShowStations", "Alignment", + "Mostrar marcas de estación en 3D").ShowStations = False + + 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 + if wire.isNull(): + return + + total_len = wire.Length + obj.TotalLength = total_len + if total_len <= 0: + return + + interval = obj.StationInterval.Value + if interval <= 0: + interval = 20000 + + n_stations = max(2, int(total_len / interval) + 1) + obj.NumberOfStations = n_stations + + stations = np.linspace(0, total_len, n_stations).tolist() + obj.Stations = stations + + # Calcular radios de curva en cada vértice del wire + self._compute_curve_radii(obj, wire) + + # Cache + self._cached_chainage = stations + self._cached_station_points = None + self._cached_tangents = None + + def _compute_curve_radii(self, obj, wire): + """Calcula el radio de curvatura en cada vértice de la polilínea.""" + vertices = wire.Vertexes + n = len(vertices) + if n < 3: + obj.HorizontalCurveRadii = [] + return + + radii = [] + for i in range(1, n - 1): + p0 = vertices[i - 1].Point + p1 = vertices[i].Point + p2 = vertices[i + 1].Point + + v1 = p1 - p0 + v2 = p2 - p1 + cross = FreeCAD.Vector(0, 0, 1).dot(v1.cross(v2)) + + if abs(cross) < 1.0: # casi colineal + radii.append(0.0) + else: + # Radio aproximado = |v1| * |v2| / |v1 x v2| + r = v1.Length * v2.Length / abs(cross) + radii.append(round(r, 0)) + + obj.HorizontalCurveRadii = radii + + def get_station_point(self, obj, distance): + """Devuelve el punto 3D en el eje a una progresiva dada (mm).""" + if not obj.SourceWire: + return None + try: + wire = obj.SourceWire.Shape + param = wire.getParameterByLength(distance / obj.TotalLength) + return wire.valueAt(param) + except Exception: + return None + + def get_tangent_at(self, obj, distance): + """Devuelve el vector tangente en una progresiva (normalizado).""" + if not obj.SourceWire: + return None + try: + wire = obj.SourceWire.Shape + param = wire.getParameterByLength(distance / obj.TotalLength) + t = wire.tangentAt(param) + if t.Length > 0: + t.normalize() + return t + except Exception: + return None + + def get_perpendicular_at(self, obj, distance): + """Devuelve el vector perpendicular (horizontal) en una progresiva.""" + t = self.get_tangent_at(obj, distance) + if t is None: + return None + perp = FreeCAD.Vector(-t.y, t.x, 0) + perp.normalize() + return perp + + def get_station_data(self, obj): + """ + Devuelve arrays de (puntos, tangentes, perpendiculares) para todas las estaciones. + + Returns: + tuple: (points, tangents, perps) listas de FreeCAD.Vector + """ + stations = obj.Stations + if not stations: + return [], [], [] + + points = [] + tangents = [] + perps = [] + + for s in stations: + pt = self.get_station_point(obj, s) + tg = self.get_tangent_at(obj, s) + pp = self.get_perpendicular_at(obj, s) + if pt and tg and pp: + points.append(pt) + tangents.append(tg) + perps.append(pp) + + return points, tangents, perps + + 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 \ No newline at end of file diff --git a/Road/__init__.py b/Road/__init__.py new file mode 100644 index 0000000..12cd9ff --- /dev/null +++ b/Road/__init__.py @@ -0,0 +1,7 @@ +# Road Module - PVPlant Workbench +# Sistema de carreteras con alineamiento profesional + +from .Alignment import make_alignment_from_wire, Alignment +from .CrossSection import CrossSectionBuilder +from .Road import make_road, Road +from .CutFill import calculate_cut_fill \ No newline at end of file diff --git a/Utils/gordon_profile_FP - copia.py b/Utils/gordon_profile_FP - copia.py deleted file mode 100644 index d390026..0000000 --- a/Utils/gordon_profile_FP - copia.py +++ /dev/null @@ -1,348 +0,0 @@ -# -*- coding: utf-8 -*- - -__title__ = "Freehand BSpline" -__author__ = "Christophe Grellier (Chris_G)" -__license__ = "LGPL 2.1" -__doc__ = "Creates an freehand BSpline curve" -__usage__ = """*** Interpolation curve control keys : - - a - Select all / Deselect - i - Insert point in selected segments - t - Set / unset tangent (view direction) - p - Align selected objects - s - Snap points on shape / Unsnap - l - Set/unset a linear interpolation - x,y,z - Axis constraints during grab - q - Apply changes and quit editing""" - -import os - -import FreeCAD -import FreeCADGui -import Part - -from . import ICONPATH -from . import _utils -from . import profile_editor - -TOOL_ICON = os.path.join(ICONPATH, 'editableSpline.svg') -# debug = _utils.debug -debug = _utils.doNothing - - -def check_pivy(): - try: - profile_editor.MarkerOnShape([FreeCAD.Vector()]) - return True - except Exception as exc: - FreeCAD.Console.PrintWarning(str(exc) + "\nPivy interaction library failure\n") - return False - - -def midpoint(e): - p = e.FirstParameter + 0.5 * (e.LastParameter - e.FirstParameter) - return e.valueAt(p) - - -class GordonProfileFP: - """Creates an editable interpolation curve""" - def __init__(self, obj, s, d, t): - """Add the properties""" - obj.addProperty("App::PropertyLinkSubList", "Support", "Profile", "Constraint shapes").Support = s - obj.addProperty("App::PropertyFloatConstraint", "Parametrization", "Profile", "Parametrization factor") - obj.addProperty("App::PropertyFloat", "Tolerance", "Profile", "Tolerance").Tolerance = 1e-5 - obj.addProperty("App::PropertyBool", "Periodic", "Profile", "Periodic curve").Periodic = False - obj.addProperty("App::PropertyVectorList", "Data", "Profile", "Data list").Data = d - obj.addProperty("App::PropertyVectorList", "Tangents", "Profile", "Tangents list") - obj.addProperty("App::PropertyBoolList", "Flags", "Profile", "Tangent flags") - obj.addProperty("App::PropertyIntegerList", "DataType", "Profile", "Types of interpolated points").DataType = t - obj.addProperty("App::PropertyBoolList", "LinearSegments", "Profile", "Linear segment flags") - obj.Parametrization = (1.0, 0.0, 1.0, 0.05) - obj.Proxy = self - - def get_shapes(self, fp): - if hasattr(fp, 'Support'): - sl = list() - for ob, names in fp.Support: - for name in names: - if "Vertex" in name: - n = eval(name.lstrip("Vertex")) - if len(ob.Shape.Vertexes) >= n: - sl.append(ob.Shape.Vertexes[n - 1]) - elif ("Point" in name): - sl.append(Part.Vertex(ob.Shape.Point)) - elif ("Edge" in name): - n = eval(name.lstrip("Edge")) - if len(ob.Shape.Edges) >= n: - sl.append(ob.Shape.Edges[n - 1]) - elif ("Face" in name): - n = eval(name.lstrip("Face")) - if len(ob.Shape.Faces) >= n: - sl.append(ob.Shape.Faces[n - 1]) - return sl - - def get_points(self, fp, stretch=True): - touched = False - shapes = self.get_shapes(fp) - if not len(fp.Data) == len(fp.DataType): - FreeCAD.Console.PrintError("Gordon Profile : Data and DataType mismatch\n") - return(None) - pts = list() - shape_idx = 0 - for i in range(len(fp.Data)): - if fp.DataType[i] == 0: # Free point - pts.append(fp.Data[i]) - elif (fp.DataType[i] == 1): - if (shape_idx < len(shapes)): # project on shape - d, p, i = Part.Vertex(fp.Data[i]).distToShape(shapes[shape_idx]) - if d > fp.Tolerance: - touched = True - pts.append(p[0][1]) # shapes[shape_idx].valueAt(fp.Data[i].x)) - shape_idx += 1 - else: - pts.append(fp.Data[i]) - if stretch and touched: - params = [0] - knots = [0] - moves = [pts[0] - fp.Data[0]] - lsum = 0 - mults = [2] - for i in range(1, len(pts)): - lsum += fp.Data[i - 1].distanceToPoint(fp.Data[i]) - params.append(lsum) - if fp.DataType[i] == 1: - knots.append(lsum) - moves.append(pts[i] - fp.Data[i]) - mults.insert(1, 1) - mults[-1] = 2 - if len(moves) < 2: - return(pts) - # FreeCAD.Console.PrintMessage("%s\n%s\n%s\n"%(moves,mults,knots)) - curve = Part.BSplineCurve() - curve.buildFromPolesMultsKnots(moves, mults, knots, False, 1) - for i in range(1, len(pts)): - if fp.DataType[i] == 0: - # FreeCAD.Console.PrintMessage("Stretch %s #%d: %s to %s\n"%(fp.Label,i,pts[i],curve.value(params[i]))) - pts[i] += curve.value(params[i]) - if touched: - return pts - else: - return False - - def execute(self, obj): - try: - o = FreeCADGui.ActiveDocument.getInEdit().Object - if o == obj: - return - except: - FreeCAD.Console.PrintWarning("execute is disabled during editing\n") - pts = self.get_points(obj) - if pts: - if len(pts) < 2: - FreeCAD.Console.PrintError("{} : Not enough points\n".format(obj.Label)) - return False - else: - obj.Data = pts - else: - pts = obj.Data - - tans = [FreeCAD.Vector()] * len(pts) - flags = [False] * len(pts) - for i in range(len(obj.Tangents)): - tans[i] = obj.Tangents[i] - for i in range(len(obj.Flags)): - flags[i] = obj.Flags[i] - # if not (len(obj.LinearSegments) == len(pts)-1): - # FreeCAD.Console.PrintError("%s : Points and LinearSegments mismatch\n"%obj.Label) - if len(obj.LinearSegments) > 0: - for i, b in enumerate(obj.LinearSegments): - if b: - tans[i] = pts[i + 1] - pts[i] - tans[i + 1] = tans[i] - flags[i] = True - flags[i + 1] = True - params = profile_editor.parameterization(pts, obj.Parametrization, obj.Periodic) - - curve = Part.BSplineCurve() - if len(pts) == 2: - curve.buildFromPoles(pts) - elif obj.Periodic and pts[0].distanceToPoint(pts[-1]) < 1e-7: - curve.interpolate(Points=pts[:-1], Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans[:-1], TangentFlags=flags[:-1]) - else: - curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=obj.Periodic, Tolerance=obj.Tolerance, Tangents=tans, TangentFlags=flags) - obj.Shape = curve.toShape() - - def onChanged(self, fp, prop): - if prop in ("Support", "Data", "DataType", "Periodic"): - # FreeCAD.Console.PrintMessage("%s : %s changed\n"%(fp.Label,prop)) - if (len(fp.Data) == len(fp.DataType)) and (sum(fp.DataType) == len(fp.Support)): - new_pts = self.get_points(fp, True) - if new_pts: - fp.Data = new_pts - if prop == "Parametrization": - self.execute(fp) - - def onDocumentRestored(self, fp): - fp.setEditorMode("Data", 2) - fp.setEditorMode("DataType", 2) - - -class GordonProfileVP: - def __init__(self, vobj): - vobj.Proxy = self - self.select_state = True - self.active = False - - def getIcon(self): - return TOOL_ICON - - def attach(self, vobj): - self.Object = vobj.Object - self.active = False - self.select_state = vobj.Selectable - self.ip = None - - def setEdit(self, vobj, mode=0): - if mode == 0 and check_pivy(): - if vobj.Selectable: - self.select_state = True - vobj.Selectable = False - pts = list() - sl = list() - for ob, names in self.Object.Support: - for name in names: - sl.append((ob, (name,))) - shape_idx = 0 - for i in range(len(self.Object.Data)): - p = self.Object.Data[i] - t = self.Object.DataType[i] - if t == 0: - pts.append(profile_editor.MarkerOnShape([p])) - elif t == 1: - pts.append(profile_editor.MarkerOnShape([p], sl[shape_idx])) - shape_idx += 1 - for i in range(len(pts)): # p,t,f in zip(pts, self.Object.Tangents, self.Object.Flags): - if i < min(len(self.Object.Flags), len(self.Object.Tangents)): - if self.Object.Flags[i]: - pts[i].tangent = self.Object.Tangents[i] - self.ip = profile_editor.InterpoCurveEditor(pts, self.Object) - self.ip.periodic = self.Object.Periodic - self.ip.param_factor = self.Object.Parametrization - for i in range(min(len(self.Object.LinearSegments), len(self.ip.lines))): - self.ip.lines[i].tangent = self.Object.LinearSegments[i] - self.ip.lines[i].updateLine() - self.active = True - return True - return False - - def unsetEdit(self, vobj, mode=0): - if isinstance(self.ip, profile_editor.InterpoCurveEditor) and check_pivy(): - pts = list() - typ = list() - tans = list() - flags = list() - # original_links = self.Object.Support - new_links = list() - for p in self.ip.points: - if isinstance(p, profile_editor.MarkerOnShape): - pt = p.points[0] - pts.append(FreeCAD.Vector(pt[0], pt[1], pt[2])) - if p.sublink: - new_links.append(p.sublink) - typ.append(1) - else: - typ.append(0) - if p.tangent: - tans.append(p.tangent) - flags.append(True) - else: - tans.append(FreeCAD.Vector()) - flags.append(False) - self.Object.Tangents = tans - self.Object.Flags = flags - self.Object.LinearSegments = [li.linear for li in self.ip.lines] - self.Object.DataType = typ - self.Object.Data = pts - self.Object.Support = new_links - vobj.Selectable = self.select_state - self.ip.quit() - self.ip = None - self.active = False - self.Object.Document.recompute() - return True - - def doubleClicked(self, vobj): - if not hasattr(self, 'active'): - self.active = False - if not self.active: - self.active = True - # self.setEdit(vobj) - vobj.Document.setEdit(vobj) - else: - vobj.Document.resetEdit() - self.active = False - return True - - def __getstate__(self): - return {"name": self.Object.Name} - - def __setstate__(self, state): - self.Object = FreeCAD.ActiveDocument.getObject(state["name"]) - return None - - -class GordonProfileCommand: - """Creates a editable interpolation curve""" - - def makeFeature(self, sub, pts, typ): - fp = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Freehand BSpline") - GordonProfileFP(fp, sub, pts, typ) - GordonProfileVP(fp.ViewObject) - FreeCAD.Console.PrintMessage(__usage__) - FreeCAD.ActiveDocument.recompute() - FreeCADGui.SendMsgToActiveView("ViewFit") - fp.ViewObject.Document.setEdit(fp.ViewObject) - - def Activated(self): - s = FreeCADGui.Selection.getSelectionEx() - try: - ordered = FreeCADGui.activeWorkbench().Selection - if ordered: - s = ordered - except AttributeError: - pass - - sub = list() - pts = list() - for obj in s: - if obj.HasSubObjects: - # FreeCAD.Console.PrintMessage("object has subobjects %s\n"%str(obj.SubElementNames)) - for n in obj.SubElementNames: - sub.append((obj.Object, [n])) - for p in obj.PickedPoints: - pts.append(p) - - if len(pts) == 0: - pts = [FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(5, 0, 0), FreeCAD.Vector(10, 0, 0)] - typ = [0, 0, 0] - elif len(pts) == 1: - pts.append(pts[0] + FreeCAD.Vector(5, 0, 0)) - pts.append(pts[0] + FreeCAD.Vector(10, 0, 0)) - typ = [1, 0, 0] - else: - typ = [1] * len(pts) - self.makeFeature(sub, pts, typ) - - def IsActive(self): - if FreeCAD.ActiveDocument: - return True - else: - return False - - def GetResources(self): - return {'Pixmap': TOOL_ICON, - 'MenuText': __title__, - 'ToolTip': "{}

Usage :
{}".format(__doc__, "
".join(__usage__.splitlines()))} - - -FreeCADGui.addCommand('gordon_profile', GordonProfileCommand()) diff --git a/Utils/graphics - copia.py b/Utils/graphics - copia.py deleted file mode 100644 index 76099dd..0000000 --- a/Utils/graphics - copia.py +++ /dev/null @@ -1,533 +0,0 @@ -from pivy import coin -#from pivy.utils import getPointOnScreen - -def getPointOnScreen(render_manager, screen_pos, normal="camera", point=None): - """get coordinates from pixel position""" - - pCam = render_manager.getCamera() - vol = pCam.getViewVolume() - - point = point or coin.SbVec3f(0, 0, 0) - - if normal == "camera": - plane = vol.getPlane(10) - normal = plane.getNormal() - elif normal == "x": - normal = SbVec3f(1, 0, 0) - elif normal == "y": - normal = SbVec3f(0, 1, 0) - elif normal == "z": - normal = SbVec3f(0, 0, 1) - normal.normalize() - x, y = screen_pos - vp = render_manager.getViewportRegion() - size = vp.getViewportSize() - dX, dY = size - - fRatio = vp.getViewportAspectRatio() - pX = float(x) / float(vp.getViewportSizePixels()[0]) - pY = float(y) / float(vp.getViewportSizePixels()[1]) - - if (fRatio > 1.0): - pX = (pX - 0.5 * dX) * fRatio + 0.5 * dX - elif (fRatio < 1.0): - pY = (pY - 0.5 * dY) / fRatio + 0.5 * dY - - plane = coin.SbPlane(normal, point) - line = coin.SbLine(*vol.projectPointToLine(coin.SbVec2f(pX,pY))) - pt = plane.intersect(line) - return(pt) - -COLORS = { - "black": (0., 0., 0.), - "white": (1., 1., 1.), - "grey": (.5, .5, .5), - "red": (1., 0., 0.), - "green": (0., 1., 0.), - "blue": (0., 0., 1.), - "yellow": (1., 1., 0.), - "cyan": (0., 1., 1.), - "magenta":(1., 0., 1.) -} - -class Object3D(coin.SoSeparator): - std_col = "black" - ovr_col = "red" - sel_col = "yellow" - non_col = "grey" - - def __init__(self, points, dynamic=False): - super(Object3D, self).__init__() - self.data = coin.SoCoordinate3() - self.color = coin.SoMaterial() - self.set_color() - self += [self.color, self.data] - self.start_pos = None - self.dynamic = dynamic - - # callback function lists - self.on_drag = [] - self.on_drag_release = [] - self.on_drag_start = [] - - self._delete = False - self._tmp_points = None - self.enabled = True - self.points = points - - def set_disabled(self): - self.color.diffuseColor = COLORS[self.non_col] - self.enabled = False - - def set_enabled(self): - self.color.diffuseColor = COLORS[self.std_col] - self.enabled = True - - def set_color(self, col=None): - self.std_col = col or self.std_col - self.color.diffuseColor = COLORS[self.std_col] - - @property - def points(self): - return self.data.point.getValues() - - @points.setter - def points(self, points): - self.data.point.setValue(0, 0, 0) - self.data.point.setValues(0, len(points), points) - - def set_mouse_over(self): - if self.enabled: - self.color.diffuseColor = COLORS[self.ovr_col] - - def unset_mouse_over(self): - if self.enabled: - self.color.diffuseColor = COLORS[self.std_col] - - def select(self): - if self.enabled: - self.color.diffuseColor = COLORS[self.sel_col] - - def unselect(self): - if self.enabled: - self.color.diffuseColor = COLORS[self.std_col] - - def drag(self, mouse_coords, fact=1.): - if self.enabled: - pts = self.points - for i, pt in enumerate(pts): - pt[0] = mouse_coords[0] * fact + self._tmp_points[i][0] - pt[1] = mouse_coords[1] * fact + self._tmp_points[i][1] - pt[2] = mouse_coords[2] * fact + self._tmp_points[i][2] - self.points = pts - for foo in self.on_drag: - foo() - - def drag_release(self): - if self.enabled: - for foo in self.on_drag_release: - foo() - - def drag_start(self): - self._tmp_points = self.points - if self.enabled: - for foo in self.on_drag_start: - foo() - - @property - def drag_objects(self): - if self.enabled: - return [self] - - def delete(self): - if self.enabled and not self._delete: - self._delete = True - - def check_dependency(self): - pass - - -class Marker(Object3D): - def __init__(self, points, dynamic=False): - super(Marker, self).__init__(points, dynamic) - self.marker = coin.SoMarkerSet() - self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 - self.addChild(self.marker) - - -class Line(Object3D): - def __init__(self, points, dynamic=False): - super(Line, self).__init__(points, dynamic) - self.drawstyle = coin.SoDrawStyle() - self.line = coin.SoLineSet() - self.addChild(self.drawstyle) - self.addChild(self.line) - -class Point(Object3D): - def __init__(self, points, dynamic=False): - super(Point, self).__init__(points, dynamic) - self.drawstyle = coin.SoDrawStyle() - self.point = coin.SoPointSet() - self.addChild(self.drawstyle) - self.addChild(self.point) - -class Polygon(Object3D): - def __init__(self, points, dynamic=False): - super(Polygon, self).__init__(points, dynamic) - self.polygon = coin.SoFaceSet() - self.addChild(self.polygon) - -class Arrow(Line): - def __init__(self, points, dynamic=False, arrow_size=0.04, length=2): - super(Arrow, self).__init__(points, dynamic) - self.arrow_sep = coin.SoSeparator() - self.arrow_rot = coin.SoRotation() - self.arrow_scale = coin.SoScale() - self.arrow_translate = coin.SoTranslation() - self.arrow_scale.scaleFactor.setValue(arrow_size, arrow_size, arrow_size) - self.cone = coin.SoCone() - arrow_length = coin.SoScale() - arrow_length.scaleFactor = (1, length, 1) - arrow_origin = coin.SoTranslation() - arrow_origin.translation = (0, -1, 0) - self.arrow_sep += [self.arrow_translate, self.arrow_rot, self.arrow_scale] - self.arrow_sep += [arrow_length, arrow_origin, self.cone] - self += [self.arrow_sep] - self.set_arrow_direction() - - def set_arrow_direction(self): - pts = np.array(self.points) - self.arrow_translate.translation = tuple(pts[-1]) - direction = pts[-1] - pts[-2] - direction /= np.linalg.norm(direction) - _rot = coin.SbRotation() - _rot.setValue(coin.SbVec3f(0, 1, 0), coin.SbVec3f(*direction)) - self.arrow_rot.rotation.setValue(_rot) - -class InteractionSeparator(coin.SoSeparator): - pick_radius = 20 - ctrl_keys = {"grab": "g", - "abort_grab": u"\uff1b", - "select_all": "a", - "delete": u"\uffff", - "axis_x": "x", - "axis_y": "y", - "axis_z": "z"} - - def __init__(self, render_manager): - super(InteractionSeparator, self).__init__() - self.render_manager = render_manager - self.objects = coin.SoSeparator() - self.dynamic_objects = [] - self.static_objects = [] - self.over_object = None - self.selected_objects = [] - self.drag_objects = [] - - self.on_drag = [] - self.on_drag_release = [] - self.on_drag_start = [] - - self._direction = None - - self.events = coin.SoEventCallback() - self += self.events, self.objects - - def register(self): - self._highlightCB = self.events.addEventCallback( - coin.SoLocation2Event.getClassTypeId(), self.highlightCB) - self._selectCB = self.events.addEventCallback( - coin.SoMouseButtonEvent.getClassTypeId(), self.selectCB) - self._grabCB = self.events.addEventCallback( - coin.SoMouseButtonEvent.getClassTypeId(), self.grabCB) - self._deleteCB = self.events.addEventCallback( - coin.SoKeyboardEvent.getClassTypeId(), self.deleteCB) - self._selectAllCB = self.events.addEventCallback( - coin.SoKeyboardEvent.getClassTypeId(), self.selectAllCB) - - def unregister(self): - self.events.removeEventCallback( - coin.SoLocation2Event.getClassTypeId(), self._highlightCB) - self.events.removeEventCallback( - coin.SoMouseButtonEvent.getClassTypeId(), self._selectCB) - self.events.removeEventCallback( - coin.SoMouseButtonEvent.getClassTypeId(), self._grabCB) - self.events.removeEventCallback( - coin.SoKeyboardEvent.getClassTypeId(), self._deleteCB) - self.events.removeEventCallback( - coin.SoKeyboardEvent.getClassTypeId(), self._selectAllCB) - - - def addChild(self, child): - if hasattr(child, "dynamic"): - self.objects.addChild(child) - if child.dynamic: - self.dynamic_objects.append(child) - else: - self.static_objects.append(child) - else: - super(InteractionSeparator, self).addChild(child) - -#-----------------------HIGHLIGHTING-----------------------# -# a SoLocation2Event calling a function which sends rays # -# int the scene. This will return the object the mouse is # -# currently hoovering. # - - def highlightObject(self, obj): - if self.over_object: - self.over_object.unset_mouse_over() - self.over_object = obj - if self.over_object: - self.over_object.set_mouse_over() - self.colorSelected() - - def highlightCB(self, attr, event_callback): - event = event_callback.getEvent() - pos = event.getPosition() - obj = self.sendRay(pos) - self.highlightObject(obj) - - def sendRay(self, mouse_pos): - """sends a ray through the scene and return the nearest entity""" - ray_pick = coin.SoRayPickAction(self.render_manager.getViewportRegion()) - ray_pick.setPoint(coin.SbVec2s(*mouse_pos)) - ray_pick.setRadius(InteractionSeparator.pick_radius) - ray_pick.setPickAll(True) - ray_pick.apply(self.render_manager.getSceneGraph()) - picked_point = ray_pick.getPickedPointList() - return self.objByID(picked_point) - - def objByID(self, picked_point): - for point in picked_point: - path = point.getPath() - length = path.getLength() - point = path.getNode(length - 2) - for o in self.dynamic_objects: - if point == o: - return(o) - # Code below was not working with python 2.7 (pb with getNodeId ?) - #point = list(filter( - #lambda ctrl: ctrl.getNodeId() == point.getNodeId(), - #self.dynamic_objects)) - #if point != []: - #return point[0] - return None - - - -#------------------------SELECTION------------------------# - def selectObject(self, obj, multi=False): - if not multi: - for o in self.selected_objects: - o.unselect() - self.selected_objects = [] - if obj: - if obj in self.selected_objects: - self.selected_objects.remove(obj) - else: - self.selected_objects.append(obj) - self.colorSelected() - self.selectionChanged() - - def selectCB(self, attr, event_callback): - event = event_callback.getEvent() - if (event.getState() == coin.SoMouseButtonEvent.DOWN and - event.getButton() == event.BUTTON1): - pos = event.getPosition() - obj = self.sendRay(pos) - self.selectObject(obj, event.wasCtrlDown()) - - def select_all_cb(self, event_callback): - event = event_callback.getEvent() - if (event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"])): - if event.getState() == event.DOWN: - if self.selected_objects: - for o in self.selected_objects: - o.unselect() - self.selected_objects = [] - else: - for obj in self.objects: - if obj.dynamic: - self.selected_objects.append(obj) - self.ColorSelected() - self.selection_changed() - - def deselect_all(self): - if self.selected_objects: - for o in self.selected_objects: - o.unselect() - self.selected_objects = [] - - def colorSelected(self): - for obj in self.selected_objects: - obj.select() - - def selectionChanged(self): - pass - - def selectAllCB(self, attr, event_callback): - event = event_callback.getEvent() - if (event.getKey() == ord(InteractionSeparator.ctrl_keys["select_all"])): - if event.getState() == event.DOWN: - if self.selected_objects: - for o in self.selected_objects: - o.unselect() - self.selected_objects = [] - else: - for obj in self.dynamic_objects: - if obj.dynamic: - self.selected_objects.append(obj) - self.colorSelected() - self.selectionChanged() - - -#------------------------INTERACTION------------------------# - - def cursor_pos(self, event): - pos = event.getPosition() - # print(list(getPointOnScreen1(self.render_manager, pos))) - return getPointOnScreen(self.render_manager, pos) - - - def constrained_vector(self, vector): - if self._direction is None: - return vector - if self._direction == InteractionSeparator.ctrl_keys["axis_x"]: - return [vector[0], 0, 0] - elif self._direction == InteractionSeparator.ctrl_keys["axis_y"]: - return [0, vector[1], 0] - elif self._direction == InteractionSeparator.ctrl_keys["axis_z"]: - return [0, 0, vector[2]] - - def grabCB(self, attr, event_callback): - # press grab key to move an entity - event = event_callback.getEvent() - # get all drag objects, every selected object can add some drag objects - # but the eventhandler is not allowed to call the drag twice on an object - #if event.getKey() == ord(InteractionSeparator.ctrl_keys["grab"]): - if (event.getState() == coin.SoMouseButtonEvent.DOWN and - event.getButton() == event.BUTTON1): - pos = event.getPosition() - obj = self.sendRay(pos) - if obj: - #if not obj in self.selected_objects: - #self.selectObject(obj, event.wasCtrlDown()) - self.drag_objects = set() - for i in self.selected_objects: - for j in i.drag_objects: - self.drag_objects.add(j) - # check if something is selected - if self.drag_objects: - # first delete the selection_cb, and higlight_cb - self.unregister() - # now add a callback that calls the dragfunction of the selected entities - self.start_pos = self.cursor_pos(event) - self._dragCB = self.events.addEventCallback( - coin.SoEvent.getClassTypeId(), self.dragCB) - for obj in self.drag_objects: - obj.drag_start() - for foo in self.on_drag_start: - foo() - - def dragCB(self, attr, event_callback, force=False): - event = event_callback.getEvent() - b = "" - s = "" - if type(event) == coin.SoMouseButtonEvent: - if event.getButton() == coin.SoMouseButtonEvent.BUTTON1: - b = "mb1" - elif event.getButton() == coin.SoMouseButtonEvent.BUTTON2: - b = "mb2" - if event.getState() == coin.SoMouseButtonEvent.UP: - s = "up" - elif event.getState() == coin.SoMouseButtonEvent.DOWN: - s = "down" - import FreeCAD - FreeCAD.Console.PrintMessage("{} {}\n".format(b,s)) - if ((type(event) == coin.SoMouseButtonEvent and - event.getState() == coin.SoMouseButtonEvent.UP - and event.getButton() == coin.SoMouseButtonEvent.BUTTON1) or - force): - self.register() - if self._dragCB: - self.events.removeEventCallback( - coin.SoEvent.getClassTypeId(), self._dragCB) - self._direction = None - self._dragCB = None - self.start_pos = None - for obj in self.drag_objects: - obj.drag_release() - for foo in self.on_drag_release: - foo() - self.drag_objects = [] - elif (type(event) == coin.SoKeyboardEvent and - event.getState() == coin.SoMouseButtonEvent.DOWN): - if event.getKey() == InteractionSeparator.ctrl_keys["abort_grab"]: # esc - for obj in self.drag_objects: - obj.drag([0, 0, 0], 1) # set back to zero - self.dragCB(attr, event_callback, force=True) - return - try: - key = chr(event.getKey()) - except ValueError: - # there is no character for this value - key = "_" - if key in [InteractionSeparator.ctrl_keys["axis_x"], - InteractionSeparator.ctrl_keys["axis_y"], - InteractionSeparator.ctrl_keys["axis_z"]] and key != self._direction: - self._direction = key - else: - self._direction = None - diff = self.cursor_pos(event) - self.start_pos - diff = self.constrained_vector(diff) - for obj in self.drag_objects: - obj.drag(diff, 1) - for foo in self.on_drag: - foo() - - elif type(event) == coin.SoLocation2Event: - fact = 0.1 if event.wasShiftDown() else 1. - diff = self.cursor_pos(event) - self.start_pos - diff = self.constrained_vector(diff) - for obj in self.drag_objects: - obj.drag(diff, fact) - for foo in self.on_drag: - foo() - - def deleteCB(self, attr, event_callback): - event = event_callback.getEvent() - # get all drag objects, every selected object can add some drag objects - # but the eventhandler is not allowed to call the drag twice on an object - if event.getKey() == ord(InteractionSeparator.ctrl_keys["delete"]) and (event.getState() == 1): - self.removeSelected() - - def removeSelected(self): - temp = [] - for i in self.selected_objects: - i.delete() - for i in self.dynamic_objects + self.static_objects: - i.check_dependency() #dependency length max = 1 - for i in self.dynamic_objects + self.static_objects: - if i._delete: - temp.append(i) - self.selected_objects = [] - self.over_object = None - self.selectionChanged() - for i in temp: - if i in self.dynamic_objects: - self.dynamic_objects.remove(i) - else: - self.static_objects.remove(i) - self.objects.removeChild(i) - del(i) - self.selectionChanged() - - def removeAllChildren(self): - for i in self.dynamic_objects: - i.delete() - self.dynamic_objects = [] - self.static_objects = [] - self.selected_objects = [] - self.over_object = None - super(InteractionSeparator, self).removeAllChildren() - diff --git a/Utils/profile_editor - copia (2).py b/Utils/profile_editor - copia (2).py deleted file mode 100644 index dbbb08d..0000000 --- a/Utils/profile_editor - copia (2).py +++ /dev/null @@ -1,501 +0,0 @@ -# from curve workbench - -import FreeCAD -import FreeCADGui -import Part -import PySide.QtCore as QtCore -import PySide.QtGui as QtGui -from pivy import coin - -from Utils import graphics - - -def parameterization(points, a, closed): - """Computes a knot Sequence for a set of points - fac (0-1) : parameterization factor - fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length""" - pts = points.copy() - if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point - pts.append(pts[0]) - params = [0] - for i in range(1, len(pts)): - p = pts[i] - pts[i - 1] - if isinstance(p, FreeCAD.Vector): - le = p.Length - else: - le = p.length() - pl = pow(le, a) - params.append(params[-1] + pl) - return params - -class ConnectionMarker(graphics.Marker): - def __init__(self, points): - super(ConnectionMarker, self).__init__(points, True) - -class MarkerOnShape(graphics.Marker): - def __init__(self, points, sh=None): - super(MarkerOnShape, self).__init__(points, True) - self._shape = None - self._sublink = None - self._tangent = None - self._translate = coin.SoTranslation() - self._text_font = coin.SoFont() - self._text_font.name = "Arial:Bold" - self._text_font.size = 13.0 - self._text = coin.SoText2() - self._text_switch = coin.SoSwitch() - self._text_switch.addChild(self._translate) - self._text_switch.addChild(self._text_font) - self._text_switch.addChild(self._text) - self.on_drag_start.append(self.add_text) - self.on_drag_release.append(self.remove_text) - self.addChild(self._text_switch) - if isinstance(sh, Part.Shape): - self.snap_shape = sh - elif isinstance(sh, (tuple, list)): - self.sublink = sh - - def subshape_from_sublink(self, o): - name = o[1][0] - print(name, " selected") - if 'Vertex' in name: - n = eval(name.lstrip('Vertex')) - return o[0].Shape.Vertexes[n - 1] - elif 'Edge' in name: - n = eval(name.lstrip('Edge')) - return o[0].Shape.Edges[n - 1] - elif 'Face' in name: - n = eval(name.lstrip('Face')) - return o[0].Shape.Faces[n - 1] - - def add_text(self): - self._text_switch.whichChild = coin.SO_SWITCH_ALL - self.on_drag.append(self.update_text) - - def remove_text(self): - self._text_switch.whichChild = coin.SO_SWITCH_NONE - self.on_drag.remove(self.update_text) - - def update_text(self): - p = self.points[0] - coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])] - self._translate.translation = p - self._text.string.setValues(0, 3, coords) - - @property - def tangent(self): - return self._tangent - - @tangent.setter - def tangent(self, t): - if isinstance(t, FreeCAD.Vector): - if t.Length > 1e-7: - self._tangent = t - self._tangent.normalize() - self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9 - else: - self._tangent = None - self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 - else: - self._tangent = None - self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 - - @property - def snap_shape(self): - return self._shape - - @snap_shape.setter - def snap_shape(self, sh): - if isinstance(sh, Part.Shape): - self._shape = sh - else: - self._shape = None - self.alter_color() - - @property - def sublink(self): - return self._sublink - - @sublink.setter - def sublink(self, sl): - if isinstance(sl, (tuple, list)) and not (sl == self._sublink): - self._shape = self.subshape_from_sublink(sl) - self._sublink = sl - else: - self._shape = None - self._sublink = None - self.alter_color() - - def alter_color(self): - if isinstance(self._shape, Part.Vertex): - self.set_color("white") - elif isinstance(self._shape, Part.Edge): - self.set_color("cyan") - elif isinstance(self._shape, Part.Face): - self.set_color("magenta") - else: - self.set_color("black") - - def __repr__(self): - return "MarkerOnShape({})".format(self._shape) - - def drag(self, mouse_coords, fact=1.): - if self.enabled: - pts = self.points - for i, p in enumerate(pts): - p[0] = mouse_coords[0] * fact + self._tmp_points[i][0] - p[1] = mouse_coords[1] * fact + self._tmp_points[i][1] - p[2] = mouse_coords[2] * fact + self._tmp_points[i][2] - if self._shape: - v = Part.Vertex(p[0], p[1], p[2]) - proj = v.distToShape(self._shape)[1][0][1] - # FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj)) - p[0] = proj.x - p[1] = proj.y - p[2] = proj.z - self.points = pts - for foo in self.on_drag: - foo() - - -class ConnectionPolygon(graphics.Polygon): - std_col = "green" - - def __init__(self, markers): - super(ConnectionPolygon, self).__init__( - sum([m.points for m in markers], []), True) - self.markers = markers - - for m in self.markers: - m.on_drag.append(self.updatePolygon) - - def updatePolygon(self): - self.points = sum([m.points for m in self.markers], []) - - @property - def drag_objects(self): - return self.markers - - def check_dependency(self): - if any([m._delete for m in self.markers]): - self.delete() - -class ConnectionLine(graphics.Line): - def __init__(self, markers): - super(ConnectionLine, self).__init__( - sum([m.points for m in markers], []), True) - self.markers = markers - self._linear = False - for m in self.markers: - m.on_drag.append(self.updateLine) - - def updateLine(self): - self.points = sum([m.points for m in self.markers], []) - if self._linear: - p1 = self.markers[0].points[0] - p2 = self.markers[-1].points[0] - t = p2 - p1 - tan = FreeCAD.Vector(t[0], t[1], t[2]) - for m in self.markers: - m.tangent = tan - - @property - def linear(self): - return self._linear - - @linear.setter - def linear(self, b): - self._linear = bool(b) - - @property - def drag_objects(self): - return self.markers - - def check_dependency(self): - if any([m._delete for m in self.markers]): - self.delete() - -class Edit(object): - - def __init__(self, points=[], obj=None): - self.points = list() - self.lines = list() - self.obj = obj - self.root_inserted = False - self.root = None - - self.editing = None - - # event callbacks - self.selection_callback = None - self._keyPressedCB = None - self._mouseMovedCB = None - self._mousePressedCB = None - - for p in points: - if isinstance(p, FreeCAD.Vector): - self.points.append(MarkerOnShape([p])) - elif isinstance(p, (tuple, list)): - self.points.append(MarkerOnShape([p[0]], p[1])) - elif isinstance(p, (MarkerOnShape, ConnectionMarker)): - self.points.append(p) - else: - FreeCAD.Console.PrintError("InterpoCurveEditor : bad input") - - # Setup coin objects - if self.obj: - self.guidoc = self.obj.ViewObject.Document - else: - if not FreeCADGui.ActiveDocument: - FreeCAD.newDocument("New") - self.guidoc = FreeCADGui.ActiveDocument - self.view = self.guidoc.ActiveView - self.rm = self.view.getViewer().getSoRenderManager() - self.sg = self.view.getSceneGraph() - self.setupInteractionSeparator() - - # Callbacks - #self.unregister_editing_callbacks() - #self.register_editing_callbacks() - - def setupInteractionSeparator(self): - if self.root_inserted: - self.sg.removeChild(self.root) - self.root = graphics.InteractionSeparator(self.rm) - self.root.setName("InteractionSeparator") - self.root.pick_radius = 40 - - # Populate root node - self.root += self.points - self.build_lines() - self.root += self.lines - - # set FreeCAD color scheme - for o in self.points + self.lines: - o.ovr_col = "yellow" - o.sel_col = "green" - - self.root.register() - self.sg.addChild(self.root) - self.root_inserted = True - self.root.selected_objects = list() - - def build_lines(self): - for i in range(len(self.points) - 1): - line = ConnectionLine([self.points[i], self.points[i + 1]]) - line.set_color("blue") - self.lines.append(line) - - # ------------------------------------------------------------------------- - # SCENE EVENTS CALLBACKS - # ------------------------------------------------------------------------- - - def register_editing_callbacks(self): - """ Register editing callbacks (former action function) """ - - if self._keyPressedCB is None: - self._keyPressedCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.keyPressed) - - if self._mousePressedCB is None: - self._mousePressedCB = self.root.events.addEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self.mousePressed) - - if self._mouseMovedCB is None: - self._mouseMovedCB = self.root.events.addEventCallback(coin.SoLocation2Event.getClassTypeId(), self.mouseMoved) - - def unregister_editing_callbacks(self): - """ Remove callbacks used during editing if they exist """ - - if self._keyPressedCB: - self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._keyPressedCB) - self._keyPressedCB = None - - if self._mousePressedCB: - self.root.events.removeEventCallback(coin.SoMouseButtonEvent.getClassTypeId(), self._mousePressedCB) - self._mousePressedCB = None - - if self._mouseMovedCB: - self.root.events.removeEventCallback(coin.SoLocation2Event.getClassTypeId(), self._mouseMovedCB) - self._mouseMovedCB = None - - # ------------------------------------------------------------------------- - # SCENE EVENT HANDLERS - # ------------------------------------------------------------------------- - - def keyPressed(self, attr, event_callback): - event = event_callback.getEvent() - if event.getState() == event.UP: - #FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey()) - if event.getKey() == ord("i"): - self.subdivide() - elif event.getKey() == ord("q"):# or event.getKey() == ord(65307): - if self.obj: - self.obj.ViewObject.Proxy.doubleClicked(self.obj.ViewObject) - else: - self.quit() - elif event.getKey() == ord("s"): - sel = FreeCADGui.Selection.getSelectionEx() - tup = None - if len(sel) == 1: - tup = (sel[0].Object, sel[0].SubElementNames) - for i in range(len(self.root.selected_objects)): - if isinstance(self.root.selected_objects[i], MarkerOnShape): - self.root.selected_objects[i].sublink = tup - #FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink))) - self.root.selected_objects[i].drag_start() - self.root.selected_objects[i].drag((0, 0, 0.)) - self.root.selected_objects[i].drag_release() - elif (event.getKey() == 65535) or (event.getKey() == 65288): # Suppr or Backspace - # FreeCAD.Console.PrintMessage("Some objects have been deleted\n") - pts = list() - for o in self.root.dynamic_objects: - if isinstance(o, MarkerOnShape): - pts.append(o) - self.points = pts - self.setupInteractionSeparator() - - def mousePressed(self, attr, event_callback): - """ Mouse button event handler, calls: startEditing, endEditing, addPoint, delPoint """ - event = event_callback.getEvent() - if (event.getState() == coin.SoMouseButtonEvent.DOWN) and (event.getButton() == event.BUTTON1): # left click - if not event.wasAltDown(): - ''' do something ''' - if self.editing is None: - ''' do something''' - else: - self.endEditing(self.obj, self.editing) - - elif event.wasAltDown(): # left click with ctrl down - self.display_tracker_menu(event) - - elif (event.getState() == coin.SoMouseButtonEvent.DOWN) and (event.getButton() == event.BUTTON2): # right click - self.display_tracker_menu(event) - - def mouseMoved(self, attr, event_callback): - """ Execute as callback for mouse movement. Update tracker position and update preview ghost. """ - - event = event_callback.getEvent() - pos = event.getPosition() - - ''' - if self.editing is not None: - self.updateTrackerAndGhost(event) - else: - # look for a node in mouse position and highlight it - pos = event.getPosition() - node = self.getEditNode(pos) - ep = self.getEditNodeIndex(node) - if ep is not None: - if self.overNode is not None: - self.overNode.setColor(COLORS["default"]) - self.trackers[str(node.objectName.getValue())][ep].setColor(COLORS["red"]) - self.overNode = self.trackers[str(node.objectName.getValue())][ep] - print("show menu") - # self.display_tracker_menu(event) - else: - if self.overNode is not None: - self.overNode.setColor(COLORS["default"]) - self.overNode = None - ''' - - def endEditing(self, obj, nodeIndex=None, v=None): - self.editing = None - - - # ------------------------------------------------------------------------ - # DRAFT EDIT Context menu - # ------------------------------------------------------------------------ - - def display_tracker_menu(self, event): - self.tracker_menu = QtGui.QMenu() - self.event = event - actions = None - actions = ["add point"] - - ''' - if self.overNode: - # if user is over a node - doc = self.overNode.get_doc_name() - obj = App.getDocument(doc).getObject(self.overNode.get_obj_name()) - ep = self.overNode.get_subelement_index() - - obj_gui_tools = self.get_obj_gui_tools(obj) - if obj_gui_tools: - actions = obj_gui_tools.get_edit_point_context_menu(obj, ep) - - else: - # try if user is over an edited object - pos = self.event.getPosition() - obj = self.get_selected_obj_at_position(pos) - if utils.get_type(obj) in ["Line", "Wire", "BSpline", "BezCurve"]: - actions = ["add point"] - elif utils.get_type(obj) in ["Circle"] and obj.FirstAngle != obj.LastAngle: - actions = ["invert arc"] - - if actions is None: - return - ''' - for a in actions: - self.tracker_menu.addAction(a) - - self.tracker_menu.popup(FreeCADGui.getMainWindow().cursor().pos()) - QtCore.QObject.connect(self.tracker_menu, - QtCore.SIGNAL("triggered(QAction *)"), - self.evaluate_menu_action) - - def evaluate_menu_action(self, labelname): - action_label = str(labelname.text()) - - doc = None - obj = None - idx = None - - if action_label == "add point": - self.addPoint(self.event) - - del self.event - - # ------------------------------------------------------------------------- - # EDIT functions - # ------------------------------------------------------------------------- - - def addPoint(self, event): - ''' add point to the end ''' - pos = event.getPosition() - pts = self.points.copy() - new_select = list() - point = FreeCAD.Vector(pos) - mark = MarkerOnShape([point]) - pts.append(mark) - new_select.append(mark) - self.points = pts - self.setupInteractionSeparator() - self.root.selected_objects = new_select - - def subdivide(self): - # get selected lines and subdivide them - pts = list() - new_select = list() - for o in self.lines: - #FreeCAD.Console.PrintMessage("object %s\n"%str(o)) - if isinstance(o, ConnectionLine): - pts.append(o.markers[0]) - if o in self.root.selected_objects: - #idx = self.lines.index(o) - #FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx)) - p1 = o.markers[0].points[0] - p2 = o.markers[1].points[0] - midpar = (FreeCAD.Vector(p1) + FreeCAD.Vector(p2)) / 2.0 - mark = MarkerOnShape([midpar]) - pts.append(mark) - new_select.append(mark) - pts.append(self.points[-1]) - self.points = pts - self.setupInteractionSeparator() - self.root.selected_objects = new_select - return True - - def quit(self): - self.unregister_editing_callbacks() - self.root.unregister() - self.sg.removeChild(self.root) - self.root_inserted = False \ No newline at end of file diff --git a/Utils/profile_editor - copia.py b/Utils/profile_editor - copia.py deleted file mode 100644 index 44c9837..0000000 --- a/Utils/profile_editor - copia.py +++ /dev/null @@ -1,533 +0,0 @@ -import FreeCAD -import FreeCADGui -import Part -from freecad.Curves import graphics -from pivy import coin - - -# from graphics import COLORS -# FreeCAD.Console.PrintMessage("Using local Pivy.graphics library\n") - - -def parameterization(points, a, closed): - """Computes a knot Sequence for a set of points - fac (0-1) : parameterization factor - fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length""" - pts = points.copy() - if closed and pts[0].distanceToPoint(pts[-1]) > 1e-7: # we need to add the first point as the end point - pts.append(pts[0]) - params = [0] - for i in range(1, len(pts)): - p = pts[i] - pts[i - 1] - if isinstance(p, FreeCAD.Vector): - le = p.Length - else: - le = p.length() - pl = pow(le, a) - params.append(params[-1] + pl) - return params - - -class ConnectionMarker(graphics.Marker): - def __init__(self, points): - super(ConnectionMarker, self).__init__(points, True) - - -class MarkerOnShape(graphics.Marker): - def __init__(self, points, sh=None): - super(MarkerOnShape, self).__init__(points, True) - self._shape = None - self._sublink = None - self._tangent = None - self._translate = coin.SoTranslation() - self._text_font = coin.SoFont() - self._text_font.name = "Arial:Bold" - self._text_font.size = 13.0 - self._text = coin.SoText2() - self._text_switch = coin.SoSwitch() - self._text_switch.addChild(self._translate) - self._text_switch.addChild(self._text_font) - self._text_switch.addChild(self._text) - self.on_drag_start.append(self.add_text) - self.on_drag_release.append(self.remove_text) - self.addChild(self._text_switch) - if isinstance(sh, Part.Shape): - self.snap_shape = sh - elif isinstance(sh, (tuple, list)): - self.sublink = sh - - def subshape_from_sublink(self, o): - name = o[1][0] - if 'Vertex' in name: - n = eval(name.lstrip('Vertex')) - return(o[0].Shape.Vertexes[n - 1]) - elif 'Edge' in name: - n = eval(name.lstrip('Edge')) - return(o[0].Shape.Edges[n - 1]) - elif 'Face' in name: - n = eval(name.lstrip('Face')) - return(o[0].Shape.Faces[n - 1]) - - def add_text(self): - self._text_switch.whichChild = coin.SO_SWITCH_ALL - self.on_drag.append(self.update_text) - - def remove_text(self): - self._text_switch.whichChild = coin.SO_SWITCH_NONE - self.on_drag.remove(self.update_text) - - def update_text(self): - p = self.points[0] - coords = ['{: 9.3f}'.format(p[0]), '{: 9.3f}'.format(p[1]), '{: 9.3f}'.format(p[2])] - self._translate.translation = p - self._text.string.setValues(0, 3, coords) - - @property - def tangent(self): - return self._tangent - - @tangent.setter - def tangent(self, t): - if isinstance(t, FreeCAD.Vector): - if t.Length > 1e-7: - self._tangent = t - self._tangent.normalize() - self.marker.markerIndex = coin.SoMarkerSet.DIAMOND_FILLED_9_9 - else: - self._tangent = None - self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 - else: - self._tangent = None - self.marker.markerIndex = coin.SoMarkerSet.CIRCLE_FILLED_9_9 - - @property - def snap_shape(self): - return self._shape - - @snap_shape.setter - def snap_shape(self, sh): - if isinstance(sh, Part.Shape): - self._shape = sh - else: - self._shape = None - self.alter_color() - - @property - def sublink(self): - return self._sublink - - @sublink.setter - def sublink(self, sl): - if isinstance(sl, (tuple, list)) and not (sl == self._sublink): - self._shape = self.subshape_from_sublink(sl) - self._sublink = sl - else: - self._shape = None - self._sublink = None - self.alter_color() - - def alter_color(self): - if isinstance(self._shape, Part.Vertex): - self.set_color("white") - elif isinstance(self._shape, Part.Edge): - self.set_color("cyan") - elif isinstance(self._shape, Part.Face): - self.set_color("magenta") - else: - self.set_color("black") - - def __repr__(self): - return("MarkerOnShape({})".format(self._shape)) - - def drag(self, mouse_coords, fact=1.): - if self.enabled: - pts = self.points - for i, p in enumerate(pts): - p[0] = mouse_coords[0] * fact + self._tmp_points[i][0] - p[1] = mouse_coords[1] * fact + self._tmp_points[i][1] - p[2] = mouse_coords[2] * fact + self._tmp_points[i][2] - if self._shape: - v = Part.Vertex(p[0], p[1], p[2]) - proj = v.distToShape(self._shape)[1][0][1] - # FreeCAD.Console.PrintMessage("%s -> %s\n"%(p.getValue(), proj)) - p[0] = proj.x - p[1] = proj.y - p[2] = proj.z - self.points = pts - for foo in self.on_drag: - foo() - - -class ConnectionPolygon(graphics.Polygon): - std_col = "green" - - def __init__(self, markers): - super(ConnectionPolygon, self).__init__( - sum([m.points for m in markers], []), True) - self.markers = markers - - for m in self.markers: - m.on_drag.append(self.updatePolygon) - - def updatePolygon(self): - self.points = sum([m.points for m in self.markers], []) - - @property - def drag_objects(self): - return self.markers - - def check_dependency(self): - if any([m._delete for m in self.markers]): - self.delete() - - -class ConnectionLine(graphics.Line): - def __init__(self, markers): - super(ConnectionLine, self).__init__( - sum([m.points for m in markers], []), True) - self.markers = markers - self._linear = False - for m in self.markers: - m.on_drag.append(self.updateLine) - - def updateLine(self): - self.points = sum([m.points for m in self.markers], []) - if self._linear: - p1 = self.markers[0].points[0] - p2 = self.markers[-1].points[0] - t = p2 - p1 - tan = FreeCAD.Vector(t[0], t[1], t[2]) - for m in self.markers: - m.tangent = tan - - @property - def linear(self): - return self._linear - - @linear.setter - def linear(self, b): - self._linear = bool(b) - - @property - def drag_objects(self): - return self.markers - - def check_dependency(self): - if any([m._delete for m in self.markers]): - self.delete() - - -class InterpoCurveEditor(object): - """Interpolation curve free-hand editor - my_editor = InterpoCurveEditor([points], obj) - obj is the FreeCAD object that will receive - the curve shape at the end of editing. - points can be : - - Vector (free point) - - (Vector, shape) (point on shape)""" - def __init__(self, points=[], fp=None): - self.points = list() - self.curve = Part.BSplineCurve() - self.fp = fp - self.root_inserted = False - self.periodic = False - self.param_factor = 1.0 - # self.support = None # Not yet implemented - for p in points: - if isinstance(p, FreeCAD.Vector): - self.points.append(MarkerOnShape([p])) - elif isinstance(p, (tuple, list)): - self.points.append(MarkerOnShape([p[0]], p[1])) - elif isinstance(p, (MarkerOnShape, ConnectionMarker)): - self.points.append(p) - else: - FreeCAD.Console.PrintError("InterpoCurveEditor : bad input") - # Setup coin objects - if self.fp: - self.guidoc = self.fp.ViewObject.Document - else: - if not FreeCADGui.ActiveDocument: - FreeCAD.newDocument("New") - self.guidoc = FreeCADGui.ActiveDocument - self.view = self.guidoc.ActiveView - self.rm = self.view.getViewer().getSoRenderManager() - self.sg = self.view.getSceneGraph() - self.setup_InteractionSeparator() - self.update_curve() - - def setup_InteractionSeparator(self): - if self.root_inserted: - self.sg.removeChild(self.root) - self.root = graphics.InteractionSeparator(self.rm) - self.root.setName("InteractionSeparator") - # self.root.ovr_col = "yellow" - # self.root.sel_col = "green" - self.root.pick_radius = 40 - self.root.on_drag.append(self.update_curve) - # Keyboard callback - # self.events = coin.SoEventCallback() - self._controlCB = self.root.events.addEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self.controlCB) - # populate root node - # self.root.addChild(self.events) - self.root += self.points - self.build_lines() - self.root += self.lines - # set FreeCAD color scheme - for o in self.points + self.lines: - o.ovr_col = "yellow" - o.sel_col = "green" - self.root.register() - self.sg.addChild(self.root) - self.root_inserted = True - self.root.selected_objects = list() - - def compute_tangents(self): - tans = list() - flags = list() - for i in range(len(self.points)): - if isinstance(self.points[i].snap_shape, Part.Face): - for vec in self.points[i].points: - u, v = self.points[i].snap_shape.Surface.parameter(FreeCAD.Vector(vec)) - norm = self.points[i].snap_shape.normalAt(u, v) - cp = self.curve.parameter(FreeCAD.Vector(vec)) - t = self.curve.tangent(cp)[0] - pl = Part.Plane(FreeCAD.Vector(), norm) - ci = Part.Geom2d.Circle2d() - ci.Radius = t.Length * 2 - w = Part.Wire([ci.toShape(pl)]) - f = Part.Face(w) - # proj = f.project([Part.Vertex(t)]) - proj = Part.Vertex(t).distToShape(f)[1][0][1] - # pt = proj.Vertexes[0].Point - # FreeCAD.Console.PrintMessage("Projection %s -> %s\n"%(t, proj)) - if proj.Length > 1e-7: - tans.append(proj) - flags.append(True) - else: - tans.append(FreeCAD.Vector(1, 0, 0)) - flags.append(False) - elif self.points[i].tangent: - for j in range(len(self.points[i].points)): - tans.append(self.points[i].tangent) - flags.append(True) - else: - for j in range(len(self.points[i].points)): - tans.append(FreeCAD.Vector(0, 0, 0)) - flags.append(False) - return(tans, flags) - - def update_curve(self): - pts = list() - for p in self.points: - pts += p.points - # FreeCAD.Console.PrintMessage("pts :\n%s\n"%str(pts)) - if len(pts) > 1: - fac = self.param_factor - if self.fp: - fac = self.fp.Parametrization - params = parameterization(pts, fac, self.periodic) - self.curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=self.periodic) - tans, flags = self.compute_tangents() - if any(flags): - if (len(tans) == len(pts)) and (len(flags) == len(pts)): - self.curve.interpolate(Points=pts, Parameters=params, PeriodicFlag=self.periodic, Tangents=tans, TangentFlags=flags) - if self.fp: - self.fp.Shape = self.curve.toShape() - - def build_lines(self): - self.lines = list() - for i in range(len(self.points) - 1): - line = ConnectionLine([self.points[i], self.points[i + 1]]) - line.set_color("blue") - self.lines.append(line) - - def controlCB(self, attr, event_callback): - event = event_callback.getEvent() - if event.getState() == event.UP: - # FreeCAD.Console.PrintMessage("Key pressed : %s\n"%event.getKey()) - if event.getKey() == ord("i"): - self.subdivide() - elif event.getKey() == ord("p"): - self.set_planar() - elif event.getKey() == ord("t"): - self.set_tangents() - elif event.getKey() == ord("q"): - if self.fp: - self.fp.ViewObject.Proxy.doubleClicked(self.fp.ViewObject) - else: - self.quit() - elif event.getKey() == ord("s"): - sel = FreeCADGui.Selection.getSelectionEx() - tup = None - if len(sel) == 1: - tup = (sel[0].Object, sel[0].SubElementNames) - for i in range(len(self.root.selected_objects)): - if isinstance(self.root.selected_objects[i], MarkerOnShape): - self.root.selected_objects[i].sublink = tup - FreeCAD.Console.PrintMessage("Snapped to {}\n".format(str(self.root.selected_objects[i].sublink))) - self.root.selected_objects[i].drag_start() - self.root.selected_objects[i].drag((0, 0, 0.)) - self.root.selected_objects[i].drag_release() - self.update_curve() - elif event.getKey() == ord("l"): - self.toggle_linear() - elif (event.getKey() == 65535) or (event.getKey() == 65288): # Suppr or Backspace - # FreeCAD.Console.PrintMessage("Some objects have been deleted\n") - pts = list() - for o in self.root.dynamic_objects: - if isinstance(o, MarkerOnShape): - pts.append(o) - self.points = pts - self.setup_InteractionSeparator() - self.update_curve() - - def toggle_linear(self): - for o in self.root.selected_objects: - if isinstance(o, ConnectionLine): - o.linear = not o.linear - i = self.lines.index(o) - if i > 0: - self.lines[i - 1].linear = False - if i < len(self.lines) - 1: - self.lines[i + 1].linear = False - o.updateLine() - o.drag_start() - o.drag((0, 0, 0.00001)) - o.drag_release() - self.update_curve() - - def set_tangents(self): - # view_dir = FreeCAD.Vector(0, 0, 1) - view_dir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection() - markers = list() - for o in self.root.selected_objects: - if isinstance(o, MarkerOnShape): - markers.append(o) - elif isinstance(o, ConnectionLine): - markers.extend(o.markers) - if len(markers) > 0: - for m in markers: - if m.tangent: - m.tangent = None - else: - i = self.points.index(m) - if i == 0: - m.tangent = -view_dir - else: - m.tangent = view_dir - self.update_curve() - - def set_planar(self): - # view_dir = FreeCAD.Vector(0, 0, 1) - view_dir = FreeCADGui.ActiveDocument.ActiveView.getViewDirection() - markers = list() - for o in self.root.selected_objects: - if isinstance(o, MarkerOnShape): - markers.append(o) - elif isinstance(o, ConnectionLine): - markers.extend(o.markers) - if len(markers) > 2: - vec0 = markers[0].points[0] - vec1 = markers[-1].points[0] - p0 = FreeCAD.Vector(vec0[0], vec0[1], vec0[2]) - p1 = FreeCAD.Vector(vec1[0], vec1[1], vec1[2]) - pl = Part.Plane(p0, p1, p1 + view_dir) - for o in markers: - if isinstance(o.snap_shape, Part.Vertex): - FreeCAD.Console.PrintMessage("Snapped to Vertex\n") - elif isinstance(o.snap_shape, Part.Edge): - FreeCAD.Console.PrintMessage("Snapped to Edge\n") - c = o.snap_shape.Curve - pts = pl.intersect(c)[0] - new_pts = list() - for ip in o.points: - iv = FreeCAD.Vector(ip[0], ip[1], ip[2]) - dmin = 1e50 - new = None - for op in pts: - ov = FreeCAD.Vector(op.X, op.Y, op.Z) - if iv.distanceToPoint(ov) < dmin: - dmin = iv.distanceToPoint(ov) - new = ov - new_pts.append(new) - o.points = new_pts - elif isinstance(o.snap_shape, Part.Face): - FreeCAD.Console.PrintMessage("Snapped to Face\n") - s = o.snap_shape.Surface - cvs = pl.intersect(s) - new_pts = list() - for ip in o.points: - iv = Part.Vertex(FreeCAD.Vector(ip[0], ip[1], ip[2])) - dmin = 1e50 - new = None - for c in cvs: - e = c.toShape() - d, pts, info = iv.distToShape(e) - if d < dmin: - dmin = d - new = pts[0][1] - new_pts.append(new) - o.points = new_pts - else: - FreeCAD.Console.PrintMessage("Not snapped\n") - new_pts = list() - for ip in o.points: - iv = FreeCAD.Vector(ip[0], ip[1], ip[2]) - u, v = pl.parameter(iv) - new_pts.append(pl.value(u, v)) - o.points = new_pts - for li in self.lines: - li.updateLine() - self.update_curve() - - def subdivide(self): - # get selected lines and subdivide them - pts = list() - new_select = list() - for o in self.lines: - # FreeCAD.Console.PrintMessage("object %s\n"%str(o)) - if isinstance(o, ConnectionLine): - pts.append(o.markers[0]) - if o in self.root.selected_objects: - idx = self.lines.index(o) - FreeCAD.Console.PrintMessage("Subdividing line #{}\n".format(idx)) - p1 = o.markers[0].points[0] - p2 = o.markers[1].points[0] - par1 = self.curve.parameter(FreeCAD.Vector(p1)) - par2 = self.curve.parameter(FreeCAD.Vector(p2)) - midpar = (par1 + par2) / 2.0 - mark = MarkerOnShape([self.curve.value(midpar)]) - pts.append(mark) - new_select.append(mark) - pts.append(self.points[-1]) - self.points = pts - self.setup_InteractionSeparator() - self.root.selected_objects = new_select - self.update_curve() - return(True) - - def quit(self): - self.root.events.removeEventCallback(coin.SoKeyboardEvent.getClassTypeId(), self._controlCB) - self.root.unregister() - # self.root.removeAllChildren() - self.sg.removeChild(self.root) - self.root_inserted = False - - -def get_guide_params(): - sel = FreeCADGui.Selection.getSelectionEx() - pts = list() - for s in sel: - pts.extend(list(zip(s.PickedPoints, s.SubObjects))) - return(pts) - - -def main(): - obj = FreeCAD.ActiveDocument.addObject("Part::Spline", "profile") - tups = get_guide_params() - InterpoCurveEditor(tups, obj) - FreeCAD.ActiveDocument.recompute() - - -if __name__ == '__main__': - main() diff --git a/widgets/CountSelection.py b/widgets/CountSelection.py index 2544ec8..d876cbe 100644 --- a/widgets/CountSelection.py +++ b/widgets/CountSelection.py @@ -22,7 +22,7 @@ import FreeCAD, FreeCADGui #from freecad.trails import ICONPATH -from PySide2.QtWidgets import QLabel +from PySide.QtWidgets import QLabel import copy