# /********************************************************************** # * * # * Copyright (c) 2021 Javier Braña * # * * # * This program is free software; you can redistribute it and/or modify* # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307* # * USA * # * * # *********************************************************************** import FreeCAD import Part import PVPlantSite import Utils.PVPlantUtils as utils import MeshPart as mp if FreeCAD.GuiUp: import FreeCADGui from DraftTools import translate else: # \cond def translate(ctxt,txt): return txt def QT_TRANSLATE_NOOP(ctxt,txt): return txt # \endcond import os from PVPlantResources import DirIcons as DirIcons __title__ = "PVPlant Areas" __author__ = "Javier Braña" __url__ = "http://www.sogos-solar.com" import PVPlantResources from PVPlantResources import DirIcons as DirIcons Dir3dObjects = os.path.join(PVPlantResources.DirResources, "3dObjects") ''' Default Area: ''' def makeArea(points = None, type = 0): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Area") if type == 0: _Area(obj) _ViewProviderArea(obj.ViewObject) elif type == 1: _ForbiddenArea(obj) _ViewProviderForbiddenArea(obj.ViewObject) if points: obj.Points = points return obj class _Area: def __init__(self, obj): ''' Initialize the Area object ''' self.Type = None self.obj = None def setProperties(self, obj): pl = obj.PropertiesList if not ("Base" in pl): obj.addProperty("App::PropertyLink", "Base", "Area", "Base wire" ).Base = None if not ("Type" in pl): obj.addProperty("App::PropertyString", "Type", "Area", "Points that define the area" ).Type = "Area" obj.setEditorMode("Type", 1) self.Type = obj.Type obj.Proxy = self def onDocumentRestored(self, obj): """ Method run when the document is restored """ self.setProperties(obj) class _ViewProviderArea: def __init__(self, vobj): self.Object = vobj.Object vobj.Proxy = self def attach(self, vobj): ''' Create Object visuals in 3D view. ''' self.Object = vobj.Object return def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "area.svg")) ''' def claimChildren(self): """ Provides object grouping """ return self.Object.Group ''' def setEdit(self, vobj, mode=0): """ Enable edit """ return True def unsetEdit(self, vobj, mode=0): """ Disable edit """ return False def doubleClicked(self, vobj): """ Detect double click """ pass def setupContextMenu(self, obj, menu): """ Context menu construction """ pass def edit(self): """ Edit callback """ pass def __getstate__(self): """ Save variables to file. """ return None def __setstate__(self, state): """ Get variables from file. """ return None ''' Frame Area ''' def makeFramedArea(base = None, select = None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "FrameArea") FrameArea(obj) ViewProviderFrameArea(obj.ViewObject) if base: obj.Base = base if select: frames = [] for o in select: if hasattr(o, "Proxy") and (o.Proxy.Type == "Tracker"): if not (o in frames): frames.append(o) if len(frames) > 0: print(frames) obj.Frames = frames try: group = FreeCAD.ActiveDocument.FrameZones except: group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'FrameZones') group.Label = "FrameZones" group.addObject(obj) return obj class FrameArea(_Area): def __init__(self, obj): _Area.__init__(self, obj) self.setProperties(obj) self.obj = None def setProperties(self, obj): _Area.setProperties(self, obj) pl = obj.PropertiesList if not ("Frames" in pl): obj.addProperty("App::PropertyLinkList", "Frames", "Area", "All the frames inside this area." ) if not ("FrameNumber" in pl): obj.addProperty("App::PropertyInteger", "FrameNumber", "Area", "The number of frames inside this area." ) obj.setEditorMode("FrameNumber", 1) if not ("Type" in pl): obj.addProperty("App::PropertyString", "Type", "Base", "The facemaker type to use to build the profile of this object" ).Type = "FrameArea" obj.setEditorMode("Type", 1) self.Type = "FrameArea" obj.Proxy = self self.obj = obj def onDocumentRestored(self, obj): """Method run when the document is restored.""" self.setProperties(obj) def onBeforeChange(self, obj, prop): ''' ''' pass def onChanged(self, obj, prop): if prop == "Base": if obj.Base.Shape is None: obj.Shape = Part.Shape() return import Utils.PVPlantUtils as utils base = obj.Base.Shape land = PVPlantSite.get().Terrain.Mesh vec = FreeCAD.Vector(0,0,1) wire = utils.getProjected(base, vec) tmp = mp.projectShapeOnMesh(wire, land, vec) shape = Part.makeCompound([]) for section in tmp: shape.add(Part.makePolygon(section)) obj.Shape = shape if prop == "Frames": lf = [] for o in obj.Frames: if not hasattr(o, "Proxy"): continue if o.Proxy.Type == "Tracker": lf.append(obj) obj.Frames = lf obj.FramesNumber = len(obj.Frames) def addFrame(self, frame): list = self.obj.Frames.copy() list.append(frame) self.obj.Frames = sorted(list, key=lambda x: x.Name) def execute(self, obj): ''' execute ''' #_Area.execute(self, obj) obj.FrameNumber = len(obj.Frames) class ViewProviderFrameArea(_ViewProviderArea): def __init__(self, vobj): ''' Set view properties. ''' self.Object = vobj.Object vobj.Proxy = self def getIcon(self): ''' Return object treeview icon ''' return str(os.path.join(DirIcons, "FrameArea.svg")) def claimChildren(self): """ Provides object grouping """ children = [] if self.Object.Base: children.append(self.Object.Base) return children ''' offsets ''' def makeOffsetArea(base = None, val=None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "OffsetArea") OffsetArea(obj) obj.Base = base ViewProviderOffsetArea(obj.ViewObject) if val: obj.Distance = val offsets = None try: offsetsgroup = FreeCAD.ActiveDocument.Offsets except: offsetsgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Offsets') offsetsgroup.Label = "Offsets" offsetsgroup.addObject(obj) return obj class OffsetArea(_Area): def __init__(self, obj): _Area.__init__(self, obj) self.setProperties(obj) def setProperties(self, obj): _Area.setProperties(self, obj) pl = obj.PropertiesList if not ("OffsetDistance" in pl): obj.addProperty("App::PropertyDistance", "OffsetDistance", "OffsetArea", "Base wire" ) self.Type = obj.Type = "Area_Offset" def onDocumentRestored(self, obj): """Method run when the document is restored.""" self.setProperties(obj) def execute(self, obj): import Utils.PVPlantUtils as utils base = obj.Base.Shape land = PVPlantSite.get().Terrain.Mesh vec = FreeCAD.Vector(0, 0, 1) wire = utils.getProjected(base, vec) wire = wire.makeOffset2D(obj.OffsetDistance.Value, 2, False, False, True) tmp = mp.projectShapeOnMesh(wire, land, vec) pts = [] for section in tmp: pts.extend(section) obj.Shape = Part.makePolygon(pts) class ViewProviderOffsetArea(_ViewProviderArea): def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "offset.svg")) def claimChildren(self): """ Provides object grouping """ children = [] if self.Object.Base: children.append(self.Object.Base) return children ''' Forbidden Area: ''' def makeProhibitedArea(base = None): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "ProhibitedArea") ProhibitedArea(obj) ViewProviderForbiddenArea(obj.ViewObject) if base: obj.Base = base try: group = FreeCAD.ActiveDocument.Exclusion except: group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Exclusion') group.Label = "Exclusions" group.addObject(obj) return obj class ProhibitedArea(OffsetArea): def __init__(self, obj): OffsetArea.__init__(self, obj) self.setProperties(obj) def setProperties(self, obj): OffsetArea.setProperties(self, obj) self.Type = obj.Type = "ProhibitedArea" obj.Proxy = self def onDocumentRestored(self, obj): """Method run when the document is restored.""" self.setProperties(obj) class ViewProviderForbiddenArea(_ViewProviderArea): def getIcon(self): ''' Return object treeview icon ''' return str(os.path.join(DirIcons, "area_forbidden.svg")) def claimChildren(self): """ Provides object grouping """ children = [] if self.Object.Base: children.append(self.Object.Base) return children ''' PV Area: ''' def makePVSubplant(): obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "PVSubplant") PVSubplant(obj) ViewProviderPVSubplant(obj.ViewObject) return obj class PVSubplant: def __init__(self, obj): self.setProperties(obj) self.Type = None self.obj = None def setProperties(self, obj): pl = obj.PropertiesList if not "Setup" in pl: obj.addProperty("App::PropertyEnumeration", "Setup", "PVSubplant", "The facemaker type to use to build the profile of this object" ).Setup = ["String Inverter", "Central Inverter"] if not ("Frames" in pl): obj.addProperty("App::PropertyLinkList", "Frames", "PVSubplant", "List of frames" ) if not ("Inverters" in pl): obj.addProperty("App::PropertyLinkList", "Inverters", "PVSubplant", "List of Inverters" ) if not ("Cables" in pl): obj.addProperty("App::PropertyLinkList", "Cables", "PVSubplant", "List of Cables" ) if not "TotalPVModules" in pl: obj.addProperty("App::PropertyQuantity", "TotalPVModules", "PVSubplant", "The facemaker type to use to build the profile of this object" ) if not "TotalPowerDC" in pl: obj.addProperty("App::PropertyQuantity", "TotalPowerDC", "PVSubplant", "The facemaker type to use to build the profile of this object" ) if not "Color" in pl: obj.addProperty("App::PropertyColor", "Color", "PVSubplant", "Color" ) if not "Type" in pl: obj.addProperty("App::PropertyString", "Type", "Base", "The facemaker type to use to build the profile of this object" ).Type = "PVSubplant" obj.setEditorMode("Type", 1) self.Type = obj.Type self.obj = obj obj.Proxy = self def onDocumentRestored(self, obj): """Method run when the document is restored.""" self.setProperties(obj) def onChanged(self, obj, prop): if prop == "Color": obj.ViewObject.LineColor = obj.Color obj.ViewObject.PointColor = obj.Color if prop == "Setup": if obj.Setup == "Central Inverter": if not ("StringBoxes" in obj.PropertiesList): obj.addProperty("App::PropertyLinkList", "StringBoxes", "PVSubplant", "List of String-Boxes" ) else: if hasattr(obj, "StringBoxes"): obj.removeProperty("StringBoxes") if prop == "Frames": import numpy as np # 1. Dibujar contorno: maxdist = 6000 if len(obj.Frames) > 0: onlyframes = [] for object in obj.Frames: if object.Name.startswith("Tracker"): onlyframes.append(object) obj.Frames = onlyframes pts = [] for frame in obj.Frames: for panel in frame.Shape.SubShapes[0].SubShapes[0].SubShapes: zm = panel.BoundBox.ZMax for i in range(8): pt = panel.BoundBox.getPoint(i) if pt.z == zm: pts.append(pt) import MeshTools.Triangulation as Triangulation import MeshTools.MeshGetBoundary as mgb m = Triangulation.Triangulate(np.array(pts), MaxlengthLE=maxdist, use3d=False) b = mgb.get_boundary(m) obj.Shape = b # 2. rellenar información power = 0 modulenum = 0 for frame in obj.Frames: modules = frame.Setup.ModuleColumns * frame.Setup.ModuleRows power += frame.Setup.ModulePower.Value * modules modulenum += modules obj.TotalPowerDC = power obj.TotalPVModules = modulenum obj.ViewObject.LineWidth = 5 def execute(self, obj): ''' ''' pass class ViewProviderPVSubplant: def __init__(self, vobj): ''' Set view properties. ''' self.Object = None vobj.Proxy = self def attach(self, vobj): ''' Create Object visuals in 3D view. ''' self.Object = vobj.Object return def getIcon(self): ''' Return object treeview icon. ''' return str(os.path.join(DirIcons, "subplant.svg")) def claimChildren(self): """ Provides object grouping """ children = [] if self.Object.Frames: children.extend(self.Object.Frames) return children def __getstate__(self): return None '''def onDelete(self, feature, subelements): try: for obj in self.claimChildren(): obj.ViewObject.show() except Exception as err: FreeCAD.Console.PrintError("Error in onDelete: " + str(err)) return True def canDragObjects(self): return True def canDropObjects(self): return True def canDragObject(self, dragged_object): return True def canDropObject(self, incoming_object): return hasattr(incoming_object, 'Shape') def dragObject(self, selfvp, dragged_object): objs = self.Object.Objects objs.remove(dragged_object) self.Object.Objects = objs def dropObject(self, selfvp, incoming_object): self.Object.Objects = self.Object.Objects + [incoming_object] def setEdit(self, vobj, mode=0): """ Enable edit """ return True def unsetEdit(self, vobj, mode=0): """ Disable edit """ return False def doubleClicked(self, vobj): """ Detect double click """ pass def setupContextMenu(self, obj, menu): """ Context menu construction """ pass def edit(self): """ Edit callback """ pass def __getstate__(self): """ Save variables to file. """ return None def __setstate__(self,state): """ Get variables from file. """ return None''' # Comandos: ----------------------------------------------------------------------------------------------------------- class CommandDivideArea: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area.svg")), 'Accel': "A, D", 'MenuText': "Divide Area", 'ToolTip': "Allowed Area"} def Activated(self): sel = FreeCADGui.Selection.getSelection()[0] def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False class CommandBoundary: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area.svg")), 'Accel': "A, B", 'MenuText': "Area", 'ToolTip': "Allowed Area"} def Activated(self): sel = FreeCADGui.Selection.getSelection()[0] obj = makeArea([ver.Point for ver in sel.Shape.Vertexes]) #taskd = _PVPlantPlacementTaskPanel() #FreeCADGui.Control.showDialog(taskd) def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False class CommandFrameArea: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "FrameArea.svg")), 'Accel': "A, F", 'MenuText': "Frame Area", 'ToolTip': "Frame Area"} def Activated(self): sel = FreeCADGui.Selection.getSelection() makeFramedArea(None, sel) def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False class CommandProhibitedArea: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "area_forbidden.svg")), 'Accel': "A, F", 'MenuText': "Prohibited Area", 'ToolTip': "Prohibited Area"} def Activated(self): sel = FreeCADGui.Selection.getSelection() makeProhibitedArea(sel[0]) def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False class CommandPVSubplant: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "subplant.svg")), 'Accel': "A, P", 'MenuText': "PV Subplant", 'ToolTip': "PV Subplant"} def Activated(self): area = makePVSubplant() sel = FreeCADGui.Selection.getSelection() for obj in sel: if obj.Name[:7] == "Tracker": frame_list = area.Frames frame_list.append(obj) area.Frames = frame_list def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False class CommandOffsetArea: def GetResources(self): return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "offset.svg")), 'Accel': "A, O", 'MenuText': "OffsetArea", 'ToolTip': "OffsetArea"} def Activated(self): sel = FreeCADGui.Selection.getSelection() base = None if sel: base = sel[0] obj = makeOffsetArea(base) def IsActive(self): if FreeCAD.ActiveDocument: return True else: return False if FreeCAD.GuiUp: class CommandAreaGroup: def GetCommands(self): return tuple([#'Area', 'FrameArea', 'ForbiddenArea', 'PVSubplant', 'OffsetArea' ]) def GetResources(self): return {'MenuText': 'Areas', 'ToolTip': 'Areas' } def IsActive(self): return not FreeCAD.ActiveDocument is None #FreeCADGui.addCommand('Area', CommandBoundary()) FreeCADGui.addCommand('FrameArea', CommandFrameArea()) FreeCADGui.addCommand('ForbiddenArea', CommandProhibitedArea()) FreeCADGui.addCommand('PVSubplant', CommandPVSubplant()) FreeCADGui.addCommand('OffsetArea', CommandOffsetArea()) FreeCADGui.addCommand('PVPlantAreas', CommandAreaGroup())