"""실측 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}/r3' folder.mkdir(parents=True, exist_ok=True) body = dict(schema_version='1.1',project_id=PROJECT.name,stage_id=stage,revision=3,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. 실제 물 경계 x=[-2.85,2.85], y=[-0.85,1.35], z=[-3.15,0.80] 안에 전체 원자·결합을 포함하고 앞면·윗면을 연 절개 설명 모형, 카메라 (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/r3/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',3)])