Files
dxf-to-3d-gasiseol/docs/roadmap.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

16 KiB
Raw Blame History

로드맵 — 일반 도면 세트 → 3D → IFC

지금 파이프라인은 평면도 + 단면도 조합에서 검증됐다(맨홀 일반도 4매, 11파트). 2026-08-07 에 두 번째 도면군(교량 설계지침 삽도 7장)을 넣어 1장을 관통시켰다 (중앙분리대 방호벽, 4.8058 m³ — 모델러·해석 일치). 궁극 목표는 평면 · 입면 · 단면 · 좌측면 · 우측면을 읽어 IFC 구조로 내보내는 것.

이 문서는 거기까지 무엇이 남았는지, 어떤 순서로 하면 되는지를 적는다.

두 번째 도면군이 바꾼 것. 남은 일 목록이 「연산이 부족하다」에서 「입력 해석 가정이 부족하다」로 옮겨갔다. 교량에서 실제로 막은 것은 sweep·revolve 가 아니라 단위·축척·뷰 제목·레이어·폐곡선이었다. 자세한 것은 methodology.html 11장, 교훈은 2d-3d-conversion.md #20~#26.

그 위에 얹은 파라메트릭 설계 층은 parametric.md 에 따로 있다. 구현됐다(fit/build 분리 · 규칙 · 편집 UI). 본체 목표(2D→3D)를 대체하지 않는다.


1. 뷰 종류의 일반화

지금 titles.views 는 뷰 제목을 네 가지 kind 로 분류한다.

plan        평면도        DWG(x,y) → 프로젝트(X, Y)
long        종단면        DWG(x,y) → 프로젝트(X, Z)
cross       횡단면        DWG(x,y) → 프로젝트(Y, Z)
horizontal  수평 단면      DWG(x,y) → 프로젝트(Y, X)  (swapUV)

정면도·배면도·좌측면도·우측면도를 넣으면 이렇게 된다.

뷰 보는 방향 평면 비고
평면도 −Z (위에서) XY
저면도 +Z XY 좌우 반전 → flip
정면도 +Y XZ
배면도 −Y XZ 좌우 반전 → flip
좌측면도 +X YZ
우측면도 −X YZ 좌우 반전 → flip
종단면 ±Y XZ 정면도와 같은 평면
횡단면 ±X YZ 측면도와 같은 평면
수평 단면 −Z XY 평면도와 같은 평면

결국 평면은 셋(XY · XZ · YZ)뿐이고, 나머지는 반전 여부의 문제다. kind 라는 이름이 오해를 부른다 — 앞으로는 plane + flip 으로 부르는 게 맞다.

할 일

  • titles.views 항목을 { plane, u0, v0, flip, flipV, swapUV } 한 덩어리로 합친다. 지금은 titles.views 와 anchors 로 나뉘어 있어 뷰 하나가 두 곳에 흩어져 있다.
  • 뷰가 어느 방향에서 본 것인지 도면에서 검출할 방법을 찾는다. 단면은 절단 기호(SYM)로 알 수 있지만, 입면도는 제목 말고 단서가 없다. → 제목 규칙(정면도/좌측면도 …)을 설정에 두는 게 현실적이다.

2. 입면도가 단면도와 다른 점

단면도는 잘린 면만 그리지만 입면도는 보이는 모든 것을 그린다. 그래서 입면도의 폐곡선은 구조체 경계가 아니라 투영 윤곽이다.

단면도 입면도
폐곡선의 의미 재료 영역 보이는 면의 윤곽 (여러 깊이가 겹침)
압출 가능? 그대로 가능 불가. 깊이 정보가 없다
쓸모 형상 생성 형상 검증, 높이·폭 확인, 개구부 위치

입면도로 솔리드를 만들려 하지 말 것. 입면도는

  • 다른 뷰에서 만든 형상의 외곽 검증
  • 창·문·개구부의 XZ(또는 YZ) 위치 확보
  • 마감선·레벨선 확인

에 쓰고, 실제 압출은 평면도(footprint) + 단면도(높이)로 한다.

할 일

  • 입면도용 검증 도구: 만든 3D 를 그 방향으로 투영해 입면도 윤곽과 대조
  • 개구부 위치만 뽑아 쓰는 경로 (지금 rect() + expect() 로 하는 것과 같은 방식)

3. 곡면 · 원호

지금은 직선 선분만 다룬다(src/core/parse.mjs 의 toSegments). ARC · CIRCLE · ELLIPSE 는 버린다.

