From 2cbfc1f53fdc95c11ef07ac8c63eea8906c9a89b Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 29 Sep 2026 11:18:20 +0900 Subject: [PATCH 1/3] =?UTF-8?q?chore(plan):=20=EA=B3=84=ED=9A=8D=EC=84=9C?= =?UTF-8?q?=20=EC=9A=94=EC=95=BD=20=E2=80=94=20=EB=81=9D=EB=82=9C=2010~19?= =?UTF-8?q?=EC=9E=A5=EC=9D=84=20docs/raw/plans=20=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EA=B4=80=20=C2=B7=20PLAN.md=20=EB=8A=94=201=EC=9E=A5=20=C2=B7?= =?UTF-8?q?=2020=EC=9E=A5=EB=A7=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01LayTXxoGnEer4vYpTGmYk2 --- ...29_plan_M02구조물집계_계획노선_종단저장.md | 313 ++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 docs/raw/plans/2026-09-29_plan_M02구조물집계_계획노선_종단저장.md diff --git a/docs/raw/plans/2026-09-29_plan_M02구조물집계_계획노선_종단저장.md b/docs/raw/plans/2026-09-29_plan_M02구조물집계_계획노선_종단저장.md new file mode 100644 index 00000000..1bca8fbb --- /dev/null +++ b/docs/raw/plans/2026-09-29_plan_M02구조물집계_계획노선_종단저장.md @@ -0,0 +1,313 @@ +# 지난 계획 10~19장 (2026-09-29 이관 · 모두 검증완료) + +## 10. M02 마스터 템플릿 — 표 양식 · 도면 양식 (진행 · 담당 sub1 10-1 · sub3 10-2 · sub2 10-3 · sub4 10-4·10-5) + +사용자 지시 — 대시보드에 M01 처럼 단추로 들어가는 마스터 템플릿 페이지(`M02_MasterTemplete`) · 공통 양식(좌측 패널 + 메인 · 안쪽 컨테이너 · 단추도 양식) · 양식 두 가지(엑셀처럼 도는 표 · 웹캐드 도면) · 프로그램 기본 양식을 두고 **프로젝트를 만들 때 복사**해 쓰고 그 뒤 사용자가 고침 · 첫 표 양식 = 구조물 집계표. +근거 — `tmp/M02_분석/1_얼개.md` · `2_웹캐드.md` · `3_표양식.md` · `4_구조물집계열.md`. +분석 결론 — 표 부품은 라이브러리 대신 **직접 만듦**(라이선스 · 겉모양 · 서버 재계산) · 수식은 옛 B08 TS 식 풀이기를 되살려 화면 · 서버 Node 한 벌 · 웹캐드는 iframe 과 주고받기를 그대로 다시 씀 · 오솔길엔 구조물 집계표 열 규칙이 없음(실무 표는 손으로 짬) · 지금 프로젝트 층 양식 복사는 없음(회사 층만 있음). + +### 10-0. 사용자 확정 + +- 도면 기본 양식 → 마스터 템플릿 폴더로 옮김(`resources/master_template/drawing/`) +- 양식 층 넷 — **시스템 · 회사 · 개인 · 프로젝트** · 시스템은 저장소(git) · 나머지 셋은 영구저장소(storage) 나무에 +- 프로젝트 만들 때 마스터를 프로젝트 폴더로 복사 · [초기화] = 그때 복사한 양식 · 회사 공식 · 같은 회사 남의 양식 · 다른 프로젝트 양식을 가져와 적용 +- 구조물 집계표는 설계값을 받게 만듦 · 마스터엔 값이 없고 **열마다 「프로젝트에서 무엇이 들어가는지」**를 보임 · 시스템 관리자가 열 더하기 · 지우기 · 바꾸기 +- 엑셀 같은 표 기능은 공용 폴더 아래 따로 둔 하위 폴더에(`ui_template/sheet/`) +- 머리 이름 · 열 짜임은 실무를 따르되 체계적으로(브레인 판단) · 열 묶음마다 일위대가 조합과 연결 +- 커플링밴드는 관 길이 ÷ 1본 길이로 자동 — 1본 길이 **8m**(관 종류마다 칸 · 6m 가 필요하면 사용자가 확인 뒤 바꿈) · 이음 수 = 올림(L ÷ 1본) − 1 +- 프로젝트 첫 복사는 **늘 시스템 양식** · 회사 공식이 있어도 · [회사 양식 적용] 을 누르면 회사 양식을 프로젝트 작업본에 덮어씀 +- **이미 만든 프로젝트들**에도 프로젝트 양식(작업본 + `_initial/`)을 손으로 넣어 사용자가 확인하게 함 +- 표는 50줄씩, 넘치면 다음 쪽 +- 엑셀 내보내기 안 함 · 프로젝트 안쪽 화면에 양식 쓰기는 나중(구조물 정의가 끝난 뒤) · 지금은 마스터 양식 구현에 집중 + +브레인 임의 판정: + +- (사용자 바로잡음) M02 = 시스템 관리자가 새 프로젝트 기본값(시스템 층)만 고치는 곳 · 층 고르기 · 프로젝트 고르기 · 프로젝트 층 단추는 M02 에 두지 않음(프로젝트 쪽 화면은 나중) · 서버 층 길 · 프로젝트 복사는 그대로 +- 집계표 설계값 채우기 = 서버 단독(5장 ③) · 계산 열 = 표 식(5장 ② 한 벌) · 같은 측점에 관이 둘이면 줄을 나눔 +- 프로젝트 표 [저장] = 양식(열 · 식 · 변수) + 손 입력 값만 작업본에 · 설계값과 펼친 열은 저장 안 함(열 때마다 설계에서 채움 · 5장 정본의 자리) +- 표 JSON = `3_표양식.md` 4장 틀 + 열 칸 `설명`(들어갈 것) · `바인딩`(설계 출처) · `일위대가` · `펼침`(마스터 한 칸 → 설계 값마다 열) + +### 10-1. 페이지 틀 + +- [X] 대시보드 「시스템 설정」 칸에 단추 · 길 `m02-master-template` · 로그인 · 권한(SYSTEM_ADMIN) · 서버 `/api/m02`(`main.py` 관리자 전용) — `1_얼개.md` 1장 손댈 곳 10 — 검증완료(ORCA 단추 → 페이지) +- [X] 좌측 패널(양식 목록 · 표/도면 · 새로 · 본떠 만들기 · 지우기) + 메인(편집) · M01 모달을 공용 `ui_template` 으로 올려 함께 씀 — 검증완료(ORCA · 층 넷 · 프로젝트 단추 다섯 붙임 · M01 모달 계산값 같음) +- [X] 서버 저장 — 목록 · 읽기 · 저장(판 · 원자 쓰기 · 409) · 지우기 · M01 `Store` 방식 · 자리 `resources/master_template/` — 검증완료(`test_m02_store.py` · ORCA 새로·저장·다시 열기·지우기·409) +- [X] 병합 이어 붙임 — sub2·sub3·sub4 받음 · 층 길 sub4 것 · 도면 길 sub2 것 · `tsconfig` 에 `M0*` · 검증완료(ORCA 시스템 층 표 1 · 도면 5 · 집계표 master 모드 · CAD·자리표 키 · 프로젝트 층 채움 · 단추 다섯 수로 확인 · 시험 양식 지움) + +- 남김 — 회사 층은 여기서 읽기만 · 비관리자는 시스템 층 단추 잠김 · 프로젝트 층 표 [저장] 은 채워진 문서를 그대로 저장(설계값 줄이 작업본에 들어감 — 브레인 판정 필요) · 랩탑_보조 프로젝트엔 포장 열이 없어 포장 A·줄눈 수는 못 봄(커플링 51칸 · 화면=서버=손계산 일치) + +### 10-2. 표 양식 부품 + +- [X] 격자 — 셀 편집 · 줄/열 더하기 · 지우기 · 여러 층 머리(합치기) · 단위 줄 · 합계 줄 · 키보드 · M01 칸 편집 부품 다시 씀 — `ui_template/sheet/ui_template_sheet.ts` `createSheet` · master(식 · 들어갈 것 · 일위대가 줄 · 손 열) · project(바인딩 · 계산 잠금 · 손 열만 · 전구간 맨 위 · 쪽줄 50) · 틀고정 · 열 너비 끌기 · 칸에 `=` = 그 칸만 식 +- [X] 수식 — 옛 `B08_Quantity_Formula.ts` 되살려 공용 · 열 참조 · 셀 참조 · SUM · INT · ROUND · 순환 막기 · 화면 즉시 + [저장] 때 서버 Node 재계산(5장 ②) · `package.json` 깨진 `build:formula` 고침 — `ui_template_sheet_recalc.ts` 한 벌 · `[$이름]` · ROUNDUP · 합계 줄 열마다 식 · 서버 `common_util_sheet_recalc.py` → `common_util_sheet_recalc_node.ts`(sheet 폴더 밖 — M02 화면이 폴더를 훑어 불러옴) +- [X] 저장 JSON — `3_표양식.md` 4장 초안(층 · 열 id · 식 · 줄 · 합계줄 · 보기) — 모양 `ui_template_sheet_types.ts` · 계약 칸(설명 · 바인딩 · 일위대가 · 펼침 · 손 · 변수 · 쪽줄 · 고정) + +- 검증완료 — `test_sheet_recalc.py` 5 개(실무 울진 2공구 합계 캐시값 · Decimal 거울 40 문서) · 포트 5178 M02 메인 칸에 붙여 머리 합치기 · 마스터 조작 · 쪽 50+11 · 잠금 · 손 열 합계 · 틀고정 · 너비 끌기 · 화면 풀이 = 서버 Node 풀이(계산 칸 183 · 합계) + +### 10-3. 도면 양식 + +- [X] B07 의 iframe · 주고받기 부모 흐름을 공용으로 떼어 M02 · B07 한 벌 · 도각 편집 모드로 양식 만들기 · 저장 · 목록 — `ui_template/cad_host/` · `mountDrawingTemplate`(getDoc 은 Promise) · 목록은 10-1 +- [X] 양식 장부(이름 · 종류 · 크기 · 작도 영역 · 자리표 키 · 판) · 자리표 키 목록 보이기 · 작도 영역을 양식 칸으로(지금 `_A1_INNER` 박힘) — 양식 칸 `drawing_area` · `GET /api/m02/drawing-fields` · 이름 · 종류 · 판은 10-5 층 목록 · 크기 칸은 안 둠(작도 영역으로 갈음) +- [X] 도면 기본 양식 `resources/master_template/drawing/` 로 옮김 · B07 도각 읽는 차례 프로젝트 작업본 → 시스템 · [도각 편집] 저장 = 작업본 · [기본 도각으로] = `_initial/` + +- 검증완료 — 시험 22 개 통과 · 포트 5177 B07 도형 수 그대로(표지 15 · 종단 800 · 평면 401 · 토적 287 · 표준횡단 102) · 도각 편집 열기(51) · 취소 · M02 부품 붙여 도각 51 · getDoc 51 · 자동백업 칸 `m02:*` 따로 + +- [X] 옛 회사 도각 → 회사 층 `storage/1/templates/drawing/00_template_A1.json` 복사(원본 그대로) · M02 도면 칸 메인 꽉 채움 + +- 검증완료 — 층 길 회사 목록에 보임 · [회사 양식 적용] 으로 프로젝트 작업본 덮임 · B07 도면이 그 도각으로 그려짐(표준횡단 102 · 토적 287) · [도각 편집] 저장 = 작업본(`_initial/` 그대로) · [기본 도각으로] 뒤 백업과 같음 · M02 자리표 18 · DXF 불러오기 3 · CAD 캔버스 495×571 + +### 10-6. 최종 확인 뒤 개선 (사용자 「반영하고 검증」) + +- [X] sub1 층 단추 눌림 표시 · 한 줄 · 프로젝트 기본값(마지막 고른 것 → 내 최근 것) — 검증완료(브레인 ORCA 5173 · 시스템 단추 채움색 · 글 한 줄) +- [X] sub2 CAD 머리 글 · 저장 단추 하나 · 자동백업 칸에 층 — 검증완료(브레인 ORCA · 머리 「도면 양식 편집」 · 저장 1 · 백업 칸 m02:system · m02:project:id 갈림 · CAD 695×790) +- [X] sub3 딱지(계산 · 설계값 · 값마다 · 손) · 식 줄 이름으로 · 열 폭 · 풍선 · 50줄 쪽 나눔 시험 — 검증완료(`test_sheet_pages.py` · `test_sheet_recalc.py` 8 개 · ORCA 5178 시스템 집계표 딱지 A·줄눈·커플링 = 계산 · 식 줄 「B × L+확폭」 · 머리 잘림 0 · 프로젝트 층 줄 더하기 38 번 → 55줄 쪽 50 · 5 · 합계 2쪽만 · 다시 열기로 17줄 되돌림) +- [X] sub3 머리 칸 「/」 로 층 나눔 · 줄 머리 칸 가로 고정 · 줄 머리 칸 폭 자동 — 검증완료(`test_sheet_pages.py` 9 개 · ORCA 5178 시험 양식 새 열 「관공/Φ800/관매설」 세 층 → 저장 · 다시 열기 그대로 · 끝까지 넘겨 줄 머리 칸 9 개 왼쪽 고정 · 잘림 0 · 맨 위 · 시험 양식 지움) +- [X] sub3 수 칸에 수 아닌 글 → 오류 색 + 「수가 아님」 풍선 · 합계 칸에 「수가 아닌 칸 N 개는 빼고 더함」 — 검증완료(`test_sheet_recalc.py` 6 개 · ORCA 5178 프로젝트 층 abc · 12abc · - 오류 / 5 · 1,234 · 1e3 · 빈 칸 · 공백 · =1+1 정상 · 글 칸 abc 표시 없음 · 연달아 10번 바꿔도 알림 따라감 · 다시 열기로 되돌림) +- [X] sub4 관공 관경마다 짝 · 빈 값 글 · 계산 열 바인딩 떼기 · 옛 프로젝트 표 틀 갱신 — 검증완료(시험 21 개 · ORCA 5179 랩탑_보조 채운 표 머리 = 파형강관 Φ800 · Φ1000 · Φ1500 마다 관매설 · 커플링밴드 짝 · 「높이 미입력」 · 「Φ1000×련수 미입력」 · 「쌓기 방식 미입력 60~80 H=2.5」 · 커플링 칸 is-calc(NO.4+5.05 관 10 → 1) · 옛 프로젝트 6 개 작업본 · `_initial` 12 파일 = 시스템 판 d43943e2) +- [X] 브레인 ORCA 다시 확인 — 검증완료(랩탑_메인 오류 칸 0 · 관매설·커플링 짝 · 합계 50·5·11·1·21·2·10·1 · 랩탑_보조 다시 열기·양식 오가기 오류 0 · 틀린 판 저장 409 · 파일 안 바뀜) +- [X] 재검증(사용자 「마스터 양식 집중 · 재검증」) — 도면 저장 {} 흠 · 빈 문서 400(시스템 · 층) · 머리 / 층 나눔 · 줄 머리 칸 고정 · 저장 뒤 지우기 판 맞춤 — 검증완료(브레인 ORCA · DXF 선1·글자1 · 작도 영역 820 남음 · 스크롤 끝 줄 머리 left 345 그대로 · 저장 뒤 지우기 한 번 · storage 양식 74 파일 깨진 것 0) +- [X] 전수 점검 목록(`tmp/M02_분석/8_점검목록.md`) 두 바퀴 — 흠 10(이름 까닭 · / 이름 · 저장 안 한 고침 · 머리 메뉴로 떠남 · 개인 지우기 · 덮을까 · 저장 뒤 양식 닫힘 · CAD 💾 · 엉터리 파일 500 · 수 칸 글) 고침 — 검증완료(브레인 ORCA 목록 전 항목 됨 · 시험 통과 · 타입 검사 0 · 시험 양식·개인 파일 0 · 랩탑_메인 초기화 _initial 과 같음) + +### 10-7. M02 = 시스템 층만 · 도면 칸 위 공간 줄이기 (사용자 바로잡음) + +- [X] sub1 좌측 패널에서 층 단추 넷 · 프로젝트 고르기 · 프로젝트 층 단추 다섯 뺌 · 목록 = 시스템 표 · 도면 + 새로 · 본떠 · 지우기 · 제목 줄에 도면 칸이 쓸 자리(headerSlot) +- [X] sub2 도면 칸 — 위 자리표 글 줄 뺌(CAD 자리표 패널과 중복) · CAD 제목 줄 숨김 · 작도 영역 · 파일 불러오기는 제목 줄 한 줄로 · 그림 칸 위쪽 448px → 250px 안쪽 +- [X] 브레인 점검 목록 재실행(층 항목은 서버 시험으로) — 검증완료(좌측 = 표 · 도면 목록 + 새로 · 본떠 · 지우기 · 그림 칸 위 448 → 246px · 높이 598 → 800 · 작도 영역 단추 값 다 보임 · 저장 815 남음 · 틀린 값 막힘 · 확인창 · 표 항목 됨 · sub1 창 입력 거절로 sub2 가 대신함) + +### 10-5. 양식 층 · 복사 · 가져오기 (사용자 확인) + +| 층 | 자리 | 누가 씀 | +| -------- | ------------------------------------------------------------------------------------- | ---------------------------- | +| 시스템 | `resources/master_template/{table,drawing}/` (git) | M02 시스템 관리자 | +| 회사 | `storage/{회사}/templates/` (B07 도각이 이미 여기) | 회사 관리자 — 「회사 공식」 | +| 개인 | `storage/{회사}/{사용자}/templates/` | 본인 | +| 프로젝트 | `storage/{회사}/{사용자}/{프로젝트}/templates/` + 초기 사본 `templates/_initial/` | 프로젝트 사용자 | + +- [X] 프로젝트 만들 때 시스템 양식 전부를 프로젝트 `templates/` 와 `_initial/` 에 복사 · manifest 에 출처(층 · 이름 · 판) · `_initial/` 은 그 뒤 안 씀(5장 초기값 규칙) — `M02_Template_Layers.py` · 층 폴더마다 `manifest.json` +- [X] 이미 만든 프로젝트 — storage 의 프로젝트 폴더마다 시스템 양식을 작업본 · `_initial/` 로 넣음(한 번 · 도구로) · 목록을 사용자에게 보임 — `M02_Template_Migrate.py` · 6개 넣음 · 목록 `tmp/M02_분석/7_기존프로젝트_양식.md` · 표 1 + sub2 도면 5 (두 번 돌림 · 없는 것만 더함) +- [X] [초기화] = `_initial/` 을 작업본에 덮어씀 · [회사 양식 적용] = 회사 공식을 작업본에 덮어씀 · [양식 가져오기] = 시스템 · 회사 공식 · 같은 회사 사람의 개인 양식(읽기만) · 같은 회사 다른 프로젝트 양식 → 작업본을 바꿈(초기화 기준은 안 바뀜) — 서버 길 `M02_MasterTemplete_Router_Layers.py` · 단추는 10-1 +- [X] [내 양식으로 저장] → 개인 층 · [회사 공식으로 저장] → 회사 층(회사 관리자만) · 읽는 차례 = 프로젝트 작업본 → 없으면 시스템(옛 프로젝트) — 회사 관리자 = ADMIN · 마스터 · 시스템 관리자 + +- 검증완료 — 시험 10 개 통과(임시 storage · 층 길 · 복사 · 초기화 · 회사 적용 · 가져오기 · 권한 · 넣기 도구) · 실제 storage 6 프로젝트 넣기 뒤 작업본 · `_initial/` · manifest 확인 + +### 10-4. 구조물 집계표 마스터 · 프로젝트 복사 + +- 열 규칙 확정 — `tmp/M02_분석/5_집계표_열규칙.html` (머리 4층 · 설계값 묶음 21 · 손 입력 10 · 계산 열 4 · 일위대가 후보) · **한 표**(집계 · 그외 안 나눔) · 「전구간」 = 표 맨 위 손 입력 한 줄 · 오솔길엔 구조물 집계표 없음(P&B 입력은 도면 글자만 · 횡단 물량표는 토공만) + +- [X] 구조물 집계표 마스터 한 벌 — 위 열 규칙대로 · 종류는 `B05_Profile_Structure_Types.json` 을 가리킴 — `resources/master_template/table/구조물집계표.json` · 채우기 `M02_Table_Fill.py`(`GET …/tables/{이름}/filled`) +- [X] 프로젝트 만들 때 마스터 복사(`B02` `_initialize_project_storage` 끝 · `{프로젝트}/templates/` · manifest 에 판) · 읽기 차례 프로젝트 → 마스터 · [기본으로] = 마스터 다시 복사(`apply` `from: system`) + +- 열 규칙과 다른 곳 — 관보호공은 「유입」 한 칸(설계에 유출 보호공 칸 없음 · 유출은 기슭막이 열) · 큰돌쌓기는 찰·메 · 돌 크기 · **높이**까지 펼침(면적 L×H 를 표 식으로) · 흄관 · VR관 1본 길이 빈칸 → 커플링 식 없음 +- 검증완료 — 시험 5 개 통과 · 랩탑_보조 채운 표 = 설계와 같음(관 Φ800 6 · Φ1000 1 · Φ1500 2 · B06 관 연장 9곳 · 세월교 2 · 옹벽 반중력식 H2.0 · 돌쌓기 찰 H2.5 2 · 메 H2.0 · 메 H2.5 · 큰돌쌓기 1 · 줄 16) + +- [X] 프로젝트 표 [저장] 거름 — 층 길 PUT 에서 서버가 `strip_design` · 작업본 = 양식(펼침 규칙 열 · 식 · 변수 · 머리) + 손 값(측점 줄 키) + 전구간 줄 · 채울 때 설계에 안 쓰인 펼침 열도 `양식열` 로 실어 저장 때 되살림 · 없어진 측점 손 값은 맨 아래 사용자 줄(비고 「설계에서 측점 없어짐」) + +- 검증완료 — 시험 18 개 통과 · 랩탑_보조 채움 → 손 입력 둘(취수정 NO.4+5.05 · 비고 NO.8+13.09) 저장 → 작업본 열 49 · 펼친 열 0 · 설계값 0 · 줄 3 → 다시 열기 둘 다 제자리(관 연장 10 옆) → 초기화 = `_initial` 과 같음 + +## 11. M02 개선 — 컨테이너 · 롱기튜디널 지우기 · 구조물 도면 · 산출근거 (담당 sub1 11-1 · sub2 11-2 · sub3 11-3 · sub4 11-4) + +사용자 지시 — 마스터 템플릿 = 표 양식 · 도면 양식 두 구조 · 그룹은 컨테이너 · 롱기튜디널 지움 · 집계표 일위대가 아래 도면 줄(도번 연동) · 값을 고르면 아래에 설계 컨테이너 + 상세 산출근거 표 · [도면 수정] 으로 도면 편집 · 산출근거는 엑셀과 같은 빈 표. + +### 11-0. 사용자 확정 + +- 구조물 도면 · 산출근거 표 = **집계표 열마다 하나** · 펼침 열(관 지름 · 돌쌓기 높이)은 마스터 열 하나에 도면 하나(프로젝트에서 펼친 규격들이 같은 도면을 씀) +- 도번 = 자동(구조물 도면을 만든 차례로 `구-01` …) · 도면이 없으면 도면 줄에 「없음」 +- 설계 컨테이너 = 그 열의 구조물 도면 미리보기(읽기만) + [도면 수정] · 아래에 상세 산출근거 표 +- 롱기튜디널 두 양식 = 시스템 · 이미 만든 프로젝트 6곳 작업본 · `_initial/` 사본까지 지움 +- 저장 = 새 폴더 `resources/master_template/structure/` · 좌측에서는 「도면 양식」 컨테이너 안 「구조물 도면」 묶음 +- 산출근거 빈 표 머리 = 실무 `07-구조도-소광리.xlsx` 꼴(공종 · 규격 · 산출 내역 · 단위 · 수량(m당)) · 줄은 비움 · 채우기는 나중 + +### 11-1. 좌측 컨테이너 · 롱기튜디널 지우기 + +- [x] 좌측 「표 양식」「도면 양식」 = M01 좌측과 같은 접히는 컨테이너(`ui-collapsible ui-sidebar-section` · `attachCollapsible`) · 「도면 양식」 안에 「구조물 도면」 묶음(11-3 목록) +- [x] `01_template_Longitudinal_table.json` · `01_template_Longitudinal_Yaxis_scale.json` 지움 — 시스템 + 프로젝트 6곳 작업본 · `_initial/` · manifest 줄(한 번 도는 정리) +- 검증완료(11-1): M02 시험 38 통과 · ORCA 5174 컨테이너 접고 펴기 · 목록에 롱기튜디널 없음 · 정리 전후 templates 파일 84→60(프로젝트 6곳 · 파일 24 · manifest 12) · SideHandle.select(kind, name) 추가 · 검증 대기 + +### 11-2. 집계표 도면 줄 · 칸 고르기 신호 + +- [x] 마스터 전용 줄(식 · 설명 · 일위대가) 아래 「도면」 줄 — 열의 구조물 도면 도번 · 없으면 「없음」(`ui_template/sheet/`) · 칸 글 = `SheetOptions.drawingLabel(colId)` · 글이 바뀌면 `recalc()` +- [x] 칸을 고르면 페이지로 알림(`SheetOptions.onSelect({ row, col, colId })` · row = 줄 열쇠 · col = 열 차례) · 표 부품은 한 벌(M02 · 뒤에 프로젝트 화면이 같이 씀) +- 검증완료(11-2): 시험 12 통과 · typecheck · ORCA 5177 구조물집계표 일위대가 아래 도면 줄 49칸 「없음」 · 잠김 · 줄 머리 고정 · 방향키 일위대가↔도면↔첫 줄 · 시험 표에서 라벨 · recalc 다시 그림 · onSelect(누르기 · 방향키 · 머리) · project 에 도면 줄 없음 · 검증 대기 + +### 11-3. 구조물 도면 폴더 · 서버 + +- [X] 양식 종류에 `structure` 더함 — 열 id 로 찾는 도면 + 산출근거 표 짝 · 도번 자동 · 목록 · 새로(A1 도각을 깐 빈 도면) · 저장 · 지우기 · 새 프로젝트 복사에도 실림 + - 서버 주소 · 함수(`M02_MasterTemplete_Api_Fetch.ts`) — 목록 `GET /api/m02/templates` → `listStructures()` · 읽기 `GET /api/m02/templates/structure/{열 id}` → `readTemplate("system", "structure", id, null)` · 저장 `PUT` 같은 주소 → `saveTemplate("system", "structure", id, null, 판, 문서)` · 새로 `POST /api/m02/structures/{열 id}` → `createStructure(id)`(이미 있으면 `StaleError` · 없는 열 404) · 지우기 `DELETE` 같은 주소 `?판=` → `deleteTemplate("structure", id, 판)` · 도번 목록 `GET /api/m02/structures/numbers` → `fetchStructureNumbers()` = `{열 id: 도번}`(키 없음 = 「없음」) · 문서 타입 `StructureDoc` +- 검증완료 — 시험 42 개 통과(새 `test_m02_structure.py` 4 + 기존 M02) · 8005 서버 직접 호출: 새로 구-01 · 다시 409 · 없는 열 404 · 도각 개체 51 · 산출근거 머리 다섯 · 저장 · 도번 목록 · 층 목록 · 지우기 · 시험 파일 지움 + +### 11-4. 설계 컨테이너 · 산출근거 빈 표 · [도면 수정] + +- [x] 집계표 아래 설계 컨테이너 — 고른 열의 구조물 도면 미리보기(읽기만) + [도면 수정](없으면 새로 만들어 엶 · 같은 페이지 CAD 편집으로 옮김 · 저장 안 한 고침 확인창) +- [x] 그 아래 상세 산출근거 표 — 11-0 머리 · 빈 줄 · 저장 +- 11-4 코드 = 9d1a4f9b(dsub) · M02 시험 14 통과 · typecheck · ORCA 5174 화면 검증완료(도번 구-01 · 산출근거 저장 · 열 바꿈 · 확인창) · 7e694a88 · 검증 대기 +- 브레인 임의 판정: 설계 컨테이너 미리보기는 읽기만이라 CAD 도구 모음을 숨김 · 243d00c2 검증완료(미리보기 리본 display:none · 편집 화면 높이 122) + +### 11-5. 좌측 패널 공용 틀 (담당 sub1) + +- [x] M01 카드 · 항목 모양을 `ui_template_overlay.css` 공용 클래스로 · M01 · M02 좌측이 같이 씀 · 처음 펼침 · 접힘 = M01 과 같게 · 새로/본떠/지우기 단추 줄은 칸 안 · 확인 = ORCA 로 M01 · M02 나란히 수치 대조(사용자가 보고 판단) +- 검증완료(11-5): ef5a7e97 · ORCA 5174 M01 · M02 머리 14px · 여백 12 · 둥글기 8 · 선택 표시 · 처음 접힘 같음 · M01 전후 같음 · 새로 · 지우기 됨 · 사용자 판단 대기 + +### 11-6. 구조물 도면 컨테이너 따로 · 빈 화면 (담당 sub1) + +- 사용자 바로잡음: 구조물도와 도면 양식은 다른 개념 — 좌측 「구조물 도면」 은 「표 양식」「도면 양식」 과 같은 층 컨테이너(11-0 「도면 양식 안 묶음」 을 갈음) · 비어 있으면 안 됨 +- [x] 좌측 컨테이너 셋(표 양식 · 도면 양식 · 구조물 도면) · 구조물 도면 = 구조물집계표 열마다 한 줄(도면 없어도 열 이름 + 「없음」) +- [x] 사용자 확정: 빈 도면도 열마다 미리 만들어 도번(구-01 … 열 차례) 연결 · 집계표에 열을 더해 [저장]하면 그 열 빈 도면도 만들어짐 +- [x] 집계표에서 열을 고르면 아래에 컨테이너 둘(공용 접힘 카드) — 「도면 정보」(열 · 도번 · 미리보기 · [도면 수정]) · 「상세 산출근거」(표) +- 사용자 바로잡음: 구조물 도면 항목 = 도면만(산출근거 없음) · 구조물도는 도각 없음(그린 만큼 설계 영역) +- 검증완료(11-6): 65f43ca0 · 열 49 빈 구조물 도면(도각 없음) · 열 더해 저장 → 서버가 만듦 · 좌측 줄 누르면 CAD · 시험 49 · typecheck 0 · 흠: 도각 없는 빈 CAD 열면 빨간 오류 알림(도면 양식 새로도 같음 · 전부터) + +### 11-7. 산출근거 엑셀 · 구조물 도면 다듬기 (검토 중 · sub2 엑셀 · sub4 구조물 도면) + +- 사용자 확정: 산출근거 = 엑셀 · 구글 시트와 같은 자체 스프레드시트(A00_Common 새 폴더 · 시간 걸려도 됨) · 집계표 일부를 가져오는 것 아님 +- 사용자 확정: 도면 정보 미리보기에 CAD 특성 · 도면층 패널 없음 · 마스터 CAD 리본 처음 접힘 +- [x] 깊은 검토 — 엑셀(tmp/M02_분석/9_엑셀검토.md) · 구조물 도면(10_구조물도면검토.md) → 사용자 판단 뒤 계획 +- 사용자 확정: 엑셀 파일 가져오기 안 함 → 엑셀 · 구글에서 복사해 붙여넣기 · 인쇄 · PDF · 내보내기는 비활성(나중에 한 번에) · 산출근거 = 구조물집계표 열마다 따로 파일 · 도면 치수 참조는 따로 협의 +- [x] 구조물 도면 다듬기 — 미리보기 특성 · 도면층 숨김 · 도면층 없는 도면 오류 가드(sub1 마무리 중) · 마스터 CAD 리본 처음 접힘 · 편집 제목 열 머리 글(10_구조물도면검토.md 일감 A · C) +- 검증완료(11-7 다듬기): 리본 처음 접힘 26px · 단추로 펼침 122px · B07 펼침 그대로 · 제목 구-01 · 열 머리 글 · 특성 패널 숨김 · import 가드(sub1) · 서버 · 화면 새 문서 기본 도면층 + 49 파일 채움 dd7300aa + +### 11-8. 산출근거 스프레드시트 1단계 (A00_Common/spreadsheet · 근거 9_엑셀검토.md) + +- [x] 0 계약 — 타입 · 엔진 공개 함수 머리 · 저장 JSON 견본(열마다 파일) · 브레인 승인 뒤 A~F 병행 +- 검증완료(0 계약): 26fd4fb7 · spreadsheet_types.ts · spreadsheet_view_types.ts · 겉모양 함수 · 견본 JSON · 크기 예산 압축 뒤 54 KB · 저장 resources/master_template/basis/<열 id> +- [x] A 엔진(주소 · 수식 풀이 · 의존 재계산 · 순환 · 행열 넣기 참조 옮김 · 되돌리기) · B 함수 약 45 · 숫자 형식 · 채우기 +- [x] C 격자 그리기(병합 · 틀 고정 · 숨김 · 테두리) · D 입력(한글 · 수식 입력줄 · 참조 가리키기 · 채우기 끌기) +- [x] E 도구 · 시트 탭 · 클립보드(엑셀 · 구글과 서로 붙여넣기) · F 서버 같은 계산 · M02 끼우기 · 실무 수식 2,699칸 값 대조 시험(시험만 xlsx 읽음) +- 담당: A sub3 · B sub5 · C sub6 · D sub7(잇기) · E sub8 · F sub2(임시 [스프레드시트 시험] 단추 · 금빛 시험) +- 검증완료(A): 736dac4e · 엔진 · 오류 · 순환 · 딸린 칸만 재계산 · 행열 참조 옮김 · 되돌리기 · 규칙표 시험 9 묶음 · 압축 10.5 KB +- 검증완료(D): 고름 · 편집기 · 한글 조합(compositionstart) · 수식 입력줄 · 주소 상자 · 참조 가리키기 · 단축키 · 채우기 끌기 · 되돌리기 · 진짜 A 엔진과 잇기 · 규칙 시험 7 묶음 · ORCA 시험 틀 · 압축 약 10 KB · C · B 채우기 · E 는 빈 몸이라 가짜로 잇고 들어오면 다시 잇기 +- 검증완료(D 잇기): 가짜 부품 걷고 A · B · C · E · 2단계와 createSpreadsheet 잇기 · 찾기 패널(Ctrl+F · Shift+F5 · Ctrl+H) · 메모 풍선 · Shift+F2 · 서식 붓 · 골라 붙여넣기 · 폭 자동 맞춤 · 함수 자동 완성 · M02 [스프레드시트 시험] ORCA 한 바퀴 수치 · 크기 시험 넘침(locale 약 45 KB · 엔진) 브레인 보고 +- 검증완료(D 잇기 2): 정렬 · 필터 · 셀 서식(Ctrl+1 · [서식…]) · 조건부 서식 · 유효성(막음 · 목록 ▼) · 이름 상자 · 눈금선 · 여러 범위 · 수식 분석 · 자동 합계 · 상태 표시줄 · 시트 숨김/탭 색 · M02 ORCA 수치 · 크기 시험 통과(화면 27.2 KB) · 묶어 접기(sub6) 안 옴 +- 검증완료(D 잇기 3): 칸 넣기/지우기 · 이동(F5 · Ctrl+G) · 틀 고정 · 데이터 도구 3 · 묶기/풀기(Shift+Alt+→/←) · 단축키 표 「안됨」(Ctrl+B/I/U/5 · 바깥 테두리 · Ctrl+; · Ctrl+9/0 · Ctrl+PageDown · Shift+F11 · F4) · 이름 풀이 · 자동 완성 105 · 숨긴 시트 · 탭 색 · 필터 행 숨김 · M02 ORCA 수치 · 크기 시험 통과(화면 27.0 KB · 합 60.9 KB) · 조건부 서식 색 · 초록 세모(sub6) 안 옴 +- 검증완료(D 잇기 3 끝): sub6 조건부 서식 칸 색 · 초록 세모 · 필터 행 숨김 M02 ORCA 수치 · 골라 붙여넣기 늦게 불러 크기 시험 통과(화면 27.6 KB · 합 61.5 KB) +- 검증완료(D 식 오류 막음): 편집기 확정 때 parseFormula 실패면 편집 유지 · 오류 알림 · 커서 오류 자리 · M02 ORCA =SUM() 막힘 · =SUM(1,2) → 3 · 크기 시험 통과 +- 검증완료(D 크기 줄임): sub2 · sub8 병합 뒤 화면 30,126 B → 28,102 B(예산 28,672) — 함수 도움말 · 이름 상자/관리 · 서식 붓 · 메모 편집 · 열 너비 맞춤을 처음 쓸 때 불러옴 · M02 ORCA 산출근거 카드 · 편집 화면 · 도구 모음 기본 글꼴(굴림/10 흐리게) 확인 +- 검증완료(B): 7725b39e · 함수 48 · 숫자 형식 해석 · 채우기 · 시험 69 +- A 더함: 뺀 셋(행 넣기 서식 따라감 · 자동 교차 · Sheet.기본) · Sheet.comments · 무게 시험 4 통과(sub3) +- 검증완료(E): bfd71238 · 도구 · 탭 · 우클릭 · 클립보드(안↔안 · 구글 R1C1 · 엑셀 수식 표시) · 가짜 A · B 로 ORCA · 실제 엑셀 앱 시험은 없음(앱 없음) +- 검증완료(D): 48a21351 · 한글 첫 글자 · 수식 입력줄 · 참조 가리키기 · 방향키 · 채우기 끌기 · 되돌리기 · 시험 7 · 가짜 부품으로 · 편집 좌표 = 판 안 기준(브레인 판정) +- 검증완료(F): 85fa62ae · M02 카드 · 임시 단추(inspect) · 금빛 시험 실무 수식 2,693칸 대조 갈림 0 · B 뒷일 b93e03e5(moveFormula 잇기 · 실무 숫자 형식 66 대조 · 흠 4 고침 · 분수 형식 없음) +- E 뒷일 fddaa4ed · 진짜 엔진으로 다시 · 엑셀 · 구글 HTML 견본 붙여넣기 시험 · 실제 앱 복사는 못 함(엑셀 없음 · 구글 창 포커스) · 2단계 메모 0d2d5614(sub1) +- 검증완료(C): 7d9fc6ee · 격자 4판 · 병합 · 틀 고정 · 숨김 · 폭 끌기 · hair 선 · 1만 행 스크롤 칸 수 일정 · data-addr · 메모 세모 +- 잇기 ce3df392(sub7) · M02 시험 화면에서 한글 · 재계산 · 되돌리기 · 채우기 · 서식 · 값만 붙여넣기 · 바꾸기 · 메모 · 서식 붓 · 폭 맞춤 · 정렬 · 필터(sub3) · 브레인 판정: 크기 예산 엔진 32 KB · 화면 28 KB 그대로(공용 글자표 뺌) +- 3단계 더함: 조건부 서식 · 유효성 · 이름 정의(sub4) · 분수 형식 · 함수 30(d60b1ccc · 시험 135 · sub5) — 잇기는 sub7 +- 셀 서식 창 Ctrl+1 여섯 탭 · 단축키 표 53(sub1 · e1fc529f) +- 격자 2단계 327226e2(sub6 · 확대 10~400% · 눈금선 · 여러 범위 · 고름 테두리 비낌 고침) · 통계 13 · 날짜 9 · 날짜 형식(sub5 · 시험 180 · EDATE 월말 흠 고침) +- 크기: 시험 단추 먼저 눌러도 63.3 KB · 213 KB 조각 안 받음(sub2 · 4e7f2e2c) · 엔진 31.4 · 화면 27.1 · css 3.9 = 62.5 / 64 KB +- 흠 고침: 이름 풀이 610ac856(sub3) · 자동 완성 105 함수(sub5) · 숨긴 시트 · 탭 색 ddfff14f(sub8) · 데이터 도구 · 칸 삽입 창 · 이동 · 틀 고정 메뉴 de94486a(sub1) +- [x] 2단계(인쇄 · 내보내기 뺌) — 찾기 · 바꾸기 · 메모 · 골라 붙여넣기 · 서식 붓 · 폭 자동 맞춤 · 여러 범위 · 확대 · 눈금선 끄기 · 함수 자동 완성 · 도움말(sub1) +- [x] 브레인 브라우저 검증 — [스프레드시트 시험] 단추 · 전수 목록 + 예외 입력 → 임시 단추 지움 +- 검증완료(브레인 · 메인 5173 · 9-27 23시): 점검 39 + 예외 12 · 1차 흠 10 → 고침(상태 표시줄 글색 · 확대 % · 먼 칸 스크롤 · 자동 맞춤 255자 · ##### · 틀 풀기 · 엑셀 서식 붙여넣기 · 병합 일부 막기 · 인자 수 막기 · Enter 막기) · 2차 통과 · 남은 작은 것: 도구 모음 글꼴 칸이 기본 글꼴일 때 빔 · 임시 [스프레드시트 시험] 단추는 사용자 확인 뒤 지움 +- [x] 사용자 지시(9-27 밤): 임시 시험 단추 지움 · 산출근거 카드 빈 격자 늘 보임 · 도면 정보 미리보기 카드 안에 맞춤(sub2) · 도구 모음 기본 글꼴 표시(sub8) · 뒤에 전역 깃 싱크(각 창 폴더까지) · 엑셀 실제 복사는 사용자가 확인 +- [x] 사용자 지시: 산출근거 카드 행 늘려 높이 키움 · 좌측 「산출근거」 컨테이너(구조물 도면과 같게 · 열마다 · 표번 표-01 자동) · 수정은 별도 편집 컨테이너(좌측 줄 · 카드 [산출근거 수정])(sub2) +- 검증완료(9-28): 임시 단추 지움 · 빈 격자 30행 · 도면 정보 넘침 · 표번 · 좌측 산출근거 · 전체 편집 격자 높이(e4eef2a4) · 기본 글꼴 흐리게(cd7a81a4) · 크기 62.6 KB(87417b3a) · 전역 깃 싱크 = 랩탑 메인 + sub1~8 트리 53bbad2e 같음 · 데스크탑 넷은 원격만 옮김(PC 꺼짐 · 켜면 받기) + +## 12. M02 구조물 번호 체계 · 집계표 줄 · 스프레드시트 다듬기 (담당 sub4 · sub2 · sub5 · sub6 · sub7 · sub8 · sub1) + +- 사용자 확정: 도번 SD · 표번 SC + 숫자 6자리 [종류 2][분류1 2][분류2 2] · 없는 층 00 · 한 번 준 번호 유지(새 열은 그 무리 다음 번호 · 지운 번호 비움) · 조회 후 선택 = 다른 열 것을 복사해 가져옴(열마다 따로 동작 · 같이 쓰기 없음) +- 사용자 확정: 집계표 제목행 = 종류 · 분류1 · 분류2(글 자유) · 같은 종류에 분류1 더하기 · 마스터 줄 = 일위대가 · 도면 · 산출근거(조회 후 선택) · 열 더함 = 도번 · 표번 · 파일 자동 · 열 지움 = 같이 지움 · 빈 산출근거 = 진짜 빈 시트 · 산출근거에 시트 탭 없음 +- 브레인 임의 판정: 구조물 = 꼴=수 열만 · NO · 측점 · 비고 같은 글 열은 번호 · 파일 없음 +- [x] 서버 번호 · 자동 만들기/지우기 · 옛 50 파일 새 번호로 옮김 · 빈 산출근거 · 복사 가져오기 길 · 좌측 「번호 + 구조물 이름 + 변수값」(sub4 · 3a4b3066 · fc249c00 · 2623633b · 시험 29 · SD010101~SD360000 · 46+46) +- [x] 집계표 제목행 이름 · 분류1 더하기 · 산출근거 줄 · 세 줄 조회 후 선택(sub2) +- 검증완료(12 sub2): 시험 34 통과 · typecheck · ORCA 5177 층 이름 종류 · 분류1 · 분류2(두 번 눌러 고침) · 「분류1 더하기」 = 같은 종류 끝에 [종류, 새 분류, 새 열] · 산출근거 줄 SC 번호 · 글 열(NO · 측점 · 비고) 세 줄 빈칸 · 잠김 · 일위대가 목록 37 찾아 고름 · 도면 복사 = 다른 열 45(자기 · 글 열 빠짐) → 덮어쓰기 확인 → 도번 · 열 그대로 내용만 같아짐(파일 되돌림) · 검증 대기 +- [x] 스프레드시트 테마 연동(sub6) · 엑셀 붙여넣기 서식(스타일 묶음 · 선 모양 · 색)(sub8) · 엑셀 이름 형식 Fixed · Standard · Currency 등(sub5) · 산출근거 시트 탭 없앰 · [산출근거 수정] 느림 원인(sub7) · 집계표 항목 고른 뒤 CAD 불러오기 오류(sub1) +- 검증완료(12 스프레드시트 쪽): 테마 ee64781a · 붙여넣기 서식 b2e2fcff · 이름 형식 4bbef4ac(₩ 는 재현 안 됨 · 실제 복사 내용 필요) · 시트 탭 없앰 · 편집 화면 0.08초(느림 재현 안 됨 · 옛 높이 0 흠 추정) · CAD 오류 22835ca9 +- 검증완료(sub7 산출근거 탭 · 느림): createSpreadsheet tabs:false — 카드 · 전체 편집 탭 줄 없음 · 시트 단축키 · 시트 메뉴 꺼짐 · [산출근거 수정] 5182 실측 75~83 ms(격자 만들기 ~50 · 서버 읽기 19~24 · 새 조각 0) · 찬 서버 카드 0.2 s — 느림 재현 안 됨 +- 조사(코덱스 · 9-28): 오토캐드 Ctrl+C → 웹 Ctrl+V 는 브라우저만으론 안 됨(전용 형식 · 임시 DWG 경로를 브라우저가 못 읽음) · 추천 = 오토캐드 .NET 전용 명령이 고른 개체를 DXF 글로 클립보드에 → 웹이 글 붙여넣기 → 기존 DXF 변환기(B07_DesignDetail_Engine_Frame_Import.py) 재사용 · 해치 · 치수 의미는 일부 잃음 — 착수는 사용자 판단 +- 검증완료(12 · 브레인 · 메인 5173): 좌측 SD 47 · SC 47 줄 번호 맨 앞 · 옛 구-/표- 0 · 집계표 제목 종류 · 분류1 · 분류2 · 일위대가 · 도면 · 산출근거 줄 · 사용자 열 c1 = SD010102 로 살림(충돌 dbda85e8) + +## 13. 집계표 분류3 · 면적산출 · 재료산출 · 알약 연결 (담당 sub3 조사 · sub4 서버 · sub2 집계표 · sub7 알약 · sub8 단위) + +- 사용자 확정: 제목행 = 종류 · 분류1 · 분류2 · 분류3 · 번호 8자리 [종류][분류1][분류2][분류3] · 식 · 설명 줄 지움(식 3열 계산도 지움) · 산출근거 → 면적산출(SA) · 재료산출(SM) 줄 더함 · 도면 SD · 열마다 따로 엑셀 +- 사용자 확정: 면적산출 엑셀 위 알약 = 그 열 일위대가의 입력값 · 칸을 골라 알약에 연결하면 알약 흐려짐(연결 현황) · 재료산출 알약 = 자재DB 에서 골라 칸 연결 · 엑셀에 단위 기호 고르기 +- 사용자 알림: 분류2 더하기 안 됨 → 고침 · 우클릭 「열 더하기」 → 「분류2 더하기」 · 「분류3 더하기」 새로(sub2) +- [x] 일위대가 입력값 목록 · 자재DB 목록이 어디서 오는지 조사(sub3 · 13_알약자료.md) · 브레인 판정: 면적 알약 = 물량(결과단위 분모)만 · 같은 이름 · 단위 합침 · 설계 조건 빼고 +- [x] 서버 — 8자리 · SA 로 옮김 · SM 새로 · 자동 만들기/지우기 · 복사 · 좌측 「면적산출」 · 「재료산출」(sub4) / 집계표 — 분류3 층 · 식 · 설명 줄 지움 · 면적산출 · 재료산출 줄 · 카드 · 편집 화면(sub2) + - 검증완료(sub2 집계표 몫) — 분류1 · 2 · 3 더하기 · 4층 · 줄 넷 · 카드 셋 · 복사 가져오기 · 알약 연결 · 풀기 · 저장 · 다시 열기(ORCA 5177) · 남음: 식 지움으로 test_m02_table_fill 둘 깨짐(sub4 서버 채우기) +- [x] 스프레드시트 알약 막대(sub7 · 연결 = Sheet.연결) · 단위 기호 고르기(sub8 · 74b6937b) · 알약 목록 fetchComboInputs · searchMaterials(sub3 · 32d73aa2 · ba91e9e6) +- 검증완료(sub7 알약): createSpreadsheet({links: {항목, 모드, 자재찾기}}) · 계약 Sheet.연결(시트에 둠 — 되돌림이 시트통째 한 길) · 시험 틀 harness_links 로 ORCA — 연결 · 흐림 · 주소 · 칸 세모 · 이동 · 우클릭 풀기 · 되돌리기/다시 · 행 넣기 밀림 · 열 지우기 빠짐 · 자재 찾기 · 고르기 · 연결 · getLinkedValues(식 칸 37.5) · 크기 화면 28,518 B +- 검증완료(13 · 브레인 · 메인 5173 · 사용자처럼): 좌측 SD · SA · SM 각 47 · 8자리 번호 맨 앞 · 제목 4층 · 줄 = 일위대가 · 도면 · 면적산출 · 재료산출 · 분류2/3/1 더하기 → SD02010600 · 02010601 · 02020000 + 파일 셋 · 열 지움 → 파일 셋 지움 · 면적 알약 10(UA000004) 연결 · 저장 · 다시 열기 · 풀기 · 재료 자재 찾기 · 연결 · 서버 계산값 · 단위 기호 · 테마 · 시트 탭 없음 · 복사 가져오기 · CAD 오류 0 · 전역 싱크 트리 2b125933(데스크탑 메인만 엑셀 잠금 파일로 대기) +- 검증완료(9-28 오후 · 브레인): ₩ 원인 = 엑셀 형식 \; 잘못 잘라 끝 역슬래시 → 굴림이 ₩ 로 그림 · 고침(e0f9df9b · 41a717f6 · c5ad429d) · 사용자 재료산출 표 ₩ 0칸 · 사용자 실제 복사본(87390278)엔 수식 없음 확인 · 크기 한도 128 KB · WEBCLIP(AutoLISP · 웹 Ctrl+V DXF · 흐린 안내) 브레인 붙여넣기 1→3 · 도면층 생김 · 되돌림 · 전역 싱크 트리 92ad16a8(랩탑 9 · 데스크탑 메인 · 보조 · 코덱스) +- [x] 사용자 확정: 엑셀 · 오토캐드에 파일 설치 안 함 → 엑셀 = 수식 복사 안내(흐린 글 · 알림)(sub8) · 오토캐드 = WBLOCK / DXFOUT 파일 끌어다 놓기 · LISP 지움 · 안내 바꿈(sub1) +- [x] 사용자 지시: 분류 없는 열도 제목 4칸(빈값 = 선택 없음) · 면적산출 · 재료산출 카드 2열 · 도면 정보 안 열림 원인(sub2) +- 검증완료(9-28 14:55 · 브레인): 분류 없는 열 제목 4칸(소단 rowspan 1) · 카드 = 도면 정보 한 줄 + 면적 · 재료 2열 · c4 · pv_b · sg 도면 정보 CAD 뜸 · CAD 편집 오른쪽 위 「오토캐드에서 WBLOCK(또는 DXFOUT)으로 저장 → 파일을 여기로 끌어다 놓기」 · 사용자 열 c2~c5 · rv 면적산출 표 살림(충돌 ec8812e8) · 전역 싱크 트리 eeb12002(랩탑 9 · 데스크탑 메인 · 보조 · 코덱스) +- [x] 시험이 실제 마스터 양식을 읽어 사용자 편집(rv 제목 바꿈)에 깨짐(test_m02_structure 5 · test_spreadsheet_store 1) — 시험을 견본 양식으로 떼기(sub4) + +## 14. CAD · 스프레드시트 다듬기 · 구조물 조합 표 (담당 sub4 · sub1 · sub6) + +- 사용자 확정: 집계표 단위 줄 정의 보류 · 일위대가 입력값 = 엑셀 칸 연결(이미 됨) · 마스터 집계표 = 프로젝트 복사 때 쓸 기본 정보 · 로직 +- [x] 미리보기 CAD 특성 · 도면층 패널 숨김 원복 → 처음 접힘(panelsCollapsed) · 리본 펼친 뒤 밖을 누르면 접힘(sub4) +- [x] CAD 어두운 테마 자동색(7 · 검정 → 흰) · 바탕 · 격자 · 글 점검(sub1) / 스프레드시트 자동색(검정 글 · 선 · 흰 채움 → 테마)(sub6) +- [x] 사용자 확정: 「구조물집계표 조합」 = 집계표 열마다 한 줄(프론트엔드 그대로 · 서버가 집계표 저장 때 맞춤) · 브레인 판정: 키 SS000001~ · 파일 resources/master_template/구조물집계표조합.json · 구조물 열만(sub4 · 앞 일 뒤 이어서) +- [x] 좌측 패널 손잡이 본문과 안 겹치게(sub3) · CAD 가운데 단추 팬 때 페이지 안 움직임 · 미리보기 끌기 = 화면 옮기기만 · 안내 글 숨김(sub1) +- [x] 사용자 지시: [새로] · [본떠 만들기] · [지우기] 를 표 양식 · 도면 양식 컨테이너 안으로 · 본떠 만들기 기능 재확인(sub4 · 흠 셋 고침) +- [x] 사용자 지시: 구조물 도면(카드 · 편집)에서 작도 영역 없앰 · 도면 양식은 그대로(sub1) +- 검증완료(9-28 17:2x · 브레인 · 사용자 주소): 좌측 단추 = 표 · 도면 양식 컨테이너 안(밖 0) · 구조물 카드 작도 영역 없음 · CAD 뜸 · 카드 높이 561 셋 같음 · 메인 스크롤 없음(1024/1024) · 0,0 기준점은 sub1 시험 5 통과(화면 캡처는 ORCA 실패 — 사용자 눈 확인 거리) · 전역 싱크 트리 6153a6a7 + +## 15. 조합 · 자재 목록 고치기 · 소형 단추 · 머리 글 (담당 sub2 · sub3 검토) + +- 사용자 확정: 같은 조합을 여러 열이 쓸 수도 · 안 쓸 수도 · 갈라 낸 조합을 또 고쳐도 매번 새로 · 새 이름 = 자동 + 모달에서 고침 · 키는 자동(UA · UM + 번호 · 본뜬키 칸으로 계보) · 새 조합은 M01 목록에도 보임 +- 사용자 확정: 자재 목록 = 마스터 표 「자재 조합」(resources/master_data/자재조합.json · UM) · 고치면 일위대가처럼 모달 후 갈라 냄 · 분류 더하기로 만든 열은 일위대가 · 자재조합 · 도면 · 면적 · 재료가 원본을 따라감(뒤 고침은 따로) · 연결된 알약을 누르면 칸 하이라이트 +- 사용자 요구: 면적산출에서 일위대가 조합(담은 로직) 고치기 · 재료산출에 자재 목록 표(더하기 · 빼기) · 고치면 모달로 「새 조합을 만듦」 알림 후 새로 만듦(자재 목록도) — 검토 먼저 +- [x] 검토 — C:/Aislo_wt/sub_laptop_3/tmp/M02_분석/14_조합편집검토.md(sub3) → 사용자 판단 대기 +- [x] 공용 소형 단추 size sm · M02 카드 · 좌측 컨테이너 · M01 표 안 단추 적용 · 상세 화면 번호 + 이름 잘림 없앰(sub2) — 검증완료(ff0d9775 · 검증 대기) +- [x] M01: 알약 키 이름|단위 · 본뜬키 칸 · 자재 조합 표(UM) · 화면 · 지우기 전 쓰는 열 검사(sub3) / M02 서버: branchCombo · branchMaterialCombo · 분류 더하기 따라가기(sub4) / 알약 막대: 자재 항목 호스트 콜백 · 연결 칸 하이라이트(sub7) / 화면: 조합 줄 · [조합 고치기] · 자재 목록 옆 판 · 모달(sub2) — 검증완료(sub3 b6e71c72 · 4c8ee682 · sub4 e76e6380 · 본뜬키 충돌 풂 625c1012 · sub2 e06d43a6 · 검증 대기) +- [x] 스프레드시트 메뉴 어두운 테마 검정 글 — 전수 대비 측정 · 테마 토큰(sub6) — 검증완료(2d70be32 · 브레인 재측정 대비 3 미만 0건 · 검증 대기) +- 검증완료(15 · 브레인 · 5177 IP · 사용자처럼): 조합 고치기 모달 [취소] 무변경 · [새로 만듦] UA000038(본뜬 UA000009 · 이 열만 · 옆 열 그대로) · 같은 내용 되돌림 UA000009 재사용 · 자재 조합 UM000001 → 두 번째 고침 UM000002(본뜬 UM000001) → 되돌림 재사용 · 이은 자재 빼기 확인창 · 저장 · 다시 열기 · 분류 더하기 c6 = UA · UM · SD/SA/SM 복사(연결 B3 포함) · 열 지우기 파일 셋 삭제 · SS 번호 재사용 없음 · M01 본뜬키 열 · 자재 조합 목록 · 쓰는 조합 지우기 409 · 알약 반짝임 칸 일치(B3 · O6) · 어두운 테마 메뉴 0건 · 흠 넷 → 아래 +- [x] 15장 검증 흠: 옛 알약 연결 키(c4 면적산출 3개) 이름|단위로 맞춤 · 옆 판 이은 칸 즉시 갱신 · 새 조합 이름 겹침 · 첫 자재 조합 모달 글(sub2) — 검증완료(fb5aee7e · 브레인 재검증: 메인 c4 알약 10 · 연결 3 제 자리 · 따로 붙음 0 · 이음 · 풂 즉시 · 이름 「돌붙임 찰붙임」 · 첫 모달 「이 열에 새 자재 조합을 만듦」 · 전역 트리 fc58e8a1 · 검증 대기) +- [x] 진짜 마우스 검증에서 사진으로만 보인 흠: 공용 창(ui-modal) 뒤 막 투명 · 그림자 없음(확인 창처럼 어둡게) · 표시 형식 칸 흐린 글(sub2) — 검증완료(527e68db · 브레인 진짜 마우스 사진: 겹친 창 밝은 · 어두운 테마 또렷 · 흐린 글 「표시 형식」 · 전역 트리 d22732b6 · 검증 대기) + +## 16. 전체 점검 · 개선 · 최종 검증 (M02 마스터 템플릿 + M01 조합 · 자재 조합) + +- 사용자 지시: 전체를 다 개선하고 브레인이 최종 검증. +- 방식: 영역마다 전수 목록(기능 + 예외: 빈 값 · 같은 이름 · 긴 이름 · 취소 · 저장 안 하고 떠나기 · 다시 열기 · 쓰는 것 지우기 · 밝은/어두운 테마 · IP 주소) → 진짜 마우스 · 사진 판정 → 제 몫 흠은 고치고 목록 전체 재실행 · 남의 몫은 보고 → 사용자 열 저장 안 함 · 만든 것 되돌림. +- 사용자 조건: M01 로직(단가산출 로직 · 일위대가 계산 · 조합의 뜻)은 바꾸지 않음 — M01 은 화면 흠만 · 로직 흠은 보고 +- [x] 구조물집계표 · 좌측 패널 새로 · 본떠 만들기 · 지우기 · 번호(sub4) — 검증완료(6606ef16 쓰는 양식 지우기 막음 · 16de9eed 지운 열 번호 재사용 막음 · 이름 창 커서 · Enter · 바인딩 열 손 입력 끔 · f8dda99f 도움말 대비 6.3 · 검증 대기) +- [x] 쓰는 양식 지우기 — 확인 창 전에 바로 거절 알림(sub5) — 검증완료(214c55a3 · 방위표 · 구조물집계표 알림만 · 안 쓰는 양식은 확인 창 뒤 지움 · 검증 대기) · 자재 조합 지우기도 같게(sub7 몫) +- [x] 구조물 도면 · 도면 양식 CAD(sub1) — 목록 28 · 흠 넷 고침: 값 없는 테마가 호스트(밝음)와 어긋나 CAD 만 어두움 · 첫 표시 뒤 iframe 이 커져 맞춤 어긋남(빈 도면은 기준점이 귀퉁이) · 도면 양식 📂 불러온 뒤 떠날 때 안 물음 · 작도 영역 빈 칸 안내 글 — 검증완료(IP · localhost · 밝음 · 어두움 전 목록 재실행 · 검증 대기) +- [x] 좌측 목록 두 줄 동시 선택 표시(sub5) — 검증완료(990e8e67 · 무엇으로 열든 연 직후 좌측 표시를 한 곳에서 다시 그림 · 17걸음 선택 줄 1 · 검증 대기) +- [x] 면적산출 · 재료산출 화면 · 조합 고치기 · 알약 · 자재 옆 판(sub2) — 검증완료(841af0a0 · 목록 50 · 흠 8 고침 · 검증 대기) +- [x] 조합 고치기 차례 · 메모도 내용으로(바뀌면 새 조합) · 거절 이유 글 그대로(sub2) — 검증완료(e4e2dc2a · 60246df6 떠나기 [취소] 초점 · 검증 대기) +- [x] 도구 막대 ✕ · 안내 · 확대 글 대비(sub6 — 검증완료 7389335c · 밝은 5.88 · 어두운 7.91 · 검증 대기) · 공용 확인 창 닫힘 뒤 초점 되돌림(sub2) — 로직 고르기 css 자체 로드는 sub3 검증완료 · 공용 확인 창 초점 검증완료(ee0475d0) + - [x] sub2 몫 — 같은 내용 = 차례 · 메모까지(자재는 차례) · 되돌리면 원래 키 · 거절 까닭 알림에 그대로 · 자동 이름 끝 (n) 뗌 — 검증완료(IP 차례만 · 메모만 · 되돌림 UA000009 · 422 셋 · 검증 대기) + - [x] 떠나기 확인 창 [취소] 뒤 초점을 시트 · 집계표 · 도면으로 되돌림(sub2 · sub8 발견) — 검증완료(IP 면적산출 시트 H13 · 집계표 · 화살표 이동 · 검증 대기) + - [x] 공용 확인 창 — 열 때 초점 기억 · 취소 · 확인 · Esc 로 닫히면 되돌림 · 열리면 [취소]에 초점(sub2) — 검증완료(IP M02 조합 고치기 · 떠나기 · M01 조합 뒤로 · B01 새 프로젝트 · Esc 는 확인 창만 닫음 · 검증 대기) + - [x] 조합 적용 알림 — 새로 만듦 / 같은 내용의 있는 조합 씀을 키 · 이름과 함께 · 첫 안내에 「같은 내용이 있으면 그것을 씀」(sub2) — 검증완료(IP 자재 새로 UM000001 · 다른 열 같은 내용 다시 씀 · 일위대가 되돌림 UA000009 · 검증 대기) +- [x] M01 일위대가 조합 · 자재 조합 화면(sub3) — 검증완료(ae47bcd9 · 화면 파일만 · 찾기 초점 · Enter · 떠나기 확인 · 본뜬키 칸 · 표 폭 · 금액 오른쪽 · 알림 글 · 밝은 테마 흐린 글 · 이름 60자 · 지우기 창에 키 · 이름 · 남김: 판 충돌 파일 단위 · 좌측 머리 접힘은 M01 규칙이라 그대로 · 검증 대기) +- [x] 공용 입력칸 · 고르기칸 높이 맞춤(sub3) — 검증완료(M01 미리 보기 32쌍 30px · 로직 계산 30 · 서식 창 28 · B01 날짜 42 · 밝은 · 어두운 같음 · 검증 대기) +- [x] 스프레드시트 도구 막대 · 메뉴 · 대화 창(sub6) — 검증완료(7389335c · 목록 80 · 밝은/어두운 80/80 · 검증 대기) +- [x] 스프레드시트 칸 다루기 · 식 · 복사 · 채우기 · 되돌리기(sub8) — 검증완료(52d441e3 · 흠 11 · 고름 테두리 · 채우기 손잡이 · 틀 고정 빈 띠 · 입력 형식 · 잘라 붙이기 · 어두운 테마 오류 글 · 저장본 102권 서버 재계산 같음 시험 · 검증 대기) +- [x] 긴 글 옆 빈 칸으로 넘쳐 보이기(sub8) — 검증완료(cd269045 · 수치 17 · 남김: 왼쪽 밖으로 스크롤된 칸의 넘침은 안 보임) · 떠나기 창 [취소] 뒤 초점(sub2 60246df6) +- [x] 데이터 메뉴 자리 · 머리 우클릭 잘라내기 · 붙여넣기(sub6) — 검증완료(7389335c · 검증 대기) +- [x] 사용자 지시: 자재 조합 = 마스터 템플릿 표(resources/master_template/자재조합.json · 번호는 표 안 「다음」) · M02 좌측 「자재 조합」 컨테이너(목록 · 상세 · 새로 · 본떠 만들기 · 지우기 · 쓰는 열 막기) · M01 화면 · 길에서 뺌 · 일위대가 조합은 그대로(sub7) — 검증완료(sub7 · 새로 · 이름 · 자재 빼기 · 본떠 만들기 · 찾기 · 쓰는 조합 바로 거절 · 옆 판 갈라 낸 UM 이 목록에 · 담는 자재는 자재 마스터 코드만 · 프로젝트는 복사 안 하고 마스터 키를 가리킴 · 검증 대기) +- [x] 최종 검증 흠: 자재 조합 새로 · 본떠 만들기 이름 칸 초점(sub7) · 첫 자재 조합 안내 「같은 내용이면 있는 조합」 · 새로 만듦/다시 씀 알림(sub2) · 집계표 F2 커서 끝(sub5) +- [x] 700줄 넘는 파일 나누기: spreadsheet_grid.ts 1052(sub8) · check_master.py 704(sub1) · 어두운 테마 기본 부품 color-scheme(sub3) +- [x] 전역 싱크 → 브레인 최종 검증(전체 목록 · 진짜 마우스 · 사진) → 보고 — 검증완료(12곳 트리 bedfcf83 · CAD · 스프레드시트 빌드 두 PC · 영역 9 통과 · https://claude.ai/artifact/FmWV9uoQiFBpDjQfY2FvPo) + +## 17. 구조물 집계 양식 한 묶음 · 좌측 패널 재구성 (담당 sub2 · sub4) + +- 확정: 구조물집계표 열(SS 키) = 일위대가(UA) · 자재조합(UM) · 도면(SD) · 면적산출(SA) · 재료산출(SM) 한 벌 — 구조물집계표조합.json 줄마다 다섯 칸 · 자재 조합 = 재료산출 알약에 쓴 자재 목록(master_template/자재조합.json). +- [x] 집계표 도구 막대에서 「손 입력 열」 · 「줄 더하기」 · 「줄 지우기」 단추 삭제(데이터 규칙은 그대로)(sub4) +- [x] 좌측: 「표 양식」 → 「구조물 집계 양식」 이름 바꿈 · 새로 · 본떠 만들기 · 지우기 단추 삭제 · 그 아래에 구조물 도면 · 면적산출 · 재료산출 · 자재 조합 컨테이너를 하위로 넣음(도면 양식은 그대로 별도)(sub2) — 검증완료(IP 집계표 줄 하나 · 하위 넷 들여 8px · 같이 접힘 · 밝음 · 어두움 잘림 · 대비 흠 없음 · 검증 대기) +- [x] M02 첫 화면 = 구조물집계표(왼쪽 고르라는 빈 화면 대신)(sub2) — 검증완료(들어오면 집계표 · 좌측 고름 집계표 줄 · 자재 조합 지운 뒤도 집계표 · 검증 대기) +- [x] 카드 [수정]으로 연 상세(도면 · 면적산출 · 재료산출)의 왼쪽 위 제목 라벨 오른쪽에 [돌아가기] — 누르면 구조물집계표로 돌아가고 그 열이 고른 채(저장 안 한 고침이면 확인 창)(sub4) +- [x] 면적산출 · 재료산출 편집 화면의 알약 막대를 시트 아래(하단)로 옮김(sub7) +- [x] 면적산출 · 재료산출 편집 화면 오른쪽 안쪽 패널(B05 배수유역 패널처럼 끌어서 폭 조절) — 면적산출은 일위대가 조합 목록 · 재료산출은 자재 조합 목록 · 이 열의 조합 바꾸기(고르기) · 조합 고치기(갈라 내기) 를 패널 안에서(sub2) — 검증완료(공용 사이드바 카드 · 접기 · 폭 손잡이 · 접기 손잡이 그대로 · 폭 384→503 · 최소 280 · 최대 60% · 새로고침 뒤 폭 기억 · 겹침 0 · 페이지 스크롤 0 · h_rip_c UA000009→UA000011→UA000009 · 자재 UM000001 · 이름|단위 맞는 이은 칸 유지 · 밝음 · 어두움 대비 흠 없음 · 검증 대기) +- 검증완료(17 · 브레인 · 메인 5173 · 진짜 마우스 · 사진 v1 · v2): 첫 화면 집계표 · 좌측 구조물 집계 양식 하위 넷 · 도구 막대 단추 넷만 · 상세 [돌아가기] → 집계표 h_rip_c 고른 채 카드 · 알약 막대 시트 아래(y 988 · 스크롤 0) · 오른쪽 패널(이 열의 조합 · 조합 목록 · 손잡이 · 폭 384) · 흠 1: [이 열에 쓰기] 단추가 목록 끝 글과 겹침 → sub2 — 고침 071676e0(고른 줄 아래 · 겹침 0) + +## 18. 계획노선(B04 평면도) 다듬기 (담당 dmain · dsub) + +- [x] 종단 기울기 라벨 — 줌인해 측점 하나가 보이는 크기부터 측점과 측점 사이에 기울기(%) 라벨(dmain) — 검증완료(자리 = B05 계획노선 편집 창 · 지반고 기울기 · 측점 간격 90px 이상에서만 · 6.12px/m 에서 화면 안 11개 · 30° 회전에서 12개 · 글 바로 섬 · 2.66px/m 에서 0개 · 검증 대기) +- [x] 등고선 1m 간격 보이기 토글 단추 · 5m 간격 선은 색을 조금 짙게(dmain) — 검증완료(「1m 등고선」 단추 기본 꺼짐 · LAS 없으면 잠김 · 켬 117,489px 바뀜 · 끔 0px 차이 · 5m 선 #818cf8 · 1m 선 0.5px 옅게 · 5m 선이 솎이지 않는 배율에서만 · 검증 대기) +- [x] 계획노선 회전 값 저장 — 다시 들어오면 보던 화면(회전 · 위치 · 줌)으로(dmain) — 검증완료(화면 취향 `routeedit-view` 프로젝트 칸 · 계정에도 올라감 · 다시 열기 가운데 · 6.12px/m 그대로 · 30° 그대로 · 창 크기 달라도 같은 자리 시험 · 검증 대기) +- [x] 거리 · 기울기 측정을 예정 노선에서도(dsub) — 검증완료(자리 = B05 계획노선 편집 창 · 데스크탑_보조 · 예상노선 28+11.4→29+19.7 28.3m · 15.2% · 초록 구간이 점선을 따라감 · 계획노선 23+12.6→29+11.0 118.5m · −5.0% · 검증 대기) +- [x] 횡단도 보기 선택 표시(노선 위 작은 선)에 라벨 추가(dsub) — 검증완료(측점 눈금 = 라벨 11/11 · 27/27 · 되꺾인 노선 61/61 · 비켜 놓음 28 · 겹침 0 · 공용 `drawStationTicks` 라 B04 지도 · 배수유역도도 같음 · 검증 대기) +- [x] 횡단도 보기 항목의 좌우 횡단 반폭만큼 평면도에 선 — 노선이 바뀌면 횡단 보기 켜진 동안 실시간으로 따라감(dsub) — 검증완료(반폭 = 사면이 원지반과 만나는 좌·우 · 못 만나면 샘플 반폭 · 측점 틀 파이썬·TS 짝 + 거울 시험 · 서버 샘플 좌표와 차 ≤5e-7m(측점 5곳) · 노드 끄는 중 선 (527,491)→(519,547) · 놓으면 반폭 재계산 · 되돌리기 · 취소 뒤 노선 그대로 · 검증 대기) + +## 19. 종단 저장 정합 — 사용자가 고른 값이 저장 · 다시 열기 뒤 그대로 (담당 sub5 · sub6 · sub8) + +- 사용자 확정: 저장 뒤 다시 들어왔을 때 프론트에서 사용자가 고른 정보가 그대로여야 함 · B05·B06 은 한 페이지처럼 동작. 측점 기준선 통일(계획노선 CSV vs B05 경로)은 따로 대화. +- 읽기 조사 결과: 시설 속성 붙이기(attach · carry_facility_attributes)가 거리 제한 없이 가장 가까운 관에 붙어 한 칸 밀림 · 정수 m 격자 붙이기 · 잔여거리 0.1m 표시값 재읽기로 측점 재저장 · 사용자 배치(manual) 서버 미존중 · 옛 관 이동 예약(culvertmove) 저장 때 적용. +- [x] 시설 속성 붙이기 = 측점이 같은(허용 오차 안) 관에만 · 화면(B05_Profile_UI_Drainage_Facility.ts attach)과 서버(common_util_drainage_pipes.py carry_facility_attributes) 같은 규칙 · 옛 culvertmove 적용 제거(sub5) — 검증완료(sub5 · attach 같은 측점만 · 관 옮기면 속성 따라감 · culvertmove 제거 · 시험 97 · 검증 대기) +- [x] 옵션 · 메모만 바꿀 때 측점 재쓰기 금지(Structures_Fields · Panel_Commit) · 사용자가 놓은 구조물(manual)은 격자 붙이기 · 반올림에서 제외 · 비정규 측점 키 바뀜으로 station_offsets 지워지지 않게(sub6) — 검증완료(cc3a8a71 · aecf04ac · 격자 붙이기는 브레인 판정 (다)로 그대로 · 검증 대기) +- [x] 왕복 시험: 환경 배정 storage 프로젝트에서 B05 · B06 값(관 이동 · 구조물 측점 · 옵션 · 메모 · 계획고 수정)을 바꿔 저장 → 다시 열기 → 파일(pipe_points.json · structures.json · 종단) · 화면값 대조 시험 · sub5 · sub6 push 뒤 돌려 0 차이(sub8) + From 4e7e9a6ac291ada7639056d47901b011d97b7c69 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 29 Sep 2026 11:41:35 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat(B05):=20=EC=B4=88=EA=B8=B0=EB=85=B8?= =?UTF-8?q?=EC=84=A0=20=EA=B0=88=EB=9E=98=20=EA=B3=B5=ED=86=B5=20=EC=A0=9C?= =?UTF-8?q?=EC=95=BD=20=EC=B9=B8=20=E2=80=94=20=EC=A2=85=EB=8B=A8=EA=B8=B0?= =?UTF-8?q?=EC=9A=B8=EA=B8=B0=20=C2=B7=20=EC=B5=9C=EC=86=8C=EB=B0=98?= =?UTF-8?q?=EC=A7=80=EB=A6=84=20=C2=B7=20=EA=B3=84=EB=A5=98=EC=9D=B4?= =?UTF-8?q?=EA=B2=A9=20=C2=B7=20=EC=A7=80=EB=B0=98=EA=B3=A0=EB=A6=84=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 공통 제약 기준값 확정(9-29 사용자 결정) — 후보 표 임시값 그대로 기본값, 화면에서 프로젝트마다 바꿀 수 있음 - 로직 단추 옆 제약 네 칸(비우면 placeholder 로 기본값) · 채운 칸만 POST /route/initial 몸통 criteria 로 실어 보냄 - [확인]의 initial.criteria 에도 실어 성공하면 initial_choice.json 에 겹친 칸만 남김(빈 칸은 기본값이라 안 담김) - 다시 열 때 current_initial.criteria 로 그 칸만 복원 · 캐시가 서버 갈래·비중·제약과 다르면 버림(sameBase) - 서버: generate_initial_route/_SearchGrid 가 stream_offset_m·grade_smooth_m 겹침을 받아 탐색·비교값에 반영 - 응답 criteria.pending 은 이제 늘 빈 목록 — 화면의 「후보 · 사용자 확정 대기」 글 삭제 시험: test_route_initial(+2) · test_b05_routeedit_initial(criteria 겹침·확정 표시 삭제) · test_b05_routeedit_initial_roundtrip(+1, 사본에서 계류 이격 30m 겹침 왕복) Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015sCeZz8z8YnbovsKgn7HK7 --- B05_Profile/B05_Profile_Api_RouteInitial.ts | 12 +- .../B05_Profile_Engine_RouteInitial.py | 82 ++++++--- ...B05_Profile_Engine_RouteInitial_Metrics.py | 11 +- B05_Profile/B05_Profile_Router_Replan.py | 25 +++ .../B05_Profile_UI_RouteEdit_Initial.ts | 115 ++++++++++++- .../B05_Profile_UI_RouteEdit_Initial_Model.ts | 69 ++++++-- .../B05_Profile_UI_Style_RouteEdit.css | 29 ++++ .../tester/helper_b05_routeedit_initial.cjs | 155 +++++++++--------- .../helper_b05_routeedit_initial_apply.cjs | 18 +- .../tester/test_b05_routeedit_initial.py | 34 ++-- .../test_b05_routeedit_initial_roundtrip.py | 40 ++++- resources/tester/test_route_initial.py | 72 +++++++- 12 files changed, 512 insertions(+), 150 deletions(-) diff --git a/B05_Profile/B05_Profile_Api_RouteInitial.ts b/B05_Profile/B05_Profile_Api_RouteInitial.ts index be108a83..7f8b6653 100644 --- a/B05_Profile/B05_Profile_Api_RouteInitial.ts +++ b/B05_Profile/B05_Profile_Api_RouteInitial.ts @@ -2,7 +2,7 @@ * B05_Profile_Api_RouteInitial.ts * 초기노선 갈래 요청(계획서 20) — 로직 하나를 골라 서버가 새 계획노선 노드를 낸다. * - * POST /projects/{id}/route/initial { mode, weights? } → 노드 · 비교값 · 공통 제약 기준값 + * POST /projects/{id}/route/initial { mode, weights?, criteria? } → 노드 · 비교값 · 공통 제약 기준값 * * 계산 자리는 **서버 단독**(CLAUDE.md 5장 ③) — 화면은 부르고, 받은 노드를 편집 캐시에 싣고, * 받은 비교값·기준값을 보이기만 한다. 모양은 계획서 20 「API」 줄(서버 `initial_route`) — @@ -24,9 +24,19 @@ export interface RouteInitialWeights { grade: number; } +/** 공통 제약 겹침 값 — 화면 네 칸(계획서 20 확정). 비우면 서버가 프로젝트 기본값을 쓴다. + * 이름은 서버 몸통과 그대로(snake_case) — 변환 없이 그대로 실어 보낸다. */ +export interface RouteInitialCriteria { + max_grade_pct?: number; + min_radius_m?: number; + stream_offset_m?: number; + grade_smooth_m?: number; +} + export interface RouteInitialRequest { mode: RouteInitialLogic; weights?: RouteInitialWeights; + criteria?: RouteInitialCriteria; } /** 서버 길이 아직 없다(404·405) — 화면은 이 글로 알린다. */ diff --git a/B05_Profile/B05_Profile_Engine_RouteInitial.py b/B05_Profile/B05_Profile_Engine_RouteInitial.py index 5def0cb3..d6340207 100644 --- a/B05_Profile/B05_Profile_Engine_RouteInitial.py +++ b/B05_Profile/B05_Profile_Engine_RouteInitial.py @@ -23,9 +23,12 @@ · 그래도 길이 없을 때만(지형상 불가능) 벌점 탐색으로 가장 적게 넘긴 길을 내고, 넘긴 곳을 `metrics.violations`(측점 · 값 · 사유)에 적음. `over_limit` 수 = 그 목록 수. -⚠ 사용자 확정 대기 — 아래 임시값과 호출부가 넘기는 종단기울기 상한 · 최소 곡선반지름은 지식DB -(`resources/knowledge/technical_info/01_임도`) 후보일 뿐이다(PLAN 20장 기준값 후보 표). -확정되면 이 머리의 상수만 고친다. +기준값 확정(2026-09-29 사용자 결정, PLAN 20장) — 아래 임시값이 그대로 **기본값**이 됐다 +(지식DB `resources/knowledge/technical_info/01_임도` 후보 표 그대로). 종단기울기 상한 · +최소 곡선반지름은 여전히 호출부(`_road_settings`·`_plan_criteria`)가 프로젝트별로 넘기고, +계류 이격 · 기울기 잴 지반 폭은 이 머리의 상수가 기본값이다 — 넷 모두 화면(`RouteInitialCriteria`) +에서 프로젝트마다 겹쳐 쓸 수 있다(`generate_initial_route` 의 `stream_offset_m`·`grade_smooth_m` +인자, 라우터의 `max_grade`·`min_radius_m` 대입). 응답 `criteria.pending` 은 이제 늘 빈 목록이다. """ from __future__ import annotations @@ -58,14 +61,10 @@ STREAM_CLASSES = ("소하천", "지방하천", "국가하천") ROAD_WIDTH_M = 4.0 # 탐색 복도 반폭(m) — 예상노선에서 이보다 멀리는 가지 않음(법령 값 아님 · 알고리즘 값). CORRIDOR_M = 100.0 -PENDING = ( - "max_grade_pct", - "min_radius_m", - "stream_offset_m", - "road_width_m", - "corridor_m", - "grade_smooth_m", -) +# 기준값 확정(2026-09-29 사용자 결정) — 예전엔 여기 여섯 칸이 「사용자 확정 대기」였다. +# 이제 후보 표 값 그대로 기본값으로 굳었고(화면에서 프로젝트마다 바꿀 수 있음), 빈 목록이 곧 +# 「확정됨」이다(`criteria.pending`). +PENDING: tuple[str, ...] = () # ── 탐색 · 측정 값 ─────────────────────────────────────────────────────────────── MODES = ("follow", "earthwork", "gentle", "weighted") @@ -148,20 +147,40 @@ def _choice_path(project_root: Path) -> Path: def read_initial_choice(project_root: Path) -> dict[str, Any]: - """현재 초기값(갈래 · 비중) — [확인] 때 적어 둔 것. 없거나 못 읽으면 추종.""" + """현재 초기값(갈래 · 비중 · 제약 겹침) — [확인] 때 적어 둔 것. 없거나 못 읽으면 추종. + + `criteria` 는 **사용자가 겹쳐 쓴 값만**(비운 칸은 안 담김) — 나머지는 프로젝트 기본값이라 + 화면이 다시 열 때 겹친 칸만 채우고 나머지는 placeholder(기본값)로 비워 둔다. + """ try: data = json.loads(_choice_path(project_root).read_text(encoding="utf-8")) logic = data["logic"] if data.get("logic") in MODES else "follow" - return {"logic": logic, "weights": resolve_weights(logic, data.get("weights"))} + criteria = data.get("criteria") + return { + "logic": logic, + "weights": resolve_weights(logic, data.get("weights")), + "criteria": criteria if isinstance(criteria, dict) and criteria else None, + } except (OSError, ValueError, KeyError, TypeError, AttributeError): - return {"logic": "follow", "weights": resolve_weights("follow", None)} + return {"logic": "follow", "weights": resolve_weights("follow", None), "criteria": None} def write_initial_choice( - project_root: Path, logic: str, weights: dict[str, float] | None + project_root: Path, + logic: str, + weights: dict[str, float] | None, + criteria: dict[str, float] | None = None, ) -> dict[str, Any]: - """[확인] 때만 부름 — 작업본 한 칸. 다시 열기 · 다른 PC 에서도 같은 초기값.""" - choice = {"logic": logic, "weights": resolve_weights(logic, weights)} + """[확인] 때만 부름 — 작업본 한 칸. 다시 열기 · 다른 PC 에서도 같은 초기값. + + `criteria` 는 사용자가 화면 칸에 직접 넣은 겹침 값만(빈 칸은 담지 않는다) — 프로젝트 + 기본값이 나중에 바뀌어도 사용자가 안 만진 칸은 그 새 기본값을 그대로 받는다. + """ + choice = { + "logic": logic, + "weights": resolve_weights(logic, weights), + "criteria": criteria or None, + } path = _choice_path(project_root) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(choice, ensure_ascii=False), encoding="utf-8") @@ -208,18 +227,27 @@ def generate_initial_route( weights: dict[str, float] | None, max_grade: float, min_radius_m: float, + stream_offset_m: float | None = None, + grade_smooth_m: float | None = None, ) -> dict[str, Any]: """갈래 하나로 초기 계획노선을 만들고 비교값을 붙여 돌려준다. `max_grade` 는 비율(0.14). 공통 제약을 넘지 않는 길을 먼저 찾고(탐색 상한을 `GRADE_CAP_STEPS` 차례로 낮춰 곡선 맞춤 뒤 지반 차까지 없앰), 그런 길이 없을 때만 벌점 탐색으로 가장 적게 넘긴 길을 낸다. + + `stream_offset_m`·`grade_smooth_m` 을 안 주면(None) 이 머리의 기본값을 쓴다 — 화면에서 + 프로젝트마다 겹쳐 쓸 수 있다(PLAN 20장 확정). """ if mode not in MODES: raise ValueError(f"모르는 갈래: {mode}") if len(expected) < 2: raise ValueError("예상노선 정점이 2개 미만입니다.") used = resolve_weights(mode, weights) - grid = _SearchGrid(expected, terrain, streams) + stream_offset = STREAM_OFFSET_M if stream_offset_m is None else float(stream_offset_m) + grade_smooth = GRADE_SMOOTH_M if grade_smooth_m is None else float(grade_smooth_m) + grid = _SearchGrid( + expected, terrain, streams, stream_offset_m=stream_offset, grade_smooth_m=grade_smooth + ) def attempt(hard_cap: float, bound: float | None) -> tuple[Any, dict[str, Any]]: nodes = _search_route( @@ -243,9 +271,10 @@ def generate_initial_route( outline.curves, max_grade=max_grade, min_radius_m=min_radius_m, - stream_offset_m=STREAM_OFFSET_M, + stream_offset_m=stream_offset, road_width_m=ROAD_WIDTH_M, bound_pct=None if bound is None else bound * 100.0, + grade_smooth_m=grade_smooth, ) return outline, metrics @@ -280,10 +309,11 @@ def generate_initial_route( "criteria": { "max_grade_pct": round(max_grade * 100.0, 2), "min_radius_m": round(min_radius_m, 2), - "stream_offset_m": STREAM_OFFSET_M, + "stream_offset_m": round(stream_offset, 2), "road_width_m": ROAD_WIDTH_M, "corridor_m": CORRIDOR_M, - "grade_smooth_m": GRADE_SMOOTH_M, + "grade_smooth_m": round(grade_smooth, 2), + # 기준값 확정(PLAN 20장, 2026-09-29) — 더는 후보가 아니다. 항상 빈 목록. "pending": list(PENDING), }, } @@ -323,11 +353,15 @@ class _SearchGrid: expected: list[tuple[float, float]], terrain: Terrain, streams: list[list[tuple[float, float]]], + *, + stream_offset_m: float = STREAM_OFFSET_M, + grade_smooth_m: float = GRADE_SMOOTH_M, ) -> None: step = max(1, int(round(SEARCH_CELL_M / _cell_size(terrain.x)))) self.xs, self.ys = terrain.x[::step], terrain.y[::step] # 기울기 · 옆경사는 비교값과 같은 고른 지반에서 잰다(`smoothed_ground`) - self.z = smoothed_ground(terrain)[::step, ::step] + self.z = smoothed_ground(terrain, grade_smooth_m)[::step, ::step] + self.stream_offset_m = stream_offset_m self.cell = _cell_size(self.xs) self.d_expected = _distance_grid([expected], self.xs, self.ys, self.cell) self.d_stream = _distance_grid(streams, self.xs, self.ys, self.cell) @@ -372,8 +406,8 @@ class _SearchGrid: norm = math.hypot(dr, dc) grade = np.abs(self.z[second] - self.z[first]) / (self.cell * norm) # 계류 버퍼 안에서는 계류 쪽 · 반대쪽 ±45° 로만(건너기 · 빠져나가기) - in_buffer = (self.d_stream[first] < STREAM_OFFSET_M) | ( - self.d_stream[second] < STREAM_OFFSET_M + in_buffer = (self.d_stream[first] < self.stream_offset_m) | ( + self.d_stream[second] < self.stream_offset_m ) ax = self.away_x[first] + self.away_x[second] ay = self.away_y[first] + self.away_y[second] diff --git a/B05_Profile/B05_Profile_Engine_RouteInitial_Metrics.py b/B05_Profile/B05_Profile_Engine_RouteInitial_Metrics.py index 518774a9..ddeb1fa2 100644 --- a/B05_Profile/B05_Profile_Engine_RouteInitial_Metrics.py +++ b/B05_Profile/B05_Profile_Engine_RouteInitial_Metrics.py @@ -33,12 +33,14 @@ REASON_STREAM = "계류 버퍼 안 — 건너기 · 기점 · 종점 봐줌을 REASON_STREAM_TERRAIN = "지형상 불가능 — 기울기 · 반지름을 지키며 계류 버퍼를 비켜 잇는 길 없음" -def smoothed_ground(terrain: Terrain) -> np.ndarray: - """종단기울기를 잴 지반(`GRADE_SMOOTH_M` 로 고른 DEM) — 탐색과 비교값이 같은 것을 본다.""" +def smoothed_ground(terrain: Terrain, grade_smooth_m: float = GRADE_SMOOTH_M) -> np.ndarray: + """종단기울기를 잴 지반(`grade_smooth_m` 로 고른 DEM) — 탐색과 비교값이 같은 것을 본다. + + 폭은 화면에서 바꿀 수 있다(PLAN 20장 확정 · 기본값 `GRADE_SMOOTH_M`).""" from scipy.ndimage import gaussian_filter spacing = 0.5 * (_spacing(terrain.x) + _spacing(terrain.y)) - return gaussian_filter(np.asarray(terrain.z, dtype=np.float64), GRADE_SMOOTH_M / spacing) + return gaussian_filter(np.asarray(terrain.z, dtype=np.float64), grade_smooth_m / spacing) def station_label(chainage_m: float) -> str: @@ -107,6 +109,7 @@ def route_metrics( stream_offset_m: float, road_width_m: float, bound_pct: float | None, + grade_smooth_m: float = GRADE_SMOOTH_M, ) -> dict[str, Any]: """비교값과 제약 못 지킨 구간 목록. 종단기울기 = 측점(20 m) 사이 고른 지반 기울기. @@ -115,7 +118,7 @@ def route_metrics( grid = (terrain.y, terrain.x) sampler = RegularGridInterpolator(grid, terrain.z, bounds_error=False, fill_value=None) smooth = RegularGridInterpolator( - grid, smoothed_ground(terrain), bounds_error=False, fill_value=None + grid, smoothed_ground(terrain, grade_smooth_m), bounds_error=False, fill_value=None ) points, chain = _resample(vertices, SAMPLE_M) length = float(chain[-1]) diff --git a/B05_Profile/B05_Profile_Router_Replan.py b/B05_Profile/B05_Profile_Router_Replan.py index 4ad1d17e..42edf351 100644 --- a/B05_Profile/B05_Profile_Router_Replan.py +++ b/B05_Profile/B05_Profile_Router_Replan.py @@ -94,6 +94,19 @@ class RouteInitialWeights(BaseModel): grade: float = Field(25.0, ge=0, le=100) +class RouteInitialCriteria(BaseModel): + """공통 제약 겹침 값 — 화면 네 칸(PLAN 20장 확정). 비우면(None) 프로젝트 기본값을 쓴다. + + 기본값(2026-09-29 사용자 결정 — 지식DB 후보 표 그대로 확정) — 종단기울기 상한 · 최소 + 곡선반지름은 프로젝트 설계속도 · 지형에 따른 표값, 계류 이격 100 m, 기울기 잴 지반 5 m 고름. + """ + + max_grade_pct: float | None = Field(None, ge=0, le=100) + min_radius_m: float | None = Field(None, ge=0) + stream_offset_m: float | None = Field(None, ge=0) + grade_smooth_m: float | None = Field(None, ge=0) + + class RouteInitialRequest(BaseModel): """초기 계획노선 갈래 — follow(예상노선 추종) · earthwork · gentle · weighted.""" @@ -101,6 +114,7 @@ class RouteInitialRequest(BaseModel): "follow", validation_alias=AliasChoices("logic", "mode") ) weights: RouteInitialWeights | None = None + criteria: RouteInitialCriteria | None = None class RouteReplanRequest(BaseModel): @@ -574,6 +588,7 @@ async def replan_route( project_root, choice.mode, choice.weights.model_dump() if choice.weights else None, + (choice.criteria.model_dump(exclude_none=True) or None) if choice.criteria else None, ) if choice else await asyncio.to_thread(read_initial_choice, project_root) @@ -648,6 +663,14 @@ async def initial_route( grade_class, design_speed, terrain_type = await _road_settings(project_id) # 비포장 기준 — 포장 예외(18 %)는 포장 여부가 정해진 뒤 종단 설계가 따진다(사용자 확정 대기). max_grade = legal_grade_limit_pct(grade_class, terrain_type, False, design_speed) / 100.0 + # 화면 제약 칸(PLAN 20장 확정) — 비운 칸은 위 프로젝트 기본값 그대로, 채운 칸만 겹쳐 쓴다. + criteria = request.criteria + if criteria and criteria.max_grade_pct is not None: + max_grade = criteria.max_grade_pct / 100.0 + if criteria and criteria.min_radius_m is not None: + radius_m = criteria.min_radius_m + stream_offset_m = criteria.stream_offset_m if criteria else None + grade_smooth_m = criteria.grade_smooth_m if criteria else None pool = get_db_pool() async with pool.acquire() as connection: selection = await get_surface_confirmation_params(connection, str(project_id)) @@ -666,6 +689,8 @@ async def initial_route( weights=weights, max_grade=max_grade, min_radius_m=radius_m, + stream_offset_m=stream_offset_m, + grade_smooth_m=grade_smooth_m, ) ) except (FileNotFoundError, ValueError) as error: diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Initial.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Initial.ts index 7affdc8a..04dd4c01 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Initial.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Initial.ts @@ -1,19 +1,25 @@ /* ============================================================================= * B05_Profile_UI_RouteEdit_Initial.ts - * 계획노선 편집 창의 **초기노선 갈래**(계획서 20) — 로직 단추 셋 · 비중 슬라이더 · [초기화] - * · 「현재 초기값」 · 결과 비교값 · 공통 제약 기준값. + * 계획노선 편집 창의 **초기노선 갈래**(계획서 20) — 로직 단추 셋 · 공통 제약 겹침 네 칸 · + * 비중 슬라이더 · [초기화] · 「현재 초기값」 · 결과 비교값 · 공통 제약 기준값. * * 로직 단추를 누르면 서버가 노선을 새로 셈하고(`/route/initial`), 받은 노드를 **편집값에 * 싣는다**(되돌리기 한 걸음). 그 결과가 새 초기값이라 캐시(`routeedit-initial`)에 적고, * [초기화]는 서버를 다시 부르지 않고 그 초기값으로 돌아간다. 종단 · 배수유역 · 횡단 다시 * 계산은 여기서 안 돈다 — 노선이 바뀐 뒤 [확인]에서만(`_Apply`). * 비교값 · 기준값은 서버가 준 것을 보이기만 한다(계산 자리 = 서버 단독). + * + * 공통 제약 네 칸(종단기울기 % · 최소 반지름 m · 계류 이격 m · 지반 고름 m, 계획서 20 확정) + * — 비우면 서버 기본값을 쓴다. 채운 칸만 요청에 실어 보내고(비운 칸은 서버가 알아서), 그 + * 「채운 칸」만 캐시·`initial_choice.json`에 남아 다시 열 때 되살아난다. 빈 칸의 placeholder는 + * 그 칸을 처음 비운 채 요청했을 때 서버가 답으로 준 값(진짜 기본값)을 기억해 둔 것이다. * ========================================================================== */ import { showToast } from "@ui/ui_template_elements"; import { fetchRouteInitial, ROUTE_INITIAL_MISSING, + type RouteInitialCriteria, type RouteInitialLogic, type RouteInitialWeights, } from "./B05_Profile_Api_RouteInitial"; @@ -21,6 +27,7 @@ import type { RouteEditSnapshot } from "./B05_Profile_UI_RouteEdit_History"; import { choiceLabel, criteriaLine, + CRITERIA_KEYS, DEFAULT_WEIGHTS, metricsLine, violationsLine, @@ -33,8 +40,10 @@ import { serverBase, WEIGHT_KEYS, writeInitialChoice, + type CriteriaKey, type InitialBase, type InitialChoice, + type InitialCriteria, type WeightKey, } from "./B05_Profile_UI_RouteEdit_Initial_Model"; @@ -44,6 +53,37 @@ const WEIGHT_NAME: Record = { grade: "기울기", }; +/** 공통 제약 칸 — 표시 이름 · 단위 · 서버가 준 값(`InitialCriteria`)에서 그 칸을 꺼내는 법. */ +const CRITERIA_FIELD: Record< + CriteriaKey, + { label: string; unit: string; title: string; of: (c: InitialCriteria) => number | null } +> = { + max_grade_pct: { + label: "기울기", + unit: "%", + title: "종단기울기 상한(%) — 비우면 프로젝트 기본값(설계속도 · 지형 표값)", + of: (c) => c.maxGradePct, + }, + min_radius_m: { + label: "최소R", + unit: "m", + title: "최소 곡선반지름(m) — 비우면 프로젝트 기본값", + of: (c) => c.minRadiusM, + }, + stream_offset_m: { + label: "계류", + unit: "m", + title: "계류 이격(m) — 비우면 기본값 100m", + of: (c) => c.streamOffsetM, + }, + grade_smooth_m: { + label: "지반", + unit: "m", + title: "기울기 잴 지반 고름 폭(m) — 비우면 기본값 5m", + of: (c) => c.gradeSmoothM, + }, +}; + export interface InitialPanelParams { overlay: HTMLElement; projectId: string; @@ -57,7 +97,11 @@ export interface InitialPanel { /** 노선을 읽었다 — 창을 연 편집값 · 「고친 노선인가」 · 서버가 적어 둔 현재 초기값(`current_initial`). */ ready: (opening: RouteEditSnapshot, edited: boolean, server?: unknown) => void; /** [확인]에 실어 보낼 지금 초기값. */ - current: () => { mode: RouteInitialLogic; weights: RouteInitialWeights | null }; + current: () => { + mode: RouteInitialLogic; + weights: RouteInitialWeights | null; + criteria: RouteInitialCriteria | null; + }; /** [취소] — 이 창에서 바꾼 초기값을 창을 열 때 것으로 되돌린다. */ cancel: () => void; /** [확인] 뒤 결과 캐시를 비웠다 — 초기값은 새 노선의 출발점이라 다시 적는다. */ @@ -80,6 +124,13 @@ export function createInitialPanel(params: InitialPanelParams): InitialPanel { +
+ ${CRITERIA_KEYS.map( + (key) => ``, + ).join("")} +