mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +00:00
Route _has_material_styles through tool.Root.has_material_styles
Pre-existing architectural smell on v0.8.0: core/root.py.copy_class
called a module-level _has_material_styles helper that did
ifcopenshell.util.element.get_materials() directly, bypassing the
Prophecy mock seam that every other branch in copy_class flowed
through. Symptom: test/core/test_root.py::TestCopyClass::
test_AAAAAAAAAAAA passed mock strings into copy_class, the helper
called .is_a() on the string, AttributeError.
Move the check to tool.Root.has_material_styles (paired with
assign_body_styles — they're called in sequence as "is there a
material style? if not, assign body style"). core/root.py now
calls root.has_material_styles(new) like every other dependency,
fixing the test failure and dropping the ifcopenshell.util.element
import that was the only consumer of the ifcopenshell import at
module load in core/root.py.
* core/tool.py: add abstract has_material_styles to Root interface.
* tool/root.py: add concrete classmethod near assign_body_styles.
* core/root.py: replace _has_material_styles helper call site with
root.has_material_styles; drop the local helper and its import.
* test/core/test_root.py: add the new mock expectation
root.has_material_styles("element").will_return(False) before the
existing assign_body_styles expectation.
Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -20,8 +20,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
import ifcopenshell.util.element
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
@@ -58,31 +56,12 @@ def copy_class(
|
|||||||
geometry.change_object_data(obj, data, is_global=True)
|
geometry.change_object_data(obj, data, is_global=True)
|
||||||
geometry.rename_object(data, geometry.get_representation_name(ifc.get_entity(data)))
|
geometry.rename_object(data, geometry.get_representation_name(ifc.get_entity(data)))
|
||||||
# Only assign styles if element doesn't get them from material
|
# Only assign styles if element doesn't get them from material
|
||||||
if not _has_material_styles(ifc, new):
|
if not root.has_material_styles(new):
|
||||||
root.assign_body_styles(new, obj)
|
root.assign_body_styles(new, obj)
|
||||||
collector.assign(obj)
|
collector.assign(obj)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|
||||||
def _has_material_styles(ifc: type[tool.Ifc], element: ifcopenshell.entity_instance) -> bool:
|
|
||||||
"""Check if element has styles defined through its material.
|
|
||||||
|
|
||||||
Returns True if any constituent material has a style representation,
|
|
||||||
which means styles should NOT be applied directly to the geometry.
|
|
||||||
"""
|
|
||||||
materials = ifcopenshell.util.element.get_materials(element)
|
|
||||||
|
|
||||||
if not materials:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Check if any of the constituent materials have styles
|
|
||||||
for material in materials:
|
|
||||||
if hasattr(material, "HasRepresentation") and material.HasRepresentation:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def assign_class(
|
def assign_class(
|
||||||
ifc: type[tool.Ifc],
|
ifc: type[tool.Ifc],
|
||||||
collector: type[tool.Collector],
|
collector: type[tool.Collector],
|
||||||
|
|||||||
@@ -884,6 +884,7 @@ class Root:
|
|||||||
def get_object_name(cls, obj): pass
|
def get_object_name(cls, obj): pass
|
||||||
def get_object_representation(cls, obj): pass
|
def get_object_representation(cls, obj): pass
|
||||||
def get_representation_context(cls, representation): pass
|
def get_representation_context(cls, representation): pass
|
||||||
|
def has_material_styles(cls, element): pass
|
||||||
def is_containable(cls, element): pass
|
def is_containable(cls, element): pass
|
||||||
def is_drawing_annotation(cls, element): pass
|
def is_drawing_annotation(cls, element): pass
|
||||||
def is_element_a(cls, element, ifc_class): pass
|
def is_element_a(cls, element, ifc_class): pass
|
||||||
|
|||||||
@@ -71,6 +71,18 @@ class Root(bonsai.core.tool.Root):
|
|||||||
should_use_presentation_style_assignment=props.should_use_presentation_style_assignment,
|
should_use_presentation_style_assignment=props.should_use_presentation_style_assignment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_material_styles(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||||
|
"""``True`` if any constituent material on ``element`` carries a style
|
||||||
|
representation. Body styles should NOT be applied directly when this
|
||||||
|
is True — the material-inherited style is the authoritative source.
|
||||||
|
Paired with ``assign_body_styles``: callers check this first and only
|
||||||
|
call ``assign_body_styles`` when it returns False."""
|
||||||
|
materials = ifcopenshell.util.element.get_materials(element)
|
||||||
|
if not materials:
|
||||||
|
return False
|
||||||
|
return any(getattr(m, "HasRepresentation", None) for m in materials)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def copy_representation(
|
def copy_representation(
|
||||||
cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance
|
cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ class TestCopyClass:
|
|||||||
ifc.get_entity("data").should_be_called().will_return("new_representation")
|
ifc.get_entity("data").should_be_called().will_return("new_representation")
|
||||||
geometry.get_representation_name("new_representation").should_be_called().will_return("name")
|
geometry.get_representation_name("new_representation").should_be_called().will_return("name")
|
||||||
geometry.rename_object("data", "name").should_be_called()
|
geometry.rename_object("data", "name").should_be_called()
|
||||||
|
root.has_material_styles("element").should_be_called().will_return(False)
|
||||||
root.assign_body_styles("element", "obj").should_be_called()
|
root.assign_body_styles("element", "obj").should_be_called()
|
||||||
collector.assign("obj").should_be_called()
|
collector.assign("obj").should_be_called()
|
||||||
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
||||||
|
|||||||
Reference in New Issue
Block a user