Commit Graph
67 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 fbbedc7230 fix(700줄분리): 진입 파일에서 분리 전 이름 전부 재노출
laptop-sub 가 쓴 AST 대조법(분리 전 커밋의 모듈 정의 이름 ↔ 지금 모듈 `dir()`)을
내 4개에도 적용해 누락 26개를 찾아 되살림.

- `B03_FileInput_Router.py` 누락 17 → 0 (헬퍼 13 + 청크 엔드포인트 4)
- `B03_FileInput_Router_Temp.py` 누락 5 → 0 (`_total_chunks` + 청크 엔드포인트 4)
- `B07_DesignDetail_Router_Support.py` 누락 3 → 0 (`_BASIN_MAX_DISTANCE_M` ·
  `_clip_segment` · `_geometry_lines`)
- `config/config_system.py` 누락 1 → 0 (`_SCS_SOIL` — `import *` 가 밑줄 이름을 안 실음)

검증: 네 파일 모두 **정의 이름 누락 0**(28·19·36·246개 대조), 라우트 수 유지
(B03 7 · temp 9+attach 1 · B07 7), ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:53:37 +09:00
eomsangdonandClaude Opus 5 0b16a1240c fix(B07): 분리한 유역도 이름을 진입 파일에서 재노출
`_Support_Basin.py` 로 옮긴 뒤 `CONTOUR_FILE` · `STREAM_FILE` · `_basins_crs` ·
`_geojson_payload` · `_geojson_features` · `_too_far_from_route` 가 진입 파일에서
사라져, 옛 이름을 참조하던 다른 워크트리의 테스트가 ImportError·AttributeError 로
깨졌음(laptop-sub 보고 2건). 프로덕션 호출부는 새 모듈을 직접 봐 동작에는 영향 없었음.

`as` 형태로 다시 내보내 분리 전 이름 14개가 진입 파일에서 모두 보이게 함.

검증: 진입 파일 속성 14개 전부 확인, 라우트 7개 유지, ruff check 통과,
tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:49:06 +09:00
eomsangdonandClaude Opus 5 8be60007e7 refactor(B07): 도면 조립 지원 700줄 초과 분리 — 유역도·입출력
808줄 한 파일을 셋으로 나눔 (동작 불변, 순수 이동).
- `B07_DesignDetail_Router_Support.py` 532줄 — 도면 목록·장 배치·확정 저장·재계산
- `B07_DesignDetail_Router_Support_Basin.py` 279줄 — 유역도 배경(GeoJSON·좌표 환산·도곽 클리핑)
- `B07_DesignDetail_Router_Support_Io.py` 88줄 — 원본 JSON·매니페스트 입출력, 측점 대응표

떼어낸 `watershed_source`·`clip_line_to_box` 는 본체에서 `as` 로 다시 내보내
호출부(`B07_DesignDetail_Router.py`)의 import 경로가 그대로임.

검증: 라우트 7개 그대로, 공용 브라우저 `design-drawings` 200 · `frame-template` 200,
ruff check 통과, tmp/tests 378 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 10:34:18 +09:00
eomsangdon 0f5b4e4df7 auto: 2026-09-03 18:31 (ESD_LAPTOP) 2026-09-03 18:31:54 +09:00
eomsangdonandClaude Opus 5 4fdbcaf09c fix(B07): 장 확정 수량표 결함 3건 — 측점 구분·계획고·확정 해제
장 단위 확정 흐름을 실측하다 드러난 것들이다(2026-09-03, 용화 검증본 3장).

① **측점 구분 없이 역추출** — `_table_values_from_cells()` 가 도면의 표를 전부 훑어
   마지막 값을 모든 측점에 넣었다(실측: 4개 측점 전부 지반고 837.21, 도면에는
   840.87·840.33·836.45·837.21 로 제대로 그려져 있었음). 표 엔티티 id 가
   `uuid5("{도면id}:qtable")` 이라 측점을 되짚을 수 있어 그 표만 읽는다. 옛 도면은
   id 가 안 맞으므로 종전처럼 전부 훑는 폴백을 남긴다.

② **계획고가 빈 채로 나감** — 장 배치 입력의 원본에는 계획고가 없어 계획고·절토고·성토고
   세 칸이 통째로 비었다(21개 항목 중 지반고 하나만 채워짐). `_quantity_table()` 이
   횡단 설계(`design.design_elevation_m`)도 보게 했다.

③ **확정 해제가 404** — [수정]이 도면 목록을 **설계값 없이** 만들어 장 나눔이 달라졌고,
   방금 확정한 장 id 가 목록에 없어 되돌릴 수 없었다. 목록 조회·확정과 같은 인자를 쓴다.

검증(공용 브라우저·실동작) — 확정 해제 404 → **200**, 표 값 `planned=840.87 fill=0.00` 등
측점마다 다름, manifest 수량표가 측점별로 갈림(220 · 240 · 260 · 264 · 280 각각 4/21 항목
채움 — 나머지 17칸은 사용자가 CAD 에서 채우는 자리). 시험 뒤 확정은 모두 해제해 원상복구.
pytest 383 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:43:35 +09:00
eomsangdonandClaude Opus 5 e29e3cab67 feat(B07): 유역도 정보표 측점 표기도 n+ 0.0 으로 통일
토적도만 납품 표기(`120+ 0.0`)로 바꿔 두어 유역도 정보표는 `No.6` 으로 남아 있었다
(2026-09-03 사용자 지시로 통일). `station_plus_label()` 을 토적도 전용 모듈에서 공용
`_Engine_Cad` 로 올려 두 도면이 같은 함수를 쓴다. 종단도 표·횡단도 제목의 `No.n` 표기는
그대로 둔다 — 이번 지시 범위가 아니다.

검증 — 유역도 정보표 첫 줄 `(1) No.6` → `(1) 6+ 0.0`. pytest 383 passed·17 skipped,
ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:09:14 +09:00
eomsangdonandClaude Opus 5 754e00de19 fix(B07): 장 선택 시 정보 패널을 장 단위로 표기
장(여러 측점을 담은 횡단 도면)에는 측점 단위 지반·계획 값이 없는데도 제목이
「측점 3장 (220~264m)」으로 달려 어느 측점 값인지 오해됐다. 장이면 제목을 「장」으로 바꾸고
「이 장은 측점 여러 개를 담습니다. 지반·계획 정보는 측점 도면에서 봅니다」로 안내한다.

판정은 도면 id(`cross_s…` = 장) 로 한다 — `chainage_m` 은 장에도 첫 측점 값이 실려 와
그것으로는 갈리지 않는다(실측).

검증 — 공용 브라우저 B07: 3장(220~264m) 선택 시 `장 3장 (220~264m) · 잠정 · 이 장은 측점
여러 개를 담습니다…`, 종단도 선택 시 패널 없음(종전과 같음). typecheck·prettier 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 14:06:11 +09:00
eomsangdonandClaude Opus 5 451ed97279 feat(B07): 돌쌓기 해칭 CAD 반영 + 표제란 SVG 서명 벡터 변환
해칭 — 수확기가 `g[clip-path]` 를 통째로 건너뛰어 기슭막이 형태 해칭(돌쌓기·콘크리트·
돌망태·통나무·바자)이 CAD 도면에 하나도 실리지 않았다. 클립을 쓰는 대신 **경계로 잘라서**
싣는다(2026-09-03 사용자 결정). 선분–폴리곤 교차로 안쪽 조각만 남기고, 클립 안 원(통나무
마구리)은 36각 폴리선으로 펴서 같은 방식으로 자른다. 문자는 중심점이 안일 때만 싣는다.

