chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합

- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ahnyongjooandClaude Sonnet 5.5 committed 2026-10-28 11:20:18 +09:00
commit 964a841de1
3978 files changed
+58013356

No files matched your search

+191
View File
@@ -0,0 +1,191 @@
# 철근 생성·검토·재료표 파이프라인 (대호지교 P1 두부보 작업에서 검증, 2026-10-02)
나중에 "도면(DXF) → 라이노 철근 생성 → 검토 → 재료표 DXF 출력" 프로그램으로 만들기 위한 방법 정리.
모든 Rhino 코드는 rhino_mcp `execute_rhino_code` (IronPython 2.7) 기준.
---
## 0. 환경 제약 (반드시 지킬 것)
- f-string 금지, walrus 금지, 타입힌트 금지.
- **exec 스코프 문제**: 함수 안에서 모듈 전역 변수를 못 본다. 리스트 컴프리헨션·lambda 안에서 바깥 변수 참조 시 `name 'x' is not defined` 에러.
- 해결: 루프는 `while/for`로 풀어 쓰기, 함수가 필요하면 기본인자로 바인딩 `def L(x, ents=ents):`.
- 출력은 마지막에 `result = "..."; print(result)`.
- 무거운 계산(`AreaMassProperties.Compute` 다수 호출)은 MCP 소켓 타임아웃 → 마크별로 나눠 호출, 면적 대신 isocurve 길이 사용.
- `sc.sticky[...]`에 중간 결과를 저장해서 다음 호출에서 이어 쓰기.
- 이 PC에는 Python/ezdxf 없음 → DXF 파싱은 bash `tr -d '\r' | awk`, DXF 쓰기는 IronPython으로 UTF-8 저장 후 `iconv -t CP949`.
- 라이노 오브젝트 삭제는 사용자 확인 후에만.
---
## 1. 도면(DXF) 읽기
DXF는 AC1021(UTF-8 텍스트), `$DWGCODEPAGE=ANSI_949`. group code/value가 2줄씩 반복.
- `awk/txt.awk` : TEXT/MTEXT/ATTRIB → `x y 타입 문자열` (좌표 정렬하면 표가 행 단위로 읽힘)
- `awk/geo.awk` : LINE / LWPOLYLINE / ARC → 선형 추출 (레이어 포함)
- `awk/ins.awk` : INSERT / CIRCLE 등 → 블록 삽입점 (철근 단면 점 = 블록 `CSBSEC1`)
```bash
tr -d '\r' < "철근재료표 및 상세(...).dxf" | awk -f txt.awk | sort -t$'\t' -k2,2nr -k1,1n > mat.tsv
tr -d '\r' < "코핑구조도(...).dxf" | awk -f geo.awk > cop_geo.tsv
```
### 1-1. 재료표 해석 (같은 y = 같은 행)
열 x 경계(원본): 번호|형식|직경|개수|A|B|C|D|E|R|X1|X2|J|길이|총길이|단위중량|총중량|총중량(할증)
- `"` = 위 행과 같은 직경.
- 1000mm 이상은 m(소수 3자리), 미만은 mm 정수로 표기.
- 단위중량: H16 1.560, H19 2.250, H22 3.040, H25 3.980, H29 5.040 (kg/m).
- 할증: H22 이상 6%, H19 이하 3%. 별도로 "소계(겹이음 할증 6.80%)" 행 = 소계중량×1.068.
- **형식 V(⊓뚜껑+⊔바닥 박스) 길이 규칙: L = 2A + 2B + 2C** (A=폭, 1250/1005 두 종류면 평균; B=박스 전체높이(바닥 다리); C=뚜껑 다리 510=겹침). 재료표 L은 평균, 총길이=Σ.
- **형식 VII(C8/C10)**: L = a+b+c+d, X1/X2 = 꺾임 사잇각.
### 1-2. 조립도/평면도에서 형상·위치 읽기
- 레이어 `CS-RBAR-MAJR` 선 = 철근 중심선. `CS-CONC-MAJR` = 콘크리트 외곽. `CS-CNTL` = 중심선.
- 텍스트 "주철근 조립도(정면도) ①CYCLE / ②CYCLE" 근처 ATTRIB(마크명 + "H25 @ 250")로 어떤 마크가 어느 CYCLE인지 판단.
- 조립도 좌표 → 코핑 로컬 s: `s = x - (코핑 중심 x)`; 코핑 중심 = 길이 23960 주철근 양 끝 중점 등으로 구함.
- 평면도에서 폭방향 위치: 수평 RBAR 선들의 y 값 → `w = y - 중심선y`. 치수 tick(CZDIMA1 블록) 간격(120, 130, 24@125 …)으로 피복·간격 확인.
- **검증 요령**: 도면 선 길이가 재료표 치수와 정확히 같으면(예: 4167.1, 3158.8) 그 도면 선이 철근 중심선임을 확신할 수 있다.
---
## 2. 코핑 로컬 좌표계 & 단면 외곽
```python
ang = math.radians(19.105) # 코핑 길이방향 (수평 edge 방향 히스토그램으로 구함)
u = Vector3d(cos, sin, 0) # 길이방향 s
wv = Vector3d(-sin, cos, 0) # 폭방향 w
c = coping.GetBoundingBox(True).Center # 원점
```
- 길이방향 각도: Brep.Edges 중 수평(|dz|<1)·길이>1000 edge들의 atan2 각도 히스토그램.
- 단면 외곽: `Intersection.BrepPlane(brep, Plane(c, u, Z))` → `DuplicateSegments()` → 각 세그먼트를 (s, z)로 출력.
- 뷰 정렬(좌표 안 바꾸고 보기만): `vp.SetCameraLocations(target, target - dir*1e5); vp.CameraUp = Z` (Left: dir=u, Front: dir=u를 90° 회전), `ChangeToParallelProjection(True)`.
---
## 3. 철근 중심선 생성 (피복 = 철근 중심 기준 120)
1. 단면 외곽의 각 면을 **안쪽으로 120 오프셋한 직선**들로 표현 (점 + 방향 + 안쪽 법선).
2. 인접 오프셋선끼리 교점 → 꺾임점. 2D (s,z)에서 직접 풀면 됨.
3. 도면에 고정 길이 구간이 있으면(예: a=630 수직다리, d=2331 끝 연장) 그 길이만큼 연장해 끝점 결정.
4. 3D 변환: `P = c + u*s + wv*w + Z*(z - c.Z)`; 대칭 마크는 s 부호만 반전.
5. 폭방향 위치(w)는 도면 평면도 기준. 1CYCLE: ±1630, ±1375 … ±125 (14본) / 2CYCLE: ±1500 … 0 (13본).
6. 굽힘: KDS 24 14 21 표 4.5-1 최소 내면지름(이형 D16이하 2db, D19이상 3.5db → 내면 반지름 1db/1.75db) 이상의 내면 R + 철근반지름 = 중심선 R. 적용할 R값은 사용자 결정 [미확정]. (기존 모델의 R=3db/4db는 과거 값)
```python
pc = Polyline(pts).ToPolylineCurve()
fc = Curve.CreateFilletCornersCurve(pc, Rc, tol, angtol) # 모든 내부 모서리 한 번에
brep = Brep.CreatePipe(fc, r, False, PipeCapMode.NONE, False, tol, angtol)[0]
```
검증: `1/fc.CurvatureAt(t).Length` 최소값 == Rc.
7. 블록에 넣을 때: 인스턴스 Xform 역변환 후 `InstanceDefinitions.ModifyGeometry(idx, geoms, attrs)` (**3인자 형태, 원본 attrs Duplicate** — 색상 깨짐 방지). 수정 전 기존 형상은 `<마크>_backup_YYYYMMDD` 블록정의로 보관.
### 색상 규칙 (직경별, ACI)
H10/H13=74, H16=5, H19=210, **H22 이상 전부 (104,0,25)**.
- 얕은 구조(문서인스턴스→leaf): leaf `ColorFromLayer`, 마크 레이어 색.
- 깊은 구조(C12~C18: mark→station→box→leaf): **station·box·leaf 3단계 모두 `ColorFromParent`**. 하나라도 ColorFromLayer면 엉뚱한 레이어색(코핑 황토색)으로 보임.
---
## 4. 검토(감사) 방법
### 4-1. 파이프 Brep에서 중심선 복원 (빠름)
각 face에서 isocurve 한 방향이 닫힌 원(길이 2πr)이면 파이프 면 → 다른 방향 isocurve를 원주 0과 0.5 위치에서 2개 뽑아 평균:
- 중심선 길이 = (len(iso@0) + len(iso@0.5)) / 2 (굽힘 토러스에서도 정확)
- 직선 면이면 양 끝점 평균 = 중심선 선분.
- 선분들을 끝점 근접으로 체인 연결 → 인접 선분 `Intersection.LineLine`으로 가상 교점 → **재료표 방식 치수(교점 간 거리)**와 사잇각.
- 막힌(캡) 파이프: 평면 face 중심 = 열린 끝점. 열린 파이프: `DuplicateNakedEdgeCurves` 원 중심.
### 4-2. 피복
- **철근 표면을 `BrepFace.PointAt` 격자(60×24)로 샘플 → `coping.ClosestPoint` 최소거리 + r = 중심 피복.**
- FastRenderMesh 정점 사용 금지(엉터리 값 91~104 나옴).
- 수직 레이캐스트(`Intersection.RayShoot`)는 상·하면까지 거리 참고용. 경사면 근처는 ClosestPoint 사용.
- 상단 직선 철근: "그 철근이 지나는 구간의 가장 낮은 상면"에서 120 (C26 교훈).
### 4-3. 개수
- 단순 마크: 인스턴스 정의 leaf 개수.
- 박스 마크: mark idef → station 인스턴스 → box 인스턴스 개수(인스턴스 수로 셈, 정의 수 아님).
### 4-4. 박스(V형) 측정
box 정의의 조각 중 높이가 큰 2개 = 뚜껑/바닥(아래쪽이 높은 것이 뚜껑). 나머지 = 연장 필러.
- A = 뚜껑 양 끝점 거리, 뚜껑top = bbox.Max.Z − r, 바닥bot = bbox.Min.Z + r
- 필러가 뚜껑 끝에 닿으면 뚜껑 연장, 바닥 끝에 닿으면 바닥 연장.
- C = 뚜껑top − 뚜껑끝, B = 바닥끝 − 바닥bot, 겹침 = 바닥끝 − 뚜껑끝 (510 미만이면 결함).
- 재료표 비교: 재료표 A·높이 = 모델 + 47.6 (= 2×(주철근 r + 스터럽 r)) → 도면은 스터럽이 주철근 바깥을 감쌈.
---
## 5. 재료표 DXF 출력
- 형식: **R12 (AC1009)**, `$DWGCODEPAGE=ANSI_949`, TABLES에 LTYPE CONTINUOUS / LAYER / STYLE(KOR, malgun.ttf), ENTITIES에 LINE, TEXT(72=1, 73=2 가운데정렬 + 11/21 정렬점).
- 원본 표 규격: 열 경계 상대 x = 0,1200,2400,3600,4800,6800,8800,10800,12800,14800,16800,18800,20800,22800,24800,26400,28000,29600,31200 (+비고 열 ~40000), 헤더 960(2단), 행 480, 글자 200, 제목 560.
- 행 순서: [H29 그룹][소계][소계(겹이음 6.80%)] → H25 → H22 → H19 → H16 … → 총계, 총계(겹이음).
- 소계 행은 세로선을 왼쪽 끝 + 길이 열 이후만 그림(병합셀 흉내).
- 계산: 중량(t) = 총길이 × 단위중량 / 1000, 할증 = ×1.06 또는 ×1.03, 겹이음 = ×1.068. 총계는 반올림된 소계들의 합(원본과 동일 방식).
- 저장: IronPython `System.IO.File.WriteAllText(p, txt.replace("\n","\r\n"), UTF8Encoding(False))` → bash `iconv -f UTF-8 -t CP949`.
- 검증: `RhinoDoc.CreateHeadless(None).Import(path)`로 별도 문서에 불러와 텍스트/선 개수·한글 확인 후 Dispose (루프 변수 None 처리 필수).
- 파일명 규칙: `YYMMDD_철근재료표 작업중.dxf` (도면 폴더에 저장).
---
## 5-2. 측면철근/캡타이 (C19~C25, 2026-10-02 검증)
- **배치 근거**: 단면 A-A/B-B/C-C의 철근 점(블록 `CSBSEC1`) y값 = 측면철근 높이(200 간격), x값 = 폭 위치. 정면도 점 = 폭방향으로 가로지르는 철근(C25)의 (s, z). 정면도 점 개수 합 = 재료표 수량으로 검증.
- **도면→모델 좌표**: 정면도 `s = x − (코핑 중심 x)`, `z = 7738 + (y − 기둥위 하면 y)`. 방향 반전 여부는 비대칭 마크(C12 = s 최소 쪽)로 판단.
- **VI 닫힌 고리**: 높이별로 120 오프셋 경계(헌치 대각/끝면) + WRAP(주철근 r + 고리 r, 바깥 감싸기) → 재료표 A와 정확히 일치. 닫힌 폴리라인은 **변 중간에서 시작**해야 4모서리 모두 필렛됨. 띠철근 R=2db.
- **VIII 갈고리 직선**: 135° 갈고리, 중심 R=60 (호 길이 = 재료표 b 141), 연장 c=100, L=A+2(b+c). A = 고리 A − 9. **갈고리는 수평면 안에서, 폭 바깥쪽(w 부호 방향)으로** (사용자 지시 2026-10-02). 위·아래로 꺾지 않음 (아래는 헌치 관통, 위는 잘못된 방향). 안쪽으로 꺾으면 중앙 ±106 철근끼리 갈고리가 겹침.
- **III ⊓ (C25)**: 윗변 A=3260 폭방향(w ±1630), 다리 300 아래. 점 위치가 하면에 가까우면 다리 관통 → 생성 시 `(z−300) − 하면z < 120` 자동 검출해서 보고.
- 생성 방식: 마크별 InstanceDefinition 1개(월드좌표 형상, 기준점 원점) + Identity 인스턴스 1개, leaf ColorFromLayer, 레이어 `..._1CYCLE::C{n}_H16` 파랑(0,0,255).
- 피복 검증: 긴 철근 전체를 Brep.ClosestPoint로 샘플링하면 MCP 타임아웃 → 대표 지점 몇 개를 Brep.ClosestPoint로 재고, 관통 여부는 Edge 점 `IsPointInside`로.
- 화면 격리 보기: 다른 레이어 IsVisible=False (인덱스 목록 sticky에 저장) → 파일 저장 전 복구 → 저장 → 다시 숨김.
---
## 5-3. 간섭 / 튀어나옴 / 피복 일괄 체크 (2026-10-02 검증, 전체 26마크 ~30초)
1. **중심선 선분 추출**: 각 파이프 면에서 원주 0 위치 isocurve를 `ToPolyline(0,0,0.05,0,0,0.3,0,3000,True)`로 적응 분할 → 각 정점의 곡선 매개변수 t에서 `face.PointAt(t, v0)`와 `PointAt(t, v0.5)`의 중점 = 중심점. 중첩 블록은 Xform을 곱해 내려가며 처리, 같은 leaf 형상은 캐시.
2. **간섭**: 마크별 `RTree`(선분 bbox를 r만큼 키움) → 마크 bbox가 겹치는 쌍만 `RTree.SearchOverlaps(treeA, treeB, 0, System.EventHandler[RTreeEventArgs](cb))` (`e.Id`, `e.IdB`) → `Intersection.LineLine(..., finite=True)`, 실패(평행)면 `Line.MinimumDistanceTo` → 관입 = rA + rB − 거리 > 0.5 이면 기록(철근쌍별 최대).
3. **피복/튀어나옴**: 코핑이 폭방향 압출 형상이므로, 점을 (s,w,z)로 바꿔 2D 단면 다각형 내부 판정 + 변까지 거리, 측면은 1750−|w|. 중심피복 < r 이면 튀어나옴. (Brep.ClosestPoint 대량 호출은 타임아웃, 메시 ClosestPoint는 값이 틀어짐)
4. **출력**: CSV(UTF-8 BOM, 엑셀 한글) + 요약 md + 라이노 체크 레이어(TextDot, PointCloud) — 모델 자체는 건드리지 않음.
---
## 5-4. CTC 검사 · 박스 철근 보정 · 갈고리 반전 · 재료표 정렬 (2026-10-06 검증)
**CTC(철근 중심 간격) 검사** — `rhino/07_ctc_extract.py`
- 마크별 리프 Brep 중심점을 코핑 로컬 (s,t,z)로 뽑아 JSON → 고유값(허용오차 1~2mm) 정렬 → 차이를 도면 `@` 표기와 대조.
- 도면 간격 표기는 `코핑구조도` DXF의 `CS-DIMT` TEXT (`24@125=3.000`, `11@250=2.750`, 평면 스터럽 `7@300|14@150|8@300|16@150|19@300|…`, `38@600=22.800`).
- 주의: 같은 칸의 뚜껑/바닥 조각은 s가 다를 수 있다. 클러스터의 **첫 값만** 쓰면 가짜 어긋남이 생긴다(실제로 한 번 오판함) → 칸 안의 모든 리프 s의 min/max를 같이 볼 것.
- 이 값들은 철근 마크 레이어가 꺼져 있으면 `doc.Objects` 기본 열거에서 빠진다 → `ObjectEnumeratorSettings.HiddenObjects=True`.
**박스(⊓+⊔) 바닥 조각 보정** — `rhino/05_realign_base_pieces.py`
- 증상: 피복 보정 때 바닥 조각을 헌치 법선 방향으로 옮겨서 뚜껑과 s 평면이 1~3mm 어긋남 (C12/C18 2.68, C14/C16 1.95).
- 보정: 어긋남 −dev 만큼 s 이동 + 헌치 경사 접선 방향으로 dz = (경사)·ds 이동(수직 피복 유지) → 뚜껑 윗변 중심 z에서 `Brep.Trim` 으로 상단 재절단(겹침 510).
- 박스 idef는 좌우 대칭 인스턴스가 공유하므로 idef 하나만 고치면 4개 박스에 동시 적용됨(검증은 모든 인스턴스로).
- `ModifyGeometry(idx, geoms, attrs)` 3-인자 + `attrs=ob.Attributes.Duplicate()` 로 ColorFromParent 보존. 백업 idef `<이름>_bk1006b`.
**갈고리 방향 반전** — `rhino/06_flip_hooks.py`
- 철근 축을 지나는 s-수직 평면으로 자른 단면 원 중심 = 축 위의 점 → `Transform.Mirror(축점, 폭방향 법선)` + `Brep.Flip()`. 길이 불변.
- 선택은 centroid t (갈고리 때문에 축에서 ~2mm 벗어남) 대역으로 하고, **개수가 예상과 다르면 수정하지 않고 보고**.
**재료표 DXF 텍스트 정렬** — `dxf/leftalign.ps1`
- R12/CP949 DXF에서 표 칸 왼쪽 경계(세로선) + 200 위치로 TEXT 왼쪽 정렬(72=0, 73=2). `powershell -File leftalign.ps1 -In 원본.dxf -Out 새파일.dxf -Pad 200`.
- 원본 재료표는 가운데(72=1) 242개 / 오른쪽(72=2) 58개 정렬 — 이 파이프라인의 표는 왼쪽 정렬 규칙을 쓴다(사용자 지시).
**화면/레이어 함정**
- 블록 안쪽 리프가 **다른 레이어**(예: `P1_두부(코핑)`)에 있으면, 마크 레이어를 켜도 그 레이어가 꺼져 있으면 안 그려진다. C12~C18 스터럽 리프가 코핑 레이어에 있음 → 철근만 보려면 코핑 **레이어는 켜 두고 솔리드/보조선만 객체 단위로 숨긴다**.
- 화면 격리 후 저장할 때는: 현재 표시 상태 저장 → 원래 상태 복구 + 숨긴 객체 Show → `doc.Save()` → 격리 상태 재적용. (저장은 127MB라 MCP 소켓 타임아웃이 나지만 Rhino 안에서는 계속 진행됨 → 결과 파일 생길 때까지 until-loop로 대기.)
- Left 뷰 정렬: `SetCameraLocations(target, target - dir*60000)`, dir=(cos a, sin a, 0), `CameraUp=Z`, 그 뒤 `ZoomBoundingBox`.
---
## 6. 프로그램화 시 모듈 구성 제안
1. `dxf_reader` – txt/geo/ins 추출, 재료표 행 파서, CYCLE별 마크 목록, 조립도 철근 polyline.
2. `section` – 콘크리트 Brep → 로컬 프레임(u,w,원점) → 단면 외곽 (s,z) 세그먼트.
3. `rebar_builder` – 형식(I~VIII)별 중심선 생성기(오프셋선 교점 + 고정길이), 폭방향 배열, KDS R 필렛, 파이프, 블록/레이어/색.
4. `auditor` – isocurve 중심선 복원, 치수/각도/개수/피복/겹침 비교 → 차이 리포트.
5. `table_writer` – R12 CP949 재료표 DXF (원본 규격 + 비고 열).
## 5-5. 간섭 재검사 스크립트 (2026-10-06) — `rhino/08_clash_check.py`
- 읽기 전용. 철근별 단면 원 중심 → 중심선 선분 → RTree → 선분 최단거리, 관입 = rA+rB−거리 > 0.5. 박스(뚜껑·바닥·연장 조각) 내부와 같은 철근은 제외, P1 레이어만 (P2/P10/P12 숨김 레이어 복사본 제외).
- 함정: ① `BoundingBox(p0, p1)` 는 점이 min→max 순서가 아니면 무효 상자 → 큰 교차가 통째로 누락됨. `BoundingBox([p0, p1])` 사용. ② 숨김 레이어 객체는 `ObjectEnumeratorSettings.HiddenObjects=True` 로 열거해야 함. ③ NURBS 파라미터 0.5 지점은 반대편 점이 아님 → 단면 원 `TryGetCircle` 중심 사용. ④ 실행이 길어 소켓 타임아웃이 나도 Rhino 안에서는 계속 돈다 → 결과 파일 갱신을 until-루프로 대기.