Skip to content

Commit 70bf933

Browse files
TMHSDigitalclaude
andcommitted
fix(examples): free hay-bale's bmesh on every path; per-run depsgraph-export OBJ
hay-bale (#401): a measurement helper built a bmesh inside `try: ... finally: pass`, so its early `return []` leaked the BMesh and the later bm.free() was unprotected; the --stray-vert falsifier path did from_mesh/to_mesh/free with no try at all, the literal "WRONG" example of the always-free-bmesh rule. Both now free in finally. BVHTree.FromBMesh copies the geometry, so freeing before the ray casts is safe: the piece's full output is byte-identical before and after. depsgraph-export (#404): the export wrote gettempdir()/depsgraph_export.obj and never removed it, so a previous run's OBJ could satisfy the "file exists" check when an export wrote nothing (and Blender points TMPDIR at the working directory). It now uses a pid-suffixed name and deletes the .obj and .mtl before and after. Closes #404 Refs #401 Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 192e1b1 commit 70bf933

2 files changed

Lines changed: 41 additions & 24 deletions

File tree

‎examples/depsgraph-export/depsgraph_export.py‎

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -369,22 +369,36 @@ def check(shell, obj_path, unevaluated=False):
369369
V, E, F, S = len(me.vertices), len(me.edges), len(me.polygons), len(me.loops)
370370
expected = catmull_clark_vcount(V, E, F, S, SUBSURF_LEVELS)
371371

372-
out = obj_path or os.path.join(tempfile.gettempdir(), "depsgraph_export.obj")
372+
# A per-run name, removed before and after: a fixed name let a previous
373+
# run's OBJ satisfy the "file exists" check below when this export wrote
374+
# nothing, and Blender points TMPDIR at the working directory (#404).
375+
out = obj_path or os.path.join(tempfile.gettempdir(),
376+
f"depsgraph_export_{os.getpid()}.obj")
373377
os.makedirs(os.path.dirname(os.path.abspath(out)) or ".", exist_ok=True)
374-
# obj_export writes the evaluated (modifier-applied) geometry by default
375-
bpy.ops.wm.obj_export(
376-
filepath=out,
377-
export_selected_objects=False,
378-
apply_modifiers=not unevaluated,
379-
)
380-
if not (os.path.exists(out) and os.path.getsize(out) > 0):
381-
print("ERROR: no OBJ written", file=sys.stderr)
382-
return 4
383-
exported = 0
384-
with open(out, encoding="utf-8") as f:
385-
for line in f:
386-
if line.startswith("v "):
387-
exported += 1
378+
sidecars = [out, os.path.splitext(out)[0] + ".mtl"]
379+
for path in sidecars:
380+
if os.path.exists(path):
381+
os.remove(path)
382+
try:
383+
# obj_export writes the evaluated (modifier-applied) geometry by default
384+
bpy.ops.wm.obj_export(
385+
filepath=out,
386+
export_selected_objects=False,
387+
apply_modifiers=not unevaluated,
388+
)
389+
if not (os.path.exists(out) and os.path.getsize(out) > 0):
390+
print("ERROR: no OBJ written", file=sys.stderr)
391+
return 4
392+
exported = 0
393+
with open(out, encoding="utf-8") as f:
394+
for line in f:
395+
if line.startswith("v "):
396+
exported += 1
397+
finally:
398+
if not obj_path:
399+
for path in sidecars:
400+
if os.path.exists(path):
401+
os.remove(path)
388402

389403
print(f"shell_cage V={V} E={E} F={F} S={S} levels={SUBSURF_LEVELS} "
390404
f"shell_eval_vcount={shell_eval} closed_form={expected}")

‎showcase/hay-bale/hay_bale.py‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,11 @@ def belt_seat_depths(me, loaf_comp, belt_comp, station_x, segs):
453453
pass
454454
if not bm.faces:
455455
return []
456+
# FromBMesh copies the geometry into the tree, so the BMesh can be
457+
# freed here, on the early return and the normal path alike.
456458
bvh = BVHTree.FromBMesh(bm)
457459
finally:
458-
pass
460+
bm.free()
459461
zs = [me.vertices[i].co.z for i in loaf_comp]
460462
cz = 0.5 * (min(zs) + max(zs))
461463
buckets = {}
@@ -498,7 +500,6 @@ def depth(co):
498500
vals = [d for d in (depth(co) for co in buckets[k]) if d is not None]
499501
if vals:
500502
depths.append(max(vals))
501-
bm.free()
502503
return depths
503504

504505

@@ -1142,13 +1143,15 @@ def check(skip_decimate, lift_z=False, stray_vert=False, slack_belt=False,
11421143
low.data.update()
11431144
if stray_vert:
11441145
bm = bmesh.new()
1145-
bm.from_mesh(low.data)
1146-
# Inside the existing AABB: a stray vertex parked above the bale
1147-
# blew the bounding-box budget (exit 8) before the hygiene gate
1148-
# could see it, so it proved nothing about hygiene.
1149-
bm.verts.new((0.0, 0.0, BALE_Z * 0.5))
1150-
bm.to_mesh(low.data)
1151-
bm.free()
1146+
try:
1147+
bm.from_mesh(low.data)
1148+
# Inside the existing AABB: a stray vertex parked above the bale
1149+
# blew the bounding-box budget (exit 8) before the hygiene gate
1150+
# could see it, so it proved nothing about hygiene.
1151+
bm.verts.new((0.0, 0.0, BALE_Z * 0.5))
1152+
bm.to_mesh(low.data)
1153+
finally:
1154+
bm.free()
11521155
low.data.update()
11531156

11541157
if low.data is None or len(low.data.polygons) < 6:

0 commit comments

Comments
 (0)