mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Bonsai: keep linked models cut linework in BISECT cut mode
BISECT cut mode deletes the serializer cut linework and regenerates it by bisecting Blender mesh objects, which linked models do not have - their cuts were deleted and never regenerated, so linked elements only appeared as projections and the .cut CSS rule never applied to them. remove_cut_linework now only removes cut groups whose guid resolves in the host file, keeping the serializer cut geometry for linked models. Resolving a linked entity STEP id via tool.Ifc.get_object cross-matches into the host session and can return an arbitrary host object (e.g. the drawing camera), so generate_material_layers and the linework merge now guard on element.file identity. BISECT mode also runs move_projection_to_bottom like OPENCASCADE mode: its own bisect cuts are appended last, but the retained serializer cuts of linked models are emitted before the projections and would paint underneath. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -190,6 +190,35 @@ stay compatible.
|
||||
- Verified headless with the window/door kit: moved window offset in the SVG by
|
||||
exactly 5m × scale; unmoved door at its native position; both links present.
|
||||
|
||||
### Drawings — `.cut` styling for linked models (BISECT cut mode)
|
||||
|
||||
- The default **BISECT** cut mode deletes the OpenCASCADE serializer's cut linework
|
||||
(`remove_cut_linework`) and regenerates cuts by bisecting **Blender mesh objects**
|
||||
(`generate_bisect_linework` over `context.visible_objects`). Linked models are
|
||||
instanced collections with no mesh objects, so their cuts were deleted and never
|
||||
regenerated — linked elements only ever appeared as `projection`, and the `.cut`
|
||||
CSS rule never applied to them. Long-standing gap, unrelated to moved links
|
||||
(A/B-tested against pre-branch code: identical).
|
||||
- Fix: `remove_cut_linework` only removes cut groups whose guid resolves in the
|
||||
**host** file — linked elements keep the serializer's cut geometry, which the
|
||||
merge step then classes as `cut`.
|
||||
- **Cross-file STEP-id collision**: `tool.Ifc.get_object(linked_entity)` resolves the
|
||||
entity's STEP id against the *host* session's id map and can return an arbitrary
|
||||
host object (in the test project: the drawing camera, crashing
|
||||
`generate_material_layers` with "expected 'Mesh' found 'Camera'"). Guarded via
|
||||
`element.file is tool.Ifc.get()` in `generate_material_layers` and the merge step.
|
||||
- **Paint order**: the projection-under-cut convention was enforced only in
|
||||
OPENCASCADE mode (`move_projection_to_bottom`); BISECT appends its own cut paths
|
||||
last so it never needed it — but the retained serializer cuts of linked models are
|
||||
emitted *before* the projections. BISECT now runs the same pass; `BringToFront`
|
||||
(`move_elements_to_top`) still gets the final say.
|
||||
- Known limitation: linked cut paths are raw serializer output — they skip the
|
||||
shapely path-closing/merging and the material-layer hatching pass (both need host
|
||||
Blender objects). Stroke + fill from `.cut` CSS apply; layered hatching inside
|
||||
linked cuts is a candidate follow-up.
|
||||
- Debugging note: merged cut groups carry member guids as CSS *classes*, not as the
|
||||
`ifcopenshell:guid` attribute — inspect both when checking cut output.
|
||||
|
||||
## Review round 1 (PR #8242, falken10vdl) — decisions
|
||||
|
||||
- **Path-form mismatch → duplicate documents (confirmed bug, fixed).**
|
||||
@@ -231,9 +260,10 @@ Plus:
|
||||
|
||||
- `ee43ed5526` review-round path normalization in `get_linked_models_documents` /
|
||||
`LinkIfc` (see Review round 1).
|
||||
- Drawing support for moved links and per-link queries in `create_drawing`
|
||||
(`drawing/operator.py`, `tool/project.py`) — committed together with this note
|
||||
update.
|
||||
- `1669cbcd43` drawing support for moved links and per-link queries in
|
||||
`create_drawing` (`drawing/operator.py`, `tool/project.py`).
|
||||
- `.cut` styling for linked models in BISECT cut mode + STEP-id collision guards +
|
||||
paint order (`drawing/operator.py`) — committed together with this note update.
|
||||
|
||||
End-to-end verified with a two-links-one-file kit (window/door, distinct queries):
|
||||
correct visuals on load, after save → reopen → reload, in both headless and windowed
|
||||
|
||||
@@ -682,6 +682,10 @@ class CreateDrawing(bpy.types.Operator):
|
||||
if "projection" in el.get("class", "").split():
|
||||
continue
|
||||
element = self.get_element_by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||
if element is None or element.file is not tool.Ifc.get():
|
||||
# Linked model element - no Blender object to bisect, and its
|
||||
# STEP id must not be resolved against the host session.
|
||||
continue
|
||||
if not (obj := tool.Ifc.get_object(element)):
|
||||
continue
|
||||
if not (material := ifcopenshell.util.element.get_material(element)):
|
||||
@@ -1054,6 +1058,10 @@ class CreateDrawing(bpy.types.Operator):
|
||||
if self.cprops.generate_material_layers:
|
||||
self.generate_material_layers(context, root)
|
||||
self.merge_linework_and_add_metadata(root)
|
||||
# Bisect cut linework is appended after the projections, but the
|
||||
# retained serializer cuts of linked models precede them - enforce
|
||||
# the projection-under-cut convention like OPENCASCADE mode does.
|
||||
self.move_projection_to_bottom(root)
|
||||
self.move_elements_to_top(root)
|
||||
elif self.cprops.cut_mode == "OPENCASCADE":
|
||||
self.move_projection_to_bottom(root)
|
||||
@@ -1449,9 +1457,21 @@ class CreateDrawing(bpy.types.Operator):
|
||||
continue
|
||||
|
||||
def remove_cut_linework(self, root):
|
||||
"""Remove host elements' cut linework so bisecting can regenerate it.
|
||||
|
||||
Linked model elements keep the serializer's cut geometry - bisect
|
||||
linework is generated from Blender mesh objects, and linked models
|
||||
are instanced collections without any.
|
||||
"""
|
||||
ifc_file = tool.Ifc.get()
|
||||
for el in root.findall(".//{http://www.w3.org/2000/svg}g[@{http://www.ifcopenshell.org/ns}guid]"):
|
||||
if "projection" not in el.get("class", "").split():
|
||||
el.getparent().remove(el)
|
||||
if "projection" in el.get("class", "").split():
|
||||
continue
|
||||
try:
|
||||
ifc_file.by_guid(el.get("{http://www.ifcopenshell.org/ns}guid"))
|
||||
except RuntimeError:
|
||||
continue # Linked model element.
|
||||
el.getparent().remove(el)
|
||||
|
||||
def merge_linework_and_add_metadata(self, root):
|
||||
join_criteria = ifcopenshell.util.element.get_pset(self.camera_element, "EPset_Drawing", "JoinCriteria")
|
||||
@@ -1486,7 +1506,9 @@ class CreateDrawing(bpy.types.Operator):
|
||||
classes.append("cut")
|
||||
el.set("class", " ".join(classes))
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
# Resolving a linked element's STEP id against the host session
|
||||
# would return an arbitrary host object.
|
||||
obj = tool.Ifc.get_object(element) if element is not None and element.file is tool.Ifc.get() else None
|
||||
|
||||
if not obj: # This is a linked model object. For now, do nothing.
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user