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>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,134 @@
|
||||
"""도면↔모델 불일치 시각자료 합성기 (운곡교 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
|
||||
Reference in new issue
Block a user