OverviewFilesCommitsIssuesDocsReleasesPeers

hardops / test_bool.py

105 lines · python ref:
import bpy, sys, math, gpu

gpu.init()
bpy.ops.preferences.addon_enable(module='bl_ext.user_default.hardops')

from bl_ext.user_default.hardops.utility import object as hops_object
from bl_ext.user_default.hardops.utility import addon
from bl_ext.user_default.hardops.utility import geometry_nodes
from bl_ext.user_default.hardops.ui_framework.operator_ui import Master

# headless has no context.area, so the operator's viewport HUD can't draw
Master.draw = lambda self, *args, **kwargs: None

print("\nRNA_INPUTS:", geometry_nodes.compat.RNA_INPUTS)

# --- the reported crash path: get_auto_smooth ---
bpy.ops.mesh.primitive_cube_add(size=2)
target = bpy.context.active_object
target.name = 'target'
bpy.ops.object.shade_auto_smooth(angle=math.radians(37))

print("get_auto_smooth:", hops_object.get_auto_smooth(target))
assert hops_object.get_auto_smooth(target)[0] is True
assert abs(math.degrees(hops_object.get_auto_smooth(target)[1]) - 37) < 0.01

hops_object.set_auto_smooth(target, True, angle=math.radians(50))
assert abs(math.degrees(hops_object.get_auto_smooth(target)[1]) - 50) < 0.01
print("set_auto_smooth OK")

# --- the operator that crashed, on both workflows ---
def run_difference(workflow_modifier):
    addon.preference().property.workflow_modifier = workflow_modifier

    bpy.ops.mesh.primitive_cube_add(size=1, location=(0.5, 0, 0))
    cutter = bpy.context.active_object
    cutter.name = 'cutter_' + workflow_modifier

    bpy.ops.object.select_all(action='DESELECT')
    cutter.select_set(True)
    target.select_set(True)
    bpy.context.view_layer.objects.active = target

    # execute() skips invoke(), which is what normally sets self.operator
    result = bpy.ops.hops.bool_difference(operator='DIFFERENCE')
    print(f"\nbool_difference [{workflow_modifier}] ->", result)
    assert result == {'FINISHED'}, result
    return cutter

run_difference('CLASSIC')
cutter = run_difference('NODES')

# --- the cut actually evaluates, before any round-trip mutation ---
dg = bpy.context.evaluated_depsgraph_get()
me = target.evaluated_get(dg).to_mesh()
print("evaluated verts:", len(me.vertices), "polys:", len(me.polygons))
assert len(me.polygons) > 6, 'boolean did not cut'

# --- inspect the geometry nodes boolean wrapper ---
for mod in target.modifiers:
    wrapper = geometry_nodes.NodeBoolean.new(mod)
    if not wrapper:
        continue

    print("\nHOPS node boolean:", mod.name)
    print("  operation:", wrapper.operation)
    print("  object   :", wrapper.object)
    print("  solver   :", wrapper.solver)
    print("  inset_thickness:", wrapper.inset_thickness)
    print("  weld:", wrapper.weld, "merge_threshold:", wrapper.merge_threshold)

    assert wrapper.operation == 'DIFFERENCE', wrapper.operation
    assert wrapper.object is cutter, wrapper.object

    for op in ('INTERSECT', 'UNION', 'DIFFERENCE', 'INSET', 'KNIFE'):
        wrapper.operation = op
        assert wrapper.operation == op, (op, wrapper.operation)
    wrapper.operation = 'DIFFERENCE'

    for solver in ('FLOAT', 'EXACT', 'MANIFOLD'):
        wrapper.solver = solver
        assert wrapper.solver == solver, (solver, wrapper.solver)

    wrapper.inset_thickness = 0.25
    assert abs(wrapper.inset_thickness - 0.25) < 1e-6
    wrapper.weld = True
    assert wrapper.weld is True
    wrapper.tag_faces_name = 'hops_tag'
    assert wrapper.tag_faces_name == 'hops_tag'
    wrapper.material = bpy.data.materials.new('probe_mat')
    assert wrapper.material.name == 'probe_mat'
    print("  round trips OK")
    break
else:
    raise AssertionError('no HOPS node boolean modifier found on target')

# --- prop_draw_data, used by the bevel helper panel ---
data, prop = geometry_nodes.NodeBoolean.prop_draw_data(mod, 'solver')
print("\nprop_draw_data(solver):", data, prop, '->', getattr(data, prop))
assert data.bl_rna.properties[prop] is not None

data, prop = geometry_nodes.NodeBoolean.prop_draw_data(mod, 'name')
assert (data, prop) == (mod, 'name'), (data, prop)

print("\nALL CHECKS PASSED")