# /********************************************************************** # * * # * 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 Draft import MeshPart as mp import ArchComponent import Civil.Fence.PVPlantFencePost as PVPlantFencePost import PVPlantSite import Utils.PVPlantUtils as utils import copy import math if FreeCAD.GuiUp: import FreeCADGui from PySide import QtCore, QtGui from PySide.QtCore import QT_TRANSLATE_NOOP import PySide.QtGui as QtGui from pivy import coin 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 os from PVPlantResources import DirIcons as DirIcons EAST = FreeCAD.Vector(1, 0, 0) def makePVPlantFence(section, post, path): obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence') Fence(obj) obj.Post = post obj.Base = path if FreeCAD.GuiUp: ViewProviderFence(obj.ViewObject) hide(section) hide(post) hide(path) try: fende_group = FreeCAD.ActiveDocument.Fences except: fende_group = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", 'Fences') fende_group.Label = "Fences" FreeCAD.ActiveDocument.CivilGroup.addObject(fende_group) fende_group.addObject(obj) FreeCAD.ActiveDocument.recompute() return obj def hide(obj): if hasattr(obj, 'ViewObject') and obj.ViewObject: obj.ViewObject.Visibility = False def get_parameter_from_v0_old(edge, offset): """ Return parameter at distance offset from edge.Vertexes[0].sb method in Part.TopoShapeEdge??? """ import DraftVecUtils lpt = edge.valueAt(edge.getParameterByLength(0)) vpt = edge.Vertexes[0].Point if not DraftVecUtils.equals(vpt, lpt): # this edge is flipped length = edge.Length - offset else: # this edge is right way around length = offset return edge.getParameterByLength(length) def get_parameter_from_v0(edge, offset): """Parámetro a distancia offset desde el primer vértice""" lpt = edge.valueAt(edge.getParameterByLength(0)) vpt = edge.Vertexes[0].Point if not vpt.isEqual(lpt, 1e-6): return edge.getParameterByLength(edge.Length - offset) return edge.getParameterByLength(offset) def calculatePlacement(globalRotation, edge, offset, RefPt, xlate, align, normal=None): placement = FreeCAD.Placement() placement.Rotation = globalRotation placement.move(RefPt + xlate) if not align: return placement t = edge.tangentAt(get_parameter_from_v0(edge, offset)).normalize() n = normal or FreeCAD.Vector(0, 0, 1) b = t.cross(n).normalize() # Asegurar sistema de coordenadas derecho if n.dot(t.cross(b)) < 0: b = -b # Construir matriz rotation_matrix = FreeCAD.Matrix( t.x, b.x, n.x, 0, t.y, b.y, n.y, 0, t.z, b.z, n.z, 0, 0, 0, 0, 1 ) placement.Rotation = FreeCAD.Rotation(rotation_matrix) return placement def calculatePlacementsOnPath(shapeRotation, pathwire, count, xlate, align): """ Calculates the placements of a shape along a given path so that each copy will be distributed evenly - shapeRotation: rotation offset - pathwire: path where place the shapes - count: number of sections - xlate: transformation offset """ import Part import DraftGeomUtils closedpath = DraftGeomUtils.isReallyClosed(pathwire) normal = FreeCAD.Vector(0, 0, 1) path = Part.__sortEdges__(pathwire.Edges) ends = [] cdist = 0 # ----------------------------------------------------------------------------------------------------------------- totalEdges = len(path) if not closedpath: totalEdges -= 1 # ----------------------------------------------------------------------------------------------------------------- for e in path: # find cumulative edge end distance cdist += e.Length ends.append(cdist) placements = [] # place the start shape pt = path[0].Vertexes[0].Point placements.append(calculatePlacement(shapeRotation, path[0], 0, pt, xlate, align, normal)) # closed path doesn't need shape on last vertex if not closedpath: # place the end shape pt = path[-1].Vertexes[-1].Point placements.append(calculatePlacement(shapeRotation, path[-1], path[-1].Length, pt, xlate, align, normal)) if count < 3: return placements # place the middle shapes if closedpath: stop = count else: stop = count - 1 step = float(cdist) / stop travel = step for i in range(1, stop): # which edge in path should contain this shape? # avoids problems with float math travel > ends[-1] iend = len(ends) - 1 for j in range(0, len(ends)): if travel <= ends[j]: iend = j break # place shape at proper spot on proper edge remains = ends[iend] - travel offset = path[iend].Length - remains pt = path[iend].valueAt(get_parameter_from_v0(path[iend], offset)) placements.append(calculatePlacement(shapeRotation, path[iend], offset, pt, xlate, align, normal)) travel += step return placements class Fence(ArchComponent.Component): def __init__(self, obj): ArchComponent.Component.__init__(self, obj) self.setProperties(obj) self.Posts = [] self.Foundations = [] # Does a IfcType exist? # obj.IfcType = "Fence" def setProperties(self, obj): ArchComponent.Component.setProperties(self, obj) pl = obj.PropertiesList if not "Gate" in pl: obj.addProperty("App::PropertyLinkList", "Gate", "Fence", "A single fence post") if not "Post" in pl: obj.addProperty("App::PropertyLink", "Post", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")) if not "Foundation" in pl: obj.addProperty("App::PropertyLength", "Foundation", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")) if not "Depth" in pl: obj.addProperty("App::PropertyLength", "Depth", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Depth = 650 if not "Gap" in pl: obj.addProperty("App::PropertyLength", "Gap", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Gap = 4000 if not "Angle" in pl: obj.addProperty("App::PropertyAngle", "Angle", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Angle = 60 if not "MeshOffsetZ" in pl: obj.addProperty("App::PropertyLength", "MeshOffsetZ", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshOffsetZ = 0 if not "MeshHeight" in pl: obj.addProperty("App::PropertyLength", "MeshHeight", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).MeshHeight = 2000 if not "Reinforce" in pl: obj.addProperty("App::PropertyLength", "Reinforce", "Fence", QT_TRANSLATE_NOOP("App::Property", "A single fence post")).Reinforce = 50000 ########## Datos informativos: if not "NumberOfSections" in pl: obj.addProperty("App::PropertyQuantity", "NumberOfSections", "Output", 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::PropertyQuantity", "NumberOfPosts", "Output", QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence")) obj.setEditorMode("NumberOfPosts", 1) if not "Length" in pl: obj.addProperty("App::PropertyLength", "Length", "Output", QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence")) obj.setEditorMode("Length", 1) if not "Concrete" in pl: obj.addProperty("App::PropertyVolume", "Concrete", "Output", "Concrete Volume") obj.setEditorMode("Concrete", 1) if not "PlacementList" in pl: obj.addProperty("App::PropertyPlacementList", "PlacementList", "Output", QT_TRANSLATE_NOOP("App::Property", "The number of posts used to build the fence")) obj.setEditorMode("Length", 1) self.Type = "PVPlatFence" def __getstate__(self): if hasattr(self, 'sectionFaceNumbers'): return (self.sectionFaceNumbers) return None def __setstate__(self, state): if state is not None and isinstance(state, tuple): self.sectionFaceNumbers = state[0] return None def execute(self, obj): if not obj.Base or not obj.Post: return # 1. Preparar trazado base pathwire = self.calculatePathWire(obj) pathwire = utils.getProjected(pathwire, FreeCAD.Vector(0, 0, 1)) pathwire = utils.simplifyWire(pathwire) if not pathwire or not pathwire.Edges: return # 2. Proyectar sobre terreno (con caché) self.Posts = [] self.Foundations = [] site = PVPlantSite.get() segments = mp.projectShapeOnMesh(pathwire, site.Terrain.Mesh, FreeCAD.Vector(0, 0, 1)) points=[] for segment in segments: points.extend(segment) pathwire = Part.makePolygon(points) if pathwire is None: return sectionLength = obj.Gap.Value postLength = 0 #obj.Post.Diameter.Value #considerarlo 0 porque no influye postPlacements = [] pathsegments = self.calculateSegments(obj, pathwire) count = 0 drawFirstPost = True pathLength = 0 for segment in pathsegments: segwire = Part.Wire(Part.__sortEdges__(segment)) pathLength += segwire.Length obj.NumberOfSections = self.calculateNumberOfSections(segwire.Length, sectionLength, postLength) obj.NumberOfPosts = obj.NumberOfSections + 1 count += obj.NumberOfSections downRotation = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), -90) placements = self.calculatePostPlacements(obj, segwire, downRotation) if drawFirstPost: drawFirstPost = False else: placements.pop(0) postPlacements.extend(placements) # 5. Generar geometría postShapes, postFoundation = self.calculatePosts(obj, postPlacements) mesh = self.calculate_sections(obj, postPlacements) postShapes = Part.makeCompound(postShapes) postFoundation = Part.makeCompound(postFoundation) # 6. Crear forma final obj.Shape = Part.makeCompound([postShapes, postFoundation, mesh]) # 7. Actualizar propiedades obj.NumberOfSections = count obj.NumberOfPosts = count + 1 obj.Length = pathLength obj.Concrete = count * postFoundation.SubShapes[0].Volume def calculateSegments(self, obj, pathwire): ''' Calcular los segmentos de la ruta ''' import math segments = [] segment = [pathwire.Edges[0]] segments.append(segment) for ind in range(len(pathwire.Edges) - 1): ed1 = pathwire.Edges[ind] ed2 = pathwire.Edges[ind + 1] vec1 = ed1.Vertexes[1].Point - ed1.Vertexes[0].Point vec2 = ed2.Vertexes[1].Point - ed2.Vertexes[0].Point angle = math.degrees(vec1.getAngle(vec2)) if angle > obj.Angle.Value: segment = [] segments.append(segment) segment.append(ed2) return segments def calculateNumberOfSections(self, pathLength, sectionLength, postLength): withoutLastPost = pathLength - postLength realSectionLength = sectionLength + postLength return math.ceil(withoutLastPost / realSectionLength) def calculatePostPlacements(self, obj, pathwire, rotation): postWidth = obj.Post.Diameter.Value transformationVector = FreeCAD.Vector(0, - postWidth / 2, 0) placements = calculatePlacementsOnPath(rotation, pathwire, int(obj.NumberOfSections) + 1, transformationVector, True) # The placement of the last object is always the second entry in the list. # So we move it to the end: if len(placements) > 1: placements.append(placements.pop(1)) return placements def calculatePosts(self, obj, postPlacements): posts = [] foundations = [] for placement in postPlacements: new_post = obj.Post.Shape.copy() new_post = Part.Solid(new_post) new_post.Placement = placement new_post.Placement.Base.z += 100 posts.append(new_post) foundation = Part.makeCylinder(150, 700) foundation.Placement = placement foundation.Placement.Base.z -= obj.Depth.Value #foundation = foundation.cut(new_post) foundations.append(foundation) return posts, foundations def calculate_sections(self, obj, postPlacements): offsetz = FreeCAD.Vector(0, 0, obj.MeshOffsetZ.Value) meshHeight = FreeCAD.Vector(0, 0, obj.MeshHeight.Value) points_down = [] points_up = [] for i in range(len(postPlacements) - 1): p1 = postPlacements[i].Base + offsetz p2 = postPlacements[i + 1].Base + offsetz p3 = p1 + meshHeight p4 = p2 + meshHeight points_down.extend([p1, p2]) points_up.extend([p3, p4]) shape = Part.makeRuledSurface(Part.makePolygon(points_down), Part.makePolygon(points_up)) return shape def calculatePathWire(self, obj): if obj.Base: wire = None if hasattr(obj.Base.Shape, 'Wires') and obj.Base.Shape.Wires: wire = obj.Base.Shape.Wires[0] elif obj.Base.Shape.Edges: wire = Part.Wire(obj.Base.Shape.Edges) return wire return None class ViewProviderFence(ArchComponent.ViewProviderComponent): "A View Provider for the Fence object" def __init__(self, vobj): ArchComponent.ViewProviderComponent.__init__(self, vobj) vobj.Proxy = self #vobj.addExtension("Gui::ViewProviderGroupExtensionPython") def getIcon(self): return str(os.path.join(DirIcons, "fence.svg")) def attach(self, vobj): ''' Create Object visuals in 3D view. ''' self.Object = vobj.Object # 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() # Face root. faces = coin.SoSeparator() faces.addChild(shape_hints) faces.addChild(self.face_material) faces.addChild(mat_binding) faces.addChild(self.geo_coords) faces.addChild(self.triangles) # Highlight for selection. highlight = coin.SoType.fromName('SoFCSelection').createInstance() highlight.style = 'EMISSIVE_DIFFUSE' faces.addChild(shape_hints) highlight.addChild(self.edge_material) highlight.addChild(mat_binding) highlight.addChild(self.edge_style) highlight.addChild(self.geo_coords) highlight.addChild(self.triangles) def updateData(self, obj, prop): ''' Update Object visuals when a data property changed. ''' origin = PVPlantSite.get() base = copy.deepcopy(origin.Origin) base.z = 0 #print(" - Propiedad: ", prop) if prop == "Shape": shape = obj.getPropertyByName(prop) # Get GeoOrigin. points = [ver.Point for ver in shape.Vertexes] # Set GeoCoords. geo_system = ["UTM", origin.UtmZone, "FLAT"] self.geo_coords.geoSystem.setValues(geo_system) self.geo_coords.point.values = points def claimChildren(self): children = [] if self.Object.Post: children.append(self.Object.Post) if self.Object.Base: children.append(self.Object.Base) if self.Object.Gate: children.append(self.Object.Gate) return children class FenceTaskPanel: '''The TaskPanel to setup the fence''' def __init__(self): self.section = None self.post = None self.path = None # form: ----------------------------------------------------------------------------------- self.formFence = QtGui.QWidget() self.formFence.resize(800, 640) self.formFence.setWindowTitle("Fence setup") self.formFence.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg"))) self.grid = QtGui.QGridLayout(self.formFence) # parameters self.labelPath = QtGui.QLabel() self.labelPath.setText("Recorrido:") self.linePath = QtGui.QLineEdit(self.formFence) self.linePath.setObjectName(_fromUtf8("lineEdit1")) self.linePath.readOnly = True self.grid.addWidget(self.labelPath, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.linePath, self.grid.rowCount() - 1, 1, 1, 1) self.buttonPathSelect = QtGui.QPushButton('Add') self.grid.addWidget(self.buttonPathSelect, 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.label = QtGui.QLabel() self.label.setText("Separación entre apoyos:") self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1) self.labelInterval = QtGui.QLabel() self.labelInterval.setText("Intervalo:") self.valueInterval = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueInterval.setText("4,0 m") self.grid.addWidget(self.labelInterval, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueInterval, self.grid.rowCount() - 1, 1, 1, 2) 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.label = QtGui.QLabel() self.label.setText("Mayado:") self.grid.addWidget(self.label, self.grid.rowCount(), 0, 1, -1) self.labelHeight = QtGui.QLabel() self.labelHeight.setText("Altura:") self.valueHeight = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueHeight.setText("2 m") self.grid.addWidget(self.labelHeight, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueHeight, self.grid.rowCount() - 1, 1, 1, 2) self.labelOffset = QtGui.QLabel() self.labelOffset.setText("Separación del suelo:") self.valueOffset = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueOffset.setText("0 m") self.grid.addWidget(self.labelOffset, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueOffset, self.grid.rowCount() - 1, 1, 1, 2) self.buttonPathSelect.clicked.connect(self.addPath) self.valueInterval.valueChanged.connect(self.SetupGrid) self.valueHeight.valueChanged.connect(self.SetupGrid) # self.valueDepth.valueChanged.connect(self.SetupPost) # Form para configurar el poste: ------------------------------------------------------------------- self.formPost = QtGui.QWidget() self.formPost.resize(800, 640) self.formPost.setWindowTitle("Post setup") self.formPost.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg"))) self.grid = QtGui.QGridLayout(self.formPost) # parameters self.labelDiameter = QtGui.QLabel() self.labelDiameter.setText("Diámetro:") self.valueDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueDiameter.setText("48 mm") self.grid.addWidget(self.labelDiameter, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueDiameter, self.grid.rowCount() - 1, 1, 1, 1) self.labelLength = QtGui.QLabel() self.labelLength.setText("Longitud:") self.valueLength = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueLength.setText("3,0 m") self.grid.addWidget(self.labelLength, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueLength, self.grid.rowCount() - 1, 1, 1, 1) self.labelDepth = QtGui.QLabel() self.labelDepth.setText("Profundidad:") self.valueDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueDepth.setText("700,0 mm") self.grid.addWidget(self.labelDepth, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueDepth, self.grid.rowCount() - 1, 1, 1, 1) self.valueDiameter.valueChanged.connect(self.SetupPost) self.valueLength.valueChanged.connect(self.SetupPost) self.valueDepth.valueChanged.connect(self.SetupPost) # Form para configurar la zapata: ---------------------------------------------------------- self.formFoundation = QtGui.QWidget() self.formFoundation.resize(800, 640) self.formFoundation.setWindowTitle("Post setup") self.formFoundation.setWindowIcon(QtGui.QIcon(os.path.join(DirIcons, "contours.svg"))) self.grid = QtGui.QGridLayout(self.formFoundation) # parameters self.labelFoundationDiameter = QtGui.QLabel() self.labelFoundationDiameter.setText("Cimentación:") self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1) self.labelFoundationDiameter = QtGui.QLabel() self.labelFoundationDiameter.setText("Diámetro:") self.valueFoundationDiameter = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueFoundationDiameter.setText("200,0 mm") self.grid.addWidget(self.labelFoundationDiameter, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueFoundationDiameter, self.grid.rowCount() - 1, 1, 1, 1) self.labelFoundationDepth = QtGui.QLabel() self.labelFoundationDepth.setText("Profundidad:") self.valueFoundationDepth = FreeCADGui.UiLoader().createWidget("Gui::InputField") self.valueFoundationDepth.setText("700,0 mm") self.grid.addWidget(self.labelFoundationDepth, self.grid.rowCount(), 0, 1, 1) self.grid.addWidget(self.valueFoundationDepth, self.grid.rowCount() - 1, 1, 1, 1) self.form = [self.formFence, self.formPost, self.formFoundation] # valores iniciales y creación del la valla: self.post = PVPlantFencePost.makeFencePost() self.post.Label = "Post" FreeCAD.ActiveDocument.recompute() self.fence = makePVPlantFence(self.section, self.post, self.path) def addPath(self): sel = FreeCADGui.Selection.getSelection() if len(sel) > 0: self.path = sel[0] self.linePath.setText(self.path.Label) self.fence.Base = self.path FreeCAD.ActiveDocument.recompute() def SetupPost(self): if self.post.Diameter != FreeCAD.Units.Quantity(self.valueDiameter.text()).Value: self.post.Diameter = FreeCAD.Units.Quantity(self.valueDiameter.text()).Value if self.post.Length != FreeCAD.Units.Quantity(self.valueLength.text()).Value: self.post.Length = FreeCAD.Units.Quantity(self.valueLength.text()).Value if self.post.Placement.Base.z != -FreeCAD.Units.Quantity(self.valueDepth.text()).Value: self.post.Placement.Base.z = -FreeCAD.Units.Quantity(self.valueDepth.text()).Value self.fence.Depth = FreeCAD.Units.Quantity(self.valueDepth.text()).Value FreeCAD.ActiveDocument.recompute() def SetupGrid(self): return if self.path.End.x != FreeCAD.Units.Quantity(self.valueInterval.text()).Value: self.path.End.x = FreeCAD.Units.Quantity(self.valueInterval.text()).Value if self.section.LengthFwd != FreeCAD.Units.Quantity(self.valueHeight.text()).Value: self.section.LengthFwd = FreeCAD.Units.Quantity(self.valueHeight.text()).Value FreeCAD.ActiveDocument.recompute() def makeGrid(self): return None import Draft p1 = FreeCAD.Vector(0, 0, 0) p2 = FreeCAD.Vector(4000, 0, 0) line = Draft.makeLine(p1, p2) section = FreeCAD.ActiveDocument.addObject('Part::Extrusion', 'Extrude') section.Base = line section.DirMode = "Custom" section.Dir = FreeCAD.Vector(0.0, 0.0, 1.0) section.DirLink = None section.LengthFwd = 2000.0 section.LengthRev = 0.0 section.Solid = False section.Reversed = False section.Symmetric = False section.TaperAngle = 0.0 section.TaperAngleRev = 0.0 line.Visibility = False return section # Commands --------------------------------------------------------------------------------- class CommandPVPlantFence: "the PVPlant Fence command definition" def GetResources(self): return {'Pixmap': str(os.path.join(DirIcons, "fence.svg")), 'Accel': "C, F", 'MenuText': QT_TRANSLATE_NOOP("PVPlantFence", "Fence"), 'ToolTip': QT_TRANSLATE_NOOP("PVPlantFence", "Creates a fence object from a selected section, post and path")} def IsActive(self): return not FreeCAD.ActiveDocument is None def Activated(self): self.TaskPanel = FenceTaskPanel() FreeCADGui.Control.showDialog(self.TaskPanel) if FreeCAD.GuiUp: class CommandFenceGroup: def GetCommands(self): return tuple(['PVPlantFence', 'PVPlantGate', 'PVPlantFencePost' ]) def GetResources(self): return {'MenuText': QT_TRANSLATE_NOOP("", 'PVPlantFence'), 'ToolTip': QT_TRANSLATE_NOOP("", 'PVPlantFence') } def IsActive(self): site = FreeCAD.ActiveDocument.getObject("Site") return (not (FreeCAD.ActiveDocument is None) and not (site is None) and not (site.Terrain is None)) import Civil.Fence.PVPlantFenceGate as PVPlantFenceGate FreeCADGui.addCommand('PVPlantFence', CommandPVPlantFence()) FreeCADGui.addCommand('PVPlantGate', PVPlantFenceGate.CommandPVPlantGate()) FreeCADGui.addCommand('PVPlantFencePost', PVPlantFencePost.CommandFencePost()) #FreeCADGui.addCommand('PVPlantFenceGroup', CommandFenceGroup())