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

1402 lines
63 KiB
Python

# ***************************************************************************
# * Copyright (c) 2014 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
__title__ = "FreeCAD IFC importer - Enhanced ifcopenshell-only version"
__author__ = "Yorik van Havre","Jonathan Wiedemann","Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
import six
import os
import math
import FreeCAD
import Part
import Draft
import Arch
import DraftVecUtils
import ArchIFCSchema
import importIFCHelper
## @package importIFC
# \ingroup ARCH
# \brief IFC file format importer
#
# This module provides tools to import IFC files.
DEBUG = False # Set to True to see debug messages. Otherwise, totally silent
ZOOMOUT = True # Set to False to not zoom extents after import
if open.__module__ in ['__builtin__','io']:
pyopen = open # because we'll redefine open below
# ************************************************************************************************
# ********** templates and other definitions ****
# which IFC type must create which FreeCAD type
typesmap = {
"Site": [
"IfcSite"
],
"Building": [
"IfcBuilding"
],
"Floor": [
"IfcBuildingStorey"
],
"Structure": [
"IfcBeam",
"IfcBeamStandardCase",
"IfcColumn",
"IfcColumnStandardCase",
"IfcSlab",
"IfcFooting",
"IfcPile",
"IfcTendon"
],
"Wall": [
"IfcWall",
"IfcWallStandardCase",
"IfcCurtainWall"
],
"Window": [
"IfcWindow",
"IfcWindowStandardCase",
"IfcDoor",
"IfcDoorStandardCase"
],
"Roof": [
"IfcRoof"
],
"Stairs": [
"IfcStair",
"IfcStairFlight",
"IfcRamp",
"IfcRampFlight"
],
"Space": [
"IfcSpace"
],
"Rebar": [
"IfcReinforcingBar"
],
"Panel": [
"IfcPlate"
],
"Equipment": [
"IfcFurnishingElement",
"IfcSanitaryTerminal",
"IfcFlowTerminal",
"IfcElectricAppliance"
],
"Pipe": [
"IfcPipeSegment"
],
"PipeConnector": [
"IfcPipeFitting"
],
"BuildingPart":[
"IfcElementAssembly"
]
}
# which IFC entity (product) is a structural object
structuralifcobjects = (
"IfcStructuralCurveMember",
"IfcStructuralSurfaceMember",
"IfcStructuralPointConnection",
"IfcStructuralCurveConnection",
"IfcStructuralSurfaceConnection",
"IfcStructuralAction",
"IfcStructuralPointAction",
"IfcStructuralLinearAction",
"IfcStructuralLinearActionVarying",
"IfcStructuralPlanarAction"
)
# ************************************************************************************************
# ********** get the prefs, available in import and export ****************
def getPreferences():
"""retrieves IFC preferences.
MERGE_MODE_ARCH:
0 = parametric arch objects
1 = non-parametric arch objects
2 = Part shapes
3 = One compound per storey
"""
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")
if FreeCAD.GuiUp and p.GetBool("ifcShowDialog",False):
import FreeCADGui
FreeCADGui.showPreferences("Import-Export",0)
preferences = {
'DEBUG': p.GetBool("ifcDebug",False),
'PREFIX_NUMBERS': p.GetBool("ifcPrefixNumbers",False),
'SKIP': p.GetString("ifcSkip","").split(","),
'SEPARATE_OPENINGS': p.GetBool("ifcSeparateOpenings",False),
'ROOT_ELEMENT': p.GetString("ifcRootElement","IfcProduct"),
'GET_EXTRUSIONS': p.GetBool("ifcGetExtrusions",False),
'MERGE_MATERIALS': p.GetBool("ifcMergeMaterials",False),
'MERGE_MODE_ARCH': p.GetInt("ifcImportModeArch",0),
'MERGE_MODE_STRUCT': p.GetInt("ifcImportModeStruct",1),
'CREATE_CLONES': p.GetBool("ifcCreateClones",True),
'IMPORT_PROPERTIES': p.GetBool("ifcImportProperties",False),
'SPLIT_LAYERS': p.GetBool("ifcSplitLayers",False),
'FITVIEW_ONIMPORT': p.GetBool("ifcFitViewOnImport",False),
'ALLOW_INVALID': p.GetBool("ifcAllowInvalid",False),
'REPLACE_PROJECT': p.GetBool("ifcReplaceProject",False)
}
if preferences['MERGE_MODE_ARCH'] > 0:
preferences['SEPARATE_OPENINGS'] = False
preferences['GET_EXTRUSIONS'] = False
if not preferences['SEPARATE_OPENINGS']:
preferences['SKIP'].append("IfcOpeningElement")
return preferences
# ************************************************************************************************
# ********** open and import IFC ****************
def open(filename,skip=[],only=[],root=None):
"opens an IFC file in a new document"
docname = os.path.splitext(os.path.basename(filename))[0]
docname = importIFCHelper.decode(docname,utf=True)
doc = FreeCAD.newDocument(docname)
doc.Label = docname
doc = insert(filename,doc.Name,skip,only,root)
return doc
def insert(filename,docname,skip=[],only=[],root=None,preferences=None):
"""insert(filename,docname,skip=[],only=[],root=None,preferences=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)."""
# read preference settings
if preferences is None:
preferences = getPreferences()
try:
import ifcopenshell
except:
FreeCAD.Console.PrintError("IfcOpenShell was not found on this system. IFC support is disabled\n")
FreeCAD.Console.PrintMessage("Visit https://www.freecadweb.org/wiki/Arch_IFC to learn how to install it\n")
return
if preferences['DEBUG']: print("Opening ",filename,"...",end="")
try:
doc = FreeCAD.getDocument(docname)
except:
doc = FreeCAD.newDocument(docname)
FreeCAD.ActiveDocument = doc
if preferences['DEBUG']: print("done.")
global parametrics
# allow to override the root element
if root:
preferences['ROOT_ELEMENT'] = root
# keeping global variable for debugging purposes
# global ifcfile
filename = importIFCHelper.decode(filename,utf=True)
ifcfile = ifcopenshell.open(filename)
# get file scale
ifcscale = importIFCHelper.getScaling(ifcfile)
# IfcOpenShell multiplies the precision value of the file by 100
# So we raise the precision by 100 too to compensate...
# ctxs = ifcfile.by_type("IfcGeometricRepresentationContext")
# for ctx in ctxs:
# if not ctx.is_a("IfcGeometricRepresentationSubContext"):
# ctx.Precision = ctx.Precision/100
# set default ifcopenshell options to work in brep mode
from ifcopenshell import geom
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)
# build all needed tables
if preferences['DEBUG']: print("Building types and relationships table...",end="")
# type tables
sites = ifcfile.by_type("IfcSite")
buildings = ifcfile.by_type("IfcBuilding")
floors = ifcfile.by_type("IfcBuildingStorey")
openings = ifcfile.by_type("IfcOpeningElement")
materials = ifcfile.by_type("IfcMaterial")
products, annotations = importIFCHelper.buildRelProductsAnnotations(ifcfile, preferences['ROOT_ELEMENT'])
# empty relation tables
objects = {} # { id:object, ... }
shapes = {} # { id:shaoe } only used for merge mode
structshapes = {} # { id:shaoe } only used for merge mode
sharedobjects = {} # { representationmapid:object }
parametrics = [] # a list of imported objects whose parametric relationships need processing after all objects have been created
profiles = {} # to store reused extrusion profiles {ifcid:fcobj,...}
layers = {} # { layer_name, [ids] }
# filled relation tables
# TODO for the following tables might be better use inverse attributes, done for properties
# see https://forum.freecadweb.org/viewtopic.php?f=39&t=37892
prodrepr = importIFCHelper.buildRelProductRepresentation(ifcfile)
additions = importIFCHelper.buildRelAdditions(ifcfile)
groups = importIFCHelper.buildRelGroups(ifcfile)
subtractions = importIFCHelper.buildRelSubtractions(ifcfile)
mattable = importIFCHelper.buildRelMattable(ifcfile)
colors = importIFCHelper.buildRelProductColors(ifcfile, prodrepr)
colordict = {} # { objname:color tuple } for non-GUI use
if preferences['DEBUG']: print("done.")
# only import a list of IDs and their children, if defined
if only:
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]
# start the actual import, set FreeCAD UI
count = 0
from FreeCAD import Base
progressbar = Base.ProgressIndicator()
progressbar.start("Importing IFC objects...",len(products))
if preferences['DEBUG']: print("Parsing",len(products),"BIM objects...")
# Prepare the 3D view if applicable
if preferences['FITVIEW_ONIMPORT'] and FreeCAD.GuiUp:
overallboundbox = None
import FreeCADGui
FreeCADGui.ActiveDocument.activeView().viewAxonometric()
# Create the base project object
if not preferences['REPLACE_PROJECT']:
if len(ifcfile.by_type("IfcProject")) > 0:
projectImporter = importIFCHelper.ProjectImporter(ifcfile, objects)
projectImporter.execute()
else:
# https://forum.freecadweb.org/viewtopic.php?f=39&t=40624
print("No IfcProject found in the ifc file. Nothing imported")
return doc
# handle IFC products
for product in products:
count += 1
pid = product.id()
guid = product.GlobalId
ptype = product.is_a()
if preferences['DEBUG']: print(count,"/",len(products),"object #"+str(pid),":",ptype,end="")
# build list of related property sets
psets = importIFCHelper.getIfcPropertySets(ifcfile, pid)
# add layer names to layers
if hasattr(product, "Representation") and hasattr(product.Representation, "Representations"):
if len(product.Representation.Representations) > 0:
lays = product.Representation.Representations[0].LayerAssignments
if len(lays) > 0:
layer_name = lays[0].Name
if layer_name not in list(layers.keys()):
layers[layer_name] = [pid]
else:
layers[layer_name].append(pid)
if preferences['DEBUG']: print(" layer ", layer_name, " found", ptype,end="")
else:
if preferences['DEBUG']: print(" no layer found", ptype,end="")
# checking for full FreeCAD parametric definition, overriding everything else
if psets and FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("IfcImportFreeCADProperties",False):
if "FreeCADPropertySet" in [ifcfile[pset].Name for pset in psets.keys()]:
if preferences['DEBUG']: print(" restoring from parametric definition...",end="")
obj = createFromProperties(psets,ifcfile)
if obj:
objects[pid] = obj
if preferences['DEBUG']: print("done")
continue
else:
if preferences['DEBUG']: print("failed")
# no parametric data, we go the good old way
name = str(ptype[3:])
if product.Name:
name = product.Name
if six.PY2:
name = name.encode("utf8")
if preferences['PREFIX_NUMBERS']:
name = "ID" + str(pid) + " " + name
obj = None
baseobj = None
brep = None
shape = None
# classify object and verify if we must skip it
archobj = True # assume all objects not in structuralifcobjects are architecture
structobj = False
if ptype in structuralifcobjects:
archobj = False
structobj = True
if preferences['DEBUG']: print(" (struct)",end="")
else:
if preferences['DEBUG']: print(" (arch)",end="")
if preferences['MERGE_MODE_ARCH'] == 4 and archobj:
if preferences['DEBUG']: print(" skipped.")
continue
if preferences['MERGE_MODE_STRUCT'] == 3 and not archobj:
if preferences['DEBUG']: print(" skipped.")
continue
if pid in skip: # user given id skip list
if preferences['DEBUG']: print(" skipped.")
continue
if ptype in skip: # user given type skip list
if preferences['DEBUG']: print(" skipped.")
continue
if ptype in preferences['SKIP']: # preferences-set type skip list
if preferences['DEBUG']: print(" skipped.")
continue
if preferences['REPLACE_PROJECT']: # options-enabled project/site/building skip
if ptype in ['IfcProject','IfcSite']:
if preferences['DEBUG']: print(" skipped.")
continue
elif ptype in ['IfcBuilding']:
if len(ifcfile.by_type("IfcBuilding")) == 1:
# let multiple buildings through...
if preferences['DEBUG']: print(" skipped.")
continue
elif ptype in ['IfcBuildingStorey']:
if len(ifcfile.by_type("IfcBuildingStorey")) == 1:
# let multiple storeys through...
if preferences['DEBUG']: print(" skipped.")
continue
# check if this object is sharing its shape (mapped representation)
clone = None
store = None
prepr = None
try:
prepr = product.Representation
except:
if preferences['DEBUG']: print(" ERROR unable to get object representation",end="")
if prepr and (preferences['MERGE_MODE_ARCH'] == 0) and archobj and preferences['CREATE_CLONES']:
for r in prepr.Representations:
if r.RepresentationIdentifier.upper() == "BODY":
if r.Items[0].is_a("IfcMappedItem"):
originalid = r.Items[0].MappingSource.id()
if originalid in sharedobjects:
clone = sharedobjects[originalid]
else:
sharedobjects[originalid] = None
store = originalid # flag this object to be stored later
# set additional setting for structural entities
if hasattr(settings,"INCLUDE_CURVES"):
if structobj:
settings.set(settings.INCLUDE_CURVES,True)
else:
settings.set(settings.INCLUDE_CURVES,False)
try:
cr = ifcopenshell.geom.create_shape(settings,product)
brep = cr.geometry.brep_data
except:
pass # IfcOpenShell will yield an error if a given product has no shape, but we don't care, we're brave enough
# from now on we have a brep string
if brep:
if preferences['DEBUG']: print(" "+str(int(len(brep)/1000))+"k ",end="")
# create a Part shape
shape = Part.Shape()
shape.importBrepFromString(brep,False)
shape.scale(1000.0) # IfcOpenShell always outputs in meters, we convert to mm, the freecad internal unit
if shape.isNull() and (not preferences['ALLOW_INVALID']):
if preferences['DEBUG']: print("null shape ",end="")
elif not shape.isValid() and (not preferences['ALLOW_INVALID']):
if preferences['DEBUG']: print("invalid shape ",end="")
else:
# add to the global boundbox if applicable
if preferences['FITVIEW_ONIMPORT'] and FreeCAD.GuiUp:
try:
bb = shape.BoundBox
# if preferences['DEBUG']: print(' ' + str(bb),end="")
except:
bb = None
if preferences['DEBUG']: print(' BB could not be computed',end="")
if bb and bb.isValid():
if not overallboundbox:
overallboundbox = bb
if not overallboundbox.isInside(bb):
FreeCADGui.SendMsgToActiveView("ViewFit")
overallboundbox.add(bb)
if (preferences['MERGE_MODE_ARCH'] > 0 and archobj) or structobj:
# we are not using Arch objects
# additional tweaks to set when not using Arch objects
if ptype == "IfcSpace": # do not add spaces to compounds
if preferences['DEBUG']: print("skipping space ",pid,end="")
elif structobj:
structshapes[pid] = shape
if preferences['DEBUG']: print(len(shape.Solids),"solids ",end="")
baseobj = shape
else:
shapes[pid] = shape
if preferences['DEBUG']: print(len(shape.Solids),"solids ",end="")
baseobj = shape
else:
# create base shape object
if clone:
if preferences['DEBUG']: print("clone ",end="")
else:
if preferences['GET_EXTRUSIONS'] and (preferences['MERGE_MODE_ARCH'] != 1):
# get IFC profile
profileid = None
sortmethod = None
if product.Representation:
if product.Representation.Representations:
if product.Representation.Representations[0].is_a("IfcShapeRepresentation"):
if product.Representation.Representations[0].Items:
if product.Representation.Representations[0].Items[0].is_a("IfcExtrudedAreaSolid"):
profileid = product.Representation.Representations[0].Items[0].SweptArea.id()
sortmethod = importIFCHelper.getProfileCenterPoint(product.Representation.Representations[0].Items[0])
# recompose extrusions from a shape
if not sortmethod:
if ptype in ["IfcWall","IfcWallStandardCase","IfcSpace"]:
sortmethod = "z"
else:
sortmethod = "area"
ex = Arch.getExtrusionData(shape,sortmethod) # is this an extrusion?
if ex:
# check for extrusion profile
baseface = None
addplacement = None
if profileid and (profileid in profiles):
# reuse existing profile if existing
print("shared extrusion ",end="")
baseface = profiles[profileid]
# calculate delta placement between stored profile and this one
addplacement = FreeCAD.Placement()
r = FreeCAD.Rotation(baseface.Shape.Faces[0].normalAt(0,0),ex[0].Faces[0].normalAt(0,0))
if r.Angle > 0.000001:
# use shape methods to easily obtain a correct placement
ts = Part.Shape()
ts.rotate(DraftVecUtils.tup(baseface.Shape.CenterOfMass), DraftVecUtils.tup(r.Axis), math.degrees(r.Angle))
addplacement = ts.Placement
d = ex[0].CenterOfMass.sub(baseface.Shape.CenterOfMass)
if d.Length > 0.000001:
addplacement.move(d)
if not baseface:
# this is an extrusion but we haven't built the profile yet
print("extrusion ",end="")
import DraftGeomUtils
if DraftGeomUtils.hasCurves(ex[0]) or len(ex[0].Wires) != 1:
# is this a circle?
if (len(ex[0].Edges) == 1) and isinstance(ex[0].Edges[0].Curve,Part.Circle):
baseface = Draft.makeCircle(ex[0].Edges[0])
else:
# curves or holes? We just make a Part face
baseface = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_footprint")
# bug/feature in ifcopenshell? Some faces of a shell may have non-null placement
# workaround to remove the bad placement: exporting/reimporting as step
if not ex[0].Placement.isNull():
import tempfile
fd, tf = tempfile.mkstemp(suffix=".stp")
ex[0].exportStep(tf)
f = Part.read(tf)
os.close(fd)
os.remove(tf)
else:
f = ex[0]
baseface.Shape = f
else:
# no curve and no hole, we can make a draft object
verts = [v.Point for v in ex[0].Wires[0].OrderedVertexes]
# TODO verts are different if shape is made of RectangleProfileDef or not
# is this a rectangle?
if importIFCHelper.isRectangle(verts):
baseface = Draft.makeRectangle(verts,face=True)
else:
# no hole and no curves, we make a Draft Wire instead
baseface = Draft.makeWire(verts,closed=True)
if profileid:
# store for possible shared use
profiles[profileid] = baseface
baseobj = FreeCAD.ActiveDocument.addObject("Part::Extrusion",name+"_body")
baseobj.Base = baseface
if addplacement:
# apply delta placement (stored profile)
baseobj.Placement = addplacement
baseobj.Dir = addplacement.Rotation.inverted().multVec(ex[1])
else:
baseobj.Dir = ex[1]
if FreeCAD.GuiUp:
baseface.ViewObject.hide()
if (not baseobj):
baseobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_body")
baseobj.Shape = shape
else:
# this object has no shape (storeys, etc...)
if preferences['DEBUG']: print(" no brep ",end="")
# we now have the shape, we create the final object
if preferences['MERGE_MODE_ARCH'] == 0 and archobj:
# full Arch objects
for freecadtype,ifctypes in typesmap.items():
if ptype not in ifctypes:
continue
if clone:
obj = getattr(Arch,"make"+freecadtype)(name=name)
obj.CloneOf = clone
# calculate the correct distance from the cloned object
if shape:
if shape.Solids:
s1 = shape.Solids[0]
else:
s1 = shape
if clone.Shape.Solids:
s2 = clone.Shape.Solids[0]
else:
s1 = clone.Shape
if hasattr(s1,"CenterOfMass") and hasattr(s2,"CenterOfMass"):
v = s1.CenterOfMass.sub(s2.CenterOfMass)
if product.Representation:
r = importIFCHelper.getRotation(product.Representation.Representations[0].Items[0].MappingTarget)
if not r.isNull():
v = v.add(s2.CenterOfMass)
v = v.add(r.multVec(s2.CenterOfMass.negative()))
obj.Placement.Rotation = r
obj.Placement.move(v)
else:
print("failed to compute placement ",)
else:
# no clone
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
if freecadtype in ["Wall","Structure"] and baseobj and baseobj.isDerivedFrom("Part::Extrusion"):
# remove intermediary extrusion for types that can extrude themselves
# TODO do this check earlier so we do not calculate it, to save time
obj.Base = baseobj.Base
obj.Placement = obj.Placement.multiply(baseobj.Placement)
obj.Height = baseobj.Dir.Length
obj.Normal = FreeCAD.Vector(baseobj.Dir).normalize()
bn = baseobj.Name
FreeCAD.ActiveDocument.removeObject(bn)
if (freecadtype in ["Structure","Wall"]) and not baseobj:
# remove sizes to prevent auto shape creation for types that don't require a base object
obj.Height = 0
obj.Width = 0
obj.Length = 0
if store:
sharedobjects[store] = obj
# set the placement from the storey's elevation property
if ptype == "IfcBuildingStorey":
if product.Elevation:
obj.Placement.Base.z = product.Elevation * ifcscale
break
if not obj:
# we couldn't make an object of a specific type, use default arch component
obj = Arch.makeComponent(baseobj,name=name)
# set additional properties
obj.Label = name
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
if hasattr(obj,"Description") and hasattr(product,"Description"):
if product.Description:
obj.Description = product.Description
if FreeCAD.GuiUp and baseobj:
try:
if hasattr(baseobj,"ViewObject"):
baseobj.ViewObject.hide()
except ReferenceError:
pass
# setting IFC type
try:
if hasattr(obj,"IfcType"):
obj.IfcType = ''.join(map(lambda x: x if x.islower() else " "+x, ptype[3:]))[1:]
except:
print("Unable to give IFC type ",ptype," to object ",obj.Label)
# setting uid
if hasattr(obj,"IfcData"):
a = obj.IfcData
a["IfcUID"] = str(guid)
obj.IfcData = a
# setting IFC attributes
for attribute in ArchIFCSchema.IfcProducts[product.is_a()]["attributes"]:
if attribute["name"] == "Name":
continue
# print("attribute:",attribute["name"])
if hasattr(product, attribute["name"]) and getattr(product, attribute["name"]) and hasattr(obj,attribute["name"]):
# print("Setting attribute",attribute["name"],"to",getattr(product, attribute["name"]))
setattr(obj, attribute["name"], getattr(product, attribute["name"]))
# TODO: ArchIFCSchema.IfcProducts uses the IFC version from the FreeCAD prefs.
# This might not coincide with the file being opened, hence some attributes are not properly read.
if obj:
# print number of solids
if preferences['DEBUG']:
s = ""
if hasattr(obj,"Shape"):
if obj.Shape.Solids:
s = str(len(obj.Shape.Solids))+" solids"
print(s,end="")
objects[pid] = obj
elif (preferences['MERGE_MODE_ARCH'] == 1 and archobj) or (preferences['MERGE_MODE_STRUCT'] == 0 and not archobj):
# non-parametric Arch objects (just Arch components with a shape)
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
for freecadtype,ifctypes in typesmap.items():
if ptype in ifctypes:
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
if ptype == "IfcBuildingStorey":
if product.Elevation:
obj.Placement.Base.z = product.Elevation * ifcscale
elif baseobj:
obj = Arch.makeComponent(baseobj,name=name,delete=True)
obj.Label = name
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
if hasattr(obj,"Description") and hasattr(product,"Description"):
if product.Description:
obj.Description = product.Description
try:
if hasattr(obj,"IfcType"):
obj.IfcType = ''.join(map(lambda x: x if x.islower() else " "+x, ptype[3:]))[1:]
except:
print("Unable to give IFC type ",ptype," to object ",obj.Label)
if hasattr(obj,"IfcData"):
a = obj.IfcData
a["IfcUID"] = str(guid)
obj.IfcData = a
elif pid in additions:
# no baseobj but in additions, thus we make a BuildingPart container
obj = getattr(Arch,"makeBuildingPart")(name=name)
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
else:
if preferences['DEBUG']: print(": skipped.")
continue
if obj and hasattr(obj, "GlobalId"):
obj.GlobalId = guid
elif (preferences['MERGE_MODE_ARCH'] == 2 and archobj) or (preferences['MERGE_MODE_STRUCT'] == 1 and not archobj):
# Part shapes
if ptype in ["IfcSite","IfcBuilding","IfcBuildingStorey"]:
for freecadtype,ifctypes in typesmap.items():
if ptype in ifctypes:
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
if ptype == "IfcBuildingStorey":
if product.Elevation:
obj.Placement.Base.z = product.Elevation * ifcscale
elif baseobj:
obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
obj.Shape = shape
elif pid in additions:
# no baseobj but in additions, thus we make a BuildingPart container
obj = getattr(Arch,"makeBuildingPart")(name=name)
if preferences['DEBUG']: print(": "+obj.Label+" ",end="")
else:
if preferences['DEBUG']: print(": skipped.")
continue
if preferences['DEBUG']: print("") # newline for debug prints, print for a new object should be on a new line
if obj:
obj.Label = name
objects[pid] = obj
# handle properties
if psets:
if preferences['IMPORT_PROPERTIES'] and hasattr(obj,"IfcProperties"):
# treat as spreadsheet (pref option)
if isinstance(obj.IfcProperties,dict):
# fix property type if needed
obj.removeProperty("IfcProperties")
obj.addProperty("App::PropertyLink","IfcProperties","Component","Stores IFC properties as a spreadsheet")
ifc_spreadsheet = Arch.makeIfcSpreadsheet()
n = 2
for c in psets.keys():
o = ifcfile[c]
if preferences['DEBUG']: print("propertyset Name",o.Name,type(o.Name))
catname = o.Name
for p in psets[c]:
l = ifcfile[p]
lname = l.Name
if l.is_a("IfcPropertySingleValue"):
if preferences['DEBUG']:
print("property name",l.Name,type(l.Name))
if six.PY2:
catname = catname.encode("utf8")
lname = lname.encode("utf8")
ifc_spreadsheet.set(str('A'+str(n)), catname)
ifc_spreadsheet.set(str('B'+str(n)), lname)
if l.NominalValue:
if preferences['DEBUG']:
print("property NominalValue",l.NominalValue.is_a(),type(l.NominalValue.is_a()))
print("property NominalValue.wrappedValue",l.NominalValue.wrappedValue,type(l.NominalValue.wrappedValue))
# print("l.NominalValue.Unit",l.NominalValue.Unit,type(l.NominalValue.Unit))
ifc_spreadsheet.set(str('C'+str(n)), l.NominalValue.is_a())
if l.NominalValue.is_a() in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']:
if six.PY2:
ifc_spreadsheet.set(str('D'+str(n)), "'" + str(l.NominalValue.wrappedValue.encode("utf8")))
else:
ifc_spreadsheet.set(str('D'+str(n)), "'" + str(l.NominalValue.wrappedValue))
else:
ifc_spreadsheet.set(str('D'+str(n)), str(l.NominalValue.wrappedValue))
if hasattr(l.NominalValue,'Unit'):
ifc_spreadsheet.set(str('E'+str(n)), str(l.NominalValue.Unit))
n += 1
obj.IfcProperties = ifc_spreadsheet
elif hasattr(obj,"IfcProperties") and isinstance(obj.IfcProperties,dict):
# 0.18 behaviour: properties are saved as pset;;type;;value in IfcProperties
d = obj.IfcProperties
obj.IfcProperties = importIFCHelper.getIfcProperties(ifcfile, pid, psets, d)
elif hasattr(obj,"IfcData"):
# 0.17: properties are saved as type(value) in IfcData
a = obj.IfcData
for c in psets.keys():
for p in psets[c]:
l = ifcfile[p]
if l.is_a("IfcPropertySingleValue"):
a[l.Name.encode("utf8")] = str(l.NominalValue) # no py3 support here
obj.IfcData = a
# color
if (pid in colors) and colors[pid]:
colordict[obj.Name] = colors[pid]
if FreeCAD.GuiUp:
# if preferences['DEBUG']: print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255))
if hasattr(obj.ViewObject,"ShapeColor"):
obj.ViewObject.ShapeColor = tuple(colors[pid][0:3])
if hasattr(obj.ViewObject,"Transparency"):
obj.ViewObject.Transparency = colors[pid][3]
# if preferences['DEBUG'] is on, recompute after each shape
if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute()
# attached 2D elements
if product.Representation:
for r in product.Representation.Representations:
if r.RepresentationIdentifier == "FootPrint":
annotations.append(product)
break
# additional properties for specific types
if product.is_a("IfcSite"):
if product.RefElevation:
obj.Elevation = product.RefElevation * ifcscale
if product.RefLatitude:
obj.Latitude = importIFCHelper.dms2dd(*product.RefLatitude)
if product.RefLongitude:
obj.Longitude = importIFCHelper.dms2dd(*product.RefLongitude)
if product.SiteAddress:
if product.SiteAddress.AddressLines:
obj.Address = product.SiteAddress.AddressLines[0]
if product.SiteAddress.Town:
obj.City = product.SiteAddress.Town
if product.SiteAddress.Region:
obj.Region = product.SiteAddress.Region
if product.SiteAddress.Country:
obj.Country = product.SiteAddress.Country
if product.SiteAddress.PostalCode:
obj.PostalCode = product.SiteAddress.PostalCode
project = product.Decomposes[0].RelatingObject
modelRC = next((rc for rc in project.RepresentationContexts if rc.ContextType == "Model"), None)
if modelRC and modelRC.TrueNorth:
# If the y-part of TrueNorth is 0, then the x-part should be checked.
# Declination would be -90° if x >0 and +90° if x < 0
# Only if x==0 then we can not determine TrueNorth.
# But that would actually be an invalid IFC file, because the magnitude
# of the (twodimensional) direction vector for TrueNorth shall be greater than zero.
(x, y) = modelRC.TrueNorth.DirectionRatios[:2]
obj.Declination = ((math.degrees(math.atan2(y,x))-90+180)%360)-180
if (FreeCAD.GuiUp):
obj.ViewObject.CompassRotation.Value = obj.Declination
try:
progressbar.next(True)
except(RuntimeError):
print("Aborted.")
progressbar.stop()
FreeCAD.ActiveDocument.recompute()
return
progressbar.stop()
FreeCAD.ActiveDocument.recompute()
if preferences['MERGE_MODE_STRUCT'] == 2:
if preferences['DEBUG']: print("Joining Structural shapes...",end="")
for host,children in groups.items(): # Structural
if ifcfile[host].is_a("IfcStructuralAnalysisModel"):
compound = []
for c in children:
if c in structshapes.keys():
compound.append(structshapes[c])
del structshapes[c]
if compound:
name = ifcfile[host].Name or "AnalysisModel"
if preferences['PREFIX_NUMBERS']: name = "ID" + str(host) + " " + name
obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
obj.Label = name
obj.Shape = Part.makeCompound(compound)
if structshapes: # remaining Structural shapes
obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedStruct")
obj.Shape = Part.makeCompound(structshapes.values())
if preferences['DEBUG']: print("done")
else:
if preferences['DEBUG']: print("Processing Struct relationships...",end="")
# groups
for host,children in groups.items():
if ifcfile[host].is_a("IfcStructuralAnalysisModel"):
# print(host, ' --> ', children)
obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","AnalysisModel")
objects[host] = obj
if host in objects.keys():
cobs = []
childs_to_delete = []
for child in children:
if child in objects.keys():
cobs.append(objects[child])
childs_to_delete.append(child)
for c in childs_to_delete:
children.remove(c) # to not process the child again in remaining groups
if cobs:
if preferences['DEBUG']: print("adding ",len(cobs), " object(s) to ", objects[host].Label)
Arch.addComponents(cobs,objects[host])
if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute()
if preferences['DEBUG']: print("done")
if preferences['MERGE_MODE_ARCH'] > 2: # if ArchObj is compound or ArchObj not imported
FreeCAD.ActiveDocument.recompute()
# cleaning bad shapes
for obj in objects.values():
if obj.isDerivedFrom("Part::Feature"):
if obj.Shape.isNull():
Arch.rebuildArchShape(obj)
# processing remaining (normal) groups
swallowed = []
remaining = {}
for host,children in groups.items():
if ifcfile[host].is_a("IfcGroup"):
if ifcfile[host].Name:
grp_name = ifcfile[host].Name
else:
if preferences['DEBUG']: print("no group name specified for entity: #", ifcfile[host].id(), ", entity type is used!")
grp_name = ifcfile[host].is_a() + "_" + str(ifcfile[host].id())
if six.PY2:
grp_name = grp_name.encode("utf8")
grp = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",grp_name)
grp.Label = grp_name
objects[host] = grp
for child in children:
if child in objects.keys():
grp.addObject(objects[child])
swallowed.append(child)
else:
remaining[child] = grp
if preferences['MERGE_MODE_ARCH'] == 3:
# One compound per storey
if preferences['DEBUG']: print("Joining Arch shapes...",end="")
for host,children in additions.items(): # Arch
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 preferences['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 Arch shapes
obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedArch")
obj.Shape = Part.makeCompound(shapes.values())
if preferences['DEBUG']: print("done")
else:
if preferences['DEBUG']: print("Processing Arch relationships...",end="")
first = True
# subtractions
if preferences['SEPARATE_OPENINGS']:
for subtraction in subtractions:
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
if preferences['DEBUG'] and first:
print("")
first = False
if preferences['DEBUG']: print(" subtracting",objects[subtraction[0]].Label, "from", objects[subtraction[1]].Label)
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute()
# additions
for host,children in additions.items():
if host not in objects.keys():
# print(host, 'not used')
# print(ifcfile[host])
continue
cobs = []
for child in children:
if child in objects.keys() \
and child not in swallowed: # don't add objects already in groups
cobs.append(objects[child])
if not cobs:
continue
if preferences['DEBUG'] and first:
print("")
first = False
if preferences['DEBUG'] and (len(cobs) > 10) and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart","Project"])):
# avoid huge fusions
print("more than 10 shapes to add: skipping.")
else:
if preferences['DEBUG']: print(" adding",len(cobs), "object(s) to", objects[host].Label)
Arch.addComponents(cobs,objects[host])
if preferences['DEBUG']: FreeCAD.ActiveDocument.recompute()
if preferences['DEBUG'] and first: print("done.")
FreeCAD.ActiveDocument.recompute()
# cleaning bad shapes
for obj in objects.values():
if obj.isDerivedFrom("Part::Feature"):
if obj.Shape.isNull() and not(Draft.getType(obj) in ["Site","Project"]):
Arch.rebuildArchShape(obj)
FreeCAD.ActiveDocument.recompute()
# 2D elements
if preferences['DEBUG'] and annotations:
print("Processing",len(annotations),"2D objects...")
prodcount = count
count = 0
for annotation in annotations:
anno = None
aid = annotation.id()
count += 1
if preferences['DEBUG']: print(count,"/",len(annotations),"object #"+str(aid),":",annotation.is_a(),end="")
if aid in skip:
continue # user given id skip list
if annotation.is_a() in preferences['SKIP']:
continue # preferences-set type skip list
if annotation.is_a("IfcGrid"):
axes = []
uvwaxes = ()
if annotation.UAxes:
uvwaxes = annotation.UAxes
if annotation.VAxes:
uvwaxes = uvwaxes + annotation.VAxes
if annotation.WAxes:
uvwaxes = uvwaxes + annotation.WAxes
for axis in uvwaxes:
if axis.AxisCurve:
sh = importIFCHelper.get2DShape(axis.AxisCurve,ifcscale)
if sh and (len(sh[0].Vertexes) == 2): # currently only straight axes are supported
sh = sh[0]
l = sh.Length
pl = FreeCAD.Placement()
pl.Base = sh.Vertexes[0].Point
pl.Rotation = FreeCAD.Rotation(FreeCAD.Vector(0,1,0),sh.Vertexes[-1].Point.sub(sh.Vertexes[0].Point))
o = Arch.makeAxis(1,l)
o.Length = l
o.Placement = pl
o.CustomNumber = axis.AxisTag
axes.append(o)
if axes:
name = "Grid"
grid_placement = None
if annotation.Name:
name = annotation.Name
if six.PY2:
name = name.encode("utf8")
if annotation.ObjectPlacement:
# https://forum.freecadweb.org/viewtopic.php?f=39&t=40027
grid_placement = importIFCHelper.getPlacement(
annotation.ObjectPlacement,
scaling=1
)
if preferences['PREFIX_NUMBERS']:
name = "ID" + str(aid) + " " + name
anno = Arch.makeAxisSystem(axes,name)
if grid_placement:
anno.Placement = grid_placement
print(" axis")
else:
name = "Annotation"
if annotation.Name:
name = annotation.Name
if six.PY2:
name = name.encode("utf8")
if "annotation" not in name.lower():
name = "Annotation " + name
if preferences['PREFIX_NUMBERS']: name = "ID" + str(aid) + " " + name
shapes2d = []
for rep in annotation.Representation.Representations:
if rep.RepresentationIdentifier in ["Annotation","FootPrint","Axis"]:
sh = importIFCHelper.get2DShape(rep,ifcscale)
if sh in FreeCAD.ActiveDocument.Objects:
# dirty hack: get2DShape might return an object directly if non-shape based (texts for ex)
anno = sh
else:
shapes2d.extend(sh)
if shapes2d:
sh = Part.makeCompound(shapes2d)
if preferences['DEBUG']: print(" shape")
anno = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
anno.Shape = sh
p = importIFCHelper.getPlacement(annotation.ObjectPlacement,ifcscale)
if p: # and annotation.is_a("IfcAnnotation"):
anno.Placement = p
else:
if preferences['DEBUG']: print(" no shape")
# placing in container if needed
if anno:
if aid in remaining.keys():
remaining[aid].addObject(anno)
else:
for host,children in additions.items():
if (aid in children) and (host in objects.keys()):
Arch.addComponents(anno,objects[host])
FreeCAD.ActiveDocument.recompute()
# Materials
if preferences['DEBUG'] and materials: print("Creating materials...",end="")
# print("mattable:",mattable)
# print("materials:",materials)
fcmats = {}
for material in materials:
# get and set material name
name = "Material"
if material.Name:
name = material.Name
if six.PY2:
name = name.encode("utf8")
# get material color
mdict = {}
if material.id() in colors:
mdict["DiffuseColor"] = str(colors[material.id()])
else:
for o,m in mattable.items():
if m == material.id():
if o in colors:
mdict["DiffuseColor"] = str(colors[o])
# merge materials with same name and color if setting in prefs is True
add_material = True
if preferences['MERGE_MATERIALS']:
for key in list(fcmats.keys()):
if key.startswith(name) \
and "DiffuseColor" in mdict and "DiffuseColor" in fcmats[key].Material \
and mdict["DiffuseColor"] == fcmats[key].Material["DiffuseColor"]:
mat = fcmats[key]
add_material = False
# add a new material object
if add_material is True:
mat = Arch.makeMaterial(name=name)
if mdict:
mat.Material = mdict
fcmats[mat.Name] = mat
# fill material attribute of the objects
for o,m in mattable.items():
if m == material.id():
if o in objects:
if hasattr(objects[o],"Material"):
objects[o].Material = mat
if FreeCAD.GuiUp:
# the reason behind ...
# there are files around in which the material color is different from the shape color
# all viewers use the shape color whereas in FreeCAD the shape color will be
# overwritten by the material color (if there is a material with a color).
# In such a case FreeCAD shows a different color than all common ifc viewers
# https://forum.freecadweb.org/viewtopic.php?f=39&t=38440
col = objects[o].ViewObject.ShapeColor[:3]
dig = 5
ma_color = sh_color = round(col[0], dig), round(col[1], dig), round(col[2], dig)
if "DiffuseColor" in objects[o].Material.Material:
sting_col_ = objects[o].Material.Material["DiffuseColor"]
col = tuple([float(f) for f in sting_col_.strip("()").split(",")])
ma_color = round(col[0], dig), round(col[1], dig), round(col[2], dig)
if ma_color != sh_color:
print("\nobject color != material color for object: ", o)
print(" material color is used (most software uses shape color)")
print(" obj: ", o, "label: ", objects[o].Label, " col: ", sh_color)
print(" mat: ", m, "label: ", mat.Label, " col: ", ma_color)
# print(" ", ifcfile[o])
# print(" ", ifcfile[m])
# print(" colors:")
# print(" ", o, ": ", colors[o])
# print(" ", m, ": ", colors[m])
if preferences['DEBUG'] and materials: print("done")
# Layers
if preferences['DEBUG'] and layers: print("Creating layers...", end="")
# print(layers)
for layer_name, layer_objects in layers.items():
lay = Draft.makeLayer(layer_name)
lay_grp = []
for lobj_id in layer_objects:
if lobj_id in objects:
lay_grp.append(objects[lobj_id])
lay.Group = lay_grp
FreeCAD.ActiveDocument.recompute()
if preferences['DEBUG'] and layers: print("done")
# restore links from full parametric definitions
for p in parametrics:
l = FreeCAD.ActiveDocument.getObject(p[2])
if l:
setattr(p[0],p[1],l)
# Grouping everything if required
if preferences['REPLACE_PROJECT']:
rootgroup = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup","Group")
rootgroup.Label = os.path.basename(filename)
for key,obj in objects.items():
# only add top-level objects
if not obj.InList:
rootgroup.addObject(obj)
# Save colordict in non-GUI mode
if colordict and not FreeCAD.GuiUp:
import json
d = doc.Meta
d["colordict"] = json.dumps(colordict)
doc.Meta = d
FreeCAD.ActiveDocument.recompute()
if ZOOMOUT and FreeCAD.GuiUp:
import FreeCADGui
FreeCADGui.SendMsgToActiveView("ViewFit")
print("Finished importing.")
return doc
# ************************************************************************************************
# ********** helper ****************
def createFromProperties(propsets,ifcfile):
"creates a FreeCAD parametric object from a set of properties"
obj = None
sets = []
global parametrics
appset = None
guiset = None
for pset in propsets.keys():
if ifcfile[pset].Name == "FreeCADPropertySet":
appset = {}
for pid in propsets[pset]:
p = ifcfile[pid]
appset[p.Name] = p.NominalValue.wrappedValue
elif ifcfile[pset].Name == "FreeCADGuiPropertySet":
guiset = {}
for pid in propsets[pset]:
p = ifcfile[pid]
guiset[p.Name] = p.NominalValue.wrappedValue
if appset:
oname = None
otype = None
if "FreeCADType" in appset.keys():
if "FreeCADName" in appset.keys():
obj = FreeCAD.ActiveDocument.addObject(appset["FreeCADType"],appset["FreeCADName"])
if "FreeCADAppObject" in appset:
mod,cla = appset["FreeCADAppObject"].split(".")
if "'" in mod:
mod = mod.split("'")[-1]
if "'" in cla:
cla = cla.split("'")[0]
import importlib
mod = importlib.import_module(mod)
getattr(mod,cla)(obj)
sets.append(("App",appset))
if FreeCAD.GuiUp:
if guiset:
if "FreeCADGuiObject" in guiset:
mod,cla = guiset["FreeCADGuiObject"].split(".")
if "'" in mod:
mod = mod.split("'")[-1]
if "'" in cla:
cla = cla.split("'")[0]
import importlib
mod = importlib.import_module(mod)
getattr(mod,cla)(obj.ViewObject)
sets.append(("Gui",guiset))
if obj and sets:
for realm,pset in sets:
if realm == "App":
target = obj
else:
target = obj.ViewObject
for key,val in pset.items():
if key.startswith("FreeCAD_") or key.startswith("FreeCADGui_"):
name = key.split("_")[1]
if name in target.PropertiesList:
if not target.getEditorMode(name):
ptype = target.getTypeIdOfProperty(name)
if ptype in ["App::PropertyString","App::PropertyEnumeration","App::PropertyInteger","App::PropertyFloat"]:
setattr(target,name,val)
elif ptype in ["App::PropertyLength","App::PropertyDistance"]:
setattr(target,name,val*1000)
elif ptype == "App::PropertyBool":
if val in [".T.",True]:
setattr(target,name,True)
else:
setattr(target,name,False)
elif ptype == "App::PropertyVector":
setattr(target,name,FreeCAD.Vector([float(s) for s in val.split("(")[1].strip(")").split(",")]))
elif ptype == "App::PropertyArea":
setattr(target,name,val*1000000)
elif ptype == "App::PropertyPlacement":
data = val.split("[")[1].strip("]").split("(")
data = [data[1].split(")")[0],data[2].strip(")")]
v = FreeCAD.Vector([float(s) for s in data[0].split(",")])
r = FreeCAD.Rotation(*[float(s) for s in data[1].split(",")])
setattr(target,name,FreeCAD.Placement(v,r))
elif ptype == "App::PropertyLink":
link = val.split("_")[1]
parametrics.append([target,name,link])
else:
print("Unhandled FreeCAD property:",name," of type:",ptype)
return obj
def applyColorDict(doc,colordict=None):
"""applies the contents of a color dict to the objects in the given doc.
If no colordict is given, the doc Meta property is searched for a "colordict" entry."""
if not colordict:
if "colordict" in doc.Meta:
import json
colordict = json.loads(doc.Meta["colordict"])
if colordict:
for obj in doc.Objects:
if obj.Name in colordict:
color = colordict[obj.Name]
if hasattr(obj.ViewObject,"ShapeColor"):
obj.ViewObject.ShapeColor = tuple(color[0:3])
if hasattr(obj.ViewObject,"Transparency") and (len(color) >= 4):
obj.ViewObject.Transparency = color[3]
else:
print("No valid color dict to apply")