Files
dwg-dxf-viewer-sample/docs/acadrust-0.6.5-upgrade.md
T
minsungandClaude Opus 5.5 e51114dbfd docs: record the acadrust 0.6.5 upgrade with a capture comparison report
- acadrust-0.6.5-upgrade.md: what changed in dwg-wasm and why (failsafe
  reads, 32 MiB wasm memory growth, API changes), the field-level parse
  diff with each cause, cost (wasm size, load time) and what is left.
- acadrust-0.6.5-upgrade-report.html: self-contained report with old/new
  captures of all 22 samples, zoomed checks of the formerly patched MESH
  and OLE2FRAME paths, and a diff image for each of the 5 changed views.
- license-mpl2-acadrust.md / README: the obligation is now license text
  plus source location; records why the 0.4.1 notice was wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 19:44:15 +09:00

116 lines
10 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.
# acadrust 0.4.1(패치) → 0.6.5(무수정) 업그레이드
| 항목 | 내용 |
|------|------|
| 날짜 | 2026-10-19 |
| 목적 | 로컬 패치를 없애 MPL-2.0 의무를 "고지 + 소스 링크"로 줄인다 ([`license-mpl2-acadrust.md`](./license-mpl2-acadrust.md)) |
| Rust 쪽 변경 | `hmwebviewer` 브랜치 `chore/acadrust-0.6.5` (`feat/subdmesh-render` 에서 분기) |
| 이 레포 변경 | `src/viewer2d/acadrust-dwg/` wasm 교체, 고지·문서 갱신 |
| 상태 | 수정 완료·검증됨 (샘플 20개 파싱 비교 + 브라우저 로드·스크린샷) |
| 캡처 보고서 | [`acadrust-0.6.5-upgrade-report.html`](./acadrust-0.6.5-upgrade-report.html) — 샘플 22개 기존/신규 화면 비교 (이미지 내장) |
---
## 1. 결론
- acadrust 를 고치지 않고도 기존 두 수정(MESH, OLE2FRAME)의 결과가 그대로 나온다.
- 화면 회귀는 없다. 패널을 숨기고 도면만 찍은 최종 캡처 기준으로, 샘플 22개 중 17개는 기본 화면이 픽셀 단위로 같다. 나머지 5개의 차이는 모두 데이터가 더 정확해진 결과다 (§4, [캡처 보고서](./acadrust-0.6.5-upgrade-report.html)). §4 의 스크린샷 표는 툴바가 함께 찍힌 첫 캡처 기준이라 탭 이름 차이가 섞여 있다.
- 대가: wasm 이 커졌고 (brotli 294 → 815 KB), 대형 도면 파싱이 10~16% 느려졌다 (§5).
## 2. dwg-wasm 변경 (우리 MIT 코드)
| 변경 | 이유 |
|---|---|
| `acadrust = "=0.6.5"` (crates.io, 정확한 버전 고정), vendored `rust/acadrust/` 삭제 | 무수정 사용. 고지에 적은 버전과 빌드 소스를 일치시킨다 |
| `match d.as_ref()` | 0.6 에서 `EntityType::Dimension` 이 `Box<Dimension>` 으로 바뀜 |
| `DIMENSION_ARC`, `DIMENSION_LARGE_RADIAL` 타입명 추가 | 0.6 이 새로 읽는 치수. 0.4 는 버렸고, Viewer2D 는 아직 이 이름을 그리지 않으므로 화면은 전과 같다 |
| OLE: `o.binary_data` → `o.encoded_payload()` | 0.6 은 OLE 페이로드를 envelope + compound file 로 분해해 둔다. 재조립한 바이트로 기존 DIB 탐색을 한다. 추출 이미지는 기존과 바이트 단위로 같다 |
| `DwgReadOptions::failsafe()` | 0.6 strict 모드는 레코드 하나가 깨져도 도면 전체를 거부한다. `a. 거더배치 삽도` 가 handle 0x2663(type 37)의 잘못된 배열 개수로 통째로 실패했다. failsafe 에서는 정상 로드되고, 건너뛴 레코드는 notifications 로 간다 |
| 전역 할당자: dlmalloc 을 32 MiB 단위로 확장 | 아래 §2.1 |
### 2.1 wasm 메모리 확장 비용
0.6.5 를 그대로 올리면 37 MB 노면 도면 파싱이 Node 에서 3.0초 → 7.4초로 느려졌다.
- **원인 확정** (CPU 프로파일): 시간의 55% 가 `<dlmalloc::sys::System as Allocator>::alloc`, 즉 wasm `memory.grow` 에 있었다.
- V8 에서 `memory.grow` 한 번의 비용은 늘리는 양이 아니라 **현재 메모리 크기**에 비례한다 (Node 24 실측: 16 MB 일 때 0.04 ms, 700 MB 일 때 1.6 ms).
- 0.6 은 짧게 쓰고 버리는 버퍼가 많아서, 기본 dlmalloc(64 KB 페이지 단위)이 수천 번 확장했다.
`dwg-wasm/src/lib.rs` 의 `big_step_alloc` 이 같은 dlmalloc 을 `set_granularity(32 MiB)` 로 감싸서 해결한다. acadrust 는 건드리지 않는다. 결과는 7.4초 → 3.3초. 대신 힙 끝에 최대 한 단계(32 MB)가 비어 있을 수 있다.
## 3. 검증 방법
1. **파싱 비교 (Node)**: 기존 wasm 과 새 wasm 으로 같은 DWG 19개를 파싱한다. `fixDwgKoreanText` 까지 적용한 뒤, 엔티티를 `타입#handle` 로 짝짓는다. 필드마다 정규화(숫자 12자리)한 해시로 비교했다.
2. **브라우저 (Chrome headless, 실 GPU)**: 같은 작업 트리에서 wasm 만 바꿔 20개(DWG 19 + DXF 1)를 로드한다. 상태바 `✓` 판정, 콘솔 에러, 1280×800 스크린샷 픽셀 비교를 했다.
3. **네이티브 프로브**: MESH/OLE2FRAME 값을 원본 0.4.1 · 패치본 · 0.6.5 로 비교했다 ([`license-mpl2-acadrust.md`](./license-mpl2-acadrust.md) §3).
## 4. 결과
**파싱**: 19/19 성공. 파일별 엔티티 수와 handle 집합이 완전히 같다 (354,502개 중 누락·추가 0). 필드 값이 바뀐 엔티티는 2,815개(0.79%)다.
| 바뀐 필드 | 건수 / 파일 | 원인 | 화면 영향 |
|---|---|---|---|
| `DIMENSION_*.pt10` | 1,168 / 9 | 0.4.1 이 정의점을 (0,0,0) 으로 냈다 → 실제 좌표 | 블록 없는 치수(fallback)의 선택 범위·extents 만 |
| `ATTDEF` alignmentPt / horiz·vertAlignment / widthFactor | 최대 664 / 13 | 0.4.1 이 읽지 못했다 → 실제 값 | 블록 안 ATTDEF 텍스트 위치·폭이 정확해진다 |
| `INSERT.blockName`, `tables.blocks` 이름 | 812 / 1 (ELEVATION) | 익명 블록 번호 체계가 바뀜 (`*U1` → `*U7`) | 없음. 렌더는 handle 기준이고, INSERT 이름과 블록 테이블 이름은 전 파일에서 일치(불일치 0) |
| `entityHeader.colorIndex` (트루컬러 엔티티) | 116 / 3 | 트루컬러 → 가장 가까운 ACI 근사가 개선됨. 원본 RGB 는 같다 | 원본 RGB 에 더 가까운 색 (Viewer2D 팔레트 기준 거리 130.8→117.6, 51.5→24.7) |
| `VIEWPORT.id` 0 → 1 | 27 / 10 | 0.6.5 가 레이아웃의 전체(paper) 뷰포트에 id 1 을 준다 | 7개 파일에서 전체 뷰포트를 모델 창으로 오판하던 것이 사라졌다 (아래) |
| `VIEWPORT.status.bits` | 26 / 2 | 비트 0x10000 추가 | 없음 (Viewer2D 는 `isOn` 만 사용) |
| `TEXT.text` | 1 / 1 | `500¡¿350` → `500×350` | CP949 기호 깨짐 1건 해소 |
| `OLE2FRAME.dataLength` | 1 / 1 | 페이로드 재조립으로 512 B 차이 | 없음 (이미지 바이트 동일) |
| `vars.celweight` | 전 파일 | 29 → -1 | 없음 (미사용) |
**VIEWPORT 판정**: id 가 1 로 바뀐 VP 는 모두 레이아웃마다 하나뿐인 전체 뷰포트였다 (배율 1.0000, target (0,0)). 진짜 모델 창(id ≥ 2)의 판정은 전 파일에서 그대로다.
기존에는 `Viewer2D._isModelViewport` 의 휴리스틱이 이 VP 를 모델 창으로 오판했다. 판정 조건 중 "viewCenter ≈ center" 가 맞지 않았기 때문이다. 그래서 모델 공간을 레이아웃 위에 한 번 더 투영했다. `a. 중앙분리대 삽도` 의 기본 화면을 가로지르던 검은 세로 띠가 이것이고, 업그레이드 후 사라졌으며 Fit 도 도면을 제대로 채운다.
**브라우저**: 기존·신규 모두 20/20 로드 성공. 기본 탭(Model/Layout) 동일, 콘솔 에러 동일 (첫 페이지 favicon 404 1건).
| 스크린샷 차이 | 파일 | 내용 |
|---|---|---|
| 0% (픽셀 동일) | 16개 | — |
| 16.9% | a. 중앙분리대 삽도 | 검은 띠 제거, Fit 개선, 해치 갈색이 원본 RGB 에 가까워짐 |
| 3.3% / 0.5% | a. 교량시종점 / a. 횡단경사 | 트루컬러 근사색 |
| 0.6% / 0.005% | a. 교량폭원 / ELEVATION | 툴바의 Layout 탭 이름 폭 |
Layout 탭 이름은 익명 paper 블록 번호를 따르므로 바뀌었다 (`Paper_Space1` → `Paper_Space194` 등). 원래 내부 이름이 보이던 문제(레이아웃 실제 이름 미사용)는 그대로다.
notification 수는 4개 파일에서 늘었다 (BasicSample 4→24, 거더배치 5→11 등). 0.6.5 가 진단을 더 많이 남기는 것이다.
### 4.1 추가 샘플 (`public/samples/` 신규 3개)
`dwg_18.3mb.dwg` 는 `dist2/samples` 의 같은 파일(md5 동일)이라, 실제 신규는 2개다:
`C0060203-002-굴착및시공순서도`, `dwg_1.60mb.dwg`.
- 3개 모두 기존·신규 파싱 성공이다. 0.6.5 strict 모드에서도 실패하지 않는다. 엔티티 수와 handle 집합이 같고, 브라우저 로드 성공, 콘솔 에러 0이다.
- **3D 폴리선 vertex 순서** (`dwg_1.60mb`, 옹벽·관로 7개, `원인 확정`).
- 두 버전 모두 vertex 13개는 같고 순서만 다르다.
- 0.4.1 은 vertex 를 handle 번호 순으로 정렬했다. 그래서 나중에 추가된 vertex(handle 227026, 가장 서쪽 점)가 중간에 끼어들어, 선이 최대 193 단위 되돌아갔다 오는 가짜 현(chord)이 생겼다.
- 0.6.5 는 DWG 연결 순서를 따른다 (x 가 단조 증가하는 매끈한 선). 확대 캡처에서 곡선을 가로지르던 청록 직선이 사라진 것을 확인했다.
- **ATTDEF 정렬**: 굴착및시공순서도의 동그라미 번호가 원의 아래쪽에서 중앙으로 옮겨졌다 (§4 의 ATTDEF 행과 같은 원인).
- **한글**: 0.6.5 는 CP949 를 원본에서 직접 디코딩한다.
- 기존 wasm 은 평면및종단면도(153개)와 굴착및시공순서도(160개)를 Latin-1 로 깨진 채 내보냈고, `fixDwgKoreanText` 가 복원해 왔다.
- 0.6.5 는 21개 DWG 전부 원본 깨짐 0이다. 보정 후 한글 텍스트 수는 전 파일에서 같다.
- `fixDwgKoreanText` 는 이 샘플들에서 할 일이 없어졌다. 다른 코드페이지 도면 대비용 안전망으로 남겨 둔다.
## 5. 비용
| 항목 | 0.4.1 패치본 | 0.6.5 |
|---|---|---|
| wasm raw / brotli | 864 KB / 294 KB | 2,976 KB / 815 KB |
| 노면 37 MB 파싱 (Node) | 3.0 s | 3.3~3.5 s |
| dwg_18.3mb 파싱 (Node) | 1.9 s | 2.1 s |
| 노면 브라우저 로드 | 5.4 s | 5.7 s |
| dwg_18.3mb 브라우저 로드 | 3.7 s | 4.0 s |
| wasm 메모리 (노면 / dwg_18.3mb) | 672 / 546 MB | 773 / 505 MB |
- 크기가 커진 원인: acadrust 0.6 이 DXF/DWG 쓰기, ACIS 등을 포함해 라이브러리 자체가 커졌다. 기본 feature 는 비어 있어 끌 수 있는 것이 없다.
- `opt-level = "z"` 를 시험했다: brotli 742 KB(−9%) 대신 파싱이 36% 느려져서(3.5 → 4.7 s) 채택하지 않았다.
## 6. 남은 것
- Viewer2D 가 `DIMENSION_ARC` / `DIMENSION_LARGE_RADIAL` 을 그리지 않는다. 블록 렌더 분기에 추가하면 된다. 샘플 20개에는 해당 엔티티가 없다.
- XDATA: acadrust 는 `common.extended_data` 로 읽지만 dwg-wasm 이 parseResult 에 내보내지 않는다 (업그레이드 전과 같다).
- wasm 에 컴파일되는 Rust 의존성의 라이선스 고지 생성 ([`license-mpl2-acadrust.md`](./license-mpl2-acadrust.md) §6).
- `dist2/` 재빌드와 pages.dev 재배포 전까지는 배포본이 옛 고지(0.4.1 패치)를 보여 준다.