Download src/Mod/BIM/ArchFrame.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 12.6 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/ArchFrame.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/BIM/ArchFrame.py
-
curl -L -o ArchFrame.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/BIM/ArchFrame.py
12.6 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * * | |
| # * Copyright (c) 2013 Yorik van Havre <yorik@uncreated.net> * | |
| # * * | |
| # * This file is part of FreeCAD. * | |
| # * * | |
| # * FreeCAD is free software: you can redistribute it and/or modify it * | |
| # * under the terms of the GNU Lesser General Public License as * | |
| # * published by the Free Software Foundation, either version 2.1 of the * | |
| # * License, or (at your option) any later version. * | |
| # * * | |
| # * FreeCAD 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 * | |
| # * Lesser General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Lesser General Public * | |
| # * License along with FreeCAD. If not, see * | |
| # * <https://www.gnu.org/licenses/>. * | |
| # * * | |
| # *************************************************************************** | |
| __title__ = "FreeCAD Arch Frame" | |
| __author__ = "Yorik van Havre" | |
| __url__ = "https://www.freecad.org" | |
| ## @package ArchFrame | |
| # \ingroup ARCH | |
| # \brief The Frame object and tools | |
| # | |
| # This module provides tools to build Frame objects. | |
| # Frames are objects made of a profile and an object with | |
| # edges along which the profile gets extruded | |
| import FreeCAD | |
| import ArchComponent | |
| import Draft | |
| import DraftVecUtils | |
| if FreeCAD.GuiUp: | |
| from PySide.QtCore import QT_TRANSLATE_NOOP | |
| import FreeCADGui | |
| from draftutils.translate import translate | |
| else: | |
| # \cond | |
| def translate(ctxt, txt): | |
| return txt | |
| def QT_TRANSLATE_NOOP(ctxt, txt): | |
| return txt | |
| # \endcond | |
| class _Frame(ArchComponent.Component): | |
| "A parametric frame object" | |
| def __init__(self, obj): | |
| ArchComponent.Component.__init__(self, obj) | |
| self.Type = "Frame" | |
| self.setProperties(obj) | |
| obj.IfcType = "Railing" | |
| def setProperties(self, obj): | |
| pl = obj.PropertiesList | |
| if not "Profile" in pl: | |
| obj.addProperty( | |
| "App::PropertyLink", | |
| "Profile", | |
| "Frame", | |
| QT_TRANSLATE_NOOP("App::Property", "The profile used to build this frame"), | |
| locked=True, | |
| ) | |
| if not "Align" in pl: | |
| obj.addProperty( | |
| "App::PropertyBool", | |
| "Align", | |
| "Frame", | |
| QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "Specifies if the profile must be aligned with the extrusion wires", | |
| ), | |
| locked=True, | |
| ) | |
| obj.Align = True | |
| if not "Offset" in pl: | |
| obj.addProperty( | |
| "App::PropertyVectorDistance", | |
| "Offset", | |
| "Frame", | |
| QT_TRANSLATE_NOOP( | |
| "App::Property", "An offset vector between the base sketch and the frame" | |
| ), | |
| locked=True, | |
| ) | |
| if not "BasePoint" in pl: | |
| obj.addProperty( | |
| "App::PropertyInteger", | |
| "BasePoint", | |
| "Frame", | |
| QT_TRANSLATE_NOOP("App::Property", "Crossing point of the path on the profile."), | |
| locked=True, | |
| ) | |
| if not "ProfilePlacement" in pl: | |
| obj.addProperty( | |
| "App::PropertyPlacement", | |
| "ProfilePlacement", | |
| "Frame", | |
| QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "An optional additional placement to add to the profile before extruding it", | |
| ), | |
| locked=True, | |
| ) | |
| if not "Rotation" in pl: | |
| obj.addProperty( | |
| "App::PropertyAngle", | |
| "Rotation", | |
| "Frame", | |
| QT_TRANSLATE_NOOP( | |
| "App::Property", "The rotation of the profile around its extrusion axis" | |
| ), | |
| locked=True, | |
| ) | |
| if not "Edges" in pl: | |
| obj.addProperty( | |
| "App::PropertyEnumeration", | |
| "Edges", | |
| "Frame", | |
| QT_TRANSLATE_NOOP("App::Property", "The type of edges to consider"), | |
| locked=True, | |
| ) | |
| obj.Edges = [ | |
| "All edges", | |
| "Vertical edges", | |
| "Horizontal edges", | |
| "Bottom horizontal edges", | |
| "Top horizontal edges", | |
| ] | |
| if not "Fuse" in pl: | |
| obj.addProperty( | |
| "App::PropertyBool", | |
| "Fuse", | |
| "Frame", | |
| QT_TRANSLATE_NOOP( | |
| "App::Property", "If true, geometry is fused, otherwise a compound" | |
| ), | |
| locked=True, | |
| ) | |
| def onDocumentRestored(self, obj): | |
| ArchComponent.Component.onDocumentRestored(self, obj) | |
| self.setProperties(obj) | |
| def loads(self, state): | |
| self.Type = "Frame" | |
| def execute(self, obj): | |
| if self.clone(obj): | |
| return | |
| if not self.ensureBase(obj): | |
| return | |
| if not obj.Base: | |
| return | |
| if not obj.Base.Shape: | |
| return | |
| if not obj.Base.Shape.Wires: | |
| return | |
| pl = obj.Placement | |
| if obj.Base.Shape.Solids: | |
| obj.Shape = obj.Base.Shape.copy() | |
| if not pl.isNull(): | |
| obj.Placement = obj.Shape.Placement.multiply(pl) | |
| else: | |
| if not obj.Profile: | |
| return | |
| if not obj.Profile.Shape: | |
| return | |
| if obj.Profile.Shape.findPlane() is None: | |
| return | |
| if not obj.Profile.Shape.Wires: | |
| return | |
| if not obj.Profile.Shape.Faces: | |
| for w in obj.Profile.Shape.Wires: | |
| if not w.isClosed(): | |
| return | |
| import math | |
| import DraftGeomUtils | |
| import Part | |
| baseprofile = obj.Profile.Shape.copy() | |
| if hasattr(obj, "ProfilePlacement"): | |
| if not obj.ProfilePlacement.isNull(): | |
| baseprofile.Placement = obj.ProfilePlacement.multiply(baseprofile.Placement) | |
| if not baseprofile.Faces: | |
| f = [] | |
| for w in baseprofile.Wires: | |
| f.append(Part.Face(w)) | |
| if len(f) == 1: | |
| baseprofile = f[0] | |
| else: | |
| baseprofile = Part.makeCompound(f) | |
| shapes = [] | |
| normal = DraftGeomUtils.getNormal(obj.Base.Shape) | |
| edges = obj.Base.Shape.Edges | |
| if hasattr(obj, "Edges"): | |
| if obj.Edges == "Vertical edges": | |
| rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) | |
| edges = [ | |
| e | |
| for e in edges | |
| if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] | |
| ] | |
| elif obj.Edges == "Horizontal edges": | |
| rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) | |
| edges = [ | |
| e | |
| for e in edges | |
| if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] | |
| ] | |
| elif obj.Edges == "Top horizontal edges": | |
| rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) | |
| edges = [ | |
| e | |
| for e in edges | |
| if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] | |
| ] | |
| edges = sorted(edges, key=lambda x: x.CenterOfMass.z, reverse=True) | |
| z = edges[0].CenterOfMass.z | |
| edges = [e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001] | |
| elif obj.Edges == "Bottom horizontal edges": | |
| rv = obj.Base.Placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) | |
| edges = [ | |
| e | |
| for e in edges | |
| if round(rv.getAngle(e.tangentAt(e.FirstParameter)), 4) in [0, 3.1416] | |
| ] | |
| edges = sorted(edges, key=lambda x: x.CenterOfMass.z) | |
| z = edges[0].CenterOfMass.z | |
| edges = [e for e in edges if abs(e.CenterOfMass.z - z) < 0.00001] | |
| for e in edges: | |
| bvec = DraftGeomUtils.vec(e) | |
| bpoint = e.Vertexes[0].Point | |
| profile = baseprofile.copy() | |
| rot = None # New rotation. | |
| # Supplying FreeCAD.Rotation() with two parallel vectors and | |
| # a null vector may seem strange, but the function is perfectly | |
| # able to handle this. Its algorithm will use default axes in | |
| # such cases. | |
| if obj.Align: | |
| if normal is None: | |
| rot = FreeCAD.Rotation(FreeCAD.Vector(), bvec, bvec, "ZYX") | |
| else: | |
| rot = FreeCAD.Rotation(FreeCAD.Vector(), normal, bvec, "ZYX") | |
| profile.Placement.Rotation = rot | |
| if hasattr(obj, "BasePoint"): | |
| edges = Part.__sortEdges__(profile.Edges) | |
| basepointliste = [profile.Placement.Base] | |
| for edge in edges: | |
| basepointliste.append(DraftGeomUtils.findMidpoint(edge)) | |
| basepointliste.append(edge.Vertexes[-1].Point) | |
| try: | |
| basepoint = basepointliste[obj.BasePoint] | |
| except IndexError: | |
| FreeCAD.Console.PrintMessage( | |
| translate("Arch", "Crossing point not found in profile.") + "\n" | |
| ) | |
| basepoint = basepointliste[0] | |
| else: | |
| basepoint = profile.Placement.Base | |
| delta = bpoint.sub(basepoint) # Translation vector. | |
| if obj.Offset and (not DraftVecUtils.isNull(obj.Offset)): | |
| if rot is None: | |
| delta = delta + obj.Offset | |
| else: | |
| delta = delta + rot.multVec(obj.Offset) | |
| profile.translate(delta) | |
| if obj.Rotation: | |
| profile.rotate(bpoint, bvec, obj.Rotation) | |
| # profile = wire.makePipeShell([profile], True, False, 2) TODO buggy | |
| profile = profile.extrude(bvec) | |
| shapes.append(profile) | |
| if shapes: | |
| if hasattr(obj, "Fuse"): | |
| if obj.Fuse: | |
| if len(shapes) > 1: | |
| s = shapes[0].multiFuse(shapes[1:]) | |
| s = s.removeSplitter() | |
| obj.Shape = s | |
| obj.Placement = pl | |
| return | |
| obj.Shape = Part.makeCompound(shapes) | |
| obj.Placement = pl | |
| class _ViewProviderFrame(ArchComponent.ViewProviderComponent): | |
| "A View Provider for the Frame object" | |
| def __init__(self, vobj): | |
| ArchComponent.ViewProviderComponent.__init__(self, vobj) | |
| def getIcon(self): | |
| import Arch_rc | |
| return ":/icons/Arch_Frame_Tree.svg" | |
| def claimChildren(self): | |
| p = [] | |
| if hasattr(self, "Object"): | |
| if self.Object.Profile: | |
| p = [self.Object.Profile] | |
| return ArchComponent.ViewProviderComponent.claimChildren(self) + p | |