Files
dxf-to-3d-gasiseol/docs/parametric.md
T
Claude Haiku 363c2a2a25 step3-4: 프로젝트 기본 구조 & 상세 계획 수립
- CLAUDE.md: 프로젝트 전체 가이드 및 워크플로우 정의
- CONSIDERATIONS.md: 8개 영역 고려사항 분석 (기술, 품질, 배포, UX)
- PROGRESS.md: 진행 상황 추적 및 체크리스트
- PLAN.md: 단계 5~9 상세 계획 및 에이전트 역할 정의
- 메모리: 프로젝트 정보 저장

다음: 단계 5 (에이전트/커맨드/스킬/훅 아이디어 제안)
2026-10-16 15:06:18 +09:00

23 KiB
Raw Blame History

파라메트릭 설계 층 — 설계 노트

2026-08-06 토론 정리 → 구현 완료. 12절에 실제로 만든 것과 확인된 것을 적었다. 결정 근거는 11절(순서) · 8절 (b)(대조 등급) · 8.5절(UI).


0. 대전제 — 목표는 바뀌지 않았다

이 저장소의 목표는 여전히 2D DWG → 3D 모델 · IFC 다. 파라메트릭은 그 위에 얹는 층이지 대체가 아니다. 맨홀 도면 모델링은 파이프라인을 검증하는 과정이지 그 자체가 목적이 아니다.

순서를 헷갈리면 안 된다.

DWG 를 읽어 3D 를 세운다        ← 본체. 지금 여기
   그 위에
치수를 파라미터로 뽑아 바꾼다     ← 이 문서
   그 위에
바꾼 결과가 맞는지 평가한다      ← 설계 도구가 되는 지점

1. 사용자가 원하는 것

설계란, 비슷한 도면을 가져와 새 환경에 맞게 치수를 바꿔보고, 맞는지 검토하고, 다시 바꾸는 반복 작업이다.

그래서 필요한 것은 이것뿐이다.

값을 바꾼다  →  모델이 다시 만들어진다

속도는 요구사항이 아니다. 전체 재생성으로 충분하다(현재 28파트 1~2분). 실시간 프리뷰·부분 재생성은 있으면 좋은 것이지 필수가 아니다 — 우선순위 뒤로.

모델러에 패밀리 개념이 없다는 것

EGModeler 는 Revit 패밀리 같은 것이 없다. 매번 새로 만들어야 한다. 그래도 된다 — 패밀리를 모델러가 아니라 우리가 들고 있으면 된다.

Revit 우리
패밀리 .rfa assemble() 조립 로직
타입 파라미터 params.json
인스턴스 배치 egm 재생성

