물 영상의 검증된 3D 대표 시제품과 새 장면 설계 작성
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# 물 소재 3D 시제품 작성 경계
|
||||
|
||||
- 이 폴더는 물 영상의 세 대표 장면을 재현하는 bpy 코드다. 범용 실행기는 `tools/blender/run.py`를 사용하고 수정하지 않는다.
|
||||
- 원본·렌더는 자기 `08-scenes/r2` 아래에 둔다. 이전 결과·WAV·사용자 스타일 원본·project.json은 변경하지 않는다.
|
||||
- 실제 O1H2·V자·두 종류 결합·동적 이웃·닫힌 mesh 부피와 `--disable-autoexec` 평가를 검사한다. 물리 시뮬레이션으로 주장하지 않는다.
|
||||
- draft 품질 조정은 별도 렌더 시도를 보존한다. 제출 후 결과 변경은 새 revision으로 진행하며 새 scenes 승인 전 정식 10–12를 실행하지 않는다.
|
||||
@@ -0,0 +1,18 @@
|
||||
# 물 영상의 제한된 3D 시제품
|
||||
|
||||
`build_preview.py`는 BondPair·Interface·DropletStretch 세 Scene만 만든다. 불규칙한 물 분자, 실제 연결 쌍 변화, 내부/표면 강조, 같은 부피의 3D 변형을 구현한 설명 모형이다. MD·유체 시뮬레이션은 아니다. 31컷 전체 모델·최종 영상은 scenes 승인 뒤 별도 제작한다.
|
||||
|
||||
저장소 루트에서 기존 설치를 사용한다. Blender 논리 F는 실제 F+1, MP4는 `[start,end)`다. 대표 영상은 c07 4040ms/202프레임, c16 4880ms/244프레임, c18 4000ms/200프레임의 무음 영상이다. 끝 PNG는 exclusive 경계이며 MP4 마지막 프레임보다 20ms 뒤다.
|
||||
|
||||
```powershell
|
||||
& 'C:/Program Files/Blender Foundation/Blender 5.2/blender.exe' --background --disable-autoexec --python-exit-code 1 --python projects/water-hydrogen-bond-surface-tension/blender/build_preview.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/test_model.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/prepare_artifacts.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/render_preview.py --kind image
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/render_preview.py --kind video
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/verify_design.py
|
||||
```
|
||||
|
||||
작성 순서는 새 draft 재현용이다. `prepare_artifacts.py`는 대표 미디어가 빈 08/09 초안을 작성하므로 제출한 r2에서 재실행하지 않는다. `render_preview.py`는 현재 초안에 실제 증거만 추가한다. 완료된 결과는 새 버전에서 작업한다.
|
||||
|
||||
`test_model.py`는 없는 파일·Scene 누락·H 원자 누락·부피 driver 오류의 실제 RED와 원본 GREEN을 확인한다. 부피는 시작·중간 세 곳·전체 컷 끝을 실제 mesh로 평가한다. 실제 렌더는 기존 실행기의 source hash·Scene·profile·frame 검사, PNG/MP4 probe 및 전체 디코드를 거친다. 큰 원본·렌더·실패 시도는 해당 r2의 좁은 `.gitignore`로 보존하고 작은 검증 JSON에 복사할 파일·해시를 기록한다.
|
||||
@@ -0,0 +1,217 @@
|
||||
"""물 소재의 세 대표 3D 장면만 작성한다. 물리 시뮬레이션이 아니다."""
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import math
|
||||
import shutil
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
OUT = ROOT / "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/preview.blend"
|
||||
if OUT.exists():
|
||||
history=OUT.parent/'history'
|
||||
history.mkdir(exist_ok=True)
|
||||
snapshot=history/(hashlib.sha256(OUT.read_bytes()).hexdigest()+'.blend')
|
||||
if not snapshot.exists():
|
||||
shutil.copy2(OUT,snapshot)
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
|
||||
|
||||
def material(name, color, roughness=.48, metallic=0):
|
||||
mat = bpy.data.materials.new(name)
|
||||
mat.diffuse_color = (*color, 1)
|
||||
mat.use_nodes = True
|
||||
bsdf = mat.node_tree.nodes.get("Principled BSDF")
|
||||
bsdf.inputs["Base Color"].default_value = (*color, 1)
|
||||
bsdf.inputs["Roughness"].default_value = roughness
|
||||
bsdf.inputs["Metallic"].default_value = metallic
|
||||
return mat
|
||||
|
||||
|
||||
O = material("O_Coral", (.74, .16, .11))
|
||||
H = material("H_Ivory", (.91, .84, .64))
|
||||
SOLID = material("OH_Navy", (.025, .06, .13))
|
||||
DASH = material("HO_Gold", (.83, .47, .045), .4)
|
||||
WATER = material("Water_Blue", (.06, .42, .66), .25, .08)
|
||||
CREAM = material("Studio_Cream", (.78, .73, .62), .8)
|
||||
GHOST = material("Water_Concept_Cutaway", (.25, .62, .76), .7)
|
||||
BACKDROP = bpy.data.materials.new('Cream_Emission_Backdrop')
|
||||
BACKDROP.use_nodes = True
|
||||
nodes = BACKDROP.node_tree.nodes
|
||||
nodes.clear()
|
||||
emission = nodes.new('ShaderNodeEmission')
|
||||
emission.inputs['Color'].default_value = (.88,.82,.70,1)
|
||||
output = nodes.new('ShaderNodeOutputMaterial')
|
||||
BACKDROP.node_tree.links.new(emission.outputs[0],output.inputs['Surface'])
|
||||
|
||||
|
||||
def point(obj, target):
|
||||
obj.rotation_euler = (Vector(target) - obj.location).to_track_quat('-Z', 'Y').to_euler()
|
||||
|
||||
|
||||
def scene(name, bounds, camera_position, scale):
|
||||
s = bpy.context.scene if len(bpy.data.scenes) == 1 and not bpy.context.scene.objects else bpy.data.scenes.new(name)
|
||||
s.name = name
|
||||
bpy.context.window.scene = s
|
||||
s.frame_start, s.frame_end = bounds
|
||||
s.render.engine = "BLENDER_EEVEE"
|
||||
s.render.fps = 50
|
||||
s.render.resolution_x, s.render.resolution_y = 1080, 1920
|
||||
s.render.resolution_percentage = 100
|
||||
s.eevee.taa_render_samples = 64
|
||||
s.view_settings.view_transform = 'Standard'
|
||||
world = bpy.data.worlds.new(name + "World")
|
||||
world.use_nodes = True
|
||||
world.node_tree.nodes['Background'].inputs[0].default_value = (.78, .74, .65, 1)
|
||||
world.node_tree.nodes['Background'].inputs[1].default_value = .3
|
||||
s.world = world
|
||||
cam = bpy.data.objects.new(name+"Camera", bpy.data.cameras.new(name+"Camera"))
|
||||
s.collection.objects.link(cam)
|
||||
cam.location = camera_position
|
||||
point(cam, (0, 0, 0))
|
||||
cam.data.type = 'ORTHO'
|
||||
cam.data.ortho_scale = scale
|
||||
s.camera = cam
|
||||
bpy.ops.mesh.primitive_plane_add(size=30,location=-cam.location.normalized()*6)
|
||||
backdrop=bpy.context.object
|
||||
backdrop.name=name+'CreamBackdrop'
|
||||
backdrop.rotation_euler=cam.rotation_euler
|
||||
backdrop.data.materials.append(BACKDROP)
|
||||
backdrop['role']='background'
|
||||
for label, location, energy, size in (("Key", (-4,-6,7), 250, 5), ("Fill", (4,-2,3), 150, 4), ("Rim", (0,4,5), 200, 4)):
|
||||
light = bpy.data.objects.new(name+label, bpy.data.lights.new(name+label, 'AREA'))
|
||||
light.location = location
|
||||
light.data.energy, light.data.shape, light.data.size = energy, 'DISK', size
|
||||
point(light, (0,0,0))
|
||||
s.collection.objects.link(light)
|
||||
return s
|
||||
|
||||
|
||||
def sphere(name, position, radius, mat, parent=None, segments=32, rings=16):
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(segments=segments, ring_count=rings, radius=radius, location=position)
|
||||
obj = bpy.context.object
|
||||
obj.name = name
|
||||
obj.data.materials.append(mat)
|
||||
for polygon in obj.data.polygons:
|
||||
polygon.use_smooth = True
|
||||
obj.parent = parent
|
||||
return obj
|
||||
|
||||
|
||||
def cylinder(name, a, b, radius, mat, parent=None):
|
||||
a, b = Vector(a), Vector(b)
|
||||
bpy.ops.mesh.primitive_cylinder_add(vertices=16, radius=radius, depth=(b-a).length, location=(a+b)/2)
|
||||
obj = bpy.context.object
|
||||
obj.name = name
|
||||
obj.rotation_euler = (b-a).to_track_quat('Z','Y').to_euler()
|
||||
obj.data.materials.append(mat)
|
||||
obj.parent = parent
|
||||
for polygon in obj.data.polygons:
|
||||
polygon.use_smooth = True
|
||||
return obj
|
||||
|
||||
|
||||
def molecule(name, location, rotation=0, size=1):
|
||||
root = bpy.data.objects.new(name, None)
|
||||
bpy.context.scene.collection.objects.link(root)
|
||||
root.location = location
|
||||
root.rotation_euler.z = rotation
|
||||
root.scale = (size,)*3
|
||||
root['role'] = 'molecule'
|
||||
atoms = []
|
||||
for suffix, pos, radius, mat, element in (("O", (0,0,0), .36,O,'O'), ("H1", (.72,0,.52), .23,H,'H'), ("H2",(-.72,0,.52), .23,H,'H')):
|
||||
atom = sphere(name+"_"+suffix,pos,radius,mat,root)
|
||||
atom['role'], atom['element'] = 'atom', element
|
||||
atoms.append(atom)
|
||||
for i, h in enumerate(atoms[1:]):
|
||||
bond = cylinder(name+"_OH"+str(i),(0,0,0),h.location,.065,SOLID,root)
|
||||
bond['role'] = 'covalent'
|
||||
return root, atoms
|
||||
|
||||
|
||||
def dash(name, hydrogen, oxygen, frames=None):
|
||||
# 점선의 끝점은 실제 서로 다른 분자의 H/O 월드 좌표다.
|
||||
scene = bpy.context.scene
|
||||
keys = frames or [scene.frame_start]
|
||||
segments = []
|
||||
for i in range(6):
|
||||
obj = cylinder(name+str(i),(0,0,0),(0,0,.1),.045,DASH)
|
||||
obj['role'], obj['hydrogen'], obj['oxygen'] = 'hydrogen_bond', hydrogen.name, oxygen.name
|
||||
segments.append(obj)
|
||||
for frame in keys:
|
||||
scene.frame_set(frame)
|
||||
bpy.context.view_layer.update()
|
||||
a, b = hydrogen.matrix_world.translation, oxygen.matrix_world.translation
|
||||
direction = b-a
|
||||
for i, obj in enumerate(segments):
|
||||
t1, t2 = .18+i*.11, .18+i*.11+.062
|
||||
obj.location = a+direction*((t1+t2)/2)
|
||||
obj.rotation_euler = direction.to_track_quat('Z','Y').to_euler()
|
||||
obj.scale.z = direction.length*(t2-t1)/.1
|
||||
for path in ('location','rotation_euler','scale'):
|
||||
obj.keyframe_insert(path,frame=frame)
|
||||
return segments
|
||||
|
||||
|
||||
pair = scene("BondPair", (905,1637), (0,-12,6), 11.5)
|
||||
a, aa = molecule("PairA", (-1.8,0,-.3))
|
||||
b, ba = molecule("PairB", (1.65,.15,.45), .3)
|
||||
aa[1]['label'] = '한 분자의 Hδ+'
|
||||
ba[0]['label'] = '이웃 분자의 Oδ−'
|
||||
for frame, x in ((905,1.65),(1018,1.65),(1118,.85),(1220,.85),(1637,.85)):
|
||||
b.location.x = x
|
||||
b.keyframe_insert('location',frame=frame)
|
||||
dash("Pair_HO_Dash_", aa[1], ba[0], [905,1018,1118,1220,1637])
|
||||
|
||||
interface = scene("Interface", (2469,3173), (0,-14,6), 13)
|
||||
# 옆면/뒤쪽의 청색 체적만 보이는 잘라낸 설명 모형. 경계 위 고체 막 없음.
|
||||
bpy.ops.mesh.primitive_cube_add(size=1, location=(0,1.7,-1.15))
|
||||
body = bpy.context.object
|
||||
body.name = 'WaterCutawayBody'
|
||||
body.scale = (5,1.5,3)
|
||||
body.data.materials.append(GHOST)
|
||||
body['role'] = 'cutaway_water'
|
||||
bevel=body.modifiers.new('SoftCutawayEdges','BEVEL')
|
||||
bevel.width=.055
|
||||
bevel.segments=3
|
||||
positions = [(-1.8,-.1,.25),(.4,.2,.18),(1.95,.45,.25),(-2,.3,-1.5),(-.65,.15,-1.15),(.9,.3,-1.6),(2,.35,-1.3),(-1.6,.3,-2.5),(.2,.4,-2.45),(1.6,.1,-2.6)]
|
||||
mols=[]
|
||||
for i, pos in enumerate(positions):
|
||||
root, atoms = molecule('Liquid'+str(i),pos,(-.4+i*.37),.58)
|
||||
mols.append((root,atoms))
|
||||
for frame, delta in ((2469,0),(2807,0),(2907,.10*math.sin(i+1)),(3051,.05*math.cos(i)),(3173,0)):
|
||||
root.location.x = pos[0]+delta
|
||||
root.keyframe_insert('location',frame=frame)
|
||||
for i,j in ((0,1),(1,2),(3,4),(3,7),(4,5),(4,7),(5,6),(5,8),(8,9)):
|
||||
segments = dash('LiquidDash'+str(i)+'_'+str(j)+'_',mols[i][1][1],mols[j][1][0],[2469,2807,2907,3051,3173])
|
||||
if (i,j) in ((3,4),(3,7)):
|
||||
for segment in segments:
|
||||
for frame, active in ((2469, j==4),(2807,j==4),(2907,j==7),(3051,j==7),(3173,j==7)):
|
||||
segment.scale.x = segment.scale.y = 1 if active else .001
|
||||
segment.keyframe_insert('scale',frame=frame)
|
||||
mols[4][0]['label']='물 안쪽'
|
||||
mols[1][0]['label']='물과 공기의 경계'
|
||||
for name, center, frames in (("InteriorFocus",positions[4],((2469,.01),(2807,1),(2857,.01))), ("SurfaceFocus",positions[1],((2807,.01),(2907,1),(3173,1)))):
|
||||
bpy.ops.mesh.primitive_torus_add(major_segments=48, minor_segments=10, location=(center[0],-.32,center[2]), rotation=(math.pi/2,0,0), major_radius=.74, minor_radius=.033)
|
||||
ring=bpy.context.object
|
||||
ring.name=name
|
||||
ring.data.materials.append(DASH)
|
||||
ring['role']='explanatory_highlight'
|
||||
for frame, scale in frames:
|
||||
ring.scale=(scale,)*3
|
||||
ring.keyframe_insert('scale',frame=frame)
|
||||
|
||||
stretch = scene("DropletStretch", (3173,4077), (0,-12,5), 10.5)
|
||||
drop=sphere('Droplet',(0,0,0),1.3,WATER,segments=128,rings=64)
|
||||
drop['role']='same_volume_water'
|
||||
drop['label']='같은 물의 양 · 설명용 모형'
|
||||
# 단순 driver는 --disable-autoexec 상태에서도 평가된다. 매 프레임 det(scale)=1.
|
||||
expression="1+0.85*min(max((frame-3173)/200,0),1)"
|
||||
drop.driver_add('scale',0).driver.expression=expression
|
||||
for axis in (1,2):
|
||||
drop.driver_add('scale',axis).driver.expression="("+expression+")**(-0.5)"
|
||||
OUT.parent.mkdir(parents=True,exist_ok=True)
|
||||
for s in bpy.data.scenes:
|
||||
s.frame_set(s.frame_start)
|
||||
bpy.ops.wm.save_as_mainfile(filepath=str(OUT))
|
||||
print('PREVIEW_SOURCE',OUT)
|
||||
@@ -0,0 +1,29 @@
|
||||
"""실제 영상의 시작·중간·끝 렌더를 모바일 크기로 나란히 보는 검사 보조물."""
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
from PIL import Image, ImageChops, ImageStat
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[3]
|
||||
BASE=ROOT/'projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2'
|
||||
artifact=json.loads((BASE/'artifact.json').read_text(encoding='utf-8'))
|
||||
results=[]
|
||||
for sample in artifact['payload']['preview']['samples']:
|
||||
if sample['media']['kind']!='video':
|
||||
continue
|
||||
folder=(ROOT/sample['media']['path']).parent
|
||||
evidence=json.loads((folder/'evidence.json').read_text(encoding='utf-8'))
|
||||
frames=evidence['logical_frames']
|
||||
selection=[frames[0],frames[len(frames)//2],frames[-1]]
|
||||
originals=[Image.open(folder/f'frame-{frame:08}.png').convert('RGB') for frame in selection]
|
||||
differences=[ImageChops.difference(originals[0],image) for image in originals[1:]]
|
||||
assert all(diff.getbbox() for diff in differences), '렌더 픽셀이 동일한 정지 클립'
|
||||
sheet=Image.new('RGB',(1080,640))
|
||||
for index,image in enumerate(originals):
|
||||
sheet.paste(image.resize((360,640),Image.Resampling.LANCZOS),(index*360,0))
|
||||
path=BASE/'generated-source'/f'mobile-{sample["cut_ids"][0]}.png'
|
||||
sheet.save(path)
|
||||
results.append(dict(cut_id=sample['cut_ids'][0],logical_frames=selection,pixel_sha256=[hashlib.sha256(image.tobytes()).hexdigest() for image in originals],mean_absolute_pixel_change=[sum(ImageStat.Stat(diff).mean)/3 for diff in differences],mobile_view_path=path.relative_to(ROOT).as_posix(),mobile_view_sha256=hashlib.sha256(path.read_bytes()).hexdigest(),visual_review='not_checked'))
|
||||
assert len(results)==3, '세 대표 영상이 아직 없음'
|
||||
(BASE/'motion-check.json').write_text(json.dumps(dict(result='GREEN',source_sha256=artifact['payload']['preview']['scene_file']['sha256'],views=results),ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('MOTION_GREEN: 세 영상의 실제 시작/중간/끝 RGB 픽셀 변화')
|
||||
@@ -0,0 +1,141 @@
|
||||
"""실측 07을 보존하며 새 08/09의 검토 초안만 작성한다."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
PROJECT = ROOT / 'projects/water-hydrogen-bond-surface-tension'
|
||||
DESCRIPTIONS = [
|
||||
'작은 청색 물방울의 둥근 형태를 보여주며 그 모양이 생기는 이유를 질문한다.',
|
||||
'거시적인 물방울에서 물속 분자 수준의 설명으로 관심을 옮긴다.',
|
||||
'물 분자가 산소 하나와 수소 둘로 이루어진 온전한 V자 구조임을 보여준다.',
|
||||
'산소 쪽의 부분 음전하를 강조하며 물 분자 전체가 음이온인 것으로 표현하지 않는다.',
|
||||
'수소 쪽의 부분 양전하를 산소 쪽과 구별하며 분자 전체의 중성은 유지한다.',
|
||||
'한 물 분자의 수소와 이웃 물 분자의 산소가 끌림의 양 끝이라는 관계를 보여준다.',
|
||||
'두 온전한 분자가 가까워지며 분자 사이 H···O 점선 길이가 줄고 내부 O–H 실선은 유지된다.',
|
||||
'서로 다른 분자의 H···O 사이 끌림에 수소결합이라는 이름을 연결한다.',
|
||||
'한 분자 내부의 O–H 실선과 분자 사이의 H···O 점선을 동시에 보여 두 결합을 구별한다.',
|
||||
'불규칙한 액체 분자 집단으로 범위를 넓혀 수소결합이 영구적인 끈은 아님을 설명한다.',
|
||||
'온전한 분자들이 소폭 움직이며 일부 이웃 사이 연결이 사라지고 새 연결이 생기는 과정을 보여준다.',
|
||||
'수소결합 연결이 반복해서 바뀌어 액체의 이웃 관계가 고정된 격자가 아님을 보여준다.',
|
||||
'많은 분자 사이의 끌림이 물끼리 함께 뭉치려는 응집 성질에 기여함을 연결한다.',
|
||||
'청색 물 체적과 위쪽 공기가 만나는 경계를 절개 설명 모형으로 제시한다.',
|
||||
'물 안쪽 분자를 강조해 여러 방향에 다른 물 분자 이웃이 있다는 환경을 보여준다.',
|
||||
'강조 대상을 물 안쪽에서 표면으로 옮겨 공기 방향에는 액체 물 분자 이웃이 적다는 차이를 보여준다.',
|
||||
'내부와 표면의 불규칙한 분자 배치를 유지해 액체 이웃 환경의 차이를 비교한다.',
|
||||
'같은 부피의 닫힌 물방울을 길게 변형해 표면을 넓히는 과정을 입체로 보여준다.',
|
||||
'같은 물의 양으로 길어진 모형을 유지하며 표면을 더 넓히는 데 에너지가 필요함을 설명한다.',
|
||||
'같은 부피의 물방울이 표면 넓이를 줄이는 쪽으로 모양을 바꾸려는 경향을 연결한다.',
|
||||
'길어진 물방울이 같은 부피를 유지하며 구형에 가까워지는 계획으로 표면을 줄이는 변화를 보여준다.',
|
||||
'물 표면이 넓어지는 데 저항하는 성질을 표면장력이라는 이름과 연결한다.',
|
||||
'길어진 물방울과 둥근 물방울의 모양을 같은 물의 양이라는 공통 조건에서 비교한다.',
|
||||
'같은 부피일 때 구형이 겉면을 가장 작게 만드는 관계를 두 입체 모형으로 보여준다.',
|
||||
'외부 영향이 작은 작은 물방울이 구형에 가까워지는 조건을 제시한다.',
|
||||
'작은 비접촉 방울과 큰 바닥 접촉 방울을 별도 조건 예시로 구별한다.',
|
||||
'바닥 접촉과 외부 영향으로 실제 물방울의 모양이 달라질 수 있음을 보여준다.',
|
||||
'작은 청색 물방울로 돌아와 그 주변에 별도 고체 껍질이나 막이 없음을 보여준다.',
|
||||
'분자 개념도에서 이웃 분자 사이 수소결합을 회수하며 원자와 내부 결합은 유지한다.',
|
||||
'물방울과 분자 개념도를 함께 유지해 표면을 넓히기 어려운 성질을 답의 원인으로 연결한다.',
|
||||
'분자 사이 끌림과 표면장력이 외부 영향이 작은 작은 물방울의 둥근 형태를 만든다는 답을 회수한다.'
|
||||
]
|
||||
|
||||
|
||||
def read(stage):
|
||||
return json.loads((PROJECT / f'stages/{stage}/r1/artifact.json').read_text(encoding='utf-8'))
|
||||
|
||||
|
||||
def write(stage, payload, inputs):
|
||||
folder = PROJECT / f'stages/{stage}/r2'
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
body = dict(schema_version='1.1',project_id=PROJECT.name,stage_id=stage,revision=2,inputs=[dict(stage_id=s,revision=r) for s,r in inputs],payload=payload)
|
||||
(folder/'artifact.json').write_text(json.dumps(body,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cuts = read('07-cuts')['payload']['cuts']
|
||||
# 읽은 근거는 입력 참조에 포함한다. 선행 결과는 수정하지 않는다.
|
||||
read('02-research')
|
||||
read('03-script')
|
||||
groups = [
|
||||
(1,2,'OpeningDroplet','bg-droplet','정면보다 약간 위의 3D 전경, 중심 물방울; 다음 분자 확대는 새 Scene에서 제시한다.',[
|
||||
'작은 청색 물방울이 크림색 공간 중앙에 구형에 가깝게 떠 있고 부드러운 하이라이트가 표면의 입체감을 보여준다.',
|
||||
'작은 물방울을 유지하고 오른쪽 위에 분자 확대의 위치를 표시하는 합성 주석 앵커를 제시한다.',
|
||||
'물방울과 확대 위치를 유지한 채 분자 설명으로 넘어갈 준비 상태다.'
|
||||
]),
|
||||
(3,5,'SingleMolecule','bg-molecules','3D 분자 근접, 약간 비스듬한 고정 카메라; 산소와 두 수소가 모두 가려지지 않는다.',[
|
||||
'산호색 O 구 하나·미색 H 구 둘·남색 O–H 원통 두 개의 V자 3D 분자; 주석은 합성용 앵커만 마련한다.',
|
||||
'온전한 V자 분자가 유지되고 산소 주석 앵커가 강조 위치에 있다.',
|
||||
'분자 전체 중성을 유지하며 Oδ− 합성 주석 위치만 강조한다.',
|
||||
'Oδ− 및 Hδ+ 합성 앵커가 모두 마련되며 원자 분리·전체 이온 표시는 없다.'
|
||||
]),
|
||||
(6,9,'BondPair','bg-molecules','실제 대표 Scene BondPair. 정면 위쪽 직교 카메라 (0,-12,6), ortho_scale=11.5; 내부 실선과 분자간 점선을 한 화면에 읽는다.',[
|
||||
'PairA=(-1.8,0,-0.3), PairB=(1.65,0.15,0.45). 두 온전한 V자 분자의 내부 남색 실선과 서로 다른 분자 H···O 금색 분절 원통을 구별한다.',
|
||||
'Blender F1018에서 PairA=(-1.8,0,-0.3), PairB=(1.65,0.15,0.45); H···O 점선의 양 끝이 실제 H/O 위치에 대응한다.',
|
||||
'Blender F1220에서 PairB=(0.85,0.15,0.45)로 가까워져 H···O 점선 길이가 줄었다. 내부 O–H 실선 두 개씩은 그대로다.',
|
||||
'같은 두 V자 분자·같은 카메라·금색 H···O 점선 유지; 수소결합 명칭은 합성 단계에서 별도로 배치한다.',
|
||||
'같은 두 분자의 남색 내부 O–H 실선과 금색 분자간 H···O 점선이 동시에 보이고 모든 원자는 원래 분자에 남아 있다.'
|
||||
]),
|
||||
(10,13,'LiquidNetwork','bg-molecules','불규칙한 3D 액체 분자 집단 근접. 고정 격자 대신 깊이·방향을 달리하고 카메라는 유지한다.',[
|
||||
'여러 온전한 V자 분자가 불규칙한 위치·방향에 있고 서로 다른 수의 금색 분자간 연결이 보인다.',
|
||||
'분자 전체가 소폭 이동하는 동안 일부 H···O 점선 연결이 사라질 준비 상태; 내부 실선은 유지된다.',
|
||||
'첫 H···O 점선 쌍이 사라지고 다른 분자 쌍의 점선이 나타난다. 원자 분리나 화학 반응은 없다.',
|
||||
'새 이웃과의 H···O 연결이 보이며 분자마다 연결 수·방향이 다르다. 영구 끈·정지 격자는 없다.',
|
||||
'불규칙한 집단을 유지하며 분자 사이의 응집이 거시적 물의 뭉침에 기여함을 다음 경계 모형으로 연결한다.'
|
||||
]),
|
||||
(14,17,'Interface','bg-interface','실제 대표 Scene Interface. 3D 체적을 옆면·뒤쪽만 남긴 잘라낸 설명 모형, 카메라 (0,-14,6), ortho_scale=13. 색 경계에 고체막을 추가하지 않는다.',[
|
||||
'아래쪽 청색 물 체적과 위쪽 공기 공간이 보이며 앞쪽 절개에 불규칙한 물 분자가 있다. 공기 이름은 추후 합성한다.',
|
||||
'물-공기 경계를 유지하고 안쪽 Liquid4 주변의 다양한 이웃이 보이도록 근접 강조를 시작한다.',
|
||||
'Blender F2807에서 Liquid4 안쪽 강조고리 scale=1, Liquid1 표면 강조고리 scale=0.01. 안쪽은 위·아래·옆에 이웃 물 분자가 있다.',
|
||||
'Blender F3051에서 안쪽 강조는 작아지고 Liquid1 표면 강조고리 scale=1. 표면보다 위 공기 쪽에는 액체 물 이웃을 배치하지 않는다; 별도 막은 없다.',
|
||||
'안쪽과 표면의 환경 차이가 보이는 불규칙한 분자 배치 유지. 공기는 단순화한 공간이며 진공이나 수증기 부재 주장이 아니다.'
|
||||
]),
|
||||
(18,22,'DropletStretch','bg-droplet','실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음.',[
|
||||
'Blender F3173의 닫힌 Droplet mesh scale=(1,1,1); 부드러운 청색 구형 물방울. 물의 양은 같은 조건이다.',
|
||||
'Blender F3373의 Droplet scale=(1.85,1/sqrt(1.85),1/sqrt(1.85)); 같은 부피를 유지하며 좌우로 길어지고 표면이 넓어지는 설명용 변형이다.',
|
||||
'길어진 같은 부피 물방울 유지; 에너지 필요 주석을 추후 합성한다. 실제 힘·에너지 수치는 표시하지 않는다.',
|
||||
'같은 부피 물방울이 되돌아갈 준비 상태이며 물 표면의 확장 저항을 설명한다.',
|
||||
'같은 부피의 닫힌 mesh가 구형에 가깝게 복귀한다. 복귀 애니메이션은 scenes 승인 후 정식 제작한다.',
|
||||
'구형에 가까운 물방울과 표면 확장 저항 설명이 남는다. 껍질·고체 표면층은 없다.'
|
||||
]),
|
||||
(23,24,'EqualVolumeComparison','bg-comparison','3D 비교 전경, 좌우 모형을 같은 카메라·같은 물의 양으로 비교한다. 수치·픽셀면적 비교 없음.',[
|
||||
'왼쪽 길어진 닫힌 물방울과 오른쪽 구형 물방울을 동일 부피 조건으로 나란히 배치한다.',
|
||||
'같은 물의 양 주석 앵커를 유지하며 두 형태의 겉면 차이를 보여준다.',
|
||||
'같은 부피에서 구형의 표면적이 작다는 관계를 청색 형태와 합성 주석으로 강조한다.'
|
||||
]),
|
||||
(25,27,'ShapeConditions','bg-comparison','조건 예시 3D 전경, 왼쪽 작은 비접촉 방울과 오른쪽 바닥에 닿은 큰 방울. 앞 비교와 별도 Scene.',[
|
||||
'작은 비접촉 물방울과 더 큰 바닥 접촉 물방울을 별도의 조건 예시로 배치한다. 이 컷은 동일 부피 비교가 아니다.',
|
||||
'외부 영향이 작은 작은 물방울이 구형에 가깝고 오른쪽 방울은 바닥과 접촉해 모양이 달라지는 예시다.',
|
||||
'바닥 접촉 방울의 납작한 형태가 보이며 중력·접촉면 영향의 조건 주석 앵커가 있다.',
|
||||
'두 조건 예시를 유지한다. 항상 완전한 구라는 단정·접촉각이나 크기 경계값은 없다.'
|
||||
]),
|
||||
(28,31,'AnswerDroplet','bg-droplet','도입과 같은 크림색 3D 공간·청색 방울 전경을 새 Scene에서 재제시하고 분자 개념도를 별도 위치에 둔다.',[
|
||||
'구형에 가까운 청색 물방울과 분리된 분자 개념도 위치가 보인다. 물방울 주변에 별도 껍질은 없다.',
|
||||
'동일 물방울 표면을 유지하며 추가 막이 없음을 보여준다.',
|
||||
'별도 분자 개념도에서 온전한 V자 분자 사이 금색 H···O 점선이 변하고 내부 남색 실선은 유지된다.',
|
||||
'물방울과 분자 개념도를 유지하며 표면을 넓히기 어려운 성질을 합성 주석으로 회수한다.',
|
||||
'외부 영향이 작은 작은 물방울의 둥근 형태와 수소결합·표면장력의 설명이 함께 남아 도입 질문에 답한다.'
|
||||
])
|
||||
]
|
||||
scenes, mapping = [], {}
|
||||
for start,end,name,bg,camera,states in groups:
|
||||
assert len(states)==end-start+2
|
||||
for n in range(start,end+1):
|
||||
cut=cuts[n-1]
|
||||
mapping[cut['id']]=(name,bg)
|
||||
scenes.append(dict(cut_id=cut['id'],description=DESCRIPTIONS[n-1],start_state=f"Scene={name}; F{cut['start_ms']//20+1}. "+states[n-start],end_state=f"Scene={name}; F{cut['end_ms']//20+1}. "+states[n-start+1],camera=camera))
|
||||
connections=[]
|
||||
for left,right in zip(scenes,scenes[1:]):
|
||||
same=mapping[left['cut_id']][0]==mapping[right['cut_id']][0]
|
||||
if same:
|
||||
assert left['end_state']==right['start_state']
|
||||
connections.append(dict(from_cut_id=left['cut_id'],to_cut_id=right['cut_id'],mode='continuous' if same else 'cut',reason='동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다.' if same else f"설명 범위·조건·시점 전환으로 {mapping[left['cut_id']][0]}에서 별도 {mapping[right['cut_id']][0]} Scene으로 전환한다."))
|
||||
profile=dict(engine='BLENDER_EEVEE',device='GPU',fps=50,width=1080,height=1920,samples=64,seed=0)
|
||||
write('08-scenes',dict(scenes=scenes,connections=connections,preview=dict(scene_file=dict(path=f'projects/{PROJECT.name}/stages/08-scenes/r2/generated-source/preview.blend',verified=False,sha256=None,blender_version=None),render_profile=profile,samples=[])),[('02-research',1),('03-script',1),('07-cuts',1)])
|
||||
common=["references/1.png의 단순 형태·정돈된 색·정보량을 교육용 3D로 해석한다. 크림색 단순 공간·부드러운 조명·선명한 형태를 반복한다.","산호색 O·미색 H·남색 내부 O–H 실선·금색 서로 다른 분자 H···O 점선·청색 물을 일관되게 사용한다.","상하단에는 제목·자막용 빈 공간을 두고 부분전하·한국어 주석은 나중 Remotion에서 검증해 합성한다. 3D에 글자를 굽지 않는다."]
|
||||
extras={
|
||||
'bg-droplet':('물방울의 크림색 3D 설명 공간',["청색 물방울은 닫힌 mesh이며 별도 껍질·막을 만들지 않는다.","같은 물의 양 변형은 실제 평가 mesh 부피를 시작·중간·끝에서 검사한다. 물리 시뮬레이션으로 주장하지 않는다."]),
|
||||
'bg-molecules':('분자의 3D 근접·불규칙 액체 연결 설명 공간',["온전한 물 분자마다 O1H2·V자 형태·내부 O–H 실선 두 개를 유지한다. 각도·거리·힘 수치를 표시하지 않는다.","분자간 H···O 점선은 서로 다른 분자의 원자만 연결한다. 액체는 불규칙한 이웃·변하는 연결이고 모든 분자의 연결 수를 고정하지 않는다."]),
|
||||
'bg-interface':('물-공기 경계의 3D 체적·절개 설명 공간',["물 체적을 뒤쪽·옆면에 남긴 개념도이며 위 공기는 진공 주장이 아니다. 별도 고체 표면층·전체 아래 가속 화살표가 없다.","안쪽은 다양한 방향의 액체 이웃, 표면은 위쪽 액체 이웃이 적은 환경으로 구별한다. 강조고리는 설명 표식이고 물 껍질이 아니다."]),
|
||||
'bg-comparison':('같은 부피 비교·조건 예시의 크림색 3D 공간',["동일 부피 비교와 외부 영향 조건 예시는 별도 Scene으로 구별한다.","바닥은 조건 예시에만 등장한다. 중력·접촉각·크기 경계·표면장력 수치를 임의로 표시하지 않는다."])
|
||||
}
|
||||
backgrounds=[dict(id=k,description=v[0],fixed_features=common+v[1]) for k,v in extras.items()]
|
||||
write('09-entities',dict(characters=[],backgrounds=backgrounds,appearances=[dict(cut_id=c['id'],character_ids=[],background_ids=[mapping[c['id']][1]]) for c in cuts]),[('02-research',1),('03-script',1),('07-cuts',1),('08-scenes',2)])
|
||||
@@ -0,0 +1,65 @@
|
||||
"""기존 실행기로 세 대표 컷의 PNG/MP4를 만든다. 전 컷 제작은 하지 않는다."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
BASE = Path('projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2')
|
||||
BLENDER = 'C:/Program Files/Blender Foundation/Blender 5.2/blender.exe'
|
||||
PYTHON = 'C:/Users/baram/anaconda3/python.exe'
|
||||
COMPOSITOR = Path('C:/git/ShortVideoGenerator/projects/water-hydrogen-bond-surface-tension/renderer/node_modules/@remotion/compositor-win32-x64-msvc')
|
||||
SOURCE = (BASE/'generated-source/preview.blend').as_posix()
|
||||
ARTIFACT = (BASE/'artifact.json').as_posix()
|
||||
PROJECT = 'projects/water-hydrogen-bond-surface-tension/project.json'
|
||||
PLAN = [('c07','BondPair','PairA',1017,1219,1219),('c16','Interface','WaterCutawayBody',2806,3050,3050),('c18','DropletStretch','Droplet',3172,3372,3372)]
|
||||
|
||||
|
||||
def run(args, records):
|
||||
result=subprocess.run(args,cwd=ROOT,text=True,encoding='utf-8',capture_output=True)
|
||||
records.append(dict(command=args,exit_code=result.returncode))
|
||||
(ROOT/BASE/'cli-commands.json').write_text(json.dumps(records,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
if result.returncode:
|
||||
print(result.stdout,result.stderr,flush=True)
|
||||
raise RuntimeError('실행 실패; 명령 기록과 기존 시도 보존')
|
||||
parsed=json.loads(result.stdout)
|
||||
records[-1]['source_sha256']=parsed['scene_file']['sha256']
|
||||
records[-1]['scene_name']=parsed.get('scene_name')
|
||||
if 'media' in parsed:
|
||||
records[-1]['media']=parsed['media']
|
||||
records[-1]['evidence_path']=(Path(parsed['media']['path']).parent/'evidence.json').as_posix()
|
||||
(ROOT/BASE/'cli-commands.json').write_text(json.dumps(records,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('EXIT_0',args[5],parsed.get('scene_name'),flush=True)
|
||||
return parsed
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
parser=argparse.ArgumentParser()
|
||||
parser.add_argument('--kind',choices=['image','video'],required=True)
|
||||
args=parser.parse_args()
|
||||
record_path=ROOT/BASE/'cli-commands.json'
|
||||
records=json.loads(record_path.read_text(encoding='utf-8')) if record_path.exists() else []
|
||||
for record in records:
|
||||
record.pop('stdout',None)
|
||||
record.pop('stderr',None)
|
||||
artifact=json.loads((ROOT/ARTIFACT).read_text(encoding='utf-8'))
|
||||
for cut,scene,obj,start,end,boundary in PLAN:
|
||||
assert not any(s['id']==cut+'-'+args.kind for s in artifact['payload']['preview']['samples']), '이미 반영한 sample을 같은 버전에서 재생성하지 않는다'
|
||||
common=[PYTHON,'-B','-X','utf8','tools/blender/run.py']
|
||||
inspect=run(common+['inspect','--blender',BLENDER,'--source',SOURCE,'--scene',scene,'--require-object',obj],records)
|
||||
artifact['payload']['preview']['scene_file']=inspect['scene_file']
|
||||
(ROOT/ARTIFACT).write_text(json.dumps(artifact,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
options=['--frame',str(boundary)] if args.kind=='image' else ['--start',str(start),'--end',str(end)]
|
||||
rendered=run(common+['render','--mode','preview','--project',PROJECT,'--artifact',ARTIFACT,'--output-revision','2','--blender',BLENDER,'--source',SOURCE,'--scene',scene,'--require-object',obj,'--cut-id',cut,'--ffmpeg',str(COMPOSITOR/'ffmpeg.exe'),'--ffprobe',str(COMPOSITOR/'ffprobe.exe')]+options,records)
|
||||
# 실행기의 실제 증거만 미디어 메타데이터로 사용한다.
|
||||
evidence=rendered
|
||||
if args.kind=='video':
|
||||
frames=[ROOT/Path(evidence['media']['path']).parent/f'frame-{f:08}.png' for f in (start,start+(end-start)//2,end-1)]
|
||||
pixels=[Image.open(f).convert('RGB') for f in frames]
|
||||
if not (ImageChops.difference(pixels[0],pixels[1]).getbbox() and ImageChops.difference(pixels[0],pixels[2]).getbbox()):
|
||||
(ROOT/Path(evidence['media']['path']).parent/'visual-failure.json').write_text(json.dumps(dict(error='실제 렌더 프레임이 정지함; 미디어 반영 금지',logical_frames=[start,start+(end-start)//2,end-1]),ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
raise RuntimeError('실제 렌더 프레임이 정지함; 미디어 반영 금지')
|
||||
artifact['payload']['preview']['samples'].append(dict(id=cut+'-'+args.kind,cut_ids=[cut],media=evidence['media']))
|
||||
(ROOT/ARTIFACT).write_text(json.dumps(artifact,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('READY',cut,args.kind,evidence['media']['path'],flush=True)
|
||||
@@ -0,0 +1,29 @@
|
||||
"""없는 원본·잘못된 Scene·원자 수·부피를 실제 Blender로 거부하는지 검사한다."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[3]
|
||||
BASE=ROOT/'projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2'
|
||||
BLENDER='C:/Program Files/Blender Foundation/Blender 5.2/blender.exe'
|
||||
SOURCE=BASE/'generated-source/preview.blend'
|
||||
VERIFY=ROOT/'projects/water-hydrogen-bond-surface-tension/blender/verify_model.py'
|
||||
records=[]
|
||||
|
||||
|
||||
def execute(args, expected):
|
||||
command=[BLENDER,'--background','--disable-autoexec','--python-exit-code','1']+args
|
||||
result=subprocess.run(command,cwd=ROOT,capture_output=True,text=True,encoding='utf-8')
|
||||
assert result.returncode==expected,(command,result.stdout,result.stderr)
|
||||
records.append(dict(command=command,exit_code=result.returncode,expected_exit_code=expected,reason=result.stderr.strip().splitlines()[-1:] if result.stderr.strip() else []))
|
||||
|
||||
|
||||
for name,edit in [('invalid-default','bpy.ops.wm.read_factory_settings(use_empty=True)'),('invalid-atoms',"bpy.data.objects.remove(bpy.data.objects['PairA_H2'],do_unlink=True)"),('invalid-volume',"bpy.data.objects['Droplet'].animation_data.drivers[1].driver.expression='1'")]:
|
||||
fixture=BASE/'generated-source'/f'{name}.blend'
|
||||
expression=f'import bpy; bpy.ops.wm.open_mainfile(filepath={str(SOURCE)!r}); {edit}; bpy.ops.wm.save_as_mainfile(filepath={str(fixture)!r})'
|
||||
execute(['--python-expr',expression],0)
|
||||
execute(['--python',str(VERIFY),'--','--source',str(fixture),'--out',str(BASE/'generated-source/invalid-result.json')],1)
|
||||
execute(['--python',str(VERIFY),'--','--source',str(BASE/'generated-source/missing.blend'),'--out',str(BASE/'generated-source/invalid-result.json')],1)
|
||||
execute(['--python',str(VERIFY),'--','--source',str(SOURCE),'--out',str(BASE/'model-check.json')],0)
|
||||
(BASE/'model-test-results.json').write_text(json.dumps(dict(result='GREEN',negative_cases=4,positive_cases=1,executions=records),ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('4 negative cases RED, real model GREEN')
|
||||
@@ -0,0 +1,75 @@
|
||||
"""컷·연결·새 버전 미디어와 실제 실행 증거를 검사한다."""
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import jsonschema
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[3]
|
||||
PROJECT=ROOT/'projects/water-hydrogen-bond-surface-tension'
|
||||
|
||||
|
||||
def read(path):
|
||||
return json.loads(path.read_text(encoding='utf-8'))
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
a=read(PROJECT/'stages/08-scenes/r2/artifact.json')
|
||||
b=read(PROJECT/'stages/09-entities/r2/artifact.json')
|
||||
schema=read(ROOT/'contracts/artifact.schema.json')
|
||||
jsonschema.validate(a,schema)
|
||||
jsonschema.validate(b,schema)
|
||||
cuts=read(PROJECT/'stages/07-cuts/r1/artifact.json')['payload']
|
||||
ids=[c['id'] for c in cuts['cuts']]
|
||||
assert len(ids)==31 and len(cuts['captions'])==38 and cuts['narration_duration_ms']==110560
|
||||
assert cuts['cuts'][0]['start_ms']==0 and cuts['cuts'][-1]['end_ms']==110560
|
||||
assert all(0<c['end_ms']-c['start_ms']<=5000 for c in cuts['cuts'])
|
||||
assert all(x['end_ms']==y['start_ms'] for x,y in zip(cuts['cuts'],cuts['cuts'][1:]))
|
||||
scenes=a['payload']['scenes']
|
||||
connections=a['payload']['connections']
|
||||
assert [s['cut_id'] for s in scenes]==ids and len(connections)==30
|
||||
assert [(c['from_cut_id'],c['to_cut_id']) for c in connections]==list(zip(ids,ids[1:]))
|
||||
for left,right,connection in zip(scenes,scenes[1:],connections):
|
||||
if connection['mode']=='continuous':
|
||||
assert left['end_state']==right['start_state']
|
||||
assert left['camera']==right['camera']
|
||||
else:
|
||||
assert left['start_state'].split(';')[0]!=right['start_state'].split(';')[0]
|
||||
assert b['payload']['characters']==[]
|
||||
assert [p['cut_id'] for p in b['payload']['appearances']]==ids
|
||||
bg={g['id'] for g in b['payload']['backgrounds']}
|
||||
assert all(not p['character_ids'] and set(p['background_ids'])<=bg for p in b['payload']['appearances'])
|
||||
preview=a['payload']['preview']
|
||||
source=preview['scene_file']
|
||||
assert source['verified'] and hashlib.sha256((ROOT/source['path']).read_bytes()).hexdigest()==source['sha256']
|
||||
samples=preview['samples']
|
||||
assert len(samples)==6 and len({s['id'] for s in samples})==6
|
||||
assets=[dict(path=source['path'],sha256=source['sha256'])]
|
||||
frame_reviews=[]
|
||||
for sample in samples:
|
||||
media=sample['media']
|
||||
path=ROOT/media['path']
|
||||
assert path.is_relative_to(PROJECT/'stages/08-scenes/r2') and media['verified']
|
||||
evidence_path=path.parent/'evidence.json'
|
||||
evidence=read(evidence_path)
|
||||
assert evidence['media']==media and evidence['scene_file']==source and evidence['render_profile']==preview['render_profile']
|
||||
assert (media['width'],media['height'])==(1080,1920)
|
||||
if media['kind']=='video':
|
||||
cut=next(c for c in cuts['cuts'] if c['id']==sample['cut_ids'][0])
|
||||
assert media['duration_ms']==cut['end_ms']-cut['start_ms']
|
||||
assert evidence['logical_frames']==list(range(cut['start_ms']//20,cut['end_ms']//20))
|
||||
for frame in (evidence['logical_frames'][0],evidence['logical_frames'][len(evidence['logical_frames'])//2],evidence['logical_frames'][-1]):
|
||||
f=path.parent/f'frame-{frame:08}.png'
|
||||
frame_reviews.append(f.relative_to(ROOT).as_posix())
|
||||
assets.append(dict(path=f.relative_to(ROOT).as_posix(),sha256=hashlib.sha256(f.read_bytes()).hexdigest()))
|
||||
for f in (path,evidence_path):
|
||||
assets.append(dict(path=f.relative_to(ROOT).as_posix(),sha256=hashlib.sha256(f.read_bytes()).hexdigest()))
|
||||
result=dict(result='GREEN',cuts=31,captions=38,connections=30,total_ms=110560,fps=50,total_frames=5528,profile=preview['render_profile'],source_sha256=source['sha256'],samples=6,visual_review_frames=frame_reviews,actual_assets=assets,scenes_approval='pending',formal_10_12='not_started')
|
||||
motion=read(PROJECT/'stages/08-scenes/r2/motion-check.json')
|
||||
assert motion['source_sha256']==source['sha256'] and len(motion['views'])==3
|
||||
for view in motion['views']:
|
||||
assert all(change>0 for change in view['mean_absolute_pixel_change'])
|
||||
path=ROOT/view['mobile_view_path']
|
||||
assert hashlib.sha256(path.read_bytes()).hexdigest()==view['mobile_view_sha256']
|
||||
assets.append(dict(path=view['mobile_view_path'],sha256=view['mobile_view_sha256']))
|
||||
(PROJECT/'stages/08-scenes/r2/design-check.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('DESIGN_GREEN: 31 cuts, 30 boundaries, 38 captions, 6 actual samples')
|
||||
@@ -0,0 +1,112 @@
|
||||
"""Blender 내부에서 실제 원본·구조·평가 부피를 검사한다."""
|
||||
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
|
||||
|
||||
|
||||
def verify(source):
|
||||
assert source.is_file(), "원본 파일 없음"
|
||||
bpy.ops.wm.open_mainfile(filepath=str(source))
|
||||
expected = {"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, "외부 자산"
|
||||
checks = {}
|
||||
for name, bounds in expected.items():
|
||||
scene = bpy.data.scenes[name]
|
||||
bpy.context.window.scene = scene
|
||||
assert (scene.frame_start, scene.frame_end) == bounds, "실측 범위를 덮지 못함"
|
||||
assert scene.camera and scene.render.fps == 50
|
||||
assert (scene.render.resolution_x, scene.render.resolution_y) == (1080, 1920)
|
||||
assert scene.render.engine == "BLENDER_EEVEE"
|
||||
molecules = [o for o in scene.objects if o.get("role") == "molecule"]
|
||||
for mol in molecules:
|
||||
atoms = [o for o in mol.children if o.get("role") == "atom"]
|
||||
assert sorted(o["element"] for o in atoms) == ["H", "H", "O"], "O1H2 위반"
|
||||
assert all(o.type == "MESH" and len(o.data.vertices) > 20 for o in atoms)
|
||||
assert all(o.data.materials[0].name==('O_Coral' if o['element']=='O' else 'H_Ivory') for o in atoms), '원자 색 재질 불일치'
|
||||
oxygen = next(o for o in atoms if o["element"] == "O")
|
||||
hydrogen = [o for o in atoms if o["element"] == "H"]
|
||||
assert (hydrogen[0].location - oxygen.location).cross(hydrogen[1].location - oxygen.location).length > .01, "V형태 아님"
|
||||
bonds = [o for o in mol.children if o.get("role") == "covalent"]
|
||||
assert len(bonds) == 2 and all(o.type == "MESH" for o in bonds), "내부 실선 두 개 누락"
|
||||
assert all(o.data.materials[0].name=='OH_Navy' for o in bonds), '내부 실선 재질 불일치'
|
||||
assert all(min((bond.location - (h.location+oxygen.location)/2).length for h in hydrogen)<.001 for bond in bonds), "실선의 O–H 위치 불일치"
|
||||
for segment in [o for o in scene.objects if o.get("role") == "hydrogen_bond"]:
|
||||
h, oxygen = (scene.objects[segment[k]] for k in ("hydrogen", "oxygen"))
|
||||
assert h["element"] == "H" and oxygen["element"] == "O" and h.parent != oxygen.parent, "H···O 분자간 점선 위반"
|
||||
assert segment.type == "MESH" and segment.dimensions.length > 0
|
||||
assert segment.data.materials[0].name=='HO_Gold', '분자간 점선 재질 불일치'
|
||||
checks[name] = {"molecules": len(molecules), "frame_range": list(bounds)}
|
||||
assert checks["BondPair"]["molecules"] == 2 and checks["Interface"]["molecules"] >= 8
|
||||
pair = bpy.data.scenes["BondPair"]
|
||||
bpy.context.window.scene = pair
|
||||
pair.frame_set(1018)
|
||||
before = pair.objects["PairB"].matrix_world.translation.copy()
|
||||
pair.frame_set(1117)
|
||||
assert (pair.objects["PairB"].matrix_world.translation - before).length > .1, "실제 3D 이동 없음"
|
||||
interface = bpy.data.scenes['Interface']
|
||||
bpy.context.window.scene = interface
|
||||
connection_sets, neighborhoods = [], []
|
||||
for frame in (2807,2907,3051):
|
||||
interface.frame_set(frame)
|
||||
active = sorted({(o['hydrogen'],o['oxygen']) for o in interface.objects if o.get('role')=='hydrogen_bond' and o.scale.x > .5})
|
||||
connection_sets.append(active)
|
||||
for segment in [o for o in interface.objects if o.get('role')=='hydrogen_bond']:
|
||||
h, oxygen = (interface.objects[segment[k]] for k in ('hydrogen','oxygen'))
|
||||
a,b = h.matrix_world.translation, oxygen.matrix_world.translation
|
||||
assert (segment.location-a).cross(b-a).length < .002, '점선이 실제 H···O 선분에서 벗어남'
|
||||
counts=[]
|
||||
for name in ('Liquid4','Liquid1'):
|
||||
center=interface.objects[name].matrix_world.translation
|
||||
neighbors=[o.matrix_world.translation-center for o in interface.objects if o.get('role')=='molecule' and o.name!=name and (o.matrix_world.translation-center).length<2.2]
|
||||
counts.append(dict(neighbors=len(neighbors),above=sum(p.z>.7 for p in neighbors)))
|
||||
assert counts[0]['neighbors']>counts[1]['neighbors'] and counts[0]['above']>0 and counts[1]['above']==0, '내부/표면 이웃 배치 차이 없음'
|
||||
neighborhoods.append(counts)
|
||||
assert connection_sets[0] != connection_sets[-1], '실제 활성 연결 쌍이 바뀌지 않음'
|
||||
scene = bpy.data.scenes["DropletStretch"]
|
||||
bpy.context.window.scene = scene
|
||||
droplet = scene.objects["Droplet"]
|
||||
volumes, scales = [], []
|
||||
for frame in (3173, 3223, 3273, 3323, 3373):
|
||||
scene.frame_set(frame)
|
||||
deps = bpy.context.evaluated_depsgraph_get()
|
||||
evaluated = droplet.evaluated_get(deps)
|
||||
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)
|
||||
volumes.append(abs(bm.calc_volume(signed=True)))
|
||||
scales.append(list(evaluated.scale))
|
||||
bm.free()
|
||||
evaluated.to_mesh_clear()
|
||||
relative = max(abs(v / volumes[0] - 1) for v in volumes)
|
||||
assert relative <= .01, "중간 프레임 포함 부피 변화 1% 초과"
|
||||
assert abs(scales[-1][0] - scales[0][0]) > .5, "변형이 실제 평가되지 않음"
|
||||
anchors = {}
|
||||
for name, logical in (("BondPair", 1219), ("Interface", 3050), ("DropletStretch", 3372)):
|
||||
scene = bpy.data.scenes[name]
|
||||
bpy.context.window.scene = scene
|
||||
scene.frame_set(logical + 1)
|
||||
anchors[name] = {"logical_frame":logical,"labels":{}}
|
||||
for obj in scene.objects:
|
||||
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}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--source", required=True)
|
||||
parser.add_argument("--out", required=True)
|
||||
args = parser.parse_args(sys.argv[sys.argv.index("--") + 1:])
|
||||
result = verify(Path(args.source).resolve())
|
||||
Path(args.out).write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print("MODEL_GREEN", result["source_sha256"], result["relative_volume_change"])
|
||||
@@ -0,0 +1,2 @@
|
||||
/generated-source/
|
||||
/render-attempts/
|
||||
@@ -4,6 +4,10 @@
|
||||
"stage_id": "08-scenes",
|
||||
"revision": 2,
|
||||
"inputs": [
|
||||
{
|
||||
"stage_id": "02-research",
|
||||
"revision": 1
|
||||
},
|
||||
{
|
||||
"stage_id": "03-script",
|
||||
"revision": 1
|
||||
@@ -11,15 +15,512 @@
|
||||
{
|
||||
"stage_id": "07-cuts",
|
||||
"revision": 1
|
||||
},
|
||||
{
|
||||
"stage_id": "02-research",
|
||||
"revision": 1
|
||||
}
|
||||
],
|
||||
"payload": {
|
||||
"scenes": [],
|
||||
"connections": [],
|
||||
"preview": null
|
||||
"scenes": [
|
||||
{
|
||||
"cut_id": "c01",
|
||||
"description": "작은 청색 물방울의 둥근 형태를 보여주며 그 모양이 생기는 이유를 질문한다.",
|
||||
"start_state": "Scene=OpeningDroplet; F1. 작은 청색 물방울이 크림색 공간 중앙에 구형에 가깝게 떠 있고 부드러운 하이라이트가 표면의 입체감을 보여준다.",
|
||||
"end_state": "Scene=OpeningDroplet; F220. 작은 물방울을 유지하고 오른쪽 위에 분자 확대의 위치를 표시하는 합성 주석 앵커를 제시한다.",
|
||||
"camera": "정면보다 약간 위의 3D 전경, 중심 물방울; 다음 분자 확대는 새 Scene에서 제시한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c02",
|
||||
"description": "거시적인 물방울에서 물속 분자 수준의 설명으로 관심을 옮긴다.",
|
||||
"start_state": "Scene=OpeningDroplet; F220. 작은 물방울을 유지하고 오른쪽 위에 분자 확대의 위치를 표시하는 합성 주석 앵커를 제시한다.",
|
||||
"end_state": "Scene=OpeningDroplet; F397. 물방울과 확대 위치를 유지한 채 분자 설명으로 넘어갈 준비 상태다.",
|
||||
"camera": "정면보다 약간 위의 3D 전경, 중심 물방울; 다음 분자 확대는 새 Scene에서 제시한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c03",
|
||||
"description": "물 분자가 산소 하나와 수소 둘로 이루어진 온전한 V자 구조임을 보여준다.",
|
||||
"start_state": "Scene=SingleMolecule; F397. 산호색 O 구 하나·미색 H 구 둘·남색 O–H 원통 두 개의 V자 3D 분자; 주석은 합성용 앵커만 마련한다.",
|
||||
"end_state": "Scene=SingleMolecule; F597. 온전한 V자 분자가 유지되고 산소 주석 앵커가 강조 위치에 있다.",
|
||||
"camera": "3D 분자 근접, 약간 비스듬한 고정 카메라; 산소와 두 수소가 모두 가려지지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c04",
|
||||
"description": "산소 쪽의 부분 음전하를 강조하며 물 분자 전체가 음이온인 것으로 표현하지 않는다.",
|
||||
"start_state": "Scene=SingleMolecule; F597. 온전한 V자 분자가 유지되고 산소 주석 앵커가 강조 위치에 있다.",
|
||||
"end_state": "Scene=SingleMolecule; F740. 분자 전체 중성을 유지하며 Oδ− 합성 주석 위치만 강조한다.",
|
||||
"camera": "3D 분자 근접, 약간 비스듬한 고정 카메라; 산소와 두 수소가 모두 가려지지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c05",
|
||||
"description": "수소 쪽의 부분 양전하를 산소 쪽과 구별하며 분자 전체의 중성은 유지한다.",
|
||||
"start_state": "Scene=SingleMolecule; F740. 분자 전체 중성을 유지하며 Oδ− 합성 주석 위치만 강조한다.",
|
||||
"end_state": "Scene=SingleMolecule; F905. Oδ− 및 Hδ+ 합성 앵커가 모두 마련되며 원자 분리·전체 이온 표시는 없다.",
|
||||
"camera": "3D 분자 근접, 약간 비스듬한 고정 카메라; 산소와 두 수소가 모두 가려지지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c06",
|
||||
"description": "한 물 분자의 수소와 이웃 물 분자의 산소가 끌림의 양 끝이라는 관계를 보여준다.",
|
||||
"start_state": "Scene=BondPair; F905. PairA=(-1.8,0,-0.3), PairB=(1.65,0.15,0.45). 두 온전한 V자 분자의 내부 남색 실선과 서로 다른 분자 H···O 금색 분절 원통을 구별한다.",
|
||||
"end_state": "Scene=BondPair; F1018. Blender F1018에서 PairA=(-1.8,0,-0.3), PairB=(1.65,0.15,0.45); H···O 점선의 양 끝이 실제 H/O 위치에 대응한다.",
|
||||
"camera": "실제 대표 Scene BondPair. 정면 위쪽 직교 카메라 (0,-12,6), ortho_scale=11.5; 내부 실선과 분자간 점선을 한 화면에 읽는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c07",
|
||||
"description": "두 온전한 분자가 가까워지며 분자 사이 H···O 점선 길이가 줄고 내부 O–H 실선은 유지된다.",
|
||||
"start_state": "Scene=BondPair; F1018. Blender F1018에서 PairA=(-1.8,0,-0.3), PairB=(1.65,0.15,0.45); H···O 점선의 양 끝이 실제 H/O 위치에 대응한다.",
|
||||
"end_state": "Scene=BondPair; F1220. Blender F1220에서 PairB=(0.85,0.15,0.45)로 가까워져 H···O 점선 길이가 줄었다. 내부 O–H 실선 두 개씩은 그대로다.",
|
||||
"camera": "실제 대표 Scene BondPair. 정면 위쪽 직교 카메라 (0,-12,6), ortho_scale=11.5; 내부 실선과 분자간 점선을 한 화면에 읽는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c08",
|
||||
"description": "서로 다른 분자의 H···O 사이 끌림에 수소결합이라는 이름을 연결한다.",
|
||||
"start_state": "Scene=BondPair; F1220. Blender F1220에서 PairB=(0.85,0.15,0.45)로 가까워져 H···O 점선 길이가 줄었다. 내부 O–H 실선 두 개씩은 그대로다.",
|
||||
"end_state": "Scene=BondPair; F1425. 같은 두 V자 분자·같은 카메라·금색 H···O 점선 유지; 수소결합 명칭은 합성 단계에서 별도로 배치한다.",
|
||||
"camera": "실제 대표 Scene BondPair. 정면 위쪽 직교 카메라 (0,-12,6), ortho_scale=11.5; 내부 실선과 분자간 점선을 한 화면에 읽는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c09",
|
||||
"description": "한 분자 내부의 O–H 실선과 분자 사이의 H···O 점선을 동시에 보여 두 결합을 구별한다.",
|
||||
"start_state": "Scene=BondPair; F1425. 같은 두 V자 분자·같은 카메라·금색 H···O 점선 유지; 수소결합 명칭은 합성 단계에서 별도로 배치한다.",
|
||||
"end_state": "Scene=BondPair; F1637. 같은 두 분자의 남색 내부 O–H 실선과 금색 분자간 H···O 점선이 동시에 보이고 모든 원자는 원래 분자에 남아 있다.",
|
||||
"camera": "실제 대표 Scene BondPair. 정면 위쪽 직교 카메라 (0,-12,6), ortho_scale=11.5; 내부 실선과 분자간 점선을 한 화면에 읽는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c10",
|
||||
"description": "불규칙한 액체 분자 집단으로 범위를 넓혀 수소결합이 영구적인 끈은 아님을 설명한다.",
|
||||
"start_state": "Scene=LiquidNetwork; F1637. 여러 온전한 V자 분자가 불규칙한 위치·방향에 있고 서로 다른 수의 금색 분자간 연결이 보인다.",
|
||||
"end_state": "Scene=LiquidNetwork; F1779. 분자 전체가 소폭 이동하는 동안 일부 H···O 점선 연결이 사라질 준비 상태; 내부 실선은 유지된다.",
|
||||
"camera": "불규칙한 3D 액체 분자 집단 근접. 고정 격자 대신 깊이·방향을 달리하고 카메라는 유지한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c11",
|
||||
"description": "온전한 분자들이 소폭 움직이며 일부 이웃 사이 연결이 사라지고 새 연결이 생기는 과정을 보여준다.",
|
||||
"start_state": "Scene=LiquidNetwork; F1779. 분자 전체가 소폭 이동하는 동안 일부 H···O 점선 연결이 사라질 준비 상태; 내부 실선은 유지된다.",
|
||||
"end_state": "Scene=LiquidNetwork; F2019. 첫 H···O 점선 쌍이 사라지고 다른 분자 쌍의 점선이 나타난다. 원자 분리나 화학 반응은 없다.",
|
||||
"camera": "불규칙한 3D 액체 분자 집단 근접. 고정 격자 대신 깊이·방향을 달리하고 카메라는 유지한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c12",
|
||||
"description": "수소결합 연결이 반복해서 바뀌어 액체의 이웃 관계가 고정된 격자가 아님을 보여준다.",
|
||||
"start_state": "Scene=LiquidNetwork; F2019. 첫 H···O 점선 쌍이 사라지고 다른 분자 쌍의 점선이 나타난다. 원자 분리나 화학 반응은 없다.",
|
||||
"end_state": "Scene=LiquidNetwork; F2257. 새 이웃과의 H···O 연결이 보이며 분자마다 연결 수·방향이 다르다. 영구 끈·정지 격자는 없다.",
|
||||
"camera": "불규칙한 3D 액체 분자 집단 근접. 고정 격자 대신 깊이·방향을 달리하고 카메라는 유지한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c13",
|
||||
"description": "많은 분자 사이의 끌림이 물끼리 함께 뭉치려는 응집 성질에 기여함을 연결한다.",
|
||||
"start_state": "Scene=LiquidNetwork; F2257. 새 이웃과의 H···O 연결이 보이며 분자마다 연결 수·방향이 다르다. 영구 끈·정지 격자는 없다.",
|
||||
"end_state": "Scene=LiquidNetwork; F2469. 불규칙한 집단을 유지하며 분자 사이의 응집이 거시적 물의 뭉침에 기여함을 다음 경계 모형으로 연결한다.",
|
||||
"camera": "불규칙한 3D 액체 분자 집단 근접. 고정 격자 대신 깊이·방향을 달리하고 카메라는 유지한다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c14",
|
||||
"description": "청색 물 체적과 위쪽 공기가 만나는 경계를 절개 설명 모형으로 제시한다.",
|
||||
"start_state": "Scene=Interface; F2469. 아래쪽 청색 물 체적과 위쪽 공기 공간이 보이며 앞쪽 절개에 불규칙한 물 분자가 있다. 공기 이름은 추후 합성한다.",
|
||||
"end_state": "Scene=Interface; F2605. 물-공기 경계를 유지하고 안쪽 Liquid4 주변의 다양한 이웃이 보이도록 근접 강조를 시작한다.",
|
||||
"camera": "실제 대표 Scene Interface. 3D 체적을 옆면·뒤쪽만 남긴 잘라낸 설명 모형, 카메라 (0,-14,6), ortho_scale=13. 색 경계에 고체막을 추가하지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c15",
|
||||
"description": "물 안쪽 분자를 강조해 여러 방향에 다른 물 분자 이웃이 있다는 환경을 보여준다.",
|
||||
"start_state": "Scene=Interface; F2605. 물-공기 경계를 유지하고 안쪽 Liquid4 주변의 다양한 이웃이 보이도록 근접 강조를 시작한다.",
|
||||
"end_state": "Scene=Interface; F2807. Blender F2807에서 Liquid4 안쪽 강조고리 scale=1, Liquid1 표면 강조고리 scale=0.01. 안쪽은 위·아래·옆에 이웃 물 분자가 있다.",
|
||||
"camera": "실제 대표 Scene Interface. 3D 체적을 옆면·뒤쪽만 남긴 잘라낸 설명 모형, 카메라 (0,-14,6), ortho_scale=13. 색 경계에 고체막을 추가하지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c16",
|
||||
"description": "강조 대상을 물 안쪽에서 표면으로 옮겨 공기 방향에는 액체 물 분자 이웃이 적다는 차이를 보여준다.",
|
||||
"start_state": "Scene=Interface; F2807. Blender F2807에서 Liquid4 안쪽 강조고리 scale=1, Liquid1 표면 강조고리 scale=0.01. 안쪽은 위·아래·옆에 이웃 물 분자가 있다.",
|
||||
"end_state": "Scene=Interface; F3051. Blender F3051에서 안쪽 강조는 작아지고 Liquid1 표면 강조고리 scale=1. 표면보다 위 공기 쪽에는 액체 물 이웃을 배치하지 않는다; 별도 막은 없다.",
|
||||
"camera": "실제 대표 Scene Interface. 3D 체적을 옆면·뒤쪽만 남긴 잘라낸 설명 모형, 카메라 (0,-14,6), ortho_scale=13. 색 경계에 고체막을 추가하지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c17",
|
||||
"description": "내부와 표면의 불규칙한 분자 배치를 유지해 액체 이웃 환경의 차이를 비교한다.",
|
||||
"start_state": "Scene=Interface; F3051. Blender F3051에서 안쪽 강조는 작아지고 Liquid1 표면 강조고리 scale=1. 표면보다 위 공기 쪽에는 액체 물 이웃을 배치하지 않는다; 별도 막은 없다.",
|
||||
"end_state": "Scene=Interface; F3173. 안쪽과 표면의 환경 차이가 보이는 불규칙한 분자 배치 유지. 공기는 단순화한 공간이며 진공이나 수증기 부재 주장이 아니다.",
|
||||
"camera": "실제 대표 Scene Interface. 3D 체적을 옆면·뒤쪽만 남긴 잘라낸 설명 모형, 카메라 (0,-14,6), ortho_scale=13. 색 경계에 고체막을 추가하지 않는다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c18",
|
||||
"description": "같은 부피의 닫힌 물방울을 길게 변형해 표면을 넓히는 과정을 입체로 보여준다.",
|
||||
"start_state": "Scene=DropletStretch; F3173. Blender F3173의 닫힌 Droplet mesh scale=(1,1,1); 부드러운 청색 구형 물방울. 물의 양은 같은 조건이다.",
|
||||
"end_state": "Scene=DropletStretch; F3373. Blender F3373의 Droplet scale=(1.85,1/sqrt(1.85),1/sqrt(1.85)); 같은 부피를 유지하며 좌우로 길어지고 표면이 넓어지는 설명용 변형이다.",
|
||||
"camera": "실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c19",
|
||||
"description": "같은 물의 양으로 길어진 모형을 유지하며 표면을 더 넓히는 데 에너지가 필요함을 설명한다.",
|
||||
"start_state": "Scene=DropletStretch; F3373. Blender F3373의 Droplet scale=(1.85,1/sqrt(1.85),1/sqrt(1.85)); 같은 부피를 유지하며 좌우로 길어지고 표면이 넓어지는 설명용 변형이다.",
|
||||
"end_state": "Scene=DropletStretch; F3542. 길어진 같은 부피 물방울 유지; 에너지 필요 주석을 추후 합성한다. 실제 힘·에너지 수치는 표시하지 않는다.",
|
||||
"camera": "실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c20",
|
||||
"description": "같은 부피의 물방울이 표면 넓이를 줄이는 쪽으로 모양을 바꾸려는 경향을 연결한다.",
|
||||
"start_state": "Scene=DropletStretch; F3542. 길어진 같은 부피 물방울 유지; 에너지 필요 주석을 추후 합성한다. 실제 힘·에너지 수치는 표시하지 않는다.",
|
||||
"end_state": "Scene=DropletStretch; F3657. 같은 부피 물방울이 되돌아갈 준비 상태이며 물 표면의 확장 저항을 설명한다.",
|
||||
"camera": "실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c21",
|
||||
"description": "길어진 물방울이 같은 부피를 유지하며 구형에 가까워지는 계획으로 표면을 줄이는 변화를 보여준다.",
|
||||
"start_state": "Scene=DropletStretch; F3657. 같은 부피 물방울이 되돌아갈 준비 상태이며 물 표면의 확장 저항을 설명한다.",
|
||||
"end_state": "Scene=DropletStretch; F3853. 같은 부피의 닫힌 mesh가 구형에 가깝게 복귀한다. 복귀 애니메이션은 scenes 승인 후 정식 제작한다.",
|
||||
"camera": "실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c22",
|
||||
"description": "물 표면이 넓어지는 데 저항하는 성질을 표면장력이라는 이름과 연결한다.",
|
||||
"start_state": "Scene=DropletStretch; F3853. 같은 부피의 닫힌 mesh가 구형에 가깝게 복귀한다. 복귀 애니메이션은 scenes 승인 후 정식 제작한다.",
|
||||
"end_state": "Scene=DropletStretch; F4077. 구형에 가까운 물방울과 표면 확장 저항 설명이 남는다. 껍질·고체 표면층은 없다.",
|
||||
"camera": "실제 대표 Scene DropletStretch. 닫힌 청색 3D mesh, (0,-12,5) 직교 카메라 ortho_scale=10.5. 같은 물의 양·설명용 모형 합성 앵커, 표면층 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c23",
|
||||
"description": "길어진 물방울과 둥근 물방울의 모양을 같은 물의 양이라는 공통 조건에서 비교한다.",
|
||||
"start_state": "Scene=EqualVolumeComparison; F4077. 왼쪽 길어진 닫힌 물방울과 오른쪽 구형 물방울을 동일 부피 조건으로 나란히 배치한다.",
|
||||
"end_state": "Scene=EqualVolumeComparison; F4149. 같은 물의 양 주석 앵커를 유지하며 두 형태의 겉면 차이를 보여준다.",
|
||||
"camera": "3D 비교 전경, 좌우 모형을 같은 카메라·같은 물의 양으로 비교한다. 수치·픽셀면적 비교 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c24",
|
||||
"description": "같은 부피일 때 구형이 겉면을 가장 작게 만드는 관계를 두 입체 모형으로 보여준다.",
|
||||
"start_state": "Scene=EqualVolumeComparison; F4149. 같은 물의 양 주석 앵커를 유지하며 두 형태의 겉면 차이를 보여준다.",
|
||||
"end_state": "Scene=EqualVolumeComparison; F4370. 같은 부피에서 구형의 표면적이 작다는 관계를 청색 형태와 합성 주석으로 강조한다.",
|
||||
"camera": "3D 비교 전경, 좌우 모형을 같은 카메라·같은 물의 양으로 비교한다. 수치·픽셀면적 비교 없음."
|
||||
},
|
||||
{
|
||||
"cut_id": "c25",
|
||||
"description": "외부 영향이 작은 작은 물방울이 구형에 가까워지는 조건을 제시한다.",
|
||||
"start_state": "Scene=ShapeConditions; F4370. 작은 비접촉 물방울과 더 큰 바닥 접촉 물방울을 별도의 조건 예시로 배치한다. 이 컷은 동일 부피 비교가 아니다.",
|
||||
"end_state": "Scene=ShapeConditions; F4577. 외부 영향이 작은 작은 물방울이 구형에 가깝고 오른쪽 방울은 바닥과 접촉해 모양이 달라지는 예시다.",
|
||||
"camera": "조건 예시 3D 전경, 왼쪽 작은 비접촉 방울과 오른쪽 바닥에 닿은 큰 방울. 앞 비교와 별도 Scene."
|
||||
},
|
||||
{
|
||||
"cut_id": "c26",
|
||||
"description": "작은 비접촉 방울과 큰 바닥 접촉 방울을 별도 조건 예시로 구별한다.",
|
||||
"start_state": "Scene=ShapeConditions; F4577. 외부 영향이 작은 작은 물방울이 구형에 가깝고 오른쪽 방울은 바닥과 접촉해 모양이 달라지는 예시다.",
|
||||
"end_state": "Scene=ShapeConditions; F4809. 바닥 접촉 방울의 납작한 형태가 보이며 중력·접촉면 영향의 조건 주석 앵커가 있다.",
|
||||
"camera": "조건 예시 3D 전경, 왼쪽 작은 비접촉 방울과 오른쪽 바닥에 닿은 큰 방울. 앞 비교와 별도 Scene."
|
||||
},
|
||||
{
|
||||
"cut_id": "c27",
|
||||
"description": "바닥 접촉과 외부 영향으로 실제 물방울의 모양이 달라질 수 있음을 보여준다.",
|
||||
"start_state": "Scene=ShapeConditions; F4809. 바닥 접촉 방울의 납작한 형태가 보이며 중력·접촉면 영향의 조건 주석 앵커가 있다.",
|
||||
"end_state": "Scene=ShapeConditions; F4889. 두 조건 예시를 유지한다. 항상 완전한 구라는 단정·접촉각이나 크기 경계값은 없다.",
|
||||
"camera": "조건 예시 3D 전경, 왼쪽 작은 비접촉 방울과 오른쪽 바닥에 닿은 큰 방울. 앞 비교와 별도 Scene."
|
||||
},
|
||||
{
|
||||
"cut_id": "c28",
|
||||
"description": "작은 청색 물방울로 돌아와 그 주변에 별도 고체 껍질이나 막이 없음을 보여준다.",
|
||||
"start_state": "Scene=AnswerDroplet; F4889. 구형에 가까운 청색 물방울과 분리된 분자 개념도 위치가 보인다. 물방울 주변에 별도 껍질은 없다.",
|
||||
"end_state": "Scene=AnswerDroplet; F5100. 동일 물방울 표면을 유지하며 추가 막이 없음을 보여준다.",
|
||||
"camera": "도입과 같은 크림색 3D 공간·청색 방울 전경을 새 Scene에서 재제시하고 분자 개념도를 별도 위치에 둔다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c29",
|
||||
"description": "분자 개념도에서 이웃 분자 사이 수소결합을 회수하며 원자와 내부 결합은 유지한다.",
|
||||
"start_state": "Scene=AnswerDroplet; F5100. 동일 물방울 표면을 유지하며 추가 막이 없음을 보여준다.",
|
||||
"end_state": "Scene=AnswerDroplet; F5229. 별도 분자 개념도에서 온전한 V자 분자 사이 금색 H···O 점선이 변하고 내부 남색 실선은 유지된다.",
|
||||
"camera": "도입과 같은 크림색 3D 공간·청색 방울 전경을 새 Scene에서 재제시하고 분자 개념도를 별도 위치에 둔다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c30",
|
||||
"description": "물방울과 분자 개념도를 함께 유지해 표면을 넓히기 어려운 성질을 답의 원인으로 연결한다.",
|
||||
"start_state": "Scene=AnswerDroplet; F5229. 별도 분자 개념도에서 온전한 V자 분자 사이 금색 H···O 점선이 변하고 내부 남색 실선은 유지된다.",
|
||||
"end_state": "Scene=AnswerDroplet; F5369. 물방울과 분자 개념도를 유지하며 표면을 넓히기 어려운 성질을 합성 주석으로 회수한다.",
|
||||
"camera": "도입과 같은 크림색 3D 공간·청색 방울 전경을 새 Scene에서 재제시하고 분자 개념도를 별도 위치에 둔다."
|
||||
},
|
||||
{
|
||||
"cut_id": "c31",
|
||||
"description": "분자 사이 끌림과 표면장력이 외부 영향이 작은 작은 물방울의 둥근 형태를 만든다는 답을 회수한다.",
|
||||
"start_state": "Scene=AnswerDroplet; F5369. 물방울과 분자 개념도를 유지하며 표면을 넓히기 어려운 성질을 합성 주석으로 회수한다.",
|
||||
"end_state": "Scene=AnswerDroplet; F5529. 외부 영향이 작은 작은 물방울의 둥근 형태와 수소결합·표면장력의 설명이 함께 남아 도입 질문에 답한다.",
|
||||
"camera": "도입과 같은 크림색 3D 공간·청색 방울 전경을 새 Scene에서 재제시하고 분자 개념도를 별도 위치에 둔다."
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_cut_id": "c01",
|
||||
"to_cut_id": "c02",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c02",
|
||||
"to_cut_id": "c03",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 OpeningDroplet에서 별도 SingleMolecule Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c03",
|
||||
"to_cut_id": "c04",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c04",
|
||||
"to_cut_id": "c05",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c05",
|
||||
"to_cut_id": "c06",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 SingleMolecule에서 별도 BondPair Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c06",
|
||||
"to_cut_id": "c07",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c07",
|
||||
"to_cut_id": "c08",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c08",
|
||||
"to_cut_id": "c09",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c09",
|
||||
"to_cut_id": "c10",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 BondPair에서 별도 LiquidNetwork Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c10",
|
||||
"to_cut_id": "c11",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c11",
|
||||
"to_cut_id": "c12",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c12",
|
||||
"to_cut_id": "c13",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c13",
|
||||
"to_cut_id": "c14",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 LiquidNetwork에서 별도 Interface Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c14",
|
||||
"to_cut_id": "c15",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c15",
|
||||
"to_cut_id": "c16",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c16",
|
||||
"to_cut_id": "c17",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c17",
|
||||
"to_cut_id": "c18",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 Interface에서 별도 DropletStretch Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c18",
|
||||
"to_cut_id": "c19",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c19",
|
||||
"to_cut_id": "c20",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c20",
|
||||
"to_cut_id": "c21",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c21",
|
||||
"to_cut_id": "c22",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c22",
|
||||
"to_cut_id": "c23",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 DropletStretch에서 별도 EqualVolumeComparison Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c23",
|
||||
"to_cut_id": "c24",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c24",
|
||||
"to_cut_id": "c25",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 EqualVolumeComparison에서 별도 ShapeConditions Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c25",
|
||||
"to_cut_id": "c26",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c26",
|
||||
"to_cut_id": "c27",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c27",
|
||||
"to_cut_id": "c28",
|
||||
"mode": "cut",
|
||||
"reason": "설명 범위·조건·시점 전환으로 ShapeConditions에서 별도 AnswerDroplet Scene으로 전환한다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c28",
|
||||
"to_cut_id": "c29",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c29",
|
||||
"to_cut_id": "c30",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
},
|
||||
{
|
||||
"from_cut_id": "c30",
|
||||
"to_cut_id": "c31",
|
||||
"mode": "continuous",
|
||||
"reason": "동일 Scene·논리 경계 프레임·물체·카메라 상태를 공유하며 설명의 다음 변화를 잇는다."
|
||||
}
|
||||
],
|
||||
"preview": {
|
||||
"scene_file": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/preview.blend",
|
||||
"verified": true,
|
||||
"sha256": "90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb",
|
||||
"blender_version": "5.2.2 LTS"
|
||||
},
|
||||
"render_profile": {
|
||||
"engine": "BLENDER_EEVEE",
|
||||
"device": "GPU",
|
||||
"fps": 50,
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
"samples": 64,
|
||||
"seed": 0
|
||||
},
|
||||
"samples": [
|
||||
{
|
||||
"id": "c07-image",
|
||||
"cut_ids": [
|
||||
"c07"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5b09f426cc6a97b3-ab379b2220bf/frame-00001219.png",
|
||||
"kind": "image",
|
||||
"verified": true,
|
||||
"duration_ms": null,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c16-image",
|
||||
"cut_ids": [
|
||||
"c16"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5a10b9e425756d31-40434704ca5c/frame-00003050.png",
|
||||
"kind": "image",
|
||||
"verified": true,
|
||||
"duration_ms": null,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c18-image",
|
||||
"cut_ids": [
|
||||
"c18"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/34d7d6fa6d236962-0946653f91af/frame-00003372.png",
|
||||
"kind": "image",
|
||||
"verified": true,
|
||||
"duration_ms": null,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c07-video",
|
||||
"cut_ids": [
|
||||
"c07"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/clip.mp4",
|
||||
"kind": "video",
|
||||
"verified": true,
|
||||
"duration_ms": 4040,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c16-video",
|
||||
"cut_ids": [
|
||||
"c16"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/clip.mp4",
|
||||
"kind": "video",
|
||||
"verified": true,
|
||||
"duration_ms": 4880,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c18-video",
|
||||
"cut_ids": [
|
||||
"c18"
|
||||
],
|
||||
"media": {
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/clip.mp4",
|
||||
"kind": "video",
|
||||
"verified": true,
|
||||
"duration_ms": 4000,
|
||||
"width": 1080,
|
||||
"height": 1920
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"result": "GREEN",
|
||||
"cuts": 31,
|
||||
"captions": 38,
|
||||
"connections": 30,
|
||||
"total_ms": 110560,
|
||||
"fps": 50,
|
||||
"total_frames": 5528,
|
||||
"profile": {
|
||||
"engine": "BLENDER_EEVEE",
|
||||
"device": "GPU",
|
||||
"fps": 50,
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
"samples": 64,
|
||||
"seed": 0
|
||||
},
|
||||
"source_sha256": "90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb",
|
||||
"samples": 6,
|
||||
"visual_review_frames": [
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001017.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001118.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001218.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00002806.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00002928.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00003049.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003172.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003272.png",
|
||||
"projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003371.png"
|
||||
],
|
||||
"actual_assets": [
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/preview.blend",
|
||||
"sha256": "90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5b09f426cc6a97b3-ab379b2220bf/frame-00001219.png",
|
||||
"sha256": "d5c4c1265883b89f50bffe426772cf44c0997a7932a19364d18a7b5f752406eb"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5b09f426cc6a97b3-ab379b2220bf/evidence.json",
|
||||
"sha256": "bb6aa1849a65db4f042978eb9e3d193da50e0c99ef3ab0de0b64b5c9cc34ffae"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5a10b9e425756d31-40434704ca5c/frame-00003050.png",
|
||||
"sha256": "b85249515e71d7b258f97669f62e80406f6d1e112a637cd72a8a947941bcfdfb"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/5a10b9e425756d31-40434704ca5c/evidence.json",
|
||||
"sha256": "58daebc48bf8f2c0a69ac4defed7b04db2ea5aabfa23bd4f6af5d15e032e8c1c"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/34d7d6fa6d236962-0946653f91af/frame-00003372.png",
|
||||
"sha256": "3a5aa9639921eb4e45c515c0c221c855276b785026cfe5861edb384ef6fc1915"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/34d7d6fa6d236962-0946653f91af/evidence.json",
|
||||
"sha256": "380048ebdc85eaa82b6d78f92ad2b1644ba56ea7cf85c161ba50e3ed24682da5"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001017.png",
|
||||
"sha256": "57839cdd859b72b296159f9e62effe1a0dcb06d29f4b443900a469c57b2cbb05"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001118.png",
|
||||
"sha256": "389703fb3dc35845781921bfda9ff155e859cd99ea415434d98534b2e13ee67a"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/frame-00001218.png",
|
||||
"sha256": "9715f2735c82b4804ace3424149fc88002d2e3eff116b26d0615a9714af94d3d"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/clip.mp4",
|
||||
"sha256": "a180c5c2d3890d35e67dca9ee393c88b6dbf52fe2b7a82f82eeb019296aadfac"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/b687ba0725d55e27-a1f95ff8f3d0/evidence.json",
|
||||
"sha256": "4c8c98a5c54e66c6849d4c7c4501b5c3c440ccf8225edf600a4bee363b03d579"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00002806.png",
|
||||
"sha256": "1dbbe8ff2040ac89fbdba73eda8358b50f6bc765cc463646f30663accb5f9281"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00002928.png",
|
||||
"sha256": "86976f101752570d91e9eb9550649968fd289d057802d3a6eba55066e03a5881"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/frame-00003049.png",
|
||||
"sha256": "30c45f6314d2327bc0e5399049cba637fbfd532b3a643486c0cb9e6683344c09"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/clip.mp4",
|
||||
"sha256": "f1234254a43ab67cd3e7b8c5e855b39a18913596b8049a1201e0a6f4a60eda09"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/791d67b5b8b0d08f-90e42af24cc7/evidence.json",
|
||||
"sha256": "02e66eed34b3270ed2f6613d26e587a1eccc2f5b4b7144b73cd1b67ca5176caa"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003172.png",
|
||||
"sha256": "c9c560e8442b1cc17e5b1b34370797c3cb6ff3d16001582ea524f93fcdd99497"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003272.png",
|
||||
"sha256": "d00f91e91a8133aa757ed2e3b2fe91b6c3e0e6e472ad96c675bf4954b778cfe9"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/frame-00003371.png",
|
||||
"sha256": "fa4c9328af95c4a2a651f14f654ddcb45c227660d2cf9ab8549ba425f4268faf"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/clip.mp4",
|
||||
"sha256": "db354bf19edf75ad275c2a5f0cd895abfa6be765c5bce4d85bbe35a4db68ee80"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/render-attempts/189e348aeb500188-623b27f1328f/evidence.json",
|
||||
"sha256": "f2a3b2eb421d6da588f6e83f427228d678971027242a18aefb6ffcc53c1cd874"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c07.png",
|
||||
"sha256": "b0ee1e3baf1561e5976fdeeacfb8e84d068237c5af2e0e7cdda1756ea502fe4c"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c16.png",
|
||||
"sha256": "9e39985291ebfd404240f8463dca66b49e05f2f1a77e9960deb9c9371b435e11"
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c18.png",
|
||||
"sha256": "ae6708539c7ef2cea4e20383fd50517f8872b887d195d89354ab91c680e7df60"
|
||||
}
|
||||
],
|
||||
"scenes_approval": "pending",
|
||||
"formal_10_12": "not_started"
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"source_sha256": "90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb",
|
||||
"blender_version": "5.2.2 LTS",
|
||||
"scenes": {
|
||||
"BondPair": {
|
||||
"molecules": 2,
|
||||
"frame_range": [
|
||||
905,
|
||||
1637
|
||||
]
|
||||
},
|
||||
"Interface": {
|
||||
"molecules": 10,
|
||||
"frame_range": [
|
||||
2469,
|
||||
3173
|
||||
]
|
||||
},
|
||||
"DropletStretch": {
|
||||
"molecules": 0,
|
||||
"frame_range": [
|
||||
3173,
|
||||
4077
|
||||
]
|
||||
}
|
||||
},
|
||||
"interface_frames_blender": [
|
||||
2807,
|
||||
2907,
|
||||
3051
|
||||
],
|
||||
"interface_active_pairs": [
|
||||
[
|
||||
[
|
||||
"Liquid0_H1",
|
||||
"Liquid1_O"
|
||||
],
|
||||
[
|
||||
"Liquid1_H1",
|
||||
"Liquid2_O"
|
||||
],
|
||||
[
|
||||
"Liquid3_H1",
|
||||
"Liquid4_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid5_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid7_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid6_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid8_O"
|
||||
],
|
||||
[
|
||||
"Liquid8_H1",
|
||||
"Liquid9_O"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"Liquid0_H1",
|
||||
"Liquid1_O"
|
||||
],
|
||||
[
|
||||
"Liquid1_H1",
|
||||
"Liquid2_O"
|
||||
],
|
||||
[
|
||||
"Liquid3_H1",
|
||||
"Liquid7_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid5_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid7_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid6_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid8_O"
|
||||
],
|
||||
[
|
||||
"Liquid8_H1",
|
||||
"Liquid9_O"
|
||||
]
|
||||
],
|
||||
[
|
||||
[
|
||||
"Liquid0_H1",
|
||||
"Liquid1_O"
|
||||
],
|
||||
[
|
||||
"Liquid1_H1",
|
||||
"Liquid2_O"
|
||||
],
|
||||
[
|
||||
"Liquid3_H1",
|
||||
"Liquid7_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid5_O"
|
||||
],
|
||||
[
|
||||
"Liquid4_H1",
|
||||
"Liquid7_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid6_O"
|
||||
],
|
||||
[
|
||||
"Liquid5_H1",
|
||||
"Liquid8_O"
|
||||
],
|
||||
[
|
||||
"Liquid8_H1",
|
||||
"Liquid9_O"
|
||||
]
|
||||
]
|
||||
],
|
||||
"interface_neighborhoods": [
|
||||
[
|
||||
{
|
||||
"neighbors": 6,
|
||||
"above": 2
|
||||
},
|
||||
{
|
||||
"neighbors": 4,
|
||||
"above": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"neighbors": 6,
|
||||
"above": 2
|
||||
},
|
||||
{
|
||||
"neighbors": 4,
|
||||
"above": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"neighbors": 6,
|
||||
"above": 2
|
||||
},
|
||||
{
|
||||
"neighbors": 4,
|
||||
"above": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"volume_frames_blender": [
|
||||
3173,
|
||||
3223,
|
||||
3273,
|
||||
3323,
|
||||
3373
|
||||
],
|
||||
"volumes": [
|
||||
9.193528663402905,
|
||||
9.193528830193245,
|
||||
9.193528411895626,
|
||||
9.193528263647703,
|
||||
9.193529542642318
|
||||
],
|
||||
"relative_volume_change": 9.563677294543993e-08,
|
||||
"evaluated_scales": [
|
||||
[
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.212499976158142,
|
||||
0.908153235912323,
|
||||
0.908153235912323
|
||||
],
|
||||
[
|
||||
1.4249999523162842,
|
||||
0.8377078175544739,
|
||||
0.8377078175544739
|
||||
],
|
||||
[
|
||||
1.6375000476837158,
|
||||
0.7814646363258362,
|
||||
0.7814646363258362
|
||||
],
|
||||
[
|
||||
1.850000023841858,
|
||||
0.7352146506309509,
|
||||
0.7352146506309509
|
||||
]
|
||||
],
|
||||
"label_anchors": {
|
||||
"BondPair": {
|
||||
"logical_frame": 1219,
|
||||
"labels": {
|
||||
"한 분자의 Hδ+": {
|
||||
"x": 359.69,
|
||||
"y": 927.15,
|
||||
"depth": 13.32
|
||||
},
|
||||
"이웃 분자의 Oδ−": {
|
||||
"x": 681.91,
|
||||
"y": 881.6,
|
||||
"depth": 13.35
|
||||
}
|
||||
}
|
||||
},
|
||||
"Interface": {
|
||||
"logical_frame": 3050,
|
||||
"labels": {
|
||||
"물과 공기의 경계": {
|
||||
"x": 603.07,
|
||||
"y": 923.93,
|
||||
"depth": 15.34
|
||||
},
|
||||
"물 안쪽": {
|
||||
"x": 439.17,
|
||||
"y": 1107.39,
|
||||
"depth": 15.82
|
||||
}
|
||||
}
|
||||
},
|
||||
"DropletStretch": {
|
||||
"logical_frame": 3372,
|
||||
"labels": {
|
||||
"같은 물의 양 · 설명용 모형": {
|
||||
"x": 540.0,
|
||||
"y": 960.0,
|
||||
"depth": 13.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"text_baked": false,
|
||||
"physical_simulation": false
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"result": "GREEN",
|
||||
"negative_cases": 4,
|
||||
"positive_cases": 1,
|
||||
"executions": [
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python-expr",
|
||||
"import bpy; bpy.ops.wm.open_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\preview.blend'); bpy.ops.wm.read_factory_settings(use_empty=True); bpy.ops.wm.save_as_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\invalid-default.blend')"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"expected_exit_code": 0,
|
||||
"reason": []
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py",
|
||||
"--",
|
||||
"--source",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-default.blend",
|
||||
"--out",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-result.json"
|
||||
],
|
||||
"exit_code": 1,
|
||||
"expected_exit_code": 1,
|
||||
"reason": [
|
||||
"Error: script failed, file: 'C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py', exiting."
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python-expr",
|
||||
"import bpy; bpy.ops.wm.open_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\preview.blend'); bpy.data.objects.remove(bpy.data.objects['PairA_H2'],do_unlink=True); bpy.ops.wm.save_as_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\invalid-atoms.blend')"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"expected_exit_code": 0,
|
||||
"reason": []
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py",
|
||||
"--",
|
||||
"--source",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-atoms.blend",
|
||||
"--out",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-result.json"
|
||||
],
|
||||
"exit_code": 1,
|
||||
"expected_exit_code": 1,
|
||||
"reason": [
|
||||
"Error: script failed, file: 'C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py', exiting."
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python-expr",
|
||||
"import bpy; bpy.ops.wm.open_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\preview.blend'); bpy.data.objects['Droplet'].animation_data.drivers[1].driver.expression='1'; bpy.ops.wm.save_as_mainfile(filepath='C:\\\\git\\\\ShortVideoGenerator\\\\.dryforge\\\\worktrees\\\\T3\\\\projects\\\\water-hydrogen-bond-surface-tension\\\\stages\\\\08-scenes\\\\r2\\\\generated-source\\\\invalid-volume.blend')"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"expected_exit_code": 0,
|
||||
"reason": []
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py",
|
||||
"--",
|
||||
"--source",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-volume.blend",
|
||||
"--out",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-result.json"
|
||||
],
|
||||
"exit_code": 1,
|
||||
"expected_exit_code": 1,
|
||||
"reason": [
|
||||
"Error: script failed, file: 'C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py', exiting."
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py",
|
||||
"--",
|
||||
"--source",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\missing.blend",
|
||||
"--out",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\invalid-result.json"
|
||||
],
|
||||
"exit_code": 1,
|
||||
"expected_exit_code": 1,
|
||||
"reason": [
|
||||
"Error: script failed, file: 'C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py', exiting."
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": [
|
||||
"C:/Program Files/Blender Foundation/Blender 5.2/blender.exe",
|
||||
"--background",
|
||||
"--disable-autoexec",
|
||||
"--python-exit-code",
|
||||
"1",
|
||||
"--python",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\blender\\verify_model.py",
|
||||
"--",
|
||||
"--source",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\generated-source\\preview.blend",
|
||||
"--out",
|
||||
"C:\\git\\ShortVideoGenerator\\.dryforge\\worktrees\\T3\\projects\\water-hydrogen-bond-surface-tension\\stages\\08-scenes\\r2\\model-check.json"
|
||||
],
|
||||
"exit_code": 0,
|
||||
"expected_exit_code": 0,
|
||||
"reason": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"result": "GREEN",
|
||||
"source_sha256": "90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb",
|
||||
"views": [
|
||||
{
|
||||
"cut_id": "c07",
|
||||
"logical_frames": [
|
||||
1017,
|
||||
1118,
|
||||
1218
|
||||
],
|
||||
"pixel_sha256": [
|
||||
"ae0a8bd9b961002870a42f9d5d95bd694b4ab83e12e38f0eb1e42b8671bacbee",
|
||||
"f6909898a79aed45efd47611568a0991d3b73d1788b1f12ee9ce84eee9e9421b",
|
||||
"f6909898a79aed45efd47611568a0991d3b73d1788b1f12ee9ce84eee9e9421b"
|
||||
],
|
||||
"mean_absolute_pixel_change": [
|
||||
1.7141441615226338,
|
||||
1.7141441615226338
|
||||
],
|
||||
"mobile_view_path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c07.png",
|
||||
"mobile_view_sha256": "b0ee1e3baf1561e5976fdeeacfb8e84d068237c5af2e0e7cdda1756ea502fe4c",
|
||||
"visual_review": "checked",
|
||||
"observation": "두 분자의 내부 실선과 분자간 점선이 구별되고 PairB가 가까워진 뒤 상태를 유지한다. 중간/끝 픽셀 동일은 변형 완료 후 유지 구간이다.",
|
||||
"mobile_resolution": [
|
||||
360,
|
||||
640
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c16",
|
||||
"logical_frames": [
|
||||
2806,
|
||||
2928,
|
||||
3049
|
||||
],
|
||||
"pixel_sha256": [
|
||||
"0f55a46fd850dba3c019a5f6ad061ff5f1463d3fc438c598ad8f1a8705f93458",
|
||||
"1f25633ee802cc15e9b333750d7f9ad1d5991d806524a74809d685d0be3d6789",
|
||||
"a5c05fbcb2dcb71feefb52b6a71ef4b7bbaf073c5a5e52278d85a914eb711a9e"
|
||||
],
|
||||
"mean_absolute_pixel_change": [
|
||||
1.6445371977880658,
|
||||
1.148095743312757
|
||||
],
|
||||
"mobile_view_path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c16.png",
|
||||
"mobile_view_sha256": "9e39985291ebfd404240f8463dca66b49e05f2f1a77e9960deb9c9371b435e11",
|
||||
"visual_review": "checked",
|
||||
"observation": "내부 강조가 사라지고 표면 강조가 나타난다. 불규칙한 이웃과 연결 교체가 보이며 청색 체적은 절개 설명 모형이다.",
|
||||
"mobile_resolution": [
|
||||
360,
|
||||
640
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c18",
|
||||
"logical_frames": [
|
||||
3172,
|
||||
3272,
|
||||
3371
|
||||
],
|
||||
"pixel_sha256": [
|
||||
"844e0b43c1d95e098d9c2ae14ea6260a8f52b7c1d07aa06ea32e443fb0df8eb4",
|
||||
"8caf4fd30eff6e1ec8547e2cf47eadd487c366b067c33e7a7aa5376746c096ea",
|
||||
"dd06c356f970ebb7939d04704d3f9f446f60d0aee51353c00248c75389ec64b6"
|
||||
],
|
||||
"mean_absolute_pixel_change": [
|
||||
4.760786554783951,
|
||||
7.851008551954732
|
||||
],
|
||||
"mobile_view_path": "projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2/generated-source/mobile-c18.png",
|
||||
"mobile_view_sha256": "ae6708539c7ef2cea4e20383fd50517f8872b887d195d89354ab91c680e7df60",
|
||||
"visual_review": "checked",
|
||||
"observation": "매끈한 청색 닫힌 물방울이 둥근 상태에서 좌우로 길어져 가시적인 3D 변형을 보인다.",
|
||||
"mobile_resolution": [
|
||||
360,
|
||||
640
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"result": "GREEN",
|
||||
"wav_files": 9,
|
||||
"binary_files": [
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/narration.wav",
|
||||
"sha256": "aeac3c60acf481195be1af093d633cf16ea4ea6645778d762f529e49ca5ec905",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u01.wav",
|
||||
"sha256": "6da0cdca317b77019a8c6d3a2624a66f888118aca49f4771823c174d0566fed0",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u02.wav",
|
||||
"sha256": "6fde36df1fa98e5ad4de6775b3ac21071ab0f33aa54f6c0ac14aaec9af4088bc",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u03.wav",
|
||||
"sha256": "2cd48379dfc704a6c8a06b8de21085f9f40405d94c22f5893525c9c47f0da9e4",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u04.wav",
|
||||
"sha256": "9dbfb2df6d9888127921dd17cb9c4ada4785934c3fc194aad9313dad9ec02d7e",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u05.wav",
|
||||
"sha256": "84cbd278321539207318a3a7a7cd8c3527778f5950de6cfb77b18b1352738d01",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u06.wav",
|
||||
"sha256": "5c7e19466ea3d3d01b853d9bae00e52703df6e50906aad38219689db63a60ce0",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u07.wav",
|
||||
"sha256": "38a200bab0e80f85e80719900870823d3166d5071f19866e0e44aab02d7d8fc9",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/media/06-tts/r1/u08.wav",
|
||||
"sha256": "55d16e7f65e5f183cc17d194898e9d80b70a9308a370a36c7e4c4367fdf5a904",
|
||||
"matches_original_bytes": true
|
||||
},
|
||||
{
|
||||
"path": "references/1.png",
|
||||
"sha256": "5d83e03add8cdc29f5d7b1f1d06fb87f7738a6dc32579f554fb824fb36b83e44",
|
||||
"matches_original_bytes": true
|
||||
}
|
||||
],
|
||||
"stages": [
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/01-topic/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/02-research/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/03-script/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/04-title/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/05-tts-split/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/06-tts/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
},
|
||||
{
|
||||
"path": "projects/water-hydrogen-bond-surface-tension/stages/07-cuts/r1/artifact.json",
|
||||
"matches_original_json": true,
|
||||
"matches_original_bytes": false
|
||||
}
|
||||
],
|
||||
"note": "WAV와 사용자 스타일 원본은 바이트가 동일하다. 01–07 JSON은 작업사본 checkout의 줄끝 차이가 있지만 내용은 동일하며 해당 파일을 수정하지 않았다."
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
# 새 3D 장면 검토 · 08 r2
|
||||
|
||||
검토 목표는 「물방울은 왜 동그랗게 뭉칠까?」의 기존 한국어 대본·승인 WAV·실측 07 r1을 유지하면서 교육용 3D 장면의 설명과 품질을 확인하는 것이다. **사용자 scenes 승인 대기**를 제안한다. 정식 10–12·31컷 전체 렌더·최종 영상은 이 시제품의 결과가 아니다.
|
||||
|
||||
## 내용과 연결
|
||||
|
||||
02 r1의 c1–c9 검증 주장을 03 r1의 의미 범위로 시각화했다. 새 수치·힘·거리·각도·접촉각을 표시하지 않는다. 설명용 개념도이며 MD·유체 시뮬레이션이 아니다. 도입의 작은 물방울→분자 극성→두 종류 결합→액체의 바뀌는 연결→내부와 표면→같은 부피 변형→조건과 답 회수로 이어진다. 자료에 없는 위기·실패한 대안·반전은 적용하지 않았다.
|
||||
|
||||
31개 장면과 인접 연결 30개를 새 1.1 본문에 설계했다. continuous는 같은 Scene·같은 경계 F·같은 카메라와 **완전히 같은 end/start 문자열**을 공유한다. cut은 설명 범위·비교 조건·시점 전환으로 별도 Scene 이름을 계획한다. 실제 원본에는 아래 세 대표 Scene만 있으며 나머지 계획은 scenes 승인 후 구현한다. DropletStretch의 복귀와 전체 비교/조건 장면도 정식 제작 사항이다.
|
||||
|
||||
## 실제 대표와 과학 표현
|
||||
|
||||
| 컷 | 실측 음성 구간 | 논리 영상 범위 | 끝 PNG | 이해할 변화 |
|
||||
|---|---|---|---|---|
|
||||
| c07 | 20340–24380ms | [1017,1219), 202F/4040ms | F1219 | 두 온전한 분자가 가까워지며 금색 H···O 점선 길이가 줄고 남색 내부 O–H 실선은 유지 |
|
||||
| c16 | 56120–61000ms | [2806,3050), 244F/4880ms | F3050 | 내부 강조에서 표면 강조로 바뀌고 불규칙 이웃의 연결 쌍도 실제 변화 |
|
||||
| c18 | 63440–67440ms | [3172,3372), 200F/4000ms | F3372 | 닫힌 청색 물방울이 같은 부피를 유지하며 좌우로 길어짐 |
|
||||
|
||||
논리 F는 Blender 실제 F+1이다. PNG는 exclusive 경계이며 MP4 마지막 프레임과 20ms 차이가 있다. 이미지의 확대·이동으로 3D 움직임을 대체하지 않는다.
|
||||
|
||||
- 모든 온전한 물 분자는 실제 구 mesh의 O1H2와 V자 배치, 남색 실선 원통 두 개를 유지한다. 금색 분절 원통은 서로 다른 분자의 H···O만 연결한다. 원자가 떨어지거나 반응하는 장면은 없다.
|
||||
- Interface는 **물 영역을 절개한 설명 모형**이다. 뒤·옆에 남긴 청색 체적의 인공 절개면은 벽·막·고체 껍질이 아니다. 표면 위 공기 공간은 진공이나 수증기 부재를 뜻하지 않는다. 내부와 표면의 이웃 차이는 실제 불규칙 객체 배치로 검사한다. 전체 아래 방향 가속 화살표는 없다.
|
||||
- 강조고리는 설명 표식이다. 물 표면층으로 해석하지 않도록 합성 주석에서 내부·표면·절개 모형을 구별한다. Liquid3_H1→Liquid4_O 연결이 사라지고 Liquid3_H1→Liquid7_O 연결이 나타나며 고정 격자·모든 분자의 동일 연결 수를 만들지 않는다.
|
||||
- Droplet은 128×64의 매끈한 UV 구 기반 닫힌 mesh다. x=s, y=z=s^(-1/2)의 단순 driver로 매 프레임 scale determinant=1을 유지한다. `--disable-autoexec`로 실제 재열기·평가하며 mesh 자체의 시작·중간 3곳·전체 컷 경계 부피를 검사한다. 분석용 부피 수치는 화면에 표시하지 않는다.
|
||||
|
||||
## 품질·주석·모바일
|
||||
|
||||
references/1.png의 정돈된 형태·색·정보량을 단순 공간과 부드러운 3D 명암으로 해석했다. 원본은 보존한다. 가는 외곽선의 평면 복제·캠핑 소재는 적용하지 않는다. 크림색 emission 배경, 산호색 O·미색 H·남색 내부 실선·금색 점선·청색 물을 사용한다. Pair 카메라는 11.5, Droplet 카메라는 10.5의 직교 범위로 좌우 여백을 마련했다. 낮춘 Area 조명과 Standard 색 관리를 실제 PNG로 확인하고 과노출된 초기 시도·탁한 AgX 시도를 보존했다.
|
||||
|
||||
상하단은 제목·자막 공간으로 비워 두었다. Oδ−/Hδ+는 분자의 부분전하이며 전체 분자를 이온으로 표시하지 않는다. 한국어 주석·기호는 3D에 굽지 않고 나중 Remotion에서 검증한 텍스트로 합성한다. model-check.json의 label_anchors는 끝 경계의 실제 camera 투영 픽셀 좌표이며 배치 시작점이지 최종 글자 크기·플랫폼 안전영역 보증이 아니다. Interface에는 ‘공기’, ‘물(절개 설명 모형)’, ‘물 안쪽’, ‘표면’을 구별해서 배치한다. 모형의 치수·강조고리 크기는 과학 수치가 아니다.
|
||||
|
||||
실제 대표 PNG와 영상의 시작/중간/끝 렌더를 직접 확인한 최종 경로와 모바일 관찰은 아래 실제 제출 증거에 기록한다. 최종 자막 가독성·음성 청취·31컷 전체 품질은 여기서 통과를 선언하지 않는다.
|
||||
|
||||
## 검증과 실패 보존
|
||||
|
||||
`blender/README.md`의 기존 Python·Blender·FFmpeg 경로와 실행 순서를 사용한다. 전역 설정·환경·사용자 스타일·기존 WAV·01–07 r1을 변경하지 않았다. 총괄만 project.json을 관리하며 이 담당은 승인 내용을 쓰지 않는다.
|
||||
|
||||
실제 의미검사 RED: 생성 전 없는 원본으로 verify_model.py exit 1을 확인했다. test_model.py는 없음·잘못된 Scene·H 누락·부피 driver 오류 4가지 실제 .blend를 각각 exit 1로 거부하고 원본은 exit 0으로 통과시킨다. model-test-results.json에 실제 명령과 종료코드를 기록한다. 테스트 fixture와 원본은 외부 의존성 없이 자기 generated-source에 보존한다.
|
||||
|
||||
초기 실행기의 선택 Scene 미활성 문제로 실제 PNG 픽셀이 시작/중간/끝에서 동일한 클립이 발견됐다. 파일 probe/전체 디코드 성공과 실제 움직임 확인을 분리했다. 그 시도는 미채택하고 source·PNG·MP4·evidence·visual-failure.json을 보존한다. 중단 시도도 failure.json에 이유와 실제 완료 PNG 범위를 기록했다. 최종 렌더는 실행기 수정 후 새 attempt에서 수행하며 wrapper의 실제 픽셀 변화 검사와 직접 화면 검토를 통과한 파일만 연결한다.
|
||||
|
||||
현재 최종 증거는 아래에 별도로 붙인다. `.blend`, 프레임 PNG, MP4, 로그, 실패 시도는 r2의 좁은 `.gitignore`로 실물을 보존하고 커밋하지 않는다. 작은 model/design 검사·명령 기록 JSON만 커밋한다. 총괄은 design-check.json의 actual_assets 파일·해시와 대표 evidence를 작업 루트로 복사한 뒤 실제 재검사해야 한다.
|
||||
|
||||
## 실제 제출 증거
|
||||
|
||||
원본: [preview.blend](generated-source/preview.blend), Blender **5.2.2 LTS**, SHA-256 `90300fd78369d88371f727a69f6c4b03f2d0cd3bb950d2698c48018e111a9aeb`. 실제 Scene은 BondPair/Interface/DropletStretch이며 외부 링크·이미지·글꼴·미디어·캐시가 없다. 검사 객체는 PairA/PairB, WaterCutawayBody, Droplet이다. 원자·실선·점선 객체와 실제 배치 검사는 [model-check.json](model-check.json)을 참조한다.
|
||||
|
||||
모든 최종 대표는 **BLENDER_EEVEE / GPU / 50fps / 1080×1920 / samples=64 / seed=0**이다. [실제 CLI 명령과 종료코드](cli-commands.json), [설계·파일·복사 해시 검사](design-check.json), [실제 RGB·모바일 관찰](motion-check.json), [RED→GREEN 검사](model-test-results.json), [기존 자료 보존 검사](preservation-check.json)를 연결한다. 과거 미채택 시도와 최종 제출 시도는 경로로 구별한다.
|
||||
|
||||
| 대표 | 끝 경계 PNG | 실제 무음 H.264 MP4 | 실행기 증거 |
|
||||
|---|---|---|---|
|
||||
| c07 | [F1219](render-attempts/5b09f426cc6a97b3-ab379b2220bf/frame-00001219.png) | [4040ms · 202F](render-attempts/b687ba0725d55e27-a1f95ff8f3d0/clip.mp4) | [PNG](render-attempts/5b09f426cc6a97b3-ab379b2220bf/evidence.json), [MP4](render-attempts/b687ba0725d55e27-a1f95ff8f3d0/evidence.json) |
|
||||
| c16 | [F3050](render-attempts/5a10b9e425756d31-40434704ca5c/frame-00003050.png) | [4880ms · 244F](render-attempts/791d67b5b8b0d08f-90e42af24cc7/clip.mp4) | [PNG](render-attempts/5a10b9e425756d31-40434704ca5c/evidence.json), [MP4](render-attempts/791d67b5b8b0d08f-90e42af24cc7/evidence.json) |
|
||||
| c18 | [F3372](render-attempts/34d7d6fa6d236962-0946653f91af/frame-00003372.png) | [4000ms · 200F](render-attempts/189e348aeb500188-623b27f1328f/clip.mp4) | [PNG](render-attempts/34d7d6fa6d236962-0946653f91af/evidence.json), [MP4](render-attempts/189e348aeb500188-623b27f1328f/evidence.json) |
|
||||
|
||||
위 6개 파일은 기존 실행기가 실제 probe와 전체 디코드를 통과한 결과다. source hash·Scene·렌더 프로필·논리 프레임을 대조했다. 3개의 PNG는 요청한 전체 컷 끝 경계이며 세 MP4는 정확한 `[start,end)` 전체 대표 구간이다. 마지막 영상 프레임은 각각 F1218/F3049/F3371이다.
|
||||
|
||||
실제 원본 mesh의 Blender F3173/3223/3273/3323/3373 부피는 약 9.1935287의 같은 임의 모형 단위를 유지했다. 최대 상대 변화는 **9.5636773×10⁻⁸**, 허용 기준 1% 이하다. 이 수치는 화면의 물 부피나 실험 측정값이 아니라 작성 모형의 보존 검사다. 내부와 표면의 이웃 검사 수치는 6/4, 위쪽 이웃은 2/0이며 정성적 배치 모형의 검사일 뿐 실제 액체의 고정 결합 수가 아니다. 실제 활성 H···O 쌍 교체도 검사했다.
|
||||
|
||||
최종 대표 PNG 3개와 영상 시작/중간/마지막 렌더 **9개를 view_image로 직접 열었다**. [c07 모바일 비교](generated-source/mobile-c07.png), [c16 모바일 비교](generated-source/mobile-c16.png), [c18 모바일 비교](generated-source/mobile-c18.png)는 각 패널 360×640이며 왼쪽→오른쪽이 시작→중간→마지막이다. 원본 이미지와 영상은 수정하지 않았다.
|
||||
|
||||
- c07: 두 종류 결합과 각 분자의 O1H2가 모바일에서도 구별된다. 오른쪽 분자가 가까워지고 점선이 짧아진 뒤 설명을 위해 상태를 유지한다. 중간/마지막 RGB가 같은 것은 이동 완료 후 유지 구간이며 시작과는 다르다. 원자나 점선이 좌우에 잘리지 않는다.
|
||||
- c16: 내부 강조가 사라지고 위쪽 표면 강조가 나타나며 이웃 연결이 바뀐다. 원자·남색 내부 실선·금색 점선이 보이고 공기 쪽 빈 공간과 물 체적이 구별된다. 청색 절개 체적을 실제 벽·막으로 해석하지 않도록 합성 주석이 필요하다.
|
||||
- c18: 둥근 방울→중간 타원→길어진 방울의 매끈한 실루엣이 선명하다. 반사·투명도·그림자가 설명을 가리지 않는다. 같은 부피 조건을 합성 주석으로 명시한다.
|
||||
|
||||
실제 실행 명령은 저장소 루트 기준이며 모두 기존 설치를 사용했다.
|
||||
|
||||
```powershell
|
||||
# 아래 최종 실행은 모두 종료코드 0이다. 실제 Blender negative 4개는 기대대로 종료코드 1이다.
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 contracts/check.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 tools/blender/test_runner.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/test_model.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/render_preview.py --kind image
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/render_preview.py --kind video
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/inspection_views.py
|
||||
& 'C:/Users/baram/anaconda3/python.exe' -B -X utf8 projects/water-hydrogen-bond-surface-tension/blender/verify_design.py
|
||||
```
|
||||
|
||||
계약 회귀 10개와 실행기 회귀 14개가 통과했다. 기본 source 재열기·renderer PNG/MP4 검증과 실제 RGB 관찰을 구별해 확인했다. 통합 음성 110560ms, 전체 시간 설계 5528F, 31컷/38자막은 그대로다. WAV 9개와 사용자 스타일 원본은 기존 작업루트와 바이트 동일하고 01–07 r1 JSON 내용도 동일하다. 작업사본의 JSON 줄끝 차이는 보존 검사에 명시했다.
|
||||
|
||||
초기 wrapper가 실행기 반환 구조의 없는 evidence_path를 읽어 종료코드 1이 된 시도는 실제 PNG를 보존하고 반환된 evidence 자체를 사용하도록 수정했다. 자동 저장된 invalid fixture·중단 렌더·정지 렌더·이전 품질 시도는 지우지 않았다. 정지 렌더는 새 Scene 활성화 수정 후 별도 attempt에서 전부 재생성했다.
|
||||
|
||||
사용자에게 필요한 결정은 새 08 r2/09 r2의 3D 설계·대표 품질에 대한 기존 **scenes 검토**다. 응답 없음과 담당자의 준비 완료는 승인이 아니다. 승인 전 정식 10–12 제작은 시작하지 않는다. 이후 자막·주석이 배치된 전체 31컷과 최종 영상은 다시 실제 검수한다.
|
||||
@@ -5,25 +5,288 @@
|
||||
"revision": 2,
|
||||
"inputs": [
|
||||
{
|
||||
"stage_id": "03-script",
|
||||
"stage_id": "02-research",
|
||||
"revision": 1
|
||||
},
|
||||
{
|
||||
"stage_id": "08-scenes",
|
||||
"revision": 2
|
||||
},
|
||||
{
|
||||
"stage_id": "02-research",
|
||||
"stage_id": "03-script",
|
||||
"revision": 1
|
||||
},
|
||||
{
|
||||
"stage_id": "07-cuts",
|
||||
"revision": 1
|
||||
},
|
||||
{
|
||||
"stage_id": "08-scenes",
|
||||
"revision": 2
|
||||
}
|
||||
],
|
||||
"payload": {
|
||||
"characters": [],
|
||||
"backgrounds": [],
|
||||
"appearances": []
|
||||
"backgrounds": [
|
||||
{
|
||||
"id": "bg-droplet",
|
||||
"description": "물방울의 크림색 3D 설명 공간",
|
||||
"fixed_features": [
|
||||
"references/1.png의 단순 형태·정돈된 색·정보량을 교육용 3D로 해석한다. 크림색 단순 공간·부드러운 조명·선명한 형태를 반복한다.",
|
||||
"산호색 O·미색 H·남색 내부 O–H 실선·금색 서로 다른 분자 H···O 점선·청색 물을 일관되게 사용한다.",
|
||||
"상하단에는 제목·자막용 빈 공간을 두고 부분전하·한국어 주석은 나중 Remotion에서 검증해 합성한다. 3D에 글자를 굽지 않는다.",
|
||||
"청색 물방울은 닫힌 mesh이며 별도 껍질·막을 만들지 않는다.",
|
||||
"같은 물의 양 변형은 실제 평가 mesh 부피를 시작·중간·끝에서 검사한다. 물리 시뮬레이션으로 주장하지 않는다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bg-molecules",
|
||||
"description": "분자의 3D 근접·불규칙 액체 연결 설명 공간",
|
||||
"fixed_features": [
|
||||
"references/1.png의 단순 형태·정돈된 색·정보량을 교육용 3D로 해석한다. 크림색 단순 공간·부드러운 조명·선명한 형태를 반복한다.",
|
||||
"산호색 O·미색 H·남색 내부 O–H 실선·금색 서로 다른 분자 H···O 점선·청색 물을 일관되게 사용한다.",
|
||||
"상하단에는 제목·자막용 빈 공간을 두고 부분전하·한국어 주석은 나중 Remotion에서 검증해 합성한다. 3D에 글자를 굽지 않는다.",
|
||||
"온전한 물 분자마다 O1H2·V자 형태·내부 O–H 실선 두 개를 유지한다. 각도·거리·힘 수치를 표시하지 않는다.",
|
||||
"분자간 H···O 점선은 서로 다른 분자의 원자만 연결한다. 액체는 불규칙한 이웃·변하는 연결이고 모든 분자의 연결 수를 고정하지 않는다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bg-interface",
|
||||
"description": "물-공기 경계의 3D 체적·절개 설명 공간",
|
||||
"fixed_features": [
|
||||
"references/1.png의 단순 형태·정돈된 색·정보량을 교육용 3D로 해석한다. 크림색 단순 공간·부드러운 조명·선명한 형태를 반복한다.",
|
||||
"산호색 O·미색 H·남색 내부 O–H 실선·금색 서로 다른 분자 H···O 점선·청색 물을 일관되게 사용한다.",
|
||||
"상하단에는 제목·자막용 빈 공간을 두고 부분전하·한국어 주석은 나중 Remotion에서 검증해 합성한다. 3D에 글자를 굽지 않는다.",
|
||||
"물 체적을 뒤쪽·옆면에 남긴 개념도이며 위 공기는 진공 주장이 아니다. 별도 고체 표면층·전체 아래 가속 화살표가 없다.",
|
||||
"안쪽은 다양한 방향의 액체 이웃, 표면은 위쪽 액체 이웃이 적은 환경으로 구별한다. 강조고리는 설명 표식이고 물 껍질이 아니다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "bg-comparison",
|
||||
"description": "같은 부피 비교·조건 예시의 크림색 3D 공간",
|
||||
"fixed_features": [
|
||||
"references/1.png의 단순 형태·정돈된 색·정보량을 교육용 3D로 해석한다. 크림색 단순 공간·부드러운 조명·선명한 형태를 반복한다.",
|
||||
"산호색 O·미색 H·남색 내부 O–H 실선·금색 서로 다른 분자 H···O 점선·청색 물을 일관되게 사용한다.",
|
||||
"상하단에는 제목·자막용 빈 공간을 두고 부분전하·한국어 주석은 나중 Remotion에서 검증해 합성한다. 3D에 글자를 굽지 않는다.",
|
||||
"동일 부피 비교와 외부 영향 조건 예시는 별도 Scene으로 구별한다.",
|
||||
"바닥은 조건 예시에만 등장한다. 중력·접촉각·크기 경계·표면장력 수치를 임의로 표시하지 않는다."
|
||||
]
|
||||
}
|
||||
],
|
||||
"appearances": [
|
||||
{
|
||||
"cut_id": "c01",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c02",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c03",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c04",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c05",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c06",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c07",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c08",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c09",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c10",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c11",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c12",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c13",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-molecules"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c14",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-interface"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c15",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-interface"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c16",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-interface"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c17",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-interface"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c18",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c19",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c20",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c21",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c22",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c23",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c24",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c25",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c26",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c27",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c28",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c29",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c30",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cut_id": "c31",
|
||||
"character_ids": [],
|
||||
"background_ids": [
|
||||
"bg-droplet"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# 반복 요소 검토 · 09 r2
|
||||
|
||||
**새 사용자 scenes 승인 대기**를 제안한다. 기존 scenes r1 승인은 새 3D r2 승인으로 사용하지 않는다. project.json·승인은 총괄의 소유다.
|
||||
|
||||
02 r1의 과학 근거, 03 r1 대본, 실측 07 r1과 새 08 r2를 실제 읽어 inputs에 기록했다. 31컷의 appearances는 08 순서를 따라 정확히 한 번씩 존재하며 빈 character_ids를 유지한다. 이 설명에는 인물이 필요 없다. 분자·물방울은 가짜 character로 등록하지 않고 승인 후 10의 object 자산으로 다룬다.
|
||||
|
||||
- bg-droplet: 도입·같은 부피 변형·답 회수의 크림색 3D 공간. 청색 닫힌 물방울과 부드러운 명암, 별도 막 없음, 실제 mesh 부피 검사를 유지한다.
|
||||
- bg-molecules: 단일·두 이웃·액체 연결의 3D 근접 공간. 산호 O1/미색 H2의 V자와 내부 남색 실선 두 개, 서로 다른 분자 H···O 금색 점선, 불규칙한 이웃·바뀌는 연결을 유지한다.
|
||||
- bg-interface: 물-공기 경계의 체적 절개 설명 공간. 청색 인공 절개면은 별도 벽·막이 아니며 공기는 진공으로 해석하지 않는다. 내부/표면의 실제 이웃 배치 차이와 설명 강조고리를 구별한다.
|
||||
- bg-comparison: 동일 부피 비교와 접촉면·외부 영향 조건 예시를 별도 Scene으로 구별한다. 바닥은 조건 예시에만 등장하며 접촉각·중력·크기 경계 수치를 넣지 않는다.
|
||||
|
||||
공통 고정 특징은 references/1.png의 단순 형태·정돈된 색을 교육용 3D로 해석한 밝은 크림색 배경, 부드러운 조명, 일관된 원자·결합·물 색과 상하단 글자 공간이다. 글자·부분전하는 나중 Remotion에서 검증해 배치한다. 고정 특징은 움직임·변형·연결 교체를 막지 않는다.
|
||||
|
||||
08의 같은 Scene/경계 상태를 공유하는 continuous 연결을 그대로 유지한다. 설명 범위나 조건이 바뀌는 cut에서는 다른 Scene을 계획했다. 근거에 없는 인물·캠핑 소재·상표·서사 반전은 추가하지 않았다. 대표 모델은 08 preview의 세 Scene이며 네 배경과 31 appearances의 정식 전 컷 모델을 완료했다고 주장하지 않는다.
|
||||
|
||||
검증: blender/verify_design.py는 실제 스키마·31컷 순서·빈 인물·유효 배경 ID·30연결·연속 경계 문자열/카메라를 검사한다. 최종 실제 실행 결과는 08 r2의 design-check.json·review.md와 대표 evidence에 연결한다. 10–12 시작 조건은 새 08/09에 대한 실제 사용자 scenes 승인이다.
|
||||
|
||||
실제 최종 검증: verify_design.py 종료코드 0, characters=[]·backgrounds=4·appearances=31. [08의 실제 대표 검토](../../08-scenes/r2/review.md), [설계와 복사 해시 근거](../../08-scenes/r2/design-check.json), [모바일 실제 관찰](../../08-scenes/r2/motion-check.json)을 참조한다. 02/03/07 r1과 승인 WAV를 보존했다. 새 08/09에 대한 사용자 결정은 아직 없으므로 정식 10–12를 완료하거나 새 scenes 승인을 기록하지 않는다.
|
||||
Reference in New Issue
Block a user