mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
Bonsai: fix profile reconstruction after duplicating a circle/arc in Edit Mode
Duplicating a circular or filleted-arc void in the profile CAD editor
(Shift+D on the loop's vertices) reused the same IFCCIRCLE/IFCARCINDEX
vertex group index for the new geometry, since Blender's mesh duplicate
copies vertex group weights but does not allocate a new group. On exit
from Edit Mode, auto_detect_profiles tallied group membership across the
whole mesh rather than per loop, so a group meant to hold exactly 2 (circle)
or 3 (arc) vertices ended up with double that, failing its sanity check
and blocking the edit with an "INVALID PROFILE" popup. Fixes #6944.
Scope the sanity check to each connected edge loop instead, matching how
the loops are actually converted into IfcCircle/arc segments below. Also
explicitly reject an arc/circle vertex tagged onto an isolated vertex with
no edges at all, which the old whole-mesh count also caught.
Verified live in headless Blender against the issue's repro file
(IfcFurniture "Slab.004", IfcArbitraryProfileDefWithVoids with three
IfcCircle voids): entering the profile editor, duplicating one void's
2-vertex loop and moving it produced an "INVALID PROFILE" popup before
this change, and now produces a valid profile (the original 3 voids
intact, plus the duplicate as a 4th void or a separate solid profile
depending on whether it still falls inside the outer boundary).
test/tool/test_model.py passes unchanged (32 passed, 1 pre-existing
unrelated failure present on both before and after).
Generated with the assistance of an AI coding tool.
(cherry picked from commit 0a027d47a3)
This commit is contained in:
committed by
Dion Moult
parent
a8ae234039
commit
f0f4e0d4ca
@@ -2292,32 +2292,18 @@ class Model(bonsai.core.tool.Model):
|
|||||||
deform_layer = bm.verts.layers.deform.active
|
deform_layer = bm.verts.layers.deform.active
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
group_verts = {"IFCARCINDEX": {}, "IFCCIRCLE": {}}
|
|
||||||
if deform_layer:
|
if deform_layer:
|
||||||
for vert in bm.verts:
|
for vert in bm.verts:
|
||||||
vert_group_indices = tool.Blender.bmesh_get_vertex_groups(vert, deform_layer)
|
vert_group_indices = tool.Blender.bmesh_get_vertex_groups(vert, deform_layer)
|
||||||
is_circle = False
|
is_circle = any(gi in groups["IFCCIRCLE"] for gi in vert_group_indices)
|
||||||
for group_index in vert_group_indices:
|
is_arc = any(gi in groups["IFCARCINDEX"] for gi in vert_group_indices)
|
||||||
group_type = "IFCARCINDEX" if group_index in groups["IFCARCINDEX"] else "IFCCIRCLE"
|
if (is_circle or is_arc) and not vert.link_edges:
|
||||||
group_verts[group_type].setdefault(group_index, 0)
|
return (False, "CIRCLE" if is_circle else "3POINT_ARC")
|
||||||
group_verts[group_type][group_index] += 1
|
|
||||||
if group_type == "IFCCIRCLE":
|
|
||||||
is_circle = True
|
|
||||||
if is_circle:
|
if is_circle:
|
||||||
pass # Circles are allowed to be unclosed
|
pass # Circles are allowed to be unclosed
|
||||||
elif len(vert.link_edges) != 2: # Unclosed loop or forked loop
|
elif len(vert.link_edges) != 2: # Unclosed loop or forked loop
|
||||||
return (False, "UNCLOSED_LOOP")
|
return (False, "UNCLOSED_LOOP")
|
||||||
|
|
||||||
for group_type, group_counts in group_verts.items():
|
|
||||||
if group_type == "IFCARCINDEX":
|
|
||||||
for group_count in group_counts.values():
|
|
||||||
if group_count != 3: # Each arc needs 3 verts
|
|
||||||
return (False, "3POINT_ARC")
|
|
||||||
elif group_type == "IFCCIRCLE":
|
|
||||||
for group_count in group_counts.values():
|
|
||||||
if group_count != 2: # Each circle needs 2 verts
|
|
||||||
return (False, "CIRCLE")
|
|
||||||
|
|
||||||
loop_edges = list(bm.edges)
|
loop_edges = list(bm.edges)
|
||||||
|
|
||||||
# Create loops from edges
|
# Create loops from edges
|
||||||
@@ -2340,6 +2326,28 @@ class Model(bonsai.core.tool.Model):
|
|||||||
has_found_connected_edge = True
|
has_found_connected_edge = True
|
||||||
loops.append(loop)
|
loops.append(loop)
|
||||||
|
|
||||||
|
# Sanity check, per loop rather than across the whole mesh
|
||||||
|
if deform_layer:
|
||||||
|
for loop in loops:
|
||||||
|
loop_group_counts = {"IFCARCINDEX": {}, "IFCCIRCLE": {}}
|
||||||
|
loop_verts = {v for edge in loop for v in edge.verts}
|
||||||
|
for vert in loop_verts:
|
||||||
|
for group_index in tool.Blender.bmesh_get_vertex_groups(vert, deform_layer):
|
||||||
|
if group_index in groups["IFCARCINDEX"]:
|
||||||
|
group_type = "IFCARCINDEX"
|
||||||
|
elif group_index in groups["IFCCIRCLE"]:
|
||||||
|
group_type = "IFCCIRCLE"
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
loop_group_counts[group_type].setdefault(group_index, 0)
|
||||||
|
loop_group_counts[group_type][group_index] += 1
|
||||||
|
for group_count in loop_group_counts["IFCARCINDEX"].values():
|
||||||
|
if group_count != 3: # Each arc needs 3 verts
|
||||||
|
return (False, "3POINT_ARC")
|
||||||
|
for group_count in loop_group_counts["IFCCIRCLE"].values():
|
||||||
|
if group_count != 2: # Each circle needs 2 verts
|
||||||
|
return (False, "CIRCLE")
|
||||||
|
|
||||||
tmp = ifcopenshell.file(schema=tool.Ifc.get().schema)
|
tmp = ifcopenshell.file(schema=tool.Ifc.get().schema)
|
||||||
|
|
||||||
def is_in_group(v: bmesh.types.BMVert, group_name: str) -> bool:
|
def is_in_group(v: bmesh.types.BMVert, group_name: str) -> bool:
|
||||||
|
|||||||
Reference in New Issue
Block a user