2D 도면 세트 → 파라메트릭 3D 모델
일반 방법론
평면도 · 입면도 · 좌/우측면도 · 종단면 · 횡단면 · 수평단면을 한 좌표계로 묶어
솔리드와 설계 파라미터를 만드는 절차와 판단 규칙.
기존 문서와의 관계:
docs/2d-3d-process.md는 이 저장소의 명령 절차,
docs/2d-3d-conversion.md는 사례별 교훈, 이 문서는 그 둘에서 도면 고유값을 걷어낸
방법론이다.
0한 장 요약
도면 세트를 3D로 옮기는 일은 형상 작업이 아니라 좌표계 통일 작업이다. 뷰마다 따로 노는 로컬 좌표를 하나의 프로젝트 좌표로 묶고 나면, 나머지는 기계가 한다.
도면 세트 ─┬─ 기준점(datum) ─→ 전 뷰 공통 Z (또는 X·Y)
├─ 뷰 분리 ─→ 뷰마다 로컬 (u,v)
└─ 뷰 앵커 ─→ 프로젝트 (X,Y,Z)
│
프로파일(폐곡선) · 원 · 개구부
│
fit ─→ params.json ─→ build ─→ IR ─→ 백엔드(3D/IFC)
↑ 사람은 여기만 만진다 ↓
부피·물량·규칙 검사
변하지 않는 규칙 12개
- 게이트에 넣기 전에 「이 도면이 솔리드 대상인가」를 먼저 판정한다. 표기 치수를 숫자형/기호형으로 세고, 구조 윤곽이 닫히는지 본다. 기호 치수뿐이거나(
L·β·θ·VAR) 윤곽이 안 닫히는 도면은 어느 게이트에서도 안 통과한다 — 교량 삽도 7장 중 6장이 그랬다. 이 판정은 몇 초면 된다. - 단위와 축척을 도면에서 되찾고 두 치수로 교차검증한다. DWG 모델단위는 도면군마다 다르고(1m vs 1mm), 도형마다 배율이 또 다를 수 있다(5.805배). 상수로 두면 모든 값이 조용히 배수만큼 틀린다 — 폐곡선도 부피도 정상으로 보인다.
- 기준점을 먼저 확정한다. 형상보다 앞이다. 기준점 없이 뽑은 형상은 정합 단계에서 전부 다시 한다. 단, 기준선이 아예 없는 도면이 실재한다 — 그때는 검출을 끄고 뷰별
v0로 간다. 아무 선이나 자동으로 고르지 않는다. - 기준은 텍스트가 아니라 선에서 잡고, 도면 치수 2개 이상으로 교차검증한다. 라벨은 후보를 좁히는 힌트일 뿐이다.
- 뷰 하나는 [좌표평면 · 반전 · 원점 · 축척 · 용도] 다섯 속성으로 서술된다. 도면 종류는 9가지지만 좌표평면은 XY·XZ·YZ 셋뿐 — 나머지 차이는 보는 방향(좌우 반전)이다. 종류마다 코드를 늘리지 마라.
- 입면도·측면도로 솔리드를 만들지 않는다. 잘린 면이 아니라 투영 윤곽이라 깊이가 없다. 검증과 위치에만 쓴다.
- 구간 배정표는 종방향 뷰에만 있다. 횡단면은 "그 지점의 모습"일 뿐 어디까지 유효한지 말해주지 않는다.
- 뽑아낸 도형 목록에서 무엇을 쓸지 고를 때, 목록 순서가 아니라 치수로 지정한다. "2,700×2,750 인 것"처럼. 순서로 집으면 도면이 조금 바뀔 때 아무 말 없이 다른 도형을 집는다.
- 손으로 적은 수를 넣지 않는다. 도면과의 끈이 없는 값은 도면이 바뀌어도 안 바뀌고, 파라미터로 올릴 자격도 없다.
- 속을 파낼 때는 빈 곳 없이 꽉 찬 덩어리를 먼저 만들고, 거기서 뺀다. 외곽선이 오목하면 파인 자리에 애초에 재료가 없어 벽을 만들 수 없다. 빼는 솔리드는 한 번에 하나씩, 대상보다 조금 크게.
- 의도와 결과를 따로 잰다. IR을 직접 평가한 부피와 백엔드 결과 부피를 대조해야 "우리가 틀렸나 툴이 흘렸나"가 갈린다.
- 조용히 틀리느니 예외를 던진다. 못 찾거나 여럿이면 멈춘다. 자동 선택은 나중에 몇 시간짜리 추적으로 돌아온다.
1범위와 계층
1.1 입력과 출력
| 항목 | 내용 | 상태 |
|---|---|---|
| 입력 형식 | DWG (acadrust wasm, read-only). 한 모델스페이스에 여러 뷰가 나열된 형태 | 검증 R2018 |
| 입력 형식 — DXF | 파서가 DWG 전용이다. DXF는 ① 변환(ODA File Converter 등) 후 투입 ② DXF 리더 추가 중 하나. 엔티티 모델이 같으므로 하류는 그대로 | 가설 |
| 읽는 엔티티 | LINE · LWPOLYLINE → 선분, CIRCLE → 원(따로), TEXT/MTEXT → 표제·주기·치수 | 검증 |
| 버리는 엔티티 | ARC · ELLIPSE · SPLINE · 해치 · 블록 내부 로컬 좌표 | 부분 비중은 쟀다 — 교량 도면 7장에서 ARC 0~17%(거더배치 249/1,433). 원호가 형상인 도면은 아직 미검증 |
| 모델단위 | DWG 1단위가 몇 mm인가. 도면군마다 다르다 — 맨홀군 1m, 교량 삽도군 1mm. 게다가 도형마다 배율이 또 다를 수 있다(5.805배 사례) | 검증 cfg.unitMm · viewBoxes[뷰].unitMm |
| 치수의 성격 | 표기 치수가 숫자인가 기호(L·β·θ·VAR)인가. 기호뿐이면 방법론 전체가 기대는 「도면 치수와 대조」가 성립하지 않는다 | 검증 세어서 판정한다 (11.0) |
| 출력 | 솔리드 파트(이름·재질 부착) · 설계 파라미터 · 재질별 물량 · 부피 기준선 | 검증 |
| 출력 — IFC | IR의 extrude가 IfcExtrudedAreaSolid, union/difference가 IfcBooleanResult와 1:1. 매핑 초안은 있고 emit은 미구현 | 가설 |
1.2 계층 — 무엇이 어디에 있어야 하는가
이 경계가 방법론의 뼈대다. 무너지면 두 번째 도면에서 파이프라인이 아니라 복사본이 생긴다.
| 계층 | 아는 것 | 절대 몰라야 하는 것 |
|---|---|---|
도면 설정 drawings/*.mjs | 레이어명 · 뷰 제목 · 임계값 · 앵커 · 기준점 검증항목 · 조립 판단 | — |
코어 core/* | 파싱 · 뷰 분리 · 좌표 변환 · 폐곡선 추출 · 개구 기호 · 자기교차 | 도면 고유값 일체. '-전력구' 같은 문자열이 들어가면 설계가 틀린 것 |
파라미터 params.mjs · fit.mjs · rules.mjs | 스키마 · 식 · 검증 · 규칙 | 도면도 3D도 모른다 |
|
이 경계는 두 곳에서 이미 새 있었다(교량 도면에서 드러남). ① core/datum.mjs의 UNIT_MM = 1000 — 도면 고유값이 코어에 상수로 있었다.
같은 파일의 라벨 대역(±3)·뷰 분리 문턱(2)도 「1단위=1m」를 전제한 길이였다.② rules.mjs는 "도면도 3D도 모른다"고 적혀 있었지만 실제로는
챔퍼_시작X · 샤프트 · 맨홀_벽두께처럼 첫 도면의 파라미터 이름에 묶여 있었다.
두 번째 도면에서 undefined 비교가 전부 위상 위반으로 떨어져 build가 죽었다.교훈: 경계 위반은 두 번째 도면을 넣기 전에는 안 보인다. 문서에 "안 들어간다"고 쓰는 것으로는 안 지켜진다. | ||
IR ir.mjs | "이 프로파일을 이 축으로 밀고 저걸 뺀다" | 백엔드 사정 · 도면 사정 |
백엔드 emit/* | 모델러 명령 순서 · 비동기 대기 · boolean 이항 접기 | 도면 사정 |
2좌표계 · 원점 · datum
| 개념 | 무엇을 정하는가 | 이 저장소 | Revit 대응 |
|---|---|---|---|
| 프로젝트 원점 | 3축 원점 전체. 선언한다 | frame.origin | Project Base Point |
| datum | 그중 Z=0 평면 하나. 도면에서 검출한다(점이 아니라 선) | datum.levelLabel + checks | Level (기준 레벨) |
| 앵커 | 뷰별 가로·세로축 원점 (X·Y) | anchors.u0 / v0 | 없음 — Revit은 뷰가 원점을 공유한다 |
| 도면 월드 좌표 | 측량 좌표. 빼고 버린다 | DWG x,y (예 y=483623.477) | Survey Point / 공유 좌표 |
선형 구조물이면 X 원점을 측점에 맞추는 것이 유리하다(측점 0k000의 좌표를 프로젝트 원점으로).
그건 datum이 아니라 앵커 u0 로 준다 — 그 측점 선의 뷰 좌표를 u0에 넣고,
frame.origin에 "측점 0k000"이라고 적어 근거를 남긴다.
대상 도면은 측점이 아니라 구조물 최좌단을 X=0으로 잡았다(u0: 1000).
부분 측점 기반 원점은 미검증 — 곡선 선형·구배와 함께 12장 항목이다. 교량은 11장을 볼 것.
2.1 프로젝트 좌표계를 먼저 선언한다
축 셋과 원점을 문장으로 적고 시작한다. 이걸 안 적으면 뷰마다 다른 해석이 섞인다.
frame: {
x: '길이방향 (기점 → 종점)',
y: '폭방향 (중심선이 0, 평면도 위쪽이 +)',
z: '표고 (기준고가 0, 아래로 음수)',
origin: '구조물 최좌단 × 중심선 × 기준고',
}
| 구조물 종류 | X | Y | Z 원점 후보 |
|---|---|---|---|
| 선형 토목(전력구·암거·터널) | 측점 방향 | 중심선 기준 폭 | 도로 계획고 · 토공 계획고 · GL · EL 기준선 |
| 점형 토목(맨홀·수조·기초) | 도면 가로 | 도면 세로 | GL · 상단 슬래브 상면 |
| 건축 | 그리드 X 통심선 | 그리드 Y 통심선 | 1FL · 기준층 FL |
단위는 내부 mm 정수. 백엔드에 넘기기 직전에만 m로 나눈다. mm 정수 반올림이 DWG의
부동소수 노이즈(-500.00000000001455)를 자동으로 흡수한다. 반올림을 뒤로 미루면
boolean 단계에서 터진다. 검증
2.2 datum(Z=0 기준선) 검출 — 4단계
- 라벨이 한 무리인지 먼저 본다. 신규 라벨 y를 정렬해 2m 이상 벌어진 곳에서 끊는다. 무리가 둘 이상이면 공통 기준선이 없는 도면이므로 여기서 예외를 던진다. 아래 2단계의 숨은 전제가 바로 이것이다.
- 후보 수집. 여러 뷰에 반복해서 나타나는 수평선(또는 수직 그리드선)을 모은다. 출현 횟수는 잡음 문턱으로만 쓴다.
- 라벨 근접으로 확정. 후보가 둘 이상이면(계획고는 보통 도로/토공 두 줄이다) 라벨 바로 아래·옆의 선을 고른다. 라벨은 자기 선을 가리킨다.
- 선에서 값을 읽는다. 텍스트 삽입점이 아니다. 실제로 713mm 틀렸다.
- 도면 치수 2개 이상과 대조. 하나만 두면 우연히 맞을 수 있다. 하나라도 어긋나면 datum이 틀린 것이므로 다음 단계로 가지 않는다.
유형 삽도는 같은 라벨을 도형마다 반복한다. 횡단경사 삽도에서
교량 계획고·토공 계획고 라벨 10개가 y 27,533~81,177에 흩어져
평균 56,453은 어느 도형에도 속하지 않는 값이 됐다. 결과:
교량 계획고 → y=54,885.8
토공 계획고 → y=54,885.8 ← 정의상 다른 표고인데 같은 답
라벨별 국소 선 → 9개가 전부 다르다. 54,885.8 은 그중 어느 것도 아니다
예외가 아니라 그럴듯한 숫자가 나왔다. 규칙 12(조용히 틀리느니 예외)는
검출기 자신에게도 적용된다 — 전제가 깨졌는지 스스로 검사하지 않으면
실패가 답의 모습으로 나온다.
datum: {
levelLabel: /도로\s*계획고/, // 후보를 좁히는 힌트
checks: [ // 최소 2개. 도면에 적힌 치수를 그대로
['B-B 구체 상단', (v) => v['단 면 B-B']?.layerZ['-전력구']?.[1], -4907],
['B-B 기초 하단', (v) => v['단 면 B-B']?.layerZ['!-기초']?.[0], -7757],
],
}
먼저 확인할 것: 뷰별로 검출한 기준선 y가 전부 같은 값인가. 다르면 뷰마다 Z 오프셋이 필요하고, 방법론 비용이 한 단계 올라간다.
CS-GRND)뿐이고
계획고 라벨은 1장(10개)뿐이다. 나머지는 표고선 자체가 안 그려져 있고,
도형마다 제 바닥이 서로 다른 y에 앉는다.
당시 코드는 이 경우 예외로 죽었다 —
표고 기준선을 찾지 못했다.
그 자리에서 아무 수평선이나 자동으로 고르는 것은 틀린 대응이다(조용히 어긋난다).
대응(구현됨):
datum: {mode:'none'} 으로 검출을 끄고 DWG y를 그대로 두되,
표고는 뷰마다 anchors[뷰].v0 로 준다. 위 후보 ①이 그대로 맞았다 —
앵커 규칙을 세로축으로 확장한 것 이상이 필요하지 않았다.
중앙분리대 삽도에서 두 단면의 기준면 y가 실제로 달랐다(포장면 48,812.1 vs 바닥판면 49,096.5). 그 차이가 곧 「토공용은 포장에 49mm 묻히고 교량용은 바닥판 위에 얹힌다」라는 형상 정보였다.
datum.checks가 빈 배열이 되고,
G2가 「통과」가 아니라 「해당 없음」이 된다. 방법론은 이 구멍을 안 다뤘다.
대신 검증을 G3로 미룬다 — 폐곡선의 폭·높이가 도면 표기 치수와 맞는지로 옮긴다. 중앙분리대에서는 단면 폐곡선이
585 × 1,270으로 나온 것이 곧 기준·축척·앵커를
한꺼번에 검증한 것이었다. 다만 이건 늦은 검증이다 — 틀렸을 때 원인 후보가
기준·축척·앵커 셋으로 늘어난다.
3뷰의 일반 모델
도면 한 장을 3D 공간에 스티커처럼 붙인다고 보면 된다. 붙이려면 네 가지를 알아야 한다.
| 속성 | 묻는 것 | 예 |
|---|---|---|
plane | 어느 면에 붙이나 | 평면도 → XY면, 정면도 → XZ면, 측면도 → YZ면 |
flip | 뒤집어 붙였나 | 배면도는 정면도를 좌우 뒤집은 것 |
anchor | 어디에 맞춰 붙이나 | 이 그림의 가로 1,000 자리가 X=0 |
role | 붙여서 뭘 하나 | 형상을 만든다 / 검증만 한다 |
plane은 평면도가 아니라 좌표평면이다(용어가 겹치니 주의).
그림이 놓이는 면은 셋뿐이다.
XY면 ← 평면도 · 저면도 · 수평단면
XZ면 ← 정면도 · 배면도 · 종단면
YZ면 ← 좌측면도 · 우측면도 · 횡단면
같은 면에 놓인 것들끼리의 차이는 보는 방향뿐이고 그건 좌우 반전이다.
정면도와 배면도는 둘 다 XZ면에 놓이고 X 방향만 반대다.
도면 종류가 9개여도 처리 코드는 3개면 된다 — 종류마다 코드를 늘리지 마라.
현재 코드의
plan/long/cross/horizontal이라는 kind 이름은 이 사실을 가린다.
3.1 뷰 → 평면 대응표
| 뷰 | 보는 방향 | 평면 | 도면 (u,v) → | 반전 | 절단면? | 상태 |
|---|---|---|---|---|---|---|
| 평면도 | −Z (위에서) | XY | (X, Y) | — | 아니오 | 검증 |
| 저면도 | +Z | XY | (X, −Y) | flipV | 아니오 | 가설 |
| 수평 단면 | −Z | XY | (Y, X) swapUV | 도면마다 | 예 | 검증 |
| 정면도 | +Y | XZ | (X, Z) | — | 아니오 | 가설 |
| 배면도 | −Y | XZ | (−X, Z) | flip | 아니오 | 가설 |
| 종단면 | ±Y | XZ | (X, Z) | 절단 방향 | 예 | 검증 |
| 좌측면도 | +X | YZ | (Y, Z) | 투상법 | 아니오 | 가설 |
| 우측면도 | −X | YZ | (−Y, Z) | flip | 아니오 | 가설 |
| 횡단면 | ±X | YZ | (Y, Z) | 절단 방향 | 예 | 검증 |
3.2 뷰 역할 — 무엇을 주는 뷰인가
평면만 정해서는 부족하다. 같은 XZ 평면이라도 종단면과 정면도가 하는 일이 전혀 다르다. 역할을 배정하지 않으면 입면도로 압출하는 사고가 난다.
| 역할 | 주는 것 | 전형적인 뷰 | 쓰는 법 |
|---|---|---|---|
| 생성 generator | 재료 영역 폐곡선 → 그대로 압출·로프트 | 횡단면 · 종단면 · 수평단면 | loop(뷰, 폭, 높이).ring |
| 배정 assigner | 어느 구간에 어느 단면이 유효한가 | 종단면 (선형은 필수) | 수평선을 Z 내림차순 × X 범위로 늘어놓는다 |
| 위치 locator | 개구부·관통·부속물의 좌표 | 평면도 · 수평단면 · 입면도 | opening() · circles() · rect()+expect() |
| 검증 validator | 만든 3D가 맞는지 대조할 윤곽·치수 | 입면도 · 좌/우측면도 · 상세도 | 3D를 그 방향으로 정투영해 윤곽 비교 |
판별법: 그 뷰에 해치(절단면 표시)가 있으면 단면, 없으면 투영이다. 제목만 믿지 말 것.
3.3 앵커와 flip 판정 절차 검증
뷰 안의 좌표는 뷰 좌단이 0인 u와 기준고가 0인 v다. 프로젝트 축으로 옮기려면 뷰마다 "어느 u가 프로젝트 0인가"를 정해야 한다. 투상법(1각/3각)을 가정하지 마라 — 도면마다 다르고, 틀려도 치수만 보는 동안은 안 걸린다.
- 뷰 폭·높이를 도면 표기 치수와 대조. 여기가 안 맞으면 뷰 분리부터 틀린 것이다.
- 앵커를 지어내지 말고 유도한다. 예: 뷰 세로 범위 −11,678~−1,478이 전폭 10,200이고 아래 끝이 Y=−1,900이면
v0 = -11,678 + 1,900 = -9,778. - 비대칭 특징 하나로 flip을 판정한다. 중심선 대칭 형상만 보면 뒤집힘이 안 드러난다. 샤프트·개구부·노치처럼 한쪽에만 있는 것을 고른다.
- 다른 뷰에서 이미 확정한 좌표와 교차검증. 최소 2개. 세 개면 확정이다.
// 실제로 이렇게 확정했다 — 셋이 동시에 맞으면 앵커는 틀릴 수 없다
샤프트 사각 1,300×1,300 v -10,428..-9,128 → Y -650..+650 중심선 대칭 ✓
Ø900 보어 2개 v -9,778 → Y 0 ✓
맨홀 내부 2,000×7,200 v -11,278..-4,078 → Y -1,500..5,700 ✓
expect(뷰, 폭, 높이)로 치수만 확인하는 동안은 v0가 없어도 아무 문제가
없었다. 원(CIRCLE) 좌표를 쓰는 순간 보어가 Y=−9,778로 튀어나왔다.
좌표를 쓰기 시작하면 숨어 있던 앵커가 드러난다. 수평 단면에서 이미 겪은 문제를
평면도에서 반복했다 — "세로축이 표고겠거니" 하고 넘어갔기 때문이다.
축을 뒤집거나 맞바꾸면 감김 방향이 반대가 된다. 뒤집기 횟수가 홀수면 꼭짓점 순서를 되돌려 면적을 양수로 유지한다. 코어가 자동으로 하지만, 새 평면을 추가할 때 잊기 쉬운 자리다.
3.4 뷰 분리 알고리즘 부분
여러 뷰가 한 모델스페이스에 나열돼 있으면 먼저 갈라야 한다. 간격 임계값 하나로는 절대 안 된다 — 넓게 잡으면 이웃이 딸려오고 좁게 잡으면 한 뷰가 쪼개진다. 둘 다 실제로 터졌다.
· (가) 실패 — 상세 확대 테두리 상자가 상세행과 단면행을 세로로 잇는다. 임계값을 10mm까지 좁혀도 조각이 1개였다. 구조선과 같은 레이어·같은 색이라 걸러낼 수도 없다.
· (나) 실패 — 표제 중복률 47~84%.
단면A가 4벌, 교량 시·종점이 6벌.
교량폭원은 뷰 제목이 아예 없다(표제는 시트명 하나, 나머지는 A·B·L of Road).
대응(구현됨):
cfg.viewBoxes — 뷰마다 {box:[x0,y0,x1,y1], kind}를 설정에 적는다.
선분 단위로 나눈다(조각 단위로 나누면 뷰를 잇는 다리 조각이 통째로 한쪽에 붙는다).
양 끝이 다 들어오는 선만 취해 경계를 넘나드는 선을 자동으로 버리고, 상자가 겹치면 예외를 던진다.
제목이 있고 간격이 있는 도면에서는 아래가 여전히 최선이다 — 좌표를 설정에 적지 않아도 되기 때문이다.
1. 구조 레이어 기하만 모은다 (넉넉한 struct 정규식)
2. x ↔ y 를 번갈아 가며 더 이상 갈라지지 않을 때까지 잘게 쪼갠다 (임계 0.2m)
↳ y로 나눈 뒤에야 드러나는 x 간격이 있다. 한 번씩만 하면 부족하다
3. 조각마다 "바로 위에 있는 표제"를 찾아 귀속시킨다
4. 같은 표제를 가진 조각끼리 합치면 그게 뷰다
| 장치 | 왜 | 튜닝 값 |
|---|---|---|
| 표제 최소 글자 높이 | 부재 방향 라벨(h=0.30)이 표제로 잡히면 뷰를 훔쳐간다 | minHeight — 도면의 글자 높이 분포를 먼저 세어본다 |
| 표제 세로 여유 | 크게 잡으면 옆 표제가 뷰를 통째로 가져간다 (0.6m→0.2m로 해결) | ySlack |
| 상세도·재료표도 표제 후보에 넣기 | 자기 조각을 가져가므로 뷰로 새어들지 않는다. 뷰가 아닌 표제에 귀속된 조각은 버린다 | — |
| 시트 영역 밖 버리기 | 유령 기하와 블록 로컬 좌표가 bbox·축척을 깨뜨린다 | sheet: {x0,x1,y0,y1} |
sheet는 사각형 하나다. 도면이 여러 장으로 나뉘어 각각 다른 좌표대에 있으면
배열로 확장하고 시트별 오프셋을 둬야 한다. 대상 도면은 4매가 한 좌표대에 있어 필요 없었고,
교량 삽도도 도형이 한 덩어리라 필요 없었다(빈 시트 테두리가 옆에 하나 더 있었지만 유령이다).
축척 혼재는 실재한다. 같은 삽도 묶음 안에서 교량폭원은 1:1인데 중앙분리대 방호벽 단면은 5.805배였다.
unitMm을 뷰 스펙에 두는 것으로 해결했다.
4절차 — 11개 게이트
3D부터 세우면 어디가 틀렸는지 알 수 없다. 아래 순서를 건너뛰지 않는다. 각 게이트는 통과 기준이 있고, 실패하면 그 자리에서 멈춘다.
| 게이트 | 통과 기준 | 실패 시 도구 | 대표 함정 |
|---|---|---|---|
| G0 백엔드 준비 | 모델링 명령이 등록돼 있는가 (연결 여부가 아니다) | egm guide 개수 — 39면 뷰어 모드 |
파이프는 살아 있고 status도 정상인데 생성만 조용히 실패 |
| G1 파싱·시트 | 엔티티 수가 나오고, 시트 밖 좌표가 걸러졌는가 | inspect — 레이어 무관 전수 |
유령 기하 · INSERT 블록 로컬 좌표 · 캐시 파일명 슬러그 충돌 |
| G1.5 도면 적격 신규 | 표기 치수가 숫자인가(기호 L·β·θ·VAR 비율) · 모델단위·축척이 확정되는가 · 구조 윤곽이 닫히는가 |
치수 텍스트 분류 계수 · DIMENSION.actualMeasurement ↔ 치수 글자 대조 · gaps의 「선 없음」 비율 |
기호 치수뿐이라 대조할 상대가 없다 · 단위가 1000배 · 구성 도해라 애초에 안 닫힌다 |
| G2 기준점 | 도면 치수 2개 이상과 일치. 기준선이 없는 도면이면 「해당 없음」으로 명시하고 검증을 G5로 미룬다 | datum.checks 확장 · datum.mode:'none' + 뷰별 v0 |
텍스트 위치로 잡음(713mm) · 계획고 후보 둘(420mm) · 기준선이 아예 없는데 아무거나 자동 선택 |
| G3 뷰 분리 | 뷰 폭·높이가 도면 표기와 일치. 서로 다른 뷰의 심도가 같게 나오는가 | 임계값·minHeight 조정 → 안 되면 viewBoxes(명시 영역) |
이웃 뷰 혼입(폭 부풀음) · 뷰 잘림(하단 누락) · 제목 없음/중복 · 테두리 상자가 뷰를 이어 조각이 1개 |
| G4 평면·앵커·flip | 비대칭 특징 좌표가 다른 뷰와 일치 (2개 이상). 뷰별 축척이 다르면 여기서 확정한다 — 표기 치수 2개로 역산 | 3.3 절차 · viewBoxes[뷰].unitMm |
수평 단면의 세로축을 Z로 봄 · 평면도 v0 누락 · 도형마다 축척이 다름 |
| G5 프로파일 품질 | 자기교차 0. 꼭짓점 수가 예상과 맞는가. 끊김의 원인이 분류됐는가. 폐곡선의 폭·높이가 도면 표기와 맞는가 — G2를 건너뛴 도면은 여기가 유일한 검증점이다 | gaps · --loops · report(그림) |
개구 기호가 링 오염 · 가구(케이블랙) 혼입 · 레이어 오분류 · 끊김 0인데 폐곡선 0(겹치는 동일선상 선분) · 윤곽이 진짜로 안 닫힘 |
| G6 조립(IR) | 참조 무결성 · 소모 파트 keep 플래그 · loft 단면 꼭짓점 수 일치 |
validateIR |
loft 위상 불일치 · 중간 파트가 최종에 남음 |
| G7 파라미터화 | 등급 분포가 납득되는가. 위상 규칙 위반 0 | fit → params.json · rules |
도면과 끈 없는 값을 파라미터로 올림 |
| G8 좌표 검산 | --dry 출력의 좌표가 예상과 같은가 |
명령 목록 육안 검산 | 압출 방향 미지정 · 단위 환산 누락 |
| G9 생성·부피 | 파트 수 == modelCount. IR 의도 부피 == 실측 부피. 기준선 회귀 통과 |
measure --ir · --primitives |
boolean이 인자를 흘림 · 면으로만 맞닿은 union · 커터가 자기 부피보다 많이 뺌 |
4.1 새 도면 인테이크에서 실제로 드는 시간
| 단계 | 사람 판단 | 비용 |
|---|---|---|
| 파싱 · 끊김 진단 · 폐곡선 추출 · 명령 생성 | 없음 | 0 — 도면이 바뀌어도 그대로 돈다 |
| 레이어 · 뷰 제목 · 앵커 · 기준점 검증항목 | 설정값 채우기 | 설정 파일 하나. 반나절 |
| 조립 스펙 | 필요 — 여기 한 군데뿐 | 형상 복잡도에 비례 |
| 변형 도면(같은 형식, 다른 치수) | params 값만 | 도면을 아예 안 읽는다 |
4.2 그림으로 읽는다 — 기하가 못 주는 의미
기하 파싱은 "이 선이 무엇인지" 모른다. 구체 외곽선인지 치수보조선인지, 여기가 벽인지 개구인지 좌표만으로는 안 갈린다. 실제로 맨홀 좌측 X=15,400 이 벽인지 개구인지 선만 보고는 판정이 안 됐다. 사람은 그림을 보면 즉시 안다.
그래서 그림과 좌표를 한 쌍으로 묶는다. 이게 조립(5장) 판단의 재료다 — 조립이 "사람 판단이 들어가는 유일한 자리"인데, 그 판단을 그림 없이 하면 추측이 된다.
①캡처 ②사이드카 ③조회
도면 ────────────→ 이미지 ──── 픽셀↔좌표 변환 ────→ 프로젝트 mm ────→ 엔티티·링·레이어
↑ │
└──────────── ④오버레이(추출 결과를 그림 위에) ─────────┘
사람: 이미지에 표시 → ②로 좌표 확정 → ③으로 "그 자리에 무슨 엔티티가 있나"
캡처 규칙 셋
| 규칙 | 왜 |
|---|---|
| 전 레이어를 그린다 | 우리가 버린 것까지 보여야 "왜 안 잡혔나"를 판단한다. 프로파일 레이어는 진하게, 나머지는 흐리게. 파싱이 버리는 ARC·ELLIPSE도 그린다 — 그게 빠진 자리가 보여야 한다 |
| 축척은 지정하고 캔버스를 역산 | 렌더 결과에서 축척을 되짚으면 여백·DPI·레터박싱 어딘가에서 반드시 어긋난다. 뷰 bbox는 이미 아니까 "5 mm/px"을 정하고 캔버스 크기를 거기서 계산한다 → unitsPerPx가 계산값이 아니라 내가 정한 값이 된다 |
| 사이드카를 같이 떨군다 | 이미지만 있으면 "여기"를 말로 설명해야 한다. 변환식이 붙어 있으면 픽셀이 곧 좌표가 된다 |
사이드카 — 그림과 좌표를 잇는 다리
{ "view": "단 면 B-B", "kind": "cross",
"image": { "w": 1675, "h": 1388 },
"originDwg": [-48351.647, 483619.770], // 이미지 좌상단 픽셀의 도면 좌표
"unitsPerPx": 0.004, // 내가 정한 값 (역산 아님)
"yFlipped": true, // 화면 y는 아래로, 도면 y는 위로
"datumY": 483623.477, // 프로젝트 Z=0
"viewLeftDwgX": -48350.447, // 뷰 좌단 = 로컬 u=0
"anchor": { "u0": 2150 },
"howTo": [ "dwgX = originDwg[0] + px * unitsPerPx",
"dwgY = originDwg[1] - py * unitsPerPx",
"u(mm) = round((dwgX - viewLeftDwgX) * 1000)",
"z(mm) = round((dwgY - datumY) * 1000)",
"프로젝트 좌표 = anchor 적용 (u0/v0/flip/swapUV)" ] }
-환기구의 이 선분
때문에 링이 안 닫힌다"로 확정된다. 말로 주고받으면 서로 다른 곳을 본다.
지금 되는 것 / 없는 것
| 단계 | 수단 | 상태 |
|---|---|---|
| ① 뷰별 캡처 (SVG·PNG) | capture.mjs. --crop으로 접합부만 확대 | 있다 |
| ② 픽셀 → 프로젝트 좌표 | 사이드카 .json | 있다 |
| ③ 좌표 → 엔티티 조회 | inspect.mjs(뷰 전수) · probe.mjs --ring(링 좌표) — 사람이 목록에서 눈으로 찾는다 | 거칠다 |
| ③′ 점·영역으로 엔티티 집기 | "이 좌표 반경 200mm 안의 선분·링·원·개구부를 나열" 하는 도구 | 없다 |
| ④ 추출 결과를 도면 위에 겹쳐 그리기 | report.mjs는 링만 따로 그린다. 캡처 위 오버레이는 아니다 | 없다 |
| 3D 결과를 같은 규칙으로 렌더 | render.mjs → model-X/Y/Z.svg + 치수선 | 있다 |
③′와 ④가 다음에 만들 것이다. 둘이 있으면 왕복이 닫힌다 — 사람이 그림에 표시 → 엔티티 확정 → 설정 한 줄 수정 → 다시 그림으로 확인. 지금은 ③에서 목록을 눈으로 훑느라 끊긴다.
3D 렌더도 같은 축척 규칙으로 뽑아야 도면과 나란히 놓고 대조된다. 입면도·측면도 검증(3.2 validator)이 붙는 자리가 여기다.
5조립 스펙(IR) — 사람 판단이 들어가는 유일한 자리
5.1 IR 연산
| op | 필드 | 쓰는 곳 | 상태 |
|---|---|---|---|
box | min[x,y,z], max[x,y,z] | 순수 직육면체 | 검증 |
extrude | profile, axis, from, to, cap, drift | 구간 내 단면 불변. drift 면 빗각 기둥(단면이 평면 안에서 미끄러짐) | 검증 |
loft | sections:[{profile, at}, …] | 두 지점 사이 단면 선형 변화 | boolean 금지 결과가 안 닫힌다 → 11.4.6 |
intersect | members:[id,…] | 두 방향이 동시에 맞아야 하는 형상 — 평면 윤곽 ∩ 정면 윤곽 | 검증 |
cylinder | radius, from, to | 원형 관통·기둥 | 부분 Z축 한정 |
union | members:[id,…] | 봉투 만들기 · 구간 합치기 | 검증 |
difference | base, cutters:[id,…] | 공동·개구부 | 검증 |
sweep | (측점 기반. 미구현) | 곡선 선형·구배 | 가설 |
5.2 연산 결정 트리
이 형상을 어떻게 만들까?
┌ 단면이 구간 내내 같은가?
│ 예 → extrude. 압출축 = 단면 평면의 법선
│ 아니오 ↓
├ 두 지점 사이에서 선형으로 변하는가?
│ 예 → loft. ★ 꺾이는 자리마다 단면을 놓고 구간을 끊는다
│ 아니오 ↓
├ 축이 곡선이거나 구배가 있는가?
│ 예 → sweep (미구현). 지금은 구간을 잘게 끊어 loft/extrude로 근사
│ 아니오 ↓
├ 원형 관통·기둥인가?
│ 예 → cylinder. 지름으로 집는다. 선분과 섞지 않는다
│ 아니오 ↓
└ 층 구조가 수평 단면들로 주어지는가?
예 → 층별 XY 프로파일을 Z로 쌓아 union (조각이 Z로 진짜 겹치게)
5.3 조립 5원칙
① 치수로 집는다
순서(loops[0])로 집으면 도면이 조금만 바뀌어도 조용히 다른 면을 집는다. 면적 순위는 흔들리고 치수는 안 흔들린다. 못 찾거나 여럿이면 예외를 던진다.
loop('단 면 B-B', 2700, 2750).ring
circles('평 면 도', 900, /^-전력구$/)
opening('단 면 H-H', 1200, 2000)
② 꽉 찬 덩어리부터 만든다
외곽선이 오목하면(ㄷ자·계단형) 그대로 압출한 솔리드는 파인 자리가 비어 있다. 그 자리에 벽을 세우려고 공동을 빼도 뺄 재료가 없어 아무 일도 안 일어난다. 파인 면을 따로 압출해 합쳐서 빈 곳 없는 덩어리를 만든 다음에 뺀다.
shell = 오목한 외곽 압출
fill = 파인 면 압출 ← 폐곡선 추출이 이미 뽑아준 면
solid = union(shell, fill) ← 여기서 비로소 꽉 찬다
body = solid − 공동들 ← 이제 벽이 남는다
③ 구간은 합친 뒤 한 번만 뺀다
단면이 다른 구간을 각각 빼면 경계에서 반드시 구멍이 난다 — 큰 쪽 공동이 여유만큼 작은 쪽으로 넘어가 단차 벽면을 파먹는다. 공동끼리는 전이점에서 겹치지도 벌어지지도 않게 나눈다.
④ 커터는 하나씩, 밖으로
떨어진 커터끼리는 교차하지 않아 union이 죽는다. 커터는 하나씩 먹인다. 대상 면에서 정확히 시작하면 boolean이 불안정해지므로 모든 방향으로 여유(OVER)를 준다.
⑤ 꺾이는 자리마다 단면
loft는 두 단면 사이를 곧게 이을 뿐 꺾인 자리를 만들지 못한다. 꺾이는 좌표가 방향마다 다르면(옆·위 −300, 아래 −100) loft를 또 끊는다. 중간 단면 값은 상수로 박지 말고 내분점을 코드가 계산하게 둔다.
보조: 수직 단차는 저절로 생긴다
앞 구간이 작게 끝나고 뒤 구간이 크게 시작하면 그 차이가 곧 단차 벽면이다. 따로 만들 것이 없다. 종방향 헌치만 별도 쐐기로 되붙인다.
5.4 압출축과 벽 방향 검증
YZ 단면을 X축으로 밀면 → Y 방향 벽만 생긴다. X 방향은 열린다
XZ 단면을 Y축으로 밀면 → X 방향 벽만 생긴다. Y 방향은 열린다
나머지 방향 벽은 두 단면을 교집합할 필요 없이 공동의 압출 범위를 좁혀서 만든다. 외곽은 전 구간을 채우고 공동만 안쪽으로 줄이면 앞뒤로 벽이 남는다. 공동의 끝이 솔리드 내부라 면이 겹치지 않아 여유도 필요 없다.
단면이 어느 평면인지는 절단 기호가 결정한다. 절단선 양끝 두 점의 x가 같으면 YZ, y가 같으면 XZ다. 폭 치수만으로는 구분이 안 되는 경우가 있다(X도 2,600, Y도 2,600).
5.5 프로파일이 못 쓸 때 — bbox 재구성
링은 못 믿어도 bbox는 믿을 수 있다. 형상이 직사각형이라는 걸 알면 bbox만으로 복원된다.
단, expect()를 남겨 도면과의 끈을 유지한다 — 값이 어긋나면 거기서 걸린다.
expect('단 면 H-H', 1200, 2000, '환기 OPEN부 (2.0x1.2)'); // 실재 확인만
profile('mh.ventHole', 'XY', rect(15800, -500, 17000, 1500)); // 실제 프로파일
오목한 외곽은 rect()로 못 만든다. 그때는 X자 교차 분할이 필요하거나,
수평 단면에서 층별로 다시 세우는 쪽이 낫다.
6파라메트릭화
DWG ──fit──→ params/<이름>.json ──build --params──→ IR ──→ 3D
↑ 사람은 여기만 만진다. build 는 도면을 안 본다
loop('단 면 B-B', 2700, 2750)처럼
치수를 도면 조회 키로 쓰는 한, 사용자가 2700을 3000으로 바꾸면 그런 폐곡선이 없어 예외가 난다.
도면에 묶여 있으면 파라메트릭이 성립하지 않는다.
검증:
views.json을 치운 채 --params로 빌드해 명령 목록이 바이트 단위로 동일했다.
6.1 값 하나의 생김새
{
"구체_외폭": { "v": 3800, "src": "measured", "from": "단 면 C-C 외곽 폭",
"fitted": 3800, "grade": "exact" },
"벽두께": { "v": 400, "src": "design", "min": 300, "max": 600, "step": 50 },
"구체_내폭": { "expr": "구체_외폭 - 2*벽두께" },
"샤프트": [ { "no":1, "x0":1900, "x1":3200 }, { "no":2, … } ]
}
| 필드 | 뜻 | 파생 효과 |
|---|---|---|
src: measured | 도면에서 뽑았다 | 바꾸면 "도면과 다름" 경고. 막지는 않는다 — 설계를 바꾸는 게 목적이다 |
src: design | 사람이 정한다 | 자유롭게 바꾼다 |
expr | 식 → 자동으로 derived | 위상 정렬로 평가. 순환이면 예외(구속 솔버가 아니다) |
min/max/step | 유효 범위 | 검증이자 UI 위젯이 그대로 나온다 |
fitted | 도면에서 뽑은 원값 | 현재값과 비교해 도면 이탈 표시 |
| 배열 | 치수가 아니라 개수가 바뀌는 것 | 샤프트 2→3, 환기구 없음 |
6.2 재현 등급 — 파라미터로 못 만드는 형상 다루기 검증
프로파일을 파라미터로 "생성"하면 도면의 실제 형상을 잃는다. 50mm 보도 단차 같은 자잘한 것이 생성기로 재현이 안 되면 조용히 빠진다. 그래서 완전일치를 요구하지 않고 등급을 매긴다.
| 등급 | 조건 | 처리 |
|---|---|---|
exact | 꼭짓점 집합이 mm 단위로 일치 | 파라미터로 완전 대체 |
tolerant | bbox·면적·꼭짓점 수 일치, 개별 점 ±ε | 경고를 남기고 파라미터 사용 |
opaque | 생성기로 재현 불가 | 도면 링을 좌표째 얼려 params에 박는다 |
opaque가 핵심이다. "재현 못 하면 파라미터화 안 한다"가 아니라 "재현 못 하는 링은 값으로
얼려 넣는다". 그러면 build가 도면을 안 봐도 되면서 자잘한 형상이 빠지지도 않는다.
그 링은 못 바꾸는 것일 뿐이고, 그게 정직하다.
등급 분포가 곧 "이 도면이 얼마나 파라메트릭한가"의 척도다
(대상 도면: opaque 8 · exact 2 · tolerant 1).
6.3 파라미터로 올릴 자격 검증
const BORE_R = 450; // 원본 엔티티에서 눈으로 읽어 적음 — 이런 값은 바꿔도
도면과 대조할 방법이 없다. 도면이 바뀌어도 안 바뀐다. 먼저 circles()·loop()·
opening()으로 도면에서 집게 만들고, 다른 값과 대조해 어긋나면 빌드가 죽게 한 뒤에
파라미터로 올린다.
6.4 규칙 층 — 폼과 도구를 가르는 것
| 종류 | 뜻 | 동작 | 예 |
|---|---|---|---|
| 위상 topology | 형상을 만들 수 없게 된다 | 막는다 (exit 1) | loft 단면 위치 역전 · 벽두께가 절반 초과 · 보어가 벽을 뚫음 · 구간이 안 맞닿음 |
| 설계 design | 만들어지지만 기준 미달 | 경고. 사람이 판단 | 내공 높이 < 2,100 · 토피 < 1,000 · 최소 벽두께 · 개구 Ø600 |
규칙은 부재 종류에 붙는 것이지 도면에 붙는 게 아니다. 도면별 예외가 필요하면 문턱 자체를 파라미터로 노출한다. 모델러까지 가서 비틀린 면으로 만나는 것보다 훨씬 싸다.
6.5 편집 UI는 얇다 검증
build --params → emit → run --reset --export → measure --ir → render --dims
파이프가 이미 CLI 단계 사슬이라 UI가 한 일은 params 편집 + 그 사슬 순서대로 실행뿐이다.
min/max/step이 그대로 입력 위젯이 됐고 src는 태그, fitted는 변경 표시가 됐다.
갱신 버튼이 검증을 태워야 도구가 된다 — 단순 실행이면 폼이다.
② 갱신 1회당 씬 초기화 1번. 초기화를 연달아 반복하면 모델러 파이프가 죽는다.
7검증 체계
7.1 의도와 결과를 따로 잰다 검증
부피가 이상할 때 물어야 할 것은 둘이다. 이걸 못 가르면 엉뚱한 데를 판다 — 실제로 손으로 기대값을 세 번 조립했는데 세 번 다 커터 범위를 놓쳐 틀렸고, 그 값으로 모델러를 의심하며 시간을 버렸다.
우리 조립(IR)이 틀렸나? ← 의도 : IR을 격자로 CSG 평가 (모델러 없이)
백엔드가 IR대로 안 만들었나? ← 결과 : OBJ 삼각형을 발산 정리로 실측
파트 부피 m³ IR 의도 차이
전력구_구체 100.5403 100.5350 0.0053 ✓
맨홀2_구체 65.3577 74.6819 −9.3242 ✗ ← 여기만 백엔드가 흘렸다
격자 평가는 25mm 격자에서 0.1% 안팎이라 소수 셋째 자리는 못 보지만, boolean이 죽는 사고는 언제나 m³ 단위로 어긋나므로 충분하다.
대응: boolean이 없으면 격자를 안 쓴다 —
box는 변의 곱,
extrude는 신발끈 면적 × 길이, loft는 심프슨(꼭짓점 선형 보간이면
단면적이 2차식이라 정확), union은 멤버 bbox가 안 겹치면 합.
전부 0.0000 ✓로 떨어졌다. 출력에 해석/격자를 같이 찍는다.
교훈: 검사기가 거짓 경보를 내면 사람이 ✗를 무시하게 되고, 그때 진짜 사고가 지나간다.
7.2 이상 신호 우선순위
| # | 신호 | 확정력 | 뜻 |
|---|---|---|---|
| 1 | 자기교차 링 | 확정 | 그 자체가 결함. 압출하면 솔리드가 쓰레기가 되고 이후 boolean이 조용히 무력화된다 |
| 2 | 커터가 자기 부피보다 많이 뺌 | 확정 | 물리적으로 불가능 → boolean이 죽었다. 형상 논쟁이 필요 없다 |
| 3 | 파트 수 ≠ modelCount | 확정 | boolean이 인자를 흘렸다. 남은 커터가 겹쳐 있어 겉보기엔 멀쩡하다 |
| 4 | IR에 없는 그룹이 결과에 등장 | 확정 | 3과 같은 사고인데 어느 커터가 남았는지까지 알려준다 |
| 5 | 뷰 폭·높이 ≠ 도면 치수 | 강함 | 뷰 분리 또는 앵커 문제 |
| 6 | 꼭짓점 수 이상 | 의심 | 4=정상 사각형 · 8=개구 기호 오염 · 114=가구 혼입 · 10=헌치 있는 정상 |
| 7 | 의도 vs 실측 부피 차 | 강함 | 차이가 일정하면 union 조각 손실, 커터마다 다르면 커터 문제 |
| 8 | 기준선 회귀 실패 | 강함 | 이전에 사람이 확인한 형상이 바뀌었다 |
| 9 | 열린 변(비다양체) | 참고 | 이음매 T 정점. 부피·렌더링은 정상이나 IFC에서 걸릴 수 있다 |
7.3 기대값은 자동으로 안 나온다 — 그리고 그게 맞다
커터는 관통을 확실히 하려고 대상 밖으로 튀어나와 있고 공동끼리 겹치기도 한다. 그래서
Σ멤버도 base − Σ커터도 틀린 값이 된다. 틀린 기대값은 기대값이 없는 것보다 나쁘다.
새 형상의 기대값은 원시 파트 부피표를 재료로 사람이 한 번 조립하고, 맞으면 기준선으로 얼린다.
두 단면 사이 면적은 t의 2차식이므로 심프슨 V = L·(A0 + 4·A(½) + A1)/6이
근사가 아니라 정확하다. loft 검산에 그대로 쓴다.
baselines/<도면>.volumes.json은 out/과 달리 재생성으로 복구되지 않는다.
사람이 한 번 확인한 부피가 남아 있어야 다음 변경이 형상을 건드렸는지 알 수 있다.
7.4 그림으로 보는 검증 검증
- 추출 결과 뷰어 — 뷰별 링을 SVG로 그리고 자기교차·꼭짓점 수·원·IR·부피를 한 장에. 숫자로 반나절 걸린 원인이 그림에서는 빨간 X 두 개로 즉시 보였다.
- 도면 캡처 + 좌표 사이드카 — 픽셀↔도면 좌표 변환을 JSON으로 같이 떨군다. 이미지에서 짚은 지점이 곧 프로젝트 좌표가 된다.
- 3D 정투영 렌더 — 만든 모델을 X·Y·Z로 투영. 입면도·측면도 대조가 여기 붙는다(3.2의 validator 역할).
- 치수선 렌더 — 파라미터를 가리키는 치수선을 렌더에 얹는다. 2D 도면이 치수로 형상을 증명하듯이.
8함정 사전
전부 실제로 밟은 것이다. 증상 → 원인 → 대응 순.
8.1 도면·데이터
| 증상 | 원인 | 대응 |
|---|---|---|
| bbox·축척이 통째로 이상 | 유령 기하가 먼 좌표대에 있다 | sheet 영역 밖을 먼저 버린다 |
| 원점 근처 뷰에 남의 원이 무더기 | INSERT 블록 로컬 좌표 | 같은 시트 필터로 거른다 |
| 뷰가 통째로 빠진다 | 구조 외곽선이 뷰마다 다른 레이어에 있다 | 후보 레이어를 넉넉히. struct는 넓게 |
| 프로파일 꼭짓점이 100개 넘음 | 케이블랙·철근 같은 가구가 섞임 | body는 좁게. struct와 분리 |
| 폐곡선이 안 닫힘 | 대부분 레이어 오분류(선은 있다) | 진단 도구로 "오분류 / 누락"을 가른다 — 대응 비용이 100배 차이난다 |
| 기존 닫힌 폴리라인이 쓸모없음 | 전부 해치·심볼 조각 | 기대하지 말 것. 좌표만 뽑아 새로 만든다 |
| 치수 정의점이 0으로 옴 | 파서 한계 | actualMeasurement·textMidPt는 정상. DEFPOINTS 활용 |
| 그려진 길이가 실제가 아니다 | 파단선. 기둥이 6,100 으로 그려져 있는데 실제 33,800. 폐곡선·부피가 그럴듯해 숫자로 안 걸린다 | CS-CUTL 류가 뷰 안에 있으면 의심한다. 생략량은 표고 텍스트 차 − 도형 길이 로 되찾는다 |
| 한 파일에 도형이 여러 벌 쌓여 있다 | 도면집 스택. 제목·치수 없는 잔재가 65,000 간격으로 17벌 | 뷰 상자를 눈으로 잡지 말고 도곽 블록에서 계산한다 — INSERT 위치 × scale × 용지(A1 841×594) |
| 「단면도」 안쪽 선을 다른 부재로 읽음 | 절단면이 아니라 투영이다. 먼 쪽 모서리가 겹쳐 보인다 | 그 치수가 외곽 − 2×R 로 떨어지면 투영이다 |
| 평면 모서리가 45° 모따기 | 파서가 LWPOLYLINE bulges 를 버려 90° 호가 현 하나로 눌린다 | 호 끝점은 정확하므로 R 은 되찾는다. 링 면적을 그대로 쓰면 코너당 71,350 mm² 부푼다 |
| 구조 형상이 표로 잡힌다 | 격자로 보면 평면도가 73행×83열짜리 표다 | 표 색인에 레이어 필터(CS-TABL-*)를 준다 |
| 다른 DWG인데 엔티티 수·좌표가 똑같다 | 파싱 캐시 키 충돌. 두 번 다르게 밟았다.
① 슬러그 [^\w.-]→_ — \w가 한글을 안 먹어 거더배치·교대배치·교량폭원이 같은 이름이 됐다.
② 고쳐서 쓴 목록 인덱스 — 폴더에 파일이 하나 늘자 전부 한 칸씩 밀려 [6]이 옆 도면 캐시를 읽었다 |
캐시 키는 파일 경로 해시. 인덱스는 목록 순서에, 슬러그는 문자 집합에 의존한다. 캐시는 틀린 답을 조용히 재사용하므로 "같은 결과가 나온다"를 우연으로 넘기지 말 것 |
| 모든 치수가 정확히 1000배 | DWG 모델단위가 도면군마다 다르다. 맨홀군 1단위=1m, 교량 삽도군 1단위=1mm | unitMm을 설정으로. DIMENSION.actualMeasurement와 치수 글자를 비교하면 즉시 확정된다(3000.00 ↔ 3.000m) |
| 도면 표기 치수와 대조가 불가능 | 유형별 삽도는 치수가 기호다 — L · L1 · β · θ · VAR · n@3,600 | 숫자 치수 대 기호 치수를 세어서 먼저 판정한다. 기호뿐이면 G2를 통과할 방법이 없으므로 그 도면은 대상에서 뺀다 |
| 구조선과 치수선이 같은 레이어 | 표준 레이어(CS-CONC-MAJR)에 둘 다 올린 도면이 있다 | 레이어 말고 색이 가른다 — colorIndex 0(ByBlock, 치수 블록 안) vs 7(ByLayer, 구체). 걸 수 있는 축을 레이어 하나로 두면 안 된다 |
| 뽑힌 형상이 어느 도면에도 없는 조합 | 한 시트에 날짜 다른 작업본이 겹쳐 그려져 있다. 교량시종점 삽도는 TYPE1~4 한 벌(주황 테두리)과 「교량 시·종점 …전면」 한 벌(자홍)이 서로 어긋나게 포개져 있다. 뷰 상자가 그 경계에 걸치면 두 벌이 섞인다 | 시트 전체를 먼저 뜬다 → 테두리·표제로 벌을 가른다 → 도형 하나를 떠서 눈으로 확인 → 그 다음 상자. 숫자로는 안 걸린다 — 폐곡선도 치수도 그럴듯하게 나온다. 날짜 도장은 개정 이력이 아니라 작업본 구분일 수 있다 |
| 같은 뷰에 축척이 다른 도형 | 확대 상세가 같은 도형 안에 들어 있다(교좌받침 상세). 가로·세로 배율이 서로 다르기도 하다 — 받침이 153×182 인데 상세에서는 461×196 | 치수로 집으면 저절로 갈리지만 갈렸는지 확인한다. fit() 에 좌표 범위 검사를 넣는다 |
| 같은 부재가 1 mm 다르게 나온다 | DWG 부동소수를 꼭짓점마다 따로 반올림한다. 대칭 배치된 부재에서만 드러난다 (교대 5,192/5,191 · 받침 154/153×3) | loops(…, tol) 로 열되 기본값은 0. 여는 것은 부르는 쪽의 판단이고 1~2 mm 는 형상이 아니라 잡음이라는 뜻이다 |
| 표제·개정 도장이 같은 좌표에 여러 개 | 개정판을 지우지 않고 겹쳐 둔다(240912/241031/241115) | 기하는 대체로 안 겹친다(중복률 1~10%). 표제 개수로 뷰 수를 세지 말 것 |
8.2 좌표·뷰
| 증상 | 원인 | 대응 |
|---|---|---|
| Z가 713mm 어긋남 | 기준을 텍스트 위치로 잡음 | 선에서 잡는다 |
| Z가 420mm 어긋남 | 계획고 선 후보가 둘(도로/토공) | 라벨 바로 아래 선 |
| 서로 다른 두 표고 라벨이 같은 선을 가리킨다 | 라벨 y를 평균내는데, 같은 라벨이 도형마다 반복돼 평균이 어느 도형에도 안 속한다. 교량 계획고·토공 계획고 10개가 y 27,533~81,177에 흩어져 있었다 | 라벨 y를 무리로 끊어보고 둘 이상이면 던진다. 예외가 아니라 그럴듯한 숫자가 나오는 실패라 대조 항목이 없으면 영영 안 잡힌다 |
| 뷰 폭이 부풀음 | 이웃 상세도 조각이 붙어옴 | 교대 분할 + 표제 귀속. 상세도도 표제 후보에 |
| 뷰 하단이 잘림 | 부재 방향 라벨이 표제로 잡힘 | minHeight 상향 |
| 좌표가 엉뚱한 값 | 수평 단면·평면도의 세로축을 Z로 봄 | v0·swapUV. 3.3 절차로 판정 |
| 면적이 음수 / 면이 뒤집힘 | 반전·맞바꿈으로 감김 방향이 반대 | 뒤집기 홀수면 링 역순 |
| 임계값을 10mm까지 좁혀도 조각이 1개 | 상세 확대 테두리 상자(Δy=5,117)가 상세행과 단면행을 세로로 잇는다. 구조선과 같은 레이어·같은 색이라 못 거른다 | 임계값 조절을 포기하고 명시 뷰 영역으로 간다. 「간격이 존재한다」는 전제 자체가 깨진 경우다 |
| 같은 제목이 N벌 — 뷰가 하나로 뭉침 | 유형별 삽도는 사례마다 같은 제목을 붙인다 (단면A×4 · 교량 시·종점×6) | 제목은 뷰의 유일 키가 아니다. (제목, 위치) 쌍이거나 명시 영역이 필요하다. 표제 중복률을 먼저 센다 — 7장 전부 47~84%였다 |
| 뷰 제목 자체가 없다 | 표제가 시트명 하나뿐이고 나머지는 A · B · L of Road 같은 기호 | 글자 높이 하한을 어떻게 잡아도 안 나온다. 명시 영역 외에 길이 없다 |
| 도형마다 축척이 다르다 | 모델스페이스가 1:1이라는 전제가 삽도에서 깨진다. 중앙분리대 방호벽 단면은 5.805배로 그려져 있었다 | 배율은 도면 하나가 아니라 뷰마다 받는다. 값은 표기 치수 2개로 역산해 교차검증한다 (높이 7,372.3↔1,270 · 바닥폭 3,393.9↔585, 차이 0.06%) |
8.3 프로파일
| 증상 | 원인 | 대응 |
|---|---|---|
| 부피가 정확히 2배 | 개구 기호(X 표시)가 링을 자기교차시킴 | 기호를 기하 서명(같은 bbox·반대 기울기 대각선 쌍)으로 걷어낸다. 레이어로는 못 거른다 |
| 사람 눈엔 멀쩡한데 안 잡힘 | 모서리가 몇 mm 안 닫혀 있다 | bbox → rect() 재구성 + expect() |
| 재료가 한 덩어리로 뭉침 | 경계선 레이어를 추출에서 뺐다 | 면이 갈리려면 경계선이 있어야 한다 — 함께 넣고 배정은 나중에 |
| 폐곡선이 원 윤곽을 따라 돔 | 원을 다각형으로 근사해 선분에 섞음 | 원은 따로 모아 지름으로 집는다 |
| X자 교차가 안 쪼개짐 | 평면 분할이 T자 교차만 처리 | 기호는 사전 제거. 남으면 X자 분할 추가 필요 부분 |
| 끊김이 0인데 폐곡선도 0 | 같은 직선 위에서 겹치는 두 선(끝점은 다르다). 중복 제거는 T자 분할 전에만 돌아서, 분할이 겹친 구간을 양쪽에서 똑같이 잘라 같은 변을 두 벌 만든다 → 반변이 4개가 되어 면 순회가 엉킨다 | T자 분할 뒤에 한 번 더 중복 제거. 증상이 "닫혔는데 안 나온다"라 끊김 진단으로는 절대 안 잡힌다 |
| 폭·높이만 틀리고 면적은 맞다 | 끝이 아무 데도 안 닿는 매달린 변이 있으면 면 순회가 거기까지 갔다 되돌아온다. 왕복이라 면적은 0 인데 bbox 는 부푼다. 게다가 bbox 를 collinear 제거 전 링으로 재고 있었다 (상부공 폭 12,752 → 12,951) | bbox 는 collinear 를 뺀 링으로 잰다. area 만 보면 절대 못 잡는다 |
| 진단 도구와 본편이 다른 값 | 진단 도구가 projectViews 를 안 거치고 buildLoops 를 직접 불렀다 — 개구 기호 제거·앵커·평면 변환이 빠진다 |
본편과 같은 함수를 쓴다. 다르면 도구를 못 믿게 되고, 그 순간 검증 체계 전체가 죽는다 |
| 윤곽이 진짜로 안 닫혀 있다 | 구성 도해(폭원·배치)는 닫힌 단면을 그리지 않는다. 띠와 열린 외곽선만 있다 | 끊김 원인 분류에서 「선 없음」 비율을 본다. 교량폭원은 56곳 중 54곳이 그랬다 — 설정으로 못 고친다. 그 도면은 솔리드 대상이 아니다 |
8.4 조립
| 증상 | 원인 | 대응 |
|---|---|---|
| 전 구간이 같은 단면으로 나옴 | 횡단면 하나를 전체에 압출 | 종단면이 구간 배정표다. 수평선을 Z 내림차순 × X 범위로 읽는다 |
| 안이 들여다보임 | 한 평면 단면만 압출해 반대 방향 벽이 없다 | 공동의 압출 범위를 좁혀 벽을 남긴다 |
| 구간 경계에 구멍 | 구간별로 따로 difference | 몸통을 union한 뒤 한 번만 뺀다 |
| union이 예외로 죽음 | 떨어진 커터끼리 union 시도 | 커터는 하나씩. union하는 건 "구간 몸통"이지 커터가 아니다 |
| 커터가 46 m³를 파먹음 | 커터를 대상 바깥면에서 시작 | 모든 방향으로 OVER 여유 |
| difference가 조각을 통째로 흘림 | 면으로만 맞닿은 솔리드를 union | 부피로 겹치게 만든 뒤 union. 단 압출축 방향으로만 늘린다 (통째로 부풀리면 더 나빠진다) |
| 같은 커터 재사용 실패 | boolean이 피연산자를 소모 | 조각 수만큼 커터를 따로 만든다. 안 겹치는 조각엔 아예 넣지 않는다 |
| 경사면 하나가 끝까지 올라감 | loft로 두 번 꺾으려 함 | 꺾이는 자리마다 단면. 방향별로 꺾이는 좌표가 다르면 또 끊는다 |
| loft 면이 비틀림 | 두 단면의 시작 꼭짓점이 어긋남 | 링 정규화(반시계 · v최소→u최소 시작) + 꼭짓점 수 일치 강제 |
| loft가 부풂 | 단면 3개 이상 → 스플라인 보간 | 2단면씩. 결과 정점에 중간 행이 없으면 직선 룰드다 |
| 두 번째 도면의 build가 위상 위반으로 죽음 | 설계 규칙이 첫 도면의 파라미터 이름에 묶여 있다. 없는 값이 undefined로 비교돼 전부 실패로 떨어진다 | 규칙에 needs(요구 파라미터)를 붙여 해당 없으면 건너뛴다. 규칙 층은 "도면 고유값 없음"을 자처했지만 실제로는 지켜지지 않았다 |
8.5 백엔드(모델러)
백엔드 특이사항은 전부 emit/ 안에 가둔다. 아래는 EGModeler 사례지만,
다른 툴로 바꿔도 같은 성격의 함정이 그 자리에 온다.
| 증상 | 원인 | 대응 |
|---|---|---|
명령이 조용히 무시됨, created:[] | 뷰어 모드라 모델링 명령이 등록조차 안 됐다 | 작업 시작 전에 등록 명령 개수 확인. 연결 정상 ≠ 모델링 가능 |
| 커터 8개가 씬에 남음 | boolean이 이항이다 — 셋 이상은 무시 | 둘씩 접는다. modelCount 대조로 즉시 잡힌다 |
| 압출이 엉뚱한 방향 | Cap 단독은 항상 +Z로 민다 | 항상 방향을 명시. Cap 플래그는 방향 지정 앞에 |
| 서피스가 나옴 | loft 기본이 서피스 | Cap으로 막아 솔리드. 원본 삭제 플래그는 건드리지 않는다 |
| 부피는 맞는데 boolean 이 샌다 | loft 결과가 안 닫힌다. Cap 을 덧대도 4각형 단면조차 열린 변 12개. 발산 정리 부피는 정확해서 부피 검사로는 절대 안 걸린다 | loft 를 boolean 인자로 쓰지 않는다. 단면이 평행이동만 하면 자유방향 압출(drift)로 대체 — 그건 닫힌다. → 11.4.6 |
| 이후 명령이 전부 무시 | 선택 명령을 안 닫음 | Esc로 닫는다 |
| 파일이 안 생기는데 오류도 없음 | 경로의 _가 스크립트 특수문자 | 큰따옴표 필수 + 파일 존재·mtime을 직접 확인 |
| 씬 초기화 명령이 조용히 무시됨 | 뷰 조작 명령을 ~ 접두로 보내 끝나지 않는 상태로 물렸다. status.command가 그 이름으로 남는다 | 명령 상태를 매번 확인. 접두 없는 Esc로 푼다. 연결 정상 ≠ 명령 수용은 뷰어 모드 말고 여기서도 성립한다 |
| 파이프가 죽음(프로세스는 살아 있음) | 씬 초기화 반복 | 초기화는 갱신당 1회. 죽으면 재시작 외에 방법 없다 |
9새 도면 인테이크 워크시트
9.1 채워야 할 것 (순서대로)
- 도면 적격 판정 먼저 — 표기 치수를 숫자형/기호형으로 세고, 구조 윤곽이 닫히는지 본다. 기호뿐이거나 안 닫히면 솔리드 대상이 아니다. 여기서 걸러야 나머지 아홉 항목을 헛되이 채우지 않는다
- 모델단위 —
DIMENSION.actualMeasurement↔ 치수 글자를 비교해unitMm확정. 도면군마다 다르다 - 구조물 종류·좌표계 선언 — X·Y·Z 방향과 원점을 문장으로
- 시트 영역 — 전체 좌표 분포를 히스토그램으로 보고 유령 기하를 가른다
- 레이어 4갈래 —
struct(넓게) /body(좁게) /material/ground.body ⊆ struct. 레이어가 구조선과 치수선을 안 가르면 색·선종으로 거른다(entityFilter) - 표제 글자 높이 분포 — 시트명 / 뷰 제목 / 부재 라벨의 h를 세어
minHeight결정. 표제 중복률도 같이 센다 — 높으면 제목으로 뷰를 못 가른다 - 뷰 목록 — 제목 →
{plane, flip, role}. 단면은 절단 기호 좌표로 평면 판정. 제목이 없거나 중복이면viewBoxes로 좌표를 짚는다 - 기준점 — 라벨 정규식 + 도면 치수 검증 2개 이상. 기준선이 없으면
mode:'none'으로 명시하고 검증을 G5로 미룬다 - 앵커 — 뷰별
u0/v0/flip/flipV/swapUV/unitMm. 비대칭 특징으로 교차검증. 축척이 다르면 표기 치수 2개로 역산해 교차검증한다 - 구간 배정표 — 종방향 뷰에서 X 범위 × 단면 대응
- 조립 스펙 — 5장의 결정 트리와 5원칙
- 규칙 — 이 부재 종류의 위상 규칙 · 설계 기준
9.2 설정 파일 스켈레톤
export default {
name: '<도면이름>',
dwg: path.join(DRAWINGS_SRC, '<파일>.dwg'),
// 이 밖의 좌표는 버린다 (유령 기하 · 블록 로컬 좌표)
sheet: { x0: …, x1: …, y0: …, y1: … },
// DWG 1단위가 몇 mm인가. **도면군마다 다르다** (맨홀군 1000, 교량 삽도군 1).
// DIMENSION.actualMeasurement 와 치수 글자를 비교해 확정한다.
unitMm: 1000,
// 레이어로 못 가르는 것을 거른다. colorIndex 0 = ByBlock = 치수 블록 안.
// 구조선과 치수선이 같은 레이어인 도면에서만 쓴다.
entityFilter: (e) => (e.entityHeader?.colorIndex ?? -1) === 7,
layers: {
struct: /…/, // 뷰 분리용. 넉넉하게 — 가구·주기가 들어가도 된다
body: /…/, // 프로파일용. 좁게 — 가구가 섞이면 링이 오염된다
material: /…/, // 기초·잡석 등 별개 재료. 추출엔 함께 넣는다
ground: /…/, // 기준선(계획고·GL·FL) 검출용
},
titles: {
minHeight: 0.32, // 부재 라벨과 뷰 제목을 가르는 글자 높이
views: {
'평 면 도': 'plan', // → XY
'단 면 A-A': 'long', // → XZ (절단 기호 두 점의 y가 같다)
'단 면 B-B': 'cross', // → YZ (두 점의 x가 같다)
'단 면 H-H': 'horizontal', // → XY (다른 단면 위에 찍힌 수평 절단)
// 입면도·측면도를 넣을 때: 평면은 XZ/YZ지만 role은 validator다.
// 절단면이 아니므로 여기서 프로파일을 뽑아 압출하면 안 된다.
},
},
segment: { gap: 0.2, xTol: 0.5, ySlack: 0.2 }, // 단위는 DWG 단위다 (unitMm 에 맞춘다)
// 제목이 없거나 중복이면 **좌표로 짚는다**. titles.views 와 둘 중 하나만 쓴다.
// y 값은 눈대중이 아니라 구조선 좌표 뭉치의 경계에서 뽑고, 상자는 그 빈틈 안에 둔다.
viewBoxes: {
'<뷰>': { box: [x0, y0, x1, y1], kind: 'cross',
unitMm: 1 / 5.805 }, // 도형마다 축척이 다르면 여기서 준다
},
datum: {
mode: 'none', // 기준선이 도면에 없을 때. 표고는 뷰별 v0 가 준다
levelLabel: /…/, // 기준선 후보를 좁히는 힌트. 값은 선에서
checks: [ // 최소 2개. 도면에 적힌 치수 그대로
['<설명>', (v) => v['<뷰>']?.layerZ['<레이어>']?.[1], -0000],
],
},
frame: { x: '…', y: '…', z: '…', origin: '…' },
anchors: { // 유도하고 교차검증한다. 지어내지 않는다
'평 면 도': { u0: …, v0: … }, // 평면도도 v0가 필요하다
'단 면 B-B': { u0: … }, // 수직 단면은 v가 이미 표고
'단 면 H-H': { u0: …, v0: …, swapUV: true },
},
expectExtents: { '<뷰>': [폭, 높이] }, // 분리 자동 확인
fit({ loop, circles, opening, expect }) {
// 도면을 보는 **유일한** 통로. 여기서 params.json이 나온다
expect('<뷰>', 폭, 높이, '왜 이게 있어야 하는가');
return { params: { … }, rings: { … } };
},
assemble({ p, ring, profile, part, rect }) {
// 도면 조회 도우미를 주지 않는다 — 주면 다시 도면에 묶인다
},
};
titles.views(평면)와 anchors(앵커) 두 곳에 흩어져 있다.
3장의 모델대로 { plane, u0, v0, flip, flipV, swapUV, role } 한 덩어리로 합치는 것이 맞다.
뷰 종류를 늘리는 작업(입면도·측면도)을 시작하기 전에 정리하는 편이 싸다.
10도면 작성자에게 요청할 것
효과 대비 비용이 큰 순서. 1번 하나로 검출 로직 전체가 사라진다.
| 요청 | 없앨 수 있는 것 | 비용 |
|---|---|---|
레이어 3D-ORIGIN에 뷰마다 기준점 POINT 하나 | 기준점 검출 · 앵커 유도 · 텍스트/선 혼동 사고 전부 | 뷰당 클릭 한 번 |
| 구조체 외곽선 레이어 통일 | 레이어 오분류 진단(끊김 50곳 중 20곳이 이것이었다) | 낮음 |
| 개구 기호를 별도 레이어로 | 자기교차 링 · bbox 재구성 우회 | 낮음 |
| 가구(케이블랙·철근)를 구조 레이어에서 분리 | body/struct 이중 정의 | 낮음 |
| 시트 영역 밖 기하 정리 | 유령 기하 필터 | 낮음 |
| 뷰 제목 표기·글자 높이 규약 | 표제 임계값 튜닝 | 낮음 |
| 단면 절단 기호를 모든 단면에 표기 | 평면 판정을 제목 추측에 의존하는 것 | 낮음 |
도면을 뷰별 파일로 쪼개달라고 하지는 말 것. 표고 정합과 절단기호 매핑을 같이 잃는다(3.4).
11구조물 유형별 차이
1~4장(정렬 · 뷰 · 게이트)과 7장(검증)은 구조물이 뭐든 안 바뀐다. 바뀌는 것은 좌표 기준과 쓰는 연산이다.
| 선형 토목 전력구·터널·도로 | 교량 | 점형 토목 맨홀·수조·기초 | 건축 | |
|---|---|---|---|---|
| 좌표 기준 | 중심선 + 계획고 | 선형(측점) + 받침 둘로 나뉜다 | 구조물 외면 + GL | 그리드 통심선 + FL |
| 주 연산 | extrude · loft | sweep · loft · 프리미티브 · revolve | extrude · 프리미티브 | extrude (층별) |
| 구간 배정 뷰 | 종단면 | 측면도(일반도) | 없음 — 단면이 하나 | 층 평면도 |
| 반복 단위 | 구간 | 경간 · 거더 · 말뚝 | — | 층 · 스팬 |
| 상태 | 검증 | 부분 | 검증 | 가설 |
11.0 실증 결과 — 교량 도면 7장에서 실제로 일어난 일 검증
03 교량시종점 · 04 교량폭원 · 05 중앙분리대 · 09 거더배치 · 10 횡단경사 · 13 교대배치).
한 구조물을 여러 방향에서 본 좌표가 물린 도면 세트가 아니라, 서로 다른 개념을
각각 N벌 변형으로 그린 낱장들이다. 아래 결론은 그 조건에서 나온 것이고,
실제 교량 일반도(측면도+평면도+횡단면이 물린 것)에는 11.1~11.3이 그대로 남아 있다.
| 도면 | 엔티티 | 숫자 치수 | 기호 치수 | ARC | 막힌 곳 |
|---|---|---|---|---|---|
| 거더배치 | 1,433 | 2 | 62 | 249 (17%) | G1 — 치수가 L·β 뿐 |
| 교대배치 | 2,138 | 51 | 37 | 181 (8%) | G1 — θ 가변 도해 |
| 교량시종점 | 4,432 | 0 | 6 | 492 (11%) | 관통 — 2지간 교량 95파트 820.36 m³ (11.0.4). 「치수 0개 → 대상 아님」은 오판이었다 |
| 교량폭원 | 2,824 | 151 | 10 | 0 | 관통 — 상부공 7파트 180.36 m³ (11.0.3). 처음엔 G5에서 죽었다 — 폐곡선 버그를 고치자 나왔다 |
| 교면포장 | 1,304 | 14 | 2 | 0 | G1 — 층상 구성표 |
| 중앙분리대 | 2,159 | 108 | 7 | 0 | 관통 — 방호벽 4.8058 m³ (11.0.1) |
| 횡단경사 | 5,635 | 156 | 139 | 64 (1%) | G1 — S% · TL 가변 |
L · β · θ · VAR · n@3,600 · S%)면 대조할 상대가 없다.
파싱 직후 TEXT/MTEXT를 숫자형/기호형으로 나눠 세는 것만으로
「이 도면은 솔리드 대상이 아니다」가 몇 초에 판정된다. 위 표가 그 결과다.
이 판정은 게이트 이전에 온다 — G0.5쯤이다. 방법론에 없던 단계다.
11.0.1 통과한 것 — 중앙분리대 방호벽
7장 중 닫힌 구조 단면을 가진 유일한 도면이었다. 세운 것은 토공용 구간 → 변화구간(2m) → 교량용 구간의 방호벽 한 줄이다.
단면A (토공용, 585×1319) ──extrude X 0→5,000──┐
├─ loft 5,000→7,000 ← 변화구간
단면B (교량용, 584×1270) ──extrude 7,000→12,000┘
union → 중앙분리대_방호벽
부피 모델러 4.8058 m³ IR 격자(25mm) 4.7970 해석 프리즈모이드 4.8058
modelCount 1 = 최종 파트 수 1
- 축척을 되찾아야 했다. 이 단면은 5.805배로 그려져 있다. 표기 치수 2개(높이 1,270 · 바닥폭 585)로 역산해 교차검증했다 — 차이 0.06%
- 두 단면의 기준면 y가 달랐다. 포장면 48,812.1 vs 바닥판면 49,096.5. 그 차이가 곧 「토공용은 포장에 49mm 묻힌다」는 형상이었다
- difference가 하나도 없는 첫 모델. 방호벽은 속이 찬 콘크리트다. 뺄 게 없으니 boolean 함정(8.4)이 통째로 안 나왔다 — 즉 8.4는 아직 교량에서 검증되지 않았다
- loft 하나로 끝났다. 변화구간이 실제로 직선 전이(꺾이는 자리 없음)였기 때문이다. 교훈 #16(꺾이면 끊는다)이 여기서는 해당 없었다
11.0.3 교량 상부공 — 폐곡선 버그를 고치자 나왔다
교량폭원은 처음에 G5에서 죽었다 — 끊김 56곳 중 54곳이 「도면에 선 없음」이었다. 그런데 겹치는 동일선상 선분 버그(8.3)를 고치고 다시 돌리자 폐곡선이 새로 나왔다. 「도면이 안 닫는다」의 상당 부분이 실은 우리가 못 닫은 것이었다. 판정을 내리기 전에 도구부터 의심할 것.
폭원 띠 상 24,300 × 240 바닥판 전폭 ↔ 도면 표기 24,300
분리교량 12,300 × 240 · 15,900 × 240 좌·우 상판
440 × 1,320 ×3 방호울타리
11,400 × 50 마모층 ↔ 도면 표기 11,400 (도로 폭)
| 값 | 이 도면에서 | 다른 도면에서 |
|---|---|---|
| 바닥판 두께 | 폐곡선 240 | 교면포장 삽도 「콘크리트 교량 바닥판 240mm」 |
| 마모층 두께 | 폐곡선 50 | 교면포장 삽도 「마모층 50mm」 |
| 방호울타리 높이 | 폐곡선 1,320 (포장면 위 1,270) | 중앙분리대 삽도 표기 1,270 |
서로 다른 삽도가 같은 값을 준다. 유형 삽도 묶음은 좌표가 물려 있지 않아 뷰 사이 교차검증이 안 된다고 봤는데(11.0), 치수 값 수준에서는 물려 있다. 공통 datum이 없는 도면군에서 쓸 수 있는 교차검증 경로가 이것이다.
세운 것: 좌·우 상판 · 마모층 · 방호울타리 4기를 경간 20m(설계값)로 직선 압출.
7파트 180.356 m³, 의도 대비 전부 0.0000.
넷째 방호울타리는 도면 윤곽이 안 닫혀 재구성했다 — 들임(10mm) 규칙을
측정된 두 부재로 확인한 뒤 적용했고, 그렇게 놓은 외면 좌표가 gaps가
짚은 끊김 좌표와 같은 값으로 떨어지는 것이 검증이다. 등급은 tolerant —
도면 좌표를 옮긴 게 아니라 만든 것이므로 opaque가 아니다.
11.0.4 교량 한 기 — 「표기 치수 0개」를 잘못 잘랐다
② 「대조할 상대가 없다」도 틀렸다 — 그 도면 안에 없다는 뜻일 뿐이고, 다른 도면과 표준 부재 치수는 여전히 상대가 된다.
1:1 인지는 크기를 아는 표준 부재로 확정했다. 도면 밖에서 온 눈금이다.
폐곡선 153×182 이 4개 ← 탄성받침 150×180. 다른 게 이 치수로 4개 나란할 이유가 없다
교대 벽체 4,680×6,506 교량 교대 자릿수
상부공 형고 1,421 지간 12,752 에 L/h≈9 — 반일체식으로 타당
접속슬래브 두께 383 표준 300~400
넷이 **동시에** 타당해지는 배율은 1 뿐이다. 10배만 달라도 받침이 1.5 m 나 15 mm 가 된다.
그 도면 하나로 2지간 교량 한 기가 열렸다 — 이 도면군에서 하부공이 부재로 그려진 유일한 도면이다.
바닥판 26,178×277 · 거더 12,303/12,748×1,144 ×5본씩
교대(벽체+확대기초) 4,680×7,953 ×2 · 날개벽 4,563×6,932 ×4
교각 두부 2,244×1,334 · 기둥 1,586×3,904 · 확대기초 5,081×1,334
교좌받침 153×182 ×8열 · 받침콘크리트 ×3 · 접속슬래브 3,705×383 ×2
말뚝 Ø518 ×10위치 (연직이라 IR 의 Z축 cylinder 가 그대로 맞았다)
95파트 · 820.36 m³ · 의도 대비 전부 0.0000
폐곡선 25개가 나왔고 치수도 그럴듯했다. 검증표는 전부 통과했다. 그림을 보고서야 알았다. 4.2(캡처)가 선택이 아니라 필수인 이유다.
11.0.5 도면이 정보를 주는 방식 — 형상만이 아니다
| 부재 | 도면이 주는 형태 | 읽는 법 | 현재 |
|---|---|---|---|
| 구체·교대·교각 | 닫힌 윤곽 | 폐곡선 | 있다 |
| 관통구·기둥 | CIRCLE | 지름으로 집는다 | 있다 |
| 개구부 | X 기호 | 기하 서명으로 되살린다 | 있다 |
| 말뚝 | 해치 + 중심선 | 중심선 x = 위치, 윤곽 두 줄 간격 = 지름 | 없다 — pick 이 선을 못 집는다 |
| 철근·수량 | 주기 텍스트 | — | 없다 |
형상이 없다고 정보가 없는 게 아니다. 개구 기호를 되살린 것과 같은 종류의
이득이 중심선·해치에 남아 있다. circles() 를 만든 것과 같은 이유로
centerlines() 가 필요하다.
11.0.2 범위에서 뺀 것 — 명시
| 뺀 것 | 왜 |
|---|---|
| 상부공(거더·바닥판)·하부공(교대·교각·말뚝) | 표기 치수가 있는 도면이 없다. 교량시종점은 측면도가 6벌 있지만 숫자 치수가 0개다 |
| 교량 바닥판 횡단면(교량폭원) | 폭원 구성 도해라 닫힌 단면을 안 그린다. 끊김 54곳이 「도면에 선 없음」 — 설정으로 못 고친다 |
| 방호벽 설치 연장 | 도면이 「중앙분리대 설치 범위」로만 표시한다. 각 5,000mm를 설계값으로 명시했다 — 도면에서 읽은 척하지 않는다 |
| 변화구간 2,000의 기하 교차검증 | 평면도 쪽 표기 치수가 하나뿐이라 그쪽 축척을 독립으로 되찾을 수 없었다. 출처를 「도면 주기 글자」로 남겼다 |
| ARC · SPLINE | 모델링한 도면에는 0개였다(운이 좋았다). 버린 도면들에는 8~17%가 있다 — 11.2 참고 |
11.1 교량 — 기준이 하나가 아니라 체인이다 가설
전력구는 프로젝트 원점 하나로 전부 기술된다. 교량은 안 된다. 상부공은 선형을 타고(곡선·편경사로 기울어짐) 하부공은 연직으로 선다. 두 좌표계가 다르므로 잇는 지점이 필요하고, 그 지점이 교좌받침이다.
프로젝트 원점
└─ 선형 (측점 s · 오프셋 t · 표고) ← 상부공은 전부 여기서 나온다
└─ 받침 (측점 + 오프셋 + 받침표고) ← 두 좌표계를 잇는 유일한 접점
└─ 교각 로컬 (연직 · 사각) ← 하부공은 전부 여기서 나온다
└─ 기초 · 말뚝 배치 격자
도면이 이미 이렇게 기술한다 — 하부공 도면은 받침 중심 좌표와 받침 표고로 교각을 잡고, 거기서 아래로 교각·확대기초·말뚝을 내린다. 그래서 받침을 앵커로 삼는 것이 도면을 그대로 옮기는 것이 된다. 받침을 안 쓰고 절대좌표로 잡으면 사각(skew)·편경사가 들어오는 순간 값이 전부 손계산이 된다.
11.2 교량에 필요한 확장
| 항목 | 왜 | 현재 | 실증 |
|---|---|---|---|
| 측점 기반 sweep | 평면곡선 · 종단곡선 · 편경사. 직선축 from/to로는 표현 불가 | 없음. IFC4.3 IfcSectionedSolidHorizontal과 의미가 같다 | 미확인 — 곡선 선형을 준 도면이 없었다. 삽도는 전부 직선 구간 도해다 |
| revolve | 원형 교각 두부 · 정착부 | 없음 | 불필요했다 — 교각은 사각 + 벌어진 두부다. 세웠다(11.0.4) |
| 임의축 cylinder | 경사 말뚝 · 사교 교각 | Z축 한정 | 불필요했다 — 말뚝이 전부 연직이라 Z축 cylinder 가 그대로 맞았다. Ø518 ×10위치를 세웠다 |
| 부재군 로컬 프레임 | 상부(선형) ↔ 하부(연직) 좌표계 전환 | 없음 — 단일 좌표계 전제 | 미확인 |
| ARC · SPLINE 파싱 | 곡선 거더 · 원형 단면 | 버린다 | 비중 측정됨 — 거더배치 17% · 교량시종점 11% · 교대배치 8% · 횡단경사 1%. 모델링한 도면(중앙분리대)은 0% |
| 배열 파라미터 | 경간 N · 거더 M본 · 말뚝 배치 | 있다 — 6장 배열이 그대로 맞는다 | 미확인 |
| 뷰별 축척 신규 | 삽도는 도형마다 배율이 다르다 | 추가됨 — viewBoxes[뷰].unitMm | 검증 — 5.805배 사례 |
| 명시 뷰 영역 신규 | 제목이 없거나 중복인 도면 | 추가됨 — cfg.viewBoxes | 검증 |
| 레이어 아닌 필터 신규 | 구조선과 치수선이 같은 레이어 | 추가됨 — cfg.entityFilter(색·선종) | 검증 |
| 복수 선택기 신규 | 같은 부재가 여러 벌인 것이 정상 (거더·받침·말뚝) | 추가됨 — loops(뷰, w, h, tol) | 검증 |
| 중심선 선택기 신규 | 말뚝처럼 해치 + 중심선으로만 그려지는 부재 | 없다 — 스크립트로 재서 설정에 적었다 | 구멍 |
빗나감: revolve와 임의축 cylinder는 필요 없었다. 원형 교각도 경사 말뚝도 7장 어디에도 없다. 「교량이면 revolve가 필요하다」는 교량 일반론이지 이 도면군의 사실이 아니었다.
못 예측한 것: 실제로 막은 것은 위 목록에 하나도 없었다 — 단위(1mm vs 1m) · 뷰 제목 부재 · 뷰별 축척 · 레이어가 치수선을 안 가름 · 폐곡선 중복 변 버그. 전부 연산이 아니라 입력 해석 층의 문제다. 11.2는 「무슨 연산이 더 필요한가」만 물었는데, 실제 병목은 「도면을 읽어들이는 가정」이었다.
11.3 교량에서는 측면도의 지위가 다르다 부분
3.2에서 입면도·측면도는 검증(validator) 역할로 못박았다. 교량에서도 솔리드를 만들면 안 되는 것은 같지만, 교량 일반도의 측면도는 구간 배정표(assigner)를 겸한다 — 경간 분할 · 교각 위치 · 형고 변화 · 받침 표고가 전부 거기 있다. 전력구에서 종단면이 하던 일을 교량에서는 측면도가 한다.
지간) · 신축유간 ·
접속슬래브 · 말뚝 배치가 전부 있었다. 측면도가 배정 정보를 든다는 주장은 형태상 맞다.
확인 못 함: 그 측면도에 숫자 치수가 하나도 없다(기호 라벨뿐). 배정표로 쓸 수 있는지는 표기 치수가 있는 실제 일반도가 있어야 판정된다.
새로 드러난 것: 삽도에서는 측면도가 세 번째 역할을 한다 — 유형 카탈로그(TYPE1 반일체식 · TYPE2 단부슬래브 · TYPE3 일체식 · TYPE4 골조식). 같은 제목이 6벌 반복되는 원인이 이것이고, 그래서 제목 기반 뷰 분리가 깨졌다(3.4).
11.4 삽도(유형 도해)는 별도의 구조물 유형이다 검증
일반도와 삽도는 같은 파이프라인으로 다룰 수 없다. 표로 갈라둔다.
| 일반도 전력구 4매 | 유형 삽도 교량 7장 | |
|---|---|---|
| 치수 | 숫자. 대조 가능 | 기호(L·β·θ·VAR)가 섞인다. 도면마다 비율을 세어야 한다 |
| 뷰 제목 | 유일 (단 면 B-B) | 없거나 N벌 중복 |
| 축척 | 모델스페이스 1:1 | 도형마다 다르다. 표기 치수 2개로 역산 |
| 표고 기준 | 전 뷰 공통 계획고선 | 없다. 뷰별 v0 |
| 윤곽 | 닫힌 재료 영역 | 구성 도해는 안 닫는다(띠·열린 외곽선) |
| 레이어 | 구조/치수/지반이 갈림 | 표준 레이어 하나에 구조선과 치수선이 같이 올라간다 |
| 솔리드 대상 | 전부 | 일부만 — 7장 중 1장 |
11.4 실증 결과 — 교각 P9 한 부재를 끝까지 검증
산외교_파이형_교각일반도(울산P9).dwg — 평면도 · 정면도 · 단면도 · 단면 A-A ·
기초 평면도 · 교량받침 상세 · 솔플레이트 상세 + 표 3개가 좌표가 물린 채 한 도곽에 있다.
11장이 「교량 일반도에는 11.1~11.3이 그대로 남아 있다」고 유보해둔 그 조건이다.
부재 하나(교각)를 코핑→기둥→기초→말뚝→받침→문양 순으로 끝까지 밀었다.
파트 77 · 660.4154 m³ · 모델러 실측 대비 불일치 0건 · 열린 변 0
코핑 1(intersect) · 원형기둥 2 · 확대기초 2 · 버림 2 · 강관말뚝 44
받침콘크리트 6 · 모르타르 6 · 교좌받침 6 · 솔플레이트 6 · 미관문양 2
구체 콘크리트 fck=27MPa 359.3411 미관 문양 3.3585
기초 콘크리트 fck=27MPa 177.4500 받침콘크리트 1.8438
강관말뚝 Φ508.0×9T 108.7954 탄성받침 0.3469
버림 CON'C fck=15MPa 8.9780 무수축 모르타르 0.1555
솔플레이트 0.1299
11.4.1 방법론 판정 — 「해봤다」가 아니라 참/거짓으로
| 방법론 주장 | 교각에서 | 근거 |
|---|---|---|
| 2.2 표고 기준은 전 뷰 공통 | 거짓 → 그런데 되찾을 수 있다 | 공통 기준선이 없어 datum.mode:'none' 으로 시작했다. 그런데 받침 6기의 표
EL.'X' = 코핑상면 + h1 + h2 가 6기 모두 같은 절대표고 79.447을 낸다.
기준선이 없어도 표가 기준을 준다 |
| 3.4 제목으로 뷰를 가른다 | 참이나 안 썼다 | 제목이 있다(CZ-TEX1 h=700). 그런데 뷰끼리 치수선으로 이어져 있어
제목 그룹핑이 안 갈린다. 도곽 안 좌표(viewBoxes)로 짚었다 |
| 5.2 연산 결정 트리 | 부족했다 | intersect 가 트리에 없었다. 코핑은 「평면 윤곽 ∩ 정면 윤곽」이라
어느 한 방향 압출로도 안 나온다. 추가함 |
| 11.1 기준 체인 (받침 → 교각 로컬) | 참 · 처음 검증 | 받침배치도 「교량받침 좌표」의 절대 측량좌표 + 방위각으로 로컬 원점을 못 박았다.
역산 오차 원점 2 mm · 끝단 37 mm. IFC IfcMapConversion 으로 나간다 |
| 11.2 revolve 필요 | 불필요 | 기둥이 Ø2,200 연직 원기둥이라 cylinder 로 그대로 선다.
회색 보조선(ci=8) 8줄이 원기둥 모선이라 각기둥이 아님을 재확인해준다 |
| 11.2 임의축 cylinder | 해당없음 | 경사말뚝이 없다. 44본 전부 연직 |
| 11.0.5 「형상이 없어도 정보는 있다」 | 참 · 메웠다 | 말뚝 44본이 CSPILEH INSERT 블록이다. 블록이 위치와 치수를 둘 다 준다 —
scale 254 = 반지름, 도면 주기 Φ508.0X9T 와 일치. blocks() 를 만들었다 |
11.4.2 도면이 정보를 주는 방식 — 표가 추가됐다
| 부재 | 도면이 주는 형태 | 읽는 법 | 현재 |
|---|---|---|---|
| 코핑·기둥·기초 | 닫힌 윤곽 | 폐곡선 | 있다 |
| 기둥 단면 | CIRCLE | 지름으로 집는다 | 있다 |
| 말뚝 | INSERT 블록 | 삽입점 = 위치, 배율 = 치수 | 있다 — blocks() |
| 받침 적층·표고 | 표 | 이름표로 표를 집고 열 이름으로 값을 | 있다 — tables |
| 미관 문양 | 다른 색의 윤곽 | 뷰별 entityFilter | 있다 |
| 기둥 높이 | 치수 텍스트 (형상은 파단) | — | 없다 — 텍스트 셀렉터 |
그래서
run.mjs 가 도면을 읽을 때 (이름표, 표) 쌍으로 색인해
out/<도면>.tables.json 에 얼려두고, fit() 이 pick.tables 로
열 이름으로 꺼낸다. 없는 표·없는 열이면 던진다.
11.4.3 세 갈래가 서로를 검산한다
형상 · 표 · 다른 도면이 같은 값을 서로 다른 길로 낸다. fit() 이 매번 확인하고 어긋나면 던진다.
| 대조 | 결과 |
|---|---|
표 S1/S2 → 교축 위치 ↔ 평면도 폐곡선 중심 | 6/6 ≤3 mm |
표 h4 ↔ 솔플레이트 표 T | 6/6 완전일치 |
h1+h2+h3+h4 ↔ 표 E | 6/6 |
S1+S2 ↔ 코핑 교축폭 3,200 | 6/6 |
솔플 T ↔ (T1+T2)/2 | 6/6 |
솔플 경사쪽 판정: T2−T1=16 ↔ 전/후열 총고차 29 ÷ 1,600 × L 840 = 15.2 | 가정 없이 유도됐다 |
| 파단 생략량: 표고 텍스트 · 치수 8.816/10.916 · 기둥 도형길이 6,100 | 세 갈래 모두 27,700 |
단위: 도곽 A1×100 · 블록 scale 254 ↔ Φ508 · 격자 1,300 ↔ [email protected] · 받침배치도 A1×300 | 넷 다 1단위 = 1 mm |
11.4.4 파단선 — 이 도면 유형의 최대 함정 검증
신호는
CS-CUTL 레이어다. 가로 파단선(정면도·단면도 같은 y)과
말뚝마다의 파단 원호(R=655.6 ×45)가 거기 있다.
대응 — 생략량을 손으로 적지 않는다.
파단_생략량 = 기둥_높이 − 기둥_도형길이 로 derived 가 된다.
(기둥_높이는 치수 텍스트라 아직 design 이다)
11.4.5 코어에 새로 들어간 것
| 무엇 | 왜 필요했나 | 일반성 |
|---|---|---|
intersect 연산 | 코핑 = 평면 윤곽 ∩ 정면 윤곽. 한 방향 압출로는 안 나온다 | 「두 방향이 동시에 맞아야 하는 형상」 전부 |
extrude.drift (빗각 기둥) | 단면이 평행이동만 하는 전이부. Loft 를 못 쓰기 때문 | 테이퍼 코핑·확폭부 |
blocks() 셀렉터 | 말뚝이 INSERT 블록. 배율이 곧 치수다 | 블록으로만 그린 부재 전부 |
표 추출 (core/table.mjs) | 받침 적층·표고가 표에만 있다 | 모든 DWG. 격자·이름표·병합셀 자동 |
뷰별 entityFilter | 미관 문양이 본체와 다른 색이다 | 「뷰마다 무엇이 구조선인가가 다르다」 |
dropCollinear 중복점 제거 | 매달린 변 왕복이 길이 0 변을 남긴다 | 전 도면 |
| 교집합 해석 부피 | 격자로는 measure --ir 합격선 0.05 m³ 를 못 넘긴다 | 압출∩압출 · 종단∩평면(높이 가변) |
emit/ifc.mjs | IFC 출력이 없었다 | IFC4 · 해석 솔리드 우선 |
11.4.6 Loft 는 boolean 에 못 쓴다 — 가장 비싼 발견 검증
Loft 는 가이드상 서피스만 만든다. C=True 는 조용히
무시되고 Cap 을 덧대도 안 닫힌다 — 4각형 단면조차 열린 변 12개다.
발산 정리로 재는 부피는 정확히 맞으므로 부피 검사만으로는 절대 안 걸린다. 그런데 boolean 인자로 넣으면 샌다 — 코핑이 102.38 → 98.65 가 됐다.
대안 —
Extrude C=True D F {기준점} {방향점} 는 자유방향을 받고
결과가 닫힌 솔리드다(정점 8·면 12·열린 변 0 확인). 단면이 평행이동만 하면
loft 대신 이것을 쓴다.
기존 모델 재검증 필요 —
manhole1 이 loft 결과를 union·difference 인자로 쓴다.
11.4.7 새로 밟은 함정
| 증상 | 원인 | 대응 |
|---|---|---|
| 한 파일에 도형이 17벌 쌓여 있다 | 도면집 스택. 제목·치수가 없는 잔재 | 도곽 블록에서 상자를 계산한다 — CXBLKA1 INSERT 위치 × scale × A1(841×594) |
| 「단면도」 안쪽 선을 다른 부재로 읽었다 | 절단면이 아니라 투영이다. 먼 쪽 모서리가 겹쳐 보인다 | 그 선의 치수가 외곽 − 2×R 로 떨어지면 투영이다 (3,200−2×500=2,200) |
| 평면 모서리가 45° 모따기로 나온다 | 파서가 LWPOLYLINE 의 bulges 를 버린다. 90° 호(bulge 0.41421)가 현 하나로 눌린다 | 호 끝점은 정확하므로 R 은 되찾을 수 있다. 링 면적을 바로 쓰면 코너당 71,350 mm² 부푼다 |
| 평면 윤곽이 두 줄인데 하나만 썼다 | 바깥 윤곽(Y±7,000)과 상면 단차선(Y±6,700)이 둘 다 R=500 필렛 | 두 호의 중심 X 가 같으면 그 사이는 평면 띠다 → loft/빗각기둥 |
| 적분값이 0.2 m³ 샜다 | 구간 끝점이 링 꼭짓점이라 스캔라인 교차 수가 어긋난다 | 내부 노드 구적(가우스)을 쓴다. 심프슨은 끝점을 밟는다 |
| 구조 형상이 표로 잡힌다 | 평면도 격자가 73행×83열짜리 「표」가 된다 | 표 색인에 레이어 필터(CS-TABL-*)를 준다 |
DIMENSION.actualMeasurement 로 단위를 정하면 틀린다 | 주요 치수는 m 표기(8.816), 상세는 mm 표기(950). 표시값이 온다 | 도곽 블록 배율·블록 scale·격자 간격으로 교차검증한다 |
11.4.8 IFC 출력
node src/emit/ifc.mjs out/pier-p9.ir.json → out/pier-p9.ifc (IFC4, 170 KB)
IFCBEAM 1(코핑) · IFCCOLUMN 2 · IFCFOOTING 4 · IFCPILE 44
IFCPLATE 6(솔플레이트) · IFCDISCRETEACCESSORY 6(받침) · IFCBUILDINGELEMENTPROXY 14
해석 솔리드를 우선한다. 원기둥은 IfcCircleProfileDef 로 나가 진짜 원이다 —
메시로 내면 검증하는 쪽이 「왜 Ø508 이 아니냐」를 다시 겪는다. boolean 결과(코핑·문양 2)만
이미 검증된 IfcFacetedBrep 이다.
절대 위치는 IfcMapConversion 으로 준다. 모델은 로컬 원점에 두고 좌표를
3억 mm 로 부풀리지 않는다 — 뷰어가 정밀도를 잃는다.
12미검증 영역 — 다음 도면에서 확인할 것
실증하며 채울 목록. 각 항목에 확인 방법과 예상 대응을 미리 적어뒀다.
| 항목 | 확인 방법 | 예상 대응 |
|---|---|---|
| 입면도 · 좌/우측면도 가설 | 해치 유무로 단면/투영을 가른다. 폐곡선을 뽑아 재료 영역이 아닌 것이 확인되는지 본다 | role=validator로 고정. 3D 정투영 렌더와 윤곽 대조하는 경로를 만든다 |
| flip 판정 가설 | 비대칭 특징 하나로 3.3 절차 적용 | 투상법을 설정값으로 두지 말고 도면에서 판정. 판정 결과를 앵커에 기록 |
| DXF 입력 가설 | 같은 도면을 DXF로 받아 엔티티 목록이 동등한지 대조 | 변환 후 투입이 먼저. 상시 필요하면 DXF 리더를 parse.mjs 뒤에 붙인다 — 하류는 그대로 |
| 공통 datum이 없는 도면 검증 | 뷰별 검출 기준선 y가 서로 다른가 | 실재했다 — 교량 삽도 7장 전부. datum:{mode:'none'} + 뷰별 anchors.v0로 해결.
예상 대응 ①이 그대로 맞았다. 다만 G2의 교차검증이 같이 사라져 검증이 G3로 밀린다(2.2).
예상 못 한 것: 「기준선이 없다」가 예외로 드러나지 않는다. 계획고 라벨이 있는 도면 (횡단경사)에서 검출기가 그럴듯한 틀린 y를 돌려줬다 — 서로 다른 두 표고 라벨이 같은 답을 냈다. 라벨 무리 검사를 넣어 던지게 고쳤다 |
| 다중 시트 가설 · 축척 혼재 검증 | 좌표 분포가 여러 덩어리인가. 같은 부재의 치수가 뷰마다 배수 관계인가 | 다중 시트는 여전히 미확인(빈 시트 테두리만 봤다).
축척 혼재는 실재. 교량폭원 1:1 · 중앙분리대 방호벽 5.805배.
viewBoxes[뷰].unitMm으로 해결 — 예상대로 「뷰별 축척 계수를 앵커에」가 맞았다.
추가로 드러난 것: DWG 모델단위 자체가 도면군마다 다르다(1m vs 1mm).
이건 예상 목록에 없었고 core에 상수로 박혀 있었다 |
| 원호·곡면 구조 부분 | ARC/ELLIPSE 엔티티 비중을 센다 | 비중 측정됨: 거더배치 17% · 교량시종점 11% · 교대배치 8% · 횡단경사 1% · 나머지 0%. 세는 것만으로는 부족했다 — 비중이 큰 도면들은 ARC 이전에 치수(기호)에서 먼저 막혔다. 선분 근사는 아직 안 했다 |
| 곡선 선형 · 구배 가설 | 종단이 직선 from/to로 표현되는가 |
IR이 측점(station)을 다뤄야 한다. sweep 연산 추가 |
| 제목 없는 뷰 검증 | 표제 귀속에 실패하는 조각이 남는가 | 실재했다. 교량폭원은 제목이 아예 없고(조각 1개 → 뷰 0개 · orphan 1,810선),
나머지는 제목이 N벌 중복이다(중복률 47~84%).
「절단 기호 좌표로 역귀속」은 못 썼다 — 절단 기호 자체가 없는 도면이었다.
cfg.viewBoxes(위치 기반 수동 매핑)가 실제 해법이었다.
예상 대응 중 fallback 쪽만 맞았다 |
| 교량 도면 부분 | 상부공이 직선 from/to로 표현되는가. 하부공 도면이 받침 좌표·표고로 기술돼 있는가 |
7장 중 1장(중앙분리대)만 끝까지 통과 — 4.8058 m³, 모델러·해석 일치. 상세는 11.0. 상부공·하부공 질문은 답을 못 얻었다(표기 치수가 있는 일반도가 없었다). 실제로 막은 것은 연산이 아니라 입력 해석이었다 — 단위 · 제목 · 축척 · 레이어 · 폐곡선 버그. 11.2의 예측(sweep·revolve·경사 말뚝)은 하나도 병목이 아니었다 |
| 유형 삽도(도해) 검증 신규 | 치수가 숫자인가 기호인가를 세어본다. 구조 윤곽이 닫히는가 | 일반도와 다른 유형으로 갈라 다룬다(11.4). 기호 치수뿐이거나 윤곽이 안 닫히면 솔리드 대상이 아니다 — 게이트로 밀어넣기 전에 걸러낸다 |
| DWG 모델단위 혼재 검증 신규 | DIMENSION.actualMeasurement와 치수 글자를 비교한다 |
cfg.unitMm. 상수로 두면 두 번째 도면군에서 모든 값이 1000배로
조용히 틀린다 — 폐곡선도 부피도 정상적으로 나오기 때문에 알아채기 어렵다 |
| 건축 도면 가설 | datum이 FL, 앵커가 그리드 통심선으로 바뀌는가 | 규칙 층을 부재 종류별로 분리(벽·슬래브·보). 그리드는 datum의 X·Y 확장 |
| IFC 출력 가설 | part에 ifcClass 추가 후 왕복 |
civil 계열 엔티티명은 스키마 직접 확인. 열린 변(T 정점) 처리 방침 결정 |
| 수평 단면 기반 층 스택 부분 | 층별 XY 프로파일을 Z로 쌓았을 때 union 손실이 사라지는가 | 조각이 Z로 진짜 겹치게 만든다. 평면 헌치가 자동으로 따라온다 |
12.1 실증할 때 기록할 것
- 어느 게이트에서 몇 번 막혔는가 — 방법론의 약한 고리다
- 설정으로 해결됐는가, 코드 수정이 필요했는가 — 후자면 코어에 도면 고유값이 샌 것
- 등급 분포(exact/tolerant/opaque) — 생성기가 얼마나 부족한지의 척도
- 새로 나온 함정 — 증상·원인·대응 3열로 8장에 추가
- 이 문서에서 틀린 것 — 표본 하나로 일반화한 자리가 어디였는지
13용어·필드 사전
13.1 뷰 스펙
| 필드 | 뜻 |
|---|---|
plane | 이 뷰가 그리는 평면. XY · XZ · YZ |
u0 | 가로축 원점. 프로젝트 = u − u0 |
v0 | 세로축 원점. 수직 단면은 생략(표고가 이미 기준) |
flip / flipV | 축 방향이 반대. 뒤집기 홀수면 링 감김을 되돌린다 |
swapUV | 도면 가로축이 프로젝트 Y, 세로축이 X (수평 단면) |
role | generator · assigner · locator · validator (3.2) |
13.2 도면 조회 도우미 — fit()만 쓴다
| 도우미 | 용도 | 실패 시 |
|---|---|---|
loop(뷰, 폭, 높이) | 그 치수의 폐곡선 | 0개·2개 이상이면 예외. 자기교차면 예외 |
circles(뷰, 지름, 레이어) | 원(관통·기둥). u→v 정렬 | 없으면 예외 + 있는 목록 제시 |
opening(뷰, 폭, 높이) | 개구 기호에서 되찾은 개구부 | 0개·2개 이상이면 예외 |
rect(u0,v0,u1,v1) | 직사각 프로파일 | — 값은 반드시 다른 뷰의 실측치 |
expect(뷰, 폭, 높이, 이유) | 실재 확인만. rect 교차검증용 | 없으면 예외 |
13.3 등급·출처
| 값 | 뜻 |
|---|---|
src: measured / design / derived | 도면에서 뽑음 / 사람이 정함 / 식으로 나옴 |
grade: exact / tolerant / opaque | 파라미터로 완전 재현 / ±ε / 재현 불가 → 좌표째 동결 |
kind: topology / design | 규칙 종류 — 막는다 / 경고한다 |
keep: false | boolean에 소모될 중간 파트. 최종 산출물에만 이름·색을 준다 |
이 문서의 위치 — docs/methodology.html.
절차는 docs/2d-3d-process.md, 사례별 교훈은 docs/2d-3d-conversion.md,
파라메트릭 설계 노트는 docs/parametric.md, 남은 일은 docs/roadmap.md.
개정 방침 — 새 도면을 실증할 때마다 12장의 항목을 검증 등급으로 승격시키고, 새로 밟은 함정을 8장에 3열로 추가한다. 표본 하나로 일반화한 자리를 표시해두는 것이 이 문서의 정직함이다.