물 영상 정식 3D 기준·끝점·31개 컷 제작

This commit is contained in:
NINI
2026-10-10 03:25:24 +09:00
parent 5ee2b2e972
commit f8f12f963d
40 changed files with 18669 additions and 21 deletions
@@ -19,6 +19,7 @@ def containment(scene):
evaluated = body.evaluated_get(deps)
mesh = evaluated.to_mesh()
water = np.array([tuple(evaluated.matrix_world @ v.co) for v in mesh.vertices])
faces=[list(face.vertices) for face in mesh.polygons]
evaluated.to_mesh_clear()
low, high = water.min(axis=0), water.max(axis=0)
# 실제 물 표시 mesh에서 경계를 얻고 중심·별도 메타데이터만으로 판정하지 않는다.
@@ -26,6 +27,7 @@ def containment(scene):
projection = np.array([tuple(world_to_camera_view(scene,scene.camera,Vector(b))-origin)[:2] for b in ((1,0,0),(0,1,0),(0,0,1))])
offset = np.array(tuple(origin)[:2])
screen = (water @ projection + offset) * (1080,1920)
face_polygons=[screen[face] for face in faces]
points = sorted(set(map(tuple,screen)))
def cross(a,b,c):
return (b[0]-a[0])*(c[1]-a[1])-(b[1]-a[1])*(c[0]-a[0])
@@ -57,6 +59,19 @@ def containment(scene):
minimum = np.minimum(minimum,margin)
projected = (vertices @ projection + offset) * (1080,1920)
assert (projected.min(axis=0)>0).all() and (projected.max(axis=0)<(1080,1920)).all(), '분자 화면 잘림'
covered=np.zeros(len(projected),dtype=bool)
for polygon in face_polygons:
following=np.roll(polygon,-1,axis=0)
area=sum(a[0]*b[1]-b[0]*a[1] for a,b in zip(polygon,following))
if abs(area)<.001:
continue
inside=np.ones(len(projected),dtype=bool)
for a,b in zip(polygon,following):
edge=b-a
signed=(edge[0]*(projected[:,1]-a[1])-edge[1]*(projected[:,0]-a[0]))*(1 if area>0 else -1)
inside &= signed>=-.001
covered |= inside
assert covered.all(), f'실제 파란 절개 면 투영 밖: F{logical} {obj.name}'
for a,b in zip(hull,np.roll(hull,-1,axis=0)):
edge = b-a
distance = (edge[0]*(projected[:,1]-a[1])-edge[1]*(projected[:,0]-a[0]))/np.linalg.norm(edge)
@@ -68,13 +83,13 @@ def containment(scene):
for face in body.data.polygons:
points=np.array([tuple(body.matrix_world @ body.data.vertices[i].co) for i in face.vertices])
assert not np.allclose(points[:,1],low[1]) and not np.allclose(points[:,2],high[2]), '앞면·윗면 절개가 닫힘'
return dict(logical_frame_start=2468,logical_frame_end=3172,frames=705,water_bounds=[low.tolist(),high.tolist()],minimum_axis_margin=minimum.tolist(),minimum_projected_margin_px=pixel_margin,evaluated_vertices=vertex_count,mesh_roles=['atom','covalent','active_hydrogen_bond'],open_faces=['front','top'])
return dict(logical_frame_start=2468,logical_frame_end=3172,frames=705,water_bounds=[low.tolist(),high.tolist()],minimum_axis_margin=minimum.tolist(),minimum_projected_margin_px=pixel_margin,evaluated_vertices=vertex_count,mesh_roles=['atom','covalent','active_hydrogen_bond'],open_faces=['front','top'],projection_check='actual_face_union_and_outer_hull')
def verify(source):
def verify(source, expected=None):
assert source.is_file(), "원본 파일 없음"
bpy.ops.wm.open_mainfile(filepath=str(source))
expected = {"BondPair": (905, 1637), "Interface": (2469, 3173), "DropletStretch": (3173, 4077)}
expected = expected or {"BondPair": (905, 1637), "Interface": (2469, 3173), "DropletStretch": (3173, 4077)}
assert set(bpy.data.scenes) and set(s.name for s in bpy.data.scenes) == set(expected), "대표 Scene 누락/혼입"
assert not any(d.library for d in bpy.data.objects), "외부 객체"
assert not bpy.data.images and not bpy.data.fonts and not bpy.data.movieclips, "외부 자산"