Skip to content

Commit 2e0fb61

Browse files
Refactoring
1 parent a136de5 commit 2e0fb61

File tree

7 files changed

+960
-121
lines changed

7 files changed

+960
-121
lines changed

β€Žhistory/build_scenes.shβ€Ž

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
5+
BLENDER=${BLENDER:-blender}
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SCRIPT="$SCRIPT_DIR/run_scene.py"
8+
9+
SCENES=(
10+
ising_descent
11+
history_lattice
12+
history_dag
13+
crdt_convergence
14+
entropy_collapse
15+
)
16+
17+
if [ $# -eq 0 ]; then
18+
echo "No scene specified β€” rendering all scenes."
19+
for s in "${SCENES[@]}"; do
20+
echo "Rendering $s"
21+
$BLENDER -b -P "$SCRIPT" -- "$s"
22+
done
23+
exit 0
24+
fi
25+
26+
SCENE="$1"
27+
28+
echo "Rendering $SCENE"
29+
$BLENDER -b -P "$SCRIPT" -- "$SCENE"

β€Žhistory/run_scene.pyβ€Ž

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import bpy
2+
import os
3+
import sys
4+
import importlib
5+
import inspect
6+
7+
8+
def clear_scene():
9+
bpy.ops.object.select_all(action="SELECT")
10+
bpy.ops.object.delete(use_global=False)
11+
12+
13+
def discover_scenes():
14+
15+
scenes = {}
16+
17+
base_dir = os.path.dirname(__file__)
18+
19+
for fname in os.listdir(base_dir):
20+
21+
if not fname.startswith("scenes_") or not fname.endswith(".py"):
22+
continue
23+
24+
module_name = fname[:-3]
25+
26+
module = importlib.import_module(module_name)
27+
28+
for name, obj in inspect.getmembers(module):
29+
30+
if inspect.isfunction(obj) and name.startswith("build_"):
31+
key = name.replace("build_", "")
32+
scenes[key] = obj
33+
34+
return scenes
35+
36+
37+
def parse_args():
38+
39+
argv = sys.argv
40+
41+
if "--" not in argv:
42+
return None
43+
44+
argv = argv[argv.index("--") + 1:]
45+
46+
if len(argv) == 0:
47+
return None
48+
49+
return argv[0]
50+
51+
52+
def run_scene(scene_name, anim_dir="./renders"):
53+
54+
scene = bpy.context.scene
55+
56+
clear_scene()
57+
58+
scenes = discover_scenes()
59+
60+
if scene_name not in scenes:
61+
raise ValueError(
62+
f"Scene '{scene_name}' not found. Available: {list(scenes.keys())}"
63+
)
64+
65+
builder = scenes[scene_name]
66+
67+
try:
68+
builder(scene, os.path.join(anim_dir, scene_name))
69+
except TypeError:
70+
builder(scene)
71+
72+
73+
def main():
74+
75+
scene_name = parse_args()
76+
77+
scenes = discover_scenes()
78+
79+
# If a scene is specified, run only that
80+
if scene_name is not None:
81+
run_scene(scene_name)
82+
return
83+
84+
# Otherwise run all scenes
85+
print("No scene specified β€” rendering all scenes\n")
86+
87+
for name in scenes:
88+
print(f"Running {name}")
89+
run_scene(name)
90+
91+
92+
if __name__ == "__main__":
93+
main()
Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import os
22
import random
3-
from mathutils import Vector
43
import bpy
4+
from mathutils import Vector
55
from scene_utils import ensure_collection, make_material, add_sphere, add_cylinder_edge, add_camera_and_light
66

7+
78
def build_crdt_convergence(scene, anim_dir: str):
9+
810
random.seed(3)
11+
912
col = ensure_collection("CRDT")
1013

1114
add_camera_and_light(camera_loc=(12, -12, 8), look_at=(6, 0, 1))
@@ -15,70 +18,129 @@ def build_crdt_convergence(scene, anim_dir: str):
1518
make_material("rep1", rgba=(0.7, 1.0, 0.7, 1.0), emission=0.25),
1619
make_material("rep2", rgba=(0.7, 0.8, 1.0, 1.0), emission=0.25),
1720
]
21+
1822
mat_edge = make_material("merge_edge", rgba=(0.2, 0.2, 0.25, 1.0), emission=0.0)
1923

2024
replicas = 3
2125
ops_total = 18
2226
steps = 90
2327

24-
ops = list(range(ops_total))
25-
random.shuffle(ops)
28+
ops_pool = list(range(ops_total))
29+
random.shuffle(ops_pool)
2630

2731
op_sets = [set() for _ in range(replicas)]
32+
2833
for r in range(replicas):
2934
for _ in range(4):
30-
op_sets[r].add(ops.pop())
35+
op_sets[r].add(ops_pool.pop())
36+
37+
op_objs = [[None] * ops_total for _ in range(replicas)]
3138

