agentleFS
s-choungs-choung/Research-Skills/skills/figure_construction/SKILL.md

figure_construction

Skill36 starsChanged 6 months agoMITView on GitHub

Use when composing a multi-panel paper figure (main figure, SI figure, collage of existing plots) or when the user says 콜라주, 콜라쥬, 패널 합쳐, main figure 구성, figure 구성, 그림 합치기, 서브플롯 정렬, 프레임 맞춰, 논문 그림 조립, panel layout, composite figure. Re-plots panels from source data in one matplotlib figure instead of pasting images, so frames, colours, and font sizes match.

The file

Files
SKILL.md

figure_construction: 논문 다중 패널 그림 조립

결론 먼저

  • 콜라주는 그림 파일을 붙이는 작업이 아니라 한 matplotlib figure 안에서 다시 그리는 작업이다.
    • 기존 SVG나 PNG를 나란히 놓으면 frame 선, 글자 크기, 색이 어긋난다.
    • 패널마다 만든 스크립트에서 데이터 계산 부분만 가져와 새 스크립트 하나에 모은다.
  • 한 그림 안에서 다음 세 가지는 예외 없이 같아야 한다.
    • 좌우 이웃 패널의 frame 위아래 선 위치, 상하 이웃 패널의 frame 좌우 선 위치.
    • 종별 색 코드.
    • 글자 크기(축 라벨, 틱, 레전드, 패널 글자, 주석).

레이아웃 규칙

  • GridSpec 하나로 모든 패널을 잡는다. subplots_adjust나 bbox_inches='tight'로 개별 패널을 움직이지 않는다.
    • 같은 행의 패널은 같은 높이, 같은 열의 패널은 같은 폭으로 둔다. 서로 다른 크기가 필요하면 GridSpec의 height_ratios, width_ratios로 잡고 셀 경계는 공유한다.
    • 이미지 패널(구조 렌더, 스냅샷)은 imshow(aspect='auto')를 피하고 aspect='equal'로 두되, 이미지 자체를 셀 비율에 맞춰 미리 잘라 둔다. 이미지가 셀을 못 채우면 frame이 어긋난 것으로 본다.
  • 축 라벨이 이웃 패널과 같은 뜻이면 한 번만 쓴다.
    • 같은 행에서 y축 라벨이 같으면 왼쪽 패널에만, 같은 열에서 x축 라벨이 같으면 아래 패널에만.
    • 같은 x축 범위를 쓰는 패널은 sharex로 묶고 틱도 같게 한다.
  • 레전드는 그림 전체에 한 개를 원칙으로 한다.
    • 종별 색이 전 패널 공통이면 첫 패널이나 그림 상단에 한 번만.
    • 패널마다 뜻이 다른 표기(실선 대 띠, 진한 색 대 연한 색)만 해당 패널 안에 둔다.
  • 패널 글자는 fig.text가 아니라 각 ax.text(-0.18, 1.02, 'a', transform=ax.transAxes)처럼 축 좌표 기준으로 둔다. 그래야 패널 위치가 바뀌어도 따라간다.
    • 소문자, bold, 축 라벨보다 1~2 pt 크게.

