물 분자 전체를 실제 물 영역 안에 포함한 r3 시제품 검증
This commit is contained in:
@@ -6,9 +6,71 @@ from pathlib import Path
|
||||
import sys
|
||||
import bpy
|
||||
import bmesh
|
||||
import numpy as np
|
||||
from mathutils import Vector
|
||||
from bpy_extras.object_utils import world_to_camera_view
|
||||
|
||||
|
||||
def containment(scene):
|
||||
"""전체 프레임의 실제 mesh 정점을 물 영역·화면 윤곽과 대조한다."""
|
||||
bpy.context.window.scene = scene
|
||||
body = scene.objects['WaterCutawayBody']
|
||||
deps = bpy.context.evaluated_depsgraph_get()
|
||||
evaluated = body.evaluated_get(deps)
|
||||
mesh = evaluated.to_mesh()
|
||||
water = np.array([tuple(evaluated.matrix_world @ v.co) for v in mesh.vertices])
|
||||
evaluated.to_mesh_clear()
|
||||
low, high = water.min(axis=0), water.max(axis=0)
|
||||
# 실제 물 표시 mesh에서 경계를 얻고 중심·별도 메타데이터만으로 판정하지 않는다.
|
||||
origin = world_to_camera_view(scene,scene.camera,Vector((0,0,0)))
|
||||
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)
|
||||
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])
|
||||
lower, upper = [], []
|
||||
for chain, vertices in ((lower,points),(upper,reversed(points))):
|
||||
for p in vertices:
|
||||
while len(chain)>=2 and cross(chain[-2],chain[-1],p)<=0:
|
||||
chain.pop()
|
||||
chain.append(p)
|
||||
hull = np.array(lower[:-1]+upper[:-1])
|
||||
minimum = np.full(3,np.inf)
|
||||
pixel_margin = float('inf')
|
||||
vertex_count = 0
|
||||
for logical in range(2468,3173):
|
||||
scene.frame_set(logical+1)
|
||||
deps = bpy.context.evaluated_depsgraph_get()
|
||||
for obj in scene.objects:
|
||||
role = obj.get('role')
|
||||
if role not in ('atom','covalent','hydrogen_bond') or (role=='hydrogen_bond' and obj.scale.x<=.0011):
|
||||
continue
|
||||
evaluated = obj.evaluated_get(deps)
|
||||
mesh = evaluated.to_mesh()
|
||||
local = np.array([tuple(v.co) for v in mesh.vertices])
|
||||
transform = np.array(evaluated.matrix_world)
|
||||
vertices = local @ transform[:3,:3].T + transform[:3,3]
|
||||
evaluated.to_mesh_clear()
|
||||
margin = np.minimum(vertices-low,high-vertices).min(axis=0)
|
||||
assert margin.min()>=.03, f'물 영역 밖 또는 여백 부족: F{logical} {obj.name}, 축 여백 {margin.tolist()}'
|
||||
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(), '분자 화면 잘림'
|
||||
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)
|
||||
assert distance.min()>=0, f'파란 물 영역 투영 밖: F{logical} {obj.name}'
|
||||
pixel_margin = min(pixel_margin,float(distance.min()))
|
||||
vertex_count += len(vertices)
|
||||
# 앞면과 윗면은 실제 열린 절개이며 물의 고체 껍질을 만들지 않는다.
|
||||
assert len(body.data.polygons)==4, '물 절개 면 수 불일치'
|
||||
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'])
|
||||
|
||||
|
||||
def verify(source):
|
||||
assert source.is_file(), "원본 파일 없음"
|
||||
bpy.ops.wm.open_mainfile(filepath=str(source))
|
||||
@@ -52,6 +114,7 @@ def verify(source):
|
||||
assert (pair.objects["PairB"].matrix_world.translation - before).length > .1, "실제 3D 이동 없음"
|
||||
interface = bpy.data.scenes['Interface']
|
||||
bpy.context.window.scene = interface
|
||||
water_containment = containment(interface)
|
||||
connection_sets, neighborhoods = [], []
|
||||
for frame in (2807,2907,3051):
|
||||
interface.frame_set(frame)
|
||||
@@ -99,7 +162,7 @@ def verify(source):
|
||||
if obj.get("label"):
|
||||
p = world_to_camera_view(scene, scene.camera, obj.matrix_world.translation)
|
||||
anchors[name]['labels'][obj["label"]] = {"x": round(p.x * 1080, 2), "y": round((1-p.y)*1920, 2), "depth": round(p.z, 2)}
|
||||
return {"source_sha256": hashlib.sha256(source.read_bytes()).hexdigest(), "blender_version": bpy.app.version_string, "scenes": checks, "interface_frames_blender":[2807,2907,3051], "interface_active_pairs":connection_sets,"interface_neighborhoods":neighborhoods,"volume_frames_blender": [3173,3223,3273,3323,3373], "volumes": volumes, "relative_volume_change": relative, "evaluated_scales": scales, "label_anchors": anchors, "text_baked": False, "physical_simulation": False}
|
||||
return {"water_containment":water_containment,"source_sha256": hashlib.sha256(source.read_bytes()).hexdigest(), "blender_version": bpy.app.version_string, "scenes": checks, "interface_frames_blender":[2807,2907,3051], "interface_active_pairs":connection_sets,"interface_neighborhoods":neighborhoods,"volume_frames_blender": [3173,3223,3273,3323,3373], "volumes": volumes, "relative_volume_change": relative, "evaluated_scales": scales, "label_anchors": anchors, "text_baked": False, "physical_simulation": False}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user