Skip to content

Commit 82633e9

Browse files
committed
graphics: deterministic source-bbox ortho framing per orbit view, retaining camera scale proof
1 parent 4ebdbee commit 82633e9

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

‎tools/corpus/blender_corpus4008_2d_art_orbit_pilot_v1.py‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,45 @@
3030
VERSION = "real-sas-toon-shader-plus-silhouette-ink-v1"
3131

3232

33+
def choose_ortho_scale(base_scale, span_x, span_y, *, target_min=0.58,
34+
target_max=0.86):
35+
"""Correct source-space framing by actual projected source bounds.
36+
37+
Object corner bounds can overestimate visible silhouette. Real PNG QA is
38+
still authoritative for legibility and crop, not these estimates.
39+
"""
40+
require(all(math.isfinite(x) and x>0 for x in
41+
(base_scale,span_x,span_y)),"INVALID_PROJECTED_BOUNDS")
42+
# Scale is linear inverse of projected occupied fraction.
43+
# If both target spans cannot fit, favor no cropping over minimum span.
44+
ratio=max(min(span_x,span_y)/target_min,
45+
max(span_x,span_y)/target_max)
46+
require(math.isfinite(ratio) and ratio>0,"INVALID_ORTHO_RATIO")
47+
return max(0.20, min(10000., base_scale*ratio))
48+
49+
50+
def set_source_space_orbit_frame(bpy, camera, meshes, base_scale):
51+
from mathutils import Vector
52+
cam_inv=camera.matrix_world.inverted()
53+
xyz=[]
54+
for obj in meshes:
55+
for co in obj.bound_box:
56+
xyz.append(cam_inv @ (obj.matrix_world @ Vector(co)))
57+
require(xyz,"NO_MESH_CAMERA_BOUNDS")
58+
# At square resolution with an orthographic camera, width and height
59+
# span fractions are both world-space projected span/ortho_scale.
60+
raw_span_x=(max(x.x for x in xyz)-min(x.x for x in xyz))/base_scale
61+
raw_span_y=(max(x.y for x in xyz)-min(x.y for x in xyz))/base_scale
62+
scale=choose_ortho_scale(base_scale,raw_span_x,raw_span_y)
63+
camera.data.ortho_scale=scale
64+
return {
65+
"projected_source_bbox_fraction_x_before_adjustment":raw_span_x,
66+
"projected_source_bbox_fraction_y_before_adjustment":raw_span_y,
67+
"source_bbox_correction_based_on_not_human_court":True,
68+
"camera_ortho_scale":scale,
69+
}
70+
71+
3372
def image_ink(source: Path, output: Path):
3473
from PIL import Image, ImageChops, ImageEnhance, ImageFilter, ImageOps
3574
im = Image.open(source).convert("RGBA")
@@ -93,6 +132,8 @@ def render_orbit(bpy, *, source: Path, expected_sha256: str,
93132
cam.name = "SOURCE_SPACE_ORBIT__ANATOMICAL_FRONT_UNKNOWN"
94133
cam.data.type = "ORTHO"
95134
cam.data.ortho_scale = max(2.15 * radius, 0.5)
135+
base_ortho_scale = float(cam.data.ortho_scale)
136+
scene_meshes = [ob for ob in bpy.data.objects if ob.type == "MESH"]
96137
bpy.context.scene.camera = cam
97138
scene = configure_scene(bpy, resolution)
98139
scene.render.engine = "CYCLES"
@@ -126,6 +167,9 @@ def render_orbit(bpy, *, source: Path, expected_sha256: str,
126167
cam.location = center + direction.normalized()*max(radius*3.7, 1.0)
127168
cam.rotation_euler = (center-cam.location).to_track_quat("-Z", "Y").to_euler()
128169
bpy.context.view_layer.update()
170+
framing = set_source_space_orbit_frame(
171+
bpy, cam, scene_meshes, base_ortho_scale)
172+
bpy.context.view_layer.update()
129173
matrix = finite_matrix(cam.matrix_world)
130174
for ob,idx,mat in original_materials:
131175
ob.material_slots[idx].material = mat
@@ -158,6 +202,7 @@ def render_orbit(bpy, *, source: Path, expected_sha256: str,
158202
"view_id": "SOURCE_ORBIT_%02d" % i,
159203
"source_space_orbit_degrees": 45*i,
160204
"camera_source_space_matrix": matrix,
205+
"camera_projection_details": framing,
161206
"camera_axis_authored_front_qualified": False,
162207
"camera_source_up_qualified": False,
163208
"source_reference_png": source_path.name,

0 commit comments

Comments
 (0)