- 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>
110 lines
5.0 KiB
Python
110 lines
5.0 KiB
Python
"""빠른 DXF 크롭 렌더러 (LINE/ARC/CIRCLE/INSERT 분해/TEXT/MTEXT)
|
|
사용: python -I fastrender.py <dxf> <x0> <x1> <y0> <y1> <out.png> [width_px]
|
|
캐시: <dxf>.cache.pkl (분해한 선분·글자)
|
|
"""
|
|
import sys, os, pickle, math, re
|
|
sys.path.insert(0, 'pylib')
|
|
import ezdxf
|
|
import matplotlib
|
|
matplotlib.use('Agg')
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.collections import LineCollection
|
|
from matplotlib import font_manager
|
|
|
|
f = sys.argv[1]
|
|
x0, x1, y0, y1 = map(float, sys.argv[2:6])
|
|
out = sys.argv[6]
|
|
wpx = int(sys.argv[7]) if len(sys.argv) > 7 else 2400
|
|
DARK = len(sys.argv) > 8 and sys.argv[8] == 'dark'
|
|
cache = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cache_' + os.path.basename(f) + '.pkl')
|
|
ACI = {1: 'red', 2: '#b8a000', 3: 'green', 4: '#00a0a0', 5: 'blue', 6: 'magenta', 7: 'black', 8: 'gray', 9: 'gray', 30: '#ff8000', 41: '#b08800'}
|
|
ACID = {1: '#ff3030', 2: '#ffff00', 3: '#00e000', 4: '#00e0e0', 5: '#5080ff', 6: '#ff40ff', 7: '#ffffff', 8: '#aaaaaa', 9: '#aaaaaa', 30: '#ff9a1f', 41: '#e6c200'}
|
|
FONT = re.compile(r'\\f[^;]*;')
|
|
|
|
def color_of(e, doc):
|
|
c = e.dxf.color
|
|
if c == 256 or c is None:
|
|
try:
|
|
c = doc.layers.get(e.dxf.layer).dxf.color
|
|
except Exception:
|
|
c = 7
|
|
c = abs(c)
|
|
return ACI.get(c, '#555555')
|
|
|
|
def arc_pts(cx, cy, r, a0, a1, n=24):
|
|
if a1 < a0:
|
|
a1 += 360
|
|
return [(cx + r * math.cos(math.radians(a0 + (a1 - a0) * i / n)), cy + r * math.sin(math.radians(a0 + (a1 - a0) * i / n))) for i in range(n + 1)]
|
|
|
|
def collect(doc):
|
|
segs = []; texts = []
|
|
def walk(ents, depth=0):
|
|
for e in ents:
|
|
t = e.dxftype()
|
|
col = color_of(e, doc)
|
|
lay = e.dxf.layer
|
|
if t == 'LINE':
|
|
segs.append(((e.dxf.start.x, e.dxf.start.y), (e.dxf.end.x, e.dxf.end.y), col, lay))
|
|
elif t == 'ARC':
|
|
p = arc_pts(e.dxf.center.x, e.dxf.center.y, e.dxf.radius, e.dxf.start_angle, e.dxf.end_angle)
|
|
for a, b in zip(p[:-1], p[1:]):
|
|
segs.append((a, b, col, lay))
|
|
elif t == 'CIRCLE':
|
|
p = arc_pts(e.dxf.center.x, e.dxf.center.y, e.dxf.radius, 0, 360, 36)
|
|
for a, b in zip(p[:-1], p[1:]):
|
|
segs.append((a, b, col, lay))
|
|
elif t in ('LWPOLYLINE', 'POLYLINE'):
|
|
try:
|
|
pts = [(v[0], v[1]) for v in e.get_points()] if t == 'LWPOLYLINE' else [(v.dxf.location.x, v.dxf.location.y) for v in e.vertices]
|
|
for a, b in zip(pts[:-1], pts[1:]):
|
|
segs.append((a, b, col, lay))
|
|
except Exception:
|
|
pass
|
|
elif t == 'INSERT' and depth < 3:
|
|
try:
|
|
walk(e.virtual_entities(), depth + 1)
|
|
except Exception:
|
|
pass
|
|
try:
|
|
for a in e.attribs:
|
|
if a.dxf.tag == 'RB_NO' or True:
|
|
texts.append((a.dxf.insert.x, a.dxf.insert.y, a.dxf.text, a.dxf.height, ACI[3], 0))
|
|
except Exception:
|
|
pass
|
|
elif t == 'MTEXT':
|
|
s = FONT.sub('', e.text).replace('{', '').replace('}', '').replace('\\P', '\n')
|
|
texts.append((e.dxf.insert.x, e.dxf.insert.y, s, e.dxf.char_height, col, e.dxf.get('rotation', 0)))
|
|
elif t == 'TEXT':
|
|
texts.append((e.dxf.insert.x, e.dxf.insert.y, e.dxf.text, e.dxf.height, col, e.dxf.get('rotation', 0)))
|
|
elif t == 'POINT':
|
|
segs.append(((e.dxf.location.x - 20, e.dxf.location.y), (e.dxf.location.x + 20, e.dxf.location.y), col, lay))
|
|
walk(doc.modelspace())
|
|
return segs, texts
|
|
|
|
if os.path.exists(cache) and os.path.getmtime(cache) > os.path.getmtime(f):
|
|
segs, texts = pickle.load(open(cache, 'rb'))
|
|
else:
|
|
doc = ezdxf.readfile(f)
|
|
segs, texts = collect(doc)
|
|
pickle.dump((segs, texts), open(cache, 'wb'))
|
|
|
|
fp = font_manager.FontProperties(fname=r'C:\Windows\Fonts\malgun.ttf')
|
|
w = x1 - x0; h = y1 - y0
|
|
fig = plt.figure(figsize=(wpx / 100.0, wpx / 100.0 * h / w), dpi=100, facecolor=('#222831' if DARK else 'white'))
|
|
ax = fig.add_axes([0, 0, 1, 1])
|
|
sel = [s for s in segs if max(s[0][0], s[1][0]) >= x0 and min(s[0][0], s[1][0]) <= x1 and max(s[0][1], s[1][1]) >= y0 and min(s[0][1], s[1][1]) <= y1]
|
|
def rc(c):
|
|
if not DARK:
|
|
return c
|
|
inv = {v: ACID[k] for k, v in ACI.items()}
|
|
return inv.get(c, '#dddddd')
|
|
lc = LineCollection([(s[0], s[1]) for s in sel], colors=[rc(s[2]) for s in sel], linewidths=0.6)
|
|
ax.add_collection(lc)
|
|
px_per_unit = wpx / w
|
|
for x, y, s, hh, col, rot in texts:
|
|
if x0 <= x <= x1 and y0 <= y <= y1:
|
|
ax.text(x, y, s, fontproperties=fp, fontsize=max(4, hh * px_per_unit * 0.72), color=rc(col), rotation=rot, ha='left', va='bottom')
|
|
ax.set_xlim(x0, x1); ax.set_ylim(y0, y1); ax.set_aspect('equal'); ax.axis('off')
|
|
fig.savefig(out, dpi=100, facecolor=fig.get_facecolor())
|
|
print('ok', len(sel), out)
|