- CLAUDE.md: 프로젝트 전체 가이드 및 워크플로우 정의 - CONSIDERATIONS.md: 8개 영역 고려사항 분석 (기술, 품질, 배포, UX) - PROGRESS.md: 진행 상황 추적 및 체크리스트 - PLAN.md: 단계 5~9 상세 계획 및 에이전트 역할 정의 - 메모리: 프로젝트 정보 저장 다음: 단계 5 (에이전트/커맨드/스킬/훅 아이디어 제안)
525 lines
23 KiB
Markdown
525 lines
23 KiB
Markdown
# 2D → 3D 제작 과정
|
||
|
||
DWG 도면 한 벌을 3D 솔리드로 세우기까지 **실제로 무엇을 어떤 순서로 했는지**를
|
||
기록한다. 다른 프로젝트로 옮길 때 이 문서만 따라가면 재현된다.
|
||
|
||
- 판단의 근거·함정·교훈은 [`2d-3d-conversion.md`](2d-3d-conversion.md) 에 따로 있다.
|
||
이 문서는 **절차**, 그 문서는 **왜 그렇게 하는가**.
|
||
- 코드: [`src/`](../src/)
|
||
- 대상: `drawings-src/04manhole1-simplify.dwg` (맨홀 #1 및 출입구 일반도, 4매)
|
||
|
||
---
|
||
|
||
## 0. 준비물
|
||
|
||
| 항목 | 이 프로젝트에서의 위치 | 옮길 때 확인할 것 |
|
||
|---|---|---|
|
||
| DWG 파서 | `vendor/acadrust-dwg/` (wasm, read-only) | wasm 파일 2개(`.js`, `_bg.wasm`)를 같이 옮긴다 |
|
||
| Node | 18+ (ESM, top-level await 사용) | — |
|
||
| 3D 툴 | EGModeler + `egm` CLI (`D:\GiteaAll\EG-BIM_Modeler`) | 앱이 **실행 중**이어야 한다. `egm status` 로 확인 |
|
||
| Python | 3.10+ (`src/run_egm.py`) | 표준 라이브러리만 쓴다 |
|
||
|
||
`egm` 은 Named Pipe 로 실행 중인 EGModeler 에 명령을 보내는 CLI 다.
|
||
연결 확인:
|
||
|
||
```sh
|
||
egm status # {"command":null,"modelCount":0,"selectedCount":0}
|
||
```
|
||
|
||
`EGModeler가 실행 중이지 않거나 응답하지 않습니다.` 가 나오면 앱을 먼저 띄운다.
|
||
|
||
**`status` 가 응답한다고 모델링이 되는 건 아니다.** 뷰어 모드면 모델링 명령이
|
||
등록조차 안 된 채로 파이프만 살아 있다. 등록된 명령 개수로 가른다.
|
||
|
||
```sh
|
||
egm guide | python -c "import sys,json;print(len(json.load(sys.stdin)))"
|
||
# 39 → 뷰어 모드. Polyline·Extrude·Boolean* 이 통째로 없다. 모델링 불가
|
||
# 207 → 정상
|
||
```
|
||
|
||
39 이 나오면 모델러 UI 에서 **Edit Mode** 로 바꾼다 (View Mode / Edit Mode 토글).
|
||
Assistive Features 의 `ModelerMode` 와는 다른 값이다. 자세한 배경은
|
||
[`2d-3d-conversion.md`](2d-3d-conversion.md) 교훈 #15.
|
||
|
||
---
|
||
|
||
## 1. 전체 흐름
|
||
|
||
```
|
||
DWG ─parse─→ 엔티티 ─segment─→ 뷰 ─datum─→ 프로젝트좌표 ─loops─→ 프로파일
|
||
│
|
||
조립 스펙 (IR, JSON)
|
||
↙ ↓ ↘
|
||
egm IFC three.js
|
||
```
|
||
|
||
명령 4개로 끝난다.
|
||
|
||
```sh
|
||
cd src
|
||
|
||
node src/run.mjs manhole1 # ① 파싱 · 기준점 · 뷰 분리 → out/manhole1.views.json
|
||
node src/build.mjs manhole1 # ② 프로파일 추출 · 조립 → out/manhole1.ir.json
|
||
node src/emit/egm.mjs out/manhole1.ir.json # ③ 모델러 명령으로 번역 → out/manhole1.egm.json
|
||
python src/run_egm.py out/manhole1.egm.json # ④ 실행
|
||
```
|
||
|
||
`src/run.mjs` 는 검증이 깨지면 **종료 코드 1** 을 낸다. CI 게이트로 바로 쓸 수 있다.
|
||
|
||
---
|
||
|
||
## 2. 단계별 절차
|
||
|
||
### ① 파싱 · 기준점 · 뷰 분리 — `src/run.mjs`
|
||
|
||
```sh
|
||
node src/run.mjs manhole1 # 산출물까지 저장
|
||
node src/run.mjs manhole1 --report # 검증표만 출력
|
||
```
|
||
|
||
출력 예:
|
||
|
||
```
|
||
도면 manhole1 — R2018, 엔티티 7035
|
||
|
||
표고 기준선 DWG y = 483623.47666155506 (뷰 11곳에 반복, 총연장 61492mm)
|
||
✓ B-B 구체 상단 실측 -4907 도면 -4907
|
||
✓ B-B 기초 하단 실측 -7757 도면 -7757
|
||
✓ B-B 잡석 하단 실측 -8057 도면 -8057
|
||
✓ D-D 구체 상단 실측 -4907 도면 -4907
|
||
|
||
뷰 kind 조각 선 폭mm 높이mm Z 범위
|
||
평 면 도 plan 1 72 20200 10200 -1478 ~ -11678 ✓
|
||
단 면 A-A long 2 123 20200 10839 232 ~ -10607
|
||
...
|
||
✓ 검증 통과 — 뷰 분리와 표고 기준이 도면 치수와 일치
|
||
```
|
||
|
||
**이 단계를 통과하지 못하면 다음으로 가지 않는다.** 여기서 틀린 채로 3D 를
|
||
세우면 어디가 원인인지 알 수 없게 된다.
|
||
|
||
문제가 나오면:
|
||
|
||
| 증상 | 확인 도구 |
|
||
|---|---|
|
||
| 뷰가 없다 / 크기가 안 맞는다 | `node src/inspect.mjs manhole1 "<뷰>"` — 그 영역의 모든 엔티티를 레이어 무관하게 훑는다 |
|
||
| 기준점 검증 실패 | `datum.checks` 의 레이어 이름이 맞는지, 계획고 선이 `layers.ground` 에 잡히는지 |
|
||
|
||
### ② 프로파일 추출 · 조립 — `src/build.mjs`
|
||
|
||
```sh
|
||
node src/build.mjs manhole1 --loops # 뷰별 폐곡선 목록 (조립 스펙 작성용)
|
||
node src/build.mjs manhole1 # IR 생성
|
||
```
|
||
|
||
`--loops` 출력이 조립 스펙을 쓰는 **재료 목록**이다.
|
||
|
||
```
|
||
단 면 B-B (cross)
|
||
# 0 2700× 2750 bbox[-1350..1350, -7657..-4907] 꼭짓점 4 부모 -
|
||
# 1 2100× 2150 bbox[-1050..1050, -7357..-5207] 꼭짓점 10 부모 #0
|
||
# 2 4300× 300 bbox[-2150..2150, -8057..-7757] 꼭짓점 4 부모 -
|
||
# 3 2900× 100 bbox[-1450..1450, -7757..-7657] 꼭짓점 4 부모 -
|
||
```
|
||
|
||
폭×높이가 도면 표기 치수와 일치하는지 먼저 눈으로 확인한다. 여기가 맞으면
|
||
그 뒤는 거의 틀리지 않는다.
|
||
|
||
한 뷰만 자세히 보려면:
|
||
|
||
```sh
|
||
node src/probe.mjs manhole1 "단 면 B-B" # 표
|
||
node src/probe.mjs manhole1 "단 면 B-B" --ring # 꼭짓점 좌표까지
|
||
```
|
||
|
||
### ③ 모델러 명령으로 번역 — `src/emit/egm.mjs`
|
||
|
||
IR 의 연산을 egm 명령으로 바꾼다. 여기서만 egm 의 사정을 안다.
|
||
|
||
```
|
||
extrude → Polyline(닫힌 커브) → SelByUids → Extrude C=True D F {벡터}
|
||
loft → 단면마다 Polyline → SelByUids(단면 순서대로) → Loft C=True Enter
|
||
box → Box {대각1} {대각2} {높이}
|
||
difference → SelByUids(base) → SelByUids(A=true, cutters) → BooleanDifference
|
||
union → SelByUids(첫째) → SelByUids(A=true, 나머지) → BooleanUnion
|
||
```
|
||
|
||
`Loft` 는 기본이 **서피스**라 `C=True`(Cap)로 막아야 솔리드가 나온다.
|
||
단면 커브는 `L`(DeleteOriginal, 기본 true)로 자동 삭제된다. 별도 `Cap` 명령은
|
||
제자리 수정이라 새 uid 가 안 나오므로 여기서는 못 쓴다.
|
||
|
||
### ④ 실행 — `src/run_egm.py`
|
||
|
||
```sh
|
||
python src/run_egm.py out/manhole1.egm.json # 실행
|
||
python src/run_egm.py out/manhole1.egm.json --dry # 보내지 않고 명령만 출력
|
||
```
|
||
|
||
`--dry` 로 좌표를 먼저 검산하는 습관을 들이면 모델러를 왕복하지 않아도 된다.
|
||
|
||
```
|
||
[ 0] duct.solid — extrude
|
||
egm ~Polyline 0,-1.9,-7.757 0,1.9,-7.757 0,1.9,-3.157 0,-1.9,-3.157 0,-1.9,-7.757
|
||
egm ~SelByUids A=false <duct.solid$crv> Esc
|
||
egm ~Extrude C=True D F 0,0,0 15.4,0,0
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 새 도면을 추가하는 절차
|
||
|
||
**코드는 건드리지 않는다.** `src/drawings/` 에 파일 하나만 추가한다.
|
||
|
||
### 3.1 뼈대 만들기
|
||
|
||
`src/drawings/manhole1.mjs` 를 복사해 이름을 바꾸고 `dwg` 경로를 고친다.
|
||
`layers` / `titles.views` 는 일단 대충 채우고 ①을 돌린다.
|
||
|
||
### 3.2 레이어 알아내기
|
||
|
||
```sh
|
||
node src/run.mjs <도면> --report # 일단 실패해도 좋다
|
||
node src/inspect.mjs <도면> "<뷰>" # 그 영역의 레이어 실태
|
||
```
|
||
|
||
`src/inspect.mjs` 는 레이어 필터를 무시하고 전부 보여준다. 여기서
|
||
구조체 외곽선이 어느 레이어에 있는지 눈으로 고른다.
|
||
|
||
### 3.3 레이어를 세 갈래로 나눈다
|
||
|
||
```js
|
||
layers: {
|
||
struct: /…/, // 뷰 분리용. 넉넉하게. 가구·주기까지 들어가도 된다
|
||
body: /…/, // 구체 프로파일용. 좁게. 케이블랙 같은 가구는 빼야 한다
|
||
material: /…/, // 기초·잡석·보도 등 별개 재료. 프로파일 추출엔 함께 넣는다
|
||
ground: /…/, // 계획고·GL 선. 표고 기준 검출용
|
||
}
|
||
```
|
||
|
||
`body ⊆ struct` 는 코드가 강제한다(`src/run.mjs`). 어겨도 조용히 틀리지 않는다.
|
||
|
||
### 3.4 표고 기준 잡기
|
||
|
||
```js
|
||
datum: {
|
||
levelLabel: /도로\s*계획고/, // 기준선을 찾는 힌트. 값은 선에서 가져온다
|
||
checks: [ // 도면 치수와 대조. 최소 2개
|
||
['B-B 구체 상단', (v) => v['단 면 B-B']?.layerZ['-전력구']?.[1], -4907],
|
||
['B-B 기초 하단', (v) => v['단 면 B-B']?.layerZ['!-기초']?.[0], -7757],
|
||
],
|
||
}
|
||
```
|
||
|
||
`layerZ[레이어] = [최저 Z, 최고 Z]`. 도면에 적힌 치수를 그대로 넣는다.
|
||
|
||
### 3.5 뷰별 앵커 잡기
|
||
|
||
각 뷰의 u(가로) 좌표 중 무엇이 프로젝트 0 인지 정한다.
|
||
`--loops` 로 폐곡선 bbox 를 보고 도면 치수와 맞춰가며 찾는다.
|
||
|
||
```js
|
||
anchors: {
|
||
'평 면 도': { u0: 1000 }, // u → X
|
||
'단 면 B-B': { u0: 2150 }, // u → Y (중심선)
|
||
'단 면 F-F': { u0: -14700 }, // XZ 단면
|
||
'단 면 H-H': { u0: 1900, v0: -17125, swapUV: true }, // 수평 단면
|
||
}
|
||
```
|
||
|
||
| 옵션 | 뜻 |
|
||
|---|---|
|
||
| `u0` | 가로축 원점. `프로젝트 = u - u0` |
|
||
| `v0` | 세로축 원점. 수직 단면은 생략(표고가 이미 기준) |
|
||
| `flip` / `flipV` | 축 방향이 반대일 때 |
|
||
| `swapUV` | 도면 가로축이 프로젝트 Y, 세로축이 X 일 때 (수평 단면) |
|
||
|
||
### 3.6 조립 스펙 쓰기
|
||
|
||
파이프라인에서 **사람 판단이 들어가는 유일한 자리**다.
|
||
|
||
```js
|
||
assemble({ loop, profile, part, rect, expect }) {
|
||
profile('duct.outer', 'YZ', loop('단 면 B-B', 2700, 2750).ring);
|
||
part({ id: 'duct.solid', op: 'extrude', profile: 'duct.outer', axis: 'X',
|
||
from: 0, to: 15400, cap: true, keep: false });
|
||
...
|
||
}
|
||
```
|
||
|
||
| 도우미 | 용도 |
|
||
|---|---|
|
||
| `loop(뷰, 폭, 높이)` | 그 치수의 폐곡선을 집는다. 0개나 2개 이상이면 예외 |
|
||
| `profile(id, 평면, ring)` | 프로파일 등록. 평면은 `'XY' \| 'XZ' \| 'YZ'` |
|
||
| `part({...})` | 파트 추가 |
|
||
| `rect(u0,v0,u1,v1)` | 직사각 프로파일. 폐곡선이 인접 형상과 붙어 못 쓸 때 |
|
||
| `expect(뷰, 폭, 높이, 이유)` | 그 폐곡선이 실재하는지 확인만. `rect` 값 교차검증용 |
|
||
|
||
IR 연산:
|
||
|
||
| op | 필드 |
|
||
|---|---|
|
||
| `box` | `min:[x,y,z]`, `max:[x,y,z]` |
|
||
| `extrude` | `profile`, `axis`, `from`, `to`, `cap` |
|
||
| `union` | `members:[id,…]` |
|
||
| `difference` | `base`, `cutters:[id,…]` |
|
||
| `loft` | `sections:[{profile, at},…]` — `at` 은 평면 법선축 위 위치 |
|
||
|
||
**loft 는 꺾인 자리를 못 만든다.** 두 단면 사이를 곧게 이을 뿐이라, 형상이
|
||
꺾이는 X 마다 단면을 놓고 구간을 끊어야 한다. 단면은 2개씩만 쓴다 — 3개
|
||
이상이면 스플라인 보간으로 부푼다. 두 단면은 **꼭짓점 수가 같아야** 하고,
|
||
링 정규화는 `addProfile` 이 자동으로 한다. 자세한 배경은
|
||
[`2d-3d-conversion.md`](2d-3d-conversion.md) 교훈 #16.
|
||
|
||
`keep: false` 는 boolean 에 소모될 중간 파트. 최종 산출물에만 `name`/`color` 를 준다.
|
||
|
||
---
|
||
|
||
## 4. 이 도면에서 실제로 만든 것
|
||
|
||
### 4.1 좌표계
|
||
|
||
```
|
||
X = 전력구 길이방향, 0 = 구조물 최좌단
|
||
Y = 폭방향, 0 = 전력구 중심선
|
||
Z = 표고, 0 = 도로 계획고 (DWG y = 483623.477, 자동 검출)
|
||
단위 = 내부 mm 정수, egm 넘길 때만 m
|
||
```
|
||
|
||
### 4.2 구간 구성 (A-A 종단면에서 읽음)
|
||
|
||
```
|
||
X -1,000 ~ 0 입구 스터브 B-B 구체 2,700×2,750
|
||
X 0 ~ 15,400 전력구 본체 C-C 구체 3,800×4,600 / 내부 3,000×3,800
|
||
X 1,900 ~ 3,200 사다리 샤프트 1 Z -3,157 ~ 0
|
||
X 12,600 ~ 13,900 사다리 샤프트 2 Z -3,157 ~ -39
|
||
X 15,400 ~ 18,200 맨홀 #2 Z -10,007 ~ +182, Y -1,900 ~ 8,300
|
||
X 18,200 ~ 19,200 출구 스터브 B-B
|
||
```
|
||
|
||
### 4.3 뷰 ↔ 부재 대응
|
||
|
||
| 뷰 | 평면 | 쓰인 곳 |
|
||
|---|---|---|
|
||
| 평 면 도 | XY | X 구간 확정, 샤프트 위치, 환기구 footprint |
|
||
| 단 면 A-A | XZ | **구간 배정표** — 어디서 어디까지 어느 단면인지 |
|
||
| 단 면 B-B / D-D | YZ | 스터브 단면 (2,700×2,750) |
|
||
| 단 면 C-C | YZ | 전력구 본체 단면 (3,800×4,600) |
|
||
| 단 면 E-E | YZ | 맨홀 전체 (챔버·격실·환기구 다리) |
|
||
| 단 면 F-F / G-G | XZ | 환기구 X 벽, 기초·잡석 |
|
||
| 단 면 H-H | XY | 환기 OPEN부 개구 (1.2×2.0) |
|
||
| 단 면 I-I | XY | 집수정 점검구 개구 (1.2×1.0) |
|
||
| 단 면 J-J | XY | 침사조·집수조 평면 (E-E 교차검증) |
|
||
|
||
### 4.4 최종 산출 파트
|
||
|
||
```
|
||
● 맨홀1_전력구_구체 스터브+본체 union 후 공동 차감
|
||
● 맨홀1_전력구_기초 4,000 × 100 fck=18MPa
|
||
● 맨홀1_전력구_잡석 5,400 × 500
|
||
● 맨홀1_전력구_출구스터브
|
||
● 맨홀1_사다리샤프트_1 / _2
|
||
● 맨홀1_맨홀2_구체 봉투 union 후 공동·개구부 차감
|
||
● 맨홀1_맨홀2_기초 / _잡석
|
||
● 맨홀1_환기구_기초 / _잡석
|
||
```
|
||
|
||
트리 이름은 `대분류_중분류_소분류` 형식이라 ObjectTreeViewer 에서 자동 그룹화된다.
|
||
|
||
---
|
||
|
||
## 5. 검증 절차
|
||
|
||
3D 를 세우기 **전에** 아래를 순서대로 통과시킨다. 한 단계라도 건너뛰면
|
||
나중에 어디가 원인인지 추적이 불가능해진다.
|
||
|
||
```
|
||
0. egm guide | wc -l 뷰어 모드가 아닌가 (39 면 뷰어 — 모델링 불가)
|
||
1. node src/run.mjs <도면> 지반선 Z 가 전 뷰에서 일치하는가
|
||
기준점이 도면 치수 2개 이상과 맞는가
|
||
뷰 폭·높이가 도면 치수와 맞는가
|
||
2. node src/gaps.mjs <도면> 프로파일이 안 닫히는 곳이 있는가
|
||
원인이 레이어 오분류인가 선 누락인가
|
||
3. node src/build.mjs <도면> --loops
|
||
폐곡선 치수가 도면 표기와 맞는가
|
||
node src/build.mjs <도면> --circles
|
||
원 지름이 도면 표기(Ø900·Ø100)와 맞는가
|
||
node src/report.mjs <도면> → out/<도면>.html
|
||
**자기교차 링이 있는가** (있으면 압출부터 쓰레기다)
|
||
링을 그림으로 확인. 표로는 못 보이는 것이 보인다
|
||
4. node src/build.mjs <도면> IR 검증 (참조 무결성, 소모 파트 keep 플래그)
|
||
5. python run_egm.py ... --dry
|
||
좌표가 예상대로 나오는가
|
||
6. python run_egm.py ... --export=out/<도면>.obj
|
||
실행 + OBJ 내보내기
|
||
7. egm status modelCount 가 최종 파트 수와 같은가
|
||
8. node src/measure.mjs <도면> --ir
|
||
의도(IR 격자 평가) vs 결과(모델러 OBJ) 대조
|
||
+ 기준선 (실패 시 exit 1)
|
||
```
|
||
|
||
7 이 어긋나면 boolean 이 인자를 흘린 것이다 (교훈 #14). 형상만 봐서는 못 잡는다 —
|
||
남은 커터 솔리드가 구체와 겹쳐 있어 겉보기엔 멀쩡하다.
|
||
|
||
### 꼭짓점 수를 먼저 본다
|
||
|
||
`--loops` 출력에서 **직사각형이어야 할 것이 4가 아니면 의심**한다. 링이 오염됐다는
|
||
신호다. 이 도면에서 실제로 나온 값:
|
||
|
||
```
|
||
꼭짓점 4 정상 직사각형
|
||
꼭짓점 10 헌치(150×150)가 있는 정상 형상
|
||
꼭짓점 8 개구 기호(X 표시)가 사각형 안을 가로질러 링이 자기교차
|
||
꼭짓점 114 케이블 지지대가 프로파일 레이어에 섞임
|
||
```
|
||
|
||
링이 오염됐으면 **bbox 는 여전히 정확하므로** `rect()` 로 재구성하고
|
||
`expect()` 로 치수만 확인한다.
|
||
|
||
### 부피를 대조한다 — `src/measure.mjs`
|
||
|
||
비틀린 loft 는 형상만 봐서는 못 잡는다. 부피가 어긋나는 것으로만 드러난다.
|
||
|
||
```sh
|
||
python src/run_egm.py out/manhole1-trans.egm.json --export=out/manhole1-trans.obj
|
||
node src/measure.mjs manhole1-trans # 기준선 대조 (실패 시 exit 1)
|
||
node src/measure.mjs manhole1-trans --save # 지금 값을 기준선으로 얼린다
|
||
node src/measure.mjs manhole1-trans --primitives # IR 원시 파트 부피표도
|
||
```
|
||
|
||
OBJ 삼각형을 발산 정리로 잰다. `~SetObjectName` 으로 붙인 이름이 OBJ 의 `g` 로
|
||
나오므로 파트 대조가 이름 하나로 끝난다. 나오는 것:
|
||
|
||
```
|
||
파트 부피 m³ 표면적 m² 삼각형 열린변 재질
|
||
맨홀1_전력구_구체 100.5403 507.471 10862 28 구체 콘크리트 fck=27MPa
|
||
맨홀1_사다리샤프트_1 3.3217 28.356 3127 13 …
|
||
합계 107.1425
|
||
```
|
||
|
||
**IR 에 없는 그룹이 나오면 boolean 이 인자를 흘린 것이다** — `modelCount` 대조와
|
||
같은 신호인데 어느 커터가 남았는지까지 알려준다.
|
||
|
||
`열린변`은 다양체 여부다. 이음매를 맞대어 붙이면 양쪽 삼각분할이 달라 T 정점이
|
||
남는다(교훈 #16). 부피·렌더링에는 문제가 없지만 IFC 로 갈 때 걸릴 수 있다.
|
||
|
||
#### 기준선은 `baselines/` 에 두고 **커밋한다**
|
||
|
||
`out/` 과 달리 재생성으로 복구되지 않는다. 사람이 한 번 확인한 부피가 남아야
|
||
다음 변경이 형상을 건드렸는지 알 수 있다.
|
||
|
||
#### 기대값은 자동으로 안 나온다 — 그리고 그게 맞다
|
||
|
||
`measure.mjs` 는 **boolean 을 합성하지 않는다.** 커터는 관통을 확실히 하려고
|
||
대상 밖으로 `OVER` 만큼 튀어나와 있고(교훈 #13), 샤프트 보어는 본체 공동과
|
||
겹친다. 그래서 `Σ멤버` 도 `base − Σ커터` 도 틀린 값이 된다. **틀린 기대값은
|
||
기대값이 없는 것보다 나쁘다.**
|
||
|
||
새 형상의 기대값은 `--primitives` 표를 재료로 사람이 한 번 조립하고, 맞으면
|
||
`--save` 로 얼린다. 두 단면 사이 면적은 t 의 2차식이라 심프슨
|
||
`L·(A0 + 4·A(½) + A1)/6` 이 **근사가 아니라 정확**하다.
|
||
|
||
```
|
||
duct.solid 5.1975 + 1.6540 + 0.9760 + 269.1920 = 277.0195
|
||
샤프트 1 원시합 5.3299 − 보어(솔리드 범위로 클립) 2.0084 = 3.3215 실측 3.3217
|
||
└ 원기둥이 다면체로 나와 미세하게 크다
|
||
```
|
||
|
||
결과의 **정점 X 값에 중간 행이 없는지**도 같이 본다. 없으면 직선 룰드다.
|
||
|
||
### `src/gaps.mjs` 읽는 법
|
||
|
||
```
|
||
뷰 채택선 경계 레이어 누락 고치면 되는 레이어
|
||
단 면 J-J 22 0 0 0 —
|
||
단 면 E-E 69 1 3 0 잡석, 선형
|
||
```
|
||
|
||
| 열 | 뜻 | 대응 |
|
||
|---|---|---|
|
||
| 경계 | 뷰 밖으로 이어져 잘린 것 | 결함 아님. 무시 |
|
||
| 레이어 | 선은 있는데 다른 레이어에 있음 | `layers.body` 에 추가 (설정 한 줄) |
|
||
| 누락 | 도면에 선이 없음 | 도면 수정 또는 스냅 오차 확인 |
|
||
|
||
이 도면은 끊김 50곳 중 29곳이 뷰 경계, 20곳이 레이어 오분류, **선이 실제로
|
||
없는 곳은 1곳**(그것도 1mm 스냅 오차)이었다.
|
||
|
||
---
|
||
|
||
## 6. 도구 목록
|
||
|
||
| 파일 | 역할 |
|
||
|---|---|
|
||
| `src/run.mjs` | 파싱 · 기준점 검출 · 뷰 분리 · 검증 |
|
||
| `src/build.mjs` | 프로파일 추출 · 조립 스펙(IR) 생성 |
|
||
| `src/emit/egm.mjs` | IR → egm 명령 목록 |
|
||
| `src/run_egm.py` | 명령 실행 (`--dry`, `--export=<obj>` 지원) |
|
||
| `src/measure.mjs` | OBJ 부피 실측 · 기준선 대조 · 재질별 물량 |
|
||
| `src/report.mjs` | 추출 결과 뷰어 (한 장짜리 HTML). 링 그림 · 자기교차 · 원 · IR |
|
||
| `src/capture.mjs` | 뷰별 **도면** 캡처 + 좌표 사이드카 (전 레이어, `--crop`·`--png`) |
|
||
| `src/render.mjs` | 만든 **3D** 를 정투영 렌더 (`--axis`·`--part`·`--clip`·`--png`) |
|
||
| `src/volume.mjs` | IR 을 격자로 평가 — 모델러 없이 "의도" 부피 |
|
||
| `src/fit.mjs` | 도면 → `params/<이름>.json` |
|
||
| `src/params.mjs` | 파라미터 스키마·식·검증 |
|
||
| `src/rules.mjs` | 설계 규칙 (위상은 막고, 설계는 경고) |
|
||
| `src/ui.mjs` | 파라미터 편집 + 갱신 UI (로컬 서버) |
|
||
| `src/core/views.mjs` | 뷰 → 프로젝트 좌표. `build.mjs` 와 `report.mjs` 가 **같이 쓴다** |
|
||
| `src/probe.mjs` | 뷰 하나의 폐곡선을 표로 (`--ring` 으로 좌표까지) |
|
||
| `src/gaps.mjs` | 프로파일 끊김 진단 + 원인 분류 |
|
||
| `src/inspect.mjs` | 뷰 영역의 모든 엔티티를 레이어 무관하게 |
|
||
| `src/materials.mjs` | 뷰별 재질 주기 수집 · 레이어 ↔ 재질 대응 확인 |
|
||
| `src/core/parse.mjs` | DWG → 엔티티, 선분 펴기, 표제 수집 |
|
||
| `src/core/datum.mjs` | 표고 기준 검출 · 좌표 변환 |
|
||
| `src/core/segment.mjs` | 뷰 분리 (교대 간격분할 + 제목 귀속) |
|
||
| `src/core/loops.mjs` | 선분 → 닫힌 프로파일 (평면 분할) |
|
||
| `src/ir.mjs` | 조립 스펙 스키마 · 빌더 · 검증 |
|
||
| `src/drawings/*.mjs` | **도면별 설정.** 새 도면은 여기에 파일 하나 |
|
||
| `src/drawings/manhole1-trans.mjs` | 전이부만 세우는 최소 모델. `manhole1` 설정을 상속하고 `assemble()` 만 교체 — 한 부분을 떼어 검증할 때의 본보기 |
|
||
|
||
---
|
||
|
||
## 7. 모델러 조작 시 주의
|
||
|
||
`Modeler_AI/docs/egm-usage.md` 중 이 작업에서 특히 물리기 쉬운 것.
|
||
|
||
- **`Extrude C=True` 단독은 항상 +Z 로 민다.** 커브 평면의 법선을 따르지 않는다.
|
||
`src/emit/egm.mjs` 는 항상 `D F {기준점} {방향점}` 으로 방향을 명시한다.
|
||
- **`C=True` 는 `D F` 앞에** 와야 한다. 뒤에 오면 무시되고 서피스가 나온다.
|
||
- **닫힌 `Polyline` 에 `Enter` 를 붙이지 말 것.** 자동 종료되므로 남은 신호가
|
||
다음 명령으로 새어나간다.
|
||
- **`SelByUids` 는 `Esc` 로 닫는다.** 안 닫으면 이후 명령이 전부 무시된다.
|
||
- 명령은 비동기다. `egm status` 의 `command` 가 `null` 이 될 때까지 기다린다.
|
||
`src/run_egm.py` 의 `wait()` 가 그 일을 한다.
|
||
- **`BooleanDifference`/`BooleanUnion` 은 두 개만 먹는다.** 플러그인이
|
||
`TargetModels[0]`, `[1]` 만 쓰므로 셋 이상 선택하면 나머지는 조용히 무시된다.
|
||
`src/emit/egm.mjs` 의 `fold()` 가 둘씩 접는다.
|
||
- **뷰어 모드면 모델링 명령이 등록조차 안 된다.** 0절의 `egm guide` 확인을
|
||
건너뛰지 말 것.
|
||
- **`ExportByPath` 의 경로는 반드시 큰따옴표로 감싼다.** `_` 가 egm 스크립트
|
||
특수문자라, 경로에 밑줄이 있으면 오류도 경고도 없이 **아무 파일도 안 생긴다**.
|
||
`result` 는 정상으로 보이고 `created:[]` 도 원래 그런 명령이라 신호가 없다.
|
||
내보내기 전에 `SelAll` 이 필요하고, `.obj` 만 나온다(`.stl` 은 파일이 안 생긴다).
|
||
`src/run_egm.py` 의 `export_obj()` 가 이걸 다 하고 파일 생성까지 확인한다.
|
||
|
||
### 씬 초기화가 실패할 때
|
||
|
||
`SelAll` → `Delete` 직후 상태가 남아 다음 명령이 씹히는 경우가 있다.
|
||
|
||
```sh
|
||
egm "Esc"; egm "!Cancel"; egm "SelAll"; egm "Delete"; egm "Esc"; egm "!Cancel"
|
||
egm status # command 가 null 인지 확인
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 현재 미완
|
||
|
||
| 항목 | 상태 | 필요한 것 |
|
||
|---|---|---|
|
||
| 전이부 챔퍼 | `manhole1-trans` 에서 완성 (챔퍼·단차·종방향 헌치) | `manhole1` 본편에 이관 |
|
||
| 이음매 T 정점 | 열린 변 21개 (기하는 닫힘) | IFC 내보낼 때 재검토 |
|
||
| Φ100 관통 ×5 | 미구현 | 평면도에서 전선인입 2 · 우수관연결 2 · 유도배수관 1 위치 특정 |
|
||
| 나선계단 | 미구현 | 현 IR 연산으로 표현 불가. `Sweep1` 또는 전용 연산 추가 |
|
||
| 방호난간 L=7.2m | 미구현 | H-H 에 표기 |
|
||
| IFC 출력 | 미구현 | `emit/ifc.mjs`. 레이어 ↔ 재질 매핑은 이미 확보 |
|
||
| 브라우저 연동 | 미구현 | `src/core/*` 순수화 + localhost 브리지 |
|