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())