서명 — SVG 로 올라온 자산만 CAD 폴리선으로 바꾼다(2026-09-03 사용자 결정). DXF 로 나가도
선으로 남고 확대해도 깨지지 않는다. `B07_DesignDetail_Engine_Cad_Svg` 신설(path M·L·H·V·
C·Q·S·T·Z 지원, 곡선은 16분할, viewBox 비율 유지·y 뒤집기). PNG·JPG 는 그릴 선이 없으므로
종전대로 ImageEntity, 읽기 실패도 원래 그림을 그대로 둔다.

검증 — `tmp/tests/test_b07_signature_vector.py` 5건 신설(파싱·비율·엔티티 교체·래스터 유지·
손상 SVG), 전체 383 passed·17 skipped. typecheck·prettier·ruff 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:46:54 +09:00
eomsangdonandClaude Opus 5 ce4fcc1f13 feat(B07): 토적도 길이별 자동 축척 + 납품 도면 표기 8행 반영
축척 — 노선 연장에 맞춰 **한 장에 들어가는 가장 큰 그림**을 고른다(2026-09-03 사용자 결정).
후보는 도면 관행 축척(1:500~1:6,000, `DRAWING_SCALE_MASSHAUL_H_CANDIDATES`)이고 A1 작도영역
가용 폭 700mm 를 기준으로 판정한다. 실측 — 40m 노선 1:500 · 용화 1,106m 1:2,000 ·
3,465m 1:5,000. 세로는 종이 1mm=50㎥ 고정(도면끼리 비교하려면 같아야 한다).

표기 8행(사용자 확정) — ① 장비명 무대→종무대 ② 값 표기 `Q= 162.41M3`(등호 뒤 한 칸)
③ 지시선 계단형·balloon 모서리 접점 ④ 평형선·띠 경계현 빨강 ⑤ 측점 `120+ 0.0`
⑥ 표 눈금 신설(정규 빨강·추가 회색) ⑦ 행 이름 3.2mm·자간 벌림 ⑧ M.N 소수 2자리 `0+16.90`.

`MM_H` 상수를 없애고 `mm_h` 를 그리기 함수에 넘긴다 — 축척이 노선마다 달라졌기 때문.

검증 — `tmp/tests/test_b07_masshaul_basin.py` 갱신·신설(축척 3건·표기 8행·지시선·눈금),
전체 378 passed·17 skipped, ruff format 무변경.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:37:05 +09:00
eomsangdon 94883733d5 auto: 2026-09-02 19:08 (EOMSANGDON-HOME) 2026-09-02 19:08:55 +09:00
eomsangdonandClaude Opus 5 dde40101bb fix(B07): 표제란 로고·서명 원천을 계정·회사 자산으로 이관
- 서명: `projects.signature_asset_id` 대신 설계자(`designer_user_id`, 없으면 소유자)
  계정의 `company_assets` SIGNATURE 자산을 따라감 (여러 벌이면 최신 1건)
- 로고: 프로젝트가 고른 것 우선, 없으면 `companies.logo_asset_id` (014) 로 폴백
- `projects.signature_asset_id` 는 컬럼만 남기고 더 읽지 않음 — B01 이 항상 null 저장

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 19:01:56 +09:00
eomsangdonandClaude Opus 5 65d6fd261f fix(B07): 도각 편집 띠 사이드바 이관·깨진 그림에 렌더 루프 정지 해소
- 편집 띠를 CAD 위 절대배치에서 도면 목록 하단 액션 칸으로 옮김 —
  1행 편집 띠, 2행 [도각 편집]·[현재 도면 확정] (리본과 겹쳐 문구·버튼이 찌그러지던 문제)
- `drawImage` 가 'broken' 상태 그림에서 던지던 예외로 렌더 루프가 끊겨
  이후 모든 도면이 백지로 남던 결함 해소 — 못 읽은 그림은 건너뜀
- `ImageEntity` 가 자리표시자(`{{회사로고}}`)를 절대 URL 로 굳히던 저장 경로 수정 —
  원본 주소를 보관해 그대로 돌려주고, 자리표시자는 요청조차 하지 않음
- 버튼 줌(ZOOMIN·ZOOMOUT)이 배율만 바꿔 도면이 화면 밖으로 밀리던 것을
  화면 중심 고정으로 수정

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 18:12:28 +09:00
eomsangdonandClaude Opus 5 2bdad0947b fix(B07): 명령행 토글 기본값 숨김으로 변경
- 상태막대 `명령행` 토글의 초기 상태를 `useState(true)` → `useState(false)` 로 변경
- 진입 시 `--cad-command-height` 가 `0px` 로 잡혀 캔버스 세로 공간 확보
- 토글 동작·명령행 구현 자체는 불변

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 17:48:10 +09:00
eomsangdon 1250cabe71 fix(B07): CAD 그림 그리기의 y 뒤집기 누락 수정 — 도각 로고·서명이 화면에 안 보이던 원인
- screenCanvas.drawController.drawImage 가 worldToTarget 결과를 그대로 써서 그림이
  거울상 자리(캔버스 밖)에 놓였음. 선·글자처럼 canvasSize.y - y 로 뒤집고 회전 부호도 반대로
- vitest 추가: 그림 중심의 뒤집힌 y 자리, 회전 부호·원상 복구
- 검증(공용 브라우저, 종단면도): 로고 상자 비배경 픽셀 0 → 14708(파랑 11661),
  서명 상자 498 → 2810(빨강 852). 뷰는 187% 로 정확히 복귀
2026-09-02 12:24:26 +09:00
eomsangdonandClaude Opus 5 00d30587b3 feat(B01,B07): 담당자 회사 구성원 지정 + 로고·서명 회사 공유 자산 모달
- B01 프로젝트 수정: 과업책임자·분야별책임자·설계자를 회사 구성원 목록(select)에서
  지정. 회사 로고·설계자 서명 칸은 누르면 등록 목록 모달(선택·삭제·신규 추가 업로드).
- 백엔드: `company_assets`(013) CRUD 라우트 신설 — 목록·업로드(Form+File, 2MB·확장자
  검사)·수정·소프트 삭제·파일 서빙. `_scope_company` 로 SYSTEM_ADMIN 만 타 회사 지정,
  `_check_project_refs` 로 담당자·자산이 같은 회사·같은 종류인지 검사(400).
- projects 조회·수정에 pm/field_lead/designer/logo_asset/signature_asset 5칸 합류.
  중복 3곳을 `_project_rows` 로 묶어 700줄 제한 유지.
- B07 표제란: 로고·서명 출처를 `companies.logo_path`/`users.signature_path` 에서
  프로젝트가 고른 `company_assets` 로 전환(삭제된 자산은 그림째 제외).
- 검증: pytest 143 통과(신규 4), tsc·ruff·prettier 통과. 공용 브라우저 사용자 경로
  (업로드→선택→저장→재열기) 및 종단도 API 표제란 글자 4건·그림 2건 수치 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 12:02:49 +09:00
