Import S-CANVAS source + iter=1~7 lint cleanup
S-CANVAS (Saman Corp.) — DXF + DEM + AI 기반 3D 조감도 생성 엔진. ~24k LOC Python (scanvas_maker.py 7072 LOC GUI + 구조물 파서/빌더 다수). 이 커밋은 7-iter cleanup이 적용된 상태로 import: - F821 8 + B023 6: 비동기 lambda + except/loop 변수 캡처 NameError (Py3.13에서 reproduce 확인된 진짜 버그) - RUF012 4 + RUF013 1: ClassVar / implicit Optional 명시화 - F811/B905/B904/F401/F841/W293/F541/UP/SIM/RUF/PLR 700+ cleanup/modernization 신규 파일: - ruff.toml: target=py313, Korean unicode/저자 스타일/도메인 복잡도 무력화 - requirements-py313.txt: pyproj>=3.7, scipy>=1.14, numpy>=2.0.2 (Py3.13 wheel) - .gitignore: gcp-key.json, 캐시, 백업, 생성 이미지 제외 검증: ruff 0 errors, py_compile 0 errors, import 33/33 OK on Py3.13.13. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
119
params_to_json.py
Normal file
119
params_to_json.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""파서 결과 → JSON 브리지 (S-CANVAS env 측 generic 헬퍼).
|
||||
|
||||
이 모듈은 bpy를 import하지 않으므로 기존 S-CANVAS conda 환경에서 그대로 사용 가능.
|
||||
파서가 생성한 dataclass 파라미터(IntakeTowerParams, GateParams, ...)를 JSON으로 직렬화.
|
||||
|
||||
사용:
|
||||
# 취수탑
|
||||
from intake_tower_parser import parse_intake_tower
|
||||
from params_to_json import dump_dataclass_to_json
|
||||
p = parse_intake_tower(dxf_paths)
|
||||
dump_dataclass_to_json(p, "intake_params.json")
|
||||
|
||||
# 여수로 수문
|
||||
from gate_parser import parse_gate_dxf
|
||||
from params_to_json import dump_dataclass_to_json
|
||||
p = parse_gate_dxf(plan_dxf, section_dxf)
|
||||
dump_dataclass_to_json(p, "gate_params.json")
|
||||
|
||||
이후 Blender 헤드리스로:
|
||||
blender --background --python <builder_bpy>.py -- ^
|
||||
--params <params>.json --blend out.blend --render out.png
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _to_serializable(obj):
|
||||
"""dataclass / list / tuple / dict / 기본형 → JSON 직렬화 가능 구조."""
|
||||
if hasattr(obj, "__dataclass_fields__"):
|
||||
return {k: _to_serializable(v) for k, v in asdict(obj).items()}
|
||||
if isinstance(obj, (list, tuple)):
|
||||
return [_to_serializable(x) for x in obj]
|
||||
if isinstance(obj, dict):
|
||||
return {k: _to_serializable(v) for k, v in obj.items()}
|
||||
if isinstance(obj, (str, int, float, bool)) or obj is None:
|
||||
return obj
|
||||
# numpy / 기타: 가능하면 float, 아니면 str로 폴백
|
||||
try:
|
||||
return float(obj)
|
||||
except (TypeError, ValueError):
|
||||
return str(obj)
|
||||
|
||||
|
||||
def dump_dataclass_to_json(params, path: str) -> str:
|
||||
"""dataclass 인스턴스 → JSON 파일.
|
||||
|
||||
구조 종류와 무관하게 동작 (IntakeTowerParams, GateParams, ...).
|
||||
Returns: 작성한 절대 경로 (str)
|
||||
"""
|
||||
payload = _to_serializable(params)
|
||||
out_path = Path(path).resolve()
|
||||
out_path.write_text(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return str(out_path)
|
||||
|
||||
|
||||
# 이전 버전 호환 alias (intake_tower_3d_builder_bpy 가이드와의 호환)
|
||||
dump_intake_tower_params = dump_dataclass_to_json
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# CLI: 구조물 종류 자동 감지 → JSON 변환
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _cli_intake_tower(out_json: str, dxf_paths: list[str]) -> None:
|
||||
from intake_tower_parser import parse_intake_tower
|
||||
params = parse_intake_tower(dxf_paths)
|
||||
print(params.summary())
|
||||
final = dump_dataclass_to_json(params, out_json)
|
||||
print(f"\nJSON written: {final}")
|
||||
|
||||
|
||||
def _cli_gate(out_json: str, plan_dxf: str, section_dxf: str | None = None) -> None:
|
||||
from gate_parser import parse_gate_dxf
|
||||
params = parse_gate_dxf(plan_dxf, section_dxf)
|
||||
print(params.summary())
|
||||
final = dump_dataclass_to_json(params, out_json)
|
||||
print(f"\nJSON written: {final}")
|
||||
|
||||
|
||||
def _print_usage_and_exit():
|
||||
print("Usage:")
|
||||
print(" python params_to_json.py intake <out.json> <dxf_1> [dxf_2 ...]")
|
||||
print(" python params_to_json.py gate <out.json> <plan.dxf> [section.dxf]")
|
||||
print("")
|
||||
print("Examples:")
|
||||
print(" python params_to_json.py intake intake.json \\")
|
||||
print(" SAMPLE_CAD/취수탑1.dxf SAMPLE_CAD/취수탑2.dxf")
|
||||
print("")
|
||||
print(" python params_to_json.py gate gate.json \\")
|
||||
print(" Gate_Sample/수문_1.dxf Gate_Sample/수문_2.dxf")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
_print_usage_and_exit()
|
||||
|
||||
kind = sys.argv[1].lower()
|
||||
out_json = sys.argv[2]
|
||||
rest = sys.argv[3:]
|
||||
|
||||
if kind in ("intake", "intake_tower", "tower"):
|
||||
_cli_intake_tower(out_json, rest)
|
||||
elif kind in ("gate", "spillway", "weir"):
|
||||
plan = rest[0]
|
||||
section = rest[1] if len(rest) > 1 else None
|
||||
_cli_gate(out_json, plan, section)
|
||||
else:
|
||||
print(f"Unknown structure kind: {kind!r}")
|
||||
_print_usage_and_exit()
|
||||
Reference in New Issue
Block a user