Fix #5544. Assign / add of objects with styles are now supported.

This commit is contained in:
Dion Moult
2024-10-09 18:27:36 +11:00
parent 11eb1c26f0
commit bba862e3ae
5 changed files with 170 additions and 64 deletions
+2 -16
View File
@@ -210,12 +210,7 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
ifc_representation_class=self.ifc_representation_class,
)
if self.should_add_representation and obj.data and len(obj.data.vertices):
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
verts = [v.co / unit_scale for v in obj.data.vertices]
faces = [p.vertices[:] for p in obj.data.polygons]
item = builder.mesh(verts, faces)
representation = builder.get_representation(ifc_context, [item])
representation = tool.Geometry.export_mesh_to_tessellation(obj, ifc_context)
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(
tool.Ifc,
@@ -373,16 +368,7 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
pass
elif representation_template == "OBJ" and props.representation_obj:
obj.matrix_world = props.representation_obj.matrix_world
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
items = []
meshes = tool.Geometry.split_by_loose_parts(props.representation_obj)
for mesh in meshes:
verts = [v.co / unit_scale for v in mesh.vertices]
faces = [p.vertices[:] for p in mesh.polygons]
items.append(builder.mesh(verts, faces))
bpy.data.meshes.remove(mesh)
representation = builder.get_representation(ifc_context, items)
representation = tool.Geometry.export_mesh_to_tessellation(props.representation_obj, ifc_context)
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(
tool.Ifc,
+32
View File
@@ -27,6 +27,7 @@ import multiprocessing
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.api.grid
import ifcopenshell.api.style
import ifcopenshell.geom
import ifcopenshell.guid
import ifcopenshell.util.element
@@ -1600,3 +1601,34 @@ class Geometry(bonsai.core.tool.Geometry):
@classmethod
def reload_representation_item_ids(cls, representation: ifcopenshell.entity_instance, data: bpy.types.Mesh) -> None:
data["ios_item_ids"] = [i["item"].id() for i in ifcopenshell.util.representation.resolve_items(representation)]
@classmethod
def export_mesh_to_tessellation(cls, obj: bpy.types.Object, ifc_context) -> ifcopenshell.entity_instance:
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
items = []
meshes = cls.split_by_loose_parts(obj)
for mesh in meshes:
verts = [v.co / unit_scale for v in mesh.vertices]
faces = [p.vertices[:] for p in mesh.polygons]
item = builder.mesh(verts, faces)
items.append(item)
material_index = mesh.polygons[0].material_index
if materials := list(mesh.materials):
material = materials[material_index]
if not (style := tool.Style.get_style(material)):
style = ifcopenshell.api.run("style.add_style", tool.Ifc.get(), name=material.name)
if material.use_nodes:
ifc_class = "IfcSurfaceStyleRendering"
attributes = tool.Style.get_surface_rendering_attributes(material)
else:
ifc_class = "IfcSurfaceStyleShading"
attributes = tool.Style.get_surface_shading_attributes(material)
ifcopenshell.api.style.add_surface_style(
tool.Ifc.get(), style=style, ifc_class=ifc_class, attributes=attributes
)
tool.Ifc.link(style, material)
material.use_fake_user = True
ifcopenshell.api.style.assign_item_style(tool.Ifc.get(), item=item, style=style)
bpy.data.meshes.remove(mesh)
return builder.get_representation(ifc_context, items)
+2 -48
View File
@@ -594,55 +594,9 @@ class Style(bonsai.core.tool.Style):
@classmethod
def assign_style_to_representation_item(
cls, representation_item: ifcopenshell.entity_instance, style: Optional[ifcopenshell.entity_instance] = None
cls, item: ifcopenshell.entity_instance, style: Optional[ifcopenshell.entity_instance] = None
) -> None:
"""Assign specified style to a representation item and unassign all previously assigned styles."""
ifc_file = representation_item.file
if not (styled_item := next(iter(representation_item.StyledByItem), None)):
if style is None:
return
if ifc_file.schema == "IFC2X3":
style = ifc_file.create_entity("IfcPresentationStyleAssignment", (style,))
ifc_file.create_entity("IfcStyledItem", representation_item, (style,))
return
styled_item_styles = styled_item.Styles
if style and styled_item_styles == (style,):
return
if ifc_file.schema == "IFC4X3":
if style is None:
ifc_file.remove(styled_item)
return
styled_item.Styles = (style,)
return
# < 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:
ifc_file.remove(style_)
else:
assignment = style_
if assignment.Styles != (style,):
assignment.Styles = (style,)
if style is None:
ifc_file.remove(styled_item)
return
if assignment:
if styled_item_styles == (assignment,):
return
styled_item.Styles = (assignment,)
return
styled_item.Styles = (style,)
return ifcopenshell.api.style.assign_item_style(tool.Ifc.get(), item=item, style=style)
@classmethod
def get_representation_item_style(
@@ -27,6 +27,7 @@ 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
@@ -43,6 +44,7 @@ __all__ = [
"add_style",
"add_surface_style",
"add_surface_textures",
"assign_item_style",
"assign_material_style",
"assign_representation_styles",
"edit_presentation_style",
@@ -0,0 +1,132 @@
# 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/>.
import ifcopenshell
from typing import Optional, Union
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 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])
"""
if not (styled_item := next(iter(item.StyledByItem), None)):
if style is None:
return
if file.schema == "IFC2X3":
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