eomsangdonandClaude Opus 5 313a7dde43 feat(B01,B07): 설계일자 사용자 지정 입력 + 도면번호 자동 부여
사용자 확정(2026-09-02): 설계일자는 확정일 자동이 아니라 **사용자가 넣는다**. 도면번호는
Claude 판단으로 목록 순번을 쓴다.

설계일자
- `013_company_assets.sql` 로 `projects.design_date DATE` 추가(같은 마이그레이션에
  로고·서명 공유 테이블 자리도 함께 만들었음 — 배선은 다음 커밋).
- B01 프로젝트 수정 모달에 날짜 칸. 공용 `createInputField` 가 `type="date"` 를 받게 넓힘.
- 표제란에는 도면 표기 관행대로 `2026. 09. 02.` 꼴로 적는다.

도면번호
- 단건 조회는 목록 순서를 모른다 — 알려면 횡단 장 계획을 다시 계산해야 하고 도면을 열
  때마다 그러면 비싸다. **목록이 매긴 번호를 manifest 에 적어 두고** 단건 조회가 읽는다.
  목록은 화면 진입 때 늘 먼저 뜨므로 성립하고, 목록이 바뀌면 다음 조회에서 다시 적힌다.
- `add_title_fields()` 신설 — 도면을 읽는 도중에 아는 값을 문맥에 얹는 창구.

검증: `pytest tmp/tests/ -q` 139 passed / 0 failed, 루트 `tsc --noEmit` 통과.
실사용자 경로(5174, wdw) — 대시보드 [수정] 모달 라벨 10개(설계일자 칸 추가 확인) →
`2026-09-02` 입력 → [확인] → 종단면도 표제란에 `2026. 09. 02.`.
도면번호는 목록 13건 기준 종단 1 · 유역도 6 · 용지도 13 이 도각에 그대로 실림
(표지는 도각을 두르지 않아 번호 칸이 없다).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 11:38:50 +09:00
eomsangdonandClaude Opus 5 56aed2f2aa feat(B01,B07): 표지 연도·기번·사업량을 사람이 넣는 자리 신설
표제란에 남아 있던 빈칸 중 연도·기번과 사업량은 프로그램이 지어낼 수 없는 값이라
입력 자리를 만든다. 값의 출처를 정하지 않고 받을 칸만 세운다.

DB (`012_title_block_inputs.sql`, ADD COLUMN·NULL 허용)
- `projects.project_number` — 표지 연도·기번(실무문서 폴더명 관행:
  `2024년 간선임도(기번3-울진.대흥)`)
- `projects.work_amount` — 표지 사업량. 단위·표기가 사업 종류마다 달라 자유 문자열.

B01 프로젝트 수정
- 수정 모달에 시행청·연도기번·사업량 3칸 추가. 011 로 만든 시행청도 여기서 처음 입력
  가능해짐(그전에는 스크립트로만 넣었음).
- 목록·단건 조회 4곳과 UPDATE 에 세 칸을 함께 실음. USER 권한은 다른 칸과 같이 잠금.

B07
- `_title_block_fields` 가 연도기번·사업량도 실어 보냄. 비어 있으면 종전대로 빈칸.

검증: `pytest tmp/tests/ -q` 139 passed / 0 failed(표지 값 1건 신규), 루트 `tsc --noEmit`
통과. 실사용자 경로 실측(5174) — 대시보드 wdw 행 [수정] 클릭 → 모달 라벨 9개에 새 3칸
확인 → 연도기번·사업량 입력 → [확인] 저장 → 표지 도면 Text 7개 **빈칸 0 · `{{` 잔존 0**,
화면에도 연도기번·`wdw 설계도`·위치·사업량·시행청이 모두 그려짐.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:28:25 +09:00
eomsangdonandClaude Opus 5 07926e3867 fix(B07): 빈 도면의 "작도 영역을 넘습니다" 경고 제거
빈 도면 7종이 도각만 두르는데도 경고를 냈음 — `build_blank_drawing()` 이 콘텐츠
bbox 로 작도 영역(`_A1_INNER`, 770×520mm)을 그대로 넘겼고, 검사 기준은 여백을 뺀
수용 한도(739×499mm)라 담을 내용이 없는데도 넘친 것으로 잡혔음.

- `usable_bbox()` 신설 — 작도 영역 한가운데의 수용 한도 크기 bbox. 중심이 같아
  도각 배치(이동량 0)는 그대로.
- 한도와 **같은** 크기는 넘친 것이 아니므로 비교에 부동소수 여유(1e-6)를 둠.
  한도를 그대로 넘기면 마지막 자리 오차로 경고가 났음.

검증: `pytest tmp/tests/ -q` 138 passed / 0 failed(경고 없음을 잠그는 1건 신규).
백엔드 재시작 뒤 화면 실측(5174, wdw): 빈 도면 7종 전부 200, 새 서버 로그의
"작도 영역을 넘습니다" **0건**.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 10:02:35 +09:00
eomsangdonandClaude Opus 5 63428f2516 feat(B07): 표제란 축척 칸을 도면 축척 상수에서 채움
축척은 지어내는 값이 아니라 이미 정해져 있던 것 — `config_system` 의 도면별 축척
상수를 그대로 표제란에 싣는다. 도각이 `A1 = 1 :` 를 이미 찍으므로 **분모만** 낸다.

- `scale_fields()` 신설 — A3 는 A1 도면을 절반으로 뽑는 종이라 분모가 2배. 가로·세로가
  다른 도면(종단·토적)은 이름표를 붙여 함께 적는다.
- 종단면도 `1000(H) · 200(V)` / 횡단면도 `100` / 유역도 `6000` / 토적도 `2000(H)`.
  A3 는 각각 2배. 담을 내용이 없는 빈 도면·표지는 축척이 없어 빈칸 그대로.

검증: `pytest tmp/tests/ -q` 135 passed / 0 failed(축척 2건 신규 — 상수 유래·A3 2배,
호출부가 넘긴 값이 도각에 실림). 백엔드 재시작 뒤 화면 실측(5174, wdw): 종단
`1000(H) · 200(V)`/`2000(H) · 400(V)`, 횡단 `100`/`200`, 유역도 `6000`/`12000`,
토적도 `2000(H)`/`4000(H)`, 표준도 빈칸.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 09:54:16 +09:00
eomsangdonandClaude Opus 5 a5fba04778 fix(B07): 그림 테두리는 집었을 때만 그림
도각의 로고·서명 자리에 흰 사각형이 남던 것 — `ImageEntity.draw()` 가 폴리곤 네 변을
선택·강조 여부와 무관하게 늘 그렸음. 출력·내보내기가 같은 `draw()` 를 다른
DrawController 로 타므로 화면만의 문제가 아니라 산출물에도 실렸음.

- 도각층만 특례로 두지 않고 `ImageEntity` 전체에서 뺌. 일반 CAD 관행대로 **집었을
  때만**(강조·선택) 테두리를 보임 — 앞으로 붙일 그림(현황사진 등)에도 같은 규칙.
- 자리 표시가 필요하면 도각 편집 화면에서 집으면 보임.

