diff --git a/src/Mod/Arch/ArchBuildingPart.py b/src/Mod/Arch/ArchBuildingPart.py
index 5f4d5497e1..ce7c7fd9cc 100644
--- a/src/Mod/Arch/ArchBuildingPart.py
+++ b/src/Mod/Arch/ArchBuildingPart.py
@@ -461,7 +461,7 @@ class BuildingPart(ArchIFC.IfcProduct):
return g
def touchChildren(self,obj):
-
+
"Touches all descendents where applicable"
for child in obj.Group:
@@ -472,6 +472,15 @@ class BuildingPart(ArchIFC.IfcProduct):
elif Draft.getType(child) in ["Group","BuildingPart"]:
self.touchChildren(child)
+ def addObject(self,obj,child):
+
+ "Adds an object to the group of this BuildingPart"
+
+ if not child in obj.Group:
+ g = obj.Group
+ g.append(child)
+ obj.Group = g
+
class ViewProviderBuildingPart:
diff --git a/src/Mod/Arch/ArchIFC.py b/src/Mod/Arch/ArchIFC.py
index 0f39cc14e7..f2502b22d1 100644
--- a/src/Mod/Arch/ArchIFC.py
+++ b/src/Mod/Arch/ArchIFC.py
@@ -12,7 +12,10 @@ else:
import ArchIFCSchema
-IfcTypes = [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in ArchIFCSchema.IfcProducts.keys()]
+def uncamel(t):
+ return ''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:]
+
+IfcTypes = [uncamel(t) for t in ArchIFCSchema.IfcProducts.keys()]
class IfcRoot:
"""This class defines the common methods and properties for managing IFC data.
diff --git a/src/Mod/Arch/ArchMaterial.py b/src/Mod/Arch/ArchMaterial.py
index de817b83f6..c4481e35e8 100644
--- a/src/Mod/Arch/ArchMaterial.py
+++ b/src/Mod/Arch/ArchMaterial.py
@@ -44,7 +44,7 @@ __url__ = "http://www.freecadweb.org"
# This module provides tools to add materials to
# Arch objects
-def makeMaterial(name="Material"):
+def makeMaterial(name="Material",color=None,transparency=None):
'''makeMaterial(name): makes an Material object'''
if not FreeCAD.ActiveDocument:
@@ -56,6 +56,12 @@ def makeMaterial(name="Material"):
if FreeCAD.GuiUp:
_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
diff --git a/src/Mod/Arch/ArchSpace.py b/src/Mod/Arch/ArchSpace.py
index 793841fab7..ae8160e71d 100644
--- a/src/Mod/Arch/ArchSpace.py
+++ b/src/Mod/Arch/ArchSpace.py
@@ -348,6 +348,15 @@ class _Space(ArchComponent.Component):
objs.append((o.Object,el))
obj.Boundaries = objs
+ def addObject(self,obj,child):
+
+ "Adds an object to this Space"
+
+ if not child in obj.Group:
+ g = obj.Group
+ g.append(child)
+ obj.Group = g
+
def getShape(self,obj):
"computes a shape from a base shape and/or boundary faces"
@@ -358,8 +367,8 @@ class _Space(ArchComponent.Component):
pl = obj.Placement
#print("starting compute")
- # 1: if we have a base shape, we use it
+ # 1: if we have a base shape, we use it
if obj.Base:
if hasattr(obj.Base,'Shape'):
if obj.Base.Shape.Solids:
@@ -379,6 +388,12 @@ class _Space(ArchComponent.Component):
else:
bb.add(b[0].Shape.BoundBox)
if not bb:
+ # compute area even if we are not calculating the shape
+ if obj.Shape and obj.Shape.Solids:
+ if hasattr(obj.Area,"Value"):
+ a = self.getArea(obj)
+ if obj.Area.Value != a:
+ obj.Area = a
return
shape = Part.makeBox(bb.XLength,bb.YLength,bb.ZLength,FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin))
#print("created shape from boundbox")
diff --git a/src/Mod/Arch/CMakeLists.txt b/src/Mod/Arch/CMakeLists.txt
index 1e9a2041cd..b12846a045 100644
--- a/src/Mod/Arch/CMakeLists.txt
+++ b/src/Mod/Arch/CMakeLists.txt
@@ -14,6 +14,7 @@ SET(Arch_SRCS
importIFC.py
importIFClegacy.py
importIFCHelper.py
+ importIFCmulticore.py
exportIFCHelper.py
Arch.py
ArchBuilding.py
diff --git a/src/Mod/Arch/Resources/ui/preferences-ifc.ui b/src/Mod/Arch/Resources/ui/preferences-ifc.ui
index c1ad754e10..5ce5a33834 100644
--- a/src/Mod/Arch/Resources/ui/preferences-ifc.ui
+++ b/src/Mod/Arch/Resources/ui/preferences-ifc.ui
@@ -20,25 +20,25 @@
9
- -
-
-
- Show this dialog when importing
-
-
- ifcShowDialog
-
-
- Mod/Arch
-
-
-
-
General options
+
-
+
+
+ Show this dialog when importing
+
+
+ ifcShowDialog
+
+
+ Mod/Arch
+
+
+
-
@@ -76,6 +76,46 @@ One object is the base object, the others are clones.
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 16
+ 20
+
+
+
+
+ -
+
+
+ Number of cores to use:
+
+
+
+ -
+
+
+ EXPERIMENAL - The number of cores to use in multicore mode. Keep 0 to disable multicore mode, or 1 to use multicore mode in single-core mode (safer if you get crashes). Max value should be your number of cores - 1, ex: 3 of you have a quad-core CPU.
+
+
+ ifcMulticore
+
+
+ Mod/Arch
+
+
+
+
+
@@ -422,6 +462,11 @@ FreeCAD object properties
qPixmapFromMimeSource
+
+ Gui::PrefSpinBox
+ QSpinBox
+
+
Gui::PrefCheckBox
QCheckBox
diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py
index 01afd84675..2aeca48d81 100644
--- a/src/Mod/Arch/importIFC.py
+++ b/src/Mod/Arch/importIFC.py
@@ -170,7 +170,8 @@ def getPreferences():
'SPLIT_LAYERS': p.GetBool("ifcSplitLayers",False),
'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport",False),
'ALLOW_INVALID': p.GetBool("ifcAllowInvalid",False),
- 'REPLACE_PROJECT': p.GetBool("ifcReplaceProject",False)
+ 'REPLACE_PROJECT': p.GetBool("ifcReplaceProject",False),
+ 'MULTICORE':p.GetInt("ifcMulticore",0)
}
if preferences['MERGE_MODE_ARCH'] > 0:
@@ -217,6 +218,10 @@ def insert(srcfile,docname,skip=[],only=[],root=None,preferences=None):
# read preference settings
if preferences is None:
preferences = getPreferences()
+
+ if preferences["MULTICORE"] and (not hasattr(srcfile,"by_guid")):
+ import importIFCmulticore
+ return importIFCmulticore.insert(srcfile,docname,preferences)
try:
import ifcopenshell
diff --git a/src/Mod/Arch/importIFCHelper.py b/src/Mod/Arch/importIFCHelper.py
index a07f6324e2..d05f8dc719 100644
--- a/src/Mod/Arch/importIFCHelper.py
+++ b/src/Mod/Arch/importIFCHelper.py
@@ -340,11 +340,24 @@ def buildRelMaterialColors(ifcfile, prodrepr):
pass
+def getColorFromMaterial(material):
+
+ if material.HasRepresentation:
+ rep = material.HasRepresentation[0]
+ if hasattr(rep,"Representations") and rep.Representations:
+ rep = rep.Representations[0]
+ if rep.is_a("IfcStyledRepresentation"):
+ return getColorFromStyledItem(rep)
+ return None
+
+
def getColorFromStyledItem(styled_item):
# styled_item should be a IfcStyledItem
+ if styled_item.is_a("IfcStyledRepresentation"):
+ styled_item = styled_item.Items[0]
+
if not styled_item.is_a("IfcStyledItem"):
- print("Not a IfcStyledItem passed.")
return None
rgb_color = None
diff --git a/src/Mod/Arch/importIFCmulticore.py b/src/Mod/Arch/importIFCmulticore.py
new file mode 100644
index 0000000000..5065a5135a
--- /dev/null
+++ b/src/Mod/Arch/importIFCmulticore.py
@@ -0,0 +1,263 @@
+# ***************************************************************************
+# * Copyright (c) 2020 Yorik van Havre *
+# * *
+# * 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 *
+# * *
+# ***************************************************************************
+
+from __future__ import print_function
+
+"""FreeCAD IFC importer - Multicore version"""
+
+import sys
+import time
+import os
+import FreeCAD
+import Draft
+import Arch
+import importIFC
+import importIFCHelper
+from FreeCAD import Base
+import ArchIFC
+
+layers = {} # ifcid : Draft_Layer
+materials = {} #ifcid : Arch_Material
+objects = {} #ifcid : Arch_Component
+
+
+def open(filename):
+
+ "opens an IFC file in a new document"
+
+ return insert(filename)
+
+
+def insert(filename,docname=None,preferences=None):
+
+ """imports the contents of an IFC file in the given document"""
+
+ import ifcopenshell
+ from ifcopenshell import geom
+
+ # reset global values
+ global layers
+ global materials
+ global objects
+ layers = {}
+ materials = {}
+ objects = {}
+
+ # statistics
+ starttime = time.time() # in seconds
+ filesize = os.path.getsize(filename) * 0.000001 # in megabytes
+ print("Opening",filename+",",round(filesize,2),"Mb")
+
+ # setup ifcopenshell
+ if not preferences:
+ preferences = importIFC.getPreferences()
+ settings = ifcopenshell.geom.settings()
+ settings.set(settings.USE_BREP_DATA,True)
+ settings.set(settings.SEW_SHELLS,True)
+ settings.set(settings.USE_WORLD_COORDS,True)
+ if preferences['SEPARATE_OPENINGS']:
+ settings.set(settings.DISABLE_OPENING_SUBTRACTIONS,True)
+ if preferences['SPLIT_LAYERS'] and hasattr(settings,"APPLY_LAYERSETS"):
+ settings.set(settings.APPLY_LAYERSETS,True)
+
+ # setup document
+ if not FreeCAD.ActiveDocument:
+ if not docname:
+ docname = os.path.splitext(os.path.basename(filename))[0]
+ doc = FreeCAD.newDocument(docname)
+ doc.Label = docname
+ FreeCAD.setActiveDocument(doc.Name)
+
+ # open the file
+ ifcfile = ifcopenshell.open(filename)
+ progressbar = Base.ProgressIndicator()
+ productscount = len(ifcfile.by_type("IfcProduct"))
+ progressbar.start("Importing "+str(productscount)+" products...",productscount)
+ cores = preferences["MULTICORE"]
+ iterator = ifcopenshell.geom.iterator(settings,ifcfile,cores)
+ iterator.initialize()
+ count = 0
+
+ # process objects
+ while True:
+ item = iterator.get()
+ if item:
+ brep = item.geometry.brep_data
+ ifcproduct = ifcfile.by_id(item.guid)
+ obj = createProduct(ifcproduct,brep)
+ progressbar.next(True)
+ writeProgress(count,productscount,starttime)
+ count += 1
+ if not iterator.next():
+ break
+
+ # finished
+ progressbar.stop()
+ FreeCAD.ActiveDocument.recompute()
+ endtime = round(time.time()-starttime,1)
+ fs = round(filesize,1)
+ ratio = int(endtime/filesize)
+ endtime = "%02d:%02d" % (divmod(endtime, 60))
+ writeProgress() # this cleans the line
+ print("Finished importing",fs,"Mb in",endtime,"s, or",ratio,"s/Mb")
+ return FreeCAD.ActiveDocument
+
+
+def writeProgress(count=None,total=None,starttime=None):
+
+ """write progress to console"""
+
+ if not FreeCAD.GuiUp:
+ if count is None:
+ sys.stdout.write("\r")
+ return
+ r = count/total
+ elapsed = round(time.time()-starttime,1)
+ if r:
+ rest = elapsed*((1-r)/r)
+ eta = "%02d:%02d" % (divmod(rest, 60))
+ else:
+ eta = "--:--"
+ hashes = '#'*int(r*10)+' '*int(10-r*10)
+ fstring = '\rImporting '+str(total)+' products... [{0}] {1}%, ETA: {2}'
+ sys.stdout.write(fstring.format(hashes, int(r*100),eta))
+
+
+def createProduct(ifcproduct,brep):
+
+ """creates an Arch object from an IFC product"""
+
+ import Part
+
+ shape = Part.Shape()
+ shape.importBrepFromString(brep,False)
+ shape.scale(1000.0) # IfcOpenShell outputs in meters
+ if ifcproduct.is_a("IfcSpace"):
+ obj = Arch.makeSpace()
+ else:
+ obj = Arch.makeComponent()
+ obj.Shape = shape
+ if ifcproduct.Name:
+ obj.Label = ifcproduct.Name
+ objects[ifcproduct.id()] = obj
+ setAttributes(obj,ifcproduct)
+ setProperties(obj,ifcproduct)
+ createLayer(obj,ifcproduct)
+ createMaterial(obj,ifcproduct)
+ createModelStructure(obj,ifcproduct)
+ return obj
+
+
+def setAttributes(obj,ifcproduct):
+
+ """sets the IFC attributes of a component"""
+
+ ifctype = ArchIFC.uncamel(ifcproduct.is_a())
+ if ifctype in ArchIFC.IfcTypes:
+ obj.IfcType = ifctype
+ for attr in dir(ifcproduct):
+ if attr in obj.PropertiesList:
+ value = getattr(ifcproduct,attr)
+ if value:
+ try:
+ setattr(obj,attr,value)
+ except:
+ pass
+
+
+def setProperties(obj,ifcproduct):
+
+ """sets the IFC properties of a component"""
+
+ props = obj.IfcProperties
+ for prel in ifcproduct.IsDefinedBy:
+ if prel.is_a("IfcRelDefinesByProperties"):
+ pset = prel.RelatingPropertyDefinition
+ if pset.is_a("IfcPropertySet"):
+ for prop in pset.HasProperties:
+ if hasattr(prop,"NominalValue"):
+ propname = prop.Name+";;"+pset.Name
+ v = [p.strip("'") for p in str(prop.NominalValue).strip(")").split(")")]
+ propvalue = ";;".join(v)
+
+
+def createLayer(obj,ifcproduct):
+
+ """sets the layer of a component"""
+
+ global layers
+
+ if ifcproduct.Representation:
+ for rep in ifcproduct.Representation.Representations:
+ for layer in rep.LayerAssignments:
+ if not layer.id() in layers:
+ layers[layer.id()] = Draft.makeLayer(layer.Name)
+ layers[layer.id()].Proxy.addObject(layers[layer.id()],obj)
+
+
+def createMaterial(obj,ifcproduct):
+
+ """sets the material of a component"""
+
+ global materials
+
+ for association in ifcproduct.HasAssociations:
+ if association.is_a("IfcRelAssociatesMaterial"):
+ material = association.RelatingMaterial
+ if material.is_a("IfcMaterialList"):
+ material = material.Materials[0] # take the first one for now...
+ if material.is_a("IfcMaterial"):
+ if not material.id() in materials:
+ color = importIFCHelper.getColorFromMaterial(material)
+ materials[material.id()] = Arch.makeMaterial(material.Name,color=color)
+ obj.Material = materials[material.id()]
+
+
+def createModelStructure(obj,ifcobj):
+
+ """sets the parent containers of an IFC object"""
+
+ global objects
+
+ parentlist = []
+ if hasattr(ifcobj,"ContainedInStructure"):
+ for rel in ifcobj.ContainedInStructure:
+ parentlist.append(rel.RelatingStructure)
+ elif hasattr(ifcobj,"Decomposes"):
+ for rel in ifcobj.Decomposes:
+ if rel.is_a("IfcRelAggregates"):
+ parentlist.append(rel.RelatingObject)
+ for parent in parentlist:
+ if not parent.id() in objects:
+ if parent.is_a("IfcProject"):
+ parentobj = Arch.makeProject()
+ elif parent.is_a("IfcSite"):
+ parentobj = Arch.makeSite()
+ else:
+ parentobj = Arch.makeBuildingPart()
+ parentobj.Label = parent.Name
+ setAttributes(parentobj,parent)
+ setProperties(parentobj,parent)
+ createModelStructure(parentobj,parent)
+ objects[parent.id()] = parentobj
+ if hasattr(objects[parent.id()].Proxy,"addObject"):
+ objects[parent.id()].Proxy.addObject(objects[parent.id()],obj)
+