114 lines
5.4 KiB
Python
114 lines
5.4 KiB
Python
"""정식 물 영상의 실제 9개 장면·기준 재사용·부피·주석 위치를 검사한다."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import bpy
|
|
import bmesh
|
|
from bpy_extras.object_utils import world_to_camera_view
|
|
sys.path.insert(0,str(Path(__file__).resolve().parent))
|
|
from verify_model import verify
|
|
ROOT=Path(__file__).resolve().parents[3]
|
|
PROJECT=ROOT/'projects/water-hydrogen-bond-surface-tension'
|
|
CUTS=json.loads((PROJECT/'stages/07-cuts/r1/artifact.json').read_text(encoding='utf-8'))['payload']['cuts']
|
|
SCENES=json.loads((PROJECT/'stages/08-scenes/r3/artifact.json').read_text(encoding='utf-8'))['payload']['scenes']
|
|
EXPECTED={}
|
|
for cut,plan in zip(CUTS,SCENES):
|
|
name=plan['start_state'].split(';')[0].split('=')[1]
|
|
start,end=cut['start_ms']//20+1,cut['end_ms']//20+1
|
|
EXPECTED[name]=(min(EXPECTED.get(name,(start,end))[0],start),end)
|
|
|
|
|
|
def volume(obj):
|
|
evaluated=obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
|
mesh=evaluated.to_mesh()
|
|
bm=bmesh.new()
|
|
bm.from_mesh(mesh)
|
|
assert all(e.is_manifold for e in bm.edges), '닫힌 물방울 mesh 아님'
|
|
bm.transform(evaluated.matrix_world)
|
|
value=abs(bm.calc_volume(signed=True))
|
|
bm.free()
|
|
evaluated.to_mesh_clear()
|
|
return value
|
|
|
|
|
|
def run(source,animated):
|
|
result=verify(source,EXPECTED)
|
|
assert bpy.context.scene.get('approved_source_sha256') or bpy.data.scenes['BondPair'].get('approved_source_sha256'), '승인 원본 연결 없음'
|
|
assert not bpy.data.libraries and not bpy.data.images and not bpy.data.fonts and not bpy.data.sounds
|
|
comparison=bpy.data.scenes['EqualVolumeComparison']
|
|
bpy.context.window.scene=comparison
|
|
comparison.frame_set(comparison.frame_start)
|
|
volumes=[volume(comparison.objects[name]) for name in ['ComparisonLong','ComparisonRound']]
|
|
assert abs(volumes[0]/volumes[1]-1)<.01, '비교 두 물방울 부피 불일치'
|
|
result['comparison_volumes']=volumes
|
|
anchors=[]
|
|
for cut,plan in zip(CUTS,SCENES):
|
|
scene=bpy.data.scenes[plan['start_state'].split(';')[0].split('=')[1]]
|
|
bpy.context.window.scene=scene
|
|
start,end=cut['start_ms']//20,cut['end_ms']//20
|
|
samples=[]
|
|
for logical in sorted(set([start,(start+end)//2,end])):
|
|
scene.frame_set(logical+1)
|
|
labels={}
|
|
for obj in scene.objects:
|
|
if obj.get('anchor'):
|
|
point=world_to_camera_view(scene,scene.camera,obj.matrix_world.translation)
|
|
assert 0<point.x<1 and 0<point.y<1 and point.z>0, '주석 위치 화면 밖'
|
|
labels[obj['anchor']]=dict(object_name=obj.name,x=round(point.x*1080,3),y=round((1-point.y)*1920,3),depth=round(point.z,3))
|
|
assert labels, '컷 주석 앵커 누락'
|
|
samples.append(dict(logical_frame=logical,blender_frame=logical+1,labels=labels))
|
|
anchors.append(dict(cut_id=cut['id'],scene_name=scene.name,start_frame=start,end_frame=end,samples=samples))
|
|
result['cut_anchors']=anchors
|
|
if animated:
|
|
assert bpy.data.scenes['BondPair'].get('reference_source_sha256'), '실제 10 원본 연결 없음'
|
|
scene=bpy.data.scenes['DropletStretch']
|
|
bpy.context.window.scene=scene
|
|
values=[]
|
|
for logical in range(3172,4077):
|
|
scene.frame_set(logical+1)
|
|
values.append(volume(scene.objects['Droplet']))
|
|
relative=max(abs(v/values[0]-1) for v in values)
|
|
assert relative<.01, '복귀 포함 물방울 부피 1% 초과'
|
|
scene.frame_set(3853)
|
|
assert max(abs(s-1) for s in scene.objects['Droplet'].scale)<.001, 'c21 구형 복귀 없음'
|
|
result['full_drop_volume']=dict(logical_start=3172,logical_end=4076,frames=905,relative_change=relative)
|
|
network=bpy.data.scenes['LiquidNetwork']
|
|
bpy.context.window.scene=network
|
|
pair_sets=[]
|
|
maximum=0
|
|
for frame in range(1637,2470):
|
|
network.frame_set(frame)
|
|
pairs=set()
|
|
for obj in network.objects:
|
|
if obj.get('role')!='hydrogen_bond' or obj.scale.x<=.0011:
|
|
continue
|
|
h,o=network.objects[obj['hydrogen']],network.objects[obj['oxygen']]
|
|
assert h.parent!=o.parent and h['element']=='H' and o['element']=='O'
|
|
a,b=h.matrix_world.translation,o.matrix_world.translation
|
|
error=(obj.location-a).cross(b-a).length
|
|
assert error<.003, '액체 점선이 실제 H···O 선분에서 벗어남'
|
|
maximum=max(maximum,error)
|
|
pairs.add((h.name,o.name))
|
|
if frame in [1637,2019,2257,2469]:
|
|
pair_sets.append(sorted(pairs))
|
|
assert pair_sets[0]!=pair_sets[1] and pair_sets[1]!=pair_sets[2], '액체 연결 반복 교체 없음'
|
|
result['network_frames']=833
|
|
result['network_active_pairs']=pair_sets
|
|
result['network_maximum_line_error']=maximum
|
|
result['animated']=animated
|
|
result['result']='GREEN'
|
|
return result
|
|
|
|
|
|
if __name__=='__main__':
|
|
parser=argparse.ArgumentParser()
|
|
parser.add_argument('--source',required=True)
|
|
parser.add_argument('--out',required=True)
|
|
parser.add_argument('--animated',action='store_true')
|
|
args=parser.parse_args(sys.argv[sys.argv.index('--')+1:])
|
|
result=run(Path(args.source),args.animated)
|
|
Path(args.out).write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
|
print('정식 원본 검사 GREEN',result['source_sha256'])
|