Files
Pier_v1/context-migration/기술노트_DWG판독_Rhino모델링환경.md
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 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>
2026-10-28 11:20:18 +09:00

91 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 기술노트 — DWG 판독·Rhino 모델링 작업 환경 함정
```
소유자: 심영표
확정일: 2026-10-07
최근확인일: 2026-10-20
재확인 조건: ODA·Rhino·Python 버전이 바뀌거나 Rhino MCP 도구가 갱신될 때
```
## DWG → DXF
- `dwg2dxf`(LibreDWG, 기존 박스 프로젝트 기준 변환기)는 이 PC에 **없음**. ODA File Converter 27.1.0 사용: `C:\Program Files\ODA\ODAFileConverter 27.1.0\ODAFileConverter.exe <in_dir> <out_dir> ACAD2018 DXF 0 1 "*.DWG"`
- 한글 경로도 정상 변환. RAG 원본 보호를 위해 임시 폴더에 복사 후 변환 (`Code/교각모델링/convert_dwg.py`)
- 콘솔 출력 한글이 깨지면 `PYTHONIOENCODING=utf-8` 지정 (파일 내용 자체는 정상)
## DXF 판독
- Python 3.12에 ezdxf 1.4.4 설치되어 있음. matplotlib는 2026-10-07에 설치(3.11.2) — 렌더링용
- ezdxf TEXT에는 `char_height` 속성이 없음(MTEXT만). `e.dxf.get("char_height")`는 TEXT에서 예외 발생 → dxftype 분기
- ezdxf 렌더 PNG에서 한글은 □로 표시됨(폰트 미매핑). 문자는 survey JSON으로 읽을 것
## Rhino MCP (Rhino 8, 헤드리스 slot)
- `get_viewport_image`가 **회색 빈 화면만 반환**함(Perspective/Front, 카메라 지정 모두). 시각 확인은 대체 수단 필요 — 동일 방식 재시도 금지, 단면 재투영 수치 검증 또는 사용자 육안 확인으로 대체
- 새 문서 기본 단위 mm, 허용오차 0.001. 스크립트는 `__rhino_doc__` 사용, 보관 파일은 `exec(open(path, encoding="utf-8").read(), {...})`로 실행
- 객체 삭제·이동(Transform deleteOriginal) 후 저장하면 **삭제 객체(undo 기록)가 저장 개수에 포함**됨(34 → 68). 저장 전 `doc.ClearUndoRecords(True)` 실행
- `save_doc`은 직전 파일을 `.3dmbak`으로 남김 → 폴더 `backup/`으로 이동
- 검증은 저장 파일을 **새 slot에 open_doc 후 재실행**하면 파일 실재성과 정확성을 함께 확인할 수 있음
- Rhino slot이 작업 도중 닫힐 수 있다(2026-10-07 aardvark, 원인 미확인). `rhino_closed` 오류가 나면 다른 slot을 쓰거나 spawn_slot으로 새로 띄운다
- **`Brep.CreatePipe(..., fitRail=True, ...)`는 직선-원호-직선 레일에서 빈 결과를 내고, 대량 실행 시 헤드리스 Rhino가 크래시함(2026-10-07)** → `fitRail=False` 사용. 동일 방식 재시도 금지
- 대량 생성 전에 형상 종류별로 1개씩 먼저 시험한다
## 검증 설계 교훈
- 음성 대조(일부러 객체를 옮겨 FAIL이 나오는지 확인)는 반드시 **위치 이동과 높이 이동을 모두** 넣는다. 2026-10-07 대좌 검사기가 Z 검사만 있어 X 3mm 이동을 놓쳤다 → 간격·절대위치 검사를 추가해 해결
- 피복·내부 검사에서 여러 부재의 `ClosestPoint`를 쓰면 부재 간 내부 접합면까지 거리를 재서 음수 피복이 나온다 → 콘크리트를 `CreateBooleanUnion`으로 합친 외곽면 기준으로 잰다
- 검사 정의는 재료표 정의와 맞춘다(예: 재료표 L에 커플러 길이 포함 여부). 1차 FAIL은 증거 파일로 보존한다
- **`for o in doc.Objects`는 숨긴 객체를 건너뛴다.** 사용자가 Rhino 창에서 객체를 숨기면 검증이 '없음'으로 오판한다 → `ObjectEnumeratorSettings(HiddenObjects=True, LockedObjects=True)`로 센다 (2026-10-07 P1-FT)
- `open_doc(clearFirst=True)`는 숨긴 객체를 지우지 않는다 → 다른 파일을 열어 비교할 때는 새 slot을 띄운다
- 여러 조각을 이은 곡선의 `Domain.Mid`는 길이 중간이 아니다(세그먼트 매개변수). 비대칭 철근(갈고리 한쪽)의 위치는 바운딩 상자로 잰다
- `Brep.CreateBooleanUnion`은 입력 순서에 따라 실패할 수 있다 → 순서를 바꿔 재시도한다
- 음성 대조는 마크마다 **수평 위치와 절대 높이**를 모두 변형해 본다. 길이·범위 검사만으로는 평행 이동을 못 잡는다(2026-10-07 B9 +5mm 미검출 → 하단 높이 검사 추가)
- MCP 호출은 300초 제한이 있다. 철근 관 1,000개 이상의 `VolumeMassProperties`는 제한을 넘는다 → 철근 관 지문은 바운딩 + 면 수로 잰다
- Rhino slot이 다른 호출 중 응답 불능이 되면 MCP가 자동으로 새 slot을 띄워 **빈 문서에서** 다음 스크립트를 실행할 수 있다 → 파일을 연 직후 객체 수(숨김 포함)를 반드시 확인한다. 실패한 열기가 남긴 객체로 문서가 오염되면 slot을 닫고 새로 띄운다
- 갈고리 방향처럼 추정한 형상은 곡면(하면 원호) 근처에서 콘크리트를 뚫을 수 있다 → 콘크리트 내부 표본 검사로 반드시 확인
- 바운딩 최고점·최저점으로 '높이'를 재면 위·아래로 꺾인 갈고리를 잡는다 → 수평부 중간점을 쓴다
- 사각 부재의 '교축방향'을 긴 변으로 판별하지 않는다(받침 NO.3는 교축직각 변이 더 김) → 방향(축과의 각도)으로 판별한다 (2026-10-08)
- 검사 항목 설계 시 높이·크기·각도만 넣고 **수평 위치(평면 중심)**를 빠뜨리기 쉽다 → 음성 대조 전에 수평·높이 각각에 대응하는 검사가 있는지 먼저 확인한다
- 세션이 바뀌면(익일) Rhino slot이 모두 종료되어 있다 → 저장 파일을 새 slot에 열고 객체 수부터 확인
## 보고서 PDF (reportlab)
- 2026-10-08 reportlab 5.0.1·pypdf·pypdfium2 설치. 한글은 `C:\Windows\Fonts\malgun.ttf`/`malgunbd.ttf`
- **맑은 고딕에는 음수 기호 U+2212(−) 글리프가 없어 빈칸으로 출력** → 변환 시 '-'로 치환. 다른 기호는 fontTools cmap으로 사전 점검
- 표의 짧은 열이 글자 단위로 줄바꿈됨 → 열 최소 폭 = 가장 긴 단어 폭
- 결과 확인은 pypdfium2로 페이지를 PNG 렌더링해 육안 점검 + pypdf 텍스트 추출
## Rhino 화면 이미지 만들기 (2026-10-14, 발표자료용)
- **뷰포트 캡처는 쓰지 말 것**: 이 PC에서 `/runscript`로 띄운 Rhino는 Shaded 모드를 지정해도 `ViewCapture`·`CaptureToBitmap` 결과가 항상 와이어프레임(면 채움 없음). 동일 방식 재시도 금지
- **Rhino 렌더러는 됨**: 스크립트에서 `RunScript("_-Render")` → `_-SaveRenderWindowAs "<ASCII경로>.png"` → `_-CloseRenderWindow`. 해상도·배경은 `doc.RenderSettings`(`UseViewportSize=False`, `ImageSize`, `BackgroundStyle.SolidColor`, `BackgroundColorTop`). 이미지 1장 약 30초
- 앱 .3dm은 `File3dm.Read`로 읽어 새 문서에 `Objects.Add`로 복사(활성 문서 새로 열린 상태, `OpenHeadless` 아님). 레이어 색은 객체 색으로 복사, 재질을 객체별로 만들어 `Transparency`(0~1)로 콘크리트 반투명 + 철근 색 유지
- **콘크리트(Brep)는 Mesh로 변환해 추가**해야 렌더에 나옴(`Mesh.CreateFromBrep(QualityRenderMesh)` → 합쳐서 추가). 철근 파이프 Brep은 그대로 됨
- 카메라: `vp.SetCameraLocations(target, camera)` — **인자 순서가 (목표점, 카메라점)**(거꾸로 넣으면 아래에서 올려다봄) 후 `ZoomBoundingBox`
- 간섭 확대 이미지: 철근 마크별 색 지정 + 나머지 회색, 포커스 점 ± pad 상자로 맞춤. 스크립트 `scratchpad/shot.py`(세션 임시 — 필요하면 `tools/`로 옮길 것)
## 앱 시험 일괄 실행·셸 함정 (2026-10-16)
- **시험 일괄 실행기**: `Code/교각모델링앱/tools/run_all_tests.py <fast|rhino|all> [시험명...]` — 시험마다 로그 `검증/_rerun_<이름>_<날짜>.log`, 요약 `검증/_rerun_summary_<그룹>_<날짜>.json`. fast = Rhino 없는 12개(약 40초), rhino = 실제 Rhino 7개(수십 분). 같은 그룹명으로 이름 일부만 다시 돌리면 요약 파일이 덮어써지므로 그룹명을 바꿔 돌릴 것(예: rhino2)
- **Windows 콘솔(cp949)에서 em dash(—) 등 출력 시 UnicodeEncodeError** — 실행기 상단에서 `sys.stdout.reconfigure(encoding="utf-8")`, 하위 시험은 `PYTHONIOENCODING=utf-8`. 동일 방식 재시도 금지
- **Bash 도구에서 큰 python 헤레독(`python - <<'E'`)에 `'''` 삼중 따옴표가 있으면 "unexpected EOF"로 실패** → 스크립트를 파일로 쓰고 실행(패치 스크립트는 어차피 `Code/`에 보관해야 함). 짧은 코드는 가능
- 백그라운드 `nohup ... &` 로 띄운 작업은 하네스가 완료를 추적하지 않음 — 시작한 셸의 종료 알림이 먼저 오므로 완료로 오해하지 말고 출력 파일의 `DONE` 줄로 확인
- 같은 시험을 재실행하면 `검증/*_data` 폴더는 시험이 다시 만든다(실제 `data/` 교각은 건드리지 않음). 단 `test_okseong`·`test_rampa`·`test_saejae`는 실제 `data/<교각>/runs/current`의 게이트 보고서를 읽으므로 웹 일괄 시험이 덮어쓰면 실패 → 엔진 `tools/run_gates.py <data> <data>/runs/current`로 개별 실행해 복원(OPEN #25)
- Rhino 렌더(위 절)로 모델 육안 확인이 가능함 — 새 교각 육안 확인(OPEN #3)에 쓸 수 있는데 아직 안 했음
## 앱 시험 작성 함정 (2026-10-19)
- **`web/store.py`는 `DATA`를 import 시점에 읽는다.** 시험에서 `store`(또는 `app`)를 `PIERAPP_DATA` 환경변수를 설정하기 **전에** import하면(헬퍼 함수 안의 지연 import 포함) 이후 `create_pier` 등이 실제 `data/`에 쓴다 — 2026-10-19 `test_anchors` 첫 실행이 `data/앵커시험`·`data/복사시험`을 실제 데이터에 만들었다(삭제함). 시험은 `os.environ["PIERAPP_DATA"] = ...` → `import app, store` 순서를 지키고, `assert store.DATA == TD`로 확인한다
- 시험 간 상태 의존: `test_pier_batch_button`은 `test_web_batch`가 만든 `배치P1`(전 게이트 승인 상태)을 읽으므로 반드시 바로 뒤에 실행(사이에 `test_individual_after_batch`가 끼면 FAIL). `run_all_tests.py`의 RHINO 목록 순서 유지
- 앵커 따라가기 시험 결과(같은 도면 틀 A 45건 중 오답 0, 다른 틀 36쌍 A·B 0)는 `tools/test_anchors.py`에서 재현 가능 — 재조사 불필요
- 화면 확인 방법: `tools/make_ui_check_data.py`로 임시 데이터를 만들고 `tools/serve_ui_check.py`(포트 5057)를 띄운 뒤 헤드리스 Edge `--screenshot`으로 캡처(`검증/ui_check_shots/`). 서버는 시험 뒤 종료(PowerShell `Get-CimInstance Win32_Process`로 `serve_ui_check` 찾아 `Stop-Process`)
- **기하(선 길이) 앵커 시도(2026-10-19) — 동일 방식 재시도 금지**: 앵커 없는 숫자 필드에 콘크리트·철근·중심선 레이어 선분 길이를 대응시켜 새 도면에서 같은 위치·방향 선분을 따라가는 방식은 값이 바뀐 필드를 하나도 복원하지 못했고(울산 P1↔P2, 운곡교 P1↔P2), 철근 위치 목록(좌표)을 길이로 오인해 오답 A가 섞였다. 위치·외곽 점열은 뷰 기준점 대비 좌표라 뷰 기준점을 도면 틀마다 확정해야 한다. 문자 앵커 + 표 교차 확인이 현재 최선
- 운곡교 P3·P4 `pier.json`의 template은 `운곡교p2`(P2를 복사해 만든 교각)다 — 그래서 현재 값 = P2 값, 도면 = 자기 것. 앵커는 `운곡교P1형`을 불러왔고 값은 반영하지 않았다
## 도면 읽기 도구(`engine/geomread.py`)의 규칙과 함정 (2026-10-20)
- **틀**: 운곡교 P1 도면 틀(P1~P4)만. 첫 도면(D1) 좌표가 P1 도면과 같음: 코핑 평면 중심 (12162.4, 26412.3), 정면도 중심 x 12159.4, 단면도 기초 중심 x 30133.6, 기초 하면 y 8115.6 = 기초 EL, 코핑 EL = y + (최고 단 옆 'EL.' 글자 − 그 단 y). 틀 일치율은 템플릿 앵커의 랜드마크 글자 비율(70 % 미만이면 거절). 새재1교·옥성교는 0 %.
- **재현 검증이 먼저**: 읽기 규칙은 완성된 교각(P1)에서 같은 값이 나오는지 확인한 뒤에만 믿음 — `tools/test_geomread.py`(P1·P3·P2·P4, 초기 P4 백업 144~150건 = 검증값).
- **비교 허용오차는 값의 출처에 따라**: 글자·재료표에서 온 값(기초·말뚝·기둥·철근 개수·길이·반경·재료표 셀·코핑 상단 철근 a)은 **정확 비교**, CAD 선·원·폴리라인에서 잰 값만 1.5 mm(각도 0.05). **동일 방식 재시도 금지**: 모든 숫자에 1.5 mm 를 주면 개수 36 대 37, 재료표 17.247 대 17.377, 상단 철근 a 11855 대 11856 이 '같다'로 가려진다(P2 첫 반영에서 재료표 사본·후프 개수가 빠졌고 P4 G4 C1·C4 2 mm 실패의 원인).
- **C14 점은 템플릿 점 좌표에 맞춰 찾지 않는다**: x 오프셋이 교각마다 다름(P1·P3·P4: 13/913…, P2 상단 두 줄: 0/900…). 도면의 철근 원(R27, CS-RBAR-MAJR)을 읽고, 템플릿 C14 높이(+ 높이 이동) 5 mm 안의 원만 취함(기둥 후프 점은 x≈±902 이고 한 점은 C14 높이에서 23 mm 떨어짐, P2 의 C14 x=900 과는 2 mm). 오른쪽 반만 그려져 있어 좌우 대칭, 가운데(|x|≤20)는 한 번, 템플릿에서 왼쪽 짝이 없던 점(P1 x 2913 근처)은 오른쪽 전용.
- **재료표의 직경 칸 '"' 는 표 순서상 바로 윗 줄**이다(마크 번호 순서가 아님) — P1~P6 을 번호 순으로 이어받으면 틀림.
- **재료표 B9 개수는 격자(체스판)로 계산**: 38×29/2 = 551 = 모델이 놓는 개수, 재료표 493 과 다르면 계획된 FAIL 로 등록(P2·옛 P2). P3·P4 는 격자와 재료표가 같음.
- **작도 한쪽만 그려진 값(C14, 기초 중간 층 B11·B12, 좌측 스터럽 윗면)은 좌우 대칭 가정** — 재료표 개수와 어긋나면(P4 B11 30 대 32) 작도를 채택하고 계획된 FAIL 등록 후 설계자 확인.
- **`hooks_up` 처럼 점 좌표로 대상을 지정하는 목록은 점이 바뀌면 조용히 깨진다**(P3 G4 코핑 밖 점 56개) — 앱 확인 C9 가 `hooks_up ⊂ c14.points` 를 검사함.
- **교각 이름을 바꾸면 모델 객체 이름 접두어(`P1-FT`)가 바뀐다** — `params.display.hidden_ids` 도 같이 바꿔야 함(안 바꾸면 일괄 실행이 마지막 단계에서 오류로 중단).
- **고정(F) 항목(pier_id·display)을 도면에서 읽기로 바꾸면 앱 확인 C7 이 사유 기록을 요구** — 반영 화면이 `edit_log.json` 에 자동 기록하도록 수정함(이전 서버 프로세스는 재시작해야 반영).
- **실제 사용 데이터에 의존하는 시험은 깨진다**: 사용자가 `운곡교p2`·`운곡교P4` 를 삭제·재작성하면서 시험 6종이 깨짐 → 시험은 G5 까지 승인된 복사본(`testlib.verified_pier`)·휴지통 복사본(`testlib.pier_dir`)을 쓴다. Windows 의 글로빙은 대소문자를 구분하지 않아 `운곡교p2_*` 가 `운곡교P2_*` 를 집는다 — 이름 접두어를 대소문자 구분으로 다시 거른다.
- **Flask 는 디버그 꺼짐이면 템플릿을 캐시한다** — 화면·라우트를 고친 뒤에는 사용자가 앱을 껐다 켜야 새 메뉴·버튼이 보인다(2026-10-20 '도면에서 읽기가 없다'의 원인).