13 KiB
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 — 본 문서와 독립, 둘 다 필요 |
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():
*Model_Space/*Paper_Space를 space 로 인식- 사용자 블록 정의만 메인 루프에서 스킵 (INSERT 전개용)
- 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 활성 시:
- (순서: 아래 이슈 #5) Model을 각 활성 VIEWPORT에 투영
- 변환 (2D / top, ODA·acadrust writer 규약):
DCS = R(+twist) * (model − viewTarget)
paper = center + (paperHeight / viewHeight) * (DCS − viewCenter)
- paper 사각형 Cohen–Sutherland 클리핑
_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 처리
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 처리
- VP Model 투영을 paper 엔티티 루프보다 먼저 방출
- VP 선분
z = -1, paperz = 0(카메라 +Z 기준 뒤/앞) - 텍스트:
underlay(VP) / 일반 (paper) 분리,renderOrder0 → 1 - 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 처리
- 좌표 표시 (Model): WCS → model UCS
cad = project(WCS − ucsOrigin)(축 단위 직교 시 subtract origin)
Layout/Paper: identity (시트 좌표) - Pick/measure: 내부 WCS (
_screenToWcs) - Fit: 가시 기하 0.5%–99.5% 백분위 AABB (이상치 제거)
- 스크린→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 |
확인 절차
bb.dwg열기 → Model 탭 (기본이 Layout이면 반드시 Model로)- Fit → 우하단 좌표가 대략 위 UCS 범위 안
- 커서 오른쪽 박스 모서리 ≈ X=0, Y=0
- 왼쪽 박스 ≈ 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 는 한글 문서 대로 별도 |
| 7 | C0060202-001 + BasicSample 회귀 |
하지 말 것
| 금지 | 이유 |
|---|---|
| acadrust 등 오픈소스 파서 수정 | 정책 |
| SORTENTS를 위해 파서 개조 | 뷰어 규칙으로 대체 |
| Model+Paper 동시 렌더 복귀 | 이슈 #1 재발 |
| 밀도 기반 Fit 재도입 | 이슈 #3 에서 철회 |
minTextH = diag×0.0002 단독 |
이슈 #4 재발 |
10. 잔여 / 후속 (뷰어 측만)
- VIEWPORT 안 솔리드 HATCH/SOLID — 윤곽 위주 근사
- 다중 named Layout 탭 캡션 (블록명
*Paper_Space*이상) - TILEMODE/CTAB 을 파서 없이 알 수 없으면 현재 휴리스틱 유지
- 비직교 VIEWPORT (3D viewDirection)
- (선택) Model 이중 군집 점프 UI
- (선택) Fit “전체/선택” 분리는 요구 시만
11. 한 줄 요약
Model/Paper를 나눠 그리고, Layout은 VIEWPORT로 Model을 투영하되 paper 아래에 깐다.
Fit은 가시 엔티티 bbox. 글자 높이는 CAD median을 넘기지 않는다.
오픈소스 파서는 수정하지 않으며, draw-order는 Layout 관례로 처리한다.
12. 참고 파일 (샘플)
| 경로 | 역할 |
|---|---|
../src/viewer2d/cadSpaces.ts |
공간 목록·기본 탭·owner 클로저 |
../src/viewer2d/Viewer2D.js |
필터, VP 투영, draw order, minTextH, fit, 좌표 |
../src/viewer2d/Viewer2D.d.ts |
타입 |
../src/main.ts |
탭·좌표 UI 글루 |
../index.html |
#spaces, #coords |
korean-dwg-text-encoding.md |
CP949 한글 깨짐 (병행 필수) |