검증: `ImageEntity.test.ts` 신규 2건 — 안 집은 그림은 `drawLine` 0회·`drawImage` 1회,
집은 그림은 4회·1회. `npx vitest run` 89 passed / 0 failed, check-types·build 통과.
화면 실측(5174, wdw 표준도): 두 슬롯 사각형 안 흰 픽셀(RGB>200) **0**,
그림 픽셀은 그대로(로고 56 · 서명 106).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:50:02 +09:00
eomsangdonandClaude Opus 5 b1593bce83 feat(B07): 표제란에 로고·서명 자리 신설 + 시행청·담당자 DB 확장
사용자 지시(2026-09-02) — 로고·서명을 임의로 만들어 표제란에 넣고, 위치·크기는
도각을 고치면 따라오게 하며, 시행청·과업책임자 등은 DB를 넓혀 채울 것.

도각(표제란)
- `00_template_A1.json` 에 그림 자리 2개 추가 — 회사 로고는 용역회사 칸 왼쪽
  (316~348 × 20~36mm), 설계자 서명은 설계 칸 아래(638~680 × 17.5~25.5mm).
  자리·크기가 **템플릿 좌표로만** 정해지므로 도각을 고치면 그대로 따라옴.
- `_fill_placeholders` 가 그림 자리(`imageData`)의 `{{키}}` 도 채움. 값을 못 구하면
  빈 문자열을 남기지 않고 **엔티티째 제거** — 빈 값은 CAD 가 깨진 그림으로 그림.
- `_transform_entity` 가 `points` 배열도 옮김. 그림(로고·서명)과 띠(Hatch)가 이 키를
  쓰는데 여태 변환 대상이 아니라 도각을 옮기면 제자리에 남았음.

DB (`011_title_block.sql`, 전부 ADD COLUMN·NULL 허용)
- `projects` — `client_org`(시행청), `pm_user_id`·`field_lead_user_id`·`designer_user_id`
- `companies.logo_path` · `users.signature_path` — 그림은 파일로 두고 경로만 담음
  (기존 `storage_path`·`input_files` 와 같은 방식)
- FK 는 걸지 않음 — 사유를 파일 머리말에 적음(소프트 삭제·LEFT JOIN·4환경 공유).

배선
- `_title_block_fields` 가 시행청·과업책임자·분야별책임자·설계자(배정 없으면 소유자)와
  로고·서명 data URL 까지 실어 보냄.
- `read_stored_asset()` 신설 — `storage/` 기준 상대 경로의 **파일**을 읽음.
  `resolve_stored_project_path()` 는 폴더를 만드는 프로젝트 루트용이라 파일에 못 씀.

CAD 결함 1건 (이번 작업에서 드러남)
- `screenCanvas.drawController.drawImage` 가 `(width, height)` 를 좌표처럼 변환해
  화면 오프셋과 y 뒤집기가 섞여 들어갔음 — 그림이 제 자리를 벗어나고 비율이 무너짐.
  크기는 배율만 곱하고 자리는 세계 중심으로 잡도록 고침(SVG 컨트롤러와 같은 방식).
- 검증 창구 `__aisloCad.screen(x, y)` 추가 — 세계→화면 좌표. 그림·글자가 제 자리에
  그려졌는지 픽셀로 판정할 때 씀.

검증: `pytest tmp/tests/ -q` 135 passed / 0 failed(그림 자리 3건 신규),
`npx vitest run` 87 passed / 0 failed, `check-types`·`build` 통과.
화면 실측(5174, wdw): 표준도 API Text 24개 `{{` 잔존 0, Image 2개가 도각 좌표
(316,20)-(348,36)·(638,18)-(680,26)에 실림. 캔버스 픽셀 판정 — 두 자리 모두 배경색
외 픽셀이 그려짐(로고 88px·서명 64px), 로고 파랑(20,70,140)이 슬롯 안에서만 검출.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:35:25 +09:00
eomsangdonandClaude Opus 5 23fea9846b fix(B07): 표지도 표제란 값을 받게 함
교차검증에서 표지만 빈칸으로 나갔음 — 문맥값을 합치는 자리가 `frame_entities` 안에
있어, 도각을 두르지 않는 표지 경로(`build_cover_drawing`)가 비껴갔음.

- 합치는 자리를 치환 함수 `_fill_placeholders` 한 곳으로 옮김. 도각을 쓰든 안 쓰든
  `{{키}}` 를 치환하는 모든 경로가 같은 값을 받음 — 앞으로 생길 경로도 자동으로 닿음.
- 표지 docstring 의 "값 공급은 다음 판 몫" 문구 정정.

검증: `tmp/tests/test_b07_title_block_fields.py` 에 표지 케이스 추가(공사명·위치가
표지에 실리고 `{{` 잔존 0). `pytest tmp/tests/ -q` 132 passed / 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:11:56 +09:00
eomsangdonandClaude Opus 5 793e1aad37 fix(B07): 표제란 값 공급 통로 + CAD 회귀 6건 해소
표제란(도각) 값 공급
- `use_title_fields()` 문맥 변수 신설 — 회사 도각 폴더와 같은 방식이라 작도 엔진
  6개의 서명을 고치지 않음. `frame_entities` 가 문맥값 위에 호출부 값(도면명)을 얹음.
- 라우터가 도면 한 건을 그리기 전에 DB 값을 세움: 공사명·위치(projects),
  용역회사(companies), 설계자(users). 못 채운 자리는 빈칸 — 도각 원본의 남의 값이
  도면으로 나가지 않음.
- 미공급 항목과 사유를 `_title_block_fields` 주석에 명시: 시행청·과업책임자·
  분야별책임자는 DB 칸 부재(B02 마이그레이션 대기), 축척·사업량·연도기번은 임의 수치
  금지, 설계일자는 채울 시점 정의 미결.

CAD 회귀 6건 (87건 중 81 passed → 87 passed)
- `window` 부재 5건: `notifyWindow()` 한 곳으로 모으고 `window` 없는 Node 시험에서는
  통지를 건너뜀. `triggerReactUpdate` 가 이미 같은 이유로 시험을 건너뛰던 것과 같은 결.
- `find-closest-entity`: 목의 호가 90°까지만 돌아 클릭점에서 147px 떨어져 있었음 —
  시험 의도(호가 가장 가깝다)대로 3/4바퀴로 되돌림.
- 선색 기대값 4건: 앱 기본 선색이 흰 종이 배경에 맞춰 검정으로 바뀐 뒤 상류 기대값
  `#fff` 가 남아 있었음 — `getActiveLineColor()` 대조로 교체.
- 좌표 NaN: 없어진 `TOOLBAR_WIDTH` 를 시험 보조가 아직 참조 — 입력 컨트롤러가 캔버스
  bounding rect 를 쓰도록 바뀐 현행에 맞춤.
- 호 각도 비교: 같은 각의 음수·2π 표기 차이라 [0, 2π) 정규화 후 대조.

검증: `npx vitest run` 87 passed / 0 failed, `npm run check-types`·`npm run build` 통과,
`pytest tmp/tests/ -q` 131 passed / 0 failed(신규 `test_b07_title_block_fields.py` 4건 포함).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 08:00:39 +09:00
eomsangdonandClaude Opus 5 4cb9b15939 style: 저장소 전체 포맷터 일괄 적용 (prettier·biome·ruff)
파일마다 포맷 폭이 달라(≈80 대 100) 한 줄만 고쳐도 포맷터가 무관한 줄을 대량
재포맷했음. 사용자 지시로 전체를 한 번에 맞춤. 코드 동작 변경 없음 — 포맷만.

