[IfcOpenShell-Python] How do I change the color from an ifc entity?

edited August 2022 in General

Hi all
so here i have a simple cube in an ifc file.

When i open it in an ifc-viewer, the cube is gray like in the image above in the spoiler tag.
So how can i change the color from this cube with ifcopenshell-python?
Thanks for the reply :)

Comments

  • AceAce
    edited August 2022

    Hey @Martin156131, I don't know how to do it with just IfcOpenShell, but with Blenderbim that would be about changing the IfcStyle:
    Here's a thread with the same request:
    https://community.osarch.org/discussion/comment/8780/#Comment_8780

    You change the colour in the viewport display tab and then click update IFCstyle to save it

  • Here is a red ifc cube for example:

  • edited August 2022

    Thanks for the feedback!
    So it is possible to change the color, however I search the method how to do it in python.
    Because i am trying to write a script to make from different obj files one ifc file, so it could be nice when the different ifc entity have a different color, instead having all uniformly gray shading.

  • AceAce
    edited August 2022

    If I look at your cube Ifc written out vs the red one you need to include these elements:

    #79=IFCSURFACESTYLE('Red',.BOTH.,(#80));
    #80=IFCSURFACESTYLERENDERING(#81,0.,#82,$,$,$,IFCNORMALISEDRATIOMEASURE(0.),IFCSPECULARROUGHNESS(0.4),.NOTDEFINED.);
    #81=IFCCOLOURRGB($,0.800000071525574,0.0212754905223846,0.0212754905223846);
    #82=IFCCOLOURRGB($,0.800000011920929,0.800000011920929,0.800000011920929);
    #83=IFCSTYLEDITEM(#73,(#79),'Material');
    #84=IFCMATERIAL('Red',$,$);
    #85=IFCSTYLEDITEM($,(#79),'Material');
    #86=IFCSTYLEDREPRESENTATION(#15,'Body',$,(#85));
    #87=IFCMATERIALDEFINITIONREPRESENTATION($,$,(#86),#84);
    

    I don't code so my usefulness ends here

  • edited August 2022

    @Martin156131

    So it is possible to change the color, however I search the method how to do it in python. Because i am trying to write a script to make from different obj files one ifc file, so it could be nice when the different ifc entity have a different color,

    You could try this code snippet, I copied it from @brunopostle

            mymaterial = ifcfile.createIfcMaterial("wall material")
            material_layer = ifcfile.createIfcMaterialLayer(mymaterial, 0.2, None)
            material_layer_set = ifcfile.createIfcMaterialLayerSet([material_layer], None)
            material_layer_set_usage = ifcfile.createIfcMaterialLayerSetUsage(material_layer_set, "AXIS2", "POSITIVE", -0.1)
    
    
            style = ifcopenshell.api.run("style.add_style", ifcfile, name="brick")
            ifcopenshell.api.run(
                "style.add_surface_style",
                ifcfile,
                style=style,
                attributes={
                    "SurfaceColour": {
                        "Name": None,
                        "Red": 2.2,
                        "Green": 0.8,
                        "Blue": 0.5,
                    },
                    "DiffuseColour": {
                        "Name": None,
                        "Red": 2.2,
                        "Green": 0.8,
                        "Blue": 0.5,
                    },
                    "Transparency": 0.0,
                    "ReflectanceMethod": "PLASTIC",
                },
            )
            ifcopenshell.api.run(
                "style.assign_material_style",
                ifcfile,
                material=mymaterial,
                style=style,
                context=context,
            )
    
          ifcfile.createIfcRelAssociatesMaterial(create_guid(), owner_history, RelatedObjects=[ifc_wall], RelatingMaterial=material_layer_set_usage)
    

    cvillagrasatheoryshawMartin156131Arv
  • There are two ways for an object to receive a colour: 1) its geometry could directly have a style, or 2) it can be associated with a material, and that material can have a style. @Coen's code snippet shows the latter. The former, however, is more appropriate for your usecase as you don't have any materials it seems.

    # Create a blank style
    style = ifcopenshell.api.run("style.add_style", ifc, name="My style")
    # Give that style a surface shading colour
    ifcopenshell.api.run("style.add_surface_style", ifc, style=style, ifc_class="IfcSurfaceStyleShading", attributes={
                "SurfaceColour": { "Name": None, "Red": 1., "Green": 0., "Blue": 0. }
            })
    # Assign the style to your geometry's representation (IfcShapeRepresentation)
    ifcopenshell.api.run("style.assign_representation_styles", ifc, shape_representation=representation, styles=[style])
    
    Martin156131CoenberndArv
  • Thank you all for your awesome help!

    The jupyternotebook code, that i used:

    My yellow cube ?:

    MoultAceCoenbrunopostle
  • @Coen Thanks for the snippet ! Just as a heads up AFAIK unless you're working with HDR images you should not set the color channels with values > 1.0, the result may not be physically correct or may be clamped by the software you'll open it in. For instance Blender technically supports RGB channels with values > 1 (you have to type it in the fields since the slider doesn't let you) but I'm not sure how it will be handled by other softs. Cheers :)

    CoenArv
  • I am a bit confused why this happens, I've made an IfcWallType instance and added to a project with this script:

    import ifcopenshell
    from ifcopenshell.api import run
    import numpy
    import bpy
    
    ifc_file = ifcopenshell.file()
    project = run("root.create_entity", ifc_file, ifc_class="IfcProject", name="Demo")
    
    run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    context = run("context.add_context", ifc_file, context_type="Model")
    body = run("context.add_context", ifc_file,context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context)
    
    # Create a site, building, and storey. Many hierarchies are possible.
    site = run("root.create_entity", ifc_file, ifc_class="IfcSite", name="My Site")
    building = run("root.create_entity", ifc_file, ifc_class="IfcBuilding", name="Building A")
    storey = run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey", name="Ground Floor")
    
    # Since the site is our top level location, assign it to the project
    # Then place our building on the site, and our storey in the building
    run("aggregate.assign_object", ifc_file, relating_object=project, product=site)
    run("aggregate.assign_object", ifc_file, relating_object=site, product=building)
    run("aggregate.assign_object", ifc_file, relating_object=building, product=storey)
    
    
    def read_from_csv():
        print ('read from csv')
    
    def create_wall():
    
        ifc_material = run("material.add_material", ifc_file, name="brick")
        ifc_walltype = run("root.create_entity", ifc_file, ifc_class="IfcWallType", name="wall_demo")
        relating_material = run("material.assign_material", ifc_file, product=ifc_walltype, type="IfcMaterialLayerSet")
        layer_set = relating_material.RelatingMaterial
        layer = run("material.add_layer", ifc_file, layer_set=layer_set, material=ifc_material)
        layer.LayerThickness = 0.2
    
        print ('hier', ifc_walltype)
    
    
        ifc_walltype_instance = run("root.create_entity", ifc_file, ifc_class="IfcWall", relating_type=ifc_walltype, name='walltype_instance')
    
        representation = run("geometry.add_wall_representation",ifc_file,context=body,length=5,height=3,thickness=layer.LayerThickness)
    
        matrix_1 = numpy.array(
                (
                    (1.0, 0.0, 0.0, 0.0),
                    (0.0, 1.0, 0.0, 0.0),
                    (0.0, 0.0, 1.0, 0.0),
                    (0.0, 0.0, 0.0, 1.0),
                )
            )
    
        run("type.assign_type", ifc_file, related_object=ifc_walltype_instance, relating_type=ifc_walltype)   
        run("geometry.edit_object_placement",ifc_file,product=ifc_walltype_instance ,matrix=matrix_1)
        run("spatial.assign_container", ifc_file, relating_structure=storey, product=ifc_walltype_instance)
        run("geometry.assign_representation", ifc_file, product=ifc_walltype, representation=representation)
    
    
    
        style = ifcopenshell.api.run("style.add_style", ifc_file, name="My style")
    
        run("style.add_surface_style", ifc_file, style=style, ifc_class="IfcSurfaceStyleShading", attributes={
                    "SurfaceColour": { "Name": None, "Red": 1., "Green": 0., "Blue": 0. }
               })
        run("style.assign_representation_styles", ifc_file, shape_representation=representation, styles=[style])
    
    
    
    def create_beam():
        #create material
        #create ifcmaterialprofileset
        #assign material to profileset
        #create type
        #create occurence
        #place occurence
        #representation = run("geometry.add_profile_representation", ifc_file, context=representations["body"], profile=profile, depth=5)
        print ('create beam')
    
    
    create_wall()
    
    file_name="demo_library"
    folder_path = "C:\\Algemeen\\00_prive\\BlenderScripts\\BlenderBIM_create_objects\\ifc_library\\" 
    filename = str(file_name) + ".ifc"
    file_path = (folder_path + filename)
    ifc_file.write(file_path)
    
    def load_ifc_automatically():
    
        if (bool(ifc_file)) == True:
            project = ifc_file.by_type('IfcProject')
    
            if project is not None:
                for i in project:
                    collection_name = 'IfcProject/' + i.Name
    
                collection = bpy.data.collections.get(str(collection_name))
    
                if collection is not None:
                    for obj in collection.objects:
                        bpy.data.objects.remove(obj, do_unlink=True)
    
                    bpy.data.collections.remove(collection)
    
            #for material in bpy.data.materials:
            #    material.user_clear()
            #    bpy.data.materials.remove(material)
    
            bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)         
            bpy.ops.bim.load_project(filepath=file_path)
    
    load_ifc_automatically()
    

    Creates a beautiful red wall


    Now when I open the BIM tool in BlenderBIM, and want to add another IfcWallType instance, this happens:

    When trying to save the project Blender crashes, when I remove the colours I just get a grey colour and BlenderBIM works
    When I open the project in BIM Vision I see this:

    My aim is to create a library of different coloured WallType instances.
    This is the line where BIMVision claims there is an error

    #107=IFCCARTESIANTRANSFORMATIONOPERATOR3D(#104,#105,#103,1.,#106);
    #108=IFCMAPPEDITEM(#95,#107);
    #109=IFCSHAPEREPRESENTATION(#11,'Body','MappedRepresentation',(#108));
    
  • Some further experimenting, when I use this for assigning a colour:

    #brunopostle gorgious snippet
       style = run("style.add_style", ifc_file, name=ifc_material.Name)
       run("style.add_surface_style",ifc_file,style=style,attributes={"SurfaceColour": {"Name": ifc_material.Name,"Red": 0.8,"Green": 0.5,"Blue": 0.2,},"Transparency": 0,"ReflectanceMethod": "PLASTIC",})
        run("style.assign_material_style",ifc_file,material=ifc_material,style=style,context=context)
    

    Instead of this like my previous post:

        #moult snippet
        style = ifcopenshell.api.run("style.add_style", ifc_file, name="My style")
        run("style.add_surface_style", ifc_file, style=style, ifc_class="IfcSurfaceStyleShading", attributes={"SurfaceColour": { "Name": None, "Red": 1., "Green": 0., "Blue": 0. }})
        run("style.assign_representation_styles", ifc_file, shape_representation=representation, styles=[style])
    

    It creates also a colour, and when I try to add an IfcWallType instance it preserves the colour:

    However I am unable to open it in BIMVision, BIM Vision gives this log:

    #67 0YP$_BwmD0kQ48ohs7NscA  Wall    wall_demo       
    
  • When I try to save the IFC project when adding another wall I get this weird error when I ran Blender form the command prompt:

    fake_module: addon missing 'bl_info' gives bad performance!: 'C:\\Users\\cclaus\\AppData\\Roaming\\Blender Foundation\\Blender\\3.3\\scripts\\addons\\Z-Anatomy\\__init__.py'
    Error   : EXCEPTION_ACCESS_VIOLATION
    Address : 0x00007FFF744712B9
    Module  : _ifcopenshell_wrapper.pyd
    Thread  : 00003678
    Writing: C:\Users\cclaus\AppData\Local\Temp\blender.crash.txt
    
  • Don't understand why the IfcWallType instance suddenly is transparant and why it crashes :(

  • Even when I remove all the style lines and the load_ifc_automaticaly method, then manullay loading it in Blender. Like so:

    import ifcopenshell
    from ifcopenshell.api import run
    import numpy
    import bpy
    
    ifc_file = ifcopenshell.file()
    project = run("root.create_entity", ifc_file, ifc_class="IfcProject", name="Demo")
    
    run("unit.assign_unit", ifc_file, length={"is_metric": True, "raw": "METERS"})
    context = run("context.add_context", ifc_file, context_type="Model")
    body = run("context.add_context", ifc_file,context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context)
    
    # Create a site, building, and storey. Many hierarchies are possible.
    site = run("root.create_entity", ifc_file, ifc_class="IfcSite", name="My Site")
    building = run("root.create_entity", ifc_file, ifc_class="IfcBuilding", name="Building A")
    storey = run("root.create_entity", ifc_file, ifc_class="IfcBuildingStorey", name="Ground Floor")
    
    # Since the site is our top level location, assign it to the project
    # Then place our building on the site, and our storey in the building
    run("aggregate.assign_object", ifc_file, relating_object=project, product=site)
    run("aggregate.assign_object", ifc_file, relating_object=site, product=building)
    run("aggregate.assign_object", ifc_file, relating_object=building, product=storey)
    
    
    def read_from_csv():
        print ('read from csv')
    
    def create_wall():
    
        ifc_material = run("material.add_material", ifc_file, name="brick")
        ifc_walltype = run("root.create_entity", ifc_file, ifc_class="IfcWallType", name="wall_demo")
        relating_material = run("material.assign_material", ifc_file, product=ifc_walltype, type="IfcMaterialLayerSet")
        layer_set = relating_material.RelatingMaterial
        layer = run("material.add_layer", ifc_file, layer_set=layer_set, material=ifc_material)
        layer.LayerThickness = 0.2
    
        print ('hier', ifc_walltype)
    
    
        ifc_walltype_instance = run("root.create_entity", ifc_file, ifc_class="IfcWall", relating_type=ifc_walltype, name='wall_demo')
    
        representation = run("geometry.add_wall_representation",ifc_file,context=body,length=5,height=3,thickness=layer.LayerThickness)
    
        matrix_1 = numpy.array(
                (
                    (1.0, 0.0, 0.0, 0.0),
                    (0.0, 1.0, 0.0, 0.0),
                    (0.0, 0.0, 1.0, 0.0),
                    (0.0, 0.0, 0.0, 1.0),
                )
            )
    
        #moult snippet
        #style = ifcopenshell.api.run("style.add_style", ifc_file, name="My style")
        #run("style.add_surface_style", ifc_file, style=style, ifc_class="IfcSurfaceStyleShading", attributes={"SurfaceColour": { "Name": 'red', "Red": 1., "Green": 0., "Blue": 0. }})
        #run("style.assign_representation_styles", ifc_file, shape_representation=representation, styles=[style])
    
        #brunopostle gorgious snippet
        #style = run("style.add_style", ifc_file, name=ifc_material.Name)
        #run("style.add_surface_style",ifc_file,style=style,attributes={"SurfaceColour": {"Name": ifc_material.Name,"Red": 0.8,"Green": 0.5,"Blue": 0.2,},"Transparency": 0,"ReflectanceMethod": "PLASTIC",})
        #run("style.assign_material_style",ifc_file,material=ifc_material,style=style,context=context)
    
    
    
        run("type.assign_type", ifc_file, related_object=ifc_walltype_instance, relating_type=ifc_walltype)   
        run("geometry.edit_object_placement",ifc_file,product=ifc_walltype_instance ,matrix=matrix_1)
        run("spatial.assign_container", ifc_file, relating_structure=storey, product=ifc_walltype_instance)
        run("geometry.assign_representation", ifc_file, product=ifc_walltype, representation=representation)
        create_wall()
    

    Then, I close Blender, open it in BIMVision:

    Opening it in BlenderBIM

    Then trying to add a wall, this works, when when trying to Miter join with Shift+Y or Shift+T
    I get this crash

    Error: Python: Traceback (most recent call last):
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\workspace.py", line 394, in invoke
        return self.execute(context)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\tool\ifc.py", line 105, in execute
        IfcStore.execute_ifc_operator(self, context)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\ifc.py", line 418, in execute_ifc_operator
        result = getattr(operator, "_execute")(context)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\workspace.py", line 385, in _execute
        getattr(self, f"hotkey_{self.hotkey}")()
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\workspace.py", line 500, in hotkey_S_Y
        bpy.ops.bim.join_wall(join_type="V")
      File "C:\Program Files\Blender Foundation\Blender 3.3\3.3\scripts\modules\bpy\ops.py", line 113, in __call__
        ret = _op_call(self.idname_py(), None, kw)
    RuntimeError: Error: Python: Traceback (most recent call last):
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\tool\ifc.py", line 105, in execute
        IfcStore.execute_ifc_operator(self, context)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\ifc.py", line 418, in execute_ifc_operator
        result = getattr(operator, "_execute")(context)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\wall.py", line 106, in _execute
        joiner.join_V([o for o in selected_objs if o != context.active_object][0], context.active_object)
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\wall.py", line 1072, in join_V
        self.recreate_wall(element1, wall1, axis1["reference"], axis1["reference"])
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\bim\module\model\wall.py", line 1168, in recreate_wall
        if tool.Ifc.is_moved(obj):
      File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.3\scripts\addons\blenderbim\tool\ifc.py", line 59, in is_moved
        if not obj.BIMObjectProperties.location_checksum:
    ReferenceError: StructRNA of type Object has been removed
    Location: C:\Program Files\Blender Foundation\Blender 3.3\3.3\scripts\modules\bpy\ops.py:113
    
    Ace
  • I can replicate your problem.
    That's all i got. ;)

  • @theoryshaw said:
    I can replicate your problem.
    That's all i got. ;)

    At least it's excluded it's not some funny add-on on my end which is causing this :-)

  • When I use python to create an IfcProjectLibrary instead of IfProject and load them into BlenderBIM, everything works fine. Just wondering how I would use python to load IFC files from the IfcProjectLibrary automatically.

    Opus
Sign In or Register to comment.