대상 필요한 것
모따기·필렛 이미 직선으로 그려져 있으면 그대로 됨 (지금 잘 되고 있다)
원형 관통 (Ø900·Ø100) 됨 — collectCircles → circles() → cylinder
원호 벽체 ARC 를 선분으로 근사(tessellate)하거나 IR 에 호 지원 추가
나선계단 전용 연산. Sweep1 또는 파라메트릭 생성

CIRCLE — 된다 (2026-08-06)

node src/build.mjs <도면> --circles     # 뷰별 원 목록 (지름 · 레이어 · 좌표)
circles('평 면 도', 900, /^-전력구$/)     // 지름으로 집는다. 폐곡선과 같은 규칙
circle('평 면 도', 100)                  // 정확히 하나일 때. 여럿이면 예외

선분과 섞지 않는다. 원을 다각형으로 근사해 평면 분할에 넣으면 폐곡선이 그 윤곽을 따라 돌아 프로파일이 오염된다. 원은 그 자체가 형상이라 cylinder 로 바로 간다.

뷰 귀속은 중심 좌표로 한다 — 뷰 분리(조각 클러스터링)에 원을 넣으면 관통 구멍 하나가 뷰를 잇는 다리가 될 수 있다. 이 도면은 133개 중 129개가 귀속됐다.

부수 효과: 평면도에 v0 앵커가 필요해졌다. 평면도는 세로축이 표고가 아니라 폭방향(Y)인데, 지금까지 치수(폭×높이)만 봐서 드러나지 않았다. 좌표를 직접 쓰는 순간 필요해진다. 수평 단면(H/I/J)과 같은 문제였다.

남은 것: Ø100 관통 5개(환기구 4 · 배수관 1). 평면도에 있는 1개는 X=17,690 으로 맨홀 #2 영역이라 그 형상을 세운 뒤에 뚫는다.


4. IFC 출력

이미 확보된 것

