Capture M16 gap execution artifacts
This commit is contained in:
72
tools/web/check-action-change-frame-desktop.py
Normal file
72
tools/web/check-action-change-frame-desktop.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChangeFrameObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChangeFrameObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-change-frame-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
if bpy.context.scene.frame_current != 4:
|
||||
raise RuntimeError(f"unexpected anim.change_frame frame: {bpy.context.scene.frame_current}")
|
||||
if before["name"] != "WebGapAnimChangeFrameObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.change_frame action: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-change-frame-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
frame_after = bpy.context.scene.frame_current
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after or frame_after != 4:
|
||||
raise RuntimeError("anim.change_frame save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00150",
|
||||
"operation": "ANIM_CHANGE_FRAME_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"changedToFrame": 4,
|
||||
"currentFrame": frame_after,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-change-frame-desktop-ok changedToFrame=4 channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
69
tools/web/check-action-channel-select-keys-desktop.py
Normal file
69
tools/web/check-action-channel-select-keys-desktop.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelSelectKeysObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelSelectKeysObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channel-select-keys-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [[True, True, True], [False, False, False], [False, False, False]]
|
||||
if before["name"] != "WebGapAnimChannelSelectKeysObjectAction" or [channel["selected"] for channel in before["channels"]] != expected:
|
||||
raise RuntimeError(f"unexpected anim.channel_select_keys result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channel-select-keys-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channel_select_keys save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00151",
|
||||
"operation": "ANIM_CHANNEL_SELECT_KEYS_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedChannel": {"path": "location", "index": 0},
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channel-select-keys-desktop-ok selected=location[0] channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
104
tools/web/check-action-channel-view-pick-desktop.py
Normal file
104
tools/web/check-action-channel-view-pick-desktop.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelViewPickObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelViewPickObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def view_report():
|
||||
area = None
|
||||
for screen in bpy.data.screens:
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.spaces.active.ui_mode == "ACTION" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is not None:
|
||||
break
|
||||
if area is None:
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
region = next((candidate for candidate in area.regions if candidate.type == "WINDOW"), None)
|
||||
if region is None:
|
||||
raise RuntimeError("Action Editor window region is missing")
|
||||
xmin, ymin = region.view2d.region_to_view(0, 0)
|
||||
xmax, ymax = region.view2d.region_to_view(region.width - 1, region.height - 1)
|
||||
return {
|
||||
"cur": {
|
||||
"xmin": round(float(xmin), 6),
|
||||
"xmax": round(float(xmax), 6),
|
||||
"ymin": round(float(ymin), 6),
|
||||
"ymax": round(float(ymax), 6),
|
||||
},
|
||||
"mask": {
|
||||
"xmin": 0,
|
||||
"xmax": int(region.width - 1),
|
||||
"ymin": 0,
|
||||
"ymax": int(region.height - 1),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def report_state():
|
||||
return {"action": action_report(), "view2d": view_report()}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channel-view-pick-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
before = report_state()
|
||||
if before["action"]["name"] != "WebGapAnimChannelViewPickObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channel_view_pick action: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channel-view-pick-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
after = report_state()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channel_view_pick save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00152",
|
||||
"operation": "ANIM_CHANNEL_VIEW_PICK_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"action": after["action"],
|
||||
"view2d": after["view2d"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channel-view-pick-desktop-ok view2d=exact saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
69
tools/web/check-action-channels-bake-desktop.py
Normal file
69
tools/web/check-action-channels-bake-desktop.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsBakeObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsBakeObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-bake-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 2.0, 3.0, 4.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsBakeObjectAction" or len(before["channels"]) != 3 or any(channel["frames"] != expected for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_bake result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-bake-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_bake save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00153",
|
||||
"operation": "ANIM_CHANNELS_BAKE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"bakeRange": [1, 5],
|
||||
"step": 1.0,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-bake-desktop-ok range=1..5 step=1 channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
78
tools/web/check-action-channels-clean-empty-desktop.py
Normal file
78
tools/web/check-action-channels-clean-empty-desktop.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsCleanKeepObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsCleanKeepObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def state_report():
|
||||
empty = bpy.data.objects.get("WebGapAnimChannelsCleanEmptyObject")
|
||||
keep = bpy.data.objects.get("WebGapAnimChannelsCleanKeepObject")
|
||||
if empty is None or keep is None:
|
||||
raise RuntimeError("clean-empty fixture objects are missing")
|
||||
return {
|
||||
"emptyAnimData": empty.animation_data is not None,
|
||||
"keepAction": action_report(),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-clean-empty-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = state_report()
|
||||
if before["emptyAnimData"] or len(before["keepAction"]["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_clean_empty result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-clean-empty-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = state_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_clean_empty save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00154",
|
||||
"operation": "ANIM_CHANNELS_CLEAN_EMPTY_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"emptyAnimDataRemoved": True,
|
||||
"keepAction": after["keepAction"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-clean-empty-desktop-ok removedEmpty=true keepChannels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
69
tools/web/check-action-channels-click-desktop.py
Normal file
69
tools/web/check-action-channels-click-desktop.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsClickObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsClickObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-click-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [[True, True, True], [False, False, False], [False, False, False]]
|
||||
if before["name"] != "WebGapAnimChannelsClickObjectAction" or [channel["selected"] for channel in before["channels"]] != expected:
|
||||
raise RuntimeError(f"unexpected anim.channels_click result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-click-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_click save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00155",
|
||||
"operation": "ANIM_CHANNELS_CLICK_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"clickedChannel": {"path": "location", "index": 0},
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-click-desktop-ok selected=location[0] channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
tools/web/check-action-channels-collapse-desktop.py
Normal file
68
tools/web/check-action-channels-collapse-desktop.py
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsCollapseObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsCollapseObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-collapse-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsCollapseObjectAction" or len(before["channels"]) != 3 or any(channel["frames"] != expected for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_collapse result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-collapse-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_collapse save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00156",
|
||||
"operation": "ANIM_CHANNELS_COLLAPSE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"collapsedAll": True,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-collapse-desktop-ok collapsedAll=true channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
tools/web/check-action-channels-delete-desktop.py
Normal file
68
tools/web/check-action-channels-delete-desktop.py
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsDeleteObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsDeleteObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-delete-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
if before["name"] != "WebGapAnimChannelsDeleteObjectAction" or before["channels"]:
|
||||
raise RuntimeError(f"unexpected anim.channels_delete result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-delete-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_delete save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00157",
|
||||
"operation": "ANIM_CHANNELS_DELETE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"deletedChannel": {"path": "location", "index": 0},
|
||||
"remainingChannels": 0,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-delete-desktop-ok deleted=location[0] remaining=2 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
tools/web/check-action-channels-editable-toggle-desktop.py
Normal file
71
tools/web/check-action-channels-editable-toggle-desktop.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsEditableToggleObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsEditableToggleObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"editable": not curve.lock,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-editable-toggle-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsEditableToggleObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_editable_toggle result: {before}")
|
||||
if any(channel["frames"] != expected or channel["editable"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels were not toggled non-editable: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-editable-toggle-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_editable_toggle save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00158",
|
||||
"operation": "ANIM_CHANNELS_EDITABLE_TOGGLE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"toggledChannels": 3,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-editable-toggle-desktop-ok toggled=3 editable=false saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
74
tools/web/check-action-channels-expand-desktop.py
Normal file
74
tools/web/check-action-channels-expand-desktop.py
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsExpandObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsExpandObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
groups = []
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
groups.extend({"name": group.name, "expanded": group.show_expanded} for group in bag.groups)
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"group": curve.group.name if curve.group else None,
|
||||
})
|
||||
groups.sort(key=lambda value: value["name"])
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "groups": groups, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-expand-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsExpandObjectAction" or before["groups"] != [{"name": "Object Transforms", "expanded": True}]:
|
||||
raise RuntimeError(f"unexpected anim.channels_expand groups: {before}")
|
||||
if len(before["channels"]) != 3 or any(channel["frames"] != expected or channel["group"] != "Object Transforms" for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_expand channels: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-expand-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_expand save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00159",
|
||||
"operation": "ANIM_CHANNELS_EXPAND_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"expandedGroups": 1,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-expand-desktop-ok expanded=Object Transforms saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
tools/web/check-action-channels-fcurves-enable-desktop.py
Normal file
71
tools/web/check-action-channels-fcurves-enable-desktop.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsFCurvesEnableObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsFCurvesEnableObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"enabled": curve.is_valid,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-fcurves-enable-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsFCurvesEnableObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_fcurves_enable result: {before}")
|
||||
if any(channel["frames"] != expected or not channel["enabled"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"F-Curves were not enabled: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-fcurves-enable-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_fcurves_enable save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00160",
|
||||
"operation": "ANIM_CHANNELS_FCURVES_ENABLE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"enabledChannels": 3,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-fcurves-enable-desktop-ok enabled=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
tools/web/check-action-channels-group-desktop.py
Normal file
73
tools/web/check-action-channels-group-desktop.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsGroupObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsGroupObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
groups = []
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
groups.extend(group.name for group in bag.groups)
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"group": curve.group.name if curve.group else None,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "groups": sorted(groups), "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-group-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsGroupObjectAction" or before["groups"] != ["WebGapTransforms"]:
|
||||
raise RuntimeError(f"unexpected anim.channels_group groups: {before}")
|
||||
if len(before["channels"]) != 3 or any(channel["frames"] != expected or channel["group"] != "WebGapTransforms" for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_group channels: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-group-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_group save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00161",
|
||||
"operation": "ANIM_CHANNELS_GROUP_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"groups": 1,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-group-desktop-ok group=WebGapTransforms channels=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
69
tools/web/check-action-channels-move-desktop.py
Normal file
69
tools/web/check-action-channels-move-desktop.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsMoveObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsMoveObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-move-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsMoveObjectAction" or [channel["index"] for channel in before["channels"]] != [1, 0, 2]:
|
||||
raise RuntimeError(f"unexpected anim.channels_move order: {before}")
|
||||
if any(channel["frames"] != expected for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_move frames: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-move-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_move save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00162",
|
||||
"operation": "ANIM_CHANNELS_MOVE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"direction": "DOWN",
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-move-desktop-ok direction=DOWN order=1,0,2 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
81
tools/web/check-action-channels-rename-desktop.py
Normal file
81
tools/web/check-action-channels-rename-desktop.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsRenameObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsRenameObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"enabled": curve.is_valid,
|
||||
})
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-rename-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
bpy.context.view_layer.update()
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsRenameObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channels_rename action: {before}")
|
||||
if [channel["path"] for channel in before["channels"]] != [
|
||||
"rotation_euler", "location_missing", "location_missing"
|
||||
]:
|
||||
raise RuntimeError(f"unexpected anim.channels_rename paths: {before}")
|
||||
if [channel["index"] for channel in before["channels"]] != [0, 1, 2]:
|
||||
raise RuntimeError(f"unexpected anim.channels_rename indices: {before}")
|
||||
if any(channel["frames"] != expected for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_rename frames: {before}")
|
||||
# Blender recomputes the transient F-Curve error flag while loading a
|
||||
# file, so the persisted evidence is the renamed RNA path and keyframes.
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-rename-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
bpy.context.view_layer.update()
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_rename save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00163",
|
||||
"operation": "ANIM_CHANNELS_RENAME_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"renamedChannel": {"from": "location_missing", "to": "rotation_euler", "index": 0},
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-rename-desktop-ok path=rotation_euler index=0 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
tools/web/check-action-channels-select-all-desktop.py
Normal file
80
tools/web/check-action-channels-select-all-desktop.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSelectAllObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSelectAllObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": bool(curve.select),
|
||||
"selectedKeyframes": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-select-all-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsSelectAllObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channels_select_all action: {before}")
|
||||
if [channel["path"] for channel in before["channels"]] != ["location", "location", "location"]:
|
||||
raise RuntimeError(f"unexpected anim.channels_select_all paths: {before}")
|
||||
if [channel["index"] for channel in before["channels"]] != [0, 1, 2]:
|
||||
raise RuntimeError(f"unexpected anim.channels_select_all indices: {before}")
|
||||
if any(channel["frames"] != expected for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_select_all frames: {before}")
|
||||
if not all(channel["selected"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"not all animation channels were selected: {before}")
|
||||
if any(any(channel["selectedKeyframes"]) for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels_select_all changed keyframe selection: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-select-all-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_select_all save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00164",
|
||||
"operation": "ANIM_CHANNELS_SELECT_ALL_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selection": "SELECT",
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-select-all-desktop-ok selected=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
82
tools/web/check-action-channels-select-box-desktop.py
Normal file
82
tools/web/check-action-channels-select-box-desktop.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSelectBoxObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSelectBoxObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": bool(curve.select),
|
||||
"selectedKeyframes": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-select-box-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected_frames = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsSelectBoxObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channels_select_box action: {before}")
|
||||
if [channel["path"] for channel in before["channels"]] != ["location", "location", "location"]:
|
||||
raise RuntimeError(f"unexpected anim.channels_select_box paths: {before}")
|
||||
if [channel["index"] for channel in before["channels"]] != [0, 1, 2]:
|
||||
raise RuntimeError(f"unexpected anim.channels_select_box indices: {before}")
|
||||
if any(channel["frames"] != expected_frames for channel in before["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_select_box frames: {before}")
|
||||
if not all(channel["selected"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"not all animation channels were selected: {before}")
|
||||
if any(any(channel["selectedKeyframes"]) for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels_select_box changed keyframe selection: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-select-box-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_select_box save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00165",
|
||||
"operation": "ANIM_CHANNELS_SELECT_BOX_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selection": "SELECT",
|
||||
"selectedChannels": len(after["channels"]),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-select-box-desktop-ok selected=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
84
tools/web/check-action-channels-select-filter-desktop.py
Normal file
84
tools/web/check-action-channels-select-filter-desktop.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSelectFilterObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSelectFilterObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def filter_text():
|
||||
for screen in bpy.data.screens:
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.spaces.active.ui_mode == "ACTION"),
|
||||
None,
|
||||
)
|
||||
if area is not None:
|
||||
return area.spaces.active.dopesheet.filter_fcurve_name
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-select-filter-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
before = {"filterText": filter_text(), "action": action_report()}
|
||||
if before["filterText"] != "Location":
|
||||
raise RuntimeError(f"unexpected anim.channels_select_filter text: {before}")
|
||||
if before["action"]["name"] != "WebGapAnimChannelsSelectFilterObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channels_select_filter action: {before}")
|
||||
if len(before["action"]["channels"]) != 3 or any(channel["frames"] != [1.0, 3.0, 5.0] for channel in before["action"]["channels"]):
|
||||
raise RuntimeError(f"unexpected anim.channels_select_filter channels: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-select-filter-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
after = {"filterText": filter_text(), "action": action_report()}
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_select_filter save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00166",
|
||||
"operation": "ANIM_CHANNELS_SELECT_FILTER_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"filterText": after["filterText"],
|
||||
"action": after["action"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-select-filter-desktop-ok filter=Location saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
tools/web/check-action-channels-setting-disable-desktop.py
Normal file
73
tools/web/check-action-channels-setting-disable-desktop.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSettingDisableObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSettingDisableObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"editable": not curve.lock,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-setting-disable-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsSettingDisableObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_setting_disable action: {before}")
|
||||
if any(channel["frames"] != expected or channel["editable"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels were not disabled/protected: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-setting-disable-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_setting_disable save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00167",
|
||||
"operation": "ANIM_CHANNELS_SETTING_DISABLE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"setting": "PROTECT",
|
||||
"disabledChannels": len(after["channels"]),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-setting-disable-desktop-ok setting=PROTECT disabled=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
tools/web/check-action-channels-setting-enable-desktop.py
Normal file
73
tools/web/check-action-channels-setting-enable-desktop.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSettingEnableObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSettingEnableObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"editable": not curve.lock,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-setting-enable-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsSettingEnableObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_setting_enable action: {before}")
|
||||
if any(channel["frames"] != expected or channel["editable"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels were not enabled/protected: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-setting-enable-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_setting_enable save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00168",
|
||||
"operation": "ANIM_CHANNELS_SETTING_ENABLE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"setting": "PROTECT",
|
||||
"enabledChannels": len(after["channels"]),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-setting-enable-desktop-ok setting=PROTECT enabled=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
73
tools/web/check-action-channels-setting-toggle-desktop.py
Normal file
73
tools/web/check-action-channels-setting-toggle-desktop.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsSettingToggleObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsSettingToggleObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"editable": not curve.lock,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-setting-toggle-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsSettingToggleObjectAction" or len(before["channels"]) != 3:
|
||||
raise RuntimeError(f"unexpected anim.channels_setting_toggle action: {before}")
|
||||
if any(channel["frames"] != expected or channel["editable"] for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels were not toggled to protected: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-setting-toggle-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_setting_toggle save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00169",
|
||||
"operation": "ANIM_CHANNELS_SETTING_TOGGLE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"setting": "PROTECT",
|
||||
"toggledChannels": len(after["channels"]),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-setting-toggle-desktop-ok setting=PROTECT toggled=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
75
tools/web/check-action-channels-ungroup-desktop.py
Normal file
75
tools/web/check-action-channels-ungroup-desktop.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsUngroupObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsUngroupObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
groups = []
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
groups.extend(group.name for group in bag.groups)
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"group": curve.group.name if curve.group else None,
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "groups": sorted(groups), "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-ungroup-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["name"] != "WebGapAnimChannelsUngroupObjectAction" or before["groups"]:
|
||||
raise RuntimeError(f"unexpected anim.channels_ungroup groups: {before}")
|
||||
if len(before["channels"]) != 3 or any(channel["frames"] != expected or channel["group"] is not None for channel in before["channels"]):
|
||||
raise RuntimeError(f"channels were not ungrouped: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-ungroup-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_ungroup save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00170",
|
||||
"operation": "ANIM_CHANNELS_UNGROUP_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"groups": len(after["groups"]),
|
||||
"ungroupedChannels": len(after["channels"]),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-ungroup-desktop-ok groups=0 ungrouped=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
111
tools/web/check-action-channels-view-selected-desktop.py
Normal file
111
tools/web/check-action-channels-view-selected-desktop.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report(action):
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": bool(curve.select),
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def view_report():
|
||||
area = None
|
||||
for screen in bpy.data.screens:
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.spaces.active.ui_mode == "ACTION"),
|
||||
None,
|
||||
)
|
||||
if area is not None:
|
||||
break
|
||||
if area is None:
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
region = next((candidate for candidate in area.regions if candidate.type == "WINDOW"), None)
|
||||
if region is None:
|
||||
raise RuntimeError("Action Editor window region is missing")
|
||||
view = region.view2d
|
||||
xmin, ymin = view.region_to_view(0, 0)
|
||||
xmax, ymax = view.region_to_view(region.width - 1, region.height - 1)
|
||||
return {
|
||||
"areaType": area.type,
|
||||
"uiMode": area.spaces.active.ui_mode,
|
||||
"view2d": {
|
||||
"cur": {
|
||||
"xmin": round(float(xmin), 6),
|
||||
"xmax": round(float(xmax), 6),
|
||||
"ymin": round(float(ymin), 6),
|
||||
"ymax": round(float(ymax), 6),
|
||||
},
|
||||
"mask": {
|
||||
"xmin": 0,
|
||||
"xmax": int(region.width - 1),
|
||||
"ymin": 0,
|
||||
"ymax": int(region.height - 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-channels-view-selected-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
obj = bpy.data.objects.get("WebGapAnimChannelsViewSelectedObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapAnimChannelsViewSelectedObject Action is missing")
|
||||
before = {"action": action_report(obj.animation_data.action), **view_report()}
|
||||
expected = [1.0, 3.0, 5.0]
|
||||
if before["action"]["name"] != "WebGapAnimChannelsViewSelectedObjectAction":
|
||||
raise RuntimeError(f"unexpected anim.channels_view_selected action: {before}")
|
||||
if len(before["action"]["channels"]) != 3 or any(channel["frames"] != expected or not channel["selected"] for channel in before["action"]["channels"]):
|
||||
raise RuntimeError(f"channels were not selected: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-channels-view-selected-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
reopened_obj = bpy.data.objects.get("WebGapAnimChannelsViewSelectedObject")
|
||||
after = {"action": action_report(reopened_obj.animation_data.action), **view_report()}
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.channels_view_selected save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00171",
|
||||
"operation": "ANIM_CHANNELS_VIEW_SELECTED_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedChannels": len(after["action"]["channels"]),
|
||||
"action": after["action"],
|
||||
"view2d": after["view2d"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-channels-view-selected-desktop-ok selected=3 view2d=exact saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
90
tools/web/check-action-clear-useless-actions-desktop.py
Normal file
90
tools/web/check-action-clear-useless-actions-desktop.py
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report(action):
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {
|
||||
"name": action.name,
|
||||
"users": action.users,
|
||||
"fakeUser": bool(action.use_fake_user),
|
||||
"channels": channels,
|
||||
}
|
||||
|
||||
|
||||
def state_report():
|
||||
actions = {
|
||||
action.name: action_report(action)
|
||||
for action in bpy.data.actions
|
||||
if action.name.startswith("WebGapAnimClearUseless")
|
||||
}
|
||||
used = bpy.data.objects.get("WebGapAnimClearUselessUsedObject")
|
||||
if used is None or used.animation_data is None or used.animation_data.action is None:
|
||||
raise RuntimeError("used Action is missing")
|
||||
return {"actions": actions, "usedAction": used.animation_data.action.name}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-clear-useless-actions-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = state_report()
|
||||
expected = {
|
||||
"WebGapAnimClearUselessUsedAction",
|
||||
"WebGapAnimClearUselessLibraryAction",
|
||||
}
|
||||
if set(before["actions"]) != expected:
|
||||
raise RuntimeError(f"clear_useless_actions did not remove only the empty Action: {before}")
|
||||
if before["usedAction"] != "WebGapAnimClearUselessUsedAction":
|
||||
raise RuntimeError(f"used Action drifted: {before}")
|
||||
if any(len(value["channels"]) != 3 for value in before["actions"].values()):
|
||||
raise RuntimeError(f"preserved Actions have unexpected curves: {before}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-clear-useless-actions-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = state_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.clear_useless_actions save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00172",
|
||||
"operation": "ANIM_CLEAR_USELESS_ACTIONS_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"removedEmptyAction": True,
|
||||
"actions": after["actions"],
|
||||
"usedAction": after["usedAction"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-clear-useless-actions-desktop-ok removedEmpty=true preserved=2 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
99
tools/web/check-action-copy-driver-button-desktop.py
Normal file
99
tools/web/check-action-copy-driver-button-desktop.py
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def driver_report(obj):
|
||||
if obj is None or obj.animation_data is None:
|
||||
raise RuntimeError("copy-driver fixture animation data is missing")
|
||||
drivers = []
|
||||
for curve in obj.animation_data.drivers:
|
||||
driver = curve.driver
|
||||
drivers.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"expression": driver.expression if driver else "",
|
||||
"type": driver.type if driver else "",
|
||||
"variableCount": len(driver.variables) if driver else 0,
|
||||
})
|
||||
drivers.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return drivers
|
||||
|
||||
|
||||
def state_report():
|
||||
obj = bpy.data.objects.get("WebGapAnimCopyDriverButtonObject")
|
||||
if obj is None:
|
||||
raise RuntimeError("copy-driver fixture object is missing")
|
||||
return {"drivers": driver_report(obj), "value": round(float(obj["drive_target"]), 6)}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-copy-driver-button-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = state_report()
|
||||
expected = [{
|
||||
"path": '["drive_target"]',
|
||||
"index": 0,
|
||||
"expression": "frame * 2.0 + 1.0",
|
||||
"type": "SCRIPTED",
|
||||
"variableCount": 0,
|
||||
}]
|
||||
if before["drivers"] != expected:
|
||||
raise RuntimeError(f"unexpected copy_driver_button source driver: {before}")
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "PROPERTIES")
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
result = bpy.ops.anim.copy_driver_button()
|
||||
if result != {"CANCELLED"}:
|
||||
raise RuntimeError(f"unexpected ANIM_OT_copy_driver_button result: {result}")
|
||||
after_cancel = state_report()
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"copy_driver_button cancellation changed Main data: {before} != {after_cancel}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-copy-driver-button-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = state_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.copy_driver_button save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00173",
|
||||
"operation": "ANIM_COPY_DRIVER_BUTTON_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"drivers": after["drivers"],
|
||||
"value": after["value"],
|
||||
"operatorStatus": "CANCELLED",
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(f"anim-copy-driver-button-desktop-ok drivers=1 status={report['operatorStatus']} mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"anim-copy-driver-button-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
98
tools/web/check-action-driver-button-add-desktop.py
Normal file
98
tools/web/check-action-driver-button-add-desktop.py
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def driver_report(obj):
|
||||
if obj is None or obj.animation_data is None:
|
||||
return []
|
||||
drivers = []
|
||||
for curve in obj.animation_data.drivers:
|
||||
driver = curve.driver
|
||||
drivers.append(
|
||||
{
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"expression": driver.expression if driver else "",
|
||||
"type": driver.type if driver else "",
|
||||
"variableCount": len(driver.variables) if driver else 0,
|
||||
}
|
||||
)
|
||||
drivers.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return drivers
|
||||
|
||||
|
||||
def state_report(obj):
|
||||
if obj is None:
|
||||
raise RuntimeError("driver-button-add fixture object is missing")
|
||||
return {"drivers": driver_report(obj), "value": round(float(obj["drive_target"]), 6)}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit(
|
||||
"usage: blender -b --python check-action-driver-button-add-desktop.py -- FIXTURE REPORT"
|
||||
)
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
output.unlink(missing_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
obj = bpy.data.objects.get("WebGapAnimDriverButtonAddObject")
|
||||
before = state_report(obj)
|
||||
if before != {"drivers": [], "value": 4.5}:
|
||||
raise RuntimeError(f"unexpected driver_button_add source state: {before}")
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "PROPERTIES")
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
poll = bool(bpy.ops.anim.driver_button_add.poll())
|
||||
if poll:
|
||||
raise RuntimeError("ANIM_OT_driver_button_add unexpectedly polled true without an active RNA button")
|
||||
after_cancel = state_report(obj)
|
||||
if after_cancel != before:
|
||||
raise RuntimeError(f"driver_button_add cancellation changed Main data: {before} != {after_cancel}")
|
||||
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-anim-driver-button-add-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = state_report(bpy.data.objects.get("WebGapAnimDriverButtonAddObject"))
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError("anim.driver_button_add save/reopen drift")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00174",
|
||||
"operation": "ANIM_DRIVER_BUTTON_ADD_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"drivers": after["drivers"],
|
||||
"value": after["value"],
|
||||
"poll": poll,
|
||||
"operatorStatus": "CANCELLED",
|
||||
"mainMutation": "NONE",
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("anim-driver-button-add-desktop-ok poll=false status=CANCELLED mainMutation=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print(f"anim-driver-button-add-desktop-failed: {error}")
|
||||
raise SystemExit(1)
|
||||
71
tools/web/check-action-select-leftright-desktop.py
Normal file
71
tools/web/check-action-select-leftright-desktop.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapSelectLeftRightObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapSelectLeftRightObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-select-leftright-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
if before["name"] != "WebGapSelectLeftRightObjectAction" or any(
|
||||
channel["selected"] != [True, True, False] for channel in before["channels"]
|
||||
):
|
||||
raise RuntimeError(f"unexpected action.select_leftright result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-select-leftright-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.select_leftright save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00139",
|
||||
"operation": "ACTION_SELECT_LEFTRIGHT_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"mode": "LEFT",
|
||||
"selectedFrames": [1, 3],
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-select-leftright-desktop-ok channels=3 mode=left selectedFrames=1,3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
70
tools/web/check-action-select-less-desktop.py
Normal file
70
tools/web/check-action-select-less-desktop.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapSelectLessObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapSelectLessObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-select-less-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
if before["name"] != "WebGapSelectLessObjectAction" or any(
|
||||
channel["selected"] != [False, True, False] for channel in before["channels"]
|
||||
):
|
||||
raise RuntimeError(f"unexpected action.select_less result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-select-less-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.select_less save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00140",
|
||||
"operation": "ACTION_SELECT_LESS_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedFrame": 3,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-select-less-desktop-ok channels=3 selectedFrame=3 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
70
tools/web/check-action-select-linked-desktop.py
Normal file
70
tools/web/check-action-select-linked-desktop.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapSelectLinkedObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapSelectLinkedObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-select-linked-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [[True, True, True], [True, True, True], [True, True, True]]
|
||||
if before["name"] != "WebGapSelectLinkedObjectAction" or [channel["selected"] for channel in before["channels"]] != expected:
|
||||
raise RuntimeError(f"unexpected action.select_linked result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-select-linked-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.select_linked save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00141",
|
||||
"operation": "ACTION_SELECT_LINKED_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedChannel": "location[0]",
|
||||
"selectedFrames": [1, 3, 5],
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-select-linked-desktop-ok channels=3 selectedChannel=location[0] selectedFrames=1,3,5 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
70
tools/web/check-action-select-more-desktop.py
Normal file
70
tools/web/check-action-select-more-desktop.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapSelectMoreObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapSelectMoreObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-select-more-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected = [[True, True, True], [True, True, True], [True, True, True]]
|
||||
if before["name"] != "WebGapSelectMoreObjectAction" or [channel["selected"] for channel in before["channels"]] != expected:
|
||||
raise RuntimeError(f"unexpected action.select_more result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-select-more-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.select_more save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00142",
|
||||
"operation": "ACTION_SELECT_MORE_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedChannel": "location[0]",
|
||||
"selectedPerChannel": [[1, 3, 5], [1, 3, 5], [1, 3, 5]],
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-select-more-desktop-ok channels=3 selectedChannel=location[0] selectedFrames=1,3,5 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
tools/web/check-action-snap-desktop.py
Normal file
71
tools/web/check-action-snap-desktop.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapSnapObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapSnapObject Action is missing")
|
||||
action = obj.animation_data.action
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return {"name": action.name, "channels": channels}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-snap-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
expected_frames = [[1.0, 4.0, 5.0]] * 3
|
||||
expected_selected = [[False, True, False]] * 3
|
||||
if before["name"] != "WebGapSnapObjectAction" or [channel["frames"] for channel in before["channels"]] != expected_frames or [channel["selected"] for channel in before["channels"]] != expected_selected:
|
||||
raise RuntimeError(f"unexpected action.snap result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-snap-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.snap save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00143",
|
||||
"operation": "ACTION_SNAP_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"type": "CFRA",
|
||||
"currentFrame": 4,
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-snap-desktop-ok channels=3 type=CFRA currentFrame=4 saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
tools/web/check-action-stash-and-create-desktop.py
Normal file
40
tools/web/check-action-stash-and-create-desktop.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def nla_report():
|
||||
obj = bpy.data.objects.get("WebGapStashCreateObject")
|
||||
if obj is None or obj.animation_data is None:
|
||||
raise RuntimeError("WebGapStashCreateObject animation data is missing")
|
||||
tracks = []
|
||||
for track in obj.animation_data.nla_tracks:
|
||||
tracks.append({"name": track.name, "strips": [{"name": strip.name, "action": strip.action.name if strip.action else None, "frameStart": round(float(strip.frame_start), 6), "frameEnd": round(float(strip.frame_end), 6)} for strip in track.strips]})
|
||||
return {"activeAction": obj.animation_data.action.name if obj.animation_data.action else None, "tracks": tracks}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-stash-and-create-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = nla_report()
|
||||
if before["activeAction"] != "Action" or len(before["tracks"]) != 1 or len(before["tracks"][0]["strips"]) != 1:
|
||||
raise RuntimeError(f"unexpected action.stash_and_create result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-stash-create-reopen-", suffix=".blend"); os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True); bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False); after = nla_report()
|
||||
finally: pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after: raise RuntimeError(f"action.stash_and_create save/reopen drift: {before} != {after}")
|
||||
report = {"schemaVersion": 1, "task": "M16-GAP-00145", "operation": "ACTION_STASH_AND_CREATE_DESKTOP", "fixture": str(fixture), "fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(), "nla": after, "saveReopen": "EXACT", "blenderVersion": bpy.app.version_string}
|
||||
output.parent.mkdir(parents=True, exist_ok=True); output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"); print("action-stash-and-create-desktop-ok tracks=1 strips=1 activeAction=Action saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__": main()
|
||||
65
tools/web/check-action-stash-desktop.py
Normal file
65
tools/web/check-action-stash-desktop.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def nla_report():
|
||||
obj = bpy.data.objects.get("WebGapStashObject")
|
||||
if obj is None or obj.animation_data is None:
|
||||
raise RuntimeError("WebGapStashObject animation data is missing")
|
||||
tracks = []
|
||||
for track in obj.animation_data.nla_tracks:
|
||||
tracks.append({
|
||||
"name": track.name,
|
||||
"strips": [{
|
||||
"name": strip.name,
|
||||
"action": strip.action.name if strip.action else None,
|
||||
"frameStart": round(float(strip.frame_start), 6),
|
||||
"frameEnd": round(float(strip.frame_end), 6),
|
||||
} for strip in track.strips],
|
||||
})
|
||||
return {"activeAction": obj.animation_data.action.name if obj.animation_data.action else None, "tracks": tracks}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-stash-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = nla_report()
|
||||
if before["activeAction"] is not None or len(before["tracks"]) != 1 or len(before["tracks"][0]["strips"]) != 1:
|
||||
raise RuntimeError(f"unexpected action.stash result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-stash-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = nla_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.stash save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00144",
|
||||
"operation": "ACTION_STASH_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"nla": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-stash-desktop-ok tracks=1 strips=1 activeAction=none saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
80
tools/web/check-action-unlink-desktop.py
Normal file
80
tools/web/check-action-unlink-desktop.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_channels(action):
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return channels
|
||||
|
||||
|
||||
def action_report():
|
||||
obj = bpy.data.objects.get("WebGapActionUnlinkObject")
|
||||
if obj is None or obj.animation_data is None:
|
||||
raise RuntimeError("WebGapActionUnlinkObject animation data is missing")
|
||||
action = bpy.data.actions.get("WebGapActionUnlinkObjectAction")
|
||||
if action is None:
|
||||
raise RuntimeError("unlinked Action data-block is missing")
|
||||
return {
|
||||
"activeAction": obj.animation_data.action.name if obj.animation_data.action else None,
|
||||
"unlinkedAction": {
|
||||
"name": action.name,
|
||||
"users": action.users,
|
||||
"useFakeUser": action.use_fake_user,
|
||||
"channels": action_channels(action),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-unlink-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=False)
|
||||
before = action_report()
|
||||
if before["activeAction"] is not None or before["unlinkedAction"]["users"] != 1 or not before["unlinkedAction"]["useFakeUser"]:
|
||||
raise RuntimeError(f"unexpected action.unlink result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-unlink-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=False)
|
||||
after = action_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.unlink save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00146",
|
||||
"operation": "ACTION_UNLINK_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-unlink-desktop-ok activeAction=none fakeUser=true saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
81
tools/web/check-action-view-all-desktop.py
Normal file
81
tools/web/check-action-view-all-desktop.py
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_view_report():
|
||||
screen = next((candidate for candidate in bpy.data.screens if candidate.name == "Animation"), bpy.context.screen)
|
||||
area = next((candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR"), None)
|
||||
if area is None:
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
if area.spaces.active.ui_mode != "ACTION":
|
||||
raise RuntimeError(f"unexpected Dopesheet mode: {area.spaces.active.ui_mode}")
|
||||
region = next((candidate for candidate in area.regions if candidate.type == "WINDOW"), None)
|
||||
if region is None:
|
||||
raise RuntimeError("Action Editor window region is missing")
|
||||
view = region.view2d
|
||||
xmin, ymin = view.region_to_view(0, 0)
|
||||
xmax, ymax = view.region_to_view(region.width - 1, region.height - 1)
|
||||
return {
|
||||
"areaType": area.type,
|
||||
"uiMode": area.spaces.active.ui_mode,
|
||||
"view2d": {
|
||||
"cur": {
|
||||
"xmin": round(float(xmin), 6),
|
||||
"xmax": round(float(xmax), 6),
|
||||
"ymin": round(float(ymin), 6),
|
||||
"ymax": round(float(ymax), 6),
|
||||
},
|
||||
"mask": {
|
||||
"xmin": 0,
|
||||
"xmax": int(region.width - 1),
|
||||
"ymin": 0,
|
||||
"ymax": int(region.height - 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-view-all-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
before = action_view_report()
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-view-all-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
after = action_view_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.view_all save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00147",
|
||||
"operation": "ACTION_VIEW_ALL_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(
|
||||
"action-view-all-desktop-ok area=DOPESHEET_EDITOR mode=ACTION "
|
||||
"saveReopen=exact"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
91
tools/web/check-action-view-frame-desktop.py
Normal file
91
tools/web/check-action-view-frame-desktop.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_view_report():
|
||||
screen = None
|
||||
area = None
|
||||
for candidate_screen in bpy.data.screens:
|
||||
candidate_area = next(
|
||||
(candidate for candidate in candidate_screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.spaces.active.ui_mode == "ACTION"),
|
||||
None,
|
||||
)
|
||||
if candidate_area is not None:
|
||||
screen, area = candidate_screen, candidate_area
|
||||
break
|
||||
if area is None:
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
region = next((candidate for candidate in area.regions if candidate.type == "WINDOW"), None)
|
||||
if region is None:
|
||||
raise RuntimeError("Action Editor window region is missing")
|
||||
view = region.view2d
|
||||
xmin, ymin = view.region_to_view(0, 0)
|
||||
xmax, ymax = view.region_to_view(region.width - 1, region.height - 1)
|
||||
return {
|
||||
"areaType": area.type,
|
||||
"uiMode": area.spaces.active.ui_mode,
|
||||
"currentFrame": int(bpy.context.scene.frame_current),
|
||||
"view2d": {
|
||||
"cur": {
|
||||
"xmin": round(float(xmin), 6),
|
||||
"xmax": round(float(xmax), 6),
|
||||
"ymin": round(float(ymin), 6),
|
||||
"ymax": round(float(ymax), 6),
|
||||
},
|
||||
"mask": {
|
||||
"xmin": 0,
|
||||
"xmax": int(region.width - 1),
|
||||
"ymin": 0,
|
||||
"ymax": int(region.height - 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-view-frame-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
before = action_view_report()
|
||||
if before["currentFrame"] != 3:
|
||||
raise RuntimeError(f"unexpected current frame: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-view-frame-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
after = action_view_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.view_frame save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00148",
|
||||
"operation": "ACTION_VIEW_FRAME_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"action": after,
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print(
|
||||
"action-view-frame-desktop-ok area=DOPESHEET_EDITOR mode=ACTION currentFrame=3 "
|
||||
"saveReopen=exact"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
113
tools/web/check-action-view-selected-desktop.py
Normal file
113
tools/web/check-action-view-selected-desktop.py
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def action_channels(action):
|
||||
channels = []
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
channels.append({
|
||||
"path": curve.data_path,
|
||||
"index": curve.array_index,
|
||||
"frames": [round(float(keyframe.co.x), 6) for keyframe in curve.keyframe_points],
|
||||
"selected": [bool(keyframe.select_control_point) for keyframe in curve.keyframe_points],
|
||||
})
|
||||
channels.sort(key=lambda value: (value["path"], value["index"]))
|
||||
return channels
|
||||
|
||||
|
||||
def action_view_report():
|
||||
screen = None
|
||||
area = None
|
||||
for candidate_screen in bpy.data.screens:
|
||||
candidate_area = next(
|
||||
(candidate for candidate in candidate_screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.spaces.active.ui_mode == "ACTION"),
|
||||
None,
|
||||
)
|
||||
if candidate_area is not None:
|
||||
screen, area = candidate_screen, candidate_area
|
||||
break
|
||||
if area is None:
|
||||
raise RuntimeError("Action Editor area is missing")
|
||||
region = next((candidate for candidate in area.regions if candidate.type == "WINDOW"), None)
|
||||
if region is None:
|
||||
raise RuntimeError("Action Editor window region is missing")
|
||||
view = region.view2d
|
||||
xmin, ymin = view.region_to_view(0, 0)
|
||||
xmax, ymax = view.region_to_view(region.width - 1, region.height - 1)
|
||||
obj = bpy.data.objects.get("WebGapActionViewSelectedObject")
|
||||
if obj is None or obj.animation_data is None or obj.animation_data.action is None:
|
||||
raise RuntimeError("WebGapActionViewSelectedObject Action is missing")
|
||||
return {
|
||||
"areaType": area.type,
|
||||
"uiMode": area.spaces.active.ui_mode,
|
||||
"action": {
|
||||
"name": obj.animation_data.action.name,
|
||||
"channels": action_channels(obj.animation_data.action),
|
||||
},
|
||||
"view2d": {
|
||||
"cur": {
|
||||
"xmin": round(float(xmin), 6),
|
||||
"xmax": round(float(xmax), 6),
|
||||
"ymin": round(float(ymin), 6),
|
||||
"ymax": round(float(ymax), 6),
|
||||
},
|
||||
"mask": {
|
||||
"xmin": 0,
|
||||
"xmax": int(region.width - 1),
|
||||
"ymin": 0,
|
||||
"ymax": int(region.height - 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 2:
|
||||
raise SystemExit("usage: blender -b --python check-action-view-selected-desktop.py -- FIXTURE REPORT")
|
||||
fixture, output = (pathlib.Path(value).resolve() for value in arguments)
|
||||
bpy.ops.wm.open_mainfile(filepath=str(fixture), load_ui=True)
|
||||
before = action_view_report()
|
||||
expected = [[False, True, False]] * 3
|
||||
if before["action"]["name"] != "WebGapActionViewSelectedObjectAction" or [channel["selected"] for channel in before["action"]["channels"]] != expected:
|
||||
raise RuntimeError(f"unexpected action.view_selected result: {before}")
|
||||
descriptor, temporary = tempfile.mkstemp(prefix="m16-action-view-selected-reopen-", suffix=".blend")
|
||||
os.close(descriptor)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath=temporary, check_existing=False, compress=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=temporary, load_ui=True)
|
||||
after = action_view_report()
|
||||
finally:
|
||||
pathlib.Path(temporary).unlink(missing_ok=True)
|
||||
if before != after:
|
||||
raise RuntimeError(f"action.view_selected save/reopen drift: {before} != {after}")
|
||||
report = {
|
||||
"schemaVersion": 1,
|
||||
"task": "M16-GAP-00149",
|
||||
"operation": "ACTION_VIEW_SELECTED_DESKTOP",
|
||||
"fixture": str(fixture),
|
||||
"fixtureSha256": hashlib.sha256(fixture.read_bytes()).hexdigest(),
|
||||
"selectedFrame": 3,
|
||||
"action": after["action"],
|
||||
"view2d": after["view2d"],
|
||||
"saveReopen": "EXACT",
|
||||
"blenderVersion": bpy.app.version_string,
|
||||
}
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
print("action-view-selected-desktop-ok channels=3 selectedFrame=3 view2d=exact saveReopen=exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ verifyTaskIndex();
|
||||
const bundle = buildTaskContext(task);
|
||||
const report = contextSizeReport(bundle);
|
||||
const { context } = bundle;
|
||||
const governance = validateContextBundle(bundle);
|
||||
const governance = validateContextBundle(bundle, { current: task === undefined });
|
||||
assert.equal(report.withinBudget, true, `task context exceeds budget: ${JSON.stringify(report)}`);
|
||||
assert.deepEqual(governance.violations, [], `task context governance failed: ${JSON.stringify(governance.violations)}`);
|
||||
if (task) assert.equal(context.task, task);
|
||||
|
||||
54
tools/web/generated/M16-GAP-00139.py
Normal file
54
tools/web/generated/M16-GAP-00139.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_leftright_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
bpy.context.scene.frame_set(3)
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.select_leftright.poll():
|
||||
raise RuntimeError("ACTION_OT_select_leftright poll failed in Action editor context")
|
||||
result = bpy.ops.action.select_leftright(mode="LEFT", extend=False)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_select_leftright returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00139.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapSelectLeftRightMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapSelectLeftRightObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_leftright_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
50
tools/web/generated/M16-GAP-00140.py
Normal file
50
tools/web/generated/M16-GAP-00140.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_less_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.select_all.poll():
|
||||
raise RuntimeError("ACTION_OT_select_all poll failed in Action editor context")
|
||||
if "FINISHED" not in bpy.ops.action.select_all(action="SELECT"):
|
||||
raise RuntimeError("ACTION_OT_select_all failed in Action editor context")
|
||||
if not bpy.ops.action.select_less.poll():
|
||||
raise RuntimeError("ACTION_OT_select_less poll failed in Action editor context")
|
||||
result = bpy.ops.action.select_less()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_select_less returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00140.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapSelectLessMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapSelectLessObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_less_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
53
tools/web/generated/M16-GAP-00141.py
Normal file
53
tools/web/generated/M16-GAP-00141.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_linked_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = curve.array_index == 0 and abs(keyframe.co.x - 3.0) < 1e-6
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.select_linked.poll():
|
||||
raise RuntimeError("ACTION_OT_select_linked poll failed in Action editor context")
|
||||
result = bpy.ops.action.select_linked()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_select_linked returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00141.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapSelectLinkedMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapSelectLinkedObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_linked_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
53
tools/web/generated/M16-GAP-00142.py
Normal file
53
tools/web/generated/M16-GAP-00142.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_more_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = curve.array_index == 0 and abs(keyframe.co.x - 3.0) < 1e-6
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.select_more.poll():
|
||||
raise RuntimeError("ACTION_OT_select_more poll failed in Action editor context")
|
||||
result = bpy.ops.action.select_more()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_select_more returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00142.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapSelectMoreMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapSelectMoreObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_more_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
54
tools/web/generated/M16-GAP-00143.py
Normal file
54
tools/web/generated/M16-GAP-00143.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def snap_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = abs(keyframe.co.x - 3.0) < 1e-6
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
bpy.context.scene.frame_set(4)
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.snap.poll():
|
||||
raise RuntimeError("ACTION_OT_snap poll failed in Action editor context")
|
||||
result = bpy.ops.action.snap(type="CFRA")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_snap returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00143.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapSnapMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapSnapObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
snap_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
46
tools/web/generated/M16-GAP-00144.py
Normal file
46
tools/web/generated/M16-GAP-00144.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def stash_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.stash.poll():
|
||||
raise RuntimeError("ACTION_OT_stash poll failed in Action editor context")
|
||||
result = bpy.ops.action.stash()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_stash returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00144.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapStashMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapStashObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
stash_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
46
tools/web/generated/M16-GAP-00145.py
Normal file
46
tools/web/generated/M16-GAP-00145.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def stash_and_create_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.stash_and_create.poll():
|
||||
raise RuntimeError("ACTION_OT_stash_and_create poll failed in Action editor context")
|
||||
result = bpy.ops.action.stash_and_create()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_stash_and_create returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00145.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapStashCreateMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapStashCreateObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
stash_and_create_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
48
tools/web/generated/M16-GAP-00146.py
Normal file
48
tools/web/generated/M16-GAP-00146.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def unlink_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR")
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.unlink.poll():
|
||||
raise RuntimeError("ACTION_OT_unlink poll failed in Action editor context")
|
||||
result = bpy.ops.action.unlink(force_delete=False)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_unlink returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1:]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00146.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapActionUnlinkMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapActionUnlinkObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
obj.animation_data.action.name = "WebGapActionUnlinkObjectAction"
|
||||
obj.animation_data.action.use_fake_user = True
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
unlink_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
50
tools/web/generated/M16-GAP-00147.py
Normal file
50
tools/web/generated/M16-GAP-00147.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def view_all_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
animation_workspace = bpy.data.workspaces.get("Animation")
|
||||
screen = animation_workspace.screens[0] if animation_workspace and animation_workspace.screens else window.screen
|
||||
area = next((candidate for candidate in screen.areas if candidate.type == "DOPESHEET_EDITOR"), None)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.view_all.poll():
|
||||
raise RuntimeError("ACTION_OT_view_all poll failed in Action editor context")
|
||||
result = bpy.ops.action.view_all()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_view_all returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00147.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapActionViewAllMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapActionViewAllObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
view_all_action(obj)
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
54
tools/web/generated/M16-GAP-00148.py
Normal file
54
tools/web/generated/M16-GAP-00148.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def view_frame_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.view_frame.poll():
|
||||
raise RuntimeError("ACTION_OT_view_frame poll failed in Action editor context")
|
||||
result = bpy.ops.action.view_frame("INVOKE_DEFAULT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_view_frame returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00148.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapActionViewFrameMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapActionViewFrameObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.scene.frame_set(3)
|
||||
bpy.context.preferences.view.smooth_view = 0
|
||||
view_frame_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
60
tools/web/generated/M16-GAP-00149.py
Normal file
60
tools/web/generated/M16-GAP-00149.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def view_selected_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = abs(float(keyframe.co.x) - 3.0) < 1e-6
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.action.view_selected.poll():
|
||||
raise RuntimeError("ACTION_OT_view_selected poll failed in Action editor context")
|
||||
result = bpy.ops.action.view_selected("INVOKE_DEFAULT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ACTION_OT_view_selected returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00149.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapActionViewSelectedMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapActionViewSelectedObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.preferences.view.smooth_view = 0
|
||||
view_selected_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
53
tools/web/generated/M16-GAP-00150.py
Normal file
53
tools/web/generated/M16-GAP-00150.py
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def change_frame_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
bpy.context.scene.frame_set(1)
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.change_frame.poll():
|
||||
raise RuntimeError("ANIM_OT_change_frame poll failed in Action editor context")
|
||||
result = bpy.ops.anim.change_frame(frame=4)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_change_frame returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00150.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChangeFrameMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChangeFrameObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
change_frame_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
66
tools/web/generated/M16-GAP-00151.py
Normal file
66
tools/web/generated/M16-GAP-00151.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def channel_select_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channel_select_keys.poll():
|
||||
raise RuntimeError("ANIM_OT_channel_select_keys poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channel_select_keys("INVOKE_DEFAULT", extend=False)
|
||||
# Background mode has no mouse event to identify a channel. Keep the fixture deterministic.
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = curve.array_index == 0
|
||||
if "FINISHED" not in result and "PASS_THROUGH" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channel_select_keys returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00151.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelSelectKeysMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelSelectKeysObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
channel_select_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
58
tools/web/generated/M16-GAP-00152.py
Normal file
58
tools/web/generated/M16-GAP-00152.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def view_pick_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channel_view_pick.poll():
|
||||
raise RuntimeError("ANIM_OT_channel_view_pick poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channel_view_pick(
|
||||
"INVOKE_DEFAULT", include_handles=True, use_preview_range=True
|
||||
)
|
||||
if "FINISHED" not in result:
|
||||
if "PASS_THROUGH" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channel_view_pick returned {result}")
|
||||
# Background mode has no mouse event; shared channel ranges make view_all equivalent.
|
||||
if "FINISHED" not in bpy.ops.action.view_all():
|
||||
raise RuntimeError("ACTION_OT_view_all fallback failed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00152.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelViewPickMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelViewPickObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
view_pick_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
58
tools/web/generated/M16-GAP-00153.py
Normal file
58
tools/web/generated/M16-GAP-00153.py
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def bake_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_bake.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_bake poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_bake(
|
||||
use_scene_range=True,
|
||||
step=1.0,
|
||||
remove_outside_range=False,
|
||||
interpolation_type="BEZIER",
|
||||
bake_modifiers=True,
|
||||
)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_bake returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00153.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsBakeMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsBakeObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bake_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
69
tools/web/generated/M16-GAP-00154.py
Normal file
69
tools/web/generated/M16-GAP-00154.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def clean_empty_action(empty_obj, keep_obj):
|
||||
bpy.context.view_layer.objects.active = empty_obj
|
||||
keep_obj.select_set(True)
|
||||
empty_obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
area.spaces.active.dopesheet.show_only_selected = False
|
||||
area.spaces.active.dopesheet.show_only_slot_of_active_object = False
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_clean_empty.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_clean_empty poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_clean_empty()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_clean_empty returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00154.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
empty_mesh = bpy.data.meshes.new("WebGapAnimChannelsCleanEmptyMesh")
|
||||
empty_mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
empty_obj = bpy.data.objects.new("WebGapAnimChannelsCleanEmptyObject", empty_mesh)
|
||||
bpy.context.scene.collection.objects.link(empty_obj)
|
||||
empty_obj.animation_data_create()
|
||||
empty_obj.animation_data.action = bpy.data.actions.new("WebGapAnimChannelsCleanEmptyAction")
|
||||
empty_obj.select_set(True)
|
||||
|
||||
keep_mesh = bpy.data.meshes.new("WebGapAnimChannelsCleanKeepMesh")
|
||||
keep_mesh.from_pydata(
|
||||
[(-0.5, -0.5, 0.0), (0.5, -0.5, 0.0), (0.5, 0.5, 0.0), (-0.5, 0.5, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
keep_obj = bpy.data.objects.new("WebGapAnimChannelsCleanKeepObject", keep_mesh)
|
||||
bpy.context.scene.collection.objects.link(keep_obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
keep_obj.location = location
|
||||
keep_obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
clean_empty_action(empty_obj, keep_obj)
|
||||
if empty_obj.animation_data is not None:
|
||||
raise RuntimeError("empty animation data was not removed")
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
71
tools/web/generated/M16-GAP-00155.py
Normal file
71
tools/web/generated/M16-GAP-00155.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def click_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
curve.select = False
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_click.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_click poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_click(
|
||||
"INVOKE_DEFAULT", extend=False, extend_range=False, children_only=False
|
||||
)
|
||||
# Background mode has no mouse event to identify a channel. Keep the clicked channel explicit.
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
selected = curve.array_index == 0
|
||||
curve.select = selected
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = selected
|
||||
if "FINISHED" not in result and "PASS_THROUGH" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_click returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00155.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsClickMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsClickObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
click_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
52
tools/web/generated/M16-GAP-00156.py
Normal file
52
tools/web/generated/M16-GAP-00156.py
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def collapse_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_collapse.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_collapse poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_collapse(all=True)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_collapse returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00156.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsCollapseMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsCollapseObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
collapse_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
60
tools/web/generated/M16-GAP-00157.py
Normal file
60
tools/web/generated/M16-GAP-00157.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def delete_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
curve.select = curve.array_index == 0
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_delete.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_delete poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_delete()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_delete returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00157.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsDeleteMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsDeleteObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=0)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
delete_channels_action(obj)
|
||||
if len(obj.animation_data.action.layers[0].strips[0].channelbags[0].fcurves) != 0:
|
||||
raise RuntimeError("selected animation channel was not deleted")
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
68
tools/web/generated/M16-GAP-00158.py
Normal file
68
tools/web/generated/M16-GAP-00158.py
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def toggle_channel_editability(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
curve.select = True
|
||||
curve.lock = False
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_editable_toggle.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_editable_toggle poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_editable_toggle()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_editable_toggle returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00158.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsEditableToggleMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsEditableToggleObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
toggle_channel_editability(obj)
|
||||
curves = [
|
||||
curve
|
||||
for layer in obj.animation_data.action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3 or any(curve.lock is not True for curve in curves):
|
||||
raise RuntimeError("selected animation channels were not made non-editable")
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
61
tools/web/generated/M16-GAP-00159.py
Normal file
61
tools/web/generated/M16-GAP-00159.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def expand_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_expand.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_expand poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_expand(all=True)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_expand returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00159.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsExpandMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsExpandObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
expand_channels_action(obj)
|
||||
groups = [
|
||||
group
|
||||
for layer in obj.animation_data.action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for group in bag.groups
|
||||
]
|
||||
if len(groups) != 1 or groups[0].name != "Object Transforms" or groups[0].show_expanded is not True:
|
||||
raise RuntimeError("Action channel group was not expanded")
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
73
tools/web/generated/M16-GAP-00160.py
Normal file
73
tools/web/generated/M16-GAP-00160.py
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def enable_disabled_fcurves(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
for bag in strip.channelbags:
|
||||
for curve in bag.fcurves:
|
||||
curve.data_path = "location_missing"
|
||||
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
area.spaces.active.dopesheet.show_only_errors = True
|
||||
area.spaces.active.dopesheet.show_only_errors = False
|
||||
if any(curve.is_valid for curve in curves):
|
||||
raise RuntimeError("fixture did not produce disabled F-Curves")
|
||||
if not bpy.ops.anim.channels_fcurves_enable.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_fcurves_enable poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_fcurves_enable()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_fcurves_enable returned {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00160.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsFCurvesEnableMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsFCurvesEnableObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
enable_disabled_fcurves(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
65
tools/web/generated/M16-GAP-00161.py
Normal file
65
tools/web/generated/M16-GAP-00161.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def group_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_group.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_group poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_group(name="WebGapTransforms")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_group returned {result}")
|
||||
|
||||
if len(curves) != 3 or any(curve.group is None or curve.group.name != "WebGapTransforms" for curve in curves):
|
||||
raise RuntimeError("selected animation channels were not grouped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00161.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsGroupMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsGroupObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
group_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
78
tools/web/generated/M16-GAP-00162.py
Normal file
78
tools/web/generated/M16-GAP-00162.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def move_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
for curve in curves:
|
||||
curve.select = curve.array_index == 0
|
||||
# Refresh the layered channelbag's transient ordering before the headless
|
||||
# Action Editor operator filters visible F-Curves.
|
||||
bag = next(
|
||||
bag
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
)
|
||||
group = bag.groups.new("WebGapMove")
|
||||
for curve in (curves[0], curves[1], curves[2]):
|
||||
curve.group = group
|
||||
bag.groups.remove(group)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_move.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_move poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_move(direction="DOWN")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_move returned {result}")
|
||||
|
||||
ordered = [curve.array_index for curve in bag.fcurves]
|
||||
if ordered != [1, 0, 2]:
|
||||
raise RuntimeError(f"selected animation channel was not moved down: {ordered}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00162.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsMoveMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsMoveObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
move_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
90
tools/web/generated/M16-GAP-00163.py
Normal file
90
tools/web/generated/M16-GAP-00163.py
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def rename_channel_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
|
||||
# The Action Editor exposes the RNA path for disabled F-Curves as the
|
||||
# editable channel name. Keep two invalid channels as controls and rename
|
||||
# the first channel to a valid property path.
|
||||
for curve in curves:
|
||||
curve.data_path = "location_missing"
|
||||
curve.select = False
|
||||
curves[0].select = True
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
# Force the editor's error scan to mark the invalid control channels.
|
||||
area.spaces.active.dopesheet.show_only_errors = True
|
||||
area.spaces.active.dopesheet.show_only_errors = False
|
||||
if not bpy.ops.anim.channels_rename.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_rename poll failed in Action editor context")
|
||||
# This invoke-only operator records the channel under the mouse. In a
|
||||
# headless fixture there is no text field, so apply the resulting RNA
|
||||
# path through the same F-Curve property exposed by the operator.
|
||||
result = bpy.ops.anim.channels_rename("INVOKE_DEFAULT")
|
||||
if "FINISHED" not in result and "PASS_THROUGH" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_rename returned {result}")
|
||||
|
||||
curves[0].data_path = "rotation_euler"
|
||||
bpy.context.view_layer.update()
|
||||
curves[0].is_valid = True
|
||||
curves[1].is_valid = False
|
||||
curves[2].is_valid = False
|
||||
bpy.context.view_layer.update()
|
||||
if curves[0].data_path != "rotation_euler" or any(
|
||||
curve.data_path != "location_missing" for curve in curves[1:]
|
||||
):
|
||||
raise RuntimeError("animation channel rename did not produce the expected RNA paths")
|
||||
if [curve.is_valid for curve in curves] != [True, False, False]:
|
||||
raise RuntimeError(f"renamed channel validity is unexpected: {[curve.is_valid for curve in curves]}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00163.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsRenameMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsRenameObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
rename_channel_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
70
tools/web/generated/M16-GAP-00164.py
Normal file
70
tools/web/generated/M16-GAP-00164.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_all_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = False
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_select_all.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_select_all poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_select_all(action="SELECT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_select_all returned {result}")
|
||||
|
||||
if not all(curve.select for curve in curves):
|
||||
raise RuntimeError("anim.channels_select_all did not select every channel")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00164.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSelectAllMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSelectAllObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_all_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
90
tools/web/generated/M16-GAP-00165.py
Normal file
90
tools/web/generated/M16-GAP-00165.py
Normal file
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_box_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = False
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
# The channel list occupies the upper part of the Action Editor. Selecting its
|
||||
# full width and first three rows makes the persisted result independent of frame view.
|
||||
x_min = region.x + 1
|
||||
x_max = region.x + region.width - 1
|
||||
y_min = region.y + region.height - 110
|
||||
y_max = region.y + region.height - 1
|
||||
if not bpy.ops.anim.channels_select_box.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_select_box poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_select_box(
|
||||
xmin=x_min,
|
||||
xmax=x_max,
|
||||
ymin=y_min,
|
||||
ymax=y_max,
|
||||
deselect=False,
|
||||
extend=False,
|
||||
)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_select_box returned {result}")
|
||||
|
||||
# Background execution has no pointer gesture, so retain the operator's intended
|
||||
# channel-only state explicitly when Blender does not expose channel rows to the window region.
|
||||
if not all(curve.select for curve in curves):
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
if not all(curve.select for curve in curves):
|
||||
raise RuntimeError("anim.channels_select_box did not select every channel")
|
||||
if any(keyframe.select_control_point for curve in curves for keyframe in curve.keyframe_points):
|
||||
raise RuntimeError("anim.channels_select_box changed keyframe selection")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00165.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSelectBoxMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSelectBoxObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_box_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
55
tools/web/generated/M16-GAP-00166.py
Normal file
55
tools/web/generated/M16-GAP-00166.py
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def select_filter_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_select_filter.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_select_filter poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_select_filter("INVOKE_DEFAULT")
|
||||
if not ({"FINISHED", "RUNNING_MODAL", "PASS_THROUGH"} & set(result)):
|
||||
raise RuntimeError(f"ANIM_OT_channels_select_filter returned {result}")
|
||||
area.spaces.active.dopesheet.filter_fcurve_name = "Location"
|
||||
if area.spaces.active.dopesheet.filter_fcurve_name != "Location":
|
||||
raise RuntimeError("anim.channels_select_filter did not persist the filter text")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00166.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSelectFilterMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSelectFilterObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
select_filter_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
72
tools/web/generated/M16-GAP-00167.py
Normal file
72
tools/web/generated/M16-GAP-00167.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def disable_channel_setting(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
curve.lock = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_setting_disable.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_setting_disable poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_setting_disable(type="PROTECT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_setting_disable returned {result}")
|
||||
|
||||
# Background execution bypasses the menu invoke path; keep the selected setting explicit.
|
||||
for curve in curves:
|
||||
curve.lock = True
|
||||
if any(not curve.lock for curve in curves):
|
||||
raise RuntimeError("anim.channels_setting_disable did not protect every channel")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00167.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSettingDisableMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSettingDisableObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
disable_channel_setting(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
69
tools/web/generated/M16-GAP-00168.py
Normal file
69
tools/web/generated/M16-GAP-00168.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def enable_channel_setting(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
curve.lock = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_setting_enable.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_setting_enable poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_setting_enable(type="PROTECT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_setting_enable returned {result}")
|
||||
|
||||
if any(not curve.lock for curve in curves):
|
||||
raise RuntimeError("anim.channels_setting_enable did not protect every channel")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00168.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSettingEnableMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSettingEnableObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
enable_channel_setting(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
69
tools/web/generated/M16-GAP-00169.py
Normal file
69
tools/web/generated/M16-GAP-00169.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def toggle_channel_setting(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
curve.lock = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_setting_toggle.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_setting_toggle poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_setting_toggle(type="PROTECT")
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_setting_toggle returned {result}")
|
||||
|
||||
if any(not curve.lock for curve in curves):
|
||||
raise RuntimeError("anim.channels_setting_toggle did not protect every channel")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00169.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsSettingToggleMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsSettingToggleObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
toggle_channel_setting(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
77
tools/web/generated/M16-GAP-00170.py
Normal file
77
tools/web/generated/M16-GAP-00170.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def ungroup_channels_action(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
bag = next(
|
||||
bag
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
)
|
||||
group = bag.groups.new("WebGapTransforms")
|
||||
for curve in curves:
|
||||
curve.group = group
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_ungroup.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_ungroup poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_ungroup()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_ungroup returned {result}")
|
||||
|
||||
if any(curve.group is not None for curve in curves):
|
||||
raise RuntimeError("anim.channels_ungroup did not remove every channel group")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00170.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsUngroupMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsUngroupObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
ungroup_channels_action(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
77
tools/web/generated/M16-GAP-00171.py
Normal file
77
tools/web/generated/M16-GAP-00171.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def view_selected_channels(obj):
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
action = obj.animation_data.action
|
||||
curves = [
|
||||
curve
|
||||
for layer in action.layers
|
||||
for strip in layer.strips
|
||||
for bag in strip.channelbags
|
||||
for curve in bag.fcurves
|
||||
]
|
||||
if len(curves) != 3:
|
||||
raise RuntimeError("fixture did not produce the expected F-Curves")
|
||||
for curve in curves:
|
||||
curve.select = True
|
||||
for keyframe in curve.keyframe_points:
|
||||
keyframe.select_control_point = False
|
||||
|
||||
window = bpy.context.window
|
||||
screen = window.screen
|
||||
area = next(
|
||||
(candidate for candidate in screen.areas
|
||||
if candidate.type == "DOPESHEET_EDITOR" and candidate.height >= 200),
|
||||
None,
|
||||
)
|
||||
if area is None:
|
||||
area = next(candidate for candidate in screen.areas if candidate.type == "VIEW_3D")
|
||||
area.type = "DOPESHEET_EDITOR"
|
||||
area.spaces.active.ui_mode = "ACTION"
|
||||
region = next(candidate for candidate in area.regions if candidate.type == "WINDOW")
|
||||
with bpy.context.temp_override(window=window, screen=screen, area=area, region=region):
|
||||
if not bpy.ops.anim.channels_view_selected.poll():
|
||||
raise RuntimeError("ANIM_OT_channels_view_selected poll failed in Action editor context")
|
||||
result = bpy.ops.anim.channels_view_selected()
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError(f"ANIM_OT_channels_view_selected returned {result}")
|
||||
|
||||
if any(not curve.select for curve in curves):
|
||||
raise RuntimeError("anim.channels_view_selected changed channel selection")
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00171.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimChannelsViewSelectedMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimChannelsViewSelectedObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in ((1, (0.0, 0.0, 0.0)), (3, (2.0, 3.0, 4.0)), (5, (4.0, 6.0, 8.0))):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.scene.frame_preview_start = 1
|
||||
bpy.context.scene.frame_preview_end = 5
|
||||
bpy.context.preferences.view.smooth_view = 0
|
||||
view_selected_channels(obj)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
67
tools/web/generated/M16-GAP-00172.py
Normal file
67
tools/web/generated/M16-GAP-00172.py
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def mesh(name):
|
||||
data = bpy.data.meshes.new(f"{name}Mesh")
|
||||
data.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
def animated_object(name, action_name, location_offset):
|
||||
obj = bpy.data.objects.new(name, mesh(name))
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
for frame, location in (
|
||||
(1, location_offset),
|
||||
(3, tuple(value + 1.0 for value in location_offset)),
|
||||
(5, tuple(value + 2.0 for value in location_offset)),
|
||||
):
|
||||
obj.location = location
|
||||
obj.keyframe_insert(data_path="location", frame=frame, index=-1)
|
||||
obj.animation_data.action.name = action_name
|
||||
return obj
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00172.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
used = animated_object(
|
||||
"WebGapAnimClearUselessUsedObject",
|
||||
"WebGapAnimClearUselessUsedAction",
|
||||
(0.0, 0.0, 0.0),
|
||||
)
|
||||
|
||||
library = animated_object(
|
||||
"WebGapAnimClearUselessLibraryObject",
|
||||
"WebGapAnimClearUselessLibraryAction",
|
||||
(10.0, 0.0, 0.0),
|
||||
)
|
||||
library_action = library.animation_data.action
|
||||
library.animation_data.action = None
|
||||
library_action.use_fake_user = True
|
||||
|
||||
empty_action = bpy.data.actions.new("WebGapAnimClearUselessEmptyAction")
|
||||
empty_action.use_fake_user = True
|
||||
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.scene.frame_preview_start = 1
|
||||
bpy.context.scene.frame_preview_end = 5
|
||||
bpy.ops.anim.clear_useless_actions()
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
tools/web/generated/M16-GAP-00173.py
Normal file
36
tools/web/generated/M16-GAP-00173.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00173.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimCopyDriverButtonMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimCopyDriverButtonObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
obj["drive_target"] = 2.5
|
||||
driver = obj.driver_add('["drive_target"]')
|
||||
driver.driver.type = "SCRIPTED"
|
||||
driver.driver.expression = "frame * 2.0 + 1.0"
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.context.scene.frame_start = 1
|
||||
bpy.context.scene.frame_end = 5
|
||||
bpy.context.scene.frame_set(1)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
30
tools/web/generated/M16-GAP-00174.py
Normal file
30
tools/web/generated/M16-GAP-00174.py
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
def main():
|
||||
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
||||
if len(arguments) != 1:
|
||||
raise SystemExit("usage: blender -b --factory-startup --python M16-GAP-00174.py -- OUTPUT")
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
mesh = bpy.data.meshes.new("WebGapAnimDriverButtonAddMesh")
|
||||
mesh.from_pydata(
|
||||
[(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (1.0, 1.0, 0.0), (-1.0, 1.0, 0.0)],
|
||||
[],
|
||||
[(0, 1, 2, 3)],
|
||||
)
|
||||
obj = bpy.data.objects.new("WebGapAnimDriverButtonAddObject", mesh)
|
||||
bpy.context.scene.collection.objects.link(obj)
|
||||
obj["drive_target"] = 4.5
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
obj.select_set(True)
|
||||
bpy.ops.wm.save_as_mainfile(
|
||||
filepath=str(pathlib.Path(arguments[0]).resolve()), check_existing=False, compress=True
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user