Files
create/src/Mod/Arch/importIFCmulticore.py

294 lines
9.6 KiB
Python

# ***************************************************************************
# * 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 *
# * *
# ***************************************************************************
"""FreeCAD IFC importer - Multicore version"""
from __future__ import print_function
import sys
import time
import os
import FreeCAD
import Draft
import Arch
import importIFC
import importIFCHelper
from FreeCAD import Base
import ArchIFC
# global dicts to store ifc object/freecad object relationships
layers = {} # ifcid : Draft_Layer
materials = {} #ifcid : Arch_Material
objects = {} #ifcid : Arch_Component
subs = {} #host_ifcid: [child_ifcid,...]
adds = {} #host_ifcid: [child_ifcid,...]
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
global adds
global subs
layers = {}
materials = {}
objects = {}
adds = {}
subs = {}
# 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
processRelationships()
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
objects[ifcproduct.id()] = obj
setAttributes(obj,ifcproduct)
setProperties(obj,ifcproduct)
createLayer(obj,ifcproduct)
createMaterial(obj,ifcproduct)
createModelStructure(obj,ifcproduct)
setRelationships(obj,ifcproduct)
return obj
def setAttributes(obj,ifcproduct):
"""sets the IFC attributes of a component"""
ifctype = ArchIFC.uncamel(ifcproduct.is_a())
if ifcproduct.Name:
obj.Label = ifcproduct.Name
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
for parent in importIFCHelper.getParents(ifcobj):
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()
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)
def setRelationships(obj,ifcobj):
"""sets additions/subtractions"""
global adds
global subs
if hasattr(ifcobj,"HasOpenings") and ifcobj.HasOpenings:
for rel in ifcobj.HasOpenings:
subs.setdefault(ifcobj.id(),[]).append(rel.RelatedOpeningElement)
# TODO: assemblies & booleans
def processRelationships():
"""process all stored relationships"""
for dom in ((subs,"Subtractions"),(adds,"Additions")):
for key,vals in dom[0]:
if key in objects:
for val in vals:
if val in objects:
if hasattr(objects[key],dom[1]):
g = getattr(objects[key],dom[1])
g.append(val)
setattr(objects[key],dom[1],g)