레이어가 재질을 인코딩한다는 사실(노하우 문서 교훈 #6). 그래서 클래스·재질 배정에 필요한 정보가 이미 IR 에 실려 있다.

part({ …, name: '맨홀1_전력구_구체',
        material: '구체 콘크리트 fck=27MPa' })

매핑 초안

부재 IFC 클래스 재질
벽체 IfcWall Concrete C27
슬래브 IfcSlab Concrete C27
기초 IfcFooting Concrete C18
잡석·토목섬유 civil 계열 (IFC4.3 엔티티명 확인 필요) —
집합 요소 IfcBuiltElement 또는 civil 계열 —
  • 재질 속성 → IfcMaterial + Pset_MaterialConcrete(CompressiveStrength)
  • 층상 구성(잡석 → 기초 → 구체) → IfcMaterialLayerSet
  • 설계 정보(fck · fy · 설계법 · 하중) → IfcProject 속성

IfcWall/IfcSlab/IfcFooting/IfcMaterial*/Pset_MaterialConcrete 는 IFC4 표준이라 그대로 써도 된다. 잡석·토목섬유용 IFC4.3 civil 엔티티 이름은 스키마를 직접 확인하고 확정할 것. 위 표의 후보는 검증 전이다.

할 일

  • part 에 ifcClass 필드 추가 (지금은 material 만 있다)
  • src/emit/ifc.mjs — IR → IFC4 STEP(.ifc)
  • 형상 표현: IfcExtrudedAreaSolid 가 지금 IR 의 extrude 와 1:1 로 맞는다. difference/union 은 IfcBooleanResult 로 그대로 옮겨진다. IR 을 IFC 를 염두에 두고 설계해둔 게 여기서 값을 한다.

5. 우선순위 제안

2026-08-06 에 순서를 정했다 — 본체(A)를 좁혀서 먼저, 그 다음 파라메트릭(B). 근거는 parametric.md 11절.

✔ 0. 부피 측정 자동화 (measure.mjs)      손계산 → 회귀 검사. B 의 quantity 몸통
✔ 1. CIRCLE → circles() 헬퍼            손으로 적은 R450 이 도면 대조로 바뀌었다
  2. manhole1-trans 를 본편으로 통합      지금 반쪽 모델이 둘이다. 기초·잡석 이관
  3. 맨홀 #2 + 출구 스터브                제일 크다. 파라미터 표본을 대표성 있게 만든다
✔ 4. 파라메트릭 (parametric.md 10·12절)   fit/build 분리 · 규칙 · 편집 UI 까지 완료
  5. 뷰 설정 통합 (titles.views + anchors + viewBoxes → 한 덩어리)   ← 아래 참고
  6. emit/ifc.mjs                        IR 이 이미 준비돼 있다
  7. 입면도 검증 경로                     새 도면 종류를 받으려면 필요
  8. Sweep 백엔드 (선형 곡선부·구배)       Loft 는 완료

5 의 값이 올라갔다. 뷰 하나를 서술하는 자리가 이제 셋이다 — titles.views(평면) · anchors(앵커·축척) · viewBoxes(영역·평면·축척). unitMm 이 viewBoxes 와 anchors 양쪽에 있고, 뷰 분리 방식에 따라 어느 쪽이 읽히는지가 갈린다. 뷰 종류를 늘리기 전에 정리하는 편이 싸다 — { plane, box?, u0, v0, flip, flipV, swapUV, unitMm, role } 한 덩어리로.

교량군에서 새로 생긴 것 (전부 구현됨, 정리 필요):

cfg.unitMm                DWG 모델단위. 도면군마다 다르다
cfg.entityFilter          레이어 말고 색·선종으로 거른다
cfg.viewBoxes             제목 없는/중복인 도면의 뷰 영역
cfg.datum.mode='none'     공통 기준선이 없는 도면
viewBoxes[뷰].unitMm      도형별 축척
rules[].needs             규칙 적용 범위 (부재 타입 태그의 임시 대용)

아직 없는 것 (교량 일반도를 받으면 바로 필요해진다):

G1.5 도면 적격 판정 도구   숫자/기호 치수 계수 + 윤곽 닫힘률. 지금은 손으로 센다
축척 역산 도구             표기 치수 2개 ↔ 기하 길이 대조. 지금은 손으로 잰다
부재 타입 태그             rules 를 파라미터 이름이 아니라 부재 종류에 붙인다
받침 기반 로컬 프레임       상부(선형) ↔ 하부(연직) 좌표계 전환 (methodology 11.1)

1 을 3 보다 앞에 두는 이유는 로드맵상 흔해서만이 아니다. 파라메트릭의 전제이기도 하다 — 지금 샤프트 보어 R450 은 원본 엔티티에서 눈으로 읽어 손으로 적은 값이라 도면과의 끈이 아예 없다. CIRCLE 지원이 들어와야 loop()/expect() 로 대조되고, 그제서야 보어 반지름이 정당한 파라미터가 된다.

1·2·3 은 서로 독립이라 순서를 바꿔도 된다.

Loft 백엔드는 끝났다 — 전이부(챔퍼·수직 단차·종방향 헌치)를 세워 부피 오차 0.0002% 로 검증했다. 배경은 2d-3d-conversion.md 교훈 #16. 남은 것은 rail 을 따라가는 스윕이다 (Sweep1 C=True V=True 가 IFC4.3 IfcSectionedSolidHorizontal 과 의미가 같다). 그러려면 IR 이 직선축 from/to 대신 측점(station) 을 다뤄야 한다. 4 는 실제 입면도가 있는 도면을 하나 받아야 설계가 확정된다.


6. 미완 목록 (맨홀 도면 기준)

항목 상태
스터브↔본체 전이부 완료. assembleDuct() 를 본편·trans 가 공유한다
관로↔맨홀 관통구 완료. 개구 기호에서 좌표가 나온다. 합치기 전에 뚫는다
구조물만 (기초·잡석 제외) 완료. 5파트
맨홀 #2 부피 9.32 손실 IR 은 맞고(74.68) 모델러가 흘린다(65.36). 원인은 면으로만 맞닿은 union — 교훈 #19
외부 헌치 5개 완료. 아래 참조
내부 헌치 (100×100·150×150) 미구현. 맨홀을 수평 단면에서 Z 로 쌓아야 따라온다
H/I/J 내부 링 끊김 H-H 내부가 Y 1,800~3,700 만 닫힌다. 위 항목의 전제
Ø100 관통 5개 위치 확인됨 (전선인입 ×2 · 우수관연결 ×2 · 유도배수관 ×1). 미구현
침사구 (0.8×0.8) I-I 개구 기호로 잡힌다. opening('단 면 I-I', 800, 800)
나선계단 · 방호난간 현 IR 연산으로 표현 불가
기초 · 잡석 · 보도 뺐다. 되살리는 데 필요한 값은 manhole1.mjs 주석에 있다

외부 헌치 — 완료 (2026-08-06)

평면도 대각선 21개 중 외부 표면 모서리인 5개를 세 종류로 나눠 넣었다. Z 구간은 도면에서 뽑았지 지어내지 않았다.

종류 도면 근거 구현 부피 변화
출구 스터브 챔퍼 A-A (18200,-4607)→(18500,-4907) 300×300 · (18200,-8007)→(18550,-7657) 350×350 loft 2단 +0.577
전력구↔맨홀 접합 평면 (15100,1900)→(15400,2200) 300×300 XY 삼각형 → Z 압출 → union +0.224
챔버↔환기구 다리 평면 (15400,4100)→(15500,4200) 100×100 ×2 XY 사각 쐐기 → Z 압출 → 커터 −0.029

3D 검증 완료 — modelCount 5, 의도 vs 결과가 셋 다 맞는다.

파트                    실측      IR 의도       차이
맨홀1_전력구_구체       100.7473  100.7592  −0.0119 ✓   접합 헌치 들어감
맨홀1_전력구_출구스터브    3.5474    3.5490  −0.0016 ✓   출구 챔퍼 2단 들어감
맨홀1_사다리샤프트_1·2    3.3217/3.2805        ✓        건드리지 않은 것 확인
맨홀1_맨홀2_구체         66.0875   74.6527  −8.5652 ✗   기존 손실 (수평단면 재구성 대기)

출구는 입구와 대칭이 아니다. 위·옆은 300×300(X 18,20018,500)인데 아래는 350×350(18,20018,550)이라 꺾이는 X 가 다르다. loft 하나로 이으면 아래 챔퍼가 300 짜리로 짧아진다 — 입구에서 이미 겪은 교훈 #16 이 그대로 반복됐다.

검산: 격자 +0.5765 vs 심프슨 해석 +0.5749 — 0.3% 일치.

평면 방향 헌치는 XZ 단면에 안 나타난다. 다리 100×100 의 Z 범위를 F-F·G-G 에서 찾으려 했지만 없다(거기 있는 150×150 은 다리 내부 헌치다). 그래서 fit() 이 E-E 외곽 링에서 Y>4,200 구간의 Z 범위를 코드로 뽑는다 → −5,257 ~ 176.

다리 테이퍼의 부피 변화가 작은(−0.029) 데는 이유가 있다. E-E 링을 보면 하부 블록이 Y=4,100 에서 끝나고([4100,-10007]→[4100,-5557]) 그 위로 다리가 선다. 그래서 테이퍼가 걸리는 Y 4,100~4,200 구간에는 재료가 100mm 높이밖에 없다.

맨홀 #2 를 수평 단면 기반으로 다시 세우면 셋이 한꺼번에 풀린다

부피 9.32 손실   조각이 Z 로 진짜 겹쳐 union 이 한 덩어리가 된다
내부 헌치        H/I/J 의 XY 프로파일에 모따기가 이미 들어 있다
층별 정확도      E-E 치수 사슬이 각 층의 Z 범위를 그대로 준다

전제는 H/I/J 의 내부 링 끊김 수리다. 조사는 끝났다.

gaps.mjs 는 H-H·I-I 에 끊김 0 이라고 한다 — 선이 없는 게 아니라 개구부 윤곽이 안 닫혀 있어 면이 새는 것이다(교훈 #12 와 같은 종류). 선분을 직접 뽑아 실태를 확정했다.

H-H 내부는 격벽으로 3칸이다 (전부 X 15,800~17,800, 모서리 150×150 모따기)
  A  Y -1,500 ~ 1,500   3,000        ← 환기 OPEN부가 들어 있어 안 닫힘
  B  Y  1,800 ~ 3,700   1,900        ← 유일하게 폐곡선으로 잡힌다 (#2)
  C  Y  3,700 ~ 8,000   4,300        ← 점검구·방호난간이 들어 있어 안 닫힘
격벽  Y 1,500~1,800 (300)

A·C 가 안 닫히는 이유는 개구부 좌측 변이 000-구조물실선 에 Y -1,000~1,000 로만 그려져 있기 때문이다(개구는 Y -500~1,500). 대응은 교훈 #12 대로 rect()/opening() 으로 재구성 — 위 좌표가 그 재료다.


7. 검증 기준선

baselines/<도면>.volumes.json 은 커밋한다. out/ 과 달리 재생성으로 복구되지 않기 때문이다 — 사람이 한 번 확인한 부피가 남아 있어야 다음 변경이 형상을 건드렸는지 알 수 있다.

도면 파트 부피 m³ 확인
manhole1-trans 3 107.1425 원시 파트 합으로 교차검증
manhole1 11 — 기준선 없음. 통합 후 뜬다