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>
31 KiB
S-CANVAS — 프로그램 핵심 소개 (NotebookLM 발표자료 소스)
목적: 본 문서는 NotebookLM 에 업로드하여 발표 슬라이드, Audio Overview, 마인드맵 등을 자동 생성하기 위한 단일 소스 텍스트 입니다. S-CANVAS 프로그램의 정체성·아키텍처·핵심 기술·워크플로·차별화 포인트를 한 파일에 응축했습니다.
1. 한 줄 요약
S-CANVAS — Generative Design & Visualization Engine. CAD 도면(DXF)과 실제 공개 DEM·위성영상·Generative AI 를 결합해 건설/토목 프로젝트의 3D 조감도와 구조물 시각화를 자동 생성하는 데스크톱 엔진. Saman Corp. 자체 개발.
- 원래 명칭:
EG-VIEW(AI 기반 조감도 생성 시스템) - 2026-04-24 리브랜딩:
S-CANVAS(Generative Design & Visualization Engine) - 메인 진입점:
scanvas_maker.py - 플랫폼: Windows 10/11, Python + CustomTkinter GUI
2. 배경 & 문제 정의
2.1 기존 토목·건설 시각화의 한계
전통적인 CAD 평면도는 2D이며, 등고선·구조물 윤곽만 담겨 있어 실제 지형 맥락(주변 산세, 강, 식생, 도로망)이 빠져 있다. 발주처 보고·주민 설명회에서는 "실제 어떻게 보이는가" 가 중요하지만, 기존 워크플로에서는:
- CAD 도면을 3D 모델러(Civil 3D/Revit/Rhino) 로 다시 그리고
- GIS 데이터를 별도로 받아 좌표 정합
- 위성영상을 매핑하고
- 렌더 엔진(Lumion/Twinmotion/V-Ray) 으로 조명·재질·하늘 설정
- 카메라 앵글 잡고 이미지 출력
이 과정이 수일에서 수주 걸리며, 변경이 생길 때마다 대부분의 단계를 다시 수행해야 한다.
2.2 S-CANVAS 의 가설
"DXF 등고선·구조물 레이어 + 공개 DEM + 위성타일 + Generative AI 만으로, 클릭 4번에 사실 기반 3D 조감도를 만들 수 있다."
- DXF 의 등고선 → TIN(Triangulated Irregular Network) 자동 생성
- DXF 범위 밖 지형 → AWS Open Terrain Tiles(글로벌 DEM) 자동 채움
- 표면 텍스처 → Google/ArcGIS/Vworld 위성 타일을 UV 매핑
- 최종 사실감 향상 → Gemini/Stability AI 가 구조 보존 모드로 재렌더
2.3 핵심 가치 제안 (Value Proposition)
| 기존 | S-CANVAS |
|---|---|
| 모델링 수일 | TIN 생성 30초 |
| 외부 라이선스 GIS | 공개 DEM 자동 페치 |
| 수동 좌표 정합 | 4점 매칭 자동 GeoRef |
| 렌더 1회 수시간 | AI 렌더 1분 이내 |
| 변경 → 재모델링 | 변경 → 재실행 (캐시 재활용) |
3. 시스템 아키텍처
3.1 모듈 구성 (Top Level)
scanvas_maker.py # 메인 GUI 엔트리 (~6300 LOC, CustomTkinter)
├── splash.py # 인트로 MP4 스플래시 (cv2 + Tk Toplevel)
├── dem_extender.py # AWS Terrarium DEM 페치 + 도넛 링 메시
├── geo_referencing.py # 4점 매칭 GeoReferencing 워크플로
├── structure_placement.py # 구조물 위치/방향 자동 인식 + TIN 굴착
├── structure_templates.py # 구조물 유형 레지스트리 (Registry 패턴)
├── intake_tower_3d_builder.py # 취수탑 3D
├── intake_tower_parser.py # 취수탑 DXF 치수 파싱
├── retaining_wall_3d_builder.py # 옹벽 3D
├── retaining_wall_parser.py
├── gate_3d_builder.py # 수문 3D
├── gate_parser.py
├── valve_chamber_3d_builder.py # 제수변실 3D
├── valve_chamber_parser.py
├── detail_parser.py # 상세도면 DXF 치수 파서 (TEXT/MTEXT/DIMENSION)
├── filename_classifier.py # 파일명 → 구조물 유형 추정
├── polygon_reconstructor.py
├── dxf_geometry.py # DXF 엔티티 → 좌표
├── tile_downloader.py # 위성 XYZ 타일 다운로드
├── view_detector.py / view_reconstructor.py # 도면 뷰 영역 인식
├── optional_detector.py
├── gemini_renderer.py # Gemini API/Vertex AI 렌더링 워커
├── structure_vlm_feedback.py # Gemini Vision 으로 구조물 결과 검증
├── egview_maker.py 의 후신: # (이전 명칭, 현재 scanvas_maker.py 로 통합/리네임)
│
└── harness/ # 품질·재현성·이력 서브시스템
├── seed_manager.py # DXF 해시 기반 결정론적 seed
├── quality_validator.py # OpenCV 기반 이미지 자동 QA
├── prompt_registry.py # 프롬프트 버전 관리 (YAML)
└── logger.py # SQLite ORM (JobRecord) + structlog
prompt_templates/prompt_v1.yaml # 렌더 프롬프트 (시간대/앙각/구조보존)
structure_types/structure_v1.yaml # 구조물 유형 정의
Design/ # 브랜딩 자산
├── logo_V2.png # 로고 (다크 bg → 런타임 스트립)
├── Logo.png # 이전 로고 (투명 bg)
├── SAMAN_CI.gif # Saman Corp 크레딧
├── logo_intro.mp4 # 인트로 스플래시 (8s, 24fps, 1280×720)
├── homepage_sample.png # UI 디자인 레퍼런스
└── page_sample.png
cache/dem/ # AWS Terrarium 타일 캐시
scanvas_jobs.db # SQLite 작업 이력 (Harness)
scanvas_harness.log # structlog 출력
scanvas_diagnostic.log # Step 1 진단 로그
3.2 데이터 흐름 (Pipeline)
[DXF 도면] → [레이어 분류] → [등고선/구조물 분리]
↓
[GeoRef 4점 매칭] → [투영 CRS 결정 (EPSG:5187 등)]
↓
[TIN 생성 (Delaunay)] → [TIN core 정밀 영역 지정 (선택)]
↓
[DEM 자동 페치 (AWS Terrarium)] → [도넛 링 메시] → [smoothstep blend]
↓
[seam-free 통합 메시 (merge_points + compute_normals)]
↓
[위성 타일 다운로드] → [UV 매핑 (texture_map_to_plane)]
↓
[3D 미리보기 (PyVista)] → [카메라 앵글 캡처] → [제어맵]
↓
[Gemini/Stability AI 렌더링 (구조 보존 모드)]
↓
[QualityValidator 자동 검증] → [scanvas_jobs.db 이력 기록]
↓
[고해상도 PNG 출력]
3.3 외부 의존성
Python 라이브러리 (주요):
customtkinter— 모던 Tk GUI 프레임워크tkintermapview— 위성지도 뷰ezdxf— DXF 파싱numpy,scipy— 수치 연산, Delaunaypyvista+vtk— 3D 메시 렌더링pyproj— 좌표계 변환Pillow (PIL)— 이미지 처리opencv-python (cv2)— 비디오 / 이미지 검증requests— HTTP 타일 페치google-genai— Gemini SDK (Vertex AI 또는 API Key)sqlalchemy+structlog— 이력 추적PyYAML— 프롬프트/구조물 정의
외부 데이터 소스:
- AWS Open Terrain Tiles (
s3.amazonaws.com/elevation-tiles-prod/terrarium) — 글로벌 DEM, ~30m 정확도, API 키 불필요 - Google Satellite / ArcGIS World Imagery / Bing Aerial / Vworld — 위성 타일 (사용자 선택)
- Google Vertex AI / Gemini API — 생성형 렌더링
- (옵션) Stability AI API — img2img 폴백
4. 핵심 워크플로 (Step-by-Step)
Step 0 — DXF 로드 & 레이어 분류
DXF 를 열면 자동으로 레이어를 분석해 구조물 유형(취수탑/제수변실/옹벽/수문/
지형 등고선/도로/하천 등)을 추정. filename_classifier.py 가 파일명 패턴(예:
"신설 취수탑.dxf") 으로 1차 후보를 제시하고, 사용자가 GUI 다이얼로그에서 최종
확정. 결과는 structure_v1.yaml 의 정의에 따라 빌더로 라우팅.
Step 1 — TIN 생성 (DXF)
지형 레이어의 등고선 점·LINE/LWPOLYLINE 정점을 추출 → Delaunay 삼각화 → zero-basing (원점을 도면 bbox 좌하단으로 평행이동, 부동소수점 정밀도 보존) → PyVista PolyData 메시 생성.
핵심 디테일:
- 단위 자동 감지 → KATEC 좌표(EPSG:5187 등) 는 항상 m 로 강제 (
unit_override="m") - DEM datum offset 사전 계산 → 이후 단계에서 공통 offset 재사용으로 seam Z 단차 0 보장
- DEM 격자도 사전 페치 →
_dem_elev_grid/_dem_grid_bounds로 캐시
Step 1.5 — DEM 으로 TIN 확장
DXF 범위 밖이 절벽처럼 잘리는 문제 해결. AWS Terrarium 에서 외곽 도넛 링을 받아 TIN 과 이어 붙임.
3-Zone 구조 (메모리화된 핵심 결정):
- Core (
tin_core_bbox안): 원본 TIN 100% 보존. 사용자가 정밀 영역으로 지정. - Transition (core ~ bbox,
blend_width_m): smoothstep3t² − 2t³으로 TIN ↔ DEM Z 부드럽게 블렌드. - DEM 외곽 링: AWS DEM 으로 채움.
Seam 처리 (다층 방어):
- 수직 datum 보정: TIN 경계 근처 점들의 (TIN Z – DEM Z) 중앙값을 offset 으로 차감
- 공유 정점 weld: TIN bbox 변 정점을 DEM 링 inner 경계로 강제 → 동일 XY 공유
- smoothstep feather: 경계 0 = TIN, feather_m = DEM 으로 C¹ 연속
- 벽 컷 (slope_ratio 기반):
slope_ratio = z_span / max_edge가 4.0 (≈76°) 초과 + Z스팬 30m + max_edge 5m 충족 시 "벽 삼각형" 으로 컷. 절대 Z 컷은 절대 사용하지 않음 (구멍이 뚫림 — 메모리화된 교훈) - 링 Laplacian 1pass: 톱니 fin 마지막 평활. 경계 정점은 pin
Step 2 — 위성지도 결합 (Draping)
선택한 타일 서버에서 확장된 bbox 전체의 위성 이미지 다운로드 → 단일 큰
이미지로 합성 → texture_map_to_plane 으로 UV 매핑 → 텍스처 입힌 3D 메시.
Seam-free 통합 렌더링 (2026-04-24 후속 수정):
- 이전:
tin_mesh와tin_extension_mesh가 두 개의 별도 PolyData → 경계에서 쉐이딩 불연속 (사각 선이 보이는 증상) - 수정:
merge(merge_points=True, tolerance=0.01)로 공유 정점 weld → 위상적 단일 표면.compute_normals(feature_angle=180)로 모든 edge smooth → seam 법선 평활. UV 좌표는_uv_mapping_params저장 후 merge 후 재적용.
Step 3 — 제어맵 추출
PyVista 카메라 앵글로 오프스크린 캡처:
capture_textured.png— 위성+DEM 합성된 control mapcapture_depth.png— 깊이 맵 (Eye-Dome Lighting 활성화)
이 2개 이미지가 Step 4 의 AI 렌더 입력.
Step 4 — AI 렌더링 (구조 보존 모드)
prompt_templates/prompt_v1.yaml 기반으로 프롬프트 구성:
- 시간대 프리셋: daytime / sunset / night / dawn / overcast
- 앙각 프리셋: top_down / high_angle / oblique / low_angle
- 구조 보존 지시: "maintain exact terrain shape, contours, and layout from the input image"
- 품질 향상: "8K ultra sharp detail, professional drone photography quality"
- 네거티브: "blurry, distorted, watermark, changed terrain layout, moved structures..."
렌더 엔진 3종:
- Gemini (Vertex AI) — google-genai SDK, GCP 인증
- Gemini (API Key) — google-genai SDK, AI Studio 키
- Stability AI (API) — 3단계 폴백:
- Conservative Upscale (원본 보존 최우선)
- Creative Upscale (조금 더 창의적)
- img2img 초저강도 (strength=0.2)
각 렌더 호출은 Harness 가 결정론적 seed + prompt 버전 + 품질 점수를 자동 기록 → 동일 입력에 동일 출력 보장.
5. 핵심 기술 컴포넌트 / 혁신 포인트
5.1 DEM 자동 통합 (dem_extender.py)
- AWS Open Terrain Tiles terrarium PNG 디코딩:
elev_m = (R*256 + G + B/256) - 32768 - 좌표계 변환: 투영 CRS(예: EPSG:5187) → WGS84 → 타일 좌표 (Web Mercator z/x/y)
- 캐시: SHA1(bbox+zoom) 으로 PNG 캐시 → 재실행 시 네트워크 0
- 로컬 GeoTIFF 우선:
cache/dem/local.tif가 있으면 NGII 5m DEM 등 고정밀 데이터 우선 사용 (rasterio 필요)
5.2 Seam-free 메시 통합 (Render-fix 2026-04-24)
원래 메모리: "벽 이슈는 create_tin_from_dxf 부터 점검. 판정은 slope_ratio 만, 절대 Z 컷은 구멍 낸다."
문제: TIN+DEM 을 두 개 별도 PolyData 로 add_mesh 하면 normal 평균이 메시 경계를 못 넘어 사각 쉐이딩 선이 보임.
해결 (1순위 + 2순위 동시):
merged = target.merge(ext_mesh, merge_points=True, tolerance=0.01)
if not isinstance(merged, pv.PolyData):
merged = merged.extract_surface()
# 텍스처 모드: UV 재적용 (TCoords 가 merge 에서 소실되므로)
if textured and uv_params:
merged = merged.texture_map_to_plane(origin=..., point_u=..., point_v=...,
inplace=False)
merged.compute_normals(feature_angle=180.0, auto_orient_normals=True,
consistent_normals=True, inplace=True)
merge_points=True→ 공유 경계 정점 물리적 weld (1순위 unified Delaunay 효과)feature_angle=180→ 모든 edge smooth, 경계 normal 평균 (2순위 normal averaging 효과)- 폴백: 실패 시 기존 2-mesh 렌더 경로 유지
5.3 구조물 자동 빌드 (structure_*_3d_builder.py)
| 유형 | 빌더 | 입력 |
|---|---|---|
| 취수탑 | intake_tower_3d_builder.py |
DXF 평면도 → 외곽 폴리곤 + 높이 |
| 옹벽 | retaining_wall_3d_builder.py |
DXF 단면도 → 단면 + 길이 |
| 수문 | gate_3d_builder.py |
DXF 정면도 → 게이트 형상 + 슬라브 |
| 제수변실 | valve_chamber_3d_builder.py |
DXF 평면도 → 박스 + 슬래브 + 점검구 |
구조물 위치인식 → 굴착 → TIN수정 → 3D배치 (메모리화된 워크플로):
- DXF 레이어에서 폴리곤 인식
- PCA 로 frame 방향 결정 (
compute_orientation_from_points) - TIN 에 굴착 pad 영역 평탄화 + smoothstep 전이 + Delaunay 재계산
- 메시 4개 quad 코너에 fit (
fit_meshes_to_quad) - 구조물 메시를
embed_offset으로 약간 묻어 TIN 관통 방지
메시 방향 보정 (메모리화된 디테일):
- CW(시계방향) picks + Y-flip + detail/TIN 상대회전 + PCA frame_angle → 앞뒤 뒤집힘 문제 해결
5.4 VLM 피드백 루프 (structure_vlm_feedback.py)
Gemini Vision 으로 빌더 결과물의 시각적 정합성을 자동 검증.
- Render 후 이미지 + 원본 DXF 평면도를 함께 모델에 보냄
- 차이점 자연어로 응답 받음
- 사용자에게 표시해 재시도 결정
5.5 Geo-Referencing 3단계 (geo_referencing.py)
메모리: "미리보기 → 위치설정(4점 매칭) → 확정"
- 미리보기: DXF bbox 를 위성지도에 임시 투영 (대략 위치)
- 위치설정: 사용자가 DXF 의 4 모서리를 실제 위성지도 위치와 매칭 클릭
- 확정: Affine transform 행렬 계산 → 투영 CRS(EPSG:5187 등) 자동 결정
5.6 Harness — 재현성 + 품질 + 이력
세 컴포넌트가 모든 AI 렌더 호출을 감싼다:
SeedManager: SHA256(dxf_file_hash)[:4] → uint32 seed. 같은 DXF → 같은 seed.
QualityValidator: 3개 게이트
- 해상도 ≥ 1024px
- Laplacian variance ≥ 50.0 (선명도)
- HSV saturation 평균 ≥ 0.15 (단색 평면 출력 탐지)
JobLogger + SQLite JobRecord ORM:
id · dxf_path · dxf_hash · timestamp
seed · prompt_version · prompt_hash ← 재현성 3종
status (pending/running/done/failed)
output_path · quality_score · latency_ms
error_message
PromptRegistry: prompt_v*.yaml 버전 관리. 비교/저장/해시 역조회 API.
6. GUI 디자인 & 브랜딩
6.1 디자인 철학 (homepage_sample 참고)
- Light 기본 테마 (사용자가 Dark 토글 가능)
- 카드형 시각 계층화: 헤더 → SETTINGS → WORKFLOW → OPTIONS → 크레딧 푸터
- 1px 구분선:
("#DEE2E6", "#3F3F3F")테마 쌍으로 자동 스위칭 - uppercase 섹션 헤더: 10pt bold muted gray
- 메인 액션 강조: Step 4 버튼만 Saman 오렌지
#E67E22, 나머지는 blue 테마 파생
6.2 사이드바 구조
[ S-CANVAS Logo (logo_V2.png, 다크 bg 소프트 스트립) ]
Generative Design & Visualization Engine
─────────────────────────────────
SETTINGS
Satellite Source / Vworld Key (프리필 완료) / AI Engine /
GCP/API Key / Vertex Location / Project CRS
─────────────────────────────────
WORKFLOW
1. TIN 생성 (DXF) [filled blue]
🎯 TIN 이용 범위 (정밀 구역) [outlined]
1.5 DEM으로 TIN 확장 [outlined]
2. 위성지도 결합 [outlined]
3. 제어맵 추출 [outlined]
4. AI 렌더링 [filled ORANGE — primary CTA]
구조물 상세 3D 빌드 [filled green]
간단 치수 추가 (구) [muted]
🗔 3D 뷰 다시 열기 [filled dark]
─────────────────────────────────
OPTIONS
□ 와이어프레임 보기
뷰 버퍼 (%) [Step2/3]
□ 지형 확장 (DEM)
[Light ▼]
─────────────────────────────────
[ Saman CI (흰배경 알파 변환) ]
6.3 메인 영역
- map_frame: tkintermapview, corner_radius=12, border_color 테마쌍
- textbox: Consolas 12pt 로그
- status_bar:
● READY인디케이터 + 한 줄 상태
6.4 인트로 스플래시 (splash.py)
- 트리거:
__main__에서SCanvasApp()생성 직전 - 재생: cv2 VideoCapture 로 logo_intro.mp4 (24fps, 8s, 1280×720) 프레임 디코드 → PIL → Tk Label
- 효과:
- 알파 0→1 페이드인 400ms
- MP4 자체 애니메이션 재생
- 알파 1→0 페이드아웃 400ms 후 destroy
- frameless overrideredirect, topmost, 화면 중앙 배치
- 비디오 아래 44px 오렌지 italic tagline bar
- 안정성:
- max_duration_s=12 safety cap
- 비디오 끝 자동 감지 → 페이드아웃
- 임시 tk.Tk → 완전 destroy → SCanvasApp() 의 새 ctk.CTk 충돌 없음
- 파일 없음/cv2 실패 → silent skip
6.5 자산 처리 헬퍼
_load_image_strip_white_bg(path, threshold=240) # 흰 배경 → 알파 0 (SAMAN_CI)
_load_image_strip_dark_bg(path, v_low=30, v_high=80) # 어두운 bg 소프트 전이 (logo_V2)
logo_V2 결과: 36.7% 완전투명 / 49.5% 부분알파 / 13.8% 불투명 → halo 없는 부드러운 엣지.
7. AI 렌더링 파이프라인 상세
7.1 프롬프트 구성 (prompt_v1.yaml)
time_presets:
daytime: "bright daylight, clear blue sky, sharp shadows, vivid green vegetation"
sunset: "golden hour sunset, warm orange light, long dramatic shadows"
night: "nighttime aerial view, moonlight reflections, city lights in distance"
dawn: "early dawn, soft pink and purple sky, morning mist over valleys"
overcast: "overcast sky, diffused soft light, muted colors, atmospheric fog"
angle_presets:
top_down: "top-down overhead aerial view, directly above"
high_angle: "high-angle bird's-eye view, slightly tilted"
oblique: "oblique aerial perspective, 3/4 view showing terrain depth"
low_angle: "low-angle dramatic perspective, cinematic sweep"
structure_preservation:
- "enhance the existing satellite terrain texture and details"
- "maintain exact terrain shape, contours, and layout from the input image"
- "preserve water bodies, roads, and structural positions precisely"
- "do NOT add or remove any major landscape features"
quality_enhancement:
- "photorealistic architectural visualization"
- "professional drone photography quality"
- "8K ultra sharp detail, high dynamic range"
- "realistic vegetation depth and canopy textures"
negative_prompt: |
blurry, low quality, distorted, watermark, text, logo,
cartoon, anime, illustration, painting, sketch,
oversaturated, underexposed, noisy, artifacts,
changed terrain layout, moved structures, wrong topology
7.2 Stability AI 3단계 폴백
1단계 — Conservative Upscale
- mode: "conservative"
- creativity: UI 슬라이더 값 그대로 (기본 0.3)
- 목표: 원본 위성 텍스처 최대 보존
2단계 — Creative Upscale
- mode: "creative"
- creativity: min(strength, 0.35)
- 목표: 약간 더 사실적인 디테일 추가
3단계 — img2img 초저강도
- strength: min(strength, 0.2)
- 모델: sd3.5-large
- 목표: 위 2개 실패 시 백업
각 단계 실패 → 다음 단계 자동 폴백. 3개 모두 실패 시 fail_job(error="모든 API 방법 실패").
7.3 Gemini 경로 (gemini_renderer.py)
- Vertex AI: google-genai SDK, GCP Project ID + gcp-key.json
- API Key: aistudio.google.com 발급 키
- 동일 인터페이스로 호출 → 인증만 다름
8. 좌표계 / 단위 / 정밀도
8.1 지원 CRS (사용자 선택)
| EPSG | 이름 | 사용 영역 |
|---|---|---|
| 5187 | Korea 2000 / Central Belt 2010 | 한국 중부(서울·경기·충청) |
| 5186 | Korea 2000 / Central Belt | 한국 중부 (구) |
| 5185 | Korea 2000 / West Belt 2010 | 한국 서부 |
| 5181 | Korea 2000 / Unified CS | 통합 좌표계 |
| 3857 | Web Mercator | 글로벌 (위성 타일 호환) |
8.2 단위 강제 (메모리화된 결정)
"extract_tin_shapes 는 unit_override='m' 고정 (자동감지가 KATEC 좌표를 mm 로 오판)"
DXF $INSUNITS 헤더가 부정확한 경우가 많아 항상 m 로 강제. KATEC 류
대형 좌표값(예: x=200000, y=550000) 을 mm 로 오판하면 단위가 1000배 어긋남.
8.3 Zero-Basing
큰 절대좌표(예: EPSG:5187 의 200,000m+)는 float32 정밀도에서 sub-meter 오차
발생 → bbox 좌하단을 origin 으로 평행이동 → 모든 메시는 zero-based.
구조물 빌더는 절대좌표(structure_v1.yaml 의 origin) 와 zero-based 사이를
명시적으로 변환.
9. 메모리화된 핵심 결정 사항 (요약)
이 결정들은 과거 디버깅에서 얻은 교훈으로, 코드 곳곳의 분기 판단 기준이 됨.
- Structure Placement Workflow: 위치인식 → 굴착 → TIN수정 → 3D배치 4단계
- Geo-Referencing: 미리보기 → 4점 매칭 → 확정 3단계
- TIN Shape Unit: extract_tin_shapes 는 항상
unit_override="m"(자동감지 금지) - Excavation: 폴리곤 평탄 pad + smoothstep 전이 + Delaunay 재계산.
구조물은
embed_offset으로 TIN 관통 방지 - Mesh Orientation: CW picks + Y-flip + 상대회전 + PCA frame_angle 로 앞뒤 뒤집힘 해결
- TIN/DEM 벽 근본 접근: 벽 이슈는
create_tin_from_dxf부터 점검. 판정은slope_ratio (z_span / max_edge)만 사용. 절대 Z 컷은 구멍 낸다 - TIN 3-Zone: core(원본 TIN) / transition(smoothstep) / DEM 확장.
tin_core_bbox·blend_width_m로 제어 - CHANGELOG 의무: 모든 수정은
CHANGELOG.md에 즉시 기록 (역순, 날짜/파일/사유/diff 요지)
10. 최근 주요 수정 이력 (2026-04-24 ~)
[render-fix] DEM 확장 경계 사각 선 제거 (후속)
- 두 개의 별도 PolyData → seam shading discontinuity 발생
merge(merge_points=True, tolerance=0.01)+compute_normals(feature_angle=180)로 1순위(구조 통합) + 2순위(법선 평활) 동시 달성- 텍스처 모드에서 TCoords 소실 →
_uv_mapping_params저장 후 merge 후 재적용
[rebrand] EG-VIEW → S-CANVAS 전면 리네이밍
- 클래스
EGViewApp → SCanvasApp - 파일
egview_maker.py → scanvas_maker.py - DB/로그
egview_*.db/log → scanvas_*.db/log - 144개 문자열 occurrence (43 파일) 일괄 교체
[ui-redesign] Light 기본 테마 + 사이드바 카드형
set_appearance_mode("light")- 모든 하드코딩 색상을
(light, dark)튜플 쌍으로 → 자동 테마 스위칭 - SETTINGS / WORKFLOW / OPTIONS 섹션 헤더 + 1px 구분선
- SAMAN_CI 흰 배경 68.5% 알파 변환
[feature] Vworld 키 프리필 + logo_V2 + 인트로 스플래시
- Vworld API 키 하드코딩 (사용자 제공)
- 로고
Logo.png → logo_V2.png(다크 bg 소프트 스트립) - 신규
splash.py— cv2 기반 8초 MP4 스플래시 + 페이드 인/아웃 __main__에서show_intro_splash()→SCanvasApp()순차 실행
11. 차별화 포인트 (발표 슬라이드용 강조)
11.1 사실 기반 vs 환각 기반
경쟁 도구가 AI 의 상상으로 지형을 만들 때, S-CANVAS 는 실측 DEM + 실제 위성영상 을 베이스로 깔고 AI 는 사실감 향상만 담당. → 발주처에 실제와 다른 지형을 제출하는 사고 원천 차단.
11.2 Seam-free 통합 메시
TIN(설계 도면) + DEM(실제 지형) + 위성텍스처 + AI 렌더가 모두 하나의 연속 표면으로 합쳐짐. 사용자가 어디까지가 도면이고 어디부터가 실제 지형인지 구분 못 하도록.
11.3 결정론적 재현성 (Harness)
DXF 해시 → seed → AI 렌더. 같은 입력에 항상 같은 출력. 발주처에 제출했던 그림과 1주일 뒤 회의에서 띄울 그림이 픽셀 단위로 동일.
11.4 모듈러 구조물 빌더
취수탑·제수변실·옹벽·수문 4종 즉시 지원. structure_v1.yaml 에 정의 추가하면
새 유형 확장 가능. DXF 단면도 → 자동 치수 파싱 → 3D 배치까지 클릭 1번.
11.5 공개 데이터 + 무료 티어
AWS Open Terrain Tiles · Google Satellite · ArcGIS World Imagery 모두 무료. 유일한 유료 항목은 AI 렌더(Gemini/Stability) — 그것도 사용자 본인 키 사용.
11.6 자동 품질 게이트
모든 렌더 결과를 OpenCV 로 즉시 검증. 흐릿하거나 단색 출력은 자동 PASS/FAIL 표시 → 사용자가 100장을 일일이 확인할 필요 없음.
11.7 Saman Corp 자체 개발
국내 토목·건설 도메인 지식이 코드 곳곳에 — KATEC 단위 처리, 한국 EPSG 코드 프리셋, Vworld 타일 지원, 한글 UI·로그 등.
12. 기술 스택 요약 (한눈에)
[Frontend / GUI]
└─ CustomTkinter (Tk 기반 모던 위젯) + tkintermapview
[3D Engine]
└─ PyVista + VTK (메시 처리·렌더링)
[Geospatial]
├─ ezdxf (DXF 파싱)
├─ pyproj (좌표 변환)
├─ scipy.spatial.Delaunay (TIN)
└─ AWS Open Terrain Tiles (DEM)
[Image / Video]
├─ Pillow (PIL)
├─ OpenCV (cv2)
└─ tkintermapview (위성 타일)
[AI Rendering]
├─ Google google-genai (Vertex AI / API Key)
└─ Stability AI REST API
[Persistence]
├─ SQLAlchemy + SQLite (JobRecord)
├─ structlog (구조화 로깅)
└─ PyYAML (프롬프트/구조물 정의)
[Build / Distribution] (계획)
└─ PyInstaller (단일 .exe 또는 onedir 배포)
13. 배포 / 패키징 계획
PyInstaller 로 Windows .exe 빌드 예정. 주요 챌린지:
- PyVista + VTK 200MB+ 바이너리 →
--collect-all pyvista vtkmodules필요 - pyproj PROJ data → 명시적 hook
- google-genai → hidden imports
- 런타임 쓰기 경로(DB·로그·캐시) →
%LOCALAPPDATA%\S-CANVAS\분리 (onedir/onefile 양쪽) - 자산 경로 (
Design/,prompt_templates/,structure_types/) →sys._MEIPASS핸들링
배포 형태 (예정): dist/scanvas_maker/ 폴더 통째로 zip → 약 150-200MB 압축. 사용자는
.exe 더블클릭으로 실행, 본인 GCP 키 또는 Stability API 키만 환경변수/UI 입력.
14. 향후 로드맵
- 다국어 (i18n): 한국어/영어 토글
- 추가 구조물 유형: 배수문·교량·터널·송수관 등
- Linux/macOS 지원: 현재 Windows 전용
- 클라우드 렌더 큐: 다수 DXF 일괄 처리 후 결과 한꺼번에 ZIP
- VR/AR 출력: glTF 익스포트 → Unity/Unreal 연동
- 변경 추적: 같은 DXF 의 V1/V2 자동 비교 → 변경 영역 하이라이트
15. 핵심 메시지 (발표 마무리용)
S-CANVAS 는 토목·건설 도면을, "수일짜리 모델링 작업" 에서 "클릭 4번의 자동 파이프라인" 으로 압축한다. 사실 기반(DEM·위성) 위에서 AI 가 사실감만 더하므로 환각 위험 없이, 결정론적 재현성을 보장하면서, 발주처·주민설명회 발표용 조감도를 분 단위로 양산할 수 있다. — Saman Corp.
부록 A. 진입점 / 실행 방법
python scanvas_maker.py
진행 순서:
splash.py가 logo_intro.mp4 8초 재생 (페이드 인/아웃)SCanvasApp(ctk.CTk)메인 창 기동- (사이드바) DXF 파일 선택 → CRS 확인 → Step 1~4 순차 실행
- (옵션) 구조물 상세 3D 빌드 / 와이어프레임 / DEM 확장
- AI 렌더 결과는
rendered_birdseye.png(와 SQLite 이력)
부록 B. 디렉토리 구조
D:\2026\00_EGVIEW2\
├── scanvas_maker.py # 메인 진입점
├── splash.py # 인트로 스플래시
├── dem_extender.py
├── geo_referencing.py
├── structure_placement.py
├── structure_templates.py
├── intake_tower_parser.py + _3d_builder.py
├── retaining_wall_parser.py + _3d_builder.py
├── gate_parser.py + _3d_builder.py
├── valve_chamber_parser.py + _3d_builder.py
├── detail_parser.py
├── filename_classifier.py
├── polygon_reconstructor.py
├── dxf_geometry.py
├── tile_downloader.py
├── view_detector.py
├── view_reconstructor.py
├── optional_detector.py
├── gemini_renderer.py
├── structure_vlm_feedback.py
│
├── harness/
│ ├── __init__.py
│ ├── seed_manager.py
│ ├── quality_validator.py
│ ├── prompt_registry.py
│ └── logger.py
│
├── prompt_templates/
│ └── prompt_v1.yaml
│
├── structure_types/
│ └── structure_v1.yaml
│
├── Design/
│ ├── Logo.png
│ ├── logo_V2.png (현재 사용)
│ ├── SAMAN_CI.gif
│ ├── logo_intro.mp4 (8s, 24fps, 1280×720)
│ ├── homepage_sample.png
│ └── page_sample.png
│
├── cache/dem/ (런타임 DEM 캐시)
├── scanvas_jobs.db (SQLite 이력)
├── scanvas_harness.log (structlog)
├── scanvas_diagnostic.log (Step 1 진단)
├── CHANGELOG.md (수정 이력 역순)
└── Build_log.txt (사용자 원본 요청 로그)
부록 C. 핵심 메트릭 (발표 슬라이드 숫자용)
- 메인 모듈: 6300+ LOC (
scanvas_maker.py) - 지원 구조물 유형: 4종 (취수탑·제수변실·옹벽·수문) + 확장 가능
- 지원 좌표계: 5종 EPSG (한국 4 + Web Mercator 1)
- 지원 위성 타일 서버: 9종 (Google·ArcGIS·Bing·OSM·OpenTopo·Vworld 3종 등)
- 지원 AI 렌더 엔진: 3종 (Gemini Vertex·Gemini API·Stability)
- DEM 자동 페치: AWS Terrarium 글로벌, ~30m 정확도
- 인트로 스플래시: 8.0초 (24fps × 192 frames @ 1280×720)
- 메시 통합 weld 톨러런스: 0.01m (1cm)
- TIN 3-Zone 블렌드: smoothstep
3t² − 2t³ - 벽 컷 임계: slope_ratio > 4.0 (≈76°) AND z_span > 30m AND e_max > 5m
본 문서는 NotebookLM 의 단일 소스로 사용 가능하도록 자기완결형으로 작성되었습니다. 업로드 후 "Audio Overview", "마인드맵", "발표 슬라이드 초안" 자동 생성을 권장합니다.