모델러는 덤 렌더러다. 형상 지능은 전부 IR 위쪽에 둔다. 지금 계층 구조가 이미 그렇게 생겼다 — core/* 는 3D 를 모르고 emit/* 만 모델러 사정을 안다.


2. D-Cubed 검토 결과 — 쓰지 않는다

Siemens PLM 의 CAD 부품 라이브러리. 원래 영국 D-Cubed Ltd, 2004년 UGS 인수.

컴포넌트 하는 일
2D DCM 2D 스케치 구속 솔버 (치수·평행·접선·동심)
3D DCM 3D 어셈블리 구속 솔버
AEM / CDM / PGM 기구학 · 간섭검사 · 2D 프로파일 연산

SolidWorks · Solid Edge · NX 가 쓴다.

쓰지 않기로 한 이유

  • CAD 벤더용 상용 C++ SDK. Siemens 와 개별 계약해야 하고 공개 가격이 없다
  • 우리는 Node 다. FFI/N-API 바인딩을 직접 짜야 한다
  • 무엇보다, 우리 문제에 구속 솔버가 거의 필요 없다 (아래 3절)

오픈소스 대안 (필요해질 때 검토)

라이선스 비고
planegcs (FreeCAD 스케처 솔버) LGPL Eigen 기반. WASM 포팅본이 있다고 알고 있으나 미확인
SolveSpace slvs GPL-3 C 라이브러리 + Python 바인딩. 2D·3D

GPL 배포 조건 확인 필요. vendor/acadrust-dwg MPL-2.0 을 이미 다루고 있으니 낯선 문제는 아니다.


3. 구속 솔버가 필요 없는 이유

파라메트릭에는 성격이 다른 두 가지가 있다.

① 명시적 (history-based)   치수 = 프로그램의 입력. 값 → 계산 → 형상
                           OpenSCAD · Grasshopper · 그리고 지금 우리 파이프라인
② 변분/구속 (variational)  엔티티 + 구속을 선언하고 솔버가 위치를 찾는다
                           SolidWorks 스케치 · D-Cubed

요구사항은 ①로 거의 다 된다. 증거가 이미 코드에 있다 — drawings/manhole1-trans.mjs 의 외곽 챔퍼 중간 단면은 상수가 아니라 식이다.

const t = (CH1 - CH0) / (CH2 - CH0);
const oY = Math.round(1350 + (1650 - 1350) * t);   // 1550
const oZ = Math.round(-4907 + (-4608 + 4907) * t); // -4708

②가 필요한 것은 관계가 순환할 때다. "헌치가 두 면에 접하면서 벽 두께는 유지" 같은 것. 우리 형상은 거의 직각·45° 라 순환이 잘 안 생긴다.

결론: 파라미터 + 식 + 위상 정렬로 시작한다. 솔버는 필요한 자리를 실제로 세어본 뒤에 결정한다.


4. 핵심 구조 변경 — params.json 을 파이프라인 한가운데 끼운다

이 하나가 나머지를 거의 다 푼다.

지금    DWG ──build.mjs──→ IR ──→ 3D            치수가 코드 안에 박혀 있다

바꿀 것  DWG ──fit──→ params.json ──build──→ IR ──→ 3D
                      ↑ 사람이 여기만 만진다   도면을 다시 안 본다

빌드가 도면을 안 보는 것이 핵심이다.

지금 assemble() 은 loop('단 면 B-B', 2700, 2750) 로 치수를 도면 조회 키로 쓴다. 사용자가 2700 을 3000 으로 바꾸면 그런 폐곡선이 없어 예외가 난다. 도면에 묶여 있는 한 파라메트릭이 성립하지 않는다.

① fit    도면에서 값을 뽑아 params.json 을 쓴다. 출처까지 기록
② build  params.json 만 읽어 IR 을 만든다. 도면 없이도 돈다

부수 효과가 크다.

  • git diff params.json 이 곧 "무엇을 바꿨나"
  • 설계안 A/B/C 를 파일 셋으로 두고 나란히 비교
  • 새 도면 설정 비용이 첫 도면 한 번으로 끝난다 — 변형은 도면을 안 읽는다

5. 파일을 셋으로 가른다

지금 drawings/manhole1.mjs 한 파일이 세 가지를 섞고 있다.

types/전력구_박스.mjs     조립 로직. 파라미터를 받는다         ← 패밀리
params/맨홀1.json         그 도면에서 뽑은 값                 ← 타입 파라미터
drawings/manhole1.mjs     DWG 읽기 설정 (레이어·앵커·기준점)   ← 추출 규칙

"비슷한 도면 가져와 치수만 바꾼다" = 같은 type + 다른 params.json.

cp params/맨홀1.json params/신규현장.json
#   구체_외폭 3800 → 4200,  토피 2957 → 3500  수정
node src/build.mjs --params 신규현장     # 도면 안 읽는다

src/core/* 와 src/ir.mjs 에 도면 고유값이 들어가면 안 된다는 기존 경계는 그대로 유지된다. types/* 는 도면 고유값이 아니라 부재 종류다.


6. 파라미터 스키마 — 값만으로는 부족하다

{
  "구체_외폭":  { "v": 3800, "src": "measured", "from": "단면 C-C #1 폭" },
  "벽두께":     { "v": 400,  "src": "design",   "min": 300, "max": 600, "step": 50 },
  "구체_내폭":  { "expr": "구체_외폭 - 2*벽두께" },
  "헌치":       { "v": 150,  "src": "design" },
  "샤프트": [
    { "x": 2550,  "저부Z": -3157, "정상Z": 0 },
    { "x": 13250, "저부Z": -3157, "정상Z": -39 }
  ]
}

넷이 필요하다.

항목 왜
expr 식 파생값. 위상 정렬해 평가. 솔버 불필요
src 출처 measured 는 도면과 묶여 있다. 바꾸면 "도면과 다름" 경고
min/max/step 유효 범위. 검증이자 나중에 UI
배열 치수만이 아니라 개수가 바뀐다. 샤프트 2→3, 환기구 없음

배열이 특히 중요하다. manhole1-trans.mjs 의 샤프트를 이미 for (const s of shafts) 로 짜놨다 — 그대로 파라미터로 올라간다.

src 3분류가 도면과의 계약이다

measured  도면에서 뽑았다. 바꾸면 도면과 어긋난다 → 경고하되 막지는 않는다
design    사람이 정한다. 자유롭게 바꾼다
derived   식으로 나온다. 직접 못 바꾼다

기존 datum.checks 가 이미 이 자리를 갖고 있다. 확장하면 된다.


7. 설계 도구가 되는 지점 — 규칙과 물량

치수를 바꾸는 것보다 바꾼 결과를 즉시 평가하는 것이 설계다. 지금 파이프라인에 없는 층이 이것이고, 실제 가치는 여기서 나온다.

rules.mjs      벽두께 ≥ 최소,  내공 높이 ≥ 2,100,  피복 ≥ 기준,
               헌치가 부재보다 크지 않은가,  샤프트 보어가 벽 안에 있는가,
               개구부 최소 치수,  케이블랙 이격
quantity.mjs   콘크리트 m³ · 굴착 m³ · 거푸집 m² · 되메움

물량은 이미 공짜로 나온다. 오늘 OBJ 를 발산 정리로 재서 0.0003% 오차로 검증했다. 그 코드가 그대로 물량 산출이다.

안 A  구체 3,800×4,600           콘크리트 100.5 m³   ✓ 전 규칙 통과
안 B  구체 4,200×4,600           콘크리트 108.2 m³   ✓
안 C  구체 4,200×4,600 · 벽 300   —                  ✗ 최소 벽두께 미달

이 표가 나오는 순간 도구가 쓸모를 얻는다.


8. 위험한 곳

(a) 파라미터가 형상 위상(topology)을 바꾼다. 벽두께를 키우다 헌치보다 커지면 꼭짓점 수가 달라져 loft 대응이 깨진다. validateIR 의 꼭짓점 수 검사가 여기서 걸린다(교훈 #16). min/max 로 미리 막고, 걸리면 명확한 메시지를 낸다.

(b) 프로파일을 파라미터로 "생성"하면 도면의 실제 형상을 잃는다.

지금    loop('단 면 B-B', 2700, 2750).ring        도면이 준 실제 링
이후    box(p.스터브_외폭, p.스터브_외고, p.헌치)   파라미터로 만든 링

B-B 내부 링에 있는 50mm 보도 단차 같은 자잘한 것이 생성기로 재현이 안 되면 조용히 빠진다. fit 단계에서 "이 링이 생성기로 재현되는가"를 대조해야 한다. 그게 도면과의 마지막 끈이다.

결정 (2026-08-06) — 3등급으로 기록한다

완전일치를 요구하지 않는다. fit 이 링마다 등급을 매겨 params.json 에 남긴다.

exact     꼭짓점 집합이 mm 단위로 일치        → 파라미터로 완전 대체
tolerant  bbox · 면적 · 꼭짓점 수 일치        → 경고를 남기고 파라미터 사용
          개별 점은 ±ε
opaque    생성기로 재현 불가                  → 파라미터화하지 않는다.
                                            도면 링을 **리터럴로 얼려** params 에 박는다

opaque 가 핵심이다. "재현 못 하면 파라미터화 안 한다"가 아니라 "재현 못 하는 링은 값으로 얼려 넣는다" (src: "measured" + ring 배열). 그러면

  • 4절 불변식(build 가 도면을 안 본다)이 안 깨진다
  • 50mm 보도 단차 같은 것이 조용히 빠지지 않는다
  • 그 링은 못 바꾸는 것일 뿐이고, 그게 정직하다

등급 분포가 곧 "이 도면이 얼마나 파라메트릭한가" 의 척도가 된다. opaque 가 많으면 생성기가 부족한 것이지 도면이 이상한 게 아니다.

(c) 재생성하면 모델러의 UID 가 전부 바뀐다. 이름·색은 우리가 다시 붙이니 괜찮지만 사용자가 모델러에서 직접 손댄 것은 날아간다. "모델러에서 편집하지 않는다"를 규칙으로 못 박아야 한다. 편집은 params 에서만.

(d) 추출 설정은 여전히 도면당 반나절이다. 4절의 fit/build 분리가 이 비용을 첫 도면 한 번으로 묶어준다. 그래서 구조 변경이 이 위험까지 같이 없앤다.


8.5. 모델러 밖 UI — 값을 바꾸고 "갱신"

요구사항으로 확정(2026-08-06). 파이프가 이미 CLI 단계 사슬이라 새 아키텍처가 아니라 얇은 껍데기다.

1단계는 이미 있다 — 읽기 전용 뷰어 (report.mjs)

node src/report.mjs <도면> → out/<도면>.html. 의존성 없는 한 장짜리다. 뷰별 링을 SVG 로 그리고 자기교차·꼭짓점 수·원·IR·부피를 같이 보여준다.

만든 날 바로 값을 했다 — 맨홀 #2 의 부피가 2배로 나온 원인(mh.outer 자기교차)을 숫자로 찾는 데 반나절 걸렸는데, 그림에서는 빨간 X 두 개로 즉시 보인다(교훈 #18).

build.mjs 와 같은 projectViews() 를 쓴다. 뷰어가 자기 변환을 따로 갖고 있으면 보여주는 것과 조립되는 것이 갈라진다 — 거짓말하는 뷰어는 없는 것보다 나쁘다.

편집은 아직 안 된다. params.json 이 없어서 편집할 대상이 없다.

UI(로컬 웹앱) → params.json → build(도면 안 봄) → IR → emit/egm → run_egm.py → 모델러
                                                  ↘ three.js 프리뷰 (9절·나중)
  • 입력 위젯이 공짜로 나온다. 6절의 min/max/step/src 가 그대로 슬라이더·범위·경고다. 그러라고 스키마를 그렇게 잡았다.
  • 갱신 버튼이 검증을 태워야 도구가 된다. 단순 실행이면 폼이고, validateIR → rules.mjs(7절) → measure.mjs 기준선까지 통과 못 하면 거부하면 설계 도구다. 여기가 값이 나오는 지점이다.
  • 재생성 1~2분. 버튼 누르고 기다리는 UX 로 견딘다(1절).

못 박을 것 둘.

갱신 = 씬 초기화 포함.  Esc → SelAll → Delete → Esc.  `!Cancel` 은 쓰지 않는다
모델러에서 직접 편집 금지.  8절 (c) — 갱신하면 UID 가 전부 바뀐다. 편집은 params 에서만

순서상 위치: 10절 1~4 뒤. 지금 만들면 편집할 params.json 이 없다.


9. 나중에 (필수 아님)

  • 브라우저 프리뷰 — IR 은 이미 도구 중립이다. three.js + CSG(manifold-3d WASM 이 빠르다고 알고 있으나 미확인)로 모델러 없이 확인. 속도가 요구사항이 아니므로 뒤로.
  • 선택 재생성 — 파라미터 → 파트 의존 그래프로 바뀐 파트만 다시. 마찬가지로 뒤로.
  • 구속 솔버 — 1~5 를 하고 나서 정말 필요한 자리를 세어본 뒤 결정.
  • 모델 → 도면 역방향 — DWG writer 가 필요하다. 현재 범위 밖.

10. 1차 목표 — 전부 됐다

✔ 1. params.json 스키마 확정          v / expr / src / grade / min·max·step / 배열
✔ 2. build.mjs 를 fit / build 로 분리  --params 면 도면을 아예 안 읽는다
✔ 3. manhole1 을 params 로 전환        egm 명령 목록이 **바이트 단위로 동일**했다
✔ 4. 파라미터 바꿔 재생성 시연          본체_외폭 3800→4200, 종헌치 150→200
✔ 5. 물량                             measure.mjs 가 재질별로 낸다

3번이 좋은 시험대다. manhole1-trans.mjs 에 이미 이름이 뚜렷한 값들이 있다 — 챔퍼 200×200, 헌치 150×150, 벽두께 200, 보어 Ø900, 칼라 900, 토피 2,957.

5번은 ../src/measure.mjs 로 먼저 끝났다. 실측·기준선 대조·재질별 물량이 돈다. 남은 것은 굴착·거푸집·되메움 같은 파생 물량이다.


11. 순서 결정 (2026-08-06)

본체(A)를 좁혀서 먼저, 그 다음 파라메트릭(B). 근거 셋.

① 인스턴스 1개로 추상화를 얼리게 된다. 5절의 가치 명제는 "같은 type + 다른 params" 인데 도면이 하나뿐이라 type/params 경계를 검증할 두 번째 인스턴스가 없다. 맨홀 #2(격실·환기구·침사조)가 스키마에 요구할 것 — 배열이 아니라 선택적 존재("환기구 없음"), 중첩 격실, 구간별 단면 배정표 — 은 지금 manhole1-trans 3파트에 안 보인다.

재작업이 대칭이 아니다. A 먼저 → assemble() 본문이 늘어 변환 비용이 커지지만 그 변환은 10절 3번 그 자체다(어차피 한 번은 한다). B 먼저 → 스키마를 두 번 짠다.

② 8절 (b) 결정이 A 에 의존한다. "어디까지 엄격하게 대조하나"는 불규칙한 링이 몇 개냐에 달렸다. 지금 아는 사례(B-B 내부 50mm 보도 단차, C-C 외곽 샤프트 융합, 개구 기호로 정점 8, 케이블랙 정점 114)는 전부 manhole1-trans 밖에서 나왔다. 등급 기준은 정했지만 ε 와 등급 분포는 표본을 세어본 뒤에 확정한다.

③ CIRCLE 파서가 B 의 전제다. manhole1-trans.mjs 의 BORE_R = 450 은 원본 엔티티에서 눈으로 읽어 손으로 적은 값이다. 8절 (b) 가 말하는 "도면과의 마지막 끈"이 보어에는 아예 없다.

✔ 0. measure.mjs                        B 에서 유일하게 당겨온 항목(10절 5번).
                                        앞으로 만드는 모든 파트의 회귀 검사가 된다
  1. CIRCLE 파서 → circle() 헬퍼          손으로 적은 R450 을 대조로. Φ100 ×5 도 풀린다
  2. manhole1-trans 를 본편으로 통합       기초·잡석 이관. 지금 반쪽 모델이 둘이다
  3. 맨홀 #2 + 출구 스터브                 파라미터 표본을 대표성 있게
  4. → 10절 1~4
  5. → 8.5절 UI

부록 — 2026-08-06 기준 현재 상태

되는 것

manhole1 11파트. 전이부는 X=250 계단 근사, 지붕 개구 없음(결함)
manhole1-trans 3파트. 스터브 + 챔퍼 전이 + 본체 + 종방향 헌치 + 샤프트 2기(Ø900 원형 보어)
새 IR 연산 loft (2단면, Loft C=True), cylinder (Z축 한정, Cylinder S=True)
새 유틸 normalizeRing — loft 꼭짓점 대응용. addProfile 이 자동 적용
검증 부피 대조 107.1425 실측 / 107.1422 기대 (0.0003%)
measure.mjs OBJ 실측 · 기준선 회귀 · 재질별 물량. 기준선 baselines/manhole1-trans.volumes.json
CIRCLE circles(뷰, 지름, 레이어). 보어 R450 이 손으로 적은 수에서 도면 대조로 바뀌었다

안 되는 것 / 남은 것

맨홀 #2 (X 15,400~18,200)      챔버·격실·환기구·침사조. 제일 크다
출구 스터브 (X 18,200~19,200)   맨홀 #2 뒤라 그전에 맨홀을 해야 자리를 얻는다
기초·잡석                       manhole1.mjs 에 있다. trans 로 옮기면 됨
Ø100 관통 5개                   환기구 4 · 배수관 1. 평면도의 1개는 X=17,690 으로
                               맨홀 #2 영역이라 그 형상을 세운 뒤에 뚫는다
이음매 T 정점                   열린 변 49개. 기하는 닫힘. IFC 갈 때 재검토
IFC 출력                        emit/ifc.mjs 미구현

12. 구현 결과 (2026-08-06)

만든 것

파일 하는 일
../src/params.mjs 스키마 · 식 평가(위상 정렬) · 검증 · 도면 이탈 감지
../src/fit.mjs 도면 → params/<이름>.json. 출처(from)와 원값(fitted)까지
../src/rules.mjs 규칙 13개. 위상 8(막는다) · 설계 5(경고)
../src/volume.mjs IR 을 격자로 평가 — 모델러 없이 "의도" 부피
../src/ui.mjs 편집 + 갱신. node src/ui.mjs manhole1 → localhost:7331
../src/core/pick.mjs 도면 조회 도우미. fit() 만 쓴다

확인된 것

파라미터   스칼라 49 (measured 38 · design 3 · derived 8) + 배열 1(샤프트)
링         11  (opaque 8 · exact 2 · tolerant 1)
규칙       13개 중 위반 0
기하 불변  전환 전후 egm.json **바이트 동일**
도면 없이  views.json 을 치운 채 --params 로 빌드 → 역시 바이트 동일

8절 (b) 결정이 실제로 어떻게 쓰였나

opaque 8개가 결정의 값을 증명한다. B-B 내부(50mm 보도 단차), C-C 내부(헌치), E-E 외곽(23꼭짓점 오목 윤곽) 같은 것은 파라미터로 재현이 안 된다. 좌표째 얼려 넣으니 도면 없이도 형상이 그대로 나오고, 자잘한 형상이 조용히 빠지지도 않는다. 그 링들은 못 바꿀 뿐이고, 그게 정직하다.

exact 2개는 개구부다 — 개구 기호의 네 모서리라 직사각형으로 완전히 재현된다.

규칙이 폼과 도구를 가른다

✗  맨홀 내벽이 뒤집혔다: 16900 ≥ 16700 (벽두께 1500)
   (맨홀 벽두께가 절반을 넘으면 내부가 사라진다)

벽두께를 1,500 으로 바꾸면 빌드가 거부한다(exit 1). 모델러까지 가서 비틀린 면으로 만나는 것보다 훨씬 싸다. 설계 규칙(내공 높이·토피·최소 벽두께)은 경고만 하고 사람이 판단한다.

도면 이탈은 막지 않고 남긴다

⚠ 도면에서 뽑은 값이 바뀌어 있다 — 이 모델은 원 도면과 다르다
  본체_외폭    도면 3800 → 지금 4200   C-C 구체 외폭

설계를 바꾸는 게 이 층의 목적이니 막지 않는다. 다만 "도면대로 만든 모델"인 척하게 두면 안 된다.

그림 위에서 치수를 고친다

render.mjs --dims 가 파라미터를 가리키는 치수선을 렌더에 얹는다. 2D 도면이 치수를 적어 형상을 증명하듯이, 우리 렌더도 "이 길이가 이 파라미터다" 를 그림에서 바로 보이게 한다.

span   두 점 사이 **길이**   연장선 + 치수선 + 이름 상자
level  한 좌표의 **위치**    그 자리를 가로지르는 선 + 이름 상자

둘로 나눈 이유가 있다. 파라미터의 절반은 길이가 아니라 좌표다(본체_상단Z 같은 표고). 그런 것을 억지로 길이로 그리면 거짓말이 된다 — 표고선으로 그린다.

같이 나오는 model-<축>.dims.json 이 각 라벨의 자리를 캔버스 비율로 담는다. UI 가 그 자리에 <input> 을 얹으므로 그림에서 값을 고칠 수 있다. 비율이라 창 크기가 바뀌어도 따라간다. 표와 그림의 입력칸은 서로 동기화된다.

라벨이 많으면 겹치므로 위에서부터 훑으며 겹치는 것을 아래로 민다. 치수선 자체는 안 움직인다 — 라벨만.

47개가 그림에 올라간다. 빠진 둘(커터여유·면겹침_여유)은 형상이 아니라 수치 여유값이라 패널에만 둔다.

UI 는 정말로 얇았다

파이프가 이미 CLI 단계 사슬이라, UI 가 한 일은 params.json 편집 + 그 사슬을 순서대로 돌리는 것뿐이다. 갱신 사슬:

build --params  →  emit  →  run_egm --reset --export  →  measure --ir  →  render --dims

--reset 이 필수다. 안 비우면 갱신할 때마다 객체가 쌓여 modelCount 대조가 무의미해진다. 다만 연달아 여러 번 하면 안 된다 — 초기화를 10번쯤 반복했더니 모델러 파이프가 죽었다. 갱신 1회당 1번이면 문제없다.

min/max/step 이 그대로 입력 위젯이 됐고, src 는 태그, fitted 는 변경 표시가 됐다. 그러라고 6절에서 그렇게 설계했던 것이 그대로 맞았다.

남은 것

  • 맨홀 #2 부피 9.32 손실 — 우리 IR 은 맞고(74.68) 모델러가 흘린다(65.36). 원인은 면으로만 맞닿은 union (교훈 #19). 제대로 된 해법은 맨홀을 수평 단면에서 Z 로 쌓는 것 — 조각이 Z 로 진짜 겹치고 평면 헌치도 같이 따라온다
  • 외부 헌치 5개 (평면 좌표 확정, Z 구간 미확정)
  • quantity.mjs — 굴착·거푸집·되메움 같은 파생 물량