Arch: New multicore IFC importer
This commit is contained in:
@@ -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:
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -14,6 +14,7 @@ SET(Arch_SRCS
|
||||
importIFC.py
|
||||
importIFClegacy.py
|
||||
importIFCHelper.py
|
||||
importIFCmulticore.py
|
||||
exportIFCHelper.py
|
||||
Arch.py
|
||||
ArchBuilding.py
|
||||
|
||||
@@ -20,25 +20,25 @@
|
||||
<property name="margin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="checkBox_7">
|
||||
<property name="text">
|
||||
<string>Show this dialog when importing</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcShowDialog</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>General options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="checkBox_7">
|
||||
<property name="text">
|
||||
<string>Show this dialog when importing</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcShowDialog</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefCheckBox" name="gui::prefcheckbox_5">
|
||||
<property name="toolTip">
|
||||
@@ -76,6 +76,46 @@ One object is the base object, the others are clones.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Number of cores to use:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Gui::PrefSpinBox" name="spinBox">
|
||||
<property name="toolTip">
|
||||
<string>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.</string>
|
||||
</property>
|
||||
<property name="prefEntry" stdset="0">
|
||||
<cstring>ifcMulticore</cstring>
|
||||
</property>
|
||||
<property name="prefPath" stdset="0">
|
||||
<cstring>Mod/Arch</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -422,6 +462,11 @@ FreeCAD object properties</string>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::PrefSpinBox</class>
|
||||
<extends>QSpinBox</extends>
|
||||
<header>Gui/PrefWidgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>Gui::PrefCheckBox</class>
|
||||
<extends>QCheckBox</extends>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
263
src/Mod/Arch/importIFCmulticore.py
Normal file
263
src/Mod/Arch/importIFCmulticore.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2020 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * 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)
|
||||
|
||||
Reference in New Issue
Block a user