Files
s-canvas/S-CANVAS_brief.md
HYUNJUNGLEE b9342f6726 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>
2026-05-08 10:29:08 +09:00

738 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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**이며, 등고선·구조물 윤곽만 담겨 있어 실제 지형
맥락(주변 산세, 강, 식생, 도로망)이 빠져 있다. 발주처 보고·주민 설명회에서는
**"실제 어떻게 보이는가"** 가 중요하지만, 기존 워크플로에서는:
1. CAD 도면을 3D 모델러(Civil 3D/Revit/Rhino) 로 다시 그리고
2. GIS 데이터를 별도로 받아 좌표 정합
3. 위성영상을 매핑하고
4. 렌더 엔진(Lumion/Twinmotion/V-Ray) 으로 조명·재질·하늘 설정
5. 카메라 앵글 잡고 이미지 출력
이 과정이 **수일에서 수주** 걸리며, 변경이 생길 때마다 대부분의 단계를 다시
수행해야 한다.
### 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` — 수치 연산, Delaunay
- `pyvista` + `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 구조** (메모리화된 핵심 결정):
1. **Core** (`tin_core_bbox` 안): 원본 TIN 100% 보존. 사용자가 정밀 영역으로 지정.
2. **Transition** (core ~ bbox, `blend_width_m`): smoothstep `3t² 2t³` 으로 TIN ↔ DEM Z 부드럽게 블렌드.
3. **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 map
- `capture_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종**:
1. **Gemini (Vertex AI)** — google-genai SDK, GCP 인증
2. **Gemini (API Key)** — google-genai SDK, AI Studio 키
3. **Stability AI (API)** — 3단계 폴백:
1. Conservative Upscale (원본 보존 최우선)
2. Creative Upscale (조금 더 창의적)
3. 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순위 동시)**:
```python
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배치** (메모리화된 워크플로):
1. DXF 레이어에서 폴리곤 인식
2. PCA 로 frame 방향 결정 (`compute_orientation_from_points`)
3. TIN 에 **굴착 pad** 영역 평탄화 + smoothstep 전이 + Delaunay 재계산
4. 메시 4개 quad 코너에 fit (`fit_meshes_to_quad`)
5. 구조물 메시를 `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점 매칭) → 확정"
1. **미리보기**: DXF bbox 를 위성지도에 임시 투영 (대략 위치)
2. **위치설정**: 사용자가 DXF 의 4 모서리를 실제 위성지도 위치와 매칭 클릭
3. **확정**: Affine transform 행렬 계산 → 투영 CRS(EPSG:5187 등) 자동 결정
### 5.6 Harness — 재현성 + 품질 + 이력
세 컴포넌트가 모든 AI 렌더 호출을 감싼다:
**SeedManager**: `SHA256(dxf_file_hash)[:4] → uint32 seed`. 같은 DXF → 같은 seed.
**QualityValidator**: 3개 게이트
1. 해상도 ≥ 1024px
2. Laplacian variance ≥ 50.0 (선명도)
3. 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 자산 처리 헬퍼
```python
_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`)
```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. 메모리화된 핵심 결정 사항 (요약)
> 이 결정들은 과거 디버깅에서 얻은 교훈으로, 코드 곳곳의 분기 판단 기준이 됨.
1. **Structure Placement Workflow**: 위치인식 → 굴착 → TIN수정 → 3D배치 4단계
2. **Geo-Referencing**: 미리보기 → 4점 매칭 → 확정 3단계
3. **TIN Shape Unit**: extract_tin_shapes 는 항상 `unit_override="m"` (자동감지 금지)
4. **Excavation**: 폴리곤 평탄 pad + smoothstep 전이 + Delaunay 재계산.
구조물은 `embed_offset` 으로 TIN 관통 방지
5. **Mesh Orientation**: CW picks + Y-flip + 상대회전 + PCA frame_angle 로
앞뒤 뒤집힘 해결
6. **TIN/DEM 벽 근본 접근**: 벽 이슈는 `create_tin_from_dxf` 부터 점검. 판정은
`slope_ratio (z_span / max_edge)` 만 사용. **절대 Z 컷은 구멍 낸다**
7. **TIN 3-Zone**: core(원본 TIN) / transition(smoothstep) / DEM 확장.
`tin_core_bbox` · `blend_width_m` 로 제어
8. **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. 향후 로드맵
1. **다국어 (i18n)**: 한국어/영어 토글
2. **추가 구조물 유형**: 배수문·교량·터널·송수관 등
3. **Linux/macOS 지원**: 현재 Windows 전용
4. **클라우드 렌더 큐**: 다수 DXF 일괄 처리 후 결과 한꺼번에 ZIP
5. **VR/AR 출력**: glTF 익스포트 → Unity/Unreal 연동
6. **변경 추적**: 같은 DXF 의 V1/V2 자동 비교 → 변경 영역 하이라이트
---
## 15. 핵심 메시지 (발표 마무리용)
> **S-CANVAS 는 토목·건설 도면을, "수일짜리 모델링 작업" 에서 "클릭 4번의 자동
> 파이프라인" 으로 압축한다. 사실 기반(DEM·위성) 위에서 AI 가 사실감만 더하므로
> 환각 위험 없이, 결정론적 재현성을 보장하면서, 발주처·주민설명회 발표용 조감도를
> 분 단위로 양산할 수 있다. — Saman Corp.**
---
## 부록 A. 진입점 / 실행 방법
```bash
python scanvas_maker.py
```
진행 순서:
1. `splash.py` 가 logo_intro.mp4 8초 재생 (페이드 인/아웃)
2. `SCanvasApp(ctk.CTk)` 메인 창 기동
3. (사이드바) DXF 파일 선택 → CRS 확인 → Step 1~4 순차 실행
4. (옵션) 구조물 상세 3D 빌드 / 와이어프레임 / DEM 확장
5. 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", "마인드맵", "발표 슬라이드 초안" 자동 생성을 권장합니다.*