32-
op_objs = [[None]*ops_total for _ in range(replicas)]
39+
def op_position(replica, op_id):
3340

34-
def op_position(r, op_id):
35-
x = r * 3.0
36-
y = (op_id - (ops_total-1)/2) * 0.22
41+
x = replica * 3.0
42+
y = (op_id - (ops_total - 1) / 2) * 0.22
3743
z = 0.0
44+
3845
return Vector((x, y, z))
3946

40-
for r in range(replicas):
41-
for op_id in range(ops_total):
42-
p = op_position(r, op_id)
43-
obj = add_sphere(p, radius=0.11, name=f"op_{r}_{op_id}", material=mat_ops[r], collection=col)
44-
obj.hide_render = True
45-
obj.hide_viewport = True
46-
op_objs[r][op_id] = obj
47-
48-
def reveal_ops(frame):
47+
def create_operation_objects():
48+
4949
for r in range(replicas):
50+
for op_id in range(ops_total):
51+
52+
pos = op_position(r, op_id)
53+
54+
obj = add_sphere(
55+
pos,
56+
radius=0.11,
57+
name=f"op_{r}_{op_id}",
58+
material=mat_ops[r],
59+
collection=col,
60+
)
61+
62+
obj.hide_render = True
63+
obj.hide_viewport = True
64+
65+
op_objs[r][op_id] = obj
66+
67+
def reveal_operations(frame):
68+
69+
for r in range(replicas):
70+
5071
for op_id in op_sets[r]:
72+
5173
obj = op_objs[r][op_id]
74+
5275
obj.hide_render = False
5376
obj.hide_viewport = False
54-
obj.keyframe_insert(data_path="hide_render", frame=frame)
55-
obj.keyframe_insert(data_path="hide_viewport", frame=frame)
77+
78+
obj.keyframe_insert("hide_render", frame=frame)
79+
obj.keyframe_insert("hide_viewport", frame=frame)
80+
81+
def merge(a, b):
82+
83+
before = set(op_sets[b])
84+
85+
op_sets[b] |= op_sets[a]
86+
87+
return before != op_sets[b]
88+
89+
def inject_operation():
90+
91+
if not ops_pool:
92+
return
93+
94+
r = random.randrange(replicas)
95+
op_sets[r].add(ops_pool.pop())
96+
97+
def converged():
98+
99+
base = op_sets[0]
100+
101+
for r in range(1, replicas):
102+
if op_sets[r] != base:
103+
return False
104+
105+
return not ops_pool
106+
107+
create_operation_objects()
56108

57109
scene.frame_start = 1
58110
scene.frame_end = steps
59111

60-
reveal_ops(1)
112+
reveal_operations(1)
113+
114+
for frame in range(2, steps + 1):
61115

62-
for f in range(2, steps+1):
63116
if random.random() < 0.55:
117+
64118
a, b = random.sample(range(replicas), 2)
65-
before = set(op_sets[b])
66-
op_sets[b] |= op_sets[a]
67-
if before != op_sets[b]:
68-
add_cylinder_edge((a*3.0, 0, 0.4), (b*3.0, 0, 0.4), radius=0.03, material=mat_edge, collection=col)
69119

70-
if ops and random.random() < 0.25:
71-
r = random.randrange(replicas)
72-
op_sets[r].add(ops.pop())
120+
if merge(a, b):
121+
122+
add_cylinder_edge(
123+
(a * 3.0, 0, 0.4),
124+
(b * 3.0, 0, 0.4),
125+
radius=0.03,
126+
material=mat_edge,
127+
collection=col,
128+
)
73129

74-
reveal_ops(f)
130+
if random.random() < 0.25:
131+
inject_operation()
75132

76-
if all(op_sets[0] == op_sets[r] for r in range(1, replicas)) and not ops:
77-
scene.frame_end = f
133+
reveal_operations(frame)
134+
135+
if converged():
136+
scene.frame_end = frame
78137
break
79-
if anim_dir:
80-
os.makedirs(anim_dir, exist_ok=True)
81-
82-
scene.render.image_settings.file_format = "PNG"
83-
scene.render.filepath = os.path.join(anim_dir, "frame_")
84-
bpy.ops.render.render(animation=True, write_still=True)
138+
139+
if anim_dir:
140+
141+
os.makedirs(anim_dir, exist_ok=True)
142+
143+
scene.render.image_settings.file_format = "PNG"
144+
scene.render.filepath = os.path.join(anim_dir, "frame_")
145+
146+
bpy.ops.render.render(animation=True, write_still=True)

0 commit comments

Comments
Β (0)