# -*- coding: utf-8 -*- """CAD DXF 고속 렌더러 — ezdxf drawing addon(엔티티당 개별 패스, 대형 도면에서 10분+) 대신 전 엔티티를 세그먼트로 평탄화해 matplotlib LineCollection 한 방에 그린다(수십만 세그먼트도 수 초). 사용: python tools/cad/fast_render.py [x0 y0 x1 y1 (m, 생략 시 전체)] [--size WxH --dpi N] [--rotate DEG --center CX,CY] 예: python tools/cad/fast_render.py k2_slim.dxf overview.png python tools/cad/fast_render.py k2_slim.dxf hall7.png 300 200 390 326 --size 9x12.6 --dpi 160 # 기울어진 홀: center 기준 -DEG 회전해 축정렬 후 crop(crop 좌표는 회전 후 좌표계 m) python tools/cad/fast_render.py k2_slim.dxf hall9.png 200 90 300 230 --rotate -11 --center 245,160 """ import math import sys import ezdxf import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib.collections import LineCollection MM = 1000.0 FLATTEN_SAGITTA = 50.0 # mm — 곡선 근사 허용 오차 def seg_points(e, depth=0): """엔티티 → 폴리라인 점열들(세그먼트 체인). INSERT는 가상 엔티티로 재귀.""" t = e.dxftype() try: if t == "LINE": yield [(e.dxf.start[0], e.dxf.start[1]), (e.dxf.end[0], e.dxf.end[1])] elif t in ("LWPOLYLINE", "POLYLINE"): pts = [(p[0], p[1]) for p in (e.get_points() if t == "LWPOLYLINE" else [(v.dxf.location[0], v.dxf.location[1]) for v in e.vertices])] if len(pts) >= 2: if getattr(e, "closed", False) or (t == "POLYLINE" and e.is_closed): pts.append(pts[0]) yield pts elif t in ("ARC", "CIRCLE", "ELLIPSE", "SPLINE"): pts = [(p[0], p[1]) for p in e.flattening(FLATTEN_SAGITTA)] if len(pts) >= 2: yield pts elif t == "INSERT" and depth < 4: for ve in e.virtual_entities(): yield from seg_points(ve, depth + 1) except Exception: return def main(): src, out = sys.argv[1], sys.argv[2] rest = [a for a in sys.argv[3:] if not a.startswith("--")] crop = [float(v) for v in rest[:4]] if len(rest) >= 4 else None size = (18.0, 18.0) dpi = 80 rotate = 0.0 center = (0.0, 0.0) for i, a in enumerate(sys.argv): if a == "--size": w, h = sys.argv[i + 1].split("x") size = (float(w), float(h)) elif a == "--dpi": dpi = int(sys.argv[i + 1]) elif a == "--rotate": rotate = float(sys.argv[i + 1]) elif a == "--center": cx, cy = sys.argv[i + 1].split(",") center = (float(cx), float(cy)) doc = ezdxf.readfile(src) msp = doc.modelspace() cos_t = math.cos(math.radians(-rotate)) sin_t = math.sin(math.radians(-rotate)) def xform(x, y): """m 단위 점을 center 기준 -rotate 회전(기울어진 홀 → 축정렬).""" if rotate == 0.0: return (x, y) dx, dy = x - center[0], y - center[1] return (center[0] + dx * cos_t - dy * sin_t, center[1] + dx * sin_t + dy * cos_t) segments = [] for e in msp: if e.dxftype() in ("TEXT", "MTEXT", "ATTDEF", "HATCH", "POINT"): continue for pts in seg_points(e): segments.append([xform(x / MM, y / MM) for x, y in pts]) print(f"polylines={len(segments)}", flush=True) fig = plt.figure(figsize=size, dpi=dpi) ax = fig.add_axes([0, 0, 1, 1]) ax.add_collection(LineCollection(segments, colors="#2b3a55", linewidths=0.35)) if crop: ax.set_xlim(crop[0], crop[2]) ax.set_ylim(crop[1], crop[3]) else: ax.autoscale() ax.set_aspect("equal") ax.axis("off") fig.savefig(out, facecolor="white") print(f"OK -> {out}") if __name__ == "__main__": main()