Files
Pier_v1/Code/review_pipeline/compose_new.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 11:20:18 +09:00

135 lines
4.8 KiB
Python

"""도면↔모델 불일치 시각자료 합성기 (운곡교 M-5 이미지 형식: 헤더 / 도면 패널(파랑) / 모델 패널(주황·노랑·회색) / 차이·도면 쪽·조치).
사용: from compose_new import make; make(cfg)
cfg = {code, title, subtitle, color:(r,g,b), out,
panels:[{label, color, images:[{path, crop(optional), cap(optional)}], maxh}], rows:[(라벨, 본문)]}
"""
import os, sys
sys.path.insert(0, r"C:\Users\User\AppData\Local\Temp\claude\D--Rhino-MCP-rhino-mcp-main\f46b47bc-9105-4079-9b62-fcbee31b26ae\scratchpad\pylib")
from PIL import Image, ImageDraw, ImageFont
FN = r"C:\Windows\Fonts\malgun.ttf"
FB = r"C:\Windows\Fonts\malgunbd.ttf"
W = 2400
PAD = 40
BLUE = (60, 120, 200)
ORANGE = (230, 110, 20)
DYEL = (190, 150, 0)
GREY = (120, 120, 120)
def font(sz, bold=False):
return ImageFont.truetype(FB if bold else FN, sz)
def fit(img, w, h):
r = min(w / img.width, h / img.height)
return img.resize((max(1, int(img.width * r)), max(1, int(img.height * r))), Image.LANCZOS)
def wrap(draw, text, fnt, maxw):
lines = []
for para in text.split("\n"):
cur = ""
for ch in para:
if draw.textlength(cur + ch, font=fnt) > maxw:
lines.append(cur)
cur = ch
else:
cur += ch
lines.append(cur)
return lines
def autocrop(im, pad=30, thr=14):
bg = im.getpixel((2, 2))
px = im.load()
w, h = im.size
x0, y0, x1, y1 = w, h, 0, 0
for y in range(0, h, 2):
for x in range(0, w, 2):
p = px[x, y]
if abs(p[0] - bg[0]) + abs(p[1] - bg[1]) + abs(p[2] - bg[2]) > thr:
if x < x0: x0 = x
if x > x1: x1 = x
if y < y0: y0 = y
if y > y1: y1 = y
if x1 <= x0:
return im
return im.crop((max(0, x0 - pad), max(0, y0 - pad), min(w, x1 + pad), min(h, y1 + pad)))
def load(spec):
im = Image.open(spec["path"]).convert("RGB")
if spec.get("auto", spec["path"].find("shots") >= 0):
im = autocrop(im)
if spec.get("crop"):
im = im.crop(spec["crop"])
if spec.get("draw"):
spec["draw"](im)
return im
def make(cfg):
PW = W - 2 * PAD
# 패널 이미지 준비
prepared = []
for p in cfg["panels"]:
ims = [load(s) for s in p["images"]]
n = len(ims)
gap = 20
cw = (PW - 8 - gap * (n - 1)) // n
maxh = p.get("maxh", 560)
fitted = [fit(im, cw, maxh) for im in ims]
ph = max(f.height for f in fitted) + 8
capsp = 0
if any(s.get("cap") for s in p["images"]):
capsp = 52
prepared.append((p, fitted, ph, capsp, cw, gap))
# 본문 줄 계산
dtmp = ImageDraw.Draw(Image.new("RGB", (10, 10)))
fr = font(32)
rows = []
for lab, text in cfg["rows"]:
lines = wrap(dtmp, text, fr, PW - 230)
rows.append((lab, lines))
H = 120 + 14
for p, fitted, ph, capsp, cw, gap in prepared:
H += 50 + 6 + ph + capsp + 14
H += 12
for lab, lines in rows:
H += max(46, 42 * len(lines)) + 12
H += 24
img = Image.new("RGB", (W, H), (255, 255, 255))
d = ImageDraw.Draw(img)
# 헤더
d.rectangle([0, 0, W, 120], fill=(38, 44, 56))
d.rounded_rectangle([PAD - 6, 24, PAD + 130, 96], radius=10, fill=cfg.get("color", ORANGE))
d.text((PAD + 62, 60), cfg["code"], font=font(52, True), fill=(255, 255, 255), anchor="mm")
d.text((PAD + 170, 14), cfg["title"], font=font(48, True), fill=(255, 255, 255))
d.text((PAD + 170, 74), cfg["subtitle"], font=font(30), fill=(210, 215, 225))
y = 134
for p, fitted, ph, capsp, cw, gap in prepared:
col = p.get("color", BLUE)
d.rectangle([PAD - 6, y, W - PAD + 6, y + 50], fill=col)
d.text((PAD + 10, y + 25), p["label"], font=font(32, True), fill=(255, 255, 255), anchor="lm")
y += 56
d.rectangle([PAD - 6, y, W - PAD + 6, y + ph + capsp + 4], outline=col, width=4)
x = PAD
for f, spec in zip(fitted, p["images"]):
img.paste(f, (x, y + 4))
if spec.get("cap"):
d.text((x + 6, y + 4 + f.height + 4), spec["cap"], font=font(30, True), fill=(30, 30, 30))
x += cw + gap
y += ph + capsp + 18
y += 6
for lab, lines in rows:
h = max(46, 42 * len(lines))
d.rounded_rectangle([PAD - 6, y, PAD + 180, y + 44], radius=8, fill=(232, 236, 244))
d.text((PAD + 87, y + 22), lab, font=font(30, True), fill=(30, 30, 40), anchor="mm")
for i, ln in enumerate(lines):
d.text((PAD + 210, y + 4 + 42 * i), ln, font=fr, fill=(25, 25, 25))
y += h + 12
os.makedirs(os.path.dirname(cfg["out"]), exist_ok=True)
img.save(cfg["out"])
return img.size