"""Integration test in real Blender, including geometry edits, pruning and undo. blender -b --factory-startup --python tests/blender_roundtrip.py Writes a verified demonstration and report to out/. """ import importlib.util import sys import tempfile from pathlib import Path import bpy root = Path(__file__).resolve().parents[1] sys.path.insert(0, str(root)) from spatial_lab.examples import demo_project from spatial_lab.project import Project, write_json spec = importlib.util.spec_from_file_location('bridge', root / 'blender/bridge.py') bridge = importlib.util.module_from_spec(spec) spec.loader.exec_module(bridge) scene = bridge.create_scene('Spatial Lab / Braided atrium') bpy.context.window.scene = scene with tempfile.TemporaryDirectory() as temporary: path = Path(temporary) / 'study.json' write_json(path, demo_project()) project = Project(path) original = project.build() bridge.import_bundle(original, scene) original_check = bridge.verify_bundle(original, scene) assert original_check['passed'], original_check obj = next(o for o in scene.objects if o.get('spatial_lab_id') == 'braided-galleries/0000/main') pointer = obj.as_pointer() original_material = obj.material_slots[0].material modifier = obj.modifiers.new('Artist bevel', 'BEVEL') artist_material = bpy.data.materials.new('Artist material') obj.material_slots[0].material = artist_material marker = bpy.data.objects.new('Unrelated object', None) scene.collection.objects.link(marker) changed = project.apply([ {'action': 'set_parameter', 'name': 'width', 'value': 1.8}, {'action': 'set_parameter', 'name': 'levels', 'value': 2}, ]) assert changed['geometries'] != original['geometries'] updated = bridge.import_bundle(changed, scene) changed_check = bridge.verify_bundle(changed, scene) assert changed_check['passed'], changed_check assert updated['created'] == 0 and updated['updated'] == 16 and len(updated['removed']) == 1 assert obj.as_pointer() == pointer and obj.modifiers.get(modifier.name).as_pointer() == modifier.as_pointer() assert obj.material_slots[0].material == artist_material and marker.name in scene.objects restored = project.travel('undo') assert restored == original bridge.import_bundle(restored, scene) undo_check = bridge.verify_bundle(restored, scene) assert undo_check['passed'], undo_check assert obj.as_pointer() == pointer and obj.material_slots[0].material == artist_material obj.modifiers.remove(modifier) obj.material_slots[0].material = original_material bpy.data.objects.remove(marker, do_unlink=True) bpy.data.materials.remove(artist_material) out = root / 'out' write_json(out / 'braided_atrium.bundle.json', restored) write_json(out / 'blender_verification.json', { 'blender_version': bpy.app.version_string, 'initial': original_check, 'edited': changed_check, 'undo': undo_check, 'changed_import': updated, 'preserved_object_identity_modifiers_materials_and_unrelated_objects': True, 'undo_restored_identical_bundle': True, }) from mathutils import Vector target = Vector((0, 0, 4.5)) rotation = (target - Vector((45, -55, 35))).to_track_quat('-Z', 'Y') for screen in bpy.data.screens: for area in screen.areas: if area.type == 'VIEW_3D': view = area.spaces.active bridge.enum_set(view.shading, 'type', 'SOLID') bridge.enum_set(view.shading, 'color_type', 'MATERIAL') view.region_3d.view_location = target view.region_3d.view_distance = 62 view.region_3d.view_rotation = rotation bpy.ops.wm.save_as_mainfile(filepath=str(out / 'Braided_Atrium.blend')) print('PASS: changed geometry, pruned instance, preserved artist work, exact undo, Blender parity.')