- 프론트엔드 `.ts/.css/.html` → 저장소 prettier (`.prettierrc`, printWidth 100)
- `B07_DesignDetail/openwebcad/**` → 자체 biome (tab 들여쓰기·single quote·lineWidth 100).
  `biome format` 만 사용 — `biome lint --write` 는 포맷 아닌 코드 수정까지 하므로 제외
- 파이썬 → `ruff format` (엔진 코드는 이미 정합, resources·scratch 스크립트 24개만 변경)

두 포맷터가 서로 되돌리지 않도록 `.prettierignore` 신규 — openwebcad 와 빌드·산출물
폴더를 prettier 대상에서 뺌. `.prettierrc` 에 `endOfLine: "auto"` 추가 — 기본값 `lf` 가
`core.autocrlf=true` 로 받은 CRLF 파일을 매번 전부 다시 써서 `--list-different` 가
실제 포맷 차이를 가리고 있었음.

검증: `tsc --noEmit` 통과(루트·openwebcad 둘 다), pytest 349 passed / 17 skipped /
0 failed, CAD vitest 87건 중 81 passed / 6 failed(laptop-sub 기준선과 동일, 회귀 없음).
포맷터 재실행 시 prettier·biome 모두 변경 0건.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 07:08:24 +09:00
eomsangdonandClaude Opus 5 946d61f8df feat(B07): GROUP 묶음 JSON 저장·복원, 줌 로그 제거
- `groupId` 를 도면 JSON 에 실어 [저장] 뒤 다시 연 도면에서도 묶음 유지.
  엔티티 13종의 `toJson` 대신 직렬화 한 곳(export/import)에서 붙이고,
  복원은 순번이 아니라 id 로 — `compact` 가 null 을 걷어 index 가 어긋남.
- `setScreenScale` 의 매 호출 `console.log` 제거 — 줌·팬마다 콘솔을 덮었음.
- 검증: `group-id-roundtrip.test.ts` 신규, `npx vitest run` 87건 중 81 passed,
  실패 6건은 작업 전과 동일한 기존 결함. `npm run build` 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 06:33:19 +09:00