콜라주 배치 규칙 (2026-09-13 리뷰 반영)

  • 왼쪽 열 정렬이 1순위. 전폭 이미지 패널(a)의 frame 왼쪽 선과 그 아래 b, d의 왼쪽 spine이 같은 x에 서야 한다. 오른쪽 열(c, e)도 서로 같은 x.
    • 이미지 패널은 set_xlim(box_left_px, ...)로 그림 속 상자 선을 축 왼쪽 끝에 맞추고, ax.set_position([axB.x0, y, axE.x1 - axB.x0, h])로 아래 행 spine 폭에 맞춘다. 높이는 이미지 비율로 계산한다.
    • 패널 글자는 transAxes가 아니라 figure 좌표 fig.text로 열마다 x 하나를 공유한다. y축 라벨 길이가 패널마다 달라 transAxes 기준이면 글자가 들쭉날쭉해진다. 오른쪽 열 글자 x는 axE.x0 - (axB.x0 - LETTER_X).
  • 읽는 순서가 논리 순서. a부터 e가 왼쪽 위에서 오른쪽 아래로 가게 둔다. 내용 순서가 바뀌면 글자가 아니라 패널 자리를 바꾼다. 요약 패널(층별 점 그림)은 원본 곡선 바로 옆에, 파생 모델(에너지에서 droplet)은 아래 행에 이어서.
  • 이미지 행의 height_ratios가 커서 빈 띠가 생기면 줄인다. 치수선과 원소 legend까지 포함해 높이를 잡는다.
  • fig.legend를 위에 둘 때는 GridSpec top을 legend 높이만큼 내리고 PNG로 첫 행 글자와 겹침을 본다. overlap audit은 fig.legend를 검사하지 않는다.
  • 보충 그림에서 영역(hexane, DMF) 구분이 필요하면 바탕색에 더해 같은 x 눈금으로 자른 구조 렌더 띠를 그래프 위에 붙인다. 중복이어도 괜찮다.
  • 층 경계 같은 분석 구간은 데이터 근거가 있는 값(예: free energy가 0을 지나는 깊이)으로 정하고, 본문 그림, 보충 그림, 표에 같은 경계를 쓴다. 바꿀 때 스크립트 안 중복 상수가 덮어쓰지 않는지 grep한다.
  • 패널 메시지가 당연한 말(크면 표면 비율이 작다)이면 연속 곡선 대신 대표 조건 몇 개 비교로 바꿔 "어느 조건부터 달라지는가"를 보인다.
  • 에너지 축은 기호(U) 대신 뜻이 드러나는 metric 이름(stabilization free energy)으로.
  • xlim은 데이터가 있는 범위부터. 한쪽에 값이 없는 패널은 0부터.
  • 그림 안 글자는 full name이 기본. "hex.", "sub.", "n/a" 같은 약어 금지. 자리가 좁으면 글자를 세로로 돌리거나 위치를 옮긴다.
  • 한 패널에 한 양. 층 3개 × 이온 3개 × 조건 6개처럼 마커 종류와 legend가 두 벌 필요해지면 과하다. 결론에 쓰는 양 하나만 선으로 그리고 나머지 수치는 본문으로 보낸다.
  • 층 정의나 정규화는 공통 모듈(예: layers.py) 하나에 두고 본문 그림, 보충 그림, 표가 전부 import한다. 같은 값이 그림마다 소수 셋째 자리까지 일치하는지 print로 확인한다.

구조 렌더 패널 (Blender) 규칙 (2026-09-14, CeO2 케톤화 캠페인에서 정리)

