dwgrev — handoff (현재 상태·불변 핵심)
목적 2개. ①거래처 도면이 수정본의 수정본으로 파생돼도 현장별·타입별·위치별 판넬사이즈/마감재넘버 수정이력을 끊기지 않게 잇는다. ②변경표시 없이 오는 새 도면을 구도면 빨강 겹쳐보기로 눈으로 잡는다 — 캐드 밖 브라우저에서 가볍게. 갱신일: 2026-08-28(금) KST · 계획 = devplan
/dwgrev.html· 함정·규칙 =rules.md
NOW (2026-08-31 KST) — 7차: XCLIP(크롭) 반영
페페 = 「기존 도면에서 평면도를 크롭해서 쓰는데, 변환하면 크롭이 적용 안 되고 전체 평면도를 다 가져온다」.
🔴GeomDump 에 클립 처리가 한 줄도 없었다. 설계 문서엔 「XCLIP/뷰포트 클립 안쪽」이 게이트 조건으로
적혀 있었는데 구현이 통째로 빠져 있었다(문서에 있다고 구현된 게 아니다).
→ Clip.cs 신설 + Add(Ctx,Prim) 한 곳에서만 적용(모든 프리미티브가 지나는 문).
선은 교차점을 계산해 실제로 잘라내고, 원·호·해치는 bbox 로 버릴지만 판단한다. 중첩 블록은 push/pop.
경계를 못 읽거나 면적이 0 이면 안 자른다(잘못 자르는 것보다 낫다). 통계 clipped/clippedOut 을 JSON 에 실었다.
실물 실측(금호 구미) = 신 XCLIP 20개·잘라낸 102,971 · 구 17개·76,577 → 전체의 약 44%가 화면에 없는데 딸려오던 것. 화면도 표 셀을 넘치던 평면도가 행 안에 딱 들어간다.
검증 = _test/test_clip.py 12/12 PASS(정답을 아는 합성 도면 — 클립 ON 18선 / OFF 42선,
좌표 전부 클립 상자 안, 끝점이 경계에 붙음). 배포 = 로컬 5곳 + 사무실PC MD5 d99e9104… 전부 일치.
✅페페 확인 완료 — 「잘되고 있음」(2026-08-31). 크롭 건 종료.
남은 미실측 = 회전·축척된 블록 안의 크롭, 다각형 크롭(코드는 처리한다). 이상하면 판정은 clippedOut 수치 한 줄.
대조용 세션 2개를 미니PC에 남겼다 — 20260831_111030_gumi(크롭 전) · 20260831_123749(크롭 후).
이력 (2026-08-31 KST) — 6차: org 보정(버그) + 2점 수동 정렬
페페 = 「담당자가 도면 전체를 옮겨서 겹치기 거리차가 있다(금호 구미). 구도면 포인트를 신도면 동일 포인트로 옮기는 기능. 이동 중엔 신도면만 + 포인트만 보이면 된다」.
🔴절반은 우리 버그였다 — 구/신은 각자 자기 org 를 뺀 덤프 좌표인데 draw()·primKey() 가
그걸 무시하고 겹쳐 그리고 있었다. 금호 구미 실측 구/신 org 가 Y로 44,945mm 차이 →
화면이 45m 어긋나고 지문도 전부 어긋난다(차이 = 100%). 구/신 org 가 같던 지금까지만 우연히 맞았다.
→ shiftOf(d) 한 곳으로 보정(draw SX/SY·격자 컬링·primKey·buildDiff 차이 원 좌표 4곳이 같은 값을 쓴다).
A/B 실측 = 구도면 고유분 보정 0.706 vs 상쇄 1.000.
2점 정렬(페페 결정 = 정점 스냅) — 조작판 「정렬(2점)」 / 「해제」.
① 구도면만 보이고 기준점을 찍는다(겹쳐보기 상태면 구도면이 「차이분만」 그려져 기준점을 못 찍는다)
② 신도면만 + ①의 점만 남은 화면에서 대응점을 찍는다 → 그 차이만큼 구도면 전체 평행이동(회전·축척 없음).
스냅 = 화면 12px 안의 실제 정점에 붙고 노란 네모로 미리 보여준다. 정렬 후 buildDiff() 재계산.
정렬값은 세션별 localStorage 로 기억(새로고침해도 남는다). ESC·버튼 재클릭 = 취소.
검증 = GPU 헤드풀 _e2e_align.py 26/26 PASS(실물 금호 세션) + 기존 _e2e_dark 20/20 ·
_e2e_ui2 32/32 · _e2e_stage6 24/24 재통과. viewer.html 73,334B · 사무실PC 배포 MD5 일치.
⏳페페 = 실도면에서 정렬이 눈으로 맞는지 확인(정렬 정확성은 사람이 찍는 두 점에 달렸다).
이력 (2026-08-29 KST) — 5차: 검은 바탕 토글 (바탕=보색 초록기 · 구=마젠타 · 신=회색)
페페 질문 = 「흰 바탕은 투명개념인가? 검은 바탕 토글 + 그때 구도면 노랑」. 답 = 그렇다, 투명이다.
draw() 가 clearRect 로 지우므로 캔버스에 칠해진 바탕이 없고 보이는 흰색은 CSS --bg 하나다.
→ 조작판 「글자·해치」 줄에 검은 바탕 체크박스 추가. body.dark 가 CSS 변수만 뒤집는다
(--bg:#0a140e 초록기 도는 검정 · 구 --old:#ff00ff 순마젠타 · 신 --new:#989898 회색 / --new-solo:#ffffff). 선 색은 render() 가 매번 CSS 에서 읽으므로 다시 그리기만 하면 된다 — 렌더 코드 무변경.
--fg·--panel 은 안 건드려 조작판은 밝은 채로 읽힌다. localStorage dwgrev.dark 로 기억(DWGCV 마다 새 창이라 필요).
- 🔑신도면은 순백이 아니라 회색(#808080)이다 — 페페 지적 「흰 선이 강렬해서 마젠타를 가린다」. 원인은 휘도다: 순백 1.000 vs 마젠타 0.285 로 신도면이 3.5배 밝고, 화면 잉크의 89%(82,949px vs 10,111px)가 그 최대 밝기라 구도면이 묻힌다.
회색으로 낮추니 마젠타가 신도면보다 밝아져 순서가 뒤집힌다.
이어 페페 「회색 더 밝게 + 마젠타도 강조」 → 회색은 #989898(0.216→0.314)로 확정(페페 OK).
🔴마젠타를 「휘도」로 올린 것은 오답이었다 — 적·청이 이미 255라 초록을 섞을 수밖에 없고, 그러면 #ff70ff 처럼 연분홍(파스텔) 이 된다. 휘도 숫자는 0.401 로 올라가는데 눈에는 흐려진다(페페 즉시 교정).
🔑색은 순마젠타 #ff00ff 고정(최대 채도).
🔴글로우(drop-shadow)도 폐기 — 선 둘레 halo 가 글자와 가는 선까지 흐리게 만든다(페페 실사용, 1턴 만에 반려). 도면 뷰어에서 선명도는 강조보다 우선한다.
✅채택 = 바탕에 마젠타의 보색(초록)을 아주 옅게 — #0f0f0f → #0a140e(R10 G20 B14). 보색 바탕 위에서 마젠타는 동시대비로 더 튀고, 회색·흰 선은 무채색이라 거의 영향이 없다. 선을 한 픽셀도 안 건드리므로 흐려질 여지가 없다. 휘도는 검정과 거의 같게 유지(0.0048 → 0.0060).
단 신도면이 혼자 보일 때(「신도면만」·구도면 꺼짐)는 --new-solo = 순백 — 그땐 신도면이 주인공이다. solo = iso === "new" || !oldOn.
검토한 대안 = 구도면 2px 굵게 · 더 밝은 분홍 #ff5ce0(0.343, 흰색 1.0 에는 여전히 밀려 근본해결 아님) → 페페가 회색안 택.
- 🔴노랑은 폐기 — 검은 바탕에서 흰 선에 묻혀 안 보인다(페페 즉시 지적). 마젠타(캐드 색 6) 로 확정. 검은 바탕에서 흰색과 색상거리가 가장 멀다.
- 검증 = _e2e_dark.py 20/20 PASS(GPU 헤드풀 · 겹쳐보기 신 rgb(152,152,152) 82,949px · 구 rgb(255,0,255) 10,111px · 바탕 rgb(10,20,14) 초록기 확인 · 필터 none(흐림 없음) · 「신도면만」/구도면 꺼짐이면 신 순백 복귀) + _e2e_ui2.py 32/32 재통과 + 기존 _e2e_ui2.py 32/32 재통과
- 겹쳐보기 규칙은 그대로 — 마젠타는 「구도면에만 있는 선」 1,094개에만 (4차 결정 유지)
- 파일 = viewer.html 62,849B(줄끝이 CRLF→LF 로 정규화돼 원본보다 작다 · 내용 손실 0, 서빙 크기 일치 확인) · pfs·캐드 무변경
이력 (2026-08-29 KST) — 4차: 겹친 자리를 완전 흑백으로
🔑블렌드 모드로는 못 한다 — 그리는 대상을 줄여야 한다. 페페 지적 = 「겹치면 암적색이라 구도면 빨강과 구분이 안 된다」.
multiply 도 darken 도 실패했다. 두 캔버스 다 선이 반투명 안티에일리어싱이라 겹친 픽셀이
(1-a)·바탕 + a·혼합 으로 계산돼 (87,43,43) 같은 암적색이 어떤 블렌드를 써도 남는다(실측).
→ 겹쳐보기에서는 구도면 중 「신도면에 없는 프리미티브」만 빨강으로 그린다(draw(..., onlyDiff) + sides.old.only Uint8Array,
표시는 buildDiff 의 지문 대조가 이미 계산해둔 것을 그대로 쓴다). 겹친 자리는 신도면 검정 한 겹뿐이라 완전 흑백.
「구도면만」 모드에서는 iso !== "" 이므로 전부 그린다.
- 실측 = 암적색 픽셀 53.9% → 2.2% · 빨강 16,287 / 무채색 122,197 · 구도면 표시분 1,094 / 422,212
- 부수효과로 겹쳐보기 래스터가 가벼워졌다(화면 31,508 → 16,000개)
- 검증 = _e2e_ui2.py 32/32 PASS(GPU 헤드풀)
이력 (2026-08-29 KST) — 실사용 3차 피드백 5건 반영 완료
검증 = GPU 헤드풀 _e2e_ui2.py 30/30 PASS(21 → 30 확장) + _e2e_stage6.py 24/24 재통과 + 테스트 DB 0행 정리.
| 지적 | 처리 |
|---|---|
| 겹쳐 보면 글자가 포개져 못 읽는다 — 구도면만 따로 볼 수단이 없다 | 구도면 토글 아래에 「신도면만」·「구도면만」 2개. 누르면 그쪽만, 다시 누르면 겹쳐보기 복귀. 구도면만을 켜면 구도면 표시를 자동으로 켠다(안 그러면 빈 화면) |
| 가운데 휠 클릭 이동 | button===1 도 팬. preventDefault + auxclick 로 크롬 자동스크롤 차단. 휠클릭은 이동 전용(클릭 판정에서 뺀다 — 안 빼면 차이 원이 눌린다) |
| 줌 배율 1.5배 | ZOOM_STEP 1.15 → 1.225(+15% 의 1.5배 = +22.5%) |
| 배율 직접 입력 | 조작판에 배율 입력창. Enter 로 적용하고 화면 한가운데를 붙잡은 채 배율만 바꾼다. 래스터할 때마다 현재 배율로 값이 따라온다 |
| 차이 원이 확대하면 너무 커져 센터를 못 본다 | 화면 반지름을 [9, 40]px 로 묶는다 — 축소해도 안 사라지고 확대해도 안 커진다. + 십자 중심 표시 추가, 개수 배지는 원 밖(우상단)으로 빼 중심을 안 가린다 |
이력 (2026-08-29 00:xx KST) — 실사용 2차 피드백 6건 반영 완료
페페 실사용 「잘 뜬다」 후 지적 5건 + 질문 1건. 전부 viewer.html 안에서 끝냈다(pfs·캐드 무변경).
검증 = GPU 헤드풀 _e2e_ui2.py 21/21 PASS + 기존 _e2e_stage6.py 24/24 재통과 + 테스트 DB 0행 정리.
| # | 지적 | 처리 |
|---|---|---|
| 1 | 구/신 차이가 눈에 안 띈다 → 차이에 원 + 클릭 확대 | 차이 원 신규 — 지문 32비트 접어 개수 빼기(421k×2, 450ms) → 격자 flood-fill 군집. 실도면 36곳(작은 것 59곳은 잡음컷, 숨긴 수를 화면에 적는다). 원 가운데 14px 클릭 → 320ms ease-out 으로 그 자리를 화면 중앙에 확대 |
| 2 | 0.134배 아래에서 치수가 사라진다 | TEXT_MIN_PX 8 → 4. 배율 0.10 에서 10,005개가 새로 보인다 |
| 3 | 줌이 버벅인다 | 미리보기 변환을 팬·줌 공통으로 — 「마지막 래스터 → 현재 view」를 CSS translate+scale 로 즉시 붙이고, 손 멈추면 한 번만 래스터 |
| 4 | 구/신 색 구분이 약하다 | 구=순빨강 #ff1414 · 신=진남 #10233f + 신 캔버스에 mix-blend-mode: multiply → 겹친 자리는 어두워지고 한쪽에만 있는 선만 원색으로 남는다 |
| 5 | 투명도 게이지 → 토글 · 현장 선택 → 모달 | 구도면 0↔100 토글 버튼. 현장은 pfs 견적서 field_popup.html 양식 모달(제목 「현장 선택」·현장명/상차일 범위 검색·표·취소/선택). 500건 클라이언트 필터라 pfs 추가 호출 0 |
| 6 | 「원거리 잔재」가 뭐냐 | 답변 = 아래 「원거리 잔재」 절 |
🔴이번에 물린 것 2개(둘 다 rules.md 에 기록) = ①좌표계 2개(월드 vs 덤프 org)를 섞어 차이 원 클릭 확대가 도면 밖으로 날아가 백지 ②조작판이 길어져 #work 패널이 조작판을 덮어 체크박스를 못 눌렀다.
원거리 잔재 — 이게 뭔가
GeomDump 가 객체 밀집영역(퍼센타일 도면범위) 을 잡고, 그 밖에 있는 것을 Far 로 표시한다. 지우지 않고 분리만 하며 뷰어 체크박스(기본 OFF)로 켠다. 차이 계산에서도 뺀다.
- 왜 있나 = 거래처가 옛 도면을 재사용하면서 도면 저 멀리에 시험 선·옛 표제란·엉뚱한 좌표의 객체를 남겨둔다. 그대로 그리면 그것들 때문에 전체보기 배율이 수십 배 나빠져 정작 볼 도면이 점만 해진다.
- 왜 안 지우나 = 「도면 범위 밖」은 숨김이 아니다. 멀쩡한 객체가 거기 있을 수 있어 버리면 정보 손실이다. 그래서 분리 + 기본 OFF 다(확정결정, DiffEngine.RobustExtent 재사용).
- 실측 = 페페 도면에서 520개. 켜면 화면이 확 넓어지는 게 정상이다. 「전체보기를 눌렀는데 도면이 너무 작다」 싶으면 이게 켜져 있는지 본다.
이력 (2026-08-28) — 7단계 완료 후 실사용 1차에서 백지 버그 1건 잡아 고침
- 🔴페페 실사용 = 브라우저 백지. 원인 = 크롬 GPU 캔버스가 한 Path 에 서브패스 수만을 넘으면
stroke()를 조용히 버린다(에러 0, 통계는 「689,104개 · 626ms」로 멀쩡). 헤드리스 Playwright 는 소프트웨어 래스터라 우리 검증(성능·비전·E2E 24/24)이 전부 통과했다. - 고침 =
viewer.htmlSTROKE_BATCH = 5000— 5,000개마다 끊어stroke(). 전체보기 래스터 774ms → 194ms 로 오히려 빨라졌다. - 재검증(GPU 헤드풀 크롬) = 전체보기 구 126,077 / 신 126,018 불투명픽셀 · 20배확대 3,800 · 라벨영역 캔버스 41,624 · E2E 24/24 재통과 · 테스트 DB 3테이블 0행 정리.
- 서버에
Cache-Control: no-store(html/js/css) 추가 — 고쳐도 옛 사본 물고 「안 고쳐졌다」 오진 방지. - 상세·판정법 =
rules.md맨 위 🔴🔴 항목. 캔버스 화면은 앞으로headless=False+getImageData픽셀 세기로 검증한다.
이력 (2026-08-28) — 단계 1·2 완료, push 승인 대기
- 계획 확정 절차 완료 = 그릴링 11문 합의(페페) → Fable 계획 초안 → 코덱스(ChatGPT 앱) 상의 → devplan
/dwgrev.html+ 허브 카드 등록(2026-08-28 active dwgrev최근 보드 노출 확인). - 단계 1 산출물 =
C:\dev\pfs\migrations\patch48_dwg_rev_history.sql작성 완료(신규 3테이블, 기존 무변경). - ✅단계 1 완료 (26.8.28) — patch48 을 테스트·운영 양쪽에 적용, ALL PASS. 경로 =
pfs/scripts/ssm_run.py(로컬 py → EC2 SSM 실행). 검증 = ①op_material_spec_cellDDL 바이트 동일(테스트 643 / 운영 644, 적용 전후 무변화) ②uk_cell5키 그대로 ③신규 3테이블 생성(rev 12칼럼 / cell_hist 13 / region 11) ④조인키 타입 일치 ⑤쓰기 왕복 INSERT→DELETE PASS. 테스트 DB = EC2 로컬 MySQL 8.4.8127.0.0.1:3307· 운영 = RDS MySQL 8.4.8. - ⏳막힌 곳(해소됨 — 기록만):
1. ~~테스트 RDS 소멸~~ → 페페가 EC2 로 옮긴 것(코디가 26.7 비용검토에서 제안한 ②안 = RDS-test 삭제 → EC2 안 MySQL 직접 설치, 월 $21 절감). 접속 =
/home/ubuntu/env/pfs-test.env→127.0.0.1:3307MySQL 8.4.8. 운영 env =pfs-live.env(RDS 3306). DB 작업은 로컬 직결이 아니라pfs/scripts/ssm_run.py로 EC2 에서 돌린다. ⚠남은 잔재 =pfs/scripts/codi_phase*.py5개가 옛 테스트 RDS 호스트를 기본값으로 물고 있다 → 그대로 돌리면getaddrinfo failed. 쓸 일 생기면 먼저 갱신. 2. 운영 RDS 터널(tools/rdstunnel13306)은 반쯤 끊겨 있다 — TCP 는 붙는데 쿼리 무응답(2013). 이번엔 EC2 경유로 우회해 끝냈으나, 엑셀 파워쿼리(미니PC 원부자재발주서)는 이 터널을 쓰므로 그쪽이 필요해지면 재기동해야 한다. - ✅단계 2 코드 완료 (26.8.28) — pfs API 3종 + 로컬 뷰어 서버.
- pfs
material_spec_{dao,service,controller}.py+web/__init__.py— 313줄 추가, 삭제 0. 커밋8565232(master, push 미실행). 라우트 =/api/orderpaper/material-spec/dwgrev/<field_no>/{rev,region,hist}. 인증은 기존_relay_authorized()3갈래 재사용(새 토큰 안 만듦). C:\dev\dwgrev\server.py— pythonw 8801, 표준라이브러리만. 정적서빙 + pfs 프록시(토큰은.secrets\pfs_agent.env에서 읽어 HTML 에 안 들어간다).- 검증 = SQL 실행 12항목 ALL PASS(테스트 DB) + 서버 selftest 18항목 ALL PASS(경로탈출 차단·라우팅 실왕복 포함).
핵심 리스크였던
INSERT..SELECT MAX(rev_no)+1 FROM 같은테이블이 실제로 돌고 rev_no 1,2 자동증가 확인. - ✅단계 2 배포 완료 (26.8.28, 페페 채팅 승인 = push + 운영배포)
- 테스트 = master
8565232push → 자동배포 → E2E 8항목 ALL PASS(dwgrev/_e2e_check.py test) - 운영 =
C:\dev\pfs_deploy_wt워크트리에서 cherry-pick4221ee6→ push → E2E 8항목 ALL PASS(_e2e_check.py prod). diff 390줄(313 코드 + SQL 77) = 내 커밋과 동일 확인 후 push — merge 안 했다(분기 master 279 / deploy-live 371). 401 → 200 전환으로 배포 반영을 확인했다. - 사이트맵 재빌드 =
그룹 8 · 페이지 57 · 미반영 0 · 공용API 5(주황 없음). - 프로브 잔재 정리 = 테스트·운영 양쪽
field_no=999999·spec_no=-9990행, 3테이블 전체 행수도 0(실사용 전). - ✅단계 3 완료 (26.8.28) — 캐드 덤프 명령
DWGCV/ 헤드리스DWGCV_BENCH. - 신규
GeomDump.cs(가시성 게이트 + 블록변환 재귀 + 좌표추출) ·ViewerWriter.cs(JSON·gzip) ·DumpCommands.cs(명령·브라우저 기동) DwgReader.Warmup/ModelSpaceIds만internal로 가시성 확장(동작 변경 0줄).DiffEngine.Run()3단 매칭은 안 쓴다.- 실측(30MB · 75,894 엔티티 × 2) = 9.35초(목표 15초) · 작업세트 +469MB · 프리미티브 446,315 · JSON 33MB → gzip 5.3MB(6.2배)
- 배포 = DLL 3곳 + bin, MD5
A8A6104C4곳 일치 ·Unblock-File완료(MOTW) - 🔴게이트 진단이 잡은 실버그 4건 — 첫 실행의 「미지원 28,344」가 전부 화면에 있는데 안 그려진 것이었다.
타입별 집계(
Stats.NoDraw)를 심어 정체를 밝혔다 → 28,344 → 410(98.6% 회복). ①BlockReference=15438— 폭발 결과 안 중첩 블록이Emit으로 직행해 무시됐다 →Walk로 보낸다 ②Spline:explode실패=11423→ 파라미터 등분 샘플링 ③Ellipse=540→ 폭발이 아니라 파라미터 그대로(뷰어ellipse()) ④Solid=533→ 정점 직접, 순서 0,1,3,2(0123 으로 이으면 나비넥타이가 된다) 남은 410 = 진짜 미지원(DBPoint=320 Ole2Frame=60 Wipeout=24 Region=6) — 버리지 않고 집계로 노출한다. - ✅단계 4 완료 (26.8.28) — 브라우저 뷰어
C:\dev\dwgrev\viewer.html(Canvas 2D, 단일 파일·의존성 0). - 구/신 캔버스 2장을 겹치고 위 캔버스
opacity만 슬라이더로(재래스터 불필요 → 즉각 반응) · 구=빨강 / 신=검정 - world-grid 컬링(64×64 격자, 프리미티브 bbox 를
Float64Array로 사전계산) · 팬은 CSS transform(놓을 때만 재래스터) · 줌은 160ms 디바운스 - 8px 미만 글자 숨김 · 축소 시 해치는 첫 루프만 · 원거리 잔재 토글(기본 OFF) · 글자/해치 토글 · 전체보기 · 구도면만
- 실측(446,315 프리미티브 × 2 로드) = 전체보기 1회 래스터 119ms · 확대 상태 382개만 그려 4ms(컬링이 실제로 먹는다)
- 육안 확인 = 도곽 격자·가구 배치 정상, 확대 시 치수값(60·65·250·310)이 회전까지 정확, 경첩 블록 원/호·해치·문자 정상.
스크린샷 =
C:\dev\dwgrev\_shots\dwgrev-{fit,zoom2}.png - 🔴잡은 함정 = Windows 는
SO_REUSEADDR이 같은 포트 하이재킹을 허용한다(리눅스와 다르다). 옛pythonw서버 2개가 8801 을 동시에 물고 있어 새 코드를 고쳐도 옛 응답(8,503 bytes)이 계속 나왔다. →server.py에already_running()가드 +allow_reuse_address = False추가. 재기동 전 반드시 기존 프로세스 종료. - ✅단계 5 절반 완료 — 좌표 자가검증 PASS (26.8.28). 신규 명령
DWGCV_VERIFY. 🔑비전보다 정밀한 시험을 찾았다 — 내 좌표를 AutoCAD 자신이 계산한GeometricExtents(WCS) 와 맞댄다. 그 값은 캐드가 삽입점·회전·축척·법선을 이미 적용해 돌려주므로, 내parentToWcs × BlockTransform누적이 틀리면 바로 어긋난다. 수치로 떨어져서 눈보다 확실하다. - 결과 = 대조 73,178 · 일치 72,864 · 불일치 314(0.43%) · 2,964ms → PASS
- 남은 314건은 전부 작은 객체 + 근사(스플라인 64등분 샘플링·폴리라인 bulge 직선화). 최악도 절대 0.2mm.
- 블록 변환은 정확하다(중첩 깊이 10·비균일축척·회전 포함).
- 배포 = DLL 4곳 MD5
61557A3F일치 · Unblock-File 완료 - 🔴검증 로직에서 잡은 함정 2건(좌표 버그가 아니라 판정 기준 문제였다 — 첫 실행 4,246건 FAIL 이 전부 가짜였다) ① 호를 원 전체로 bbox 잡으면 안 된다 — 반지름 큰 얕은 호에서 실제보다 수백 배 커진다(최악 29,008%). → 양 끝점 + 구간에 실제로 들어오는 사분점만 센다. ② 판정은 크기 일치가 아니라 「중심 어긋남」으로 — 크기는 텍스트 폭 추정·해치 근사·bulge 직선화 때문에 원래 안 맞는다. 목적은 "블록 변환이 맞는가" 하나뿐이므로 중심만 본다.
- ✅단계 5 완료 (26.8.28) — 비전 대조 PASS.
dwgrev/_vision_check.py(acad→viewer2단). - 캡처 = AutoCAD 별도 인스턴스(DispatchEx) 를 띄워 도면 창을
PrintWindow(PW_RENDERFULLCONTENT)· 뷰어는 Playwright. 같은 월드 사각형으로 맞추는 법 = ZoomWindow 를 시킨 뒤 캐드가 실제로 보여준 영역을 되읽는다(VIEWCTR/VIEWSIZE+ 캡처 픽셀 종횡비). 요청한 사각형이 아니라 캐드가 확정한 사각형을 뷰어에 먹인다. - 판정(cody-vision · gemini-3.6-flash · 3쌍) — 전체보기 「배치 일치, 통째로 빠진 덩어리 없음」 · 중간확대 「선·문자 상호 일치, 빠진 요소 없음」 · 상세확대 「구조·치수·문자 일치」.
- 유일한 차이 2개는 둘 다 의도된 근사다 — ①해치 무늬 채움(덤프는 경계 loop 만 담는다) ②전체보기에서 8px 미만 글자 숨김(
TEXT_MIN_PX). 게이트 보정 필요 없음. - 캡처 =
dwgrev/_vision/{cad,viewer,pair}_{fit,mid,zoom}.png - 게이트 전/후 = 도달 75,894 → 레이어꺼짐 −8 · 미지원 −410(DBPoint 320·Ole2Frame 60·Wipeout 24·Region 6) → 통과 75,476 → 프리미티브 446,315
- ✅acad.exe 루프백 실측 OK (26.8.28) — 신규 명령
DWGCV_NET.TCP 127.0.0.1:8801 = OK·HTTP 200 · 346 bytes2회 재현. 단 dev-17 이 방화벽 차단대역에서 루프백·사설·Tailscale 을 뺀 뒤(66 CIDR)의 상태다. 「막힐까」 걱정은 끝났다. - ✅단계 6 완료 (26.8.28) — 뷰어에 현장·rev·라벨영역·이력·질문리스트.
viewer.html17KB → 41KB(단일 파일·의존성 0 유지). - 현장 피커 =
/api/orderpaper/material-spec/fields(500건) · 타입/K·F/위치 트리 =/spec-read/<field_no>— 둘 다 이미_AGENT_PATHS에 있어 pfs 를 한 줄도 안 건드렸다 - rev 등록 = 덤프 헤더의 파일 지문(
fileSize/fileTime/fileHashMD5)을 그대로 실어POST .../rev(헤더 3필드는ViewerWriter에 신규 추가) - 라벨 영역 = 드래그 → 타입/구분/위치 3선택 →
POST .../region(월드좌표로 저장 → 다음 rev 에서 원점이 달라도 그대로 뜬다) · 별도 캔버스라 도면 재래스터 없이 갱신 - 이력 = ①영역 안 문자 raw-diff 칩(구만/신만) → 부위 골라 기록 ②부위별 수동 old→new. 둘 다
POST .../hist - 질문리스트(qahelper 형) = localStorage + 마크다운 복사. 좌표는 화면에도 마크다운에도 한 글자도 안 들어간다(확정결정 10) — E2E 가 정규식으로 고정
- 검증 = 브라우저 E2E 24/24 PASS (
dwgrev/_e2e_stage6.py, 실제 pfs 테스트 왕복). 끝나고 테스트 DB 3테이블 전부 0행으로 정리 - ✅단계 7 완료 (26.8.28) — 배포 끝. 7단계 전부 끝났다.
- 런처 등록 =
pcadtools-plugin/PluginApp.csTOOLS에DWGCV「겹쳐보기」 1줄 → 팔레트에 4번째 버튼 [V] 뜨는 것 캡처로 확인(dwgrev/_vision/launcher.png) - DLL 5곳 동기화 —
DwgCompare.dllMD5A329520C·PcadTools.dll0A26DCE3· Unblock-File 완료 🔴그중%LOCALAPPDATA%\pcadtools\가 진짜 런타임인데 8월 6일자로 3주 묵어 있었다(rules.md 맨 위) - 설치 zip 재생성 =
pcadtools-plugin/pcadtools-installer-2026-08-28.zip(159,497B · 10개 항목).AcadTread.dll은 dev-17 이3231B13E(198,144B)로 맞춘 뒤 묶었다. README 에 [V] 겹쳐보기 사용법 문단 추가.
🤝 병렬 세션 조율 (26.8.28, 페페 지시)
같은 시각 다른 세션(dev-17)이 pfs 를 병렬 수정 중이다(op_material_pick = AutoCAD 픽 브리지). 현재 충돌 0 — 서로 다른 함수·다른 테이블.
dev-17 에 통지한 3가지 = ①op_material_spec_cell 의 uk_cell 불가침 ②같은 3파일은 파일 끝 append 방식으로(중간 삽입 금지 → git 충돌 회피) ③deploy-live merge 절대 금지(master 279 / deploy-live 371 분기 → pfs_deploy_wt 워크트리에서 cherry-pick 만, push 전 diff --stat 줄수 대조).
내 남은 작업은 pfs 를 더 안 건드린다(캐드 플러그인 + 로컬 뷰어 쪽).
확정 결정 13개 (그릴링 합의 — 뒤집지 마라)
| # | 항목 | 결정 |
|---|---|---|
| 1 | 비교도구 위치 | 캐드 밖 브라우저 뷰어. 플러그인 경량화는 추후과제 기록만. 단 실행 진입은 캐드 명령에서도 |
| 2 | 이력 원장 | pfs DB 단일. DWG 에는 아무것도 안 쓴다(읽기 전용) |
| 3 | 이력 입력 | 도면 자동추출 → 사람 확인 주경로 + 수동입력 병행 |
| 4 | 이력 저장 | 신규 op_material_spec_cell_hist + op_material_spec_rev. 기존 uk_cell 무손상 |
| 5 | 이력 줄기 | 현장(field_no)이 줄기, 도면은 그 밑 rev 1,2,3… 파일명·해시는 기록만 |
| 6 | DWG 읽기 | accoreconsole + 기존 DwgReader.cs. ODA 안 씀 |
| 7 | 렌더 범위 | 보이는 전부(해치 포함) — 가시성 게이트 통과분만 |
| 8 | 가시성 판정 | 게이트 + cody-vision 비전 대조 루프(페페 명시 지시) |
| 9 | 진입 | 캐드 명령 DWGCV 1개 → 브라우저 자동 오픈. 단독 실행도 가능 |
| 10 | 질문리스트 | qahelper형 변경 체크 + 질문 작성 → 마크다운 복사. 좌표 표기 안 함, 라벨은 타입/K·F/위치 |
| 11 | 위치 라벨 | matcollect 세트 좌표 재사용 + 사각형 드래그 보정, 현장에 저장해 다음 rev 재사용 |
| 12 | 성능 | 30MB×2 덤프 15초 · 팬/줌 60fps · 투명도 슬라이더 |
| 13 | 산출물 위치 | 페페가 HTML 경로를 알 필요 없다 — 백그라운드 |
이번에 안 하는 것 (명시적 보류)
- part_key 자동분류 — matcollect Phase 1 파서 의존. 완성되면 갈아끼운다(재발명 금지)
- 캐드 안 플러그인 경량화(
PaintOverlayFull36.9초·2.9GB) — 겹쳐보기를 밖으로 뺐으니 해소. 코드 안 건드림 - 기하 JSON DB 영구저장 — 15초짜리
DWGCV재실행이 싸다. DB엔 이력 메타만 - 해시 지문 rev 자동연결 — 헤더에 기록만. 사람이 「이 파일은 XX현장」 1회 지정
- 구도면 여러 파일 합본 비교 — dwgcompare 기존 미해결 과제 상속
- ODA File Converter 도입 — 페페 실무 실패 이력. 보유 AutoCAD 2024로 충분
단계 (7)
- ✅DB patch48 — 신규 3테이블. 테스트·운영 적용 완료
- 로컬 뷰어 서버 + pfs API 3종 —
C:\dev\dwgrev\server.py(pythonw·8801) + rev/region/hist - 캐드 덤프
DWGCV← 지금 여기 —dwgcompare-plugin/{GeomDump,ViewerWriter,DumpCommands}.cs신규 - 브라우저 뷰어 —
C:\dev\dwgrev\viewer.htmlCanvas 2D - 비전 검증 루프 — AutoCAD 캡처 ↔ 뷰어 캡처 cody-vision 대조 → 게이트 보정
- 현장·rev·라벨 + 이력 기록
- 배포 — DLL 3곳 동기화 + Unblock-File, pfs patch48
구조
[AutoCAD] DWGCV → 가시성 게이트 → 기하 JSON
↓
[로컬 뷰어 서버] pythonw 8801 — 정적 서빙 + pfs API 프록시(토큰은 .secrets)
↓
[브라우저] Canvas 2D 겹쳐보기 · 사각형 라벨 · qahelper 질문리스트
↓
[pfs MySQL] op_material_spec_rev / _cell_hist / _location_region
로컬 서버를 끼운 이유 = 자족형 HTML 을 file:// 로 열면 pfs 호출이 교차출처라 CORS 와일드카드가 필요하고 API 토큰이 HTML 에 평문으로 박힌다. 서버를 한 겹 끼우면 브라우저→localhost 는 동일출처(CORS 0), localhost→pfs 는 서버 간이라 무관, 토큰은 C:\dev\.secrets\.env 에서 서버가 읽어 HTML 에 안 들어간다.