kintex/tools/cad/render_hall_png.py
zio d8ef5c53df feat(m2): DWG/JPG floorplan underlay tabs + CAD extraction pipeline
Owner directives 2026-07-14~15: run the DWG pipeline; split floorplan into DWG and JPG tabs.

- tools/cad/dwg_extract.py: DWG(LibreDWG dwg2dxf) -> DXF -> layer inventory /
  keyword extraction / grid-label anchor detection (ezdxf, recover fallback)
- tools/cad/render_hall_png.py: render hall-band crops from the K1 combined
  plan+trench drawing (x0=293.5m, width 171m, Y bands 80.1m + 63m pitch,
  single load -> multi-crop). Real trench layout confirmed: 7 horizontal runs
  at ~9m pitch + central feeder (differs from assumed 6m grid seed - V61 later)
- public/media/floorplans/dwg/hall1..5.png: K1 hall underlays (3m margin crop,
  deterministic calibration from render params)
- Editor left panel new tool group with JPG/DWG tabs; DWG disabled for
  halls without CAD render yet (K2 pending). Canvas switches url+region.
- docs/analysis/cad-extraction.md: pipeline findings, limits (A3002 LibreDWG
  crash, band-to-hall numbering direction to confirm), next steps

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

73 lines
2.6 KiB
Python

# -*- coding: utf-8 -*-
"""CAD DXF → 홀별 DWG-탭 언더레이 PNG 렌더 (ezdxf drawing addon + matplotlib).
제1전시장 `전기,설비 트렌치.dwg`(평면+트렌치 통합 도면) 기준:
- 구조 그리드 Y 라벨이 63m 피치(y=80.1 시작) = 홀1~5 Y밴드
- X 원점·폭은 인자로 조정(렌더 결과를 눈으로 검증하며 캘리브레이션)
사용:
python tools/cad/render_hall_png.py <dxf> <hall_no 1~5 | 1-5> <x0_m> <width_m> <out.png|outdir> [margin_m]
예:
python tools/cad/render_hall_png.py k1_trench.dxf 1 293.5 171 hall1_dwg.png 3
python tools/cad/render_hall_png.py k1_trench.dxf 1-5 293.5 171 out_dir 3 # 1회 로드로 5개 crop
"""
import sys
import ezdxf
from ezdxf.addons.drawing import Frontend, RenderContext
from ezdxf.addons.drawing.matplotlib import MatplotlibBackend
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
# 잡음 레이어(치수·주석·표제) — 언더레이 가독성 우선
EXCLUDE_PREFIX = ("DIM", "E-TEXT", "1-TEXT", "nember", "0-기호")
EXCLUDE_TYPES = {"TEXT", "MTEXT", "ATTDEF"}
Y0_BASE_M = 80.1 # 홀1 밴드 시작(구조 그리드 Y 라벨 실측)
BAND_M = 63.0
def main():
import os
src, hall_arg, x0_m, width_m, out = sys.argv[1], sys.argv[2], float(sys.argv[3]), float(sys.argv[4]), sys.argv[5]
margin = float(sys.argv[6]) if len(sys.argv) > 6 else 3.0
if "-" in hall_arg:
a, b = hall_arg.split("-")
halls = list(range(int(a), int(b) + 1))
else:
halls = [int(hall_arg)]
doc = ezdxf.readfile(src)
msp = doc.modelspace()
# 1회 로드·1회 드로우 → 밴드별 xlim/ylim 변경 후 저장(재렌더 비용 회피)
fig = plt.figure(figsize=(17.1, 6.3), dpi=160)
ax = fig.add_axes([0, 0, 1, 1])
ctx = RenderContext(doc)
backend = MatplotlibBackend(ax)
def keep(e):
if e.dxftype() in EXCLUDE_TYPES:
return False
layer = e.dxf.layer.upper()
return not any(layer.startswith(p.upper()) for p in EXCLUDE_PREFIX)
Frontend(ctx, backend).draw_entities(e for e in msp if keep(e))
mm = 1000.0
for hall_no in halls:
y0_m = Y0_BASE_M + (hall_no - 1) * BAND_M
ax.set_xlim((x0_m - margin) * mm, (x0_m + width_m + margin) * mm)
ax.set_ylim((y0_m - margin) * mm, (y0_m + BAND_M + margin) * mm)
ax.set_aspect("equal")
ax.axis("off")
dest = os.path.join(out, f"hall{hall_no}.png") if len(halls) > 1 else out
fig.savefig(dest, facecolor="white")
print(f"OK -> {dest} band y[{y0_m}..{y0_m+BAND_M}]m x[{x0_m}..{x0_m+width_m}]m")
if __name__ == "__main__":
main()