렌더 자체(카메라, 조명, 원자 반경, PBC 상자, 원소 legend 생성)는 blender-atom-render 스킬을 따른다. 여기는 그 PNG를 그림 패널로 넣을 때의 규칙만 둔다.

  • 렌더 입력 조건. film_transparent=True RGBA, 2000 px, scale_ballradius=1.0. 한 그림 안의 렌더는 같은 시점(top, side, perspective 중 하나)과 같은 ortho 배율 규칙으로 뽑는다. 시점이 섞이면 행을 나눈다.
  • 렌더 구조와 에너지 구조는 일대일. 패널이나 strip의 각 칸은 옆 그래프가 실제로 쓴 에너지의 구조 파일을 렌더한 것이어야 한다. 합성 전에 에너지 소스 파일 목록과 렌더 파일 목록을 대조한다.
  • 자르기. 알파 채널 getbbox()로 자르고 pad 10 px을 남긴다. bbox가 원본 캔버스 경계에 닿으면 렌더가 잘린 것이니 ortho_scale을 늘려 다시 렌더한다. 패널에 넣을 때는 앞의 이미지 패널 규칙대로 셀 비율에 맞춰 여백 없이 넣는다.
  • 배율.
    • 슬랩끼리는 높이를 같게 맞춘다 (strip 기준 panel 높이 330 px). 슬랩 크기가 같으면 원자 크기도 같게 보인다.
    • 기체 분자는 높이를 슬랩의 약 32%로, 폭 상한을 슬랩 높이의 약 55%로 둔다. 높이만 맞추면 side view의 납작한 분자가 슬랩만큼 커진다.
    • 크기 비교가 의미 있는 기체 분자끼리는 1600 px frame 기준 공통 배율 하나를 쓴다.
  • reaction strip (elementary step 순서).
    • 항상 1행. 2행으로 접거나 완성된 PNG를 픽셀로 잘라 재배치하지 않는다 (이전 wrap_strip2.py 사고).
    • 칸 사이 화살표는 → 글자가 아니라 도형으로 그린다 (길이 105 px, 두께 9 px, 칸 간격 150 px). 페이지 폭으로 줄여도 보인다.
    • 칸 폭 = max(이미지 폭, 라벨 폭 + 8 px). 긴 라벨이 잘리지 않게 한다.
    • 라벨은 상태 조성 전체(CH₂CO* + H*)로, 옆 ΔG 그래프 라벨과 글자 하나까지 같게. 번호(1, 1')만 달지 않는다.
    • 라벨 크기는 strip 폭에 대한 비율로 정한다 (panel 330 px에 52 px). 11:1 strip을 6.5 in 폭에 넣어도 읽혀야 한다.
  • 그래프와 strip 합성.
    • 권장: matplotlib GridSpec 한 figure에서 위 행 ΔG 그래프, 아래 행 strip을 imshow로 넣고 두 행의 왼쪽과 오른쪽 선을 맞춘다.
    • PIL 세로 쌓기(14_make_composites.py 방식)는 폭만 맞출 뿐 frame 정렬이 안 되니 초안에만 쓴다.
    • 그래프의 상태 순서와 strip 칸 순서가 같아야 한다. 가능하면 strip 칸 중심을 그래프의 레벨 x 위치에 맞춘다.
  • phase나 모델 모음 grid. 칸 폭의 92%로 타일을 맞추고, 라벨은 칸 폭의 7.5% 크기로 타일 아래에 둔다. 행 높이는 가장 큰 타일 기준.
  • 원소 legend. 구조 렌더가 들어간 그림에는 원소 legend(renders/legend.png)를 한 번 붙인다. 원소 색을 바꿨으면 legend 구도 같은 색으로 다시 렌더한다.
  • 강조 표시. 특정 원자(H*, 표적 OH)를 가리키는 원이나 화살표는 렌더 위에 matplotlib patch로 그리고, 좌표는 구조 파일 원자 위치를 카메라 투영으로 변환해 잡는다. 손으로 찍은 원은 재렌더 때 어긋난다.
  • ΔG 그래프 라벨 (같이 붙는 경우). 레벨마다 중간체 이름을 쓴다. 겹치면 두 경로가 공유하는 중간체 이름을 한 번만 쓰고, 경로마다 다른 spectator(H*, OH*)는 표나 캡션으로 보낸다. 번호만 다는 방식은 금지 (2026-09-14 리뷰).
  • 참고 구현: 186_Grace_Wang_cowork/figures/13_make_strips.py(strip), 14_make_composites.py(합성), 24_plot_route1_H_vs_clean_numbered.py(이름 라벨 비교 그래프).

글자 크기 규칙

  • 최종 인쇄 폭을 먼저 정한다. 1열 8590 mm, 2열 170180 mm. figsize를 그 폭(inch)으로 잡고 그 안에서 폰트 pt를 정한다.
    • 인쇄 기준 축 라벨 89 pt, 틱 78 pt, 레전드 7~8 pt, 패널 글자 10 pt, 주석 7 pt.
    • 원본 패널 스크립트에서 fs=18 같은 값을 그대로 가져오지 않는다. 그 값은 단독 그림 기준이다.
  • 합치면서 글자가 작아지는 함정.
    • 큰 단독 그림을 축소해 넣으면 라벨, 틱, 레전드가 인쇄에서 5 pt 아래로 내려간다. 축소가 아니라 재플롯으로 피한다.
    • 한 패널에 위첨자, 아래첨자가 많은 레전드(Pb²⁺, Br⁻)는 mathtext가 아니라 유니코드 첨자로 써서 크기가 튀지 않게 한다.
    • savefig 뒤 SVG의 font-size 값을 grep해서 종류가 3~4개인지 확인한다. 5개 넘으면 어디선가 크기가 새고 있다.

색과 선 규칙

  • 종별 색 dict를 스크립트 맨 위에 한 번 두고 모든 패널이 그것만 참조한다.
    • 이 프로젝트 예: DMF #77AEB3, hexane #E5885D, Br #5B8FB9, Pb #B4944B, FA #6a5acd, 대조군 #7f7f7f.
  • 데이터 선에 점선을 쓰지 않는다. 구분은 색으로 한다.
    • 안내선(기준값 1, 계면 위치 0)만 점선 허용.
  • 띠(표준오차, 표준편차)는 같은 색 alpha 0.2~0.25로 통일하고, 그 뜻을 레전드에 한 항목으로 한 번만 적는다.

작업 순서

  1. 패널 목록과 각 패널의 원본 스크립트, 데이터 파일을 표로 적는다.
  2. 원본 스크립트에서 계산 부분을 함수로 떼어 새 스크립트에 옮긴다. 그리기 부분은 버린다.
  3. GridSpec 뼈대만 먼저 그려 빈 frame이 맞는지 본다.
  4. 패널을 채우고 fig.canvas.draw() 뒤 텍스트 겹침 검사(audit_text_overlap)를 돌린다.
  5. SVG와 PNG(300 dpi)를 같이 저장한다. HTML은 SVG, 투고는 PNG나 PDF.
  6. 스크린샷을 실제 인쇄 폭으로 축소해 보고 글자가 읽히는지 눈으로 확인한다.

용어와 라벨 규칙 (2026-09-09 리뷰 반영)

  • 그리기 전에 그림 전체의 용어를 표로 적고 그 수를 줄인다.
    • 같은 양에 이름이 둘이면 하나를 지운다. "interior"와 "bulk", "uniform"과 "bulk density", "excess"와 "1보다 큼"은 각각 하나로.
    • 층 이름은 세 개까지. 이 프로젝트는 contact layer(08 Å), subsurface(820 Å), bulk(20 Å 밖)만 쓴다. 0~20 Å 전체를 부를 때는 "0 to 20 Å"로 숫자 그대로.
  • 라벨을 통일하기 전에 기준(분모)이 정말 같은지 확인한다. 평면 상자의 bulk 밀도와 droplet 전체 평균 N/V는 다른 기준이라, 같은 "relative density"로 묶으면 독자가 "R이 커질수록 bulk가 올라간다"처럼 거꾸로 읽는다. 기준이 다르면 라벨을 다르게 쓰고 캡션에 "이 패널만 기준이 X"라고 적는다.
  • 축 라벨은 짧고, 같은 양이면 패널마다 같은 문장으로. 정규화 밀도는 전부 "relative density", 계면 거리는 전부 "distance from interface (Å)".
    • 정의(어떤 기준으로 나눴는지, 어느 쪽이 DMF인지)는 라벨이 아니라 캡션에 한 번.
    • 라벨에 수식을 넣지 않는다. "U (kT)"로 쓰고 U의 정의는 캡션에.
  • 맥락 없는 새 개념을 라벨에 넣지 않는다. "interface normal", "slab-uniform", "N / V" 같은 말은 독자가 처음 보는 말이면 빼거나 캡션에서 풀어 쓴다.
  • 도식에 예시 숫자를 박지 않는다. 반지름은 화살표와 "R"로 표시하고 예시값(5 nm)은 캡션으로.
  • 패널 글자는 bold 소문자 a, b, c만. "(a)" 괄호 표기는 그림 안에도 캡션에도 쓰지 않는다.
    • 글자 하나는 subplot 하나다. 2 × 3 grid면 a~f. 같은 글자를 여러 subplot에 반복하지 않는다.
    • 보충 그림은 기존 그림 여러 장을 a, b로 묶지 않는다. 그림 하나에 원본 render 하나.
  • 텍스트와 frame 사이를 띄운다. 패널 안 주석은 축 좌표 4~5 % 안쪽, ylim은 주석 높이만큼 여유를 둔다. 스크립트 끝에 frame 근접 검사를 돌린다(figstyle.audit_text_frame).
  • 이웃 패널이 같은 색을 다른 뜻으로 쓰지 않는다. b에서 DMF와 hexane 영역을 종별 색으로 칠했으면 c의 층 구분 띠는 회색 계열로.
  • 축 방향은 양수가 기본. 관심 있는 쪽(계면에서 DMF 안쪽)이 양수가 되게 부호를 정하고, 본문 그림과 보충 그림 전부 같은 부호를 쓴다. 양쪽을 다 그리는 패널은 어느 쪽이 무엇인지 그림 안에 짧게 적고, 캡션에 "양수가 DMF 쪽"을 한 번 적는다.
  • y축을 비율(A / B)로 두면 독자가 방향을 거꾸로 읽기 쉽다. 메시지가 "작을수록 많다"처럼 양의 변화면 그 양 자체(예: 표면층에 있는 이온의 %)를 그린다.
  • 패널 글자는 축 라벨보다 4 pt 크게(라벨 9 pt면 13 pt). 11 pt는 작다는 리뷰를 받았다.
  • 안내선(계면 위치 등)은 레전드 아래에서 끝나게 ymax를 줄인다.
  • 패널 하나의 메시지가 한 문장으로 안 나오면 곡선을 줄인다. 두 계열(표면층과 안쪽)을 다 그려서 뜻이 흐려지면 결론에 쓰는 쪽만 남기고, 실험 조건 범위는 회색 띠로 표시한다.
  • 레전드는 데이터가 비어 있는 자리에 둔다. 곡선이 지나는 구역과 겹치면 ylim을 늘려 자리를 만든다.
  • 이미지 패널은 여백을 남기지 않는다. 라벨과 치수는 이미지 바로 옆에 붙이고, 셀 폭을 이미지가 다 쓰게 xlim을 잡는다.
  • 정규식으로 "(a)"를 일괄 치환할 때 JS나 코드 블록까지 건드리지 않는지 확인한다. function(b)가 깨진 적이 있다.

보충 그림 번호와 소속 (2026-09-15 원고 리뷰 반영)

  • Supplementary Fig.와 Table 번호는 본문 첫 언급 순서로 매긴다. Results 언급이 먼저, Methods 언급이 그다음.
  • 그림마다 소속을 먼저 정한다.
    • 결과를 보여 주는 그림(층별 밀도 통계, 전이 free energy, complex 구조)은 Results에서 인용.
    • 계산을 믿어도 되는지 보여 주는 그림(수렴 진단, 시간 블록 RDF, 상자 크기 비교)은 Methods에서 인용.
  • 같은 이야기를 하는 그림만 한 괄호에 묶는다. 서로 다른 발견을 "Figs. 1-4"로 묶지 않는다.
  • 렌더 한 장과 곡선 여러 장이 같은 메시지(예: 상자 크기 비교)면 한 그림으로 합친다. 렌더 a, 곡선 b~d, 폭 정렬.
  • 번호를 바꾸면 SI 파일 순서, 캡션 머리 번호, 캡션 안 교차 참조를 함께 고친다(docx-scientific-formatting의 블록 이동 규칙).
  • 캡션에는 Methods에 이미 적은 수치(상자 크기, seed 수, 시점, 평균 구간, 층 깊이, 정의식)를 반복하지 않는다.

캡션

  • 캡션은 무엇을 그렸는지만 적는다. 수치, 순위, 해석은 본문으로 보낸다.
    • 축의 뜻, 기준 상태(균일 = 1, bulk DMF 기준), 색과 기호 뜻, 띠와 점선 뜻, 표본 수와 시간 창.
    • "shows that", "confirms", "가장 크다", "일치한다"는 캡션에 쓰지 않는다.
  • 패널 글자 순서대로 (a), (b) 문장을 이어 쓰고, 공통 사항(색, x축)은 마지막에 한 번 적는다.

하지 말 것

  • 기존 SVG 여러 장을 HTML grid나 이미지 편집기로 붙여 놓고 "그림 완성"이라고 하는 것.
  • 패널마다 다른 폰트 크기 변수(fs=18, fs=16, fs=14)를 그대로 두는 것.
  • bbox_inches='tight'에 의존해 여백을 맞추는 것. 패널 경계가 매번 달라진다.
  • 캡션에 결과 수치를 적는 것.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.