371 lines
13 KiB
Markdown
371 lines
13 KiB
Markdown
# Model / Layout (Paper) 뷰어 이슈 — 원인 분석 및 처리 이력
|
||
|
||
| 항목 | 내용 |
|
||
|------|------|
|
||
| 작성 목적 | 샘플에서 재현·수정한 이슈를 **원본 `hmwebviewer`** 에 이식하기 위한 기술 메모 |
|
||
| 샘플 | `dwg-dxf-viewer-sample` |
|
||
| 원본 | `hmwebviewer` (`src/viewer2d/` …) |
|
||
| 주 재현 도면 | `C0060202-001-평면및종단면도(1)(대산방향STA.18+492.08-19+411.50).dwg` |
|
||
| 관련 도면 | `C0060203-*`, `BasicSample.dwg` (회귀) |
|
||
| 최종 정리 | 2026-07-29 |
|
||
| 상태 | **샘플 패치 완료** / 원본 뷰어 UI·Viewer2D 이식은 확인 후 |
|
||
|
||
### 제약
|
||
|
||
| 대상 | 정책 |
|
||
|------|------|
|
||
| **오픈소스 파서 본체 (acadrust 등 crates.io)** | **수정 금지** (unmodified 사용) |
|
||
| SORTENTSTABLE / 헤더 확장 export | 파서 수정 없이 **뷰어 규칙으로 대체** |
|
||
| 한글 인코딩 (CP949 mojibake) | 별도 문서 [`korean-dwg-text-encoding.md`](./korean-dwg-text-encoding.md) — **본 문서와 독립, 둘 다 필요** |
|
||
|
||
---
|
||
|
||
## 0. 이슈 한눈에 보기
|
||
|
||
| # | 증상 | 원인 | 처리 (샘플) | 수정 위치 |
|
||
|---|------|------|-------------|-----------|
|
||
| 1 | 글자·선이 한곳에 뒤섞여 겹침 | Model + Paper 동시 렌더 | 공간 필터 + Model/Layout 탭 | `cadSpaces.ts`, `Viewer2D.js`, `main.ts`, `index.html` |
|
||
| 2 | Layout에 도곽만 있고 평면/종단 비어 있음 | VIEWPORT 미투영 | Layout 시 Model을 VP 창에 투영·클립 | `Viewer2D.js` |
|
||
| 3 | Fit / 좌표가 CAD와 다름, (0,0)에 없음 | WCS 표시 + 이상치 bbox | **Model UCS 좌표** + 백분위 Fit | `Viewer2D.js` |
|
||
| 4 | 선형·종단에서 글자만 거대 | `minTextH = diag×0.0002` ≈ 250u | `min(diag×0.0002, medianHeight)` | `Viewer2D.js` |
|
||
| 5 | Layout에서 VP 내용이 도곽·주석 위 | VP를 paper 뒤에 append | VP 먼저 + `z=-1` + underlay 텍스트 | `Viewer2D.js` |
|
||
| 6 | 커서 좌표 표시 | — | 우하단 X/Y (Model=UCS) | `#coords`, `screenToWorld` |
|
||
|
||
---
|
||
|
||
## 1. DWG 공간 개념 (배경)
|
||
|
||
| 공간 | 블록 이름 예 | 역할 |
|
||
|------|----------------|------|
|
||
| Model Space | `*Model_Space` | 실좌표(측량 등) 본도면 |
|
||
| Paper Space / Layout | `*Paper_Space`, `*Paper_Space0`… | 출력 시트, 도곽, 주석, VIEWPORT |
|
||
|
||
- 엔티티 `ownerHandle` → 소속 블록 핸들.
|
||
- Layout은 보통 **VIEWPORT** 로 Model 일부를 시트 좌표에 투영한다.
|
||
- Model에는 **원거리 이중 좌표**가 흔함 (예: 원점 근처 종단 + X≈-436k 근처 선형).
|
||
|
||
### 재현 도면 규모 (C0060202-001, 참고)
|
||
|
||
| Space | handle (dec / hex) | 엔티티 수 (대략) | 좌표 |
|
||
|-------|--------------------|------------------|------|
|
||
| `*Model_Space` | 31 / `1f` | ~17,377 | 측량·다중 군집 |
|
||
| `*Paper_Space` | 210 / `d2` | ~200+ | 시트 (~1.7e3–2.6e3) |
|
||
| VIEWPORT | paper 소유 | 3 | overall 1 + 종단·평면 창 2 |
|
||
|
||
---
|
||
|
||
## 2. 이슈 #1 — Model + Paper 동시 렌더
|
||
|
||
### 2.1 현상
|
||
|
||
다색 TEXT가 중앙에 뭉치고, 기하·주석이 읽기 불가.
|
||
CAD에서는 Layout 활성 시 정상 시트.
|
||
|
||
### 2.2 원인
|
||
|
||
기존 `Viewer2D.load()`:
|
||
|
||
1. `*Model_Space` / `*Paper_Space` 를 space 로 인식
|
||
2. **사용자 블록 정의**만 메인 루프에서 스킵 (INSERT 전개용)
|
||
3. Model·Paper **둘 다** 메인 루프에서 그림
|
||
|
||
→ 좌표계·스케일이 다른 두 공간을 한 씬에 합치고 `fit()` → 파국.
|
||
|
||
### 2.3 처리
|
||
|
||
| 파일 | 내용 |
|
||
|------|------|
|
||
| `src/viewer2d/cadSpaces.ts` | `listCadSpaces`, `pickDefaultSpace`, `buildAllowedOwnerHexes` |
|
||
| `Viewer2D.js` | `allowedOwners` 필터, `setSpace` / `getSpaces` / `getActiveSpace` |
|
||
| `main.ts` + `index.html` | **Model / Layout** 탭, 로드 시 기본 공간 |
|
||
|
||
**기본 탭:** Paper에 엔티티 있으면 Layout, 없으면 Model.
|
||
|
||
```
|
||
C0060202-001 → Layout
|
||
BasicSample (paper 0) → Model
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 이슈 #2 — Layout이 비어 보임 (VIEWPORT)
|
||
|
||
### 3.1 현상
|
||
|
||
공간 분리 후 Layout은 도곽·주석만 있고, CAD처럼 평면·종단이 없음.
|
||
|
||
### 3.2 원인
|
||
|
||
- Paper 엔티티만으로는 시트 장식/주석 수준.
|
||
- 본도면은 Model에 있고, CAD는 **VIEWPORT** 로 비춤.
|
||
- 뷰어가 Layout에서 Model을 VP에 투영하지 않으면 창이 비어 보임.
|
||
|
||
### 3.3 VIEWPORT 데이터
|
||
|
||
acadrust 타입에는 `Viewport` 가 있음.
|
||
뷰어는 parseResult 의 `type: "VIEWPORT"` 필드를 사용한다.
|
||
|
||
재현 도면 VP 예:
|
||
|
||
| 역할 | size (대략) | twist | 비고 |
|
||
|------|-------------|-------|------|
|
||
| overall paper | 시트 전체 | 0 | 스킵 (모델 창 아님) |
|
||
| 종단 창 | ~840×302 | 0 | Model 투영 |
|
||
| 평면 창 | ~840×215 | ~1.28 rad | Model 투영 |
|
||
|
||
**overall 판별:** `id===1` 또는 (scale≈1 ∧ viewTarget≈0 ∧ viewCenter≈paper center).
|
||
|
||
### 3.4 처리 (`Viewer2D.js`)
|
||
|
||
Layout 활성 시:
|
||
|
||
1. (순서: 아래 이슈 #5) Model을 각 활성 VIEWPORT에 투영
|
||
2. 변환 (2D / top, ODA·acadrust writer 규약):
|
||
|
||
```
|
||
DCS = R(+twist) * (model − viewTarget)
|
||
paper = center + (paperHeight / viewHeight) * (DCS − viewCenter)
|
||
```
|
||
|
||
3. paper 사각형 Cohen–Sutherland 클리핑
|
||
4. `_isModelViewport` / `_viewportModelToPaper` / `_emitModelThroughViewport`
|
||
|
||
### 3.5 파서 정책
|
||
|
||
- **acadrust(오픈소스 본체)는 수정하지 않는다.**
|
||
- VIEWPORT JSON 방출이 래퍼/기존 빌드에 이미 있으면 뷰어만 사용.
|
||
- 추가 파서 기능(SORTENTS 등)은 **넣지 않음** → 뷰어 규칙으로 충분.
|
||
|
||
---
|
||
|
||
## 4. 이슈 #3 — Zoom Fit
|
||
|
||
### 4.1 확정: 가시 엔티티 AABB
|
||
|
||
| 항목 | 내용 |
|
||
|------|------|
|
||
| 범위 | 활성 Model/Layout + 숨긴 레이어 제외, 렌더 시 `expand` |
|
||
| 이상치 | 샘플 **0.5%–99.5%** 로 극단 점 제거 → `_contentBox` |
|
||
| `fit()` | **`_contentBox` 우선** (씬 재계산보다 로드 시점과 동일) |
|
||
|
||
### 4.2 가로로 매우 긴 Model (C0060202 / bb.dwg)
|
||
|
||
실측 기하(백분위 후) 대략:
|
||
|
||
| | WCS | CAD UCS |
|
||
|--|-----|---------|
|
||
| 폭 | ~438 000 | ~-437 000 … ~1 000 |
|
||
| 높이 | **~2 900** | ~-2 950 … 0 |
|
||
| 종횡비 | **~150 : 1** | |
|
||
|
||
화면 비율(~2:1)로 **classic contain** 하면:
|
||
|
||
- 세로 시야를 폭에 맞춰 키움 → 내용 높이가 화면의 **~1%**
|
||
- **수 픽셀 두께의 가로 선**처럼 보임 (하늘색 박스도 상단에 얇은 선)
|
||
|
||
`bb.dwg`는 같은 종횡비의 단순 박스 2개라 식별 가능했고, 원본 1.7만 엔티티는 한 줄로 뭉개짐.
|
||
|
||
### 4.3 처리 (극단 스트립 Fit)
|
||
|
||
```
|
||
contentAspect = bw / bh
|
||
if contentAspect > screenAspect × 8: // 매우 가로로 김
|
||
vh = bh × pad // Y를 화면에 채움
|
||
vw = vh × screenAspect // 좌우는 잘라 보고 패닝
|
||
else if contentAspect < screenAspect / 8:
|
||
// 매우 세로로 김 → 폭 채움
|
||
else:
|
||
// 일반 contain
|
||
```
|
||
|
||
→ 선형/종단 구간이 **세로로 화면에 채워짐**. 전체 연장은 좌우 팬.
|
||
|
||
CAD status 참고 extents(Y ±10만)와 파서 실측(Y 약 3천)이 다를 수 있음 — Fit은 **파서가 그린 가시 점** 기준.
|
||
|
||
---
|
||
|
||
## 5. 이슈 #4 — 글자만 거대 (minTextH)
|
||
|
||
### 5.1 현상
|
||
|
||
선형(~-436802, -1949) 및 종단 근처에서 `MOT`/`FLOW`/수치가 등고선보다 훨씬 큼.
|
||
|
||
### 5.2 원인
|
||
|
||
| 항목 | C0060202-001 Model |
|
||
|------|---------------------|
|
||
| CAD TEXT height | ~0.3 ~ 3.5 (중앙값 ~3, FLOW≈0.3) |
|
||
| 전체 bbox 대각선 | ~1.25×10⁶ |
|
||
| 구 `minTextH = diag × 0.0002` | **~250** |
|
||
|
||
`_drawTexts` → `Math.max(t.height, minTextH)` 로 **모든 라벨이 높이 ~250** 강제.
|
||
|
||
작은 시트 도면용 “fit 시 가독성” floor가 측량 Model에서 폭주한 것.
|
||
|
||
### 5.3 처리
|
||
|
||
```js
|
||
minTextH = Math.min(diagSize * 0.0002, medianNativeHeight);
|
||
```
|
||
|
||
- 측량 Model: floor ≤ median → **사실상 CAD 높이 유지**
|
||
- 작은 용지 좌표: diag floor가 작으면 기존과 유사
|
||
|
||
---
|
||
|
||
## 6. 이슈 #5 — Layout draw order (VIEWPORT가 위)
|
||
|
||
### 6.1 현상
|
||
|
||
VIEWPORT로 올린 Model 기하/라벨이 **도곽·NOTE 위**에 그려짐.
|
||
|
||
### 6.2 원인 (뷰어)
|
||
|
||
기존: paper 패스 후 VP 투영을 **버퍼 뒤**에 append → VP가 위.
|
||
|
||
### 6.3 DWG draw-order 메타 (참고만)
|
||
|
||
| 소스 | 의미 | 정책 |
|
||
|------|------|------|
|
||
| `SORTENTSTABLE` / `ACAD_SORTENTS` | sort handle 낮을수록 아래 | acadrust에 타입 있음 |
|
||
| Header `SORTENTS` | 정렬 플래그 | — |
|
||
| parseResult 엔티티 | zOrder 등 **없음** | — |
|
||
|
||
**오픈소스 파서를 고치지 않으므로 SORTENTS export는 하지 않는다.**
|
||
Layout 관례로 고정:
|
||
|
||
> **VIEWPORT 안 Model 내용 < Paper 주석·도곽**
|
||
|
||
### 6.4 처리
|
||
|
||
1. VP Model 투영을 **paper 엔티티 루프보다 먼저** 방출
|
||
2. VP 선분 `z = -1`, paper `z = 0` (카메라 +Z 기준 뒤/앞)
|
||
3. 텍스트: `underlay` (VP) / 일반 (paper) 분리, `renderOrder` 0 → 1
|
||
4. slug/스프라이트 배치도 underlay 먼저 추가
|
||
|
||
---
|
||
|
||
## 7. 커서 좌표 / Fit 좌표계 (UCS)
|
||
|
||
### 7.1 현상
|
||
|
||
- Model에서 우하단 좌표로 (0,0) 근처를 찾아도 기하가 없음
|
||
- Fit 후 표시 좌표가 CAD status bar 와 다름
|
||
|
||
### 7.2 원인
|
||
|
||
| 프레임 | 내용 |
|
||
|--------|------|
|
||
| 기하 저장 (파서 WCS) | 예: 선형 클러스터 ≈ (162065, 476725) |
|
||
| CAD Model UCS 원점 (`vars.ucsOrigin`) | ≈ (598948, 478643) |
|
||
| CAD에 보이는 좌표 | `UCS = WCS − ucsOrigin` → 선형 ≈ **(-436883, -1918)** |
|
||
|
||
뷰어가 **WCS를 그대로** 읽어 주면 CAD 사용자 좌표와 어긋난다.
|
||
또한 Fit bbox에 극단 이상치(수 점)가 포함되면 중심이 빈 공간으로 갈 수 있다.
|
||
|
||
CAD 참고 extents (Model, 사용자 측정):
|
||
|
||
```
|
||
X: -439601.13 … 4412.05
|
||
Y: -100418.91 … 99422.03
|
||
```
|
||
|
||
### 7.3 처리
|
||
|
||
1. **좌표 표시 (Model):** WCS → **model UCS**
|
||
`cad = project(WCS − ucsOrigin)` (축 단위 직교 시 subtract origin)
|
||
Layout/Paper: identity (시트 좌표)
|
||
2. **Pick/measure:** 내부 **WCS** (`_screenToWcs`)
|
||
3. **Fit:** 가시 기하 **0.5%–99.5% 백분위** AABB (이상치 제거)
|
||
4. 스크린→WCS: ortho frustum × camera right/up (view-twist 포함)
|
||
|
||
### 7.4 검증용 심플 도면 `bb.dwg`
|
||
|
||
Model 엔티티 5개 (사각형 2 + 연결 poly + hatch 2). `ucsOrigin ≈ (598948, 478643)`.
|
||
|
||
| 덩어리 | WCS (대략) | CAD UCS (대략) |
|
||
|--------|------------|----------------|
|
||
| 선형 쪽 박스 | (161969…162448, 475696…476795) | **(-436979…-436500, -2947…-1848)** |
|
||
| 원점 쪽 박스 | (598948…600030, 477765…478643) | **(0…1081, -878…0)** — **UCS (0,0)에 모서리** |
|
||
| Fit CAD 범위 | — | X ≈ -436979…1081, Y ≈ -2947…0 |
|
||
|
||
**확인 절차**
|
||
|
||
1. `bb.dwg` 열기 → **Model** 탭 (기본이 Layout이면 반드시 Model로)
|
||
2. Fit → 우하단 좌표가 대략 위 UCS 범위 안
|
||
3. 커서 오른쪽 박스 모서리 ≈ **X=0, Y=0**
|
||
4. 왼쪽 박스 ≈ **X=-436900, Y=-2400** 부근
|
||
|
||
CAD WCS (0,0) 에는 기하가 없음. CAD status의 (0,0)은 **UCS 원점**이다.
|
||
|
||
---
|
||
|
||
## 8. 검증 체크리스트
|
||
|
||
| 확인 | 기대 |
|
||
|------|------|
|
||
| C0060202-001 기본 탭 | Layout |
|
||
| Layout | 도곽 + 평면/종단 VP, 주석이 VP 위, 한글(인코딩 패치 병행) |
|
||
| Model | ~17k 엔티티, 공간 필터로 paper 제외 |
|
||
| Fit | **현재 가시** 전체 AABB (이중 좌표면 둘 다 포함) |
|
||
| Model 줌 인 선형/종단 | 글자 크기 ≈ CAD (거대 빌보드 없음) |
|
||
| BasicSample | Model 기본, 회귀 없음 |
|
||
| 우하단 Model | 커서 좌표 ≈ CAD UCS (선형 ~-436k 등) |
|
||
| Fit 후 중심 | 빈 (0,0) WCS 가 아닌 실제 기하 범위 |
|
||
|
||
---
|
||
|
||
## 9. 원본 `hmwebviewer` 이식 가이드
|
||
|
||
| 순서 | 작업 |
|
||
|------|------|
|
||
| 1 | `cadSpaces.ts` 신규 복사 |
|
||
| 2 | `Viewer2D.js` — 공간 필터, VP 투영, draw order, minTextH, fit/AABB, 좌표 API **diff 이식** (통째 덮어쓰기 비권장) |
|
||
| 3 | `Viewer2D.d.ts` 동기화 |
|
||
| 4 | 호스트 UI: Model/Layout 탭, `pickDefaultSpace` + `spaceHandle` |
|
||
| 5 | (선택) 우하단 좌표 표시 |
|
||
| 6 | `fixDwgKoreanText` 는 [한글 문서](./korean-dwg-text-encoding.md) 대로 별도 |
|
||
| 7 | C0060202-001 + BasicSample 회귀 |
|
||
|
||
**하지 말 것**
|
||
|
||
| 금지 | 이유 |
|
||
|------|------|
|
||
| acadrust 등 오픈소스 파서 수정 | 정책 |
|
||
| SORTENTS를 위해 파서 개조 | 뷰어 규칙으로 대체 |
|
||
| Model+Paper 동시 렌더 복귀 | 이슈 #1 재발 |
|
||
| 밀도 기반 Fit 재도입 | 이슈 #3 에서 철회 |
|
||
| `minTextH = diag×0.0002` 단독 | 이슈 #4 재발 |
|
||
|
||
---
|
||
|
||
## 10. 잔여 / 후속 (뷰어 측만)
|
||
|
||
1. VIEWPORT 안 솔리드 HATCH/SOLID — 윤곽 위주 근사
|
||
2. 다중 named Layout 탭 캡션 (블록명 `*Paper_Space*` 이상)
|
||
3. TILEMODE/CTAB 을 파서 없이 알 수 없으면 현재 휴리스틱 유지
|
||
4. 비직교 VIEWPORT (3D viewDirection)
|
||
5. (선택) Model 이중 군집 점프 UI
|
||
6. (선택) Fit “전체/선택” 분리는 요구 시만
|
||
|
||
---
|
||
|
||
## 11. 한 줄 요약
|
||
|
||
> **Model/Paper를 나눠 그리고, Layout은 VIEWPORT로 Model을 투영하되 paper 아래에 깐다.
|
||
> Fit은 가시 엔티티 bbox. 글자 높이는 CAD median을 넘기지 않는다.
|
||
> 오픈소스 파서는 수정하지 않으며, draw-order는 Layout 관례로 처리한다.**
|
||
|
||
---
|
||
|
||
## 12. 참고 파일 (샘플)
|
||
|
||
| 경로 | 역할 |
|
||
|------|------|
|
||
| [`../src/viewer2d/cadSpaces.ts`](../src/viewer2d/cadSpaces.ts) | 공간 목록·기본 탭·owner 클로저 |
|
||
| [`../src/viewer2d/Viewer2D.js`](../src/viewer2d/Viewer2D.js) | 필터, VP 투영, draw order, minTextH, fit, 좌표 |
|
||
| [`../src/viewer2d/Viewer2D.d.ts`](../src/viewer2d/Viewer2D.d.ts) | 타입 |
|
||
| [`../src/main.ts`](../src/main.ts) | 탭·좌표 UI 글루 |
|
||
| [`../index.html`](../index.html) | `#spaces`, `#coords` |
|
||
| [`korean-dwg-text-encoding.md`](./korean-dwg-text-encoding.md) | CP949 한글 깨짐 (병행 필수) |
|