323 lines
10 KiB
Python
323 lines
10 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 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,...]
|
|
colors = {} # objname : (r,g,b)
|
|
|
|
|
|
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 = importIFCHelper.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
|
|
for item in iterator:
|
|
brep = item.geometry.brep_data
|
|
ifcproduct = ifcfile.by_id(item.guid)
|
|
obj = createProduct(ifcproduct,brep)
|
|
progressbar.next(True)
|
|
writeProgress(count,productscount,starttime)
|
|
count += 1
|
|
|
|
# process 2D annotations
|
|
annotations = ifcfile.by_type("IfcAnnotation")
|
|
if annotations:
|
|
print("Processing",str(len(annotations)),"annotations...")
|
|
ifcscale = importIFCHelper.getScaling(ifcfile)
|
|
for annotation in annotations:
|
|
importIFCHelper.createAnnotation(annotation,FreeCAD.ActiveDocument,ifcscale,preferences)
|
|
|
|
# post-processing
|
|
processRelationships()
|
|
storeColorDict()
|
|
|
|
# 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
|
|
objects[ifcproduct.id()] = obj
|
|
setAttributes(obj,ifcproduct)
|
|
setProperties(obj,ifcproduct)
|
|
createLayer(obj,ifcproduct)
|
|
createMaterial(obj,ifcproduct)
|
|
createModelStructure(obj,ifcproduct)
|
|
setRelationships(obj,ifcproduct)
|
|
setColor(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 Exception:
|
|
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 setColor(obj,ifcproduct):
|
|
|
|
"""sets the color of an object"""
|
|
|
|
global colors
|
|
|
|
color = importIFCHelper.getColorFromProduct(ifcproduct)
|
|
colors[obj.Name] = color
|
|
if FreeCAD.GuiUp and color:
|
|
obj.ViewObject.ShapeColor = color[:3]
|
|
|
|
|
|
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.make_layer(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].items():
|
|
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)
|
|
|
|
def storeColorDict():
|
|
|
|
"""stores the color dictionary in the document Meta if non-GUI mode"""
|
|
|
|
if colors and not FreeCAD.GuiUp:
|
|
import json
|
|
d = FreeCAD.ActiveDocument.Meta
|
|
d["colordict"] = json.dumps(colors)
|
|
FreeCAD.ActiveDocument.Meta = d
|