Generate functions for all API usecases for better static code features. See #2693.

This commit is contained in:
Dion Moult
2024-05-06 14:35:39 +10:00
parent 10f894e2ea
commit d11ec67129
330 changed files with 13283 additions and 13751 deletions
@@ -15,3 +15,16 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_style import add_style
from .add_surface_style import add_surface_style
from .add_surface_textures import add_surface_textures
from .assign_material_style import assign_material_style
from .assign_representation_styles import assign_representation_styles
from .edit_presentation_style import edit_presentation_style
from .edit_surface_style import edit_surface_style
from .remove_style import remove_style
from .remove_styled_representation import remove_styled_representation
from .remove_surface_style import remove_surface_style
from .unassign_material_style import unassign_material_style
from .unassign_representation_styles import unassign_representation_styles
@@ -17,48 +17,45 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, name=None, ifc_class="IfcSurfaceStyle"):
"""Add a new presentation style
def add_style(file, name=None, ifc_class="IfcSurfaceStyle") -> None:
"""Add a new presentation style
A presentation style is a container of visual settings (called
presentation items) that affect the appearance of objects. There are
four types of style:
A presentation style is a container of visual settings (called
presentation items) that affect the appearance of objects. There are
four types of style:
- Surface styles, which give 3D objects (which have surfaces / faces)
their colours and textures. This is the most common type of style.
- Curve styles, which give 2D and 3D curves, lines, polylines, their
stroke thickness and colour.
- Fill area styles, which gives 2D polygons and flat 3D planes their
colours, hatch patterns, tiled patterns, and pattern scales.
- Text styles, which gives text their font family, weight, variant,
size, indentation, alignment, decoration, spacing, and transformation.
- Surface styles, which give 3D objects (which have surfaces / faces)
their colours and textures. This is the most common type of style.
- Curve styles, which give 2D and 3D curves, lines, polylines, their
stroke thickness and colour.
- Fill area styles, which gives 2D polygons and flat 3D planes their
colours, hatch patterns, tiled patterns, and pattern scales.
- Text styles, which gives text their font family, weight, variant,
size, indentation, alignment, decoration, spacing, and transformation.
Once you have created a presentation style object, you can further
define the properties of your style using other API functions by adding
presentation items, such as ifcopenshell.api.style.add_surface_style.
Once you have created a presentation style object, you can further
define the properties of your style using other API functions by adding
presentation items, such as ifcopenshell.api.style.add_surface_style.
:param name: The name of the style. Used to easily identify it using a
style library.
:type name: str,optional
:param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle,
IfcFillAreaStyle, or IfcTextStyle.
:type ifc_class: str
:return: The newly created style element, based on the provided
ifc_class.
:rtype: ifcopenshell.entity_instance
:param name: The name of the style. Used to easily identify it using a
style library.
:type name: str,optional
:param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle,
IfcFillAreaStyle, or IfcTextStyle.
:type ifc_class: str
:return: The newly created style element, based on the provided
ifc_class.
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
"""
self.file = file
self.settings = {"name": name, "ifc_class": ifc_class}
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
"""
settings = {"name": name, "ifc_class": ifc_class}
def execute(self):
if self.settings["ifc_class"] == "IfcSurfaceStyle":
# Name is filled out because Revit treats this incorrectly as the material name
return self.file.createIfcSurfaceStyle(self.settings["name"], "BOTH")
if settings["ifc_class"] == "IfcSurfaceStyle":
# Name is filled out because Revit treats this incorrectly as the material name
return file.createIfcSurfaceStyle(settings["name"], "BOTH")
@@ -20,117 +20,112 @@ import ifcopenshell
import ifcopenshell.api
class Usecase:
def __init__(self, file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None):
"""Adds a new presentation item to a surface style
def add_surface_style(file, style=None, ifc_class="IfcSurfaceStyleShading", attributes=None) -> None:
"""Adds a new presentation item to a surface style
A surface style can have multiple different types of presentation items
assigned to it:
A surface style can have multiple different types of presentation items
assigned to it:
- Shading, this is the simplest item, which defines a single basic
colour and transparency that can be used to display the object on a
screen. It is an indicative colour of what the object would be in real
life. It is commonly incorrectly abused to colour code systems for MEP
equipment or object types for structural steel. If you just want to
give something a colour, this is what you need.
- Rendering, this is an advanced extension of shading, which includes
the definition of a shader for a rendering engine. You may select the
reflectance / lighting model such as PHYSICAL, for PBR style
rendering, or FLAT, for flat shading, or PHONG for older biased
rendering workflows. Based on the chosen lighting model, you may then
specify the appropriate colour maps, such as diffuse colours,
specularity, emissive component, etc. These lighting models are fully
compatible with glTF and X3D. This should be used if your model is
prepared to be rendered by a rendering engine which is compatible with
glTF / X3D shader descriptions. If you are doing archviz or 3D
rendering, this is what you need.
- Textures, this is a special type of Rendering presentation item that
uses image textures instead of single colours. Textures may be either
mapped using a bounding box stretch mapping, or with UV coordinates
for mesh-like geometry.
- Lighting, this is used to define photometrically accurate colour
parameters used in lighting simulation. If you are a simulationist,
this is what you need.
- Reflectance, this is a special type of Lighting presentation item
which includes some lesser used photometric properties, typically
required for advanced materials like glazing.
- External, this is for any other surface style defined using an
external URI. This is relevant if you are using a third-party non-glTF
compatible shader definition such as for Cycles, Renderman, V-Ray,
etc, or a complex lighting simulation definition, such as for
Radiance.
- Shading, this is the simplest item, which defines a single basic
colour and transparency that can be used to display the object on a
screen. It is an indicative colour of what the object would be in real
life. It is commonly incorrectly abused to colour code systems for MEP
equipment or object types for structural steel. If you just want to
give something a colour, this is what you need.
- Rendering, this is an advanced extension of shading, which includes
the definition of a shader for a rendering engine. You may select the
reflectance / lighting model such as PHYSICAL, for PBR style
rendering, or FLAT, for flat shading, or PHONG for older biased
rendering workflows. Based on the chosen lighting model, you may then
specify the appropriate colour maps, such as diffuse colours,
specularity, emissive component, etc. These lighting models are fully
compatible with glTF and X3D. This should be used if your model is
prepared to be rendered by a rendering engine which is compatible with
glTF / X3D shader descriptions. If you are doing archviz or 3D
rendering, this is what you need.
- Textures, this is a special type of Rendering presentation item that
uses image textures instead of single colours. Textures may be either
mapped using a bounding box stretch mapping, or with UV coordinates
for mesh-like geometry.
- Lighting, this is used to define photometrically accurate colour
parameters used in lighting simulation. If you are a simulationist,
this is what you need.
- Reflectance, this is a special type of Lighting presentation item
which includes some lesser used photometric properties, typically
required for advanced materials like glazing.
- External, this is for any other surface style defined using an
external URI. This is relevant if you are using a third-party non-glTF
compatible shader definition such as for Cycles, Renderman, V-Ray,
etc, or a complex lighting simulation definition, such as for
Radiance.
Shading is sufficient for the majority of basic models.
Shading is sufficient for the majority of basic models.
The attributes you specify will depend on the type of presentation item
you are adding. An example is shown below, but for full details please
refer to the IFC documentation.
The attributes you specify will depend on the type of presentation item
you are adding. An example is shown below, but for full details please
refer to the IFC documentation.
:param style: The IfcSurfaceStyle you want to add to presentation item
to. See ifcopenshell.api.style.add_style.
:type style: ifcopenshell.entity_instance
:param ifc_class: Choose from IfcSurfaceStyleShading,
IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
IfcExternallyDefinedSurfaceStyle.
:type ifc_class: str
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: The newly created presentation item based on the provided
ifc_class.
:rtype: ifcopenshell.entity_instance
:param style: The IfcSurfaceStyle you want to add to presentation item
to. See ifcopenshell.api.style.add_style.
:type style: ifcopenshell.entity_instance
:param ifc_class: Choose from IfcSurfaceStyleShading,
IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
IfcExternallyDefinedSurfaceStyle.
:type ifc_class: str
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: The newly created presentation item based on the provided
ifc_class.
:rtype: ifcopenshell.entity_instance
Example:
Example:
.. code:: python
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Create a simple shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Alternatively, create a rendering style.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleRendering", attributes={
# A surface colour and transparency is still supplied for
# viewport display only. This will supersede the shading
# presentation item.
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
# Alternatively, create a rendering style.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleRendering", attributes={
# A surface colour and transparency is still supplied for
# viewport display only. This will supersede the shading
# presentation item.
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
# NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
# model. In IFC4X3, you may choose PHYSICAL directly.
"ReflectanceMethod": "NOTDEFINED",
# NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
# model. In IFC4X3, you may choose PHYSICAL directly.
"ReflectanceMethod": "NOTDEFINED",
# For PBR shading, you may specify these parameters:
"DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
"SpecularColour": 0.1, # Metallic factor
"SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
})
"""
self.file = file
self.settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}}
# For PBR shading, you may specify these parameters:
"DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
"SpecularColour": 0.1, # Metallic factor
"SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
})
"""
settings = {"style": style, "ifc_class": ifc_class, "attributes": attributes or {}}
def execute(self):
style_item = self.file.create_entity(self.settings["ifc_class"])
ifcopenshell.api.run(
"style.edit_surface_style", self.file, style=style_item, attributes=self.settings["attributes"]
)
styles = list(self.settings["style"].Styles or [])
style_item = file.create_entity(settings["ifc_class"])
ifcopenshell.api.run("style.edit_surface_style", file, style=style_item, attributes=settings["attributes"])
styles = list(settings["style"].Styles or [])
select_class = self.settings["ifc_class"]
if select_class == "IfcSurfaceStyleRendering":
select_class = "IfcSurfaceStyleShading"
duplicate_items = [s for s in styles if s.is_a(select_class)]
for duplicate_item in duplicate_items:
ifcopenshell.api.run("style.remove_surface_style", self.file, style=duplicate_item)
select_class = settings["ifc_class"]
if select_class == "IfcSurfaceStyleRendering":
select_class = "IfcSurfaceStyleShading"
duplicate_items = [s for s in styles if s.is_a(select_class)]
for duplicate_item in duplicate_items:
ifcopenshell.api.run("style.remove_surface_style", file, style=duplicate_item)
styles = list(self.settings["style"].Styles or [])
styles.append(style_item)
self.settings["style"].Styles = styles
return style_item
styles = list(settings["style"].Styles or [])
styles.append(style_item)
settings["style"].Styles = styles
return style_item
@@ -20,39 +20,42 @@ import ifcopenshell
import ifcopenshell.api
def add_surface_textures(file, material=None, uv_maps=None, textures=None) -> None:
"""Add surface texture based on a Blender material definition or texture data.
:param material: The Blender material definition with a node tree that
is compatible with glTF. See one of the valid combinations here:
https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
:type material: bpy.types.Material
:param uv_maps: A list of IfcIndexedTextureMap for any
IfcTessellatedFaceSets that the representation has, obtained from
the HasTextures attribute.
:type uv_maps: list[ifcopenshell.entity_instance]
:param textures: A list of dictionaries containing:
1. Attributes to create IfcImageTexture.
2. One additional parameter `uv_mode` to map IfcImageTexture to correct
IfcTextureCoordinate type.
Possible `uv_mode` values:
* `UV` - use IfcTextureCoordinate from `uv_maps` parameter;
* `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV
based on geometry);
* `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV
based on camera position)
:type textures: list[dict]
:return: A list of IfcImageTexture
:rtype: list[ifcopenshell.entity_instance]
"""
usecase = Usecase()
# TODO: This usecase currently depends on Blender's data model
usecase.file = file
usecase.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []}
return usecase.execute()
class Usecase:
def __init__(self, file, material=None, uv_maps=None, textures=None):
"""Add surface texture based on a Blender material definition or texture data.
:param material: The Blender material definition with a node tree that
is compatible with glTF. See one of the valid combinations here:
https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
:type material: bpy.types.Material
:param uv_maps: A list of IfcIndexedTextureMap for any
IfcTessellatedFaceSets that the representation has, obtained from
the HasTextures attribute.
:type uv_maps: list[ifcopenshell.entity_instance]
:param textures: A list of dictionaries containing:
1. Attributes to create IfcImageTexture.
2. One additional parameter `uv_mode` to map IfcImageTexture to correct
IfcTextureCoordinate type.
Possible `uv_mode` values:
* `UV` - use IfcTextureCoordinate from `uv_maps` parameter;
* `Generated` - IfcTextureCoordinateGenerator with mode COORD (autogenerated UV
based on geometry);
* `Camera` - IfcTextureCoordinateGenerator with mode COORD_EYE (autogenerated UV
based on camera position)
:type textures: list[dict]
:return: A list of IfcImageTexture
:rtype: list[ifcopenshell.entity_instance]
"""
# TODO: This usecase currently depends on Blender's data model
self.file = file
self.settings = {"material": material, "uv_maps": uv_maps or [], "textures": textures or []}
def execute(self):
if self.file.schema == "IFC2X3":
# TODO: research how compatible IFC2X3 and IFC4 textures are
@@ -21,91 +21,96 @@ import ifcopenshell.api
import ifcopenshell.util.element
def assign_material_style(
file, material=None, style=None, context=None, should_use_presentation_style_assignment=False
) -> None:
"""Assigns a style to a material
A style may either be assigned directly to an object's representation,
or to a material which is then associated with the object. If both
exist, then the style assigned directly to the object's representation
takes precedence. It is recommended to use materials and assign styles
to materials. This API function provides that capability.
:param material: The IfcMaterial which you want to assign the style to.
:type material: ifcopenshell.entity_instance
:param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
you want to assign to the material. This will then be applied to all
objects that have that material.
:type style: ifcopenshell.entity_instance
:param context: The IfcGeometricRepresentationSubContext at which this
style should be used. Typically this is the Model BODY context.
:type context: ifcopenshell.entity_instance
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: None
:rtype: None
Example:
.. code:: python
# A model context is needed to store 3D geometry
model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
# Specifically, we want to store body geometry
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
# Let's create a new wall. The wall does not have any geometry yet.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's use the "3D Body" representation we created earlier to add a
# new wall-like body geometry, 5 meters long, 3 meters high, and
# 200mm thick
representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
context=body, length=5, height=3, thickness=0.2)
# Assign our new body geometry back to our wall
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
# Place our wall at the origin
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
# Let's prepare a concrete material. Note that our concrete material
# does not have any colours (styles) at this point.
concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
# Assign our concrete material to our wall
ifcopenshell.api.run("material.assign_material", model,
products=[wall], type="IfcMaterial", material=concrete)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple grey shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Now any element (like our wall) with a concrete material will have
# a grey colour applied.
ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body)
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"material": material,
"style": style,
"context": context,
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
return usecase.execute()
class Usecase:
def __init__(self, file, material=None, style=None, context=None, should_use_presentation_style_assignment=False):
"""Assigns a style to a material
A style may either be assigned directly to an object's representation,
or to a material which is then associated with the object. If both
exist, then the style assigned directly to the object's representation
takes precedence. It is recommended to use materials and assign styles
to materials. This API function provides that capability.
:param material: The IfcMaterial which you want to assign the style to.
:type material: ifcopenshell.entity_instance
:param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
you want to assign to the material. This will then be applied to all
objects that have that material.
:type style: ifcopenshell.entity_instance
:param context: The IfcGeometricRepresentationSubContext at which this
style should be used. Typically this is the Model BODY context.
:type context: ifcopenshell.entity_instance
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: None
:rtype: None
Example:
.. code:: python
# A model context is needed to store 3D geometry
model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
# Specifically, we want to store body geometry
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
# Let's create a new wall. The wall does not have any geometry yet.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's use the "3D Body" representation we created earlier to add a
# new wall-like body geometry, 5 meters long, 3 meters high, and
# 200mm thick
representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
context=body, length=5, height=3, thickness=0.2)
# Assign our new body geometry back to our wall
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
# Place our wall at the origin
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
# Let's prepare a concrete material. Note that our concrete material
# does not have any colours (styles) at this point.
concrete = ifcopenshell.api.run("material.add_material", model, name="CON01", category="concrete")
# Assign our concrete material to our wall
ifcopenshell.api.run("material.assign_material", model,
products=[wall], type="IfcMaterial", material=concrete)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple grey shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Now any element (like our wall) with a concrete material will have
# a grey colour applied.
ifcopenshell.api.run("style.assign_material_style", model, material=concrete, style=style, context=body)
"""
self.file = file
self.settings = {
"material": material,
"style": style,
"context": context,
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
def execute(self):
self.style = self.settings["style"]
if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]:
@@ -17,98 +17,100 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
def assign_representation_styles(
file,
shape_representation=None,
styles=None,
replace_previous_same_type_style=True,
should_use_presentation_style_assignment=False,
) -> None:
"""Assigns a style directly to an object representation
A style may either be assigned directly to an object's representation,
or to a material which is then associated with the object. If both
exist, then the style assigned directly to the object's representation
takes precedence. It is recommended to use materials and assign styles
to materials. However, sometimes you may want to assign colours directly
to the object representation as an override. This API function provides
that capability.
If you want to assign styles to a material instead (recommended), then
please see ifcopenshell.api.style.assign_material_style.
:param shape_representation: The IfcShapeRepresentation of the object
that you want to assign styles to. This implicitly defines the
context at which the styles should be used.
:type shape_representation: ifcopenshell.entity_instance
:param styles: A list of presentation styles, typically IfcSurfaceStyle.
The number of items in the list should correlate with the number of
items in the shape_representation's Items attribute. If you have
more items than styles, the last style is used.
:type styles: list[ifcopenshell.entity_instance]
:param replace_previous_same_type_style: Remove previously assigned styles
of the same type as currently assign style`. Defaults to `True`.
:type replace_previous_same_type_style: bool
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: List of created IfcStyledItems
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# A model context is needed to store 3D geometry
model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
# Specifically, we want to store body geometry
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
# Let's create a new wall. The wall does not have any geometry yet.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's use the "3D Body" representation we created earlier to add a
# new wall-like body geometry, 5 meters long, 3 meters high, and
# 200mm thick
representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
context=body, length=5, height=3, thickness=0.2)
# Assign our new body geometry back to our wall
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
# Place our wall at the origin
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple grey shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Now specifically our wall only will be coloured grey.
ifcopenshell.api.run("style.assign_representation_styles", model,
shape_representation=representation, styles=[style])
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"shape_representation": shape_representation,
"styles": styles or [],
"replace_previous_same_type_style": replace_previous_same_type_style,
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
return usecase.execute()
class Usecase:
def __init__(
self,
file,
shape_representation=None,
styles=None,
replace_previous_same_type_style=True,
should_use_presentation_style_assignment=False,
):
"""Assigns a style directly to an object representation
A style may either be assigned directly to an object's representation,
or to a material which is then associated with the object. If both
exist, then the style assigned directly to the object's representation
takes precedence. It is recommended to use materials and assign styles
to materials. However, sometimes you may want to assign colours directly
to the object representation as an override. This API function provides
that capability.
If you want to assign styles to a material instead (recommended), then
please see ifcopenshell.api.style.assign_material_style.
:param shape_representation: The IfcShapeRepresentation of the object
that you want to assign styles to. This implicitly defines the
context at which the styles should be used.
:type shape_representation: ifcopenshell.entity_instance
:param styles: A list of presentation styles, typically IfcSurfaceStyle.
The number of items in the list should correlate with the number of
items in the shape_representation's Items attribute. If you have
more items than styles, the last style is used.
:type styles: list[ifcopenshell.entity_instance]
:param replace_previous_same_type_style: Remove previously assigned styles
of the same type as currently assign style`. Defaults to `True`.
:type replace_previous_same_type_style: bool
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: List of created IfcStyledItems
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
# A model context is needed to store 3D geometry
model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model")
# Specifically, we want to store body geometry
body = ifcopenshell.api.run("context.add_context", model,
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
# Let's create a new wall. The wall does not have any geometry yet.
wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall")
# Let's use the "3D Body" representation we created earlier to add a
# new wall-like body geometry, 5 meters long, 3 meters high, and
# 200mm thick
representation = ifcopenshell.api.run("geometry.add_wall_representation", model,
context=body, length=5, height=3, thickness=0.2)
# Assign our new body geometry back to our wall
ifcopenshell.api.run("geometry.assign_representation", model,
product=wall, representation=representation)
# Place our wall at the origin
ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple grey shading colour and transparency.
ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 0.5, "Green": 0.5, "Blue": 0.5 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Now specifically our wall only will be coloured grey.
ifcopenshell.api.run("style.assign_representation_styles", model,
shape_representation=representation, styles=[style])
"""
self.file = file
self.settings = {
"shape_representation": shape_representation,
"styles": styles or [],
"replace_previous_same_type_style": replace_previous_same_type_style,
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
def execute(self):
if not self.settings["styles"]:
return []
@@ -17,33 +17,30 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, style=None, attributes=None):
"""Edits the attributes of an IfcPresentationStyle
def edit_presentation_style(file, style=None, attributes=None) -> None:
"""Edits the attributes of an IfcPresentationStyle
For more information about the attributes and data types of an
IfcPresentationStyle, consult the IFC documentation.
For more information about the attributes and data types of an
IfcPresentationStyle, consult the IFC documentation.
:param style: The IfcPresentationStyle entity you want to edit
:type style: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
:param style: The IfcPresentationStyle entity you want to edit
:type style: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Change the name of the style to "Foo"
ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"})
"""
self.file = file
self.settings = {"style": style, "attributes": attributes or {}}
# Change the name of the style to "Foo"
ifcopenshell.api.run("style.edit_presentation_style", model, style=style, attributes={"Name": "Foo"})
"""
settings = {"style": style, "attributes": attributes or {}}
def execute(self):
for name, value in self.settings["attributes"].items():
setattr(self.settings["style"], name, value)
for name, value in settings["attributes"].items():
setattr(settings["style"], name, value)
@@ -17,61 +17,64 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
def edit_surface_style(file, style=None, attributes=None) -> None:
"""Edits the attributes of an IfcPresentationItem
For more information about the attributes and data types of an
IfcPresentationItem, consult the IFC documentation.
The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading,
IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
IfcExternallyDefinedSurfaceStyle.
To represent a colour, a nested dictionary should be used. See the
example below.
:param style: The IfcPresentationStyle entity you want to edit
:type style: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a blank rendering style.
rendering = ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleRendering")
# Edit the attributes of the rendering style.
ifcopenshell.api.run("style.edit_surface_style", model,
style=rendering, attributes={
# A surface colour and transparency is still supplied for
# viewport display only. This will supersede the shading
# presentation item.
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
# NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
# model. In IFC4X3, you may choose PHYSICAL directly.
"ReflectanceMethod": "NOTDEFINED",
# For PBR shading, you may specify these parameters:
"DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
"SpecularColour": 0.1, # Metallic factor
"SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
})
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"style": style, "attributes": attributes or {}}
return usecase.execute()
class Usecase:
def __init__(self, file, style=None, attributes=None):
"""Edits the attributes of an IfcPresentationItem
For more information about the attributes and data types of an
IfcPresentationItem, consult the IFC documentation.
The IfcPresentationItem is expected to be one of IfcSurfaceStyleShading,
IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
IfcExternallyDefinedSurfaceStyle.
To represent a colour, a nested dictionary should be used. See the
example below.
:param style: The IfcPresentationStyle entity you want to edit
:type style: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict, optional
:return: None
:rtype: None
Example:
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a blank rendering style.
rendering = ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleRendering")
# Edit the attributes of the rendering style.
ifcopenshell.api.run("style.edit_surface_style", model,
style=rendering, attributes={
# A surface colour and transparency is still supplied for
# viewport display only. This will supersede the shading
# presentation item.
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
# NOTDEFINED is assumed to be a PHYSICAL (PBR) lighting
# model. In IFC4X3, you may choose PHYSICAL directly.
"ReflectanceMethod": "NOTDEFINED",
# For PBR shading, you may specify these parameters:
"DiffuseColour": { "Name": None, "Red": 0.9, "Green": 0.8, "Blue": 0.8 },
"SpecularColour": 0.1, # Metallic factor
"SpecularHighlight": {"SpecularRoughness": 0.5}, # Roughness factor
})
"""
self.file = file
self.settings = {"style": style, "attributes": attributes or {}}
def execute(self):
attributes = {}
for attribute in self.settings["style"].wrapped_data.declaration().as_entity().all_attributes():
@@ -19,30 +19,33 @@
import ifcopenshell.util.element
def remove_style(file, style=None) -> None:
"""Removes a presentation style
All of the presentation items of the style will also be removed.
:param style: The IfcPresentationStyle to remove.
:type style: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Not anymore!
ifcopenshell.api.run("style.remove_style", model, style=style)
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"style": style}
return usecase.execute()
class Usecase:
def __init__(self, file, style=None):
"""Removes a presentation style
All of the presentation items of the style will also be removed.
:param style: The IfcPresentationStyle to remove.
:type style: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Not anymore!
ifcopenshell.api.run("style.remove_style", model, style=style)
"""
self.file = file
self.settings = {"style": style}
def execute(self):
self.purge_styled_items(self.settings["style"])
for style in self.settings["style"].Styles or []:
@@ -17,38 +17,35 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, representation=None):
"""Removes a styled representation
def remove_styled_representation(file, representation=None) -> None:
"""Removes a styled representation
Styled representations are typically associated with materials. This
removes the representation but not the underlying styles.
Styled representations are typically associated with materials. This
removes the representation but not the underlying styles.
:param representation: The IfcStyledRepresentation to remove.
:type representation: ifcopenshell.entity_instance
:return: None
:rtype: None
:param representation: The IfcStyledRepresentation to remove.
:type representation: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Remove a styled representation
ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation)
"""
self.file = file
self.settings = {"representation": representation}
# Remove a styled representation
ifcopenshell.api.run("style.remove_styled_representation", model, representation=representation)
"""
settings = {"representation": representation}
def execute(self):
for inverse in self.file.get_inverse(self.settings["representation"]):
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
self.file.remove(inverse)
for inverse in file.get_inverse(settings["representation"]):
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
file.remove(inverse)
for item in self.settings["representation"].Items:
if item.is_a("IfcStyledItem") and self.file.get_total_inverses(item) == 1:
for style in item.Styles:
if style.is_a("IfcPresentationStyleAssignment"):
self.file.remove(style)
self.file.remove(item)
for item in settings["representation"].Items:
if item.is_a("IfcStyledItem") and file.get_total_inverses(item) == 1:
for style in item.Styles:
if style.is_a("IfcPresentationStyleAssignment"):
file.remove(style)
file.remove(item)
self.file.remove(self.settings["representation"])
file.remove(settings["representation"])
@@ -20,50 +20,47 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, style=None):
"""Removes a presentation item from a presentation style
def remove_surface_style(file, style=None) -> None:
"""Removes a presentation item from a presentation style
:param style: The IfcPresentationItem to remove.
:type style: ifcopenshell.entity_instance
:return: None
:rtype: None
:param style: The IfcPresentationItem to remove.
:type style: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a new surface style
style = ifcopenshell.api.run("style.add_style", model)
# Create a simple shading colour and transparency.
shading = ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Create a simple shading colour and transparency.
shading = ifcopenshell.api.run("style.add_surface_style", model,
style=style, ifc_class="IfcSurfaceStyleShading", attributes={
"SurfaceColour": { "Name": None, "Red": 1.0, "Green": 0.8, "Blue": 0.8 },
"Transparency": 0., # 0 is opaque, 1 is transparent
})
# Remove the shading item
ifcopenshell.api.run("style.remove_surface_style", model, style=shading)
"""
self.file = file
self.settings = {"style": style}
# Remove the shading item
ifcopenshell.api.run("style.remove_surface_style", model, style=shading)
"""
settings = {"style": style}
def execute(self):
to_delete = set()
if self.settings["style"].is_a("IfcSurfaceStyleWithTextures"):
for texture in self.settings["style"].Textures or []:
if texture.IsMappedBy:
for coordinate in texture.IsMappedBy:
to_delete.add(coordinate)
else:
to_delete.add(texture)
to_delete = set()
if settings["style"].is_a("IfcSurfaceStyleWithTextures"):
for texture in settings["style"].Textures or []:
if texture.IsMappedBy:
for coordinate in texture.IsMappedBy:
to_delete.add(coordinate)
else:
to_delete.add(texture)
for attribute in self.settings["style"]:
if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id():
to_delete.add(attribute)
for attribute in settings["style"]:
if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id():
to_delete.add(attribute)
self.file.remove(self.settings["style"])
file.remove(settings["style"])
for element in to_delete:
ifcopenshell.util.element.remove_deep2(self.file, element)
for element in to_delete:
ifcopenshell.util.element.remove_deep2(file, element)
@@ -20,78 +20,75 @@
import ifcopenshell
class Usecase:
def __init__(self, file, material=None, style=None, context=None):
"""Unassigns a style to a material
def unassign_material_style(file, material=None, style=None, context=None) -> None:
"""Unassigns a style to a material
This does the inverse of assign_material_style.
This does the inverse of assign_material_style.
:param material: The IfcMaterial which you want to unassign the style from.
:type material: ifcopenshell.entity_instance
:param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
you want to unassign from material. This will then be applied to all
objects that have that material.
:type style: ifcopenshell.entity_instance
:param context: The IfcGeometricRepresentationSubContext at which this
style should be unassigned. Typically this is the Model BODY context.
:type context: ifcopenshell.entity_instance
:return: None
:rtype: None
:param material: The IfcMaterial which you want to unassign the style from.
:type material: ifcopenshell.entity_instance
:param style: The IfcPresentationStyle (typically IfcSurfaceStyle) that
you want to unassign from material. This will then be applied to all
objects that have that material.
:type style: ifcopenshell.entity_instance
:param context: The IfcGeometricRepresentationSubContext at which this
style should be unassigned. Typically this is the Model BODY context.
:type context: ifcopenshell.entity_instance
:return: None
:rtype: None
Example:
Example:
.. code:: python
.. code:: python
ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body)
"""
self.file = file
self.settings = {
"material": material,
"style": style,
"context": context,
}
ifcopenshell.api.run("style.unassign_material_style", model, material=concrete, style=style, context=body)
"""
settings = {
"material": material,
"style": style,
"context": context,
}
def execute(self):
for definition in self.settings["material"].HasRepresentation:
for representation in definition.Representations:
if not representation.is_a("IfcStyledRepresentation"):
continue
if representation.ContextOfItems != self.settings["context"]:
continue
for item in representation.Items:
if not item.is_a("IfcStyledItem"):
continue
styles = [s for s in item.Styles if s != self.settings["style"]]
if not styles:
self.file.remove(item)
elif len(styles) != len(item.Styles):
item.Styles = styles
if not representation.Items:
self.file.remove(representation)
if not definition.Representations:
self.file.remove(definition)
# handle material constituents and shape aspects
material_constituents_names = []
for inverse in self.file.get_inverse(self.settings["material"]):
if inverse.is_a("IfcMaterialConstituent") and inverse.Name:
material_constituents_names.append(inverse.Name)
if not material_constituents_names:
return
elements = ifcopenshell.util.element.get_elements_by_material(self.file, self.settings["material"])
shape_aspects = []
for element in elements:
shape_aspects += ifcopenshell.util.element.get_shape_aspects(element)
for shape_aspect in shape_aspects:
if shape_aspect.Name not in material_constituents_names:
for definition in settings["material"].HasRepresentation:
for representation in definition.Representations:
if not representation.is_a("IfcStyledRepresentation"):
continue
if representation.ContextOfItems != settings["context"]:
continue
for item in representation.Items:
if not item.is_a("IfcStyledItem"):
continue
styles = [s for s in item.Styles if s != settings["style"]]
if not styles:
file.remove(item)
elif len(styles) != len(item.Styles):
item.Styles = styles
if not representation.Items:
file.remove(representation)
if not definition.Representations:
file.remove(definition)
for rep in shape_aspect.ShapeRepresentations:
ifcopenshell.api.run(
"style.unassign_representation_styles",
self.file,
shape_representation=rep,
styles=[self.settings["style"]],
)
# handle material constituents and shape aspects
material_constituents_names = []
for inverse in file.get_inverse(settings["material"]):
if inverse.is_a("IfcMaterialConstituent") and inverse.Name:
material_constituents_names.append(inverse.Name)
if not material_constituents_names:
return
elements = ifcopenshell.util.element.get_elements_by_material(file, settings["material"])
shape_aspects = []
for element in elements:
shape_aspects += ifcopenshell.util.element.get_shape_aspects(element)
for shape_aspect in shape_aspects:
if shape_aspect.Name not in material_constituents_names:
continue
for rep in shape_aspect.ShapeRepresentations:
ifcopenshell.api.run(
"style.unassign_representation_styles",
file,
shape_representation=rep,
styles=[settings["style"]],
)
@@ -17,43 +17,48 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
def unassign_representation_styles(
file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False
) -> None:
"""Unassigns styles directly assigned to an object representation
This does the inverse of assign_representation_styles.
:param shape_representation: The IfcShapeRepresentation of the object
that you want to unassign styles from.
:type shape_representation: ifcopenshell.entity_instance
:param styles: A list of presentation styles, typically IfcSurfaceStyle.
The number of items in the list should correlate with the number of
items in the shape_representation's Items attribute. If you have
more items than styles, the last style is used.
:type styles: list[ifcopenshell.entity_instance]
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: None
:rtype: None
Example:
.. code:: python
ifcopenshell.api.run("style.unassign_representation_styles", model,
shape_representation=representation, styles=[style])
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"shape_representation": shape_representation,
"styles": styles or [],
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
return usecase.execute()
class Usecase:
def __init__(self, file, shape_representation=None, styles=None, should_use_presentation_style_assignment=False):
"""Unassigns styles directly assigned to an object representation
This does the inverse of assign_representation_styles.
:param shape_representation: The IfcShapeRepresentation of the object
that you want to unassign styles from.
:type shape_representation: ifcopenshell.entity_instance
:param styles: A list of presentation styles, typically IfcSurfaceStyle.
The number of items in the list should correlate with the number of
items in the shape_representation's Items attribute. If you have
more items than styles, the last style is used.
:type styles: list[ifcopenshell.entity_instance]
:param should_use_presentation_style_assignment: This is a technical
detail to accomodate a bug in Revit. This should always be left as
the default of False, unless you are finding that colours aren't
showing up in Revit. In that case, set it to True, but keep in mind
that this is no longer a valid IFC. Blame Autodesk.
:type should_use_presentation_style_assignment: bool
:return: None
:rtype: None
Example:
.. code:: python
ifcopenshell.api.run("style.unassign_representation_styles", model,
shape_representation=representation, styles=[style])
"""
self.file = file
self.settings = {
"shape_representation": shape_representation,
"styles": styles or [],
"should_use_presentation_style_assignment": should_use_presentation_style_assignment,
}
def execute(self):
if not self.settings["styles"]:
return []