29.8 KB · 수정 2026-08-31 12:54
목차

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건 잡아 고침

이력 (2026-08-28) — 단계 1·2 완료, push 승인 대기

🤝 병렬 세션 조율 (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 경로를 알 필요 없다 — 백그라운드

이번에 안 하는 것 (명시적 보류)

단계 (7)

  1. ✅DB patch48 — 신규 3테이블. 테스트·운영 적용 완료
  2. 로컬 뷰어 서버 + pfs API 3종 — C:\dev\dwgrev\server.py(pythonw·8801) + rev/region/hist
  3. 캐드 덤프 DWGCV ← 지금 여기 — dwgcompare-plugin/{GeomDump,ViewerWriter,DumpCommands}.cs 신규
  4. 브라우저 뷰어 — C:\dev\dwgrev\viewer.html Canvas 2D
  5. 비전 검증 루프 — AutoCAD 캡처 ↔ 뷰어 캡처 cody-vision 대조 → 게이트 보정
  6. 현장·rev·라벨 + 이력 기록
  7. 배포 — 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 에 안 들어간다.