eomsangdon 31e27798d6 merge(B07): desktop-sub 캐드 편집 유실 방지·도면 잠금 통합 (PR #8)
편집 유실 방지와 확정 도면 잠금.
2026-09-01 20:13:59 +09:00
eomsangdon e4d2a3c75f Merge remote-tracking branch 'origin/sub_desktop_1' into sub_desktop_1 2026-09-01 19:05:28 +09:00
eomsangdonandClaude Fable 5 17e14a25ed fix(B07): 편집이 사라지지 않게 하고 확정한 도면을 잠근다
사용자 편의성 점검 15건 중 반영 확정분 12건.

편집 손실 방지
- CAD에 미저장 표시(drawingDirty)를 두고, 도면을 바꾸기 전에 묻는다.
  전에는 그은 선이 경고 없이 사라졌다(실측: 캔버스 서명이 원본과 동일).
- 도면 전환을 겹쳐 눌러도 늦게 온 응답이 화면을 덮지 않게 가드를 뒀다.
- 도면층 삭제·객체 이동을 되돌리기에 실어 Ctrl+Z로 돌아오게 했다.

확정한 도면은 읽기 전용
- runCommand 한 곳에서 보는 명령만 통과시킨다(허용 명시 방식).
- 도면을 실을 때 앞 도면의 그리기 도구를 내린다 — 켜 둔 도구가 확정본에도
  계속 그렸다.
- 확정 버튼 자리를 [현재 도면 확정] / [수정]으로 가른다. 확정 해제는 [수정]
  한 곳뿐 — 되돌리기·색 고르기로 확정이 풀리던 경로를 없앴다.

그 밖
- 잠금 도면층(도각·등고선·계류)은 마우스가 스쳐도 하이라이트하지 않는다.
- 준비 중 도면 7종도 빈 도각으로 열린다(눌리지 않는 회색 버튼 제거).
- 도면층 행이 잘리지 않게 패널을 300px로 넓히고 이름을 줄여 담는다.
- 기본 그리기 색을 검정으로 — 흰 종이에 흰 선이라 안 보였다.
- 색 고르는 동안 변경 통지가 연발하지 않는다.
- 수량표는 앞 단계 산출물이라 도면에서 고치지 못하게 막고 안내한다.
- 자동백업 칸을 도면별로 나눈다 — 되살리면 다른 도면에 붙었다.
- 횡단 장 id를 시작 측점 기준으로 바꿔 구간이 달라져도 옛 확정이 안 붙는다.
- 회사 도각 저장 전에 좌표를 검사하고, 기본 도각으로 되돌리기를 연다.
- 횡단 파일이 없어도 나머지 도면 목록은 남는다.
- 불러오는 중·실패를 화면에 표시한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 19:05:22 +09:00
eomsangdonandClaude Opus 5 9fb453cfca fix(B07): 유역도 배경은 사업지 좌표계로, 세부유역은 저장 당시 좌표계로 되돌린다
좌표계 기록 이전 저장본을 되돌리는 폴백을 넣으면서 변환을 하나로 묶었다. 그래서 도엽
배경(등고선·세류선)까지 저장본 좌표계로 되돌아가, 라벨과 실좌표계가 갈린 사업지에서
배경이 1,000km 밖에 놓였다. 도곽이 그 거리까지 벌어지면 유역도는 사실상 빈 화면이 된다
(다른 PC 보고: 배수유역도 그림 자체가 없음).

변환을 둘로 나눈다.
- 배경·노선: `context.crs`(사업지 .prj 좌표계) — 노선이 그 좌표계에 있다.
- 세부유역: 그 파일을 쓸 때 쓴 좌표계 — 옛 저장본은 노선 CSV의 EPSG 라벨로 쓰였고,
  같은 라벨로 되돌려야 원래 미터 좌표가 나온다(왕복이 상쇄된다).

되찾을 근거조차 없을 때(노선 파일 없음 등)를 위해 안전장치를 뒀다. 노선에서 50km 넘게
떨어진 유역은 빼고 경고를 남긴다 — 도면 전체를 잃는 대신 배경·노선은 그린다.
폴백에 쓰는 route_geometry import 는 함수 안으로 내렸다(순환 import 여지 제거).

검증: tmp/tests 44 passed(신규 test_watershed_source_crs.py 2건).
c1bb453f 유역도 API 재확인 — 엔티티 1,597개(등고선 1028·계류 460·배수유역 20·도각 82·
유역 제원 4·노선 1·표제 2)로 수정 전과 같다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 18:43:31 +09:00
eomsangdonandClaude Opus 5 0a1ebcfbb5 fix(B04): 도엽 서피스를 라이다와 같은 원점에 놓고, 유역 저장본에 좌표계를 남긴다
① 3D 서피스 겹쳐보기 어긋남 — `write_glb()`는 넘겨받은 상자의 중심을 원점으로 삼는데
모델마다 자기 상자를 넘겨 도엽 서피스와 라이다 지표면이 다른 원점에 섰다
(2f940d8a 실측: 수평 7.85m·높이 1.28m). 도엽 서피스가 라이다 포인트 상자
(`structured.npz`, 화면 `setReferenceBounds`와 같은 값)를 화면 원점으로 쓰게 했다.
표고 격자 상자(`bounds`)는 절취 범위 그대로 두고 `scene_bounds`를 npz에 따로 남긴다 —
등고선 API도 이 값을 먼저 써서 등고선이 메시 위에 얹힌다. 라이다 없는 사업지는 기준이
자기뿐이라 종전대로 자기 상자를 쓴다.

② 세부유역 저장본 좌표계 — `04_detailed_basins.geojson`에 변환에 쓴 좌표계를
`crs_input`으로 남기고, B07 유역도가 그 값으로 되돌린다. 기록이 없는 옛 저장본은
노선 CSV의 EPSG 라벨로 쓰였으므로 그 라벨로 되돌린다(경고 로그 + 재확정 안내).

검증: tmp/tests 42 passed (신규 test_scene_origin_and_basin_crs.py 5건).
패치 코드로 도엽 서피스를 다시 만들어 GLB 정점 상자를 대조 —
sheet [-426.58, -68.95, -386.41]~[411.42, 71.52, 379.59],
csf [-172.12, -44.06, -199.69]~[184.11, 3.22, 165.72] 로 같은 원점.
수정 전 sheet는 [-419.5, -70.24, -383.0]~[418.5, 70.24, 383.0] (자기 중심)였다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 17:52:27 +09:00
eomsangdonandClaude Opus 5 b50559d799 fix(B04): 노선 CSV의 EPSG 라벨 대신 사업지 실좌표계로 화면 좌표를 만든다
상세 배수유역 폴리곤이 지도에 안 그려졌다. 노선 CSV `crs_epsg` 열(라벨 5179)을
좌표계로 써서, 사업지 좌표계(.prj EPSG:5176)로 계산된 격자 산출물을 잘못 역투영한
탓이다 — 유역이 lon 119.79 / lat 23.08(대만 남쪽 바다)로 나가 화면 밖이었다.

`load_design_route()`는 노선을 .prj 좌표계로 재투영하며 `crs_input`만 갱신하고
`epsg` 라벨은 CSV 값 그대로 둔다(2026-08-31 확정). 그 라벨을 좌표계로 쓰던 자리를
모두 `crs_input`(= .prj 좌표계)으로 바꿨다.

- common_util_drainage_context: DrainageContext.epsg(int) → crs(str, pyproj 입력).
  상세 배수유역·관 지점 응답 좌표가 노선 위로 돌아온다.
- B07_DesignDetail_Router_Support: context.crs 그대로 사용.
- SheetSurface.build_sheet_surface_from_route: load_design_route로 노선을 읽어
  도엽 서피스를 라이다 지표면과 같은 좌표계에 만든다(기존엔 5179 격자로 만들어져
  라이다 DTM과 다른 프레임에 놓였다). LAS 없는 WF1 경로는 라벨·좌표가 한 벌이라 그대로.
- Router_Inflow._resolve_epsg: 격자 산출물과 같은 .prj 좌표계로 역투영.

검증: tmp/tests/test_drainage_crs.py 신규(용화 샘플 회귀) + tmp/tests 전체 37 passed.
build_detail 실행 결과 유역 11개 폴리곤이 lon 129.0947~129.0988 / lat 36.8107~36.8127
(노선 위)로 나온다 — 수정 전 같은 좌표는 119.7872 / 23.0817이었다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 17:39:19 +09:00
eomsangdonandClaude Opus 5 3c0e65d6cd feat(B07): 캐드에서 도각을 고치고 [완료] 한 번에 반영하는 편집 모드를 붙인다
프로그램 기본 도각(resources/template_2dDrawing)은 읽기 전용으로 두고, 고친 도각은
회사 도각(storage/{회사}/templates)으로 저장한다. 이후 그리는 도면이 그것을 쓴다.

- Engine_Template: 회사 도각 우선 로더(ContextVar로 요청마다 회사 폴더 지정),
  캐시 키에 mtime을 넣어 저장 즉시 반영(템플릿 수정에 백엔드 재시작이 필요 없어짐),
  frame_template_document()/save_company_template() 신설.
- Router: GET/PUT /api/projects/{id}/frame-template. 도각은 실치수 1:1로 오가므로
  좌표 역변환이 없다.
- UI_FrameEdit(신규): 「도각 편집」 버튼·배너·[완료]/[취소]. 완료 시 도면 캐시를 버리고
  보던 도면을 다시 싣는다. 편집 중 변경 알림이 도면 확정을 풀지 않게 막았다.

확정한 도면은 저장본을 그대로 쓰므로 옛 도각을 유지하고, 확정을 풀면 새 도각으로
다시 그려진다(2026-09-01 사용자 확정).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 14:51:46 +09:00
eomsangdonandClaude Opus 5 64d944eb0f fix(B07): 도각을 배경으로 굳혀 새 선이 잠금 도면층에 떨어지지 않게 한다
- state.setActiveLayerId(): 잠금 도면층(도각·원지반) 요청은 잠금 아닌 첫 층으로
  돌린다. 브리지·자동백업 복구·JSON 가져오기·도면층 패널·리본이 모두 이 함수를
  지나므로 가드를 한 곳에 뒀다. 잠금 아닌 층이 없으면 요청값 유지.
- RibbonWidgets: 「현재 도면층」 목록에서 잠금층을 뺀다(현재 층이 잠금이면 유지).
- Cover 엔진: 표지에 잠금 아닌 「주기」 도면층을 실어 덧그릴 자리를 만든다.
  표지는 엔티티가 전부 도각(잠금)이라 가드만으로는 그릴 곳이 없었다.
- state.resetUndoBaseline(): 도면 적재를 되돌리기 바닥으로 세운다. 첫 Ctrl+Z가
  도면을 싣기 전 빈 상태로 되돌아가 화면이 백지가 되던 결함을 닫는다.
- debug-hook: 검증용 snap() 창구 추가. state.test.ts 3건 신규.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 13:58:08 +09:00
eomsangdonandClaude Opus 5 44fbe480df feat(B07): 표지를 A1 전면 템플릿으로 만들고 도면 목록에 붙인다
표지는 DRAWING_GROUPS에 kind 없이 라벨만 있어 화면에서 열 수 없었다. 실무 설계도면
원본(울진 소광리, A3) 1쪽의 잉크 bbox를 pymupdf로 mm 실측해 A1로 2배 환산했다.
원본에 도각이 없다 — 좌우 테두리 없이 상·하 굵은 가로선 두 줄뿐이라, 사용자가 고른
"도각 없는 전면 디자인"이 실무 그대로였다.

- resources/template_2dDrawing/00_template_cover.json 신설. 엔티티 14개(재단 표식
  Point 4·띠 Hatch 3·글자 Text 7), bbox가 정확히 A1(840x594). 좌표계는 00_template_A1
  과 같아 도각 도면과 나란히 놓인다.
- 굵은 띠는 solid Hatch로 냈다. lineWidth는 캔버스 화면 픽셀이라(screenCanvas
  .drawController.ts:261) 확대해도 두꺼워지지 않아 실치수를 못 낸다.
- 글자 크기는 잉크 높이가 아니라 폭으로 잡았다. 원본은 장평이 좁은 CAD 글꼴이라
  높이를 그대로 옮기면 위치값이 종이 밖으로 44mm 넘치고 라벨과 겹친다.
- B07_DesignDetail_Engine_Cad_Cover.py 신설. frame_entities()를 쓰지 않는다 —
  _transform_entity()가 Hatch의 points 배열을 못 옮기고, 표지는 A1 실치수 고정이라
  애초에 변환이 필요 없다. 공용 함수는 건드리지 않았다.
- kind "cover" 배선: Schema Literal, Api_Fetch, UI_Page DRAWING_GROUPS,
  Router_Support 목록·빌드 분기·확정 캐시 매핑.

값(공사명·위치·사업량·시행청)은 아직 빈칸이다 — 메타 배선은 다음 판.

검증: tmp/tests/test_cover_template.py 6건 통과(A1 치수·Hatch 두께·잠금 레이어·
치환·lru_cache 오염 없음). 공용 브라우저에서 표지를 열어 엔티티 14개 전부
b08-frame, Ctrl+A 선택 0 확인.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 19:49:47 +09:00
eomsangdonandClaude Opus 5 46ac54257e fix(B07): 수량 산출표 패널이 명령 입력칸을 덮던 것을 올린다
패널이 상태막대 바로 위에 떠 있어 명령행과 겹쳤고, 명령 입력칸을 클릭으로
누를 수 없었다. bottom에 --cad-command-height를 더해 명령행 위로 띄운다.
명령행을 접으면 이 값이 0이라 예전처럼 상태막대 위에 붙는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:48:08 +09:00
eomsangdonandClaude Opus 5 9bf6de9b10 fix(B07): 잠금 도면층을 집던 경로 다섯 곳을 마저 닫는다
앞선 감사가 불완전했다. 같은 부류로 도각·원지반을 건드리던 곳이 더 있었다.

- select-tool.helpers pickEntityAt: 클릭 선택 후보에 잠금 객체가 섞여
  도각을 물면 아무 일도 안 일어나는 죽은 클릭이 됐다.
- eraser-tool: 자르기의 교차점 계산이 도각 선을 절단 경계로 썼다.
- property-tools OVERKILL: 선택이 없으면 전체가 대상이라 도각까지 지웠다.
- text-tools 찾기/바꾸기: 도각 표제란 글자를 바꿔 버렸다.
- selection-tools 유형/유사 선택: 잠금 객체까지 세어 토스트 개수가 틀렸다.

전부 getPickableEntities()로 바꾼다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:48:08 +09:00
eomsangdonandClaude Opus 5 008b0ede14 fix(B07): 유역도에서 노선을 살리고 방위표·정보표 배치와 절취면을 정리한다
노선이 유역 해칭 아래에 깔려 보이지 않았다. polyline_entity에 width를 더해
노선만 굵게(3) 그리고, 그리기 순서를 해칭·정보표 다음으로 옮긴다. bbox
계산에는 그대로 포함시켜 오른쪽 칸 위치는 흔들리지 않는다.

오른쪽 칸을 방위표(맨 위) → 유역 정보표(아래로 쌓기) 순으로 바꾸고 방위표를
14mm에서 26mm로 키운다. 도면 오른쪽 위 구석에 따로 놓던 옛 배치는 걷어낸다.

등고선·세류선 절취가 경계 바깥 점을 하나씩 물고 나와 외곽이 들쭉날쭉했다.
Liang-Barsky 선분 절취로 바꿔 끝점이 경계 위에 정확히 놓이게 하고, 그만큼
두었던 물림 여유(_CLIP_SLACK 14mm)를 없앤다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:21:38 +09:00
eomsangdonandClaude Opus 5 6a2c5d9f7b feat(B07): 기본 도면층 이름을 국문으로 바꾸고 방위표 템플릿을 정리한다
도면층 17종을 프로젝트가 이미 쓰는 용어로 맞춘다 — 원지반·계획선·구조물·
암 경계선·수량 산출표·도각·등고선·계류·노선·배수유역·유역 제원·표제·
그래프 격자·측점표·유토곡선·그래프 축·운반 구간. CAD 앱의 'Default'와
'New layer'도 기본·새 도면층으로 바꾼다. layerId는 그대로다.

방위표 템플릿(00_template_compass.json)에 원본 DXF의 표제란 글자 4개가
도면 반대편에 남아 있었다. 이 때문에 템플릿 bbox가 183x262mm로 부풀어,
긴 변을 14mm에 맞추는 compass_entities()가 방위표를 1.1x1.8mm로 줄여
놓고 있었다. 글자를 지우니 bbox 20.5x32.9mm, 방위표가 8.7x14mm로 나온다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 15:07:14 +09:00
eomsangdon 8ae61af7dd auto: 2026-08-31 14:38 (ESD_LAPTOP) 2026-08-31 14:38:53 +09:00
eomsangdonandClaude Opus 5 ee065091c1 fix(B07): 도각 같은 잠금 도면층을 설계 조작이 집지 않게 한다
잠금 필터가 공용 함수 없이 호출부마다 들어가 있어 넣은 곳은 막히고 안 넣은
곳은 샜다. Ctrl+A는 도각 103개·원지반 16개까지 전부 선택했고, 선택 삭제에는
잠금 검사가 없어 Ctrl+A → Delete 한 번에 도각이 사라졌다. 자르기와 트림·모따기
계열, 커서 강조도 도각을 집었다.

state.ts에 getPickableEntities()(잠금 도면층 제외)를 두고 집기 경로가 그걸
쓴다. 선택은 setSelectedEntityIds() 한 곳에서 막아, 앞으로 생길 선택 경로도
자동으로 잠금 객체를 담지 못한다.

화면맞춤(zoomToFitScreen)도 같은 목록에 맞춘다 — 도각까지 넣으면 A1 한 장
전체가 잡혀 화면의 40%가 여백으로 갔다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 14:38:48 +09:00
eomsangdonandClaude Opus 5 fb0b49bf60 fix(B07): 명령행 토글이 좌측 속성 패널 높이를 흔들지 않게 한다
.cad-inspector의 bottom이 --cad-command-height를 포함해서, 상태막대의
[명령행] 토글이 좌측 팔레트 높이까지 함께 바꿨다. 좌측 패널이 상위이므로
상태막대까지 내려 명령행 표시 여부와 무관하게 만든다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 10:35:13 +09:00
eomsangdonandClaude Opus 5 8ab10967cf fix(B07): 선택 그립이 선보다 아래에 찍히던 것을 바로잡는다
화면 좌표는 아래에서 위로 재는 규약인데(getCanvasPoint·drawLineScreen), 화면 사각형
채우기만 캔버스 y로 옮길 때 높이를 빼지 않았다. 그래서 아래 변에 놓아야 할 사각형이
높이만큼 아래로 내려가, 선택 그립이 선보다 8px 밑에 찍혔다. 화면 픽셀 단위 어긋남이라
줌을 해도 간격이 그대로였다.

커서 옆 입력칸은 같은 함수를 쓰므로 자리가 유지되도록 높이만큼 내려 잡았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 21:27:53 +09:00
eomsangdonandClaude Opus 5 b4a653e682 fix(B07): 우클릭 뒤에도 선택 도구가 살아 있게 하고, 커서 옆 글자를 줄인다
선택 아이콘을 누르고 우클릭하면 커서 옆 안내가 사라졌다. 우클릭은 ENTER이고,
선택 도구는 ENTER로 최종 상태(SELECTION_COMPLETED, type: final)에 들어가 멈춘다.
그 끝남은 move·copy·rotate가 선택을 자식으로 불러 쓸 때 onDone으로 필요하므로 두고,
선택이 그 자체로 활성 도구일 때만 새로 세운다.

커서를 따라다니는 글자가 18px이라 컸다. 쓰이지 않던 상수
CANVAS_INPUT_FIELD_FONT_SIZE를 12로 낮춰 두 곳이 함께 쓰게 했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 21:23:24 +09:00
eomsangdonandClaude Opus 5 8666bfdcfa revert(B07): 저지연 캔버스를 되돌린다 — 일부 스냅이 안 걸렸다
desynchronized+alpha:false로 합성 큐를 건너뛰게 했더니 일부 스냅이 동작하지 않는다는
지적. 커서 체감보다 스냅 정확도가 먼저다. 화면 캔버스를 원래 getContext('2d')로 되돌린다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 21:20:26 +09:00
eomsangdonandClaude Opus 5 9c1cdf8f4d perf(B07): 도면을 미리 받아 두고, 커서 지연과 토스트 이질감을 걷는다
도면을 열 때마다 0.8~1.0초가 걸렸다. 서버가 요청마다 도면을 새로 조립하고(0.3~0.6초),
구조물 모듈이 공유 캐시를 건너뛰고 종횡단 상세를 매번 다시 받았기 때문이다(0.2초).
진입 후 첫 장을 띄우고 나서 목록 전체를 배경에서 받아 캐시에 담고, 구조물 모듈은
B05·B06과 같은 Section_Store를 쓴다. 클릭에서 렌더까지 55~65ms로 줄었다.

CAD 알림은 react-toastify라 프로젝트 공용 토스트와 모양이 달랐다. 호출부 30여 곳을
그대로 두려고 vite alias로 라이브러리 자리에 다리를 끼워, 부모가 있으면 메시지로 넘겨
부모 토스트로 띄운다. 백업 되살리기처럼 누르는 안내를 위해 showToast에 onClick을 더했다.

커서가 미묘하게 끌리던 것은 프레임이 느려서가 아니라(16.7ms, 60fps) 합성 파이프라인
때문이다. 화면 캔버스를 desynchronized+alpha:false로 만들어 그 큐를 건너뛴다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 21:05:23 +09:00
eomsangdonandClaude Opus 5 e0760de3b8 fix(B07): 구조물이 깎은 설계선을 그리고, 한 변을 한 선으로 만든다
구조물을 세워도 설계선이 원래대로 지나갔다. 정본(design_line)에 담긴 건 트림 전
원본이고, 구조물이 깎아 내는 부분은 B06이 그릴 때 계산하기 때문이다. 수확기가
appendCrossDesignOverlay를 구조물 layout의 designTrim과 함께 태우고, 그 블록의
서버 설계선은 걷어낸다. 걷어낼 자리를 알도록 서버가 블록 테두리를 함께 내려준다.

곧은 사면 하나가 CAD에서 선 열댓 개였던 것은 B06이 설계선 점 사이마다 line 하나를
만들기 때문이다. 수확한 뒤 끝점이 맞물리는 선분을 잇고 공선점을 지운다(RDP).
측점 20m 18조각 → 폴리라인 3개(선분 7).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 17:42:59 +09:00
eomsangdonandClaude Opus 5 35b70dd4c5 fix(B07): 도각 안쪽을 채우고, 수량표를 한 줄로 세운다
여백이 많다는 지적. 도각 안쪽 작도영역(770x594)에서 5%를 떼고 배치해 사방에
70mm 가까이가 비었다. 2%로 줄이니 같은 블록으로 3장이 2장이 된다(16/7).

표가 제각각 높이에 뜨던 것은 블록을 칸 한가운데 놓았기 때문이다. 수량표 높이는
모든 블록이 같으므로 칸 아래 변에 맞춰 놓으면 표가 한 줄로 선다. 단면 높이 차이는
위쪽이 흡수한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 17:22:26 +09:00
eomsangdonandClaude Opus 5 8b284459c6 feat(B07): 횡단도에 구조물을 그리고, 장을 빈틈없이 채운다
구조물(배수관·기슭막이·세월교·BOX암거·물넘이포장)이 횡단도에서 빠져 있었다.
기하 산식이 전부 B06 화면(TS)에 있어 서버가 도형을 만들 수 없었기 때문이다.
파이썬으로 옮겨 적는 대신, 화면에 붙이지 않은 SVG에 B06 그리기 함수를 그대로
불러 그린 뒤 그 도형을 CAD 엔티티로 옮긴다. 산식은 한 벌로 남고, 앞으로 B06에
붙는 구조물·치수·글자는 손대지 않아도 도면에 따라온다.

자리를 맞추려면 실좌표(m)를 종이(mm)로 옮긴 값이 필요하다 — 서버가 측점별
cross_placements로 실어 보낸다.

장 배치는 전체 블록의 최대치로 칸을 통일하던 것을 버리고, 블록 크기를 먼저 재서
가장 많이 담기는 행 수를 고른다. 열폭은 그 열의 최대폭, 행높이는 그 행의 최대높이라
행·열은 그대로 맞는다. 같은 노선에서 4장이 3장이 됐다(12/6/5).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 16:49:57 +09:00
eomsangdonandClaude Opus 5 8c828992f7 fix(B07): 집기 반경을 화면 기준으로 잡고, 누른 채 끄는 선택을 살린다
- 집기 반경이 월드 거리였다. HIGHLIGHT_ENTITY_DISTANCE를 월드 거리와 그대로 비교해
  확대할수록 화면상 반경이 같이 커졌다(1369%에서 15월드 ≈ 205px). helpers/pick-radius로
  확대율을 나눠 쓰고 상수를 화면 픽셀 10으로 낮췄다. 하이라이트·선택·그립·시퀀스 도구가
  같은 반경을 본다.
- 누른 채 끄는 선택이 없었다. 왼쪽 버튼은 mouseup에서만 클릭을 보내 누르고 끌어도 점이
  하나만 찍혔고, 그래서 좌→우(창)·우→좌(교차) 구분이 먹히지 않는 것처럼 보였다.
  선택 도구일 때만 누를 때 첫 점을 보내고, 4px 넘게 끌고 놓으면 그 자리로 사각형을 닫는다.
  다른 도구는 예전처럼 놓을 때 한 점만 받는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:52:25 +09:00
eomsangdonandClaude Opus 5 3a86e02747 fix(B07): 리본 패널 슬라이드아웃이 잘려 캔버스에 가리던 것을 띄운다
패널 줄이 가로 스크롤 상자(overflow-x: auto)라, CSS 규칙상 세로도 auto로 계산되어
슬라이드아웃 메뉴가 리본 높이에서 잘렸다. 캔버스에 가려 보이는 증상이 이것이다.

메뉴를 화면 좌표(position: fixed)로 띄우고 라벨 버튼의 위치에서 자리를 잡는다.
오른쪽·아래로 넘치지 않게 좌표를 물리고 높이를 72vh로 제한했다.
캔버스 위에 뜨게 됐으므로 바깥을 누르면 닫히고, 다른 패널을 열면 먼저 열린 것이 닫힌다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:31:46 +09:00