kintex/tools/cad/fast_render.py
zio 832ab7f8a9 feat(m2): K2 halls 6-10 DWG underlays - DWG tab complete for all 10 halls
Owner directive 2026-07-15: render exhibition center 2 and complete the DWG tab.

- fast_render.py: --rotate/--center options (align tilted halls axis-straight);
  slim-DXF workflow notes (K2 plan 106MB DXF, basement xref/detail layers dropped)
- Hall geometry measured programmatically (long-line angle clusters) and verified
  visually: H6 -12deg 60x93, H7/H8 axis-aligned 90x126 stacked (block exactly
  252m = 2x126, floor width exactly 90m), H9 -11deg / H10 +11deg 99x132
- public/media/floorplans/dwg/hall6..10.png: floor+3m crops, calibration stays
  deterministic (same margin formula as K1)
- hallFloorplan.ts: DWG_HALL_DIMS extended to all 10 halls (rotation noted)
- docs/analysis/cad-extraction.md: K2 section with confirmed coordinates table

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 02:32:58 +09:00

108 lines
3.8 KiB
Python

# -*- coding: utf-8 -*-
"""CAD DXF 고속 렌더러 — ezdxf drawing addon(엔티티당 개별 패스, 대형 도면에서 10분+) 대신
전 엔티티를 세그먼트로 평탄화해 matplotlib LineCollection 한 방에 그린다(수십만 세그먼트도 수 초).
사용:
python tools/cad/fast_render.py <dxf> <out.png> [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()