First Commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Manage visual styles of geometry (colours, transparency, rendering, etc)
|
||||
|
||||
Geometry may have visual styles associated with it, including surface styles,
|
||||
2D curve styles, text styles, and more. Surface styles are most commonly used
|
||||
for simple colouring.
|
||||
"""
|
||||
|
||||
from .. import wrap_usecases
|
||||
from .add_style import add_style
|
||||
from .add_surface_style import add_surface_style
|
||||
from .add_surface_textures import add_surface_textures
|
||||
from .assign_item_style import assign_item_style
|
||||
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
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_style",
|
||||
"add_surface_style",
|
||||
"add_surface_textures",
|
||||
"assign_item_style",
|
||||
"assign_material_style",
|
||||
"assign_representation_styles",
|
||||
"edit_presentation_style",
|
||||
"edit_surface_style",
|
||||
"remove_style",
|
||||
"remove_styled_representation",
|
||||
"remove_surface_style",
|
||||
"unassign_material_style",
|
||||
"unassign_representation_styles",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Optional
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def add_style(
|
||||
file: ifcopenshell.file, name: Optional[str] = None, ifc_class="IfcSurfaceStyle"
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""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:
|
||||
|
||||
- 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.
|
||||
|
||||
:param name: The name of the style. Used to easily identify it using a
|
||||
style library.
|
||||
:param ifc_class: Choose from IfcSurfaceStyle, IfcCurveStyle,
|
||||
IfcFillAreaStyle, or IfcTextStyle.
|
||||
:return: The newly created style element, based on the provided
|
||||
ifc_class.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
"""
|
||||
|
||||
kwargs = {"Name": name}
|
||||
if ifc_class == "IfcSurfaceStyle":
|
||||
# Name is filled out because Revit treats this incorrectly as the material name
|
||||
kwargs["Side"] = "BOTH"
|
||||
|
||||
return file.create_entity(ifc_class, **kwargs)
|
||||
@@ -0,0 +1,143 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any, Literal, Optional
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.style
|
||||
|
||||
SURFACE_STYLE_TYPES = Literal[
|
||||
"IfcSurfaceStyleShading",
|
||||
"IfcSurfaceStyleRendering",
|
||||
"IfcSurfaceStyleWithTextures",
|
||||
"IfcSurfaceStyleLighting",
|
||||
"IfcSurfaceStyleRefraction",
|
||||
"IfcExternallyDefinedSurfaceStyle",
|
||||
]
|
||||
|
||||
|
||||
def add_surface_style(
|
||||
file: ifcopenshell.file,
|
||||
style: ifcopenshell.entity_instance,
|
||||
ifc_class: SURFACE_STYLE_TYPES = "IfcSurfaceStyleShading",
|
||||
attributes: Optional[dict[str, Any]] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new presentation item to a surface style
|
||||
|
||||
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 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.
|
||||
|
||||
:param style: The IfcSurfaceStyle you want to add to presentation item
|
||||
to. See ifcopenshell.api.style.add_style.
|
||||
:param ifc_class: Choose from IfcSurfaceStyleShading,
|
||||
IfcSurfaceStyleRendering, IfcSurfaceStyleWithTextures,
|
||||
IfcSurfaceStyleLighting, IfcSurfaceStyleReflectance, or
|
||||
IfcExternallyDefinedSurfaceStyle.
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: The newly created presentation item based on the provided
|
||||
ifc_class.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a simple shading colour and transparency.
|
||||
ifcopenshell.api.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.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",
|
||||
|
||||
# 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
|
||||
})
|
||||
"""
|
||||
attributes = attributes or {}
|
||||
style_item = file.create_entity(ifc_class)
|
||||
ifcopenshell.api.style.edit_surface_style(file, style=style_item, attributes=attributes)
|
||||
styles: list[ifcopenshell.entity_instance]
|
||||
styles = list(style.Styles or [])
|
||||
|
||||
select_class = 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.style.remove_surface_style(file, style=duplicate_item)
|
||||
|
||||
styles = list(style.Styles or [])
|
||||
styles.append(style_item)
|
||||
style.Styles = styles
|
||||
return style_item
|
||||
@@ -0,0 +1,204 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy # pyright: ignore[reportMissingImports] # ty:ignore[unresolved-import]
|
||||
|
||||
|
||||
def add_surface_textures(
|
||||
file: ifcopenshell.file,
|
||||
material: Optional[bpy.types.Material] = None,
|
||||
textures: Optional[list[dict]] = None,
|
||||
uv_maps: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""Add surface texture based on a Blender material definition or texture data.
|
||||
|
||||
Either `material` or `textures` should be provided.
|
||||
|
||||
: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
|
||||
:param uv_maps: A list of IfcIndexedTextureMap for any
|
||||
IfcTessellatedFaceSets that the representation has, obtained from
|
||||
the HasTextures attribute.
|
||||
: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)
|
||||
:return: A list of IfcImageTexture
|
||||
"""
|
||||
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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
if self.file.schema == "IFC2X3":
|
||||
# TODO: research how compatible IFC2X3 and IFC4 textures are
|
||||
return []
|
||||
|
||||
# We optimistically assume the user has specified one of these valid combinations
|
||||
# https://docs.blender.org/manual/en/dev/addons/import_export/scene_gltf2.html
|
||||
# glTF, X3D, and IFC are compatible. As long as they have something that
|
||||
# loosely resembles the node tree, we treat it as valid.
|
||||
self.textures = []
|
||||
|
||||
for texture in self.settings["textures"]:
|
||||
uv_mode = texture.get("uv_mode", None)
|
||||
texture_data = texture.copy()
|
||||
texture_data.pop("uv_mode", None)
|
||||
texture = self.file.create_entity("IfcImageTexture", **texture_data)
|
||||
if uv_mode == "Generated":
|
||||
self.file.create_entity("IfcTextureCoordinateGenerator", Maps=[texture], Mode="COORD")
|
||||
elif uv_mode == "Camera":
|
||||
self.file.create_entity("IfcTextureCoordinateGenerator", Maps=[texture], Mode="COORD-EYE")
|
||||
elif uv_mode == "UV":
|
||||
self.apply_uv_map_to_texture(texture)
|
||||
self.textures.append(texture)
|
||||
|
||||
if self.settings["material"] is None:
|
||||
return self.textures
|
||||
|
||||
output = {n.type: n for n in self.settings["material"].node_tree.nodes}.get("OUTPUT_MATERIAL", None)
|
||||
|
||||
if not output:
|
||||
return self.textures
|
||||
|
||||
bsdf = output.inputs["Surface"].links[0].from_node
|
||||
|
||||
if bsdf.type == "ADD_SHADER":
|
||||
for socket in bsdf.inputs:
|
||||
if socket.links and socket.links[0].from_node.type == "BSDF_PRINCIPLED":
|
||||
bsdf = socket.links[0].from_node
|
||||
break
|
||||
|
||||
if bsdf.type == "MIX_SHADER":
|
||||
self.detect_unlit_emissive_map(bsdf)
|
||||
elif bsdf.type == "BSDF_PRINCIPLED":
|
||||
self.detect_normal_map(bsdf)
|
||||
self.detect_emissive_map(bsdf)
|
||||
self.detect_metallicroughness_map(bsdf)
|
||||
self.detect_occlusion_map()
|
||||
self.detect_diffuse_map(bsdf)
|
||||
# We do not support Phong shading. What year is this, 1995?
|
||||
|
||||
return self.textures
|
||||
|
||||
def detect_unlit_emissive_map(self, bsdf):
|
||||
for socket in bsdf.inputs:
|
||||
if socket.links and socket.links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(socket.links[0].from_node, "EMISSIVE")
|
||||
|
||||
def detect_normal_map(self, bsdf):
|
||||
if bsdf.inputs["Normal"].links and bsdf.inputs["Normal"].links[0].from_node.type == "NORMAL_MAP":
|
||||
normal = bsdf.inputs["Normal"].links[0].from_node
|
||||
if normal.inputs["Color"].links and normal.inputs["Color"].links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(normal.inputs["Color"].links[0].from_node, "NORMAL")
|
||||
|
||||
def detect_emissive_map(self, bsdf):
|
||||
if bsdf.outputs[0].links[0].to_node.type != "ADD_SHADER":
|
||||
return
|
||||
bsdf = bsdf.outputs[0].links[0].to_node
|
||||
for socket in bsdf.inputs:
|
||||
if socket.links and socket.links[0].from_node.type == "EMISSION":
|
||||
bsdf = socket.links[0].from_node
|
||||
if bsdf.inputs["Color"].links and bsdf.inputs["Color"].links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(bsdf.inputs["Color"].links[0].from_node, "EMISSIVE")
|
||||
|
||||
def detect_metallicroughness_map(self, bsdf):
|
||||
if bsdf.inputs["Metallic"].links and bsdf.inputs["Metallic"].links[0].from_node.type == "SEPRGB":
|
||||
seprgb = bsdf.inputs["Metallic"].links[0].from_node
|
||||
if seprgb.inputs["Image"].links and seprgb.inputs["Image"].links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(seprgb.inputs["Image"].links[0].from_node, "METALLICROUGHNESS")
|
||||
if bsdf.inputs["Roughness"].links and bsdf.inputs["Roughness"].links[0].from_node.type == "SEPRGB":
|
||||
seprgb = bsdf.inputs["Roughness"].links[0].from_node
|
||||
if seprgb.inputs["Image"].links and seprgb.inputs["Image"].links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(seprgb.inputs["Image"].links[0].from_node, "METALLICROUGHNESS")
|
||||
|
||||
def detect_occlusion_map(self):
|
||||
for node in self.settings["material"].node_tree.nodes:
|
||||
if (
|
||||
node.type != "GROUP"
|
||||
or not node.node_tree
|
||||
or node.node_tree.name != "glTF Material Output"
|
||||
or not node.inputs
|
||||
or not node.inputs[0].links
|
||||
):
|
||||
continue
|
||||
from_node = node.inputs[0].links[0].from_node
|
||||
if from_node.type == "SEPRGB":
|
||||
sep = from_node
|
||||
if sep.inputs["Image"].links and sep.inputs["Image"].links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(sep.inputs["Image"].links[0].from_node, "OCCLUSION")
|
||||
elif from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(from_node, "OCCLUSION")
|
||||
|
||||
def detect_diffuse_map(self, bsdf):
|
||||
links = bsdf.inputs["Base Color"].links
|
||||
if links and links[0].from_node.type == "TEX_IMAGE":
|
||||
return self.create_surface_texture(links[0].from_node, "DIFFUSE")
|
||||
|
||||
def create_surface_texture(self, node, mode):
|
||||
import bonsai.tool as tool
|
||||
|
||||
texture = self.file.create_entity(
|
||||
"IfcImageTexture",
|
||||
RepeatS=node.extension == "REPEAT",
|
||||
RepeatT=node.extension == "REPEAT",
|
||||
Mode=mode,
|
||||
URLReference=tool.Blender.blender_path_to_posix(node.image.filepath),
|
||||
)
|
||||
self.textures.append(texture)
|
||||
self.process_texture_coordinates(node, texture)
|
||||
|
||||
def process_texture_coordinates(self, node, texture):
|
||||
if node.inputs["Vector"].links and node.inputs["Vector"].links[0].from_node.type == "TEX_COORD":
|
||||
if node.inputs["Vector"].links[0].from_socket.name == "UV":
|
||||
self.apply_uv_map_to_texture(texture)
|
||||
elif node.inputs["Vector"].links[0].from_socket.name == "Generated":
|
||||
self.file.create_entity("IfcTextureCoordinateGenerator", Maps=[texture], Mode="COORD")
|
||||
elif node.inputs["Vector"].links[0].from_socket.name == "Camera":
|
||||
self.file.create_entity("IfcTextureCoordinateGenerator", Maps=[texture], Mode="COORD-EYE")
|
||||
elif node.inputs["Vector"].links and node.inputs["Vector"].links[0].from_node.type == "UVMAP":
|
||||
self.apply_uv_map_to_texture(texture)
|
||||
|
||||
def apply_uv_map_to_texture(self, texture):
|
||||
for uv_map in self.settings["uv_maps"]:
|
||||
maps = set(uv_map.Maps or [])
|
||||
maps.add(texture)
|
||||
uv_map.Maps = list(maps)
|
||||
@@ -0,0 +1,133 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Optional, Union
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def assign_item_style(
|
||||
file: ifcopenshell.file,
|
||||
item: ifcopenshell.entity_instance,
|
||||
style: Optional[ifcopenshell.entity_instance],
|
||||
should_use_presentation_style_assignment: bool = False,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Assigns a style directly to a representation item
|
||||
|
||||
A style may either be assigned directly to an object's representation
|
||||
items, 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 item: The IfcRepresentationItem of the object
|
||||
that you want to assign styles to.
|
||||
:param style: A presentation style, typically IfcSurfaceStyle. None if you
|
||||
want to remove an existing style from the item.
|
||||
:return: Created or existing IfcStyledItem, or None if the style was removed.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A model context is needed to store 3D geometry
|
||||
model3d = ifcopenshell.api.context.add_context(model, context_type="Model")
|
||||
|
||||
# Specifically, we want to store body geometry
|
||||
body = ifcopenshell.api.context.add_context(model,
|
||||
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
|
||||
|
||||
# Let's create a new block shaped furniture. The furniture does not have any geometry yet.
|
||||
wall = ifcopenshell.api.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.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.geometry.assign_representation(model,
|
||||
product=wall, representation=representation)
|
||||
|
||||
# Place our wall at the origin
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=wall)
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a simple grey shading colour and transparency.
|
||||
ifcopenshell.api.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's only item only will be coloured grey.
|
||||
ifcopenshell.api.style.assign_item_style(model,
|
||||
style=style, item=representation.Items[0])
|
||||
"""
|
||||
if not (styled_item := next(iter(item.StyledByItem), None)):
|
||||
if style is None:
|
||||
return
|
||||
if file.schema == "IFC2X3" or should_use_presentation_style_assignment:
|
||||
style = file.create_entity("IfcPresentationStyleAssignment", (style,))
|
||||
return file.create_entity("IfcStyledItem", item, (style,))
|
||||
|
||||
styled_item_styles = styled_item.Styles
|
||||
if style and styled_item_styles == (style,):
|
||||
return styled_item
|
||||
|
||||
if file.schema == "IFC4X3":
|
||||
if style is None:
|
||||
file.remove(styled_item)
|
||||
return
|
||||
styled_item.Styles = (style,)
|
||||
return styled_item
|
||||
|
||||
# < IFC4X3
|
||||
# Can't just remove a styled item or assign a style
|
||||
# since we need to remove/change the possible style assignments.
|
||||
assignment = None
|
||||
for style_ in styled_item_styles:
|
||||
if not style_.is_a("IfcPresentationStyleAssignment"):
|
||||
continue
|
||||
# Remove second assignment.
|
||||
if style is None or assignment:
|
||||
file.remove(style_)
|
||||
else:
|
||||
assignment = style_
|
||||
if assignment.Styles != (style,):
|
||||
assignment.Styles = (style,)
|
||||
|
||||
if style is None:
|
||||
file.remove(styled_item)
|
||||
return
|
||||
|
||||
if assignment:
|
||||
if styled_item_styles == (assignment,):
|
||||
return styled_item
|
||||
styled_item.Styles = (assignment,)
|
||||
return styled_item
|
||||
|
||||
styled_item.Styles = (style,)
|
||||
return styled_item
|
||||
@@ -0,0 +1,247 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def assign_material_style(
|
||||
file: ifcopenshell.file,
|
||||
material: ifcopenshell.entity_instance,
|
||||
style: ifcopenshell.entity_instance,
|
||||
context: ifcopenshell.entity_instance,
|
||||
should_use_presentation_style_assignment: bool = 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.
|
||||
: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.
|
||||
:param context: The IfcGeometricRepresentationSubContext at which this
|
||||
style should be used. Typically this is the Model BODY context.
|
||||
: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.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A model context is needed to store 3D geometry
|
||||
model3d = ifcopenshell.api.context.add_context(model, context_type="Model")
|
||||
|
||||
# Specifically, we want to store body geometry
|
||||
body = ifcopenshell.api.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.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.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.geometry.assign_representation(model,
|
||||
product=wall, representation=representation)
|
||||
|
||||
# Place our wall at the origin
|
||||
ifcopenshell.api.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.material.add_material(model, name="CON01", category="concrete")
|
||||
|
||||
# Assign our concrete material to our wall
|
||||
ifcopenshell.api.material.assign_material(model,
|
||||
products=[wall], type="IfcMaterial", material=concrete)
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a simple grey shading colour and transparency.
|
||||
ifcopenshell.api.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.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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
self.style = self.settings["style"]
|
||||
if self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]:
|
||||
self.style = self.file.createIfcPresentationStyleAssignment([self.settings["style"]])
|
||||
|
||||
if self.settings["material"].HasRepresentation:
|
||||
self.modify_existing_definition_representation()
|
||||
else:
|
||||
self.create_new_definition_representation()
|
||||
|
||||
# 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:
|
||||
continue
|
||||
|
||||
for rep in shape_aspect.ShapeRepresentations:
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
self.file, shape_representation=rep, styles=[self.style]
|
||||
)
|
||||
|
||||
def modify_existing_definition_representation(self):
|
||||
# NOTE: while it's theoritically possible to have multiple styles per 1 material
|
||||
# (either with multiple styled items or multiple styles in 1 item)
|
||||
# we use an implicit convention that there is only 1 style per material.
|
||||
definition_representation = self.settings["material"].HasRepresentation[0]
|
||||
representation = self.get_styled_representation(definition_representation)
|
||||
if representation:
|
||||
items = list(representation.Items)
|
||||
new_items = []
|
||||
same_style_items = []
|
||||
for item in items:
|
||||
if not item.is_a("IfcStyledItem"):
|
||||
continue
|
||||
if self.has_proposed_style(item):
|
||||
return
|
||||
if self.has_same_style_type(item):
|
||||
same_style_items.append(item)
|
||||
else:
|
||||
new_items.append(item)
|
||||
item_to_reuse = same_style_items.pop(0) if same_style_items else None
|
||||
new_items.append(self.create_styled_item(item_to_reuse))
|
||||
representation.Items = new_items
|
||||
for item in same_style_items:
|
||||
if self.file.get_total_inverses(item) == 0:
|
||||
self.file.remove(item)
|
||||
else:
|
||||
representations = list(definition_representation.Representations)
|
||||
representations.append(self.create_styled_representation())
|
||||
definition_representation.Representations = representations
|
||||
|
||||
def has_proposed_style(self, styled_item: ifcopenshell.entity_instance) -> bool:
|
||||
style = self.settings["style"]
|
||||
styles = styled_item.Styles
|
||||
if style in styles:
|
||||
return True
|
||||
if self.file.schema != "IFC4X3":
|
||||
# IfcPresentationStyleAssignment is removed in IFC4X3
|
||||
for s in styles:
|
||||
if s.is_a("IfcPresentationStyleAssignment"):
|
||||
if style in s.Styles:
|
||||
return True
|
||||
return False
|
||||
|
||||
def has_same_style_type(self, styled_item: ifcopenshell.entity_instance) -> bool:
|
||||
style = self.settings["style"]
|
||||
style_class = style.is_a()
|
||||
for s in styled_item.Styles:
|
||||
s_class = s.is_a()
|
||||
if s_class == style_class:
|
||||
return True
|
||||
elif s_class == "IfcPresentationStyleAssignment":
|
||||
for ss in s.Styles:
|
||||
if ss.is_a() == style_class:
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_new_definition_representation(self):
|
||||
representation = self.create_styled_representation()
|
||||
definition_representation = self.file.create_entity(
|
||||
"IfcMaterialDefinitionRepresentation",
|
||||
**{"Representations": [representation], "RepresentedMaterial": self.settings["material"]},
|
||||
)
|
||||
|
||||
def get_styled_representation(self, definition_representation):
|
||||
representations = [
|
||||
r
|
||||
for r in definition_representation.Representations
|
||||
if r.is_a("IfcStyledRepresentation") and r.ContextOfItems == self.settings["context"]
|
||||
]
|
||||
if representations:
|
||||
return representations[0]
|
||||
|
||||
def create_styled_representation(self):
|
||||
return self.file.create_entity(
|
||||
"IfcStyledRepresentation",
|
||||
**{
|
||||
"ContextOfItems": self.settings["context"],
|
||||
"RepresentationIdentifier": self.settings["context"].ContextIdentifier,
|
||||
"Items": [self.create_styled_item()],
|
||||
},
|
||||
)
|
||||
|
||||
def create_styled_item(self, reuse_item=None):
|
||||
if reuse_item is None:
|
||||
return self.file.create_entity(
|
||||
"IfcStyledItem", **{"Styles": [self.style], "Name": self.settings["style"].Name}
|
||||
)
|
||||
|
||||
# IfcPresentationStyleAssignment we created end up not being used
|
||||
# TODO: do not create IfcPresentationStyleAssignment in the first place
|
||||
# as it might get removed
|
||||
if reuse_item.is_a("IfcPresentationStyleAssignment") and self.style.is_a("IfcPresentationStyleAssignment"):
|
||||
self.file.remove(self.style)
|
||||
self.style = reuse_item
|
||||
|
||||
reuse_item.Styles = (self.settings["style"],)
|
||||
reuse_item.Name = self.settings["style"].Name
|
||||
return reuse_item
|
||||
@@ -0,0 +1,204 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def assign_representation_styles(
|
||||
file: ifcopenshell.file,
|
||||
shape_representation: ifcopenshell.entity_instance,
|
||||
styles: list[ifcopenshell.entity_instance],
|
||||
replace_previous_same_type_style: bool = True,
|
||||
should_use_presentation_style_assignment: bool = False,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""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.
|
||||
|
||||
This function assigns styles in bulk in an ordered manner to every item in
|
||||
the representation, so the order and total styles provided is significant.
|
||||
If you want more granular control, use
|
||||
:func:`ifcopenshell.api.style.assign_item_style`.
|
||||
|
||||
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.
|
||||
: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.
|
||||
:param replace_previous_same_type_style: Remove previously assigned styles
|
||||
of the same type as currently assign style`. Defaults to `True`.
|
||||
: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.
|
||||
:return: List of created IfcStyledItems
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# A model context is needed to store 3D geometry
|
||||
model3d = ifcopenshell.api.context.add_context(model, context_type="Model")
|
||||
|
||||
# Specifically, we want to store body geometry
|
||||
body = ifcopenshell.api.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.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.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.geometry.assign_representation(model,
|
||||
product=wall, representation=representation)
|
||||
|
||||
# Place our wall at the origin
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=wall)
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a simple grey shading colour and transparency.
|
||||
ifcopenshell.api.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.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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
results: list[ifcopenshell.entity_instance]
|
||||
|
||||
def execute(self):
|
||||
if not self.settings["styles"]:
|
||||
return []
|
||||
self.settings["styles"] = self.settings["styles"].copy()
|
||||
self.results = []
|
||||
use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]
|
||||
replace_previous_same_type_style = self.settings["replace_previous_same_type_style"]
|
||||
|
||||
for element in self.file.traverse(self.settings["shape_representation"]):
|
||||
if not element.is_a("IfcShapeModel"):
|
||||
continue
|
||||
for item in element.Items:
|
||||
if not item.is_a("IfcGeometricRepresentationItem") and not item.is_a(
|
||||
"IfcTopologicalRepresentationItem"
|
||||
):
|
||||
continue
|
||||
if self.settings["styles"]:
|
||||
# If there are more items than styles, fallback to using the last style
|
||||
style = self.settings["styles"].pop(0)
|
||||
name = style.Name
|
||||
current_style_type = style.is_a()
|
||||
|
||||
# item may had previous styled item
|
||||
prev_styled_item = next((i for i in item.StyledByItem), None)
|
||||
style_assignment = None # try to find some style assignment to reuse
|
||||
|
||||
if prev_styled_item is None:
|
||||
if use_style_assignment:
|
||||
style_assignment = self.file.createIfcPresentationStyleAssignment([style])
|
||||
self.results.append(self.file.createIfcStyledItem(item, [style_assignment], name))
|
||||
else:
|
||||
self.results.append(self.file.createIfcStyledItem(item, [style], name))
|
||||
continue
|
||||
|
||||
if replace_previous_same_type_style:
|
||||
self.remove_same_type_styles(prev_styled_item, current_style_type, remove_item=False)
|
||||
for style_ in prev_styled_item.Styles:
|
||||
if style_.is_a("IfcPresentationStyleAssignment"):
|
||||
if use_style_assignment and style_assignment is None:
|
||||
style_assignment = style_
|
||||
self.remove_same_type_styles(style_assignment, current_style_type, remove_item=False)
|
||||
else:
|
||||
self.remove_same_type_styles(style_assignment, current_style_type, remove_item=True)
|
||||
|
||||
if use_style_assignment:
|
||||
if style_assignment:
|
||||
style_assignment.Styles = style_assignment.Styles + (style,)
|
||||
else:
|
||||
style_assignment = self.file.createIfcPresentationStyleAssignment([style])
|
||||
prev_styled_item.Styles = prev_styled_item.Styles + (style_assignment,)
|
||||
else:
|
||||
prev_styled_item.Styles = prev_styled_item.Styles + (style,)
|
||||
continue
|
||||
|
||||
# collect previously assigned styles
|
||||
assigned_styles = []
|
||||
for style_ in prev_styled_item.Styles:
|
||||
if style_.is_a("IfcPresentationStyleAssignment"):
|
||||
if style_assignment is None:
|
||||
style_assignment = style_
|
||||
assigned_styles.extend(style_.Styles)
|
||||
else: # IfcPresentationStyle
|
||||
assigned_styles.append(style_)
|
||||
|
||||
if style in assigned_styles:
|
||||
continue
|
||||
|
||||
if use_style_assignment:
|
||||
if style_assignment is not None:
|
||||
style_assignment.Styles = style_assignment.Styles + (style,)
|
||||
else:
|
||||
style_assignment = self.file.createIfcPresentationStyleAssignment([style])
|
||||
prev_styled_item.Styles = prev_styled_item.Styles + (style_assignment,)
|
||||
else:
|
||||
prev_styled_item.Styles = prev_styled_item.Styles + (style,)
|
||||
|
||||
return self.results
|
||||
|
||||
def remove_same_type_styles(self, style_item, current_style_type: str, remove_item: bool) -> None:
|
||||
styles = [s for s in style_item.Styles if s.is_a() != current_style_type]
|
||||
if remove_item and not styles:
|
||||
self.file.remove(style_item)
|
||||
else:
|
||||
style_item.Styles = styles
|
||||
@@ -0,0 +1,46 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_presentation_style(
|
||||
file: ifcopenshell.file, style: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> None:
|
||||
"""Edits the attributes of an IfcPresentationStyle
|
||||
|
||||
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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Change the name of the style to "Foo"
|
||||
ifcopenshell.api.style.edit_presentation_style(model, style=style, attributes={"Name": "Foo"})
|
||||
"""
|
||||
for name, value in attributes.items():
|
||||
setattr(style, name, value)
|
||||
@@ -0,0 +1,138 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any, Union
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def edit_surface_style(
|
||||
file: ifcopenshell.file, style: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||
) -> 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
|
||||
:param attributes: a dictionary of attribute names and values.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a blank rendering style.
|
||||
rendering = ifcopenshell.api.style.add_surface_style(model,
|
||||
style=style, ifc_class="IfcSurfaceStyleRendering")
|
||||
|
||||
# Edit the attributes of the rendering style.
|
||||
ifcopenshell.api.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
|
||||
return usecase.execute(style, attributes)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(self, style: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||
self.style = style
|
||||
|
||||
attribute_types: dict[str, str] = {}
|
||||
for attribute in style.wrapped_data.declaration().as_entity().all_attributes():
|
||||
attribute_type = attribute.type_of_attribute()
|
||||
if attribute_type.as_aggregation_type() is None:
|
||||
attribute_type = attribute_type.declared_type().name()
|
||||
else:
|
||||
# doesn't have .declared_type()
|
||||
attribute_type = attribute_type.type_of_element()
|
||||
attribute_types[attribute.name()] = attribute_type
|
||||
|
||||
for key, value in attributes.items():
|
||||
attribute_class = attribute_types.get(key)
|
||||
if attribute_class == "IfcColourRgb":
|
||||
self.edit_colour_rgb(key, value)
|
||||
elif key == "SpecularHighlight":
|
||||
self.edit_specular_highlight(value)
|
||||
elif attribute_class == "IfcColourOrFactor":
|
||||
self.edit_colour_or_factor(key, value)
|
||||
else:
|
||||
setattr(style, key, value)
|
||||
|
||||
def edit_colour_rgb(self, name: str, value: dict[str, Any]):
|
||||
if (attribute := getattr(self.style, name)) is None:
|
||||
attribute = self.file.createIfcColourRgb()
|
||||
setattr(self.style, name, attribute)
|
||||
attribute.Name = value.get("Name", None)
|
||||
attribute.Red = value["Red"]
|
||||
attribute.Green = value["Green"]
|
||||
attribute.Blue = value["Blue"]
|
||||
|
||||
def edit_colour_or_factor(self, name: str, value: Union[dict[str, Any], ifcopenshell.entity_instance, None]):
|
||||
if isinstance(value, dict):
|
||||
attribute = getattr(self.style, name)
|
||||
if not attribute or not attribute.is_a("IfcColourRgb"):
|
||||
colour = self.file.createIfcColourRgb(None, 0, 0, 0)
|
||||
setattr(self.style, name, colour)
|
||||
attribute = getattr(self.style, name)
|
||||
attribute[1] = value["Red"]
|
||||
attribute[2] = value["Green"]
|
||||
attribute[3] = value["Blue"]
|
||||
else: # assume it's float value for IfcNormalisedRatioMeasure or None
|
||||
existing_value = getattr(self.style, name)
|
||||
if existing_value and existing_value.id():
|
||||
self.file.remove(existing_value)
|
||||
if value is not None:
|
||||
value = self.file.create_entity("IfcNormalisedRatioMeasure", value)
|
||||
setattr(self.style, name, value)
|
||||
|
||||
def edit_specular_highlight(self, value: Union[dict[str, Any], None]) -> None:
|
||||
if value is None:
|
||||
self.style.SpecularHighlight = None
|
||||
elif value.get("IfcSpecularExponent", None):
|
||||
self.style.SpecularHighlight = self.file.createIfcSpecularExponent(value["IfcSpecularExponent"])
|
||||
elif value.get("IfcSpecularRoughness", None):
|
||||
self.style.SpecularHighlight = self.file.createIfcSpecularRoughness(value["IfcSpecularRoughness"])
|
||||
@@ -0,0 +1,93 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 collections.abc import Iterable
|
||||
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_style(file: ifcopenshell.file, style: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a presentation style
|
||||
|
||||
All of the presentation items of the style will also be removed.
|
||||
|
||||
:param style: The IfcPresentationStyle to remove.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Not anymore!
|
||||
ifcopenshell.api.style.remove_style(model, style=style)
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
return usecase.execute(style)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
|
||||
def execute(
|
||||
self, style: ifcopenshell.entity_instance, do_not_delete: Iterable[ifcopenshell.entity_instance] = ()
|
||||
) -> None:
|
||||
self.purge_inverses(style)
|
||||
ifc_class = style.is_a()
|
||||
if ifc_class == "IfcSurfaceStyle":
|
||||
for style_ in style.Styles:
|
||||
ifcopenshell.api.style.remove_surface_style(self.file, style=style_)
|
||||
elif ifc_class == "IfcFillAreaStyle":
|
||||
for style_ in style.FillStyles:
|
||||
ifcopenshell.util.element.remove_deep2(
|
||||
self.file, style_, also_consider=[style], do_not_delete=list(do_not_delete)
|
||||
)
|
||||
self.file.remove(style)
|
||||
|
||||
def purge_inverses(self, style: ifcopenshell.entity_instance) -> None:
|
||||
for inverse in self.file.get_inverse(style):
|
||||
if inverse.is_a("IfcStyledItem"):
|
||||
if len(inverse.Styles) == 1:
|
||||
self.purge_styled_representations(inverse)
|
||||
self.file.remove(inverse)
|
||||
# IfcCurveStyle -> IfcFillAreaStyleHatching.
|
||||
elif inverse.is_a("IfcFillAreaStyleHatching"):
|
||||
self.purge_fill_area_style_hatching(inverse, style)
|
||||
|
||||
def purge_styled_representations(self, styled_item: ifcopenshell.entity_instance) -> None:
|
||||
for inverse in self.file.get_inverse(styled_item):
|
||||
if inverse.is_a("IfcStyledRepresentation") and len(inverse.Items) == 1:
|
||||
self.purge_material_definition_representations(inverse)
|
||||
self.file.remove(inverse)
|
||||
|
||||
def purge_material_definition_representations(self, styled_representation: ifcopenshell.entity_instance) -> None:
|
||||
for inverse in self.file.get_inverse(styled_representation):
|
||||
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
|
||||
self.file.remove(inverse)
|
||||
|
||||
def purge_fill_area_style_hatching(
|
||||
self, fill_area_style_hatching: ifcopenshell.entity_instance, style: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
for inverse in self.file.get_inverse(fill_area_style_hatching):
|
||||
if inverse.is_a("IfcFillAreaStyle"):
|
||||
self.execute(inverse, do_not_delete=(fill_area_style_hatching, style))
|
||||
ifcopenshell.util.element.remove_deep2(self.file, fill_area_style_hatching, do_not_delete=[style])
|
||||
@@ -0,0 +1,48 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def remove_styled_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a styled representation
|
||||
|
||||
Styled representations are typically associated with materials. This
|
||||
removes the representation but not the underlying styles.
|
||||
|
||||
:param representation: The IfcStyledRepresentation to remove.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Remove a styled representation
|
||||
ifcopenshell.api.style.remove_styled_representation(model, representation=representation)
|
||||
"""
|
||||
for inverse in file.get_inverse(representation):
|
||||
if inverse.is_a("IfcMaterialDefinitionRepresentation") and len(inverse.Representations) == 1:
|
||||
file.remove(inverse)
|
||||
|
||||
for item in 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)
|
||||
|
||||
file.remove(representation)
|
||||
@@ -0,0 +1,67 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_surface_style(file: ifcopenshell.file, style: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes a presentation item from a presentation style
|
||||
|
||||
:param style: The IfcPresentationItem to remove.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a new surface style
|
||||
style = ifcopenshell.api.style.add_style(model)
|
||||
|
||||
# Create a simple shading colour and transparency.
|
||||
shading = ifcopenshell.api.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.style.remove_surface_style(model, style=shading)
|
||||
"""
|
||||
|
||||
to_delete = set()
|
||||
if style.is_a("IfcSurfaceStyleWithTextures"):
|
||||
textures = style.Textures
|
||||
if file.schema == "IFC2X3":
|
||||
to_delete.update(textures)
|
||||
else:
|
||||
for texture in textures:
|
||||
if coords := texture.IsMappedBy:
|
||||
for coordinate in coords:
|
||||
to_delete.add(coordinate)
|
||||
else:
|
||||
to_delete.add(texture)
|
||||
|
||||
for attribute in style:
|
||||
if isinstance(attribute, ifcopenshell.entity_instance) and attribute.id():
|
||||
to_delete.add(attribute)
|
||||
|
||||
file.remove(style)
|
||||
|
||||
for element in to_delete:
|
||||
ifcopenshell.util.element.remove_deep2(file, element)
|
||||
@@ -0,0 +1,101 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def unassign_material_style(
|
||||
file: ifcopenshell.file,
|
||||
material: ifcopenshell.entity_instance,
|
||||
style: ifcopenshell.entity_instance,
|
||||
context: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
"""Unassigns a style to a material
|
||||
|
||||
This does the inverse of assign_material_style.
|
||||
|
||||
:param material: The IfcMaterial which you want to unassign the style from.
|
||||
: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.
|
||||
:param context: The IfcGeometricRepresentationSubContext at which this
|
||||
style should be unassigned. Typically this is the Model BODY context.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
ifcopenshell.api.style.unassign_material_style(model, material=concrete, style=style, context=body)
|
||||
"""
|
||||
settings = {
|
||||
"material": material,
|
||||
"style": style,
|
||||
"context": context,
|
||||
}
|
||||
|
||||
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 = []
|
||||
for s in item.Styles:
|
||||
if s == settings["style"]:
|
||||
continue
|
||||
if s.is_a("IfcPresentationStyleAssignment"):
|
||||
if s.Styles == (settings["style"],):
|
||||
continue
|
||||
styles.append(s)
|
||||
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)
|
||||
|
||||
# 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.style.unassign_representation_styles(
|
||||
file, shape_representation=rep, styles=[settings["style"]]
|
||||
)
|
||||
@@ -0,0 +1,97 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell 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 Lesser General Public License for more details.
|
||||
#
|
||||
# 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 typing import Any
|
||||
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def unassign_representation_styles(
|
||||
file: ifcopenshell.file,
|
||||
shape_representation: ifcopenshell.entity_instance,
|
||||
styles: list[ifcopenshell.entity_instance],
|
||||
should_use_presentation_style_assignment: bool = 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.
|
||||
: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.
|
||||
: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.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
ifcopenshell.api.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:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
if not self.settings["styles"]:
|
||||
return []
|
||||
self.results = []
|
||||
use_style_assignment = self.file.schema == "IFC2X3" or self.settings["should_use_presentation_style_assignment"]
|
||||
|
||||
for element in self.file.traverse(self.settings["shape_representation"]):
|
||||
if not element.is_a("IfcShapeRepresentation"):
|
||||
continue
|
||||
for item in element.Items:
|
||||
if not item.is_a("IfcGeometricRepresentationItem"):
|
||||
continue
|
||||
|
||||
if not item.StyledByItem:
|
||||
continue
|
||||
|
||||
item = item.StyledByItem[0]
|
||||
if use_style_assignment:
|
||||
for style_ in item.Styles:
|
||||
if style_.is_a("IfcPresentationStyleAssignment"):
|
||||
self.remove_styles(style_)
|
||||
self.remove_styles(item)
|
||||
|
||||
def remove_styles(self, item):
|
||||
"""Removes styles from a styled item or a style assignment
|
||||
and purges item if doesn't have any styles after"""
|
||||
styles = [s for s in item.Styles if s not in self.settings["styles"]]
|
||||
if not styles:
|
||||
self.file.remove(item)
|
||||
elif len(styles) != len(item.Styles):
|
||||
item.Styles = styles
|
||||
Reference in New Issue
Block a user