Compare commits

...

1 Commits

Author SHA1 Message Date
Petru Conduraru dfefb5377c ifcopenshell: attach owning file to geom.tree select results #3189
tree.select and tree.select_box wrapped their C++ results as
entity_instance(e) with no file, so wrapped_data.file was None. That forces
entity_instance.__eq__ down the slow recursive get_info_2 attribute
comparison instead of the fast step-id path, and drops the gc-keepalive the
file reference provides. The underlying instances carry a valid file_pointer(),
so resolve the owning file via file.from_pointer() and attach it to each
result.

Verified with the compiled wrapper on basic.ifc: before, wrapped_data.file is
None and equality takes the slow path (2 get_info_2 calls); after, the file is
attached, equality/hash use the fast path (0 slow calls), for both select and
select_box.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:34:38 +03:00
@@ -370,6 +370,17 @@ CLASH_TYPE_ITEMS = ("protrusion", "pierce", "collision", "clearance")
class tree(ifcopenshell_wrapper.tree):
@staticmethod
def _wrap(e: ifcopenshell_wrapper.entity_instance) -> entity_instance:
inst = entity_instance(e)
# #3189 attach the owning file so that equality and hashing of
# selected instances use the fast step-id based path instead of
# falling back to a slow recursive attribute comparison.
ptr = e.file_pointer()
if ptr:
inst.wrapped_data.file = file.from_pointer(ptr)
return inst
def __init__(self, file: Optional[file] = None, settings: Optional[settings] = None):
args = [self]
if file is not None:
@@ -412,7 +423,7 @@ class tree(ifcopenshell_wrapper.tree):
args.append(kwargs.get("completely_within", False))
if "extend" in kwargs:
args.append(kwargs["extend"])
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select(*args)]
return [self._wrap(e) for e in ifcopenshell_wrapper.tree.select(*args)]
def select_box(self, value, **kwargs) -> list[entity_instance]:
def unwrap(value):
@@ -427,7 +438,7 @@ class tree(ifcopenshell_wrapper.tree):
args.append(kwargs.get("completely_within", False))
if "extend" in kwargs:
args.append(kwargs.get("extend", -1.0e-5))
return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select_box(*args)]
return [self._wrap(e) for e in ifcopenshell_wrapper.tree.select_box(*args)]
def clash_intersection_many(
self,