Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f80442f6c | ||
|
|
a924b1ec34 | ||
|
|
cf1298749b | ||
|
|
ce3859c487 |
No files matched your search
@@ -112,6 +112,7 @@ Vite `?url` import 로 로드됩니다.
|
||||
| `dist2` 정적 빌드 배포 절차 (공식 배포 스크립트 없음) | [`docs/deploy-dist2-static-build.md`](./docs/deploy-dist2-static-build.md) |
|
||||
| SubDMesh(MESH) 미표시 — parseResult 누락 + 큰 메쉬 잘림 | [`docs/subdmesh-rendering.md`](./docs/subdmesh-rendering.md) |
|
||||
| 선가중치(Lineweight) 미적용 — parseResult 누락 + WebGL 1px 한계 | [`docs/lineweight-rendering.md`](./docs/lineweight-rendering.md) |
|
||||
| acadrust MPL-2.0 고지 누락 (수정본 wasm 공개 배포) | [`docs/license-mpl2-acadrust.md`](./docs/license-mpl2-acadrust.md) |
|
||||
| ↳ 위 둘의 측정 결과 + 상호작용(메쉬 선분이 굵기 버킷 상한을 넘길 위험) | [`docs/improvement-results-2026-08-04.md`](./docs/improvement-results-2026-08-04.md) |
|
||||
|
||||
샘플 패치: `fixDwgKoreanText`, `cadSpaces` + Viewer2D 공간 필터, Model/Layout 탭.
|
||||
@@ -124,6 +125,11 @@ Vite `?url` import 로 로드됩니다.
|
||||
| 조각 | 라이선스 |
|
||||
|------|----------|
|
||||
| Viewer2D 포트 · 샘플 글루 | MIT (hmwebviewer 계열) |
|
||||
| acadrust (WASM 내부, `read_mesh` 로컬 패치 포함) | MPL-2.0 |
|
||||
| dxf-parser · three | MIT |
|
||||
| acadrust (WASM 내부, `read_mesh` 로컬 패치 포함) | **MPL-2.0 — 수정본** |
|
||||
| dxf-parser · three · opentype.js | MIT |
|
||||
| NanumGothic | OFL |
|
||||
|
||||
acadrust 는 파일 단위 카피레프트라 **패치한 파일의 소스를 배포 대상자에게 제공할 의무**가
|
||||
있다. 고지·MPL 원문·패치는 [`THIRD-PARTY-NOTICES.md`](./THIRD-PARTY-NOTICES.md) 와
|
||||
`public/licenses/`(배포 시 `/licenses/`)에 동봉한다.
|
||||
의무 범위와 조치 근거: [`docs/license-mpl2-acadrust.md`](./docs/license-mpl2-acadrust.md)
|
||||
@@ -0,0 +1,70 @@
|
||||
# Third-party notices
|
||||
|
||||
This viewer bundles third-party code. The notices below apply to the source
|
||||
tree **and** to the built site (`dist2/`, deployed at
|
||||
`https://dwg-dxf-viewer-sample.pages.dev/`), because the build embeds the
|
||||
components listed here.
|
||||
|
||||
Served copies of the license texts live under
|
||||
[`public/licenses/`](./public/licenses/) → `/licenses/…` on the deployed site.
|
||||
|
||||
---
|
||||
|
||||
## acadrust 0.4.1 — Mozilla Public License 2.0 — **MODIFIED**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Component | `acadrust` (DWG/DXF parser, Rust) |
|
||||
| Version | 0.4.1 |
|
||||
| License | MPL-2.0 — full text: [`public/licenses/acadrust-MPL-2.0.txt`](./public/licenses/acadrust-MPL-2.0.txt) (`/licenses/acadrust-MPL-2.0.txt`) |
|
||||
| Upstream | https://github.com/hakanaktt/acadrust · https://crates.io/crates/acadrust/0.4.1 |
|
||||
| Upstream commit | `f249c2f816acf36ee51cd5533716bdd443c2517e` (from the crate's `.cargo_vcs_info.json`) |
|
||||
| Author | Hakan AK |
|
||||
| Shipped as | compiled into `assets/acadrust_dwg_bg-*.wasm` (Executable Form) |
|
||||
|
||||
**This copy is modified.** One file differs from the published crate:
|
||||
|
||||
```
|
||||
src/io/dwg/dwg_stream_readers/object_reader/entities.rs (read_mesh array-count bounds)
|
||||
```
|
||||
|
||||
The modification bounds `read_mesh`'s array counts by the bits remaining in the
|
||||
object stream instead of the blanket 100 000-item guard, which truncated and
|
||||
thereby corrupted large SubD meshes.
|
||||
|
||||
### How to obtain the Source Code Form (MPL-2.0 §3.2)
|
||||
|
||||
1. Get the unmodified crate source — `cargo vendor`, or
|
||||
`https://crates.io/api/v1/crates/acadrust/0.4.1/download`, or the upstream
|
||||
commit above.
|
||||
2. Apply [`public/licenses/acadrust-0.4.1-read_mesh.patch`](./public/licenses/acadrust-0.4.1-read_mesh.patch)
|
||||
(`/licenses/acadrust-0.4.1-read_mesh.patch` on the deployed site):
|
||||
|
||||
```bash
|
||||
tar xf acadrust-0.4.1.crate && cd acadrust-0.4.1
|
||||
patch -p1 < acadrust-0.4.1-read_mesh.patch
|
||||
```
|
||||
|
||||
The result is the exact Source Code Form of the acadrust code compiled into the
|
||||
shipped `.wasm`. Both the original and the modified file remain under MPL-2.0.
|
||||
|
||||
---
|
||||
|
||||
## dwg-wasm — MIT
|
||||
|
||||
The wasm-bindgen wrapper that turns acadrust output into this viewer's
|
||||
`parseResult` (`hmwebviewer/rust/dwg-wasm/`). Our own code, MIT. MPL-2.0 §3.3
|
||||
permits distributing this Larger Work under different terms as long as the
|
||||
MPL-covered files above keep their license — which they do.
|
||||
|
||||
---
|
||||
|
||||
## Other components
|
||||
|
||||
| Component | License |
|
||||
|---|---|
|
||||
| three.js | MIT |
|
||||
| dxf-parser | MIT |
|
||||
| opentype.js | MIT |
|
||||
| NanumGothic (`public/fonts/`) | SIL Open Font License 1.1 |
|
||||
| Viewer2D / sample glue | MIT |
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+160
-89
@@ -1,9 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>DWG/DXF · 모듈 조합 샘플</title>
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>DWG/DXF · 모듈 조합 샘플</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d1117; --panel: #161b22; --ink: #e6edf3; --muted: #8b949e;
|
||||
@@ -66,6 +66,25 @@
|
||||
button.primary { background: #238636; border-color: #2ea043; }
|
||||
button.wasm { border-color: var(--wasm); color: var(--wasm); }
|
||||
label.btn input { display: none; }
|
||||
/* 옵션 패널 — 값은 IndexedDB(hmw-viewer/options)에 저장된다 */
|
||||
#optBody { padding: 12px 14px; overflow: auto; font-size: 12px; color: var(--ink); }
|
||||
.opt-sec { margin-bottom: 16px; }
|
||||
.opt-sec > h4 {
|
||||
margin: 0 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .05em;
|
||||
text-transform: uppercase; color: var(--muted);
|
||||
}
|
||||
.opt-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
||||
.opt-row > label { flex: 1; color: var(--ink); }
|
||||
.opt-row input[type=range] { flex: 1; min-width: 90px; accent-color: var(--accent); }
|
||||
.opt-row input[type=number] {
|
||||
width: 58px; background: #0d1117; color: var(--ink);
|
||||
border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
|
||||
font: 12px var(--mono); text-align: right;
|
||||
}
|
||||
.opt-row input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--accent); }
|
||||
.opt-unit { font: 11px var(--mono); color: var(--muted); }
|
||||
.opt-note { font: 11px var(--mono); color: var(--accent); line-height: 1.6; word-break: keep-all; }
|
||||
.opt-help { font-size: 11px; color: var(--muted); line-height: 1.5; margin: 4px 0 0; }
|
||||
#status {
|
||||
margin-left: auto; font: 12px var(--mono); color: var(--muted);
|
||||
background: rgba(22,27,34,.85); border: 1px solid var(--line);
|
||||
@@ -169,6 +188,7 @@
|
||||
|
||||
#properties { right: 12px; }
|
||||
#layers { right: 344px; display: none; }
|
||||
#options { right: 676px; width: 340px; height: auto; max-height: 70vh; display: none; }
|
||||
|
||||
#propBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
#layerBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
@@ -197,86 +217,137 @@
|
||||
background: transparent; color: var(--muted); cursor: pointer; transition: all .15s;
|
||||
}
|
||||
.layer-sub-btn:hover { color: var(--ink); border-color: var(--accent); background: rgba(63,185,80,.1); }
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-D3FqDGwP.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="toolbar">
|
||||
<label class="btn">
|
||||
Open file
|
||||
<input id="file" type="file" accept=".dwg,.dxf" />
|
||||
</label>
|
||||
<button id="sample" class="primary wasm" type="button" title="acadrust-dwg WASM">Sample DWG</button>
|
||||
<button id="sampleCivil" class="primary wasm" type="button" title="Civil Plan & Profile DWG">평면및종단면도.dwg</button>
|
||||
<button id="sampleDxf" class="primary" type="button" title="dxf-parser">Sample DXF</button>
|
||||
<div id="spaces" title="Model Space / Paper Space (Layout)"></div>
|
||||
<button id="fit" type="button">Fit (F)</button>
|
||||
<button id="theme" type="button">Theme</button>
|
||||
<button id="lwt" type="button" title="선가중치 표시 (AutoCAD LWDISPLAY)">선가중치</button>
|
||||
<button id="layersBtn" type="button">Layers</button>
|
||||
<button id="propsBtn" type="button">Props (속성)</button>
|
||||
<div id="status">ready</div>
|
||||
</div>
|
||||
|
||||
<div id="coords" title="마우스 위치 CAD 좌표 (Model=UCS, Layout=Paper 도면단위)">
|
||||
<span class="lbl">X</span><span id="coordX">—</span>
|
||||
<span class="sep">|</span>
|
||||
<span class="lbl">Y</span><span id="coordY">—</span>
|
||||
</div>
|
||||
|
||||
<!-- 레이어 (Layers) 관리 패널 -->
|
||||
<div id="layers" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3>
|
||||
<span>🎨</span> 레이어 (Layers)
|
||||
<span id="layerCount" style="font-family:var(--mono);font-size:11px;color:var(--muted);background:rgba(255,255,255,.08);padding:1px 6px;border-radius:4px;font-weight:400"></span>
|
||||
</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="collapseLayersBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closeLayersBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="layerBody">
|
||||
<div class="layer-search-wrap">
|
||||
<input id="layerSearch" class="layer-search-input" type="text" placeholder="🔍 레이어 검색..." />
|
||||
</div>
|
||||
<div class="layer-sub-actions">
|
||||
<button id="layerShowAll" class="layer-sub-btn" type="button">전체 켜기</button>
|
||||
<button id="layerHideAll" class="layer-sub-btn" type="button">전체 끄기</button>
|
||||
</div>
|
||||
<div id="layerList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 속성 (Properties) 패널 -->
|
||||
<div id="properties" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3><span>📋</span> 속성 (Properties)</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="zoomSelBtn" type="button" title="선택 요소 위치로 확대">🔍 선택 확대</button>
|
||||
<button id="collapsePropsBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closePropsBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="propBody"></div>
|
||||
</div>
|
||||
|
||||
<aside id="recipe">
|
||||
<h3>Module recipe</h3>
|
||||
<ol>
|
||||
<li><code class="mod-j">ext2d</code> — .dwg / .dxf 분기</li>
|
||||
<li><code class="mod-w">acadrust-dwg</code> 또는 <code class="mod-j">dxf-parser</code></li>
|
||||
<li><code class="mod-j">dxfAdapter</code> — DXF → 공통 스키마</li>
|
||||
<li><code class="mod-j">Viewer2D</code> + <code class="mod-j">three</code> 렌더</li>
|
||||
<li><code class="mod-j">slugText</code> + TTF 폰트</li>
|
||||
</ol>
|
||||
<p style="margin:10px 0 0">
|
||||
코드: <code>src/main.ts</code> ·
|
||||
지도: <a href="/docs/modules-dwg-dxf.html" target="_blank">docs/modules-dwg-dxf.html</a>
|
||||
</p>
|
||||
</aside>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-VgpJrp_J.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="toolbar">
|
||||
<label class="btn">
|
||||
Open file
|
||||
<input id="file" type="file" accept=".dwg,.dxf" />
|
||||
</label>
|
||||
<button id="sample" class="primary wasm" type="button" title="acadrust-dwg WASM">Sample DWG</button>
|
||||
<button id="sampleCivil" class="primary wasm" type="button" title="Civil Plan & Profile DWG">평면및종단면도.dwg</button>
|
||||
<button id="sampleDxf" class="primary" type="button" title="dxf-parser">Sample DXF</button>
|
||||
<div id="spaces" title="Model Space / Paper Space (Layout)"></div>
|
||||
<button id="fit" type="button">Fit (F)</button>
|
||||
<button id="theme" type="button">Theme</button>
|
||||
<button id="optionsBtn" type="button" title="표시 옵션 (선가중치 · 그리드 · 줌)">옵션</button>
|
||||
<button id="layersBtn" type="button">Layers</button>
|
||||
<button id="propsBtn" type="button">Props (속성)</button>
|
||||
<div id="status">ready</div>
|
||||
</div>
|
||||
|
||||
<div id="coords" title="마우스 위치 CAD 좌표 (Model=UCS, Layout=Paper 도면단위)">
|
||||
<span class="lbl">X</span><span id="coordX">—</span>
|
||||
<span class="sep">|</span>
|
||||
<span class="lbl">Y</span><span id="coordY">—</span>
|
||||
</div>
|
||||
|
||||
<!-- 레이어 (Layers) 관리 패널 -->
|
||||
<div id="layers" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3>
|
||||
<span>🎨</span> 레이어 (Layers)
|
||||
<span id="layerCount" style="font-family:var(--mono);font-size:11px;color:var(--muted);background:rgba(255,255,255,.08);padding:1px 6px;border-radius:4px;font-weight:400"></span>
|
||||
</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="collapseLayersBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closeLayersBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="layerBody">
|
||||
<div class="layer-search-wrap">
|
||||
<input id="layerSearch" class="layer-search-input" type="text" placeholder="🔍 레이어 검색..." />
|
||||
</div>
|
||||
<div class="layer-sub-actions">
|
||||
<button id="layerShowAll" class="layer-sub-btn" type="button">전체 켜기</button>
|
||||
<button id="layerHideAll" class="layer-sub-btn" type="button">전체 끄기</button>
|
||||
</div>
|
||||
<div id="layerList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 옵션 (Options) 패널 — 설정은 IndexedDB 에 저장 -->
|
||||
<div id="options" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3><span>⚙️</span> 옵션 (Options)</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="optReset" type="button" title="기본값으로">↺ 기본값</button>
|
||||
<button id="collapseOptBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closeOptBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="optBody">
|
||||
<div class="opt-sec">
|
||||
<h4>선가중치 (Lineweight)</h4>
|
||||
<div class="opt-row">
|
||||
<label for="optLwOn">표시 (LWDISPLAY)</label>
|
||||
<input id="optLwOn" type="checkbox" />
|
||||
</div>
|
||||
<div class="opt-row">
|
||||
<label for="optLwScale">표시 배율</label>
|
||||
<input id="optLwScale" type="range" min="2" max="60" step="0.5" value="11" />
|
||||
<input id="optLwScaleNum" type="number" min="1" max="80" step="0.5" value="11" />
|
||||
<span class="opt-unit">px/mm</span>
|
||||
</div>
|
||||
<div id="optLwOut" class="opt-note">—</div>
|
||||
<p class="opt-help">
|
||||
AutoCAD "선가중치 설정 → 표시 배율"과 같다. 모델 공간에서 선가중치는 줌과
|
||||
무관한 화면 픽셀로 그려지므로, 화면 DPI에 맞춰 이 값을 조정한다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="opt-sec">
|
||||
<h4>화면 (Display)</h4>
|
||||
<div class="opt-row">
|
||||
<label for="optGrid">그리드 표시</label>
|
||||
<input id="optGrid" type="checkbox" />
|
||||
</div>
|
||||
<div class="opt-row">
|
||||
<label for="optZoomSpeed">줌 속도</label>
|
||||
<input id="optZoomSpeed" type="range" min="0.2" max="3" step="0.1" value="1" />
|
||||
<input id="optZoomSpeedNum" type="number" min="0.1" max="5" step="0.1" value="1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 속성 (Properties) 패널 -->
|
||||
<div id="properties" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3><span>📋</span> 속성 (Properties)</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="zoomSelBtn" type="button" title="선택 요소 위치로 확대">🔍 선택 확대</button>
|
||||
<button id="collapsePropsBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closePropsBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="propBody"></div>
|
||||
</div>
|
||||
|
||||
<aside id="recipe">
|
||||
<h3>Module recipe</h3>
|
||||
<ol>
|
||||
<li><code class="mod-j">ext2d</code> — .dwg / .dxf 분기</li>
|
||||
<li><code class="mod-w">acadrust-dwg</code> 또는 <code class="mod-j">dxf-parser</code></li>
|
||||
<li><code class="mod-j">dxfAdapter</code> — DXF → 공통 스키마</li>
|
||||
<li><code class="mod-j">Viewer2D</code> + <code class="mod-j">three</code> 렌더</li>
|
||||
<li><code class="mod-j">slugText</code> + TTF 폰트</li>
|
||||
</ol>
|
||||
<p style="margin:10px 0 0">
|
||||
코드: <code>src/main.ts</code> ·
|
||||
지도: <a href="/docs/modules-dwg-dxf.html" target="_blank">docs/modules-dwg-dxf.html</a>
|
||||
</p>
|
||||
<!-- MPL-2.0 §3.2(b): recipients of the wasm must be told where the
|
||||
acadrust source (with our local patch) can be obtained. -->
|
||||
<p style="margin:6px 0 0">
|
||||
오픈소스 고지: <a href="/licenses/THIRD-PARTY-NOTICES.md" target="_blank">THIRD-PARTY-NOTICES</a>
|
||||
(acadrust · MPL-2.0 · <a href="/licenses/acadrust-0.4.1-read_mesh.patch" target="_blank">local patch</a>)
|
||||
</p>
|
||||
</aside>
|
||||
␍
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
# Third-party notices
|
||||
|
||||
This viewer bundles third-party code. The notices below apply to the source
|
||||
tree **and** to the built site (`dist2/`, deployed at
|
||||
`https://dwg-dxf-viewer-sample.pages.dev/`), because the build embeds the
|
||||
components listed here.
|
||||
|
||||
Served copies of the license texts live under
|
||||
[`public/licenses/`](./public/licenses/) → `/licenses/…` on the deployed site.
|
||||
|
||||
---
|
||||
|
||||
## acadrust 0.4.1 — Mozilla Public License 2.0 — **MODIFIED**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Component | `acadrust` (DWG/DXF parser, Rust) |
|
||||
| Version | 0.4.1 |
|
||||
| License | MPL-2.0 — full text: [`public/licenses/acadrust-MPL-2.0.txt`](./public/licenses/acadrust-MPL-2.0.txt) (`/licenses/acadrust-MPL-2.0.txt`) |
|
||||
| Upstream | https://github.com/hakanaktt/acadrust · https://crates.io/crates/acadrust/0.4.1 |
|
||||
| Upstream commit | `f249c2f816acf36ee51cd5533716bdd443c2517e` (from the crate's `.cargo_vcs_info.json`) |
|
||||
| Author | Hakan AK |
|
||||
| Shipped as | compiled into `assets/acadrust_dwg_bg-*.wasm` (Executable Form) |
|
||||
|
||||
**This copy is modified.** One file differs from the published crate:
|
||||
|
||||
```
|
||||
src/io/dwg/dwg_stream_readers/object_reader/entities.rs (read_mesh array-count bounds)
|
||||
```
|
||||
|
||||
The modification bounds `read_mesh`'s array counts by the bits remaining in the
|
||||
object stream instead of the blanket 100 000-item guard, which truncated and
|
||||
thereby corrupted large SubD meshes.
|
||||
|
||||
### How to obtain the Source Code Form (MPL-2.0 §3.2)
|
||||
|
||||
1. Get the unmodified crate source — `cargo vendor`, or
|
||||
`https://crates.io/api/v1/crates/acadrust/0.4.1/download`, or the upstream
|
||||
commit above.
|
||||
2. Apply [`public/licenses/acadrust-0.4.1-read_mesh.patch`](./public/licenses/acadrust-0.4.1-read_mesh.patch)
|
||||
(`/licenses/acadrust-0.4.1-read_mesh.patch` on the deployed site):
|
||||
|
||||
```bash
|
||||
tar xf acadrust-0.4.1.crate && cd acadrust-0.4.1
|
||||
patch -p1 < acadrust-0.4.1-read_mesh.patch
|
||||
```
|
||||
|
||||
The result is the exact Source Code Form of the acadrust code compiled into the
|
||||
shipped `.wasm`. Both the original and the modified file remain under MPL-2.0.
|
||||
|
||||
---
|
||||
|
||||
## dwg-wasm — MIT
|
||||
|
||||
The wasm-bindgen wrapper that turns acadrust output into this viewer's
|
||||
`parseResult` (`hmwebviewer/rust/dwg-wasm/`). Our own code, MIT. MPL-2.0 §3.3
|
||||
permits distributing this Larger Work under different terms as long as the
|
||||
MPL-covered files above keep their license — which they do.
|
||||
|
||||
---
|
||||
|
||||
## Other components
|
||||
|
||||
| Component | License |
|
||||
|---|---|
|
||||
| three.js | MIT |
|
||||
| dxf-parser | MIT |
|
||||
| opentype.js | MIT |
|
||||
| NanumGothic (`public/fonts/`) | SIL Open Font License 1.1 |
|
||||
| Viewer2D / sample glue | MIT |
|
||||
@@ -0,0 +1,111 @@
|
||||
--- a/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
||||
+++ b/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
||||
@@ -1682,20 +1682,49 @@
|
||||
MLineData { scale_factor, justification, start_point, normal, openclosed, lines_in_style, vertex_count, vertices, style_handle }
|
||||
}
|
||||
|
||||
+/// Cap an array count against the bits actually left in the object's main
|
||||
+/// stream instead of the blanket [`MAX_ARRAY_COUNT`].
|
||||
+///
|
||||
+/// MESH is the one entity whose arrays legitimately run past 100 000 items: a
|
||||
+/// road-surface mesh in a civil drawing carries hundreds of thousands of
|
||||
+/// vertices. Clamping those to the blanket cap does not merely truncate the
|
||||
+/// mesh — the reader then continues from the wrong bit offset, so the face,
|
||||
+/// edge and crease lists that follow decode as garbage (observed: a 100 000
|
||||
+/// vertex clamp left a 14-face mesh with face sizes `[64, 0, 64, 23, 0, …]`).
|
||||
+/// Bounding by the remaining bits keeps the corrupt-data protection — no
|
||||
+/// unbounded allocation is possible — without mis-parsing a valid drawing.
|
||||
+///
|
||||
+/// `min_bits_per_item` is the shortest legal encoding of one element: a
|
||||
+/// BitDouble is 2 bits at minimum (the 0.0 / 1.0 forms), so a 3BD vertex is 6
|
||||
+/// and a BitLong is 2.
|
||||
+fn stream_bounded_count(reader: &DwgMergedReader, raw: i32, min_bits_per_item: i64) -> i32 {
|
||||
+ let raw = raw.max(0) as i64;
|
||||
+ let total_bits = (reader.main().data_len() as i64).saturating_mul(8);
|
||||
+ // The handle stream follows the main stream; anything past its start is not
|
||||
+ // array data. handle_start() is 0 when the framing did not supply it.
|
||||
+ let handle_start = reader.handle_start();
|
||||
+ let end_bits = if handle_start > 0 { handle_start.min(total_bits) } else { total_bits };
|
||||
+ let remaining = (end_bits - reader.position_in_bits()).max(0);
|
||||
+ raw.min(remaining / min_bits_per_item.max(1)).min(i32::MAX as i64) as i32
|
||||
+}
|
||||
+
|
||||
pub fn read_mesh(reader: &mut DwgMergedReader) -> MeshData {
|
||||
let version = reader.read_bit_short();
|
||||
let blend_crease = reader.read_bit();
|
||||
let subdivision_level = reader.read_bit_long();
|
||||
|
||||
- let num_verts = safe_count(reader.read_bit_long());
|
||||
+ let raw_verts = reader.read_bit_long();
|
||||
+ let num_verts = stream_bounded_count(reader, raw_verts, 6);
|
||||
let mut vertices = Vec::with_capacity(num_verts as usize);
|
||||
for _ in 0..num_verts { vertices.push(reader.read_3bit_double()); }
|
||||
|
||||
- let total_face_data = safe_count(reader.read_bit_long());
|
||||
+ let raw_face_data = reader.read_bit_long();
|
||||
+ let total_face_data = stream_bounded_count(reader, raw_face_data, 2);
|
||||
let mut faces = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < total_face_data {
|
||||
- let n = safe_count(reader.read_bit_long());
|
||||
+ let raw_n = reader.read_bit_long();
|
||||
+ let n = stream_bounded_count(reader, raw_n, 2);
|
||||
i += 1;
|
||||
let mut face = Vec::new();
|
||||
for _ in 0..n {
|
||||
@@ -1705,7 +1734,8 @@
|
||||
faces.push(face);
|
||||
}
|
||||
|
||||
- let num_edges = safe_count(reader.read_bit_long());
|
||||
+ let raw_edges = reader.read_bit_long();
|
||||
+ let num_edges = stream_bounded_count(reader, raw_edges, 4);
|
||||
let mut edges = Vec::with_capacity(num_edges as usize);
|
||||
for _ in 0..num_edges {
|
||||
let s = reader.read_bit_long();
|
||||
@@ -1713,7 +1743,8 @@
|
||||
edges.push((s, e));
|
||||
}
|
||||
|
||||
- let num_creases = safe_count(reader.read_bit_long());
|
||||
+ let raw_creases = reader.read_bit_long();
|
||||
+ let num_creases = stream_bounded_count(reader, raw_creases, 2);
|
||||
let mut crease_values = Vec::with_capacity(num_creases as usize);
|
||||
for _ in 0..num_creases { crease_values.push(reader.read_bit_double()); }
|
||||
|
||||
@@ -2350,14 +2381,31 @@
|
||||
pub fn read_ole2frame(reader: &mut DwgMergedReader, version: DwgVersion) -> Ole2FrameData {
|
||||
let ver = reader.read_bit_short();
|
||||
let mode = if version.r2000_plus() { reader.read_bit_short() } else { 0 };
|
||||
- // OLE binary data can be very large (embedded images/documents),
|
||||
- // so don't use safe_count (100 KB cap). Use a generous 10 MB cap instead.
|
||||
let data_len = (reader.read_bit_long().max(0) as usize).min(10_000_000);
|
||||
let data = reader.read_bytes(data_len);
|
||||
let trailing_byte = if version.r2000_plus() { reader.read_byte() } else { 3 };
|
||||
- let upper_left = reader.read_3bit_double();
|
||||
- let lower_right = reader.read_3bit_double();
|
||||
+
|
||||
+ let mut upper_left = Vector3::new(0.0, 0.0, 0.0);
|
||||
+ let mut lower_right = Vector3::new(0.0, 0.0, 0.0);
|
||||
+
|
||||
+ // Extract 4-corner coordinates stored in the OLE binary data header starting at offset 2
|
||||
+ if data.len() >= 66 {
|
||||
+ let ul_x = f64::from_le_bytes(data[2..10].try_into().unwrap());
|
||||
+ let ul_y = f64::from_le_bytes(data[10..18].try_into().unwrap());
|
||||
+ let ul_z = f64::from_le_bytes(data[18..26].try_into().unwrap());
|
||||
+ let lr_x = f64::from_le_bytes(data[50..58].try_into().unwrap());
|
||||
+ let lr_y = f64::from_le_bytes(data[58..66].try_into().unwrap());
|
||||
+ let lr_z = if data.len() >= 74 { f64::from_le_bytes(data[66..74].try_into().unwrap()) } else { 0.0 };
|
||||
+
|
||||
+ if ul_x.is_finite() && ul_y.is_finite() && lr_x.is_finite() && lr_y.is_finite()
|
||||
+ && (ul_x != 0.0 || ul_y != 0.0 || lr_x != 0.0 || lr_y != 0.0) {
|
||||
+ upper_left = Vector3::new(ul_x, ul_y, if ul_z.is_finite() { ul_z } else { 0.0 });
|
||||
+ lower_right = Vector3::new(lr_x, lr_y, if lr_z.is_finite() { lr_z } else { 0.0 });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
let ole_type = if version.r2000_plus() { reader.read_byte() } else { 2 };
|
||||
+
|
||||
Ole2FrameData { version: ver, mode, data, trailing_byte, upper_left, lower_right, ole_type }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under the name "LEGAL", and included in all Covered Software
|
||||
distributed in Source Code Form. Except to the extent prohibited by
|
||||
statute or regulation, such description must be sufficiently detailed
|
||||
for a recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
# acadrust MPL-2.0 — 의무 사항과 조치 내역
|
||||
|
||||
| 항목 | 내용 |
|
||||
|------|------|
|
||||
| 작성 목적 | 파서(acadrust, MPL-2.0)를 담은 wasm 을 공개 배포하면서 지켜야 할 의무를 확정하고 조치 내역 기록 |
|
||||
| 대상 배포물 | `https://dwg-dxf-viewer-sample.pages.dev/` (Cloudflare Pages, 공개) |
|
||||
| 검토일 | 2026-08-04 |
|
||||
| 상태 | **조치 완료** — 고지 파일·MPL 원문·패치 동봉, 사이트에서 링크 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 결론 (세 줄)
|
||||
|
||||
1. **이번 lineweight 작업은 MPL 파일을 건드리지 않았다.** 수정한 `dwg-wasm/src/lib.rs` 는
|
||||
우리 MIT 코드다. → MPL 의무가 *새로* 생기지 않았다.
|
||||
2. 그러나 **이전부터** acadrust 를 한 파일 패치해서 wasm 에 컴파일해 공개 배포 중이었고,
|
||||
그 상태가 **MPL-2.0 §3.2 위반**이었다. 라이선스 원문도 고지도 소스 입수 경로도 없었다.
|
||||
3. **upstream(hakanaktt/acadrust)에 PR 을 올릴 의무는 없다.** MPL 은 기여 강제가 아니다.
|
||||
**배포물에 고지 + 소스 입수 경로만 넣으면 된다.** ← 이번에 그렇게 조치했다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 무엇이 MPL 이고 무엇이 우리 것인가
|
||||
|
||||
| 코드 | 위치 | 라이선스 | 이번 세션에서 수정? |
|
||||
|------|------|----------|---------------------|
|
||||
| **acadrust 0.4.1** (DWG 파서 본체) | `hmwebviewer/rust/acadrust/` | **MPL-2.0** | **아니오** |
|
||||
| dwg-wasm (wasm-bindgen 래퍼 + JSON 직렬화) | `hmwebviewer/rust/dwg-wasm/src/lib.rs` | MIT (우리 코드) | 예 (lineWeight 필드 추가) |
|
||||
| Viewer2D 외 프론트엔드 | `src/` | MIT | 예 |
|
||||
|
||||
MPL-2.0 은 **파일 단위(file-level) 카피레프트**다. GPL 처럼 프로젝트 전체로 번지지 않는다.
|
||||
MPL 파일을 고쳤으면 **그 파일만** MPL 로 유지하고 소스를 공개하면 되고,
|
||||
MPL 파일을 호출·링크하는 우리 코드(§3.3 의 "Larger Work")는 우리가 원하는 라이선스로 배포할 수 있다.
|
||||
|
||||
→ **`lib.rs` 에 필드 세 개 추가한 것은 MPL 전염 사유가 아니다.**
|
||||
|
||||
## 2. 그럼에도 의무가 있는 이유 — 이미 한 파일을 고쳤다
|
||||
|
||||
vendored acadrust 를 crates.io 원본과 diff 하면 **정확히 한 파일**이 다르다.
|
||||
|
||||
```bash
|
||||
diff -rq ~/.cargo/registry/src/index.crates.io-*/acadrust-0.4.1/src \
|
||||
hmwebviewer/rust/acadrust/src
|
||||
# → src/io/dwg/dwg_stream_readers/object_reader/entities.rs 만 differ
|
||||
```
|
||||
|
||||
`read_mesh` 의 배열 길이를 남은 비트 수로 제한하는 패치다(대형 SubD 메쉬 손상 수정,
|
||||
[`subdmesh-rendering.md`](./subdmesh-rendering.md)). **이 파일은 Modified Covered
|
||||
Software** 이고, 그 소스는 MPL 하에 제공되어야 한다.
|
||||
|
||||
그리고 `.wasm` 은 acadrust 를 컴파일해 담은 **Executable Form** 이다. 공개 사이트에서
|
||||
브라우저로 내려보내는 순간 MPL 상 "Distribution" 에 해당한다.
|
||||
|
||||
### 적용되는 조항
|
||||
|
||||
| 조항 | 요구 |
|
||||
|------|------|
|
||||
| §3.1 | Source Code Form 을 배포하면 MPL 하에 배포하고 라이선스 사본을 동봉 |
|
||||
| **§3.2** | **Executable Form 배포 시**: (a) 해당 Covered Software 의 Source Code Form 을 MPL 하에 제공하고, (b) **수령자에게 그 입수 방법을 알릴 것** (배포 비용 이상 청구 금지) |
|
||||
| §3.3 | Larger Work(우리 앱)는 다른 라이선스로 배포 가능 — 위 (a)(b)를 지키는 한 |
|
||||
| §3.4 | 라이선스·저작권 고지를 제거하지 말 것 |
|
||||
|
||||
**MPL 에는 upstream 기여 의무 조항이 없다.** §3.2 는 "수령자에게 제공"이지
|
||||
"원저자에게 반영"이 아니다.
|
||||
|
||||
## 3. 위반 상태였던 지점 (조치 전)
|
||||
|
||||
- 배포 사이트에 MPL-2.0 **원문 없음**
|
||||
- acadrust 를 쓴다는 **고지 없음** (README 표에만 한 줄, 배포물에는 미포함)
|
||||
- 패치된 `entities.rs` 소스를 **얻을 방법 없음**
|
||||
- `hmwebviewer` GitHub 저장소는 비공개(API 404)
|
||||
- 샘플 저장소는 사내 gitea
|
||||
- 즉 공개 배포 대상자가 소스에 접근할 경로가 전무
|
||||
|
||||
## 4. 조치 사항 (완료)
|
||||
|
||||
### 4-1. 배포물에 동봉
|
||||
|
||||
| 파일 | 배포 URL | 내용 |
|
||||
|------|----------|------|
|
||||
| `public/licenses/acadrust-MPL-2.0.txt` | `/licenses/acadrust-MPL-2.0.txt` | MPL-2.0 원문 (§3.1·§3.4) |
|
||||
| `public/licenses/acadrust-0.4.1-read_mesh.patch` | `/licenses/acadrust-0.4.1-read_mesh.patch` | 우리 수정분 diff (§3.2(a)) |
|
||||
| `public/licenses/THIRD-PARTY-NOTICES.md` | `/licenses/THIRD-PARTY-NOTICES.md` | 컴포넌트·버전·업스트림·수정 여부·복원 절차 (§3.2(b)) |
|
||||
|
||||
저장소 루트에도 같은 내용의 [`../THIRD-PARTY-NOTICES.md`](../THIRD-PARTY-NOTICES.md) 를 둔다.
|
||||
|
||||
### 4-2. 사이트에서 링크
|
||||
|
||||
`index.html` 우측 "Module recipe" 패널 하단에 고지 링크 추가.
|
||||
§3.2(b) 의 "reasonable manner" 요건은 **수령자가 실제로 찾을 수 있는 위치**를 요구한다.
|
||||
|
||||
### 4-3. 소스 복원 절차 명시
|
||||
|
||||
원본 크레이트 + 패치 조합이면 컴파일된 소스를 바이트 단위로 재현할 수 있다.
|
||||
|
||||
```bash
|
||||
curl -O https://crates.io/api/v1/crates/acadrust/0.4.1/download
|
||||
tar xf download && cd acadrust-0.4.1
|
||||
patch -p1 < acadrust-0.4.1-read_mesh.patch
|
||||
```
|
||||
|
||||
업스트림 커밋 `f249c2f816acf36ee51cd5533716bdd443c2517e` 도 고지에 적어 뒀다
|
||||
(크레이트의 `.cargo_vcs_info.json` 값).
|
||||
|
||||
## 5. 질문에 대한 답
|
||||
|
||||
> **"해당 레포(upstream)에 올려야 하나, 배포시 문서만 추가하면 되나?"**
|
||||
|
||||
**문서(고지) + 소스 입수 경로만 배포물에 넣으면 된다. upstream PR 은 의무가 아니다.**
|
||||
|
||||
| 선택지 | MPL 상 필요? | 판단 |
|
||||
|--------|--------------|------|
|
||||
| 배포물에 MPL 원문 + 고지 + 패치 동봉 | **필수** | ✅ 이번에 조치 |
|
||||
| upstream `hakanaktt/acadrust` 에 PR | 아니오 | 선택 — read_mesh 수정은 일반 버그픽스라 올리면 유지보수 부담이 준다 (권장, 의무 아님) |
|
||||
| `hmwebviewer` 저장소 공개 전환 | 아니오 | 불필요 — 패치 동봉으로 §3.2 충족 |
|
||||
| 패치된 acadrust 전체 소스를 이 저장소에 vendoring | 아니오 | 더 엄격한 대안. 189 파일이 샘플 저장소에 들어와 무거워짐. 원본+패치 방식은 배포판(distro) 관행과 동일하고 §3.2 를 충족 |
|
||||
|
||||
> **"lineweight 작업으로 라이선스 의무가 생겼나?"**
|
||||
|
||||
**아니오.** 이번에 고친 `dwg-wasm/src/lib.rs` 는 MIT 인 우리 파일이고, MPL 파일은
|
||||
건드리지 않았다(위 §2 diff 로 확인). 의무는 **이전 read_mesh 패치 + wasm 공개 배포**에서
|
||||
이미 발생해 있었고, 이번에 뒤늦게 정리한 것이다.
|
||||
|
||||
## 6. 앞으로 지킬 것 (체크리스트)
|
||||
|
||||
- [ ] `rust/acadrust/` 아래 파일을 고치면 → 패치 파일을 다시 뽑아 `public/licenses/` 갱신
|
||||
- [ ] acadrust 버전을 올리면 → 고지의 버전·커밋·패치를 함께 갱신
|
||||
- [ ] 새 서드파티를 번들하면 → `THIRD-PARTY-NOTICES.md` 에 행 추가
|
||||
- [ ] MPL 파일에서 라이선스 헤더 주석을 지우지 말 것 (§3.4)
|
||||
- [ ] 배포 전 `/licenses/` 3개 파일이 실제로 서빙되는지 확인
|
||||
|
||||
## 7. 참고
|
||||
|
||||
- MPL-2.0 원문: [`../public/licenses/acadrust-MPL-2.0.txt`](../public/licenses/acadrust-MPL-2.0.txt)
|
||||
- FAQ: https://www.mozilla.org/MPL/2.0/FAQ/ (Q: file-level copyleft, Q: 배포 의무 범위)
|
||||
- 수정 경위: [`subdmesh-rendering.md`](./subdmesh-rendering.md)
|
||||
- 이번 파서 변경(MIT 영역): [`lineweight-rendering.md`](./lineweight-rendering.md) §4-1
|
||||
@@ -96,10 +96,22 @@ lineweight 는 **화면 픽셀 폭**이다. 줌해도 굵기가 변하지 않는
|
||||
따라서 월드 단위가 아니라 스크린 스페이스로 그려야 한다.
|
||||
|
||||
```
|
||||
px = mm × 8 (LW_PX_PER_MM, 최대 24px)
|
||||
0.25mm ≈ 2px · 0.50mm ≈ 4px · 2.11mm ≈ 17px ← AutoCAD 기본 표시 배율과 동일 눈금
|
||||
px = mm × 11 (LW_PX_PER_MM 기본값, 최대 48px)
|
||||
0.35mm = 3.9px · 0.50mm = 5.5px · 2.11mm = 23px
|
||||
```
|
||||
|
||||
**이 배율은 상수로 확정할 수 있는 값이 아니다.** AutoCAD 도 모델 공간에서는 선가중치를
|
||||
줌과 무관한 픽셀로만 그리고, 그 환산은 "선가중치 설정 → 표시 배율" 슬라이더가 정한다.
|
||||
즉 화면 DPI·모니터에 따라 맞는 값이 달라진다.
|
||||
|
||||
- 코드 기본값: `LW_PX_PER_MM = 11` (사용자가 실제 화면에서 AutoCAD 와 나란히 놓고 맞춘 값)
|
||||
- 런타임 조정: **옵션 패널 → 선가중치 → 표시 배율**, 또는 `setLineweightScale(pxPerMm)`
|
||||
- 조정은 **재파싱 없이 즉시 반영**된다 (3-6 절)
|
||||
|
||||
> 시행착오 기록: 처음 8px/mm(96dpi 어림) → 손테스트 "얇다" 반려 → 정답지 캡처에서
|
||||
> 역산해 20px/mm → 실제 화면 대조 후 최종 11px/mm. 상수 추정이 두 번 빗나갔고,
|
||||
> 그래서 값을 코드에서 빼 옵션으로 노출했다.
|
||||
|
||||
### 3-2. 0.25mm 이하는 헤어라인 유지 — **회귀 방지 장치**
|
||||
|
||||
`-3`(Default)·`-1`(ByLayer → 레이어도 Default)이 도면 대부분을 차지한다.
|
||||
@@ -129,6 +141,17 @@ px = mm × 8 (LW_PX_PER_MM, 최대 24px)
|
||||
- **레이어 토글** — 인스턴스 지오메트리는 인덱스 재작성이 불가
|
||||
- **선택 하이라이트** — 색상 배열이 여러 버퍼로 쪼개짐
|
||||
|
||||
### 3-6. 배율 조정은 리빌드 없이
|
||||
|
||||
헤어라인 컷오프가 **mm 값**(0.25mm)이라 배율이 바뀌어도 어떤 세그먼트가 어느 버킷에
|
||||
들어가는지는 변하지 않는다. 모든 굵기가 같은 배수로 움직일 뿐이다.
|
||||
|
||||
→ 각 fat 배치가 자기 굵기를 **mm 로 기억**(`_fatBufs[].lwMm`)하고,
|
||||
`setLineweightScale()` 은 `material.linewidth` 만 다시 쓴다. 37MB 도면에서도 슬라이더를
|
||||
끄는 동안 지연이 없다.
|
||||
|
||||
반면 **표시 on/off** 는 굵기가 0이 되어 버킷 구성이 바뀌므로 재빌드가 필요하다.
|
||||
|
||||
### 3-5. 표시 on/off — AutoCAD LWDISPLAY 와 동일하게
|
||||
|
||||
굵기 표시는 CAD 에서 원래 켜고 끌 수 있는 옵션이다. 도면의 헤더 값을 기본으로 따르되
|
||||
@@ -180,9 +203,21 @@ entity lineWeight = { -3:15176, -1:2613, 13:8794, 35:37944, 40:119, 50:849 }
|
||||
|
||||
### 4-3. UI
|
||||
|
||||
- `index.html` · `src/main.ts` — 툴바 **선가중치** 버튼 (상태에 따라 강조, 툴팁에 ON/OFF)
|
||||
- `src/propertyInspector.ts` — `선가중치 (Lineweight)` 행 추가.
|
||||
`-1`→`BYLAYER`, `-2`→`BYBLOCK`, `-3`→`DEFAULT (0.25mm)`, 그 외 `0.35mm` 형식
|
||||
툴바 **옵션** 버튼 → `옵션 (Options)` 패널 (`index.html` · `src/main.ts`).
|
||||
|
||||
| 항목 | 동작 |
|
||||
|------|------|
|
||||
| 표시 (LWDISPLAY) | on/off. 체크 해제 시 전부 헤어라인 (재빌드) |
|
||||
| 표시 배율 | 슬라이더 + 숫자 입력 (px/mm). 즉시 반영 |
|
||||
| 환산 표시 | 그 도면이 실제로 쓰는 굵기만 `0.35mm → 3.9px` 식으로 표기 |
|
||||
| ↺ 기본값 | 배율·표시·그리드·줌속도 초기화 |
|
||||
|
||||
설정은 **IndexedDB** (`hmw-viewer` DB → `options` 스토어)에 저장한다
|
||||
([`../src/optionsStore.ts`](../src/optionsStore.ts)). 키: `lineweightScale`,
|
||||
`lineweightOn`, `grid`, `zoomSpeed`. 스토리지가 막혀 있어도 조용히 기본값으로 동작한다.
|
||||
|
||||
속성 패널에는 `선가중치 (Lineweight)` 행이 있다 (`src/propertyInspector.ts`).
|
||||
`-1`→`BYLAYER`, `-2`→`BYBLOCK`, `-3`→`DEFAULT (0.25mm)`, 그 외 `0.35mm` 형식.
|
||||
|
||||
### 4-4. 배포
|
||||
|
||||
@@ -231,7 +266,42 @@ LWPOLYLINE · #151257
|
||||
|
||||
cyan 픽셀 수가 감소(11502→11486) → fat 배치의 정점이 강조색으로 재도색됨을 확인.
|
||||
|
||||
### 5-4. 회귀 없음
|
||||
### 5-4. 도면이 실제로 들고 있는 수치
|
||||
|
||||
굵기는 **레이어가 아니라 엔티티**에 들어 있다. 23개 레이어 lineweight 는 **전부 `-3`
|
||||
(Default)** 이라 ByLayer 로는 아무 정보도 나오지 않는다.
|
||||
|
||||
| 레이어 | ACI | lineweight | 개수 |
|
||||
|---|---|---|---|
|
||||
| 도로 가장자리 | 1 (빨강) | **50 = 0.50mm** | 18 |
|
||||
| 도로마킹 | 255 / 7 / 2 / 4 | 50 = 0.50mm | 799 |
|
||||
| 부체도로 가장자리 | 20 | 50 = 0.50mm | 32 |
|
||||
| 비탈면방향 | 140 / 150 | 35 = 0.35mm | 34,758 |
|
||||
| 노면측구 | 4 (시안) | 35 = 0.35mm | 303 |
|
||||
| 시작·끝라인 / Drawing확폭 | 4 / 6 / 2 / 3 | 40 = 0.40mm | 105 |
|
||||
| SubContour · MainContour | 3 / 6 | 13 = 0.13mm | 8,794 |
|
||||
| **도로면 (MESH 삼각망)** | 7 | **-3 Default** | 1 (185,444 faces) |
|
||||
| Station (중심선·라벨) | 1 (빨강) | -1 → 레이어 -3 → Default | 1,082 |
|
||||
|
||||
주의: 화면의 **빨간 선이 두 종류**다. 굵은 쪽이 `도로 가장자리`(0.50mm), 도로 한가운데를
|
||||
지나는 얇은 쪽이 `Station` 중심선(Default → 헤어라인). 정답지에서 굵게 나오는 것은 전자다.
|
||||
|
||||
### 5-5. 정답지 대조 (배율 확정 근거)
|
||||
|
||||
lineweight 는 줌과 무관한 화면 픽셀이므로, 두 캡처의 **절대 픽셀 두께**를 그대로 비교한다.
|
||||
|
||||
| 배율 | `도로 가장자리` 0.50mm | `노면측구` 0.35mm (헤드리스 실측) |
|
||||
|--|--|--|
|
||||
| 8 px/mm (최초, 반려) | 4 px | 2–3 px |
|
||||
| 20 px/mm (캡처 역산) | 10 px | 7 px |
|
||||
| **11 px/mm (현재 기본값)** | **5.5 px** | **3–4 px** |
|
||||
| 25 px/mm (참고) | 12.5 px | 8–9 px |
|
||||
|
||||
캡처 이미지에서 역산한 20 은 실제 화면에서 과했다 — 캡처 DPI 와 사용자 모니터가 달라서다.
|
||||
최종값은 사용자가 뷰어와 AutoCAD 를 나란히 놓고 맞춘 **11**. 이래서 상수로 못 박지 않고
|
||||
옵션으로 뺐다.
|
||||
|
||||
### 5-6. 회귀 없음
|
||||
|
||||
- `npx tsc --noEmit` 통과
|
||||
- `npm run build` 통과
|
||||
@@ -241,8 +311,9 @@ cyan 픽셀 수가 감소(11502→11486) → fat 배치의 정점이 강조색
|
||||
|
||||
## 6. 남은 과제
|
||||
|
||||
1. **표시 배율 고정** — `LW_PX_PER_MM = 8` 이 상수다. AutoCAD 는 "선가중치 설정 → 표시 배율"
|
||||
슬라이더로 조절 가능하므로, 필요하면 `setLineweightScale()` 로 노출할 것.
|
||||
1. **배율 기본값은 환경 의존** — 11 은 특정 모니터에서 맞춘 값이다. 다른 화면에서는
|
||||
옵션 패널로 다시 맞춰야 한다. `devicePixelRatio` 로 자동 보정하는 것은 검토만 하고
|
||||
넣지 않았다(캡처·원격 데스크톱 등에서 오히려 어긋난다).
|
||||
2. **ByBlock 근사** — 블록 자식의 `-2` 는 INSERT 굵기를 상속하지만, 중첩 블록에서
|
||||
중간 INSERT 가 `-2` 인 경우까지는 추적하지 않는다(점선 처리와 동일한 기존 정책).
|
||||
3. **DXF 레이어 굵기 없음** — `dxf-parser` 의 LAYER 핸들러가 코드 370 을 무시한다.
|
||||
|
||||
+72
-1
@@ -66,6 +66,25 @@
|
||||
button.primary { background: #238636; border-color: #2ea043; }
|
||||
button.wasm { border-color: var(--wasm); color: var(--wasm); }
|
||||
label.btn input { display: none; }
|
||||
/* 옵션 패널 — 값은 IndexedDB(hmw-viewer/options)에 저장된다 */
|
||||
#optBody { padding: 12px 14px; overflow: auto; font-size: 12px; color: var(--ink); }
|
||||
.opt-sec { margin-bottom: 16px; }
|
||||
.opt-sec > h4 {
|
||||
margin: 0 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .05em;
|
||||
text-transform: uppercase; color: var(--muted);
|
||||
}
|
||||
.opt-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
||||
.opt-row > label { flex: 1; color: var(--ink); }
|
||||
.opt-row input[type=range] { flex: 1; min-width: 90px; accent-color: var(--accent); }
|
||||
.opt-row input[type=number] {
|
||||
width: 58px; background: #0d1117; color: var(--ink);
|
||||
border: 1px solid var(--line); border-radius: 6px; padding: 3px 6px;
|
||||
font: 12px var(--mono); text-align: right;
|
||||
}
|
||||
.opt-row input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--accent); }
|
||||
.opt-unit { font: 11px var(--mono); color: var(--muted); }
|
||||
.opt-note { font: 11px var(--mono); color: var(--accent); line-height: 1.6; word-break: keep-all; }
|
||||
.opt-help { font-size: 11px; color: var(--muted); line-height: 1.5; margin: 4px 0 0; }
|
||||
#status {
|
||||
margin-left: auto; font: 12px var(--mono); color: var(--muted);
|
||||
background: rgba(22,27,34,.85); border: 1px solid var(--line);
|
||||
@@ -169,6 +188,7 @@
|
||||
|
||||
#properties { right: 12px; }
|
||||
#layers { right: 344px; display: none; }
|
||||
#options { right: 676px; width: 340px; height: auto; max-height: 70vh; display: none; }
|
||||
|
||||
#propBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
#layerBody { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
@@ -213,7 +233,7 @@
|
||||
<div id="spaces" title="Model Space / Paper Space (Layout)"></div>
|
||||
<button id="fit" type="button">Fit (F)</button>
|
||||
<button id="theme" type="button">Theme</button>
|
||||
<button id="lwt" type="button" title="선가중치 표시 (AutoCAD LWDISPLAY)">선가중치</button>
|
||||
<button id="optionsBtn" type="button" title="표시 옵션 (선가중치 · 그리드 · 줌)">옵션</button>
|
||||
<button id="layersBtn" type="button">Layers</button>
|
||||
<button id="propsBtn" type="button">Props (속성)</button>
|
||||
<div id="status">ready</div>
|
||||
@@ -249,6 +269,51 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 옵션 (Options) 패널 — 설정은 IndexedDB 에 저장 -->
|
||||
<div id="options" class="cad-panel">
|
||||
<div class="hdr">
|
||||
<h3><span>⚙️</span> 옵션 (Options)</h3>
|
||||
<div class="hdr-actions">
|
||||
<button id="optReset" type="button" title="기본값으로">↺ 기본값</button>
|
||||
<button id="collapseOptBtn" class="hdr-btn collapse-btn" type="button" title="접기">▾</button>
|
||||
<button id="closeOptBtn" class="hdr-btn" type="button" title="닫기">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="optBody">
|
||||
<div class="opt-sec">
|
||||
<h4>선가중치 (Lineweight)</h4>
|
||||
<div class="opt-row">
|
||||
<label for="optLwOn">표시 (LWDISPLAY)</label>
|
||||
<input id="optLwOn" type="checkbox" />
|
||||
</div>
|
||||
<div class="opt-row">
|
||||
<label for="optLwScale">표시 배율</label>
|
||||
<input id="optLwScale" type="range" min="2" max="60" step="0.5" value="11" />
|
||||
<input id="optLwScaleNum" type="number" min="1" max="80" step="0.5" value="11" />
|
||||
<span class="opt-unit">px/mm</span>
|
||||
</div>
|
||||
<div id="optLwOut" class="opt-note">—</div>
|
||||
<p class="opt-help">
|
||||
AutoCAD "선가중치 설정 → 표시 배율"과 같다. 모델 공간에서 선가중치는 줌과
|
||||
무관한 화면 픽셀로 그려지므로, 화면 DPI에 맞춰 이 값을 조정한다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="opt-sec">
|
||||
<h4>화면 (Display)</h4>
|
||||
<div class="opt-row">
|
||||
<label for="optGrid">그리드 표시</label>
|
||||
<input id="optGrid" type="checkbox" />
|
||||
</div>
|
||||
<div class="opt-row">
|
||||
<label for="optZoomSpeed">줌 속도</label>
|
||||
<input id="optZoomSpeed" type="range" min="0.2" max="3" step="0.1" value="1" />
|
||||
<input id="optZoomSpeedNum" type="number" min="0.1" max="5" step="0.1" value="1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 속성 (Properties) 패널 -->
|
||||
<div id="properties" class="cad-panel">
|
||||
<div class="hdr">
|
||||
@@ -275,6 +340,12 @@
|
||||
코드: <code>src/main.ts</code> ·
|
||||
지도: <a href="/docs/modules-dwg-dxf.html" target="_blank">docs/modules-dwg-dxf.html</a>
|
||||
</p>
|
||||
<!-- MPL-2.0 §3.2(b): recipients of the wasm must be told where the
|
||||
acadrust source (with our local patch) can be obtained. -->
|
||||
<p style="margin:6px 0 0">
|
||||
오픈소스 고지: <a href="/licenses/THIRD-PARTY-NOTICES.md" target="_blank">THIRD-PARTY-NOTICES</a>
|
||||
(acadrust · MPL-2.0 · <a href="/licenses/acadrust-0.4.1-read_mesh.patch" target="_blank">local patch</a>)
|
||||
</p>
|
||||
</aside>
|
||||
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Third-party notices
|
||||
|
||||
This viewer bundles third-party code. The notices below apply to the source
|
||||
tree **and** to the built site (`dist2/`, deployed at
|
||||
`https://dwg-dxf-viewer-sample.pages.dev/`), because the build embeds the
|
||||
components listed here.
|
||||
|
||||
Served copies of the license texts live under
|
||||
[`public/licenses/`](./public/licenses/) → `/licenses/…` on the deployed site.
|
||||
|
||||
---
|
||||
|
||||
## acadrust 0.4.1 — Mozilla Public License 2.0 — **MODIFIED**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Component | `acadrust` (DWG/DXF parser, Rust) |
|
||||
| Version | 0.4.1 |
|
||||
| License | MPL-2.0 — full text: [`public/licenses/acadrust-MPL-2.0.txt`](./public/licenses/acadrust-MPL-2.0.txt) (`/licenses/acadrust-MPL-2.0.txt`) |
|
||||
| Upstream | https://github.com/hakanaktt/acadrust · https://crates.io/crates/acadrust/0.4.1 |
|
||||
| Upstream commit | `f249c2f816acf36ee51cd5533716bdd443c2517e` (from the crate's `.cargo_vcs_info.json`) |
|
||||
| Author | Hakan AK |
|
||||
| Shipped as | compiled into `assets/acadrust_dwg_bg-*.wasm` (Executable Form) |
|
||||
|
||||
**This copy is modified.** One file differs from the published crate:
|
||||
|
||||
```
|
||||
src/io/dwg/dwg_stream_readers/object_reader/entities.rs (read_mesh array-count bounds)
|
||||
```
|
||||
|
||||
The modification bounds `read_mesh`'s array counts by the bits remaining in the
|
||||
object stream instead of the blanket 100 000-item guard, which truncated and
|
||||
thereby corrupted large SubD meshes.
|
||||
|
||||
### How to obtain the Source Code Form (MPL-2.0 §3.2)
|
||||
|
||||
1. Get the unmodified crate source — `cargo vendor`, or
|
||||
`https://crates.io/api/v1/crates/acadrust/0.4.1/download`, or the upstream
|
||||
commit above.
|
||||
2. Apply [`public/licenses/acadrust-0.4.1-read_mesh.patch`](./public/licenses/acadrust-0.4.1-read_mesh.patch)
|
||||
(`/licenses/acadrust-0.4.1-read_mesh.patch` on the deployed site):
|
||||
|
||||
```bash
|
||||
tar xf acadrust-0.4.1.crate && cd acadrust-0.4.1
|
||||
patch -p1 < acadrust-0.4.1-read_mesh.patch
|
||||
```
|
||||
|
||||
The result is the exact Source Code Form of the acadrust code compiled into the
|
||||
shipped `.wasm`. Both the original and the modified file remain under MPL-2.0.
|
||||
|
||||
---
|
||||
|
||||
## dwg-wasm — MIT
|
||||
|
||||
The wasm-bindgen wrapper that turns acadrust output into this viewer's
|
||||
`parseResult` (`hmwebviewer/rust/dwg-wasm/`). Our own code, MIT. MPL-2.0 §3.3
|
||||
permits distributing this Larger Work under different terms as long as the
|
||||
MPL-covered files above keep their license — which they do.
|
||||
|
||||
---
|
||||
|
||||
## Other components
|
||||
|
||||
| Component | License |
|
||||
|---|---|
|
||||
| three.js | MIT |
|
||||
| dxf-parser | MIT |
|
||||
| opentype.js | MIT |
|
||||
| NanumGothic (`public/fonts/`) | SIL Open Font License 1.1 |
|
||||
| Viewer2D / sample glue | MIT |
|
||||
@@ -0,0 +1,111 @@
|
||||
--- a/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
||||
+++ b/src/io/dwg/dwg_stream_readers/object_reader/entities.rs
|
||||
@@ -1682,20 +1682,49 @@
|
||||
MLineData { scale_factor, justification, start_point, normal, openclosed, lines_in_style, vertex_count, vertices, style_handle }
|
||||
}
|
||||
|
||||
+/// Cap an array count against the bits actually left in the object's main
|
||||
+/// stream instead of the blanket [`MAX_ARRAY_COUNT`].
|
||||
+///
|
||||
+/// MESH is the one entity whose arrays legitimately run past 100 000 items: a
|
||||
+/// road-surface mesh in a civil drawing carries hundreds of thousands of
|
||||
+/// vertices. Clamping those to the blanket cap does not merely truncate the
|
||||
+/// mesh — the reader then continues from the wrong bit offset, so the face,
|
||||
+/// edge and crease lists that follow decode as garbage (observed: a 100 000
|
||||
+/// vertex clamp left a 14-face mesh with face sizes `[64, 0, 64, 23, 0, …]`).
|
||||
+/// Bounding by the remaining bits keeps the corrupt-data protection — no
|
||||
+/// unbounded allocation is possible — without mis-parsing a valid drawing.
|
||||
+///
|
||||
+/// `min_bits_per_item` is the shortest legal encoding of one element: a
|
||||
+/// BitDouble is 2 bits at minimum (the 0.0 / 1.0 forms), so a 3BD vertex is 6
|
||||
+/// and a BitLong is 2.
|
||||
+fn stream_bounded_count(reader: &DwgMergedReader, raw: i32, min_bits_per_item: i64) -> i32 {
|
||||
+ let raw = raw.max(0) as i64;
|
||||
+ let total_bits = (reader.main().data_len() as i64).saturating_mul(8);
|
||||
+ // The handle stream follows the main stream; anything past its start is not
|
||||
+ // array data. handle_start() is 0 when the framing did not supply it.
|
||||
+ let handle_start = reader.handle_start();
|
||||
+ let end_bits = if handle_start > 0 { handle_start.min(total_bits) } else { total_bits };
|
||||
+ let remaining = (end_bits - reader.position_in_bits()).max(0);
|
||||
+ raw.min(remaining / min_bits_per_item.max(1)).min(i32::MAX as i64) as i32
|
||||
+}
|
||||
+
|
||||
pub fn read_mesh(reader: &mut DwgMergedReader) -> MeshData {
|
||||
let version = reader.read_bit_short();
|
||||
let blend_crease = reader.read_bit();
|
||||
let subdivision_level = reader.read_bit_long();
|
||||
|
||||
- let num_verts = safe_count(reader.read_bit_long());
|
||||
+ let raw_verts = reader.read_bit_long();
|
||||
+ let num_verts = stream_bounded_count(reader, raw_verts, 6);
|
||||
let mut vertices = Vec::with_capacity(num_verts as usize);
|
||||
for _ in 0..num_verts { vertices.push(reader.read_3bit_double()); }
|
||||
|
||||
- let total_face_data = safe_count(reader.read_bit_long());
|
||||
+ let raw_face_data = reader.read_bit_long();
|
||||
+ let total_face_data = stream_bounded_count(reader, raw_face_data, 2);
|
||||
let mut faces = Vec::new();
|
||||
let mut i = 0;
|
||||
while i < total_face_data {
|
||||
- let n = safe_count(reader.read_bit_long());
|
||||
+ let raw_n = reader.read_bit_long();
|
||||
+ let n = stream_bounded_count(reader, raw_n, 2);
|
||||
i += 1;
|
||||
let mut face = Vec::new();
|
||||
for _ in 0..n {
|
||||
@@ -1705,7 +1734,8 @@
|
||||
faces.push(face);
|
||||
}
|
||||
|
||||
- let num_edges = safe_count(reader.read_bit_long());
|
||||
+ let raw_edges = reader.read_bit_long();
|
||||
+ let num_edges = stream_bounded_count(reader, raw_edges, 4);
|
||||
let mut edges = Vec::with_capacity(num_edges as usize);
|
||||
for _ in 0..num_edges {
|
||||
let s = reader.read_bit_long();
|
||||
@@ -1713,7 +1743,8 @@
|
||||
edges.push((s, e));
|
||||
}
|
||||
|
||||
- let num_creases = safe_count(reader.read_bit_long());
|
||||
+ let raw_creases = reader.read_bit_long();
|
||||
+ let num_creases = stream_bounded_count(reader, raw_creases, 2);
|
||||
let mut crease_values = Vec::with_capacity(num_creases as usize);
|
||||
for _ in 0..num_creases { crease_values.push(reader.read_bit_double()); }
|
||||
|
||||
@@ -2350,14 +2381,31 @@
|
||||
pub fn read_ole2frame(reader: &mut DwgMergedReader, version: DwgVersion) -> Ole2FrameData {
|
||||
let ver = reader.read_bit_short();
|
||||
let mode = if version.r2000_plus() { reader.read_bit_short() } else { 0 };
|
||||
- // OLE binary data can be very large (embedded images/documents),
|
||||
- // so don't use safe_count (100 KB cap). Use a generous 10 MB cap instead.
|
||||
let data_len = (reader.read_bit_long().max(0) as usize).min(10_000_000);
|
||||
let data = reader.read_bytes(data_len);
|
||||
let trailing_byte = if version.r2000_plus() { reader.read_byte() } else { 3 };
|
||||
- let upper_left = reader.read_3bit_double();
|
||||
- let lower_right = reader.read_3bit_double();
|
||||
+
|
||||
+ let mut upper_left = Vector3::new(0.0, 0.0, 0.0);
|
||||
+ let mut lower_right = Vector3::new(0.0, 0.0, 0.0);
|
||||
+
|
||||
+ // Extract 4-corner coordinates stored in the OLE binary data header starting at offset 2
|
||||
+ if data.len() >= 66 {
|
||||
+ let ul_x = f64::from_le_bytes(data[2..10].try_into().unwrap());
|
||||
+ let ul_y = f64::from_le_bytes(data[10..18].try_into().unwrap());
|
||||
+ let ul_z = f64::from_le_bytes(data[18..26].try_into().unwrap());
|
||||
+ let lr_x = f64::from_le_bytes(data[50..58].try_into().unwrap());
|
||||
+ let lr_y = f64::from_le_bytes(data[58..66].try_into().unwrap());
|
||||
+ let lr_z = if data.len() >= 74 { f64::from_le_bytes(data[66..74].try_into().unwrap()) } else { 0.0 };
|
||||
+
|
||||
+ if ul_x.is_finite() && ul_y.is_finite() && lr_x.is_finite() && lr_y.is_finite()
|
||||
+ && (ul_x != 0.0 || ul_y != 0.0 || lr_x != 0.0 || lr_y != 0.0) {
|
||||
+ upper_left = Vector3::new(ul_x, ul_y, if ul_z.is_finite() { ul_z } else { 0.0 });
|
||||
+ lower_right = Vector3::new(lr_x, lr_y, if lr_z.is_finite() { lr_z } else { 0.0 });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
let ole_type = if version.r2000_plus() { reader.read_byte() } else { 2 };
|
||||
+
|
||||
Ole2FrameData { version: ver, mode, data, trailing_byte, upper_left, lower_right, ole_type }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under the name "LEGAL", and included in all Covered Software
|
||||
distributed in Source Code Form. Except to the extent prohibited by
|
||||
statute or regulation, such description must be sufficiently detailed
|
||||
for a recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
+86
-11
@@ -29,6 +29,7 @@ import { setDefaultFontUrl } from './viewer2d/slugText';
|
||||
import { renderPropertyInspector } from './propertyInspector';
|
||||
import { LayerPanelManager } from './layerPanel';
|
||||
import { makePanelDraggableAndCollapsible } from './panelUtils';
|
||||
import { getAllOptions, setOption } from './optionsStore';
|
||||
|
||||
// parseDxfBuffer 내부:
|
||||
// dxf-parser → HatchHandler → dxfAdapter
|
||||
@@ -162,7 +163,7 @@ async function loadAndRender(buf: ArrayBuffer, name: string): Promise<CadParseRe
|
||||
const def = pickDefaultSpace(spaces);
|
||||
viewer.load(result, { spaceHandle: def?.handleHex ?? null });
|
||||
viewer.resize();
|
||||
syncLwtBtn();
|
||||
syncOptions();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -230,17 +231,91 @@ document.getElementById('theme')!.addEventListener('click', () => {
|
||||
viewer.setTheme(dark);
|
||||
fillLayers();
|
||||
});
|
||||
// 선가중치(LWT): 처음에는 도면의 LWDISPLAY 값을 따르고, 누르면 강제 on/off.
|
||||
const lwtBtn = document.getElementById('lwt') as HTMLButtonElement;
|
||||
const syncLwtBtn = () => {
|
||||
const on = viewer.getLineweightEnabled();
|
||||
lwtBtn.classList.toggle('primary', on);
|
||||
lwtBtn.title = `선가중치 표시 ${on ? 'ON' : 'OFF'} (AutoCAD LWDISPLAY)`;
|
||||
};
|
||||
lwtBtn.addEventListener('click', () => {
|
||||
viewer.setLineweightEnabled(!viewer.getLineweightEnabled());
|
||||
syncLwtBtn();
|
||||
// ── 옵션 패널 ────────────────────────────────────────────────────
|
||||
// 값은 IndexedDB(hmw-viewer/options)에 남는다. 선가중치 배율은 재파싱 없이
|
||||
// 즉시 반영되므로 드래그하면서 화면을 보고 맞출 수 있다.
|
||||
const optionsPanel = document.getElementById('options')!;
|
||||
const optLwOn = document.getElementById('optLwOn') as HTMLInputElement;
|
||||
const optLwScale = document.getElementById('optLwScale') as HTMLInputElement;
|
||||
const optLwScaleNum = document.getElementById('optLwScaleNum') as HTMLInputElement;
|
||||
const optLwOut = document.getElementById('optLwOut')!;
|
||||
const optGrid = document.getElementById('optGrid') as HTMLInputElement;
|
||||
const optZoomSpeed = document.getElementById('optZoomSpeed') as HTMLInputElement;
|
||||
const optZoomSpeedNum = document.getElementById('optZoomSpeedNum') as HTMLInputElement;
|
||||
|
||||
makePanelDraggableAndCollapsible(optionsPanel, {
|
||||
collapseBtnEl: document.getElementById('collapseOptBtn')!,
|
||||
});
|
||||
document.getElementById('optionsBtn')!.addEventListener('click', () => {
|
||||
optionsPanel.style.display = optionsPanel.style.display === 'flex' ? 'none' : 'flex';
|
||||
});
|
||||
document.getElementById('closeOptBtn')!.addEventListener('click', () => {
|
||||
optionsPanel.style.display = 'none';
|
||||
});
|
||||
|
||||
/** 컨트롤 값을 뷰어의 현재 상태로 되돌린다(도면을 새로 열었을 때도 호출). */
|
||||
function syncOptions() {
|
||||
const s = viewer.getLineweightScale();
|
||||
optLwOn.checked = viewer.getLineweightEnabled();
|
||||
optLwScale.value = String(s);
|
||||
optLwScaleNum.value = String(s);
|
||||
const classes = viewer.getLineweightClasses();
|
||||
optLwOut.textContent = classes.length
|
||||
? classes.map((mm) => `${mm.toFixed(2)}mm → ${Math.round(mm * s * 10) / 10}px`).join(' ')
|
||||
: `이 도면에는 굵은 선이 없다 (0.50mm 였다면 ${Math.round(0.5 * s * 10) / 10}px)`;
|
||||
}
|
||||
|
||||
function applyLwScale(v: number, persist = true) {
|
||||
if (!Number.isFinite(v) || v <= 0) return;
|
||||
viewer.setLineweightScale(v);
|
||||
if (persist) void setOption('lineweightScale', v);
|
||||
syncOptions();
|
||||
}
|
||||
optLwScale.addEventListener('input', () => applyLwScale(parseFloat(optLwScale.value)));
|
||||
optLwScaleNum.addEventListener('input', () => applyLwScale(parseFloat(optLwScaleNum.value)));
|
||||
|
||||
optLwOn.addEventListener('change', () => {
|
||||
viewer.setLineweightEnabled(optLwOn.checked);
|
||||
void setOption('lineweightOn', optLwOn.checked);
|
||||
syncOptions();
|
||||
});
|
||||
|
||||
optGrid.addEventListener('change', () => {
|
||||
viewer.setGrid(optGrid.checked);
|
||||
void setOption('grid', optGrid.checked);
|
||||
});
|
||||
|
||||
function applyZoomSpeed(v: number, persist = true) {
|
||||
if (!Number.isFinite(v) || v <= 0) return;
|
||||
viewer.setZoomSpeed(v);
|
||||
optZoomSpeed.value = String(v);
|
||||
optZoomSpeedNum.value = String(v);
|
||||
if (persist) void setOption('zoomSpeed', v);
|
||||
}
|
||||
optZoomSpeed.addEventListener('input', () => applyZoomSpeed(parseFloat(optZoomSpeed.value)));
|
||||
optZoomSpeedNum.addEventListener('input', () => applyZoomSpeed(parseFloat(optZoomSpeedNum.value)));
|
||||
|
||||
document.getElementById('optReset')!.addEventListener('click', () => {
|
||||
viewer.setLineweightScale(null); // 뷰어 기본 배율
|
||||
viewer.setLineweightEnabled(null); // 도면의 LWDISPLAY 를 다시 따름
|
||||
viewer.setGrid(false);
|
||||
applyZoomSpeed(1);
|
||||
optGrid.checked = false;
|
||||
void setOption('lineweightScale', null);
|
||||
void setOption('lineweightOn', null);
|
||||
void setOption('grid', false);
|
||||
syncOptions();
|
||||
});
|
||||
|
||||
// 저장된 옵션 복원 (IndexedDB 는 비동기라 첫 도면 로드와 경쟁하지 않도록 즉시 적용)
|
||||
void getAllOptions().then((o) => {
|
||||
if (typeof o.lineweightScale === 'number') applyLwScale(o.lineweightScale, false);
|
||||
if (typeof o.lineweightOn === 'boolean') viewer.setLineweightEnabled(o.lineweightOn);
|
||||
if (o.grid === true) { viewer.setGrid(true); optGrid.checked = true; }
|
||||
if (typeof o.zoomSpeed === 'number') applyZoomSpeed(o.zoomSpeed, false);
|
||||
syncOptions();
|
||||
});
|
||||
syncOptions();
|
||||
document.getElementById('layersBtn')!.addEventListener('click', () => {
|
||||
layersPanel.style.display = layersPanel.style.display === 'flex' ? 'none' : 'flex';
|
||||
});
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 뷰어 옵션 영구 저장소 (IndexedDB).
|
||||
*
|
||||
* localStorage 대신 IndexedDB 를 쓰는 이유: 값이 문자열로 강제되지 않고(숫자/불린/
|
||||
* 객체 그대로), 용량 제한이 사실상 없으며, 나중에 도면별 설정처럼 구조가 커져도
|
||||
* 같은 저장소를 그대로 쓸 수 있다.
|
||||
*
|
||||
* DB 하나(`hmw-viewer`) 안에 key-value 스토어(`options`) 하나만 둔다.
|
||||
* 실패(사생활 보호 모드, 스토리지 차단 등)해도 앱이 죽지 않도록 모든 API 는
|
||||
* 조용히 기본값으로 떨어진다.
|
||||
*/
|
||||
|
||||
const DB_NAME = 'hmw-viewer';
|
||||
const DB_VERSION = 1;
|
||||
const STORE = 'options';
|
||||
|
||||
let dbPromise: Promise<IDBDatabase | null> | null = null;
|
||||
|
||||
function openDb(): Promise<IDBDatabase | null> {
|
||||
if (dbPromise) return dbPromise;
|
||||
dbPromise = new Promise((resolve) => {
|
||||
if (typeof indexedDB === 'undefined') return resolve(null);
|
||||
let req: IDBOpenDBRequest;
|
||||
try {
|
||||
req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
} catch {
|
||||
return resolve(null);
|
||||
}
|
||||
req.onupgradeneeded = () => {
|
||||
const db = req.result;
|
||||
if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);
|
||||
};
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => resolve(null);
|
||||
req.onblocked = () => resolve(null);
|
||||
});
|
||||
return dbPromise;
|
||||
}
|
||||
|
||||
function tx<T>(mode: IDBTransactionMode, run: (store: IDBObjectStore) => IDBRequest): Promise<T | null> {
|
||||
return openDb().then(
|
||||
(db) =>
|
||||
new Promise<T | null>((resolve) => {
|
||||
if (!db) return resolve(null);
|
||||
try {
|
||||
const t = db.transaction(STORE, mode);
|
||||
const req = run(t.objectStore(STORE));
|
||||
req.onsuccess = () => resolve(req.result as T);
|
||||
req.onerror = () => resolve(null);
|
||||
} catch {
|
||||
resolve(null);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/** 저장된 옵션 하나. 없으면 `fallback`. */
|
||||
export async function getOption<T>(key: string, fallback: T): Promise<T> {
|
||||
const v = await tx<T>('readonly', (s) => s.get(key));
|
||||
return v === null || v === undefined ? fallback : v;
|
||||
}
|
||||
|
||||
/** 옵션 하나 저장. 저장이 막혀 있어도 예외를 던지지 않는다. */
|
||||
export async function setOption(key: string, value: unknown): Promise<void> {
|
||||
await tx('readwrite', (s) => s.put(value, key));
|
||||
}
|
||||
|
||||
/** 전체 옵션을 한 번에 읽는다 (기동 시 1회 호출용). */
|
||||
export async function getAllOptions(): Promise<Record<string, unknown>> {
|
||||
const keys = (await tx<IDBValidKey[]>('readonly', (s) => s.getAllKeys())) ?? [];
|
||||
const vals = (await tx<unknown[]>('readonly', (s) => s.getAll())) ?? [];
|
||||
const out: Record<string, unknown> = {};
|
||||
keys.forEach((k, i) => { out[String(k)] = vals[i]; });
|
||||
return out;
|
||||
}
|
||||
Vendored
+7
@@ -16,9 +16,16 @@ export class Viewer2D {
|
||||
setAccent(hex: string): void;
|
||||
setTheme(dark: boolean): void;
|
||||
setGrid(visible: boolean): void;
|
||||
setZoomSpeed(speed: number): void;
|
||||
getZoomSpeed(): number;
|
||||
/** Lineweight display (AutoCAD LWDISPLAY); null follows the drawing's own flag. */
|
||||
setLineweightEnabled(on: boolean | null): void;
|
||||
getLineweightEnabled(): boolean;
|
||||
/** Lineweight display scale in px per mm (AutoCAD's display-scale slider). Default 20. */
|
||||
setLineweightScale(pxPerMm: number | null): void;
|
||||
getLineweightScale(): number;
|
||||
/** Distinct lineweights (mm) present in the loaded drawing, ascending. */
|
||||
getLineweightClasses(): number[];
|
||||
getLayerInfo(): { name: string; colorHex: string; count: number; visible: boolean }[];
|
||||
setHiddenLayers(nameSet: Set<string>): void;
|
||||
/** Model / Paper (Layout) spaces for the loaded drawing. */
|
||||
|
||||
+52
-10
@@ -25,15 +25,17 @@ const CLICK_THRESHOLD_PX = 14;
|
||||
// ── Lineweight (AutoCAD LWDISPLAY) ─────────────────────────────────────────
|
||||
// DWG stores lineweight as an i16 in 1/100 mm (-1 ByLayer, -2 ByBlock,
|
||||
// -3 Default). AutoCAD shows it in *screen* pixels — the width does not grow
|
||||
// when you zoom in — at roughly 8 px per mm on the default display-scale
|
||||
// slider (0.25 mm ≈ 2 px, 2.11 mm ≈ 17 px), so that is the mapping used here.
|
||||
const LW_PX_PER_MM = 8;
|
||||
// when you zoom in. The px-per-mm factor is AutoCAD's "Adjust Display Scale"
|
||||
// slider and depends on the monitor, so it is only a default: 11 px/mm is the
|
||||
// value that matched an AutoCAD reference side by side (0.35 mm ≈ 4 px,
|
||||
// 0.50 mm ≈ 5.5 px). Retune per host/monitor with setLineweightScale().
|
||||
const LW_PX_PER_MM = 11;
|
||||
// LWDEFAULT: what -3 (and a layer that never set one) resolves to.
|
||||
const LW_DEFAULT_MM = 0.25;
|
||||
// At or below the CAD default the line stays in the 1-px hairline batch: a
|
||||
// drawing that never assigned weights must look exactly as it did before.
|
||||
const LW_HAIRLINE_MM = 0.25;
|
||||
const LW_MAX_PX = 24;
|
||||
const LW_MAX_PX = 48;
|
||||
// Fat lines cost 12 floats + 8 verts per segment (instanced quads). Past this
|
||||
// many segments in one weight bucket, fall back to hairline instead of risking
|
||||
// a GPU/heap stall on huge survey drawings.
|
||||
@@ -105,6 +107,7 @@ export class Viewer2D {
|
||||
this._lwForced = null;
|
||||
this._lwDisplay = false;
|
||||
this._curLwPx = 0;
|
||||
this._lwScale = null; // null = LW_PX_PER_MM (setLineweightScale)
|
||||
SlugTextEngine.shared().catch(() => {}); // warm the font load; sprite fallback covers failure
|
||||
|
||||
this._scene = new THREE.Scene();
|
||||
@@ -466,7 +469,11 @@ export class Viewer2D {
|
||||
const key = `${curDash ? curDash.key : ''}#${lwPx}`;
|
||||
let bk = segBuckets.get(key);
|
||||
if (!bk) {
|
||||
bk = { dash: curDash?.dash ?? 0, gap: curDash?.gap ?? 0, lwPx, verts: [], colors: [], segLayer: [], entIdx: -2 };
|
||||
// lwMm is kept so setLineweightScale() can retune the material width
|
||||
// without rebuilding the scene (the hairline cut-off is in mm, so
|
||||
// bucket membership does not depend on the scale).
|
||||
bk = { dash: curDash?.dash ?? 0, gap: curDash?.gap ?? 0, lwPx, lwMm: this._curLwMm || 0,
|
||||
verts: [], colors: [], segLayer: [], entIdx: -2 };
|
||||
segBuckets.set(key, bk);
|
||||
}
|
||||
// Selection highlight recolors an entity's own vertices. Its colors no
|
||||
@@ -1205,7 +1212,7 @@ export class Viewer2D {
|
||||
this._sizeLineMaterial(fmat);
|
||||
const fline = new LineSegments2(fgeom, fmat);
|
||||
if (bk.dash > 0) fline.computeLineDistances();
|
||||
this._registerFat(fline, bk.segLayer);
|
||||
this._registerFat(fline, bk.segLayer, bk.lwMm);
|
||||
bk.rt = this._fatBufs[this._fatBufs.length - 1];
|
||||
this._group.add(fline);
|
||||
} else {
|
||||
@@ -1340,7 +1347,7 @@ export class Viewer2D {
|
||||
* into the front of the instance buffers and drops `instanceCount`.
|
||||
* The untouched originals are kept so any later mask starts from clean data.
|
||||
*/
|
||||
_registerFat(mesh, segLayerArr) {
|
||||
_registerFat(mesh, segLayerArr, lwMm = 0) {
|
||||
const geo = mesh.geometry;
|
||||
const posBuf = geo.attributes.instanceStart?.data;
|
||||
const colBuf = geo.attributes.instanceColorStart?.data;
|
||||
@@ -1348,7 +1355,7 @@ export class Viewer2D {
|
||||
posBuf.setUsage(THREE.DynamicDrawUsage);
|
||||
colBuf?.setUsage(THREE.DynamicDrawUsage);
|
||||
this._fatBufs.push({
|
||||
mesh,
|
||||
mesh, lwMm,
|
||||
segLayer: Int32Array.from(segLayerArr),
|
||||
posBuf, colBuf,
|
||||
posSrc: Float32Array.from(posBuf.array),
|
||||
@@ -1377,6 +1384,34 @@ export class Viewer2D {
|
||||
/** Whether lineweights are currently being drawn. */
|
||||
getLineweightEnabled() { return this._lwDisplay; }
|
||||
|
||||
/**
|
||||
* Display scale in screen pixels per mm of lineweight — AutoCAD's
|
||||
* "Adjust Display Scale" slider. Default 20 (1 px per 0.05 mm).
|
||||
*
|
||||
* Applied to the live materials, not by rebuilding: the hairline cut-off is
|
||||
* a millimetre value, so changing the scale moves every weight by the same
|
||||
* factor without changing which bucket a segment belongs to. Retuning is
|
||||
* therefore instant even on a 37 MB drawing.
|
||||
*
|
||||
* @param {number|null} pxPerMm null restores the default.
|
||||
*/
|
||||
setLineweightScale(pxPerMm) {
|
||||
this._lwScale = (pxPerMm > 0) ? pxPerMm : null;
|
||||
const scale = this.getLineweightScale();
|
||||
for (const buf of this._fatBufs) {
|
||||
if (!(buf.lwMm > 0)) continue;
|
||||
buf.mesh.material.linewidth = Math.min(LW_MAX_PX, Math.round(buf.lwMm * scale * 10) / 10);
|
||||
}
|
||||
}
|
||||
|
||||
/** Current lineweight display scale (px per mm). */
|
||||
getLineweightScale() { return this._lwScale || LW_PX_PER_MM; }
|
||||
|
||||
/** Lineweights present in the current drawing, in mm (ascending). */
|
||||
getLineweightClasses() {
|
||||
return [...new Set(this._fatBufs.map((b) => b.lwMm).filter((m) => m > 0))].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/** Track an entity-owned object so layer toggles can flip its visibility. */
|
||||
_addObj(obj, layer) {
|
||||
const key = layer ?? this._curLayer;
|
||||
@@ -2321,10 +2356,15 @@ export class Viewer2D {
|
||||
* assigned a weight render exactly as before.
|
||||
*/
|
||||
_lwPx(entity, inheritedPx = 0) {
|
||||
this._curLwMm = 0;
|
||||
if (!this._lwDisplay) return 0;
|
||||
let raw = entity?.entityHeader?.lineWeight ?? entity?.lineWeight ?? entity?.lineweight;
|
||||
if (typeof raw !== 'number') return 0;
|
||||
if (raw === -2) return inheritedPx; // ByBlock
|
||||
if (raw === -2) { // ByBlock
|
||||
const scale = this.getLineweightScale();
|
||||
this._curLwMm = inheritedPx > 0 ? inheritedPx / scale : 0;
|
||||
return inheritedPx;
|
||||
}
|
||||
if (raw === -1) { // ByLayer
|
||||
const lh = entity.layerHandle?.value ?? entity.layerHandle;
|
||||
const byHandle = lh != null ? this._layerLwByHandle.get(String(lh)) : undefined;
|
||||
@@ -2334,7 +2374,9 @@ export class Viewer2D {
|
||||
}
|
||||
const mm = raw === -3 ? LW_DEFAULT_MM : raw / 100;
|
||||
if (!(mm > LW_HAIRLINE_MM)) return 0;
|
||||
return Math.min(LW_MAX_PX, Math.round(mm * LW_PX_PER_MM * 10) / 10);
|
||||
this._curLwMm = mm;
|
||||
const scale = this.getLineweightScale();
|
||||
return Math.min(LW_MAX_PX, Math.round(mm * scale * 10) / 10);
|
||||
}
|
||||
|
||||
// Active-viewport VIEWTWIST (radians). The sheet is un-twisted by rotating the
|
||||
|
||||
Reference in new issue
Block a user