Arch: Misc improvements for IFC workflow
* Added utility to make non-parametric Arch component * Ability for all Arch components to be a clone of another Arch component of same type * Modified the Draft Clone tool to produce Arch Clones if applicable * Fixed Arch Roofs so they can be based on a solid shape like other Arch objects * Ability to change the Root element to be imported in IFC preferences * Ability to import IFC files also as compounds, Part shapes or non-parametric Arch objects * Added an "only" parameter to importIFC.open() to import only a certain object ID. * Ability to read colors (IfcSurfaceStyle) from IFC objects
This commit is contained in:
parent
694b0339fd
commit
892b7ae509
|
@ -192,6 +192,23 @@ def removeComponents(objectsList,host=None):
|
|||
if o in a:
|
||||
a.remove(o)
|
||||
h.Objects = a
|
||||
|
||||
def makeComponent(baseobj=None,name="Component"):
|
||||
'''makeComponent([baseobj]): creates an undefined, non-parametric Arch
|
||||
component from the given base object'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
|
||||
obj.Label = translate("Arch",name)
|
||||
ArchComponent.Component(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
ArchComponent.ViewProviderComponent(obj.ViewObject)
|
||||
if baseobj:
|
||||
import Part
|
||||
if baseobj.isDerivedFrom("Part::Feature"):
|
||||
obj.Shape = baseobj.Shape
|
||||
obj.Placement = baseobj.Placement
|
||||
elif isinstance(baseobj,Part.Shape):
|
||||
obj.Shape = baseobj
|
||||
return obj
|
||||
|
||||
def fixWindow(obj):
|
||||
'''fixWindow(object): Fixes non-DAG problems in windows
|
||||
|
@ -1114,6 +1131,29 @@ class _ToggleIfcBrepFlag:
|
|||
def Activated(self):
|
||||
for o in FreeCADGui.Selection.getSelection():
|
||||
toggleIfcBrepFlag(o)
|
||||
|
||||
|
||||
class _CommandComponent:
|
||||
"the Arch Component command definition"
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'Arch_Component',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Component","Component"),
|
||||
'Accel': "C, M",
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Component","Creates an undefined architectural component")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
sel = FreeCADGui.Selection.getSelection()
|
||||
if sel:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("Arch","Create Component"))
|
||||
FreeCADGui.addModule("Arch")
|
||||
FreeCADGui.Control.closeDialog()
|
||||
for o in sel:
|
||||
FreeCADGui.doCommand("Arch.makeComponent(FreeCAD.ActiveDocument."+o.Name+")")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -1128,3 +1168,4 @@ if FreeCAD.GuiUp:
|
|||
FreeCADGui.addCommand('Arch_IfcExplorer',_CommandIfcExplorer())
|
||||
FreeCADGui.addCommand('Arch_Survey',_CommandSurvey())
|
||||
FreeCADGui.addCommand('Arch_ToggleIfcBrepFlag',_ToggleIfcBrepFlag())
|
||||
FreeCADGui.addCommand('Arch_Component',_CommandComponent())
|
||||
|
|
|
@ -40,6 +40,7 @@ if FreeCAD.GuiUp:
|
|||
else:
|
||||
def translate(ctxt,txt):
|
||||
return txt
|
||||
|
||||
|
||||
def addToComponent(compobject,addobject,mod=None):
|
||||
'''addToComponent(compobject,addobject,mod): adds addobject
|
||||
|
@ -132,8 +133,8 @@ class SelectionTaskPanel:
|
|||
FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver)
|
||||
del FreeCAD.ArchObserver
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
class ComponentTaskPanel:
|
||||
'''The default TaskPanel for all Arch components'''
|
||||
def __init__(self):
|
||||
|
@ -289,6 +290,7 @@ class Component:
|
|||
"The default Arch Component object"
|
||||
def __init__(self,obj):
|
||||
obj.addProperty("App::PropertyLink","Base","Arch",translate("Arch","The base object this component is built upon"))
|
||||
obj.addProperty("App::PropertyLink","CloneOf","Arch",translate("Arch","The object this component is cloning"))
|
||||
obj.addProperty("App::PropertyLinkList","Additions","Arch",translate("Arch","Other shapes that are appended to this object"))
|
||||
obj.addProperty("App::PropertyLinkList","Subtractions","Arch",translate("Arch","Other shapes that are subtracted from this object"))
|
||||
obj.addProperty("App::PropertyString","Description","Arch",translate("Arch","An optional description for this component"))
|
||||
|
@ -316,6 +318,17 @@ class Component:
|
|||
def onChanged(self,obj,prop):
|
||||
pass
|
||||
|
||||
def clone(self,obj):
|
||||
"if this object is a clone, sets the shape. Returns True if this is the case"
|
||||
if hasattr(obj,"CloneOf"):
|
||||
if obj.CloneOf:
|
||||
if Draft.getType(obj.CloneOf) == Draft.getType(obj):
|
||||
pl = obj.Placement
|
||||
obj.Shape = obj.CloneOf.Shape.copy()
|
||||
obj.Placement = pl
|
||||
return True
|
||||
return False
|
||||
|
||||
def getSiblings(self,obj):
|
||||
"returns a list of objects with the same type and same base as this object"
|
||||
if not hasattr(obj,"Base"):
|
||||
|
@ -642,6 +655,10 @@ class ViewProviderComponent:
|
|||
|
||||
def updateData(self,obj,prop):
|
||||
return
|
||||
|
||||
def getIcon(self):
|
||||
import Arch_rc
|
||||
return ":/icons/Arch_Component.svg"
|
||||
|
||||
def onChanged(self,vobj,prop):
|
||||
if prop == "Visibility":
|
||||
|
|
|
@ -248,6 +248,10 @@ class _Equipment(ArchComponent.Component):
|
|||
self.hideSubobjects(obj,prop)
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
pl = obj.Placement
|
||||
if obj.Base:
|
||||
if obj.isDerivedFrom("Mesh::Feature"):
|
||||
|
|
|
@ -90,6 +90,10 @@ class _Frame(ArchComponent.Component):
|
|||
obj.Role = Roles
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
if not obj.Base:
|
||||
return
|
||||
if not obj.Base.Shape:
|
||||
|
|
|
@ -265,6 +265,9 @@ class _Panel(ArchComponent.Component):
|
|||
|
||||
def execute(self,obj):
|
||||
"creates the panel shape"
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part, DraftGeomUtils
|
||||
|
||||
|
|
|
@ -158,6 +158,10 @@ class _Rebar(ArchComponent.Component):
|
|||
return None,None
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
if len(obj.InList) != 1:
|
||||
return
|
||||
if Draft.getType(obj.InList[0]) != "Structure":
|
||||
|
|
|
@ -35,19 +35,28 @@ __title__="FreeCAD Roof"
|
|||
__author__ = "Yorik van Havre", "Jonathan Wiedemann"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
def makeRoof(baseobj=None,facenr=1, angles=[45.,], run = [], idrel = [0,],thickness = [1.,], overhang=[2.,], name="Roof"):
|
||||
def makeRoof(baseobj=None,facenr=0, angles=[45.,], run = [], idrel = [0,],thickness = [1.,], overhang=[2.,], name="Roof"):
|
||||
'''makeRoof(baseobj,[facenr],[angle],[name]) : Makes a roof based on a closed wire.
|
||||
face from an existing object. You can provide a list of angles, run, idrel, thickness,
|
||||
overhang for each edges in the wire to define the roof shape. The default for angle is 45
|
||||
and the list is automatically complete to match with number of edges in the wire.'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
|
||||
obj.Label = translate("Arch",name)
|
||||
w = None
|
||||
_Roof(obj)
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderRoof(obj.ViewObject)
|
||||
if baseobj:
|
||||
obj.Base = baseobj
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
if (facenr == 0) and obj.Base.Shape.Solids:
|
||||
# the base shape is a solid and facenr hasn't been set:
|
||||
# assume its shape is copied over
|
||||
if FreeCAD.GuiUp:
|
||||
obj.Base.ViewObject.hide()
|
||||
else:
|
||||
# set facenr to 1
|
||||
facenr = 1
|
||||
if (obj.Base.Shape.Faces and obj.Face):
|
||||
w = obj.Base.Shape.Faces[obj.Face-1].Wires[0]
|
||||
elif obj.Base.Shape.Wires:
|
||||
|
@ -509,6 +518,10 @@ class _Roof(ArchComponent.Component):
|
|||
print("PROFIL " + str(i) + " : End calculs")
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part, math, DraftGeomUtils
|
||||
pl = obj.Placement
|
||||
self.baseface = None
|
||||
|
|
|
@ -236,6 +236,10 @@ class _Space(ArchComponent.Component):
|
|||
obj.Role = Roles
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
self.getShape(obj)
|
||||
|
||||
def onChanged(self,obj,prop):
|
||||
|
|
|
@ -130,7 +130,10 @@ class _Stairs(ArchComponent.Component):
|
|||
def execute(self,obj):
|
||||
|
||||
"constructs the shape of the stairs"
|
||||
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part
|
||||
self.steps = []
|
||||
self.pseudosteps = []
|
||||
|
|
|
@ -577,6 +577,9 @@ class _Structure(ArchComponent.Component):
|
|||
"creates the structure shape"
|
||||
|
||||
import Part, DraftGeomUtils
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
normal,length,width,height = self.getDefaultValues(obj)
|
||||
|
||||
|
|
|
@ -402,6 +402,9 @@ class _Wall(ArchComponent.Component):
|
|||
|
||||
def execute(self,obj):
|
||||
"builds the wall shape"
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part, DraftGeomUtils
|
||||
pl = obj.Placement
|
||||
|
|
|
@ -636,6 +636,10 @@ class _Window(ArchComponent.Component):
|
|||
|
||||
|
||||
def execute(self,obj):
|
||||
|
||||
if self.clone(obj):
|
||||
return
|
||||
|
||||
import Part, DraftGeomUtils
|
||||
pl = obj.Placement
|
||||
base = None
|
||||
|
|
|
@ -75,7 +75,7 @@ class ArchWorkbench(Workbench):
|
|||
"Arch_SectionPlane","Arch_Space","Arch_Stairs",
|
||||
"Arch_Panel","Arch_Equipment",
|
||||
"Arch_Frame","Arch_CutPlane","Arch_Add","Arch_Remove","Arch_Survey"]
|
||||
self.utilities = ["Arch_SplitMesh","Arch_MeshToShape",
|
||||
self.utilities = ["Arch_Component","Arch_SplitMesh","Arch_MeshToShape",
|
||||
"Arch_SelectNonSolidMeshes","Arch_RemoveShape",
|
||||
"Arch_CloseHoles","Arch_MergeWalls","Arch_Check",
|
||||
"Arch_IfcExplorer","Arch_ToggleIfcBrepFlag","Arch_3Views",
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<file>icons/Arch_CutPlane.svg</file>
|
||||
<file>icons/Arch_Bimserver.svg</file>
|
||||
<file>icons/Git.svg</file>
|
||||
<file>icons/Arch_Component.svg</file>
|
||||
<file>ui/archprefs-base.ui</file>
|
||||
<file>ui/archprefs-defaults.ui</file>
|
||||
<file>ui/archprefs-import.ui</file>
|
||||
|
|
96
src/Mod/Arch/Resources/icons/Arch_Component.svg
Normal file
96
src/Mod/Arch/Resources/icons/Arch_Component.svg
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2980"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="Tree_Part.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs2982">
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#71b2f8;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#002795;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2988" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="3"
|
||||
inkscape:cy="32.181818"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1053"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata2985">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g3845"
|
||||
transform="matrix(0.9624254,0,0,0.9624254,-63.864287,-126.15825)"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:bevel;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 96.18444,141.44968 L 76.37003,148.62889 L 105.80493,152.60098 L 105.08136,188.72763 L 122.34017,177.78375 L 122.91604,143.3989 L 96.18444,141.44968 z"
|
||||
id="path3823"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:bevel;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 75.57941,148.6589 L 106.28825,152.02452 L 106.28825,188.50171 L 75.16442,183.43692 L 75.57941,148.6589 z"
|
||||
id="path3825"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="M 106.05435,152.06948 L 122.78033,143.66068"
|
||||
id="path3827" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>503</width>
|
||||
<height>526</height>
|
||||
<width>463</width>
|
||||
<height>516</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -26,12 +26,83 @@
|
|||
<string>IFC import</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Import IFC objects as</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefComboBox" name="comboBox">
|
||||
<property name="toolTip">
|
||||
<string>Specifies what kind of objects will be created in FreeCAD</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcImportMode</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Parametric Arch objects</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Non-parametric Arch objects</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Simple Part shapes</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>One compound per floor</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Root element:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefLineEdit" name="lineEdit_2">
|
||||
<property name="toolTip">
|
||||
<string>Only subtypes of this element will be imported. Keep value as "IfcProduct" to import all building elements.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IfcProduct</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcRootElement</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="gui::prefcheckbox_5">
|
||||
<property name="toolTip">
|
||||
<string>Check this to display debug messages while importing IFC files</string>
|
||||
<string>Show verbose information during import and export of IFC files</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show debug messages</string>
|
||||
|
@ -86,6 +157,29 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="checkBox_3">
|
||||
<property name="toolTip">
|
||||
<string>Check this to retrieve object colors.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Read colors</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcReadColors</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
|
@ -125,6 +219,17 @@
|
|||
<string>IFC export</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>"Show debug messages" option above will apply to export too</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
|
@ -250,6 +355,11 @@
|
|||
<extends>QCheckBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
|
|
|
@ -246,24 +246,36 @@ def explore(filename=None):
|
|||
return
|
||||
|
||||
|
||||
def open(filename,skip=[]):
|
||||
def open(filename,skip=[],only=[],root=None):
|
||||
"opens an IFC file in a new document"
|
||||
|
||||
docname = os.path.splitext(os.path.basename(filename))[0]
|
||||
doc = FreeCAD.newDocument(docname)
|
||||
doc.Label = docname
|
||||
doc = insert(filename,doc.Name,skip)
|
||||
doc = insert(filename,doc.Name,skip,only,root)
|
||||
return doc
|
||||
|
||||
|
||||
def insert(filename,docname,skip=[]):
|
||||
"imports the contents of an IFC file"
|
||||
def insert(filename,docname,skip=[],only=[],root=None):
|
||||
"""insert(filename,docname,skip=[],only=[],root=None): imports the contents of an IFC file.
|
||||
skip can contain a list of ids of objects to be skipped, only can restrict the import to
|
||||
certain object ids (will also get their children) and root can be used to
|
||||
import only the derivates of a certain element type (default = ifcProduct)."""
|
||||
|
||||
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
|
||||
DEBUG = p.GetBool("ifcDebug",False)
|
||||
PREFIX_NUMBERS = p.GetBool("ifcPrefixNumbers",False)
|
||||
SKIP = p.GetString("ifcSkip","")
|
||||
SKIP = p.GetString("ifcSkip","").split(",")
|
||||
SEPARATE_OPENINGS = p.GetBool("ifcSeparateOpenings",False)
|
||||
ROOT_ELEMENT = p.GetString("ifcRootElement","IfcProduct")
|
||||
if root:
|
||||
ROOT_ELEMENT = root
|
||||
MERGE_MODE = p.GetInt("ifcImportMode",0)
|
||||
if MERGE_MODE > 0:
|
||||
SEPARATE_OPENINGS = False
|
||||
READ_COLORS = p.GetBool("ifcReadColors",True)
|
||||
if not SEPARATE_OPENINGS:
|
||||
SKIP.append("IfcOpeningElement")
|
||||
|
||||
try:
|
||||
import ifcopenshell
|
||||
|
@ -271,13 +283,15 @@ def insert(filename,docname,skip=[]):
|
|||
FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n")
|
||||
return
|
||||
|
||||
if DEBUG: print "opening ",filename,"..."
|
||||
if DEBUG: print "Opening ",filename,"...",
|
||||
try:
|
||||
doc = FreeCAD.getDocument(docname)
|
||||
except:
|
||||
doc = FreeCAD.newDocument(docname)
|
||||
FreeCAD.ActiveDocument = doc
|
||||
|
||||
if DEBUG: print "done."
|
||||
|
||||
global ifcfile # keeping global for debugging purposes
|
||||
if isinstance(filename,unicode):
|
||||
import sys #workaround since ifcopenshell currently can't handle unicode filenames
|
||||
|
@ -293,15 +307,19 @@ def insert(filename,docname,skip=[]):
|
|||
sites = ifcfile.by_type("IfcSite")
|
||||
buildings = ifcfile.by_type("IfcBuilding")
|
||||
floors = ifcfile.by_type("IfcBuildingStorey")
|
||||
products = ifcfile.by_type("IfcProduct")
|
||||
products = ifcfile.by_type(ROOT_ELEMENT)
|
||||
openings = ifcfile.by_type("IfcOpeningElement")
|
||||
annotations = ifcfile.by_type("IfcAnnotation")
|
||||
|
||||
if DEBUG: print "Building relationships table...",
|
||||
|
||||
# building relations tables
|
||||
objects = {} # { id:object, ... }
|
||||
additions = {} # { host:[child,...], ... }
|
||||
subtractions = [] # [ [opening,host], ... ]
|
||||
properties = {} # { host:[property, ...], ... }
|
||||
colors = {} # { id:(r,g,b) }
|
||||
shapes = {} # { id:shaoe } only used for merge mode
|
||||
for r in ifcfile.by_type("IfcRelContainedInSpatialStructure"):
|
||||
additions.setdefault(r.RelatingStructure.id(),[]).extend([e.id() for e in r.RelatedElements])
|
||||
for r in ifcfile.by_type("IfcRelAggregates"):
|
||||
|
@ -312,101 +330,239 @@ def insert(filename,docname,skip=[]):
|
|||
for obj in r.RelatedObjects:
|
||||
if r.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
||||
properties.setdefault(obj.id(),[]).extend([e.id() for e in r.RelatingPropertyDefinition.HasProperties])
|
||||
if READ_COLORS:
|
||||
for r in ifcfile.by_type("IfcStyledItem"):
|
||||
if r.Item and r.Styles[0].is_a("IfcPresentationStyleAssignment"):
|
||||
if r.Styles[0].Styles[0].is_a("IfcSurfaceStyle"):
|
||||
if r.Styles[0].Styles[0].Styles[0].is_a("IfcSurfaceStyleRendering"):
|
||||
if r.Styles[0].Styles[0].Styles[0].SurfaceColour:
|
||||
c = r.Styles[0].Styles[0].Styles[0].SurfaceColour
|
||||
for p in ifcfile.by_type("IfcProduct"):
|
||||
if p.Representation:
|
||||
for it in p.Representation.Representations:
|
||||
if it.Items:
|
||||
if it.Items[0].id() == r.Item.id():
|
||||
colors[p.id()] = (c.Red,c.Green,c.Blue)
|
||||
elif it.Items[0].is_a("IfcBooleanResult"):
|
||||
if (it.Items[0].FirstOperand.id() == r.Item.id()):
|
||||
colors[p.id()] = (c.Red,c.Green,c.Blue)
|
||||
if only: # only import a list of IDs and their children
|
||||
ids = []
|
||||
while only:
|
||||
currentid = only.pop()
|
||||
ids.append(currentid)
|
||||
if currentid in additions.keys():
|
||||
only.extend(additions[currentid])
|
||||
products = [ifcfile[currentid] for currentid in ids]
|
||||
|
||||
if DEBUG: print "done."
|
||||
|
||||
count = 0
|
||||
from FreeCAD import Base
|
||||
progressbar = Base.ProgressIndicator()
|
||||
progressbar.start("Importing IFC objects...",len(products))
|
||||
|
||||
# products
|
||||
for product in products:
|
||||
|
||||
pid = product.id()
|
||||
guid = product.GlobalId
|
||||
ptype = product.is_a()
|
||||
if DEBUG: print count,"/",len(products)," creating object ",pid," : ",ptype,
|
||||
name = product.Name or str(ptype[3:])
|
||||
if PREFIX_NUMBERS: name = "ID" + str(pid) + " " + name
|
||||
obj = None
|
||||
baseobj = None
|
||||
brep = None
|
||||
|
||||
if (ptype == "IfcOpeningElement") and (not SEPARATE_OPENINGS): continue
|
||||
if pid in skip: continue # user given id skip list
|
||||
if ptype in SKIP: continue # preferences-set type skip list
|
||||
|
||||
if pid in skip: # user given id skip list
|
||||
if DEBUG: print " skipped."
|
||||
continue
|
||||
if ptype in SKIP: # preferences-set type skip list
|
||||
if DEBUG: print " skipped."
|
||||
continue
|
||||
|
||||
try:
|
||||
cr = ifcopenshell.geom.create_shape(settings,product)
|
||||
brep = cr.geometry.brep_data
|
||||
except:
|
||||
pass
|
||||
pass # IfcOpenShell will yield an error if a given product has no shape, but we don't care
|
||||
|
||||
if brep:
|
||||
if DEBUG: print " ",str(len(brep)/1000),"k ",
|
||||
|
||||
shape = Part.Shape()
|
||||
shape.importBrepFromString(brep)
|
||||
|
||||
shape.scale(1000.0) # IfcOpenShell always outputs in meters
|
||||
|
||||
if not shape.isNull():
|
||||
baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body")
|
||||
baseobj.Shape = shape
|
||||
for freecadtype,ifctypes in typesmap.iteritems():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
|
||||
if MERGE_MODE > 0:
|
||||
if ptype == "IfcSpace": # do not add spaces to compounds
|
||||
if DEBUG: print "skipping space ",pid
|
||||
else:
|
||||
shapes[pid] = shape
|
||||
if DEBUG: print shape.Solids
|
||||
baseobj = shape
|
||||
else:
|
||||
baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body")
|
||||
baseobj.Shape = shape
|
||||
else:
|
||||
if DEBUG: print "null shape ",
|
||||
if not shape.isValid():
|
||||
if DEBUG: print "invalid shape. Skipping"
|
||||
continue
|
||||
|
||||
else:
|
||||
if DEBUG: print " no brep "
|
||||
|
||||
if MERGE_MODE == 0:
|
||||
|
||||
# full Arch objects
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
|
||||
obj.Label = name
|
||||
# setting role
|
||||
try:
|
||||
r = ptype[3:]
|
||||
tr = dict((v,k) for k, v in translationtable.iteritems())
|
||||
if r in tr.keys():
|
||||
r = tr[r]
|
||||
# remove the "StandardCase"
|
||||
if "StandardCase" in r:
|
||||
r = r[:-12]
|
||||
obj.Role = r
|
||||
except:
|
||||
pass
|
||||
# setting uid
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
a = obj.IfcAttributes
|
||||
a["IfcUID"] = str(guid)
|
||||
obj.IfcAttributes = a
|
||||
break
|
||||
if not obj:
|
||||
obj = baseobj
|
||||
if obj:
|
||||
sols = str(baseobj.Shape.Solids) if hasattr(baseobj,"Shape") else "[]"
|
||||
if DEBUG: print sols
|
||||
objects[pid] = obj
|
||||
|
||||
elif MERGE_MODE == 1:
|
||||
|
||||
# non-parametric Arch objects
|
||||
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=None,name=name)
|
||||
obj.Label = name
|
||||
objects[pid] = obj
|
||||
elif baseobj:
|
||||
obj = Arch.makeComponent(baseobj,name=name)
|
||||
obj.Label = name
|
||||
# setting role
|
||||
try:
|
||||
r = ptype[3:]
|
||||
tr = dict((v,k) for k, v in translationtable.iteritems())
|
||||
if r in tr.keys():
|
||||
r = tr[r]
|
||||
# remove the "StandardCase"
|
||||
if "StandardCase" in r:
|
||||
r = r[:-12]
|
||||
obj.Role = r
|
||||
except:
|
||||
pass
|
||||
# setting uid
|
||||
objects[pid] = obj
|
||||
|
||||
elif MERGE_MODE == 2:
|
||||
|
||||
# Part shapes
|
||||
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=None,name=name)
|
||||
obj.Label = name
|
||||
objects[pid] = obj
|
||||
elif baseobj:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
|
||||
obj.Label = name
|
||||
obj.Shape = shape
|
||||
|
||||
|
||||
if obj:
|
||||
|
||||
# properties
|
||||
if pid in properties:
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
a = obj.IfcAttributes
|
||||
a["IfcUID"] = str(guid)
|
||||
for p in properties[pid]:
|
||||
o = ifcfile[p]
|
||||
if o.is_a("IfcPropertySingleValue"):
|
||||
a[o.Name] = str(o.NominalValue)
|
||||
obj.IfcAttributes = a
|
||||
break
|
||||
if not obj:
|
||||
obj = baseobj
|
||||
if obj:
|
||||
sh = baseobj.Shape.ShapeType if hasattr(baseobj,"Shape") else "None"
|
||||
sols = str(baseobj.Shape.Solids) if hasattr(baseobj,"Shape") else ""
|
||||
pc = str(int((float(count)/(len(products)+len(annotations))*100)))+"% "
|
||||
if DEBUG: print pc,"creating object ",pid," : ",ptype, " with shape: ",sh," ",sols
|
||||
objects[pid] = obj
|
||||
|
||||
# properties
|
||||
if pid in properties:
|
||||
if hasattr(obj,"IfcAttributes"):
|
||||
a = obj.IfcAttributes
|
||||
for p in properties[pid]:
|
||||
o = ifcfile[p]
|
||||
if o.is_a("IfcPropertySingleValue"):
|
||||
a[o.Name] = str(o.NominalValue)
|
||||
obj.IfcAttributes = a
|
||||
# color
|
||||
if FreeCAD.GuiUp and (pid in colors):
|
||||
if DEBUG: print " setting color: ",colors[pid]
|
||||
obj.ViewObject.ShapeColor = colors[pid]
|
||||
|
||||
# if DEBUG is on, recompute after each shape
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
count += 1
|
||||
|
||||
progressbar.next()
|
||||
|
||||
progressbar.stop()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if DEBUG: print "Processing relationships..."
|
||||
if MERGE_MODE == 3:
|
||||
|
||||
if DEBUG: print "Joining shapes..."
|
||||
|
||||
for host,children in additions.items():
|
||||
if ifcfile[host].is_a("IfcBuildingStorey"):
|
||||
compound = []
|
||||
for c in children:
|
||||
if c in shapes.keys():
|
||||
compound.append(shapes[c])
|
||||
del shapes[c]
|
||||
if c in additions.keys():
|
||||
for c2 in additions[c]:
|
||||
if c2 in shapes.keys():
|
||||
compound.append(shapes[c2])
|
||||
del shapes[c2]
|
||||
if compound:
|
||||
name = ifcfile[host].Name or "Floor"
|
||||
if PREFIX_NUMBERS: name = "ID" + str(host) + " " + name
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
|
||||
obj.Label = name
|
||||
obj.Shape = Part.makeCompound(compound)
|
||||
if shapes: # remaining shapes
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature","Unclaimed")
|
||||
obj.Shape = Part.makeCompound(shapes.values())
|
||||
|
||||
# subtractions
|
||||
if SEPARATE_OPENINGS:
|
||||
for subtraction in subtractions:
|
||||
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
|
||||
#print objects[subtraction[0]].Name, objects[subtraction[1]].Name
|
||||
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
|
||||
|
||||
# additions
|
||||
for host,children in additions.iteritems():
|
||||
if host in objects.keys():
|
||||
cobs = [objects[child] for child in children if child in objects.keys()]
|
||||
if cobs:
|
||||
Arch.addComponents(cobs,objects[host])
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
else:
|
||||
|
||||
# cleaning bad shapes
|
||||
for obj in objects.values():
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
if obj.Shape.isNull():
|
||||
Arch.rebuildArchShape(obj)
|
||||
if DEBUG: print "Processing relationships..."
|
||||
|
||||
# subtractions
|
||||
if SEPARATE_OPENINGS:
|
||||
for subtraction in subtractions:
|
||||
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
|
||||
if DEBUG: print "subtracting ",objects[subtraction[0]].Name, " from ", objects[subtraction[1]].Name
|
||||
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
# additions
|
||||
for host,children in additions.items():
|
||||
if host in objects.keys():
|
||||
cobs = [objects[child] for child in children if child in objects.keys()]
|
||||
if cobs:
|
||||
if DEBUG and (len(cobs) > 10) and ( not(Draft.getType(objects[host]) in ["Site","Building","Floor"])):
|
||||
# avoid huge fusions
|
||||
print "more than 10 shapes to add: skipping."
|
||||
else:
|
||||
if DEBUG: print "adding ",cobs, " to ", objects[host].Name
|
||||
Arch.addComponents(cobs,objects[host])
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if DEBUG: print "Cleaning..."
|
||||
|
||||
# cleaning bad shapes
|
||||
for obj in objects.values():
|
||||
if obj.isDerivedFrom("Part::Feature"):
|
||||
if obj.Shape.isNull():
|
||||
Arch.rebuildArchShape(obj)
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
@ -433,10 +589,11 @@ def insert(filename,docname,skip=[]):
|
|||
count += 1
|
||||
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
FreeCADGui.SendMsgToActiveView("ViewFit")
|
||||
print "Finished importing."
|
||||
return doc
|
||||
|
||||
|
||||
|
@ -552,7 +709,7 @@ def export(exportList,filename):
|
|||
elif ifctype == "IfcBuilding":
|
||||
args = args + ["ELEMENT",None,None,None]
|
||||
elif ifctype == "IfcBuildingStorey":
|
||||
args = args + ["ELEMENT",None]
|
||||
args = args + ["ELEMENT",obj.Placement.Base.z]
|
||||
|
||||
# creating the product
|
||||
product = getattr(ifcfile,"create"+ifctype)(*args)
|
||||
|
@ -580,22 +737,26 @@ def export(exportList,filename):
|
|||
for key in obj.IfcAttributes:
|
||||
if not (key in ["IfcUID","FlagForceBrep"]):
|
||||
r = obj.IfcAttributes[key].strip(")").split("(")
|
||||
tp = r[0]
|
||||
val = "(".join(r[1:])
|
||||
val = val.strip("'")
|
||||
val = val.strip('"')
|
||||
if DEBUG: print " property ",key," : ",str(val), " (", str(tp), ")"
|
||||
if tp in ["IfcLabel","IfcText","IfcIdentifier"]:
|
||||
val = str(val)
|
||||
elif tp == "IfcBoolean":
|
||||
if val == ".T.":
|
||||
val = True
|
||||
else:
|
||||
val = False
|
||||
elif tp == "IfcInteger":
|
||||
val = int(val)
|
||||
if len(r) == 1:
|
||||
tp = "IfcText"
|
||||
val = r[0]
|
||||
else:
|
||||
val = float(val)
|
||||
tp = r[0]
|
||||
val = "(".join(r[1:])
|
||||
val = val.strip("'")
|
||||
val = val.strip('"')
|
||||
if DEBUG: print " property ",key," : ",str(val), " (", str(tp), ")"
|
||||
if tp in ["IfcLabel","IfcText","IfcIdentifier"]:
|
||||
val = str(val)
|
||||
elif tp == "IfcBoolean":
|
||||
if val == ".T.":
|
||||
val = True
|
||||
else:
|
||||
val = False
|
||||
elif tp == "IfcInteger":
|
||||
val = int(val)
|
||||
else:
|
||||
val = float(val)
|
||||
props.append(ifcfile.createIfcPropertySingleValue(str(key),None,ifcfile.create_entity(str(tp),val),None))
|
||||
if props:
|
||||
pset = ifcfile.createIfcPropertySet(ifcopenshell.guid.compress(uuid.uuid1().hex),history,'PropertySet',None,props)
|
||||
|
@ -781,8 +942,11 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
|||
for fcface in fcsolid.Faces:
|
||||
for e in fcface.Edges:
|
||||
if not isinstance(e.Curve,Part.Line):
|
||||
curves = True
|
||||
if e.curvatureAt(e.FirstParameter+(e.LastParameter-e.FirstParameter)/2) > 0.0001:
|
||||
curves = True
|
||||
break
|
||||
if curves:
|
||||
shapetype = "triangulated"
|
||||
tris = fcsolid.tessellate(tessellation)
|
||||
for tri in tris[1]:
|
||||
pts = [ifcfile.createIfcCartesianPoint(tuple(tris[0][i])) for i in tri]
|
||||
|
@ -791,6 +955,7 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
|||
face = ifcfile.createIfcFace([bound])
|
||||
faces.append(face)
|
||||
else:
|
||||
shapetype = "brep"
|
||||
for fcface in fcsolid.Faces:
|
||||
loops = []
|
||||
verts = [v.Point for v in Part.Wire(DraftGeomUtils.sortEdges(fcface.OuterWire.Edges)).Vertexes]
|
||||
|
@ -820,7 +985,6 @@ def getRepresentation(ifcfile,context,obj,forcebrep=False,subtraction=False,tess
|
|||
shell = ifcfile.createIfcClosedShell(faces)
|
||||
shape = ifcfile.createIfcFacetedBrep(shell)
|
||||
shapes.append(shape)
|
||||
shapetype = "brep"
|
||||
|
||||
if shapes:
|
||||
|
||||
|
|
|
@ -2430,6 +2430,13 @@ def clone(obj,delta=None):
|
|||
if (len(obj) == 1) and obj[0].isDerivedFrom("Part::Part2DObject"):
|
||||
cl = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython","Clone2D")
|
||||
cl.Label = "Clone of " + obj[0].Label + " (2D)"
|
||||
elif (len(obj) == 1) and hasattr(obj[0],"IfcAttributes"):
|
||||
# arch objects can be clones
|
||||
import Arch
|
||||
cl = getattr(Arch,"make"+obj[0].Proxy.Type)()
|
||||
cl.Label = "Clone of " + obj[0].Label
|
||||
cl.CloneOf = obj[0]
|
||||
return cl
|
||||
else:
|
||||
cl = FreeCAD.ActiveDocument.addObject("Part::FeaturePython","Clone")
|
||||
cl.Label = "Clone of " + obj[0].Label
|
||||
|
|
Loading…
Reference in New Issue
Block a user