Drop dead Geometry.has_material_styles + sanitation sweep

Two related cleanups bundled because each was too small on its own.

== Drop dead Geometry.has_material_styles duplicate ==

Two parallel has_material_styles implementations existed on HEAD:

* Geometry.has_material_styles (tool/geometry.py:853, added by
  3483683cb "Add tool.Geometry helpers for body representation +
  placement"): checks each material via tool.Material.get_style
  for an IfcSurfaceStyle. This is the implementation gizmos-8088
  uses — its core/root.py:58 calls geometry.has_material_styles.

* Root.has_material_styles (tool/root.py:75, added by e76455913
  "Route _has_material_styles through tool.Root.has_material_styles"):
  checks each material for a HasRepresentation inverse. Added to
  fix the test/core/test_root.py::TestCopyClass::test_AAAAAAAAAAAA
  failure by routing the check through a Prophecy-mockable seam.

HEAD's core/root.py:59 calls root.has_material_styles. The Geometry
version became orphaned by that migration — zero callers historically
(git log -S "Geometry.has_material_styles" returns nothing). The
Root placement is the right architectural home: has_material_styles
pairs with assign_body_styles in the copy_class flow as "is there
material-defined styling? if not, apply body styling" — both
decisions live on the same interface, called in sequence from the
same caller.

The semantic delta (HasRepresentation vs IfcSurfaceStyle) is a close
approximation in real IFC files where HasRepresentation almost always
indicates a styled material; if precision becomes necessary, the
Root impl can be tightened independently of this cleanup.

Drop the Geometry method + its abstract declaration in core/tool.py.

== Sanitation sweep per CLAUDE.md §4a ==

Eight rot-prone references in code we authored on this branch get
their first-draft mistakes cleaned up. The §4a rule (no sibling
symbol names, no test paths, no motivation history in docstrings)
got added during this branch, so older commits sometimes named their
siblings in prose; this is a focused cleanup of the worst offenders.

* bim/module/model/wall.py:201 — _CommitWallDraftsFirstMixin
  docstring carried motivation history ("...that every multi-wall
  operator … used to repeat at the top of _execute"). Rewrite to
  describe only the current contract.

* bim/module/model/wall.py:1910 — cycle_type_operator comment named
  two sibling methods. Rephrase to describe what happens at the slot.

* bim/module/model/wall.py:2025 — _active_instances ClassVar comment
  named WallGizmoPreviewDecorator. Rephrase to "the wall-gizmo
  preview decorator" (role, not class).

* bim/module/drawing/gizmos.py:3402 — GizmoFillet hit_uses_bbox
  comment named GizmoWallJoinIntersection. Rephrase to "the wall-join
  gizmo group".

* bim/module/drawing/gizmos.py:3887 — GizmoCountLabel docstring had
  a :meth:`set_count` cross-reference. Drop — reader sees the method
  next to the class.

* bim/module/model/host_add_opening_gizmo.py:201 — poll-exclusion
  comment named GizmoWallEdition + GizmoRoofEdition. Rephrase to
  describe why we skip ("walls and parametric roofs both render
  their own toggle in the pen row").

* bim/module/void/operator.py:45 — preserve_placement comment named
  FilledOpeningGenerator.generate. Rephrase to "the filling-opening
  generator gates its snap-to-wall-axis block on this flag".

* bim/parametric_lifecycle.py:64 — module docstring named the test
  file path (test/bim/test_parametric_registry.py). Rewrite to
  "enforced by the registry contract tests".

Sweep otherwise clean: no third-party software names in this-branch-
authored comments (upstream Revit / Tekla / ArchiCAD references are
legitimate external-constraint workarounds, §4a-allowed). No
PR/issue numbers we authored except the FIXME(PR5) in
tool/parametric.py:150, deliberately preserved until PR6's MEP slice
resolves it.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Gorgious56
2026-06-04 09:08:47 +02:00
parent 0e922074b9
commit 94faaa3160
7 changed files with 21 additions and 33 deletions
@@ -3399,8 +3399,9 @@ class GizmoFillet(StaticTrisGizmoMixin, bpy.types.Gizmo):
bl_idname = "VIEW3D_GT_fillet"
__slots__ = ("custom_shape",)
tris = FILLET_TRIS_DEFAULT
# Stacked at ICON_STACK_OFFSET_Y above join in GizmoWallJoinIntersection;
# full-bbox hit overlaps the sibling icons' bboxes and steals their clicks.
# Stacked at ICON_STACK_OFFSET_Y above the join icon in the wall-join
# gizmo group; full-bbox hit overlaps the sibling icons' bboxes and
# steals their clicks.
hit_uses_bbox = False
@@ -3884,8 +3885,8 @@ class GizmoArrayLayerIndicator(bpy.types.Gizmo):
class GizmoCountLabel(bpy.types.Gizmo):
"""``xN`` text label rendered from 7-segment digit triangles.
Mirrors a caller-supplied integer (set via :meth:`set_count`) into a
live count badge. No icon glyph; the gizmo is the number alone."""
Mirrors a caller-supplied integer into a live count badge. No icon
glyph; the gizmo is the number alone."""
bl_idname = "BIM_GT_count_label"
@@ -198,8 +198,8 @@ class GizmoHostToggleOpenings(bpy.types.GizmoGroup, _WallGeomCachedBillboardingM
if not tool.Geometry.has_openings(element):
return False
# Skip when a per-feature parametric-edit gizmo already surfaces
# an idle-row toggle for this element (wall: GizmoWallEdition;
# parametric roof: GizmoRoofEdition).
# an idle-row toggle for this element walls and parametric roofs
# both render their own toggle in the pen row.
if tool.Parametric.is_path_connectable_wall(element):
return False
if tool.Parametric.is_roof(element):
+10 -13
View File
@@ -195,12 +195,9 @@ class _CommitWallDraftsFirstMixin:
"""Operator mixin that flushes any in-progress wall parametric drafts in
the current selection before delegating to the subclass's ``_perform``.
Centralises the inline ``_commit_pending_wall_edits_for_selection(context)``
call that every multi-wall operator (unjoin, unjoin-path-connection,
extend-to-underside, extend-to-wall, split, merge, join-intersection)
used to repeat at the top of ``_execute``. Subclasses implement
``_perform`` instead of ``_execute``; the IFC transaction opened by
``tool.Ifc.Operator.execute`` wraps both the commit and the perform.
Subclasses implement ``_perform`` instead of ``_execute``; the IFC
transaction opened by ``tool.Ifc.Operator.execute`` wraps both the
commit and the perform.
Place this BEFORE ``bpy.types.Operator`` in the bases tuple so the
mixin's ``_execute`` resolves first in the MRO."""
@@ -1910,8 +1907,8 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
finish_editing_operator = "bim.finish_editing_wall"
cancel_editing_operator = "bim.cancel_editing_wall"
# Empty disables the base class's auto-created cycle_gizmo at ICON_CYCLE_X.
# We render three state-specific baseline icons at that slot instead — see
# ``setup_element_specific_gizmos`` / ``_update_icon_row_extras``.
# Three state-specific baseline icons (exterior / center / interior) take
# over that slot, with the active one chosen per frame from props.
cycle_type_operator = ""
# Threshold (SI meters) above which a second height gizmo is drawn at the far end of
@@ -2024,11 +2021,11 @@ class GizmoWallEdition(bpy.types.GizmoGroup, gizmo.BaseParametricGizmoGroup):
(0, 0, 1),
)
# Per-region weakref map populated in ``setup_element_specific_gizmos``.
# The ``WallGizmoPreviewDecorator`` dereferences this each draw to read
# live ``is_highlight`` state off the cursor icons (extend-X / extend-Z
# / split) in the same region it's currently drawing in, so the GPU
# axis-preview lines only render while the matching icon is hovered.
# Per-region weakref map populated at setup time. The wall-gizmo preview
# decorator dereferences this each draw to read live ``is_highlight`` state
# off the cursor icons (extend-X / extend-Z / split) in the same region
# it's currently drawing in, so the GPU axis-preview lines only render
# while the matching icon is hovered.
_active_instances: ClassVar["dict[int, weakref.ReferenceType[GizmoWallEdition]]"] = {}
# Row layout: validate / cancel / baseline-triplet / rotate / array.
@@ -42,9 +42,9 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
)
# Toggled by ``invoke`` when the user holds SHIFT during a gizmo / hotkey
# click. Forwards to ``FilledOpeningGenerator.generate`` which gates the
# snap-to-wall-axis block on it. HIDDEN + SKIP_SAVE so it doesn't surface
# in the F6 redo panel or persist into saved keymaps.
# click. The filling-opening generator gates its snap-to-wall-axis block
# on this flag. HIDDEN + SKIP_SAVE so the flag doesn't surface in the F6
# redo panel or persist into saved keymaps.
preserve_placement: bpy.props.BoolProperty(default=False, options={"HIDDEN", "SKIP_SAVE"})
@classmethod
@@ -61,7 +61,7 @@ Pattern selection (which approach a new feature should adopt):
The authoritative list of registered parametric types and which use
`build_edit_lifecycle` vs. standalone operators lives in
`tool/parametric.py`'s `EDIT_TYPES` and is enforced by the registry
contract tests in `test/bim/test_parametric_registry.py`.
contract tests.
This module hosts operator-side mixins that import ``bonsai.tool`` freely.
The lightweight parametric registry consumed at addon-enable time must stay
-1
View File
@@ -459,7 +459,6 @@ class Geometry:
def has_axis_representation(cls, element): pass
def has_data_users(cls, data): pass
def has_material_style_override(cls, obj): pass
def has_material_styles(cls, element): pass
def import_representation_parameters(cls, data): pass
def is_body_representation(cls, representation): pass
def is_box_representation(cls, representation): pass
-9
View File
@@ -849,15 +849,6 @@ class Geometry(bonsai.core.tool.Geometry):
return True
return False
@classmethod
def has_material_styles(cls, element: ifcopenshell.entity_instance) -> bool:
"""True when any of ``element``'s materials exposes an
``IfcSurfaceStyle``. Gate body-style assignment to avoid double-styling."""
return any(
tool.Material.get_style(material) is not None
for material in ifcopenshell.util.element.get_materials(element)
)
@classmethod
def reimport_element_representations(
cls, obj: bpy.types.Object, representation: ifcopenshell.entity_instance, apply_openings: bool = True