@@ -69,6 +69,41 @@ def set_source_space_orbit_frame(bpy, camera, meshes, base_scale):
6969 }
7070
7171
72+ def source_alpha_bbox_fraction (bpy , path : Path ):
73+ """Read generated original-source diagnostic PNG alpha, no Pillow needed."""
74+ from array import array
75+ im = bpy .data .images .load (filepath = str (path ),check_existing = False )
76+ try :
77+ w ,h = im .size
78+ require (w >= 256 and h >= 256 ,"SOURCE_ALPHA_IMAGE_DIM" )
79+ data = array ('f' ,[0.0 ])* (w * h * 4 )
80+ im .pixels .foreach_get (data )
81+ xs = [];ys = []
82+ for i in range (w * h ):
83+ if data [4 * i + 3 ] > 20 / 255 :
84+ xs .append (i % w );ys .append (i // w )
85+ require (xs ,"EMPTY_SOURCE_ALPHA" )
86+ return {
87+ "source_alpha_bbox_fraction_x" :(max (xs )- min (xs )+ 1 )/ w ,
88+ "source_alpha_bbox_fraction_y" :(max (ys )- min (ys )+ 1 )/ h ,
89+ "source_alpha_occupied_pixels" :len (xs ),
90+ }
91+ finally :
92+ bpy .data .images .remove (im ,do_unlink = True )
93+
94+
95+ def source_space_alpha_feedback_scale (current_scale ,span_x ,span_y ):
96+ """Generic 1-step image-feedback fit, no per-asset or view-specific hack."""
97+ require (all (math .isfinite (x ) and x > 0 for x in
98+ (current_scale ,span_x ,span_y )),"ALPHA_FEEDBACK_INPUT" )
99+ if min (span_x ,span_y )>= 0.46 :
100+ return current_scale ,False
101+ ratio = max (min (span_x ,span_y )/ 0.54 ,
102+ max (span_x ,span_y )/ 0.88 )
103+ require (math .isfinite (ratio ) and ratio > 0 ,"ALPHA_FEEDBACK_RATIO" )
104+ return current_scale * ratio ,True
105+
106+
72107def image_ink (source : Path , output : Path ):
73108 from PIL import Image , ImageChops , ImageEnhance , ImageFilter , ImageOps
74109 im = Image .open (source ).convert ("RGBA" )
@@ -187,6 +222,24 @@ def render_orbit(bpy, *, source: Path, expected_sha256: str,
187222 bpy .ops .render .render (write_still = True )
188223 require (source_path .is_file () and source_path .stat ().st_size > 0 ,
189224 "SOURCE_VIEW_RENDER_EMPTY" )
225+ preview = source_alpha_bbox_fraction (bpy ,source_path )
226+ new_scale ,need_refit = source_space_alpha_feedback_scale (
227+ float (cam .data .ortho_scale ),
228+ preview ["source_alpha_bbox_fraction_x" ],
229+ preview ["source_alpha_bbox_fraction_y" ])
230+ framing ["pre_refit_source_png_alpha_bbox" ]= preview
231+ framing ["source_pixel_alpha_feedback_applied" ]= need_refit
232+ if need_refit :
233+ cam .data .ortho_scale = new_scale
234+ bpy .context .view_layer .update ()
235+ # Re-render source and cel at identical corrected view/camera.
236+ scene .render .filepath = str (source_path )
237+ bpy .ops .render .render (write_still = True )
238+ require (source_path .is_file () and source_path .stat ().st_size > 0 ,
239+ "REFIT_SOURCE_RENDER_EMPTY" )
240+ framing ["camera_ortho_scale" ]= float (cam .data .ortho_scale )
241+ framing ["final_source_png_alpha_bbox" ]= source_alpha_bbox_fraction (
242+ bpy ,source_path )
190243
191244 configure_flat_study (bpy , cel = True , original_materials = original_materials )
192245 cel_path = output / ("cel_3band_orbit_%02d.png" % i )
0 commit comments