# SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * * # * Copyright (c) 2011 Yorik van Havre * # * * # * 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 * # * . * # * * # *************************************************************************** __title__ = "FreeCAD Arch API" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" ## \defgroup ARCH Arch # \ingroup PYTHONWORKBENCHES # \brief Architecture and BIM tools # # This module provides tools specialized in Building Information Modeling (BIM). # such as convenience tools to build walls, windows or structures, and # IFC import/export capabilities. '''The Arch module provides tools specialized in BIM modeling.''' import FreeCAD if FreeCAD.GuiUp: import FreeCADGui FreeCADGui.updateLocale() QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP translate = FreeCAD.Qt.translate # generic functions from ArchCommands import * from ArchWindowPresets import * # TODO: migrate this one from ArchStructure import * # make functions def makeAxis(num=1,size=1000,name=None): '''makeAxis([num],[size],[name]): makes an Axis set based on the given number of axes and interval distances''' import ArchAxis if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Axis") obj.Label = name if name else translate("Arch","Axes") ArchAxis._Axis(obj) if FreeCAD.GuiUp: ArchAxis._ViewProviderAxis(obj.ViewObject) if num: dist = [] angles = [] for i in range(num): if i == 0: dist.append(0) else: dist.append(float(size)) angles.append(float(0)) obj.Distances = dist obj.Angles = angles FreeCAD.ActiveDocument.recompute() return obj def makeAxisSystem(axes,name=None): '''makeAxisSystem(axes,[name]): makes a system from the given list of axes''' import ArchAxisSystem if not isinstance(axes,list): axes = [axes] obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","AxisSystem") obj.Label = name if name else translate("Arch","Axis System") ArchAxisSystem._AxisSystem(obj) obj.Axes = axes if FreeCAD.GuiUp: ArchAxisSystem._ViewProviderAxisSystem(obj.ViewObject) FreeCAD.ActiveDocument.recompute() return obj def makeBuildingPart(objectslist=None,baseobj=None,name=None): '''makeBuildingPart([objectslist],[name]): creates a buildingPart including the objects from the given list.''' import ArchBuildingPart obj = FreeCAD.ActiveDocument.addObject("App::GeometryPython","BuildingPart") #obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","BuildingPart") obj.Label = name if name else translate("Arch","BuildingPart") ArchBuildingPart.BuildingPart(obj) obj.IfcType = "Building Element Part" if FreeCAD.GuiUp: ArchBuildingPart.ViewProviderBuildingPart(obj.ViewObject) if objectslist: if isinstance(objectslist,(list,tuple)): obj.addObjects(objectslist) else: obj.addObject(objectslist) return obj def makeFloor(objectslist=None,baseobj=None,name=None): """makes a BuildingPart and turns it into a Floor/Level""" obj = makeBuildingPart(objectslist) obj.Label = name if name else translate("Arch","Level") obj.IfcType = "Building Storey" obj.CompositionType = "ELEMENT" return obj def makeBuilding(objectslist=None,baseobj=None,name=None): """makes a BuildingPart and turns it into a Building""" import ArchBuildingPart obj = makeBuildingPart(objectslist) obj.Label = name if name else translate("Arch","Building") obj.IfcType = "Building" obj.CompositionType = "ELEMENT" t = QT_TRANSLATE_NOOP("App::Property","The type of this building") obj.addProperty("App::PropertyEnumeration","BuildingType","Building",t) obj.BuildingType = ArchBuildingPart.BuildingTypes if FreeCAD.GuiUp: obj.ViewObject.ShowLevel = False obj.ViewObject.ShowLabel = False return obj def make2DDrawing(objectslist=None,baseobj=None,name=None): """makes a BuildingPart and turns it into a 2D drawing view""" obj = makeBuildingPart(objectslist) obj.Label = name if name else translate("Arch","Drawing") obj.IfcType = "Annotation" obj.ObjectType = "DRAWING" obj.setEditorMode("Area",2) obj.setEditorMode("Height",2) obj.setEditorMode("LevelOffset",2) obj.setEditorMode("OnlySolids",2) obj.setEditorMode("HeightPropagate",2) if FreeCAD.GuiUp: obj.ViewObject.DisplayOffset = FreeCAD.Placement() obj.ViewObject.ShowLevel = False return obj def convertFloors(floor=None): """convert the given Floor or Building (or all Arch Floors from the active document if none is given) into BuildingParts""" import Draft import ArchBuildingPart todel = [] if floor: objset = [floor] else: objset = FreeCAD.ActiveDocument.Objects for obj in objset: if Draft.getType(obj) in ["Floor","Building"]: nobj = makeBuildingPart(obj.Group) if Draft.getType(obj) == "Floor": nobj.IfcType = "Building Storey" nobj.CompositionType = "ELEMENT" else: nobj.IfcType = "Building" nobj.CompositionType = "ELEMENT" t = QT_TRANSLATE_NOOP("App::Property","The type of this building") nobj.addProperty("App::PropertyEnumeration","BuildingType","Building",t) nobj.BuildingType = ArchBuildingPart.BuildingTypes label = obj.Label for parent in obj.InList: if hasattr(parent,"Group"): if obj in parent.Group: parent.addObject(nobj) #g = parent.Group #g.append(nobj) #parent.Group = g todel.append(obj.Name) if obj.ViewObject: # some bug makes this trigger even efter the object has been deleted... obj.ViewObject.Proxy.Object = None # in case FreeCAD doesn't allow 2 objs with same label obj.Label = obj.Label+" to delete" nobj.Label = label for n in todel: from draftutils import todo todo.ToDo.delay(FreeCAD.ActiveDocument.removeObject,n) def makeCurtainWall(baseobj=None,name=None): """makeCurtainWall([baseobj],[name]): Creates a curtain wall in the active document""" import ArchCurtainWall if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","CurtainWall") obj.Label = name if name else translate("Arch","Curtain Wall") ArchCurtainWall.CurtainWall(obj) if FreeCAD.GuiUp: ArchCurtainWall.ViewProviderCurtainWall(obj.ViewObject) if baseobj: obj.Base = baseobj if FreeCAD.GuiUp: baseobj.ViewObject.hide() return obj def makeEquipment(baseobj=None,placement=None,name=None): """makeEquipment([baseobj],[placement],[name]): creates an equipment object from the given base object.""" import ArchEquipment if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Equipment") obj.Label = name if name else translate("Arch","Equipment") ArchEquipment._Equipment(obj) if baseobj: if baseobj.isDerivedFrom("Mesh::Feature"): obj.Mesh = baseobj else: obj.Base = baseobj if placement: obj.Placement = placement if FreeCAD.GuiUp: ArchEquipment._ViewProviderEquipment(obj.ViewObject) if baseobj: baseobj.ViewObject.hide() return obj def makeFence(section, post, path): """Makes a Fence object""" import ArchFence obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', 'Fence') ArchFence._Fence(obj) obj.Section = section obj.Post = post obj.Path = path if FreeCAD.GuiUp: ArchFence._ViewProviderFence(obj.ViewObject) ArchFence.hide(section) ArchFence.hide(post) ArchFence.hide(path) return obj def makeFrame(baseobj,profile,name=None): """makeFrame(baseobj,profile,[name]): creates a frame object from a base sketch (or any other object containing wires) and a profile object (an extrudable 2D object containing faces or closed wires)""" import ArchFrame if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Frame") obj.Label = name if name else translate("Arch","Frame") ArchFrame._Frame(obj) if FreeCAD.GuiUp: ArchFrame._ViewProviderFrame(obj.ViewObject) if baseobj: obj.Base = baseobj if profile: obj.Profile = profile if FreeCAD.GuiUp: profile.ViewObject.hide() return obj def makeGrid(name=None): '''makeGrid([name]): makes a grid object''' import ArchGrid obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Grid") obj.Label = name if name else translate("Arch","Grid") ArchGrid.ArchGrid(obj) if FreeCAD.GuiUp: ArchGrid.ViewProviderArchGrid(obj.ViewObject) obj.ViewObject.Transparency = 85 FreeCAD.ActiveDocument.recompute() return obj def makeMaterial(name=None,color=None,transparency=None): '''makeMaterial([name],[color],[transparency]): makes an Material object''' import ArchMaterial if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("App::MaterialObjectPython","Material") obj.Label = name if name else translate("Arch","Material") ArchMaterial._ArchMaterial(obj) if FreeCAD.GuiUp: ArchMaterial._ViewProviderArchMaterial(obj.ViewObject) getMaterialContainer().addObject(obj) if color: obj.Color = color[:3] if len(color) > 3: obj.Transparency = color[3]*100 if transparency: obj.Transparency = transparency return obj def makeMultiMaterial(name=None): '''makeMultiMaterial([name]): makes an MultiMaterial object''' import ArchMaterial obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","MultiMaterial") obj.Label = name if name else translate("Arch","MultiMaterial") ArchMaterial._ArchMultiMaterial(obj) if FreeCAD.GuiUp: ArchMaterial._ViewProviderArchMultiMaterial(obj.ViewObject) getMaterialContainer().addObject(obj) return obj def getMaterialContainer(): '''getMaterialContainer(): returns a group object to put materials in''' import ArchMaterial for obj in FreeCAD.ActiveDocument.Objects: if obj.Name == "MaterialContainer": return obj obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython","MaterialContainer") obj.Label = "Materials" ArchMaterial._ArchMaterialContainer(obj) if FreeCAD.GuiUp: ArchMaterial._ViewProviderArchMaterialContainer(obj.ViewObject) return obj def getDocumentMaterials(): '''getDocumentMaterials(): returns all the arch materials of the document''' for obj in FreeCAD.ActiveDocument.Objects: if obj.Name == "MaterialContainer": mats = [] for o in obj.Group: if o.isDerivedFrom("App::MaterialObjectPython"): mats.append(o) return mats return [] def makePanel(baseobj=None,length=0,width=0,thickness=0,placement=None,name=None): '''makePanel([baseobj],[length],[width],[thickness],[placement],[name]): creates a panel element based on the given profile object and the given extrusion thickness. If no base object is given, you can also specify length and width for a simple cubic object.''' import ArchPanel if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Panel") obj.Label = name if name else translate("Arch","Panel") ArchPanel._Panel(obj) if FreeCAD.GuiUp: ArchPanel._ViewProviderPanel(obj.ViewObject) if baseobj: obj.Base = baseobj if FreeCAD.GuiUp: obj.Base.ViewObject.hide() if width: obj.Width = width if thickness: obj.Thickness = thickness if length: obj.Length = length return obj def makePanelCut(panel,name=None): """makePanelCut(panel,[name]) : Creates a 2D view of the given panel in the 3D space, positioned at the origin.""" import ArchPanel view = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","PanelCut") view.Label = name if name else translate("Arch","View of")+" "+panel.Label ArchPanel.PanelCut(view) view.Source = panel if FreeCAD.GuiUp: ArchPanel.ViewProviderPanelCut(view.ViewObject) return view def makePanelSheet(panels=[],name=None): """makePanelSheet([panels],[name]) : Creates a sheet with the given panel cuts in the 3D space, positioned at the origin.""" import ArchPanel sheet = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","PanelSheet") sheet.Label = name if name else translate("Arch","PanelSheet") ArchPanel.PanelSheet(sheet) if panels: sheet.Group = panels if FreeCAD.GuiUp: ArchPanel.ViewProviderPanelSheet(sheet.ViewObject) return sheet def makePipe(baseobj=None,diameter=0,length=0,placement=None,name=None): "makePipe([baseobj],[diameter],[length],[placement],[name]): creates an pipe object from the given base object" import ArchPipe if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj= FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Pipe") obj.Label = name if name else translate("Arch","Pipe") ArchPipe._ArchPipe(obj) if FreeCAD.GuiUp: ArchPipe._ViewProviderPipe(obj.ViewObject) if baseobj: baseobj.ViewObject.hide() if baseobj: obj.Base = baseobj else: if length: obj.Length = length else: obj.Length = 1000 if diameter: obj.Diameter = diameter else: obj.Diameter = params.get_param_arch("PipeDiameter") obj.Width = obj.Diameter obj.Height = obj.Diameter if placement: obj.Placement = placement return obj def makePipeConnector(pipes,radius=0,name=None): "makePipeConnector(pipes,[radius],[name]): creates a connector between the given pipes" import ArchPipe if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj= FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Connector") obj.Label = name if name else translate("Arch","Connector") ArchPipe._ArchPipeConnector(obj) obj.Pipes = pipes if not radius: radius = pipes[0].Diameter obj.Radius = radius if FreeCAD.GuiUp: ArchPipe._ViewProviderPipe(obj.ViewObject) return obj def makeProfile(profile=[0,'REC','REC100x100','R',100,100]): '''makeProfile(profile): returns a shape with the face defined by the profile data''' import ArchProfile if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython", "Profile") obj.Label = profile[2] + "_" if profile[3]=="C": ArchProfile._ProfileC(obj, profile) elif profile[3]=="H": ArchProfile._ProfileH(obj, profile) elif profile[3]=="R": ArchProfile._ProfileR(obj, profile) elif profile[3]=="RH": ArchProfile._ProfileRH(obj, profile) elif profile[3]=="U": ArchProfile._ProfileU(obj, profile) elif profile[3]=="L": ArchProfile._ProfileL(obj, profile) elif profile[3]=="T": ArchProfile._ProfileT(obj, profile) else : print("Profile not supported") if FreeCAD.GuiUp: ArchProfile.ViewProviderProfile(obj.ViewObject) return obj def makeProject(sites=None, name=None): """Create an Arch project. If sites are provided, add them as children of the new project. Parameters ---------- sites: list of , optional Sites to add as children of the project. Ultimately this could be anything, however. name: str, optional The label for the project. Returns ------- The created project. WARNING: This object is obsoleted in favour of the NativeIFC project """ import ArchProject import Part if not FreeCAD.ActiveDocument: return FreeCAD.Console.PrintError("No active document. Aborting\n") obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Project") obj.Label = name if name else translate("Arch", "Project") ArchProject._Project(obj) if FreeCAD.GuiUp: ArchProject._ViewProviderProject(obj.ViewObject) if sites: obj.Group = sites return obj def makeRebar(baseobj=None,sketch=None,diameter=None,amount=1,offset=None,name=None): """makeRebar([baseobj],[sketch],[diameter],[amount],[offset],[name]): adds a Reinforcement Bar object to the given structural object, using the given sketch as profile.""" import ArchRebar if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Rebar") obj.Label = name if name else translate("Arch","Rebar") ArchRebar._Rebar(obj) if FreeCAD.GuiUp: ArchRebar._ViewProviderRebar(obj.ViewObject) if baseobj and sketch: if hasattr(sketch,"AttachmentSupport"): if sketch.AttachmentSupport: if isinstance(sketch.AttachmentSupport,tuple): if sketch.AttachmentSupport[0] == baseobj: sketch.AttachmentSupport = None elif sketch.AttachmentSupport == baseobj: sketch.AttachmentSupport = None obj.Base = sketch if FreeCAD.GuiUp: sketch.ViewObject.hide() obj.Host = baseobj elif sketch and not baseobj: # a rebar could be based on a wire without the existence of a Structure obj.Base = sketch if FreeCAD.GuiUp: sketch.ViewObject.hide() obj.Host = None elif baseobj and not sketch: obj.Shape = baseobj.Shape if diameter: obj.Diameter = diameter else: obj.Diameter = params.get_param_arch("RebarDiameter") obj.Amount = amount obj.Document.recompute() if offset is not None: obj.OffsetStart = offset obj.OffsetEnd = offset else: obj.OffsetStart = params.get_param_arch("RebarOffset") obj.OffsetEnd = params.get_param_arch("RebarOffset") obj.Mark = obj.Label return obj def makeReference(filepath=None, partname=None, name=None): """makeReference([filepath],[partname],[name]): Creates an Arch Reference object""" import ArchReference if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","ArchReference") obj.Label = name if name else translate("Arch","External Reference") ArchReference.ArchReference(obj) if FreeCAD.GuiUp: ArchReference.ViewProviderArchReference(obj.ViewObject) if filepath: obj.File = filepath if partname: obj.Part = partname import Draft Draft.select(obj) return obj def makeRoof(baseobj=None, facenr=0, angles=[45.0], run=[250.0], idrel=[-1], thickness=[50.0], overhang=[100.0], name=None): '''makeRoof(baseobj, [facenr], [angle], [name]): Makes a roof based on a closed wire or an object. You can provide a list of angles, run, idrel, thickness, overhang for each edge in the wire to define the roof shape. The default for angle is 45 and the list is automatically completed to match the number of edges in the wire. If the base object is a solid the roof uses its shape. ''' import ArchRoof import Part if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", "Roof") obj.Label = name if name else translate("Arch", "Roof") baseWire = None ArchRoof._Roof(obj) if FreeCAD.GuiUp: ArchRoof._ViewProviderRoof(obj.ViewObject) if baseobj: obj.Base = baseobj if hasattr(obj.Base, "Shape"): if obj.Base.Shape.Solids: if FreeCAD.GuiUp: obj.Base.ViewObject.hide() else: if (obj.Base.Shape.Faces and obj.Face): baseWire = obj.Base.Shape.Faces[obj.Face-1].Wires[0] if FreeCAD.GuiUp: obj.Base.ViewObject.hide() elif obj.Base.Shape.Wires: baseWire = obj.Base.Shape.Wires[0] if FreeCAD.GuiUp: obj.Base.ViewObject.hide() if baseWire: if baseWire.isClosed(): if FreeCAD.GuiUp: obj.Base.ViewObject.hide() edges = Part.__sortEdges__(baseWire.Edges) ln = len(edges) obj.Angles = ArchRoof.adjust_list_len(angles, ln, angles[0]) obj.Runs = ArchRoof.adjust_list_len(run, ln, run[0]) obj.IdRel = ArchRoof.adjust_list_len(idrel, ln, idrel[0]) obj.Thickness = ArchRoof.adjust_list_len(thickness, ln, thickness[0]) obj.Overhang = ArchRoof.adjust_list_len(overhang, ln, overhang[0]) obj.Face = facenr return obj def makeSchedule(): """makeSchedule(): Creates a schedule object in the active document""" import ArchSchedule obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Schedule") obj.Label = translate("Arch","Schedule") ArchSchedule._ArchSchedule(obj) if FreeCAD.GuiUp: ArchSchedule._ViewProviderArchSchedule(obj.ViewObject) if hasattr(obj,"CreateSpreadsheet") and obj.CreateSpreadsheet: obj.Proxy.getSpreadSheet(obj, force=True) return obj def makeSectionPlane(objectslist=None,name=None): """makeSectionPlane([objectslist],[name]) : Creates a Section plane objects including the given objects. If no object is given, the whole document will be considered.""" import ArchSectionPlane import Draft import WorkingPlane if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Section") obj.Label = name if name else translate("Arch","Section") ArchSectionPlane._SectionPlane(obj) if FreeCAD.GuiUp: ArchSectionPlane._ViewProviderSectionPlane(obj.ViewObject) if objectslist: obj.Objects = objectslist bb = FreeCAD.BoundBox() for o in Draft.get_group_contents(objectslist): if hasattr(o,"Shape") and hasattr(o.Shape,"BoundBox"): bb.add(o.Shape.BoundBox) obj.Placement = WorkingPlane.get_working_plane().get_placement() obj.Placement.Base = bb.Center if FreeCAD.GuiUp: margin = bb.XLength*0.1 obj.ViewObject.DisplayLength = bb.XLength+margin obj.ViewObject.DisplayHeight = bb.YLength+margin return obj def makeSite(objectslist=None,baseobj=None,name=None): '''makeBuilding([objectslist],[baseobj],[name]): creates a site including the objects from the given list.''' import ArchSite if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return import Part obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Site") obj.Label = name if name else translate("Arch","Site") ArchSite._Site(obj) if FreeCAD.GuiUp: ArchSite._ViewProviderSite(obj.ViewObject) if objectslist: obj.Group = objectslist if baseobj: import Part if isinstance(baseobj,Part.Shape): obj.Shape = baseobj else: obj.Terrain = baseobj return obj def makeSpace(objects=None,baseobj=None,name=None): """Creates a space object from the given objects. Parameters ---------- objects : object or List() or App::PropertyLinkSubList, optional The object or selection set that defines the space. If a single object is given, it becomes the base shape for the object. If the object or selection set contains subelements, these will be used as the boundaries to create the space. By default None. baseobj : object or List() or App::PropertyLinkSubList, optional Currently unimplemented, it replaces and behaves in the same way as the objects parameter if defined. By default None. name : str, optional The user-facing name to assign to the space object's label. By default None, in which case the label is set to "Space". Notes ----- The objects parameter can be passed using either of these different formats: 1. Single object (e.g. a Part::Feature document object). Will be used as the space's base shape. :: objects = 2. List of selection objects, as provided by ``Gui.Selection.getSelectionEx()``. This requires the GUI to be active. The `SubObjects` property of each selection object in the list defines the space's boundaries. If the list contains a single selection object without subobjects, or with only one subobject, the object in its ``Object`` property is used as the base shape. :: objects = [, ...] 3. A list of tuples that can be assigned to an ``App::PropertyLinkSubList`` property. Each tuple contains a document object and a nested tuple of subobjects that define boundaries. If the list contains a single tuple without a nested subobjects tuple, or a subobjects tuple with only one subobject, the object in the tuple is used as the base shape. :: objects = [(obj1, ("Face1")), (obj2, ("Face1")), ...] objects = [(obj, ("Face1", "Face2", "Face3", "Face4"))] """ import ArchSpace if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return space = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Space") space.Label = name if name else translate("Arch","Space") ArchSpace._Space(space) if FreeCAD.GuiUp: ArchSpace._ViewProviderSpace(space.ViewObject) if baseobj: objects = baseobj if objects: if not isinstance(objects,list): objects = [objects] isSingleObject = lambda objs: len(objs) == 1 # We assume that the objects list is not a mixed set. The type of the first # object will determine the type of the set. # Input to this function can come into three different formats. First convert it # to a common format: [ (, ["Face1", ...]), ... ] if (hasattr(objects[0], "isDerivedFrom") and objects[0].isDerivedFrom("Gui::SelectionObject")): # Selection set: convert to common format # [, ...] objects = [(obj.Object, obj.SubElementNames) for obj in objects] elif (isinstance(objects[0], tuple) or isinstance(objects[0], list)): # Tuple or list of object with subobjects: pass unmodified # [ (, ["Face1", ...]), ... ] pass else: # Single object: assume anything else passed is a single object with no # boundaries. # [ ] objects = [(objects[0], [])] if isSingleObject(objects): # For a single object, having boundaries is determined by them being defined # as more than one subelement (e.g. two faces) boundaries = [obj for obj in objects if len(obj[1]) > 1] else: boundaries = [obj for obj in objects if obj[1]] if isSingleObject(objects) and not boundaries: space.Base = objects[0][0] if FreeCAD.GuiUp: objects[0][0].ViewObject.hide() else: space.Proxy.addSubobjects(space, boundaries) return space def addSpaceBoundaries(space,subobjects): """Adds the given subobjects as defining boundaries of the given space. Parameters ---------- space : ArchSpace._Space Arch space object to add the boundaries to. subobjects : List() or App::PropertyLinkSubList List of boundaries to add to the space. Notes ----- The subobjects parameter can be passed using either of these different formats: 1. List of selection objects, as provided by ``Gui.Selection.getSelectionEx()``. This requires the GUI to be active. The `SubObjects` property of each selection object in the list defines the boundaries to add to the space. :: subobjects = [, ...] 2. A list of tuples that can be assigned to an ``App::PropertyLinkSubList`` property. Each tuple contains a document object and a nested tuple of subobjects that define the boundaries to add. :: subobjects = [(obj1, ("Face1")), (obj2, ("Face1")), ...] subobjects = [(obj, ("Face1", "Face2", "Face3", "Face4"))] """ import Draft if Draft.getType(space) == "Space": space.Proxy.addSubobjects(space,subobjects) def removeSpaceBoundaries(space,subobjects): """Remove the given subobjects as defining boundaries of the given space. Parameters ---------- space : ArchSpace._Space Arch space object to remove the boundaries from. subobjects : List() or App::PropertyLinkSubList List of boundaries to remove from the space. Notes ----- The subobjects parameter can be passed using either of these different formats: 1. List of selection objects, as provided by ``Gui.Selection.getSelectionEx()``. This requires the GUI to be active. The `SubObjects` property of each selection object in the list defines the boundaries to remove from the space. :: subobjects = [, ...] 2. A list of tuples that can be assigned to an ``App::PropertyLinkSubList`` property. Each tuple contains a document object and a nested tuple of subobjects that define the boundaries to remove. :: subobjects = [(obj1, ("Face1")), (obj2, ("Face1")), ...] subobjects = [(obj, ("Face1", "Face2", "Face3", "Face4"))] """ import Draft if Draft.getType(space) == "Space": space.Proxy.removeSubobjects(space,subobjects) def makeStairs(baseobj=None,length=None,width=None,height=None,steps=None,name=None): """makeStairs([baseobj],[length],[width],[height],[steps],[name]): creates a Stairs objects with given attributes.""" import ArchStairs if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return stairs = [] additions = [] label = name if name else translate("Arch","Stairs") def setProperty(obj,length,width,height,steps): """setProperty(obj,length,width,height,steps): sets up the basic properties for this stair""" if length: obj.Length = length else: obj.Length = params.get_param_arch("StairsLength") if width: obj.Width = width else: obj.Width = params.get_param_arch("StairsWidth") if height: obj.Height = height else: obj.Height = params.get_param_arch("StairsHeight") if steps: obj.NumberOfSteps = steps obj.Structure = "Massive" obj.StructureThickness = 150 obj.DownSlabThickness = 150 obj.UpSlabThickness = 150 obj.RailingOffsetLeft = 60 obj.RailingOffsetRight = 60 obj.RailingHeightLeft = 900 obj.RailingHeightRight = 900 if baseobj: if not isinstance(baseobj,list): baseobj = [baseobj] lenSelection = len(baseobj) if lenSelection > 1: stair = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Stairs") stair.Label = label ArchStairs._Stairs(stair) stairs.append(stair) i = 1 else: i = 0 for baseobjI in baseobj: stair = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Stairs") stair.Label = label ArchStairs._Stairs(stair) stairs.append(stair) stairs[i].Base = baseobjI if steps: stepsI = steps else: stepsI = 20 setProperty(stairs[i],None,width,height,stepsI) if i > 1: additions.append(stairs[i]) stairs[i].LastSegment = stairs[i-1] else: if len(stairs) > 1: # i.e. length >1, have a 'master' staircase created stairs[0].Base = stairs[1] i += 1 if lenSelection > 1: stairs[0].Additions = additions else: obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Stairs") obj.Label = label ArchStairs._Stairs(obj) setProperty(obj,length,width,height,steps) stairs.append(obj) if FreeCAD.GuiUp: if baseobj: for stair in stairs: ArchStairs._ViewProviderStairs(stair.ViewObject) else: ArchStairs._ViewProviderStairs(obj.ViewObject) if stairs: for stair in stairs: stair.recompute() makeRailing(stairs) # return stairs - all other functions expect one object as return value return stairs[0] else: obj.recompute() return obj def makeRailing(stairs): "simple make Railing function" import ArchPipe def makeRailingLorR(stairs,side="L"): """makeRailingLorR(stairs,side="L"): Creates a railing on the given side of the stairs, L or R""" for stair in reversed(stairs): if side == "L": outlineLR = stair.OutlineLeft outlineLRAll = stair.OutlineLeftAll stairRailingLR = "RailingLeft" elif side == "R": outlineLR = stair.OutlineRight outlineLRAll = stair.OutlineRightAll stairRailingLR = "RailingRight" if outlineLR or outlineLRAll: lrRail = makePipe(baseobj=None,diameter=0,length=0,placement=None,name=translate("Arch","Railing")) if outlineLRAll: setattr(stair, stairRailingLR, lrRail) break elif outlineLR: setattr(stair, stairRailingLR, lrRail) if stairs is None: sel = FreeCADGui.Selection.getSelection() sel0 = sel[0] stairs = [] # TODO currently consider 1st selected object, then would tackle multiple objects? if Draft.getType(sel[0]) == "Stairs": stairs.append(sel0) if Draft.getType(sel0.Base) == "Stairs": stairs.append(sel0.Base) additions = sel0.Additions for additionsI in additions: if Draft.getType(additionsI) == "Stairs": stairs.append(additionsI) else: stairs.append(sel[0]) else: print("No Stairs object selected") return makeRailingLorR(stairs,"L") makeRailingLorR(stairs,"R") def makeTruss(baseobj=None,name=None): """ makeTruss([baseobj],[name]): Creates a space object from the given object (a line) """ import ArchTruss if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Truss") obj.Label = name if name else translate("Arch","Truss") ArchTruss.Truss(obj) if FreeCAD.GuiUp: ArchTruss.ViewProviderTruss(obj.ViewObject) if baseobj: obj.Base = baseobj if FreeCAD.GuiUp: baseobj.ViewObject.hide() return obj def makeWall(baseobj=None,height=None,length=None,width=None,align=None,face=None,name=None): """Create a wall based on a given object, and returns the generated wall. TODO: It is unclear what defines which units this function uses. Parameters ---------- baseobj: , optional The base object with which to build the wall. This can be a sketch, a draft object, a face, or a solid. It can also be left as None. height: float, optional The height of the wall. length: float, optional The length of the wall. Not used if the wall is based off an object. Will use Arch default if left empty. width: float, optional The width of the wall. Not used if the base object is a face. Will use Arch default if left empty. align: str, optional Either "Center", "Left", or "Right". Effects the alignment of the wall on its baseline. face: int, optional The index number of a face on the given baseobj, to base the wall on. name: str, optional The name to give to the created wall. Returns ------- Returns the generated wall. Notes ----- Creates a new object, and turns it into a parametric wall object. This object does not yet have any shape. The wall then uses the baseobj.Shape as the basis to extrude out a wall shape, giving the new object a shape. It then hides the original baseobj. """ import ArchWall import Draft from draftutils import params if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Wall") if name: obj.Label = name else: obj.Label = translate("Arch","Wall") ArchWall._Wall(obj) if FreeCAD.GuiUp: ArchWall._ViewProviderWall(obj.ViewObject) if baseobj: if hasattr(baseobj,'Shape') or baseobj.isDerivedFrom("Mesh::Feature"): obj.Base = baseobj else: FreeCAD.Console.PrintWarning(str(translate("Arch","Walls can only be based on Part or Mesh objects"))) if face: obj.Face = face if length: obj.Length = length if width: obj.Width = width else: obj.Width = params.get_param_arch("WallWidth") if height: obj.Height = height else: obj.Height = params.get_param_arch("WallHeight") if align: obj.Align = align else: obj.Align = ["Center","Left","Right"][params.get_param_arch("WallAlignment")] if obj.Base and FreeCAD.GuiUp: if Draft.getType(obj.Base) != "Space": obj.Base.ViewObject.hide() return obj def joinWalls(walls,delete=False): """Join the given list of walls into one sketch-based wall. Take the first wall in the list, and adds on the other walls in the list. Return the modified first wall. Setting delete to True, will delete the other walls. Only join walls if the walls have the same width, height and alignment. Parameters ---------- walls: list of List containing the walls to add to the first wall in the list. Walls must be based off a base object. delete: bool, optional If True, deletes the other walls in the list. Returns ------- """ import Part import Draft import ArchWall if not walls: return None if not isinstance(walls,list): walls = [walls] if not ArchWall.areSameWallTypes(walls): return None deleteList = [] base = walls.pop() if base.Base: if base.Base.Shape.Faces: return None # Use ArchSketch if SketchArch add-on is present if Draft.getType(base.Base) == "ArchSketch": sk = base.Base else: try: import ArchSketchObject newSk=ArchSketchObject.makeArchSketch() except: if Draft.getType(base.Base) != "Sketcher::SketchObject": newSk=FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace") else: newSk=None if newSk: sk = Draft.makeSketch(base.Base,autoconstraints=True, addTo=newSk) base.Base = sk else: sk = base.Base for w in walls: if w.Base: if not w.Base.Shape.Faces: for e in w.Base.Shape.Edges: l = e.Curve if isinstance(l,Part.Line): l = Part.LineSegment(e.Vertexes[0].Point,e.Vertexes[-1].Point) sk.addGeometry(l) deleteList.append(w.Name) if delete: for n in deleteList: FreeCAD.ActiveDocument.removeObject(n) FreeCAD.ActiveDocument.recompute() base.ViewObject.show() return base def makeWindow(baseobj=None,width=None,height=None,parts=None,name=None): '''makeWindow(baseobj,[width,height,parts,name]): creates a window based on the given base 2D object (sketch or draft).''' import ArchWindow import Draft from draftutils import todo if not FreeCAD.ActiveDocument: FreeCAD.Console.PrintError("No active document. Aborting\n") return if baseobj: if Draft.getType(baseobj) == "Window": obj = Draft.clone(baseobj) return obj obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Window") ArchWindow._Window(obj) if name: obj.Label = name else: obj.Label = translate("Arch","Window") if FreeCAD.GuiUp: ArchWindow._ViewProviderWindow(obj.ViewObject) if width: obj.Width = width if height: obj.Height = height if baseobj: obj.Normal = baseobj.Placement.Rotation.multVec(FreeCAD.Vector(0,0,-1)) obj.Base = baseobj if parts is not None: obj.WindowParts = parts else: if baseobj: if baseobj.getLinkedObject().isDerivedFrom("Part::Part2DObject"): # create default component if baseobj.Shape.Wires: tp = "Frame" if len(baseobj.Shape.Wires) == 1: tp = "Solid panel" i = 0 ws = '' for w in baseobj.Shape.Wires: if w.isClosed(): if ws: ws += "," ws += "Wire" + str(i) i += 1 obj.WindowParts = ["Default",tp,ws,"1","0"] else: # bind properties from base obj if existing for prop in ["Height","Width","Subvolume","Tag","Description","Material"]: for p in baseobj.PropertiesList: if (p == prop) or p.endswith("_"+prop): obj.setExpression(prop, baseobj.Name+"."+p) if obj.Base and FreeCAD.GuiUp: obj.Base.ViewObject.DisplayMode = "Wireframe" obj.Base.ViewObject.hide() todo.ToDo.delay(ArchWindow.recolorize,[obj.Document.Name,obj.Name]) return obj