From d80ac203e13b2a075cdd97016b21f85d474146ec Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 20:33:24 +0900 Subject: [PATCH 01/43] =?UTF-8?q?chore(=EC=A0=95=EB=A6=AC):=20graphify=20?= =?UTF-8?q?=C2=B7=20=EC=9C=84=ED=82=A4=20=EC=B0=8C=EA=BA=BC=EA=B8=B0=20?= =?UTF-8?q?=E2=80=94=20.gitattributes=20=C2=B7=20.prettierignore=20?= =?UTF-8?q?=EC=9D=98=20graphify=20=EC=A4=84=20=EA=B1=B7=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01C6BNBFXFndbqYxu6sZuknK --- .gitattributes | 2 -- .prettierignore | 1 - 2 files changed, 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index 15e48d527..edae4fffe 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,5 +2,3 @@ B07_DesignDetail/openwebcad/tools/libredwg/*.exe binary B07_DesignDetail/openwebcad/tools/libredwg/*.dll binary -# graphify 그래프 — 줄끝을 바꾸면 서명(.sig)이 깨지고 창마다 헛 diff 가 남는다. -docs/wiki/graphify-out/** -text diff --git a/.prettierignore b/.prettierignore index 2e94d1794..3940c7f6a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,4 +7,3 @@ dist/ venv/ storage/ tmp/ -graphify-out/ From 7a6c6a2b95d1839fbdafae785fb658aff34b9066 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 20:35:13 +0900 Subject: [PATCH 02/43] =?UTF-8?q?chore(=EC=A0=95=EB=A6=AC):=20graphify=20?= =?UTF-8?q?=C2=B7=20=EC=9C=84=ED=82=A4=20=EC=B0=8C=EA=BA=BC=EA=B8=B0=20?= =?UTF-8?q?=E2=80=94=20main.py=20=EB=A6=AC=EB=A1=9C=EB=93=9C=20=EA=B1=B4?= =?UTF-8?q?=EB=84=88=EB=9C=80=20=EB=AA=A9=EB=A1=9D=EC=9D=98=20graphify-out?= =?UTF-8?q?=20=C2=B7=20=EC=9C=84=ED=82=A4=20=EB=A6=B0=ED=84=B0=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01C6BNBFXFndbqYxu6sZuknK --- main.py | 1 - resources/tester/scratch/wiki_linter.py | 131 ------------------------ 2 files changed, 132 deletions(-) delete mode 100644 resources/tester/scratch/wiki_linter.py diff --git a/main.py b/main.py index 3cd51fe23..8ffc770ba 100644 --- a/main.py +++ b/main.py @@ -738,7 +738,6 @@ _RELOAD_SKIP = { "docs", "resources", "scratch", - "graphify-out", "0_old", "db_management", "migrations", diff --git a/resources/tester/scratch/wiki_linter.py b/resources/tester/scratch/wiki_linter.py deleted file mode 100644 index 263b502c8..000000000 --- a/resources/tester/scratch/wiki_linter.py +++ /dev/null @@ -1,131 +0,0 @@ -import os -import re - -WIKI_DIR = r"C:\Program_coding\임도설계 및 견적자동화 프로그램 개발\docs\wiki" -PAGES_DIR = os.path.join(WIKI_DIR, "pages") -CONCEPTS_DIR = os.path.join(WIKI_DIR, "concepts") -INDEX_PATH = os.path.join(WIKI_DIR, "index.md") - -errors = [] -warnings = [] -all_wiki_files = {} # relative_path -> absolute_path - -# Collect all markdown files in wiki/ (ignoring graphify-out and memory directories) -for root, dirs, files in os.walk(WIKI_DIR): - # skip graphify-out or memory - if "graphify-out" in root or "memory" in root: - continue - for file in files: - if file.endswith(".md"): - abs_path = os.path.join(root, file) - rel_path = os.path.relpath(abs_path, WIKI_DIR).replace("\\", "/") - all_wiki_files[rel_path] = abs_path - simple_name = os.path.splitext(rel_path)[0] - all_wiki_files[simple_name] = abs_path - -# Read index.md content -with open(INDEX_PATH, "r", encoding="utf-8") as f: - index_content = f.read() - -# Pattern for wikilinks: [[link]] or [[link|label]] -wikilink_pat = re.compile(r"\[\[([^\]|]+)(?:\|[^\]]+)?\]\]") - -for rel_path, abs_path in list(all_wiki_files.items()): - if "/" not in rel_path and rel_path != "index" and rel_path != "log": - continue - if rel_path in ["index", "log", "index.md", "log.md"]: - continue - - with open(abs_path, "r", encoding="utf-8") as f: - lines = f.readlines() - - line_count = len(lines) - content = "".join(lines) - - # Rule 14: Max 100 lines - if line_count > 100: - warnings.append(f"[Line Count] `{rel_path}.md` exceeds 100 lines ({line_count} lines).") - - # Check YAML Frontmatter via simple regex - frontmatter_match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL) - if not frontmatter_match: - errors.append(f"[Frontmatter] `{rel_path}.md` has no valid YAML frontmatter.") - continue - - fm_text = frontmatter_match.group(1) - - # Simple regex parsing for status & page_id - status_match = re.search(r"^status:\s*(\w+)", fm_text, re.MULTILINE) - page_id_match = re.search(r"^page_id:\s*([^\n\r]+)", fm_text, re.MULTILINE) - - if not status_match: - errors.append(f"[Status] `{rel_path}.md` has no status field in frontmatter.") - else: - status = status_match.group(1).strip() - if status not in ["draft", "stable", "stale"]: - errors.append( - f"[Status] `{rel_path}.md` has invalid status '{status}'. Must be draft, stable, or stale." - ) - elif status == "stale": - warnings.append( - f"[Stale Page] `{rel_path}.md` is marked as stale and needs updates from raw inputs." - ) - - # Rule 5: page_id required for pages/ - if "pages/" in rel_path: - if not page_id_match: - errors.append(f"[page_id] `{rel_path}.md` is in pages/ but lacks a 'page_id' field.") - - # Check for broken wikilinks in content - links = wikilink_pat.findall(content) - for link in links: - link_clean = ( - link.strip().replace("\\", "/").split("#")[0] - ) # ignore anchor for file existence check - if not link_clean: - continue - found = False - - # 1. Absolute link from vault root (e.g. concepts/storage_paths) - if link_clean in all_wiki_files: - found = True - # 2. Simple name match (e.g. storage_paths, B01_frontend) - elif link_clean.split("/")[-1] in all_wiki_files: - found = True - # 3. Handle subpages like [[B01_Dashboard/B01_frontend]] - elif f"pages/{link_clean}" in all_wiki_files: - found = True - elif f"concepts/{link_clean}" in all_wiki_files: - found = True - # 4. Handle nested directory patterns - elif link_clean.startswith("../"): - # Resolve relative link - curr_dir = os.path.dirname(rel_path) - resolved = os.path.normpath(os.path.join(curr_dir, link_clean)).replace("\\", "/") - if resolved in all_wiki_files or resolved.replace("pages/", "") in all_wiki_files: - found = True - - if not found: - errors.append(f"[Broken Link] `{rel_path}.md` contains broken wikilink: [[{link}]]") - - # Check if this file is registered in index.md (avoid orphans) - file_base = os.path.basename(abs_path) - file_name_no_ext = os.path.splitext(file_base)[0] - if file_name_no_ext not in index_content: - # check if relative path is in index - rel_path_no_ext = rel_path.replace(".md", "") - if ( - rel_path_no_ext not in index_content - and rel_path_no_ext.split("/")[-1] not in index_content - ): - warnings.append( - f"[Orphan Page] `{rel_path}.md` is not linked or mentioned in index.md." - ) - -print("=== LINT ERRORS ===") -for e in sorted(list(set(errors))): - print(e) -print("\n=== LINT WARNINGS ===") -for w in sorted(list(set(warnings))): - print(w) -print("\nLint completed.") From b132c893bd73e1e70e456513ca8a0a325838bbb8 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 20:41:40 +0900 Subject: [PATCH 03/43] =?UTF-8?q?chore(=EC=9A=94=EA=B5=AC):=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20280=20=E2=80=94=20B06=20=EC=A2=8C=ED=95=98=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=B9=B8=20=EB=B9=84=EC=9B=80=20=C2=B7=20?= =?UTF-8?q?B05~B09=20=3F=20<=20>=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=C2=B7=20=EB=85=B8=EC=84=A0=20=EB=B0=94=EB=80=9C=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EB=AC=BC=20=EC=B8=A1=EC=A0=90=20=C2=B7=2010?= =?UTF-8?q?=20m=20=EC=8A=A4=EB=83=85=20=ED=8A=B9=EB=B3=84=EC=B8=A1?= =?UTF-8?q?=EC=A0=90=20=C2=B7=20B07=20=EC=84=A4=EC=A0=95=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=C2=B7=20CAD=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/raw/요구_1019/사용자요구_1019.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/raw/요구_1019/사용자요구_1019.md diff --git a/docs/raw/요구_1019/사용자요구_1019.md b/docs/raw/요구_1019/사용자요구_1019.md new file mode 100644 index 000000000..2c3356800 --- /dev/null +++ b/docs/raw/요구_1019/사용자요구_1019.md @@ -0,0 +1,27 @@ +# 사용자 요구 10-19 (원문 · 번호만 붙임) + +## 요구 280 (10-19 · 사용자) + +지시: 신규 계획서와 신규 요구서를 작성하기, 다른 워크트리의 코더들에게 업무지시 전 분석하여 상세 계획서 작성. + +1. B06의 횡단도의 좌측하단에 암경계가 기본적으로 보이는 영역은 사용자 선택하는 정보들을 보여주는 항목임. 그러니 초기값은 비어있는게 맞고 만약 사용자가 선이나 암경계 등을 선택하면 해당 정보를 보여줌. +2. B05 ~ B09의 영역은 현재 페이지 이동은 햄버거 메뉴의 버튼이나 B05/B06은 좌측 패널의 버튼으로 이동이 됨. 페이지의 좌측 패널 하단에 ? < > 버튼을 생성하여 페이지 이동을 할수 있게 반영. B05/B06의 경우에는 노선보기, 횡단보기 버튼은 삭제. 이때 B05와 B06의 페이지이동은 저장없이 자유롭게 이동 다른 페이지들은 현재 브라우져가 보이는 화면의 사용자 선택값이나 결과값을 저장 후 이동으로 변경, 버튼의 순서는 버튼셋 순서는 ?, <, >, 각페이지 버튼으로 구성, ?가 없는 페이지들은 신규로 생성하고 설명 추가할 생각임. +3. B05의 평면노선을 변경할 경우 구조물 측점이 이동되면서 이전 데이터가 남아 구조물이 없거나 구조물 측점이 발생됨. 이때 같은위치에 구조물 횡단이 있는 구조물을 올려두면 사라지기는 함. 평면 노선 변경에 따른 오류로 보임. 정밀확인 후 개선 필요 +4. B05의 평면노선 측점 스냅에서 기본적으로는 측점 no.에 맞춰 스냅이 되게 만들어져 있음. 이유는 곡선의 중심에는 측점이 있어야해서야. 대부분 측점no. 스냅은 잘되고 있는데 문제는 10m 단위로 변경한 경우 10스냅은 있지만 이전에 위치되어 있는 측점은 사라지게 되어 있음. 해당측점은 유지를 하고 10m 지점에 특별히 추가가 되어야하는 상황이야. 이경우에는 사용자가 특별히 만든 측점이기때문에 앞 측점들이 변하더라고 유지가 되어야함. 다시말해 일반측점은 20m 간격이지만 10m 스냅으로 변경한 지점 중 측점no + 10인 지점은 특별측점으로 일반측점화 하여 반영하면 된다는 말임. 이때 사용자가 다시 측점no.+0 지점으로 이동되면 당연히 일반측점이 되어야함. 그리고 스냅 항목중 1m와 0.1m 항목은 사용하지 않을거야. 삭제해줘. +5. B07의 좌측패널 컨테이너에는 컨테이너 접힘/펼침 화살표의 좌측에 설정 아이콘 추가, 설정 아이콘을 누르면 공용 모달창으로 생성. 컨테이너가 없는 항목은 버튼에 우측맞춤으로 생성 + - 평면도는 평면도 관리버튼을 삭제 후 설정 아이콘에 연결 + - 평면도를 제외한 다른 설정 모달창에는 기본적인 X축 Y축의 축적 변경 기능 부여, 횡단도의 경우 배치 행열 부터 안내선 숨기기 내용을 모달창 내부로 이동, 설정 아이콘은 표지는 제외. +6. B07 내장 캐드 및 페이지 개선 + - 상단 제일 상단의 여러 페이지를 보여주는 탭은 출력 탭과 같은 행에 보여주기 + - (캐드화면 높이 확보)Aislo CAD / B07 상세 설계 라벨이 존재하는 영역은 삭제, 저장과 json 내보내기 버튼은 숨기기 뒤로가기, 다시하기 버튼은 홈탭 영영에 우측맞춤.단 추가 된 것들은 홈탭행의 높이를 변화시키면 안됨. 램프와 현재도면 라벨도 삭제 + - 캐드화면 우측의 esc, 엔터, 삭제, 뒤로가기, 다시하기, 스냅버튼은 중복으로 보임. 버튼만 삭제 + - 캐드화면 하단 도면이름과 좌우버튼, 미확정&확정버튼의 컨테이너는 삭제, 불필요 + - 캐드의 좌측 패널의 하단 현재 도면 확정은 삭제 , 공용 양식 중 좌우 버튼으로 페이지 이동시 저장후 페이지 이동됨으로 불필요 + - 캐드의 텍스트에 마우스가 근접하면 텍스트 영역을 보여주는데 이때 영역이 더블클릭으로 수정접근한 영역과 다름. 수정영역과 동일하게 변경 + - 캐드의 상단 메뉴바에서 두개의 아이콘이 하이라이트 되는 경우 있음. 확인 + - 프로그램 전역에서 드롭다운 메뉴들 중 해당 항목을 선택하면 입력 후 빠져나와야 하는데 계속 해당항목에 잔류하고 있음. 그래서 마우스 스크롤이나 키보드로 제어를 하면 드롭다운 메뉴 열고 있음. + - 캐드의 선가중치의 명칭을 선 두께로 변경 + - 선색상, 선가중치, 선종류는 선을 선택하게 되면 해당 선택된 항목들의 정보를 보여줘야하며 선택의 정보가 상이하면 메뉴는 빈 항목을 보여주고 사용자가 해당항목을 선택하면 정보를 업데이트하게 변경되어야함. + - 캐드에서 도면층은 레이어 라고 명칭변경 + - 캐드화면 우측상단의 오토캐드 변환관련 설명이 줄바꿈 안되게 변경. 내용 포함. (추가 검토 내용은 M02의 구조물 조합에 dwg파일을 불러오는 기능이 있는데 이기능이 꽤 잘불러옴 현재 라벨에 설명이 되어 있는 방식에 적용한다면 설명이 필요없어보이는데...) + - 캐드의 상단 삽입 탭에 dwg파일 불러오기 기능 추가(m02 구조물조합의 도면 영역 확인) From 47b0642f5b1e5d6dc9f37f0c9aeed8021058fcfc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 21:32:43 +0900 Subject: [PATCH 04/43] =?UTF-8?q?chore(=EC=A1=B0=EC=82=AC):=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20280=20=EB=B6=84=EC=84=9D=20=EC=97=AC=EC=84=AF=20?= =?UTF-8?q?=E2=80=94=20B06=20=EC=A0=95=EB=B3=B4=20=EC=B9=B8=20=C2=B7=20?= =?UTF-8?q?=EB=93=9C=EB=A1=AD=EB=8B=A4=EC=9A=B4=20=C2=B7=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9D=B4=EB=8F=99=20=C2=B7=20=EB=85=B8?= =?UTF-8?q?=EC=84=A0=20=EB=B0=94=EB=80=9C=20=EA=B5=AC=EC=A1=B0=EB=AC=BC=20?= =?UTF-8?q?=EC=B8=A1=EC=A0=90=20=C2=B7=20=EC=8A=A4=EB=83=85=20=ED=8A=B9?= =?UTF-8?q?=EB=B3=84=EC=B8=A1=EC=A0=90=20=C2=B7=20B07=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=C2=B7=20CAD=20=EB=8F=99=EC=9E=91=20=C2=B7=20DWG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...2026-10-19_요구280_A_B06정보칸_드롭다운.md | 184 +++++++++++++ .../조사/2026-10-19_요구280_B_페이지이동.md | 160 +++++++++++ ...026-10-19_요구280_C_노선바뀜_구조물측점.md | 168 ++++++++++++ .../2026-10-19_요구280_D_스냅_특별측점.md | 131 +++++++++ .../2026-10-19_요구280_E_B07설정_화면정리.md | 215 +++++++++++++++ .../조사/2026-10-19_요구280_F_CAD동작_DWG.md | 253 ++++++++++++++++++ 6 files changed, 1111 insertions(+) create mode 100644 docs/raw/조사/2026-10-19_요구280_A_B06정보칸_드롭다운.md create mode 100644 docs/raw/조사/2026-10-19_요구280_B_페이지이동.md create mode 100644 docs/raw/조사/2026-10-19_요구280_C_노선바뀜_구조물측점.md create mode 100644 docs/raw/조사/2026-10-19_요구280_D_스냅_특별측점.md create mode 100644 docs/raw/조사/2026-10-19_요구280_E_B07설정_화면정리.md create mode 100644 docs/raw/조사/2026-10-19_요구280_F_CAD동작_DWG.md diff --git a/docs/raw/조사/2026-10-19_요구280_A_B06정보칸_드롭다운.md b/docs/raw/조사/2026-10-19_요구280_A_B06정보칸_드롭다운.md new file mode 100644 index 000000000..fdb93f3e7 --- /dev/null +++ b/docs/raw/조사/2026-10-19_요구280_A_B06정보칸_드롭다운.md @@ -0,0 +1,184 @@ +# 요구 280 A — B06 횡단 카드 왼쪽 아래 정보 칸(280-1) · 전역 드롭다운 초점(280-6) — 분석 + +분석 = 클로드 에이전트(Opus 5.5 · 읽기만) · 기준 = `main_laptop_1` `b132c893b` · 추정은 (추정). + +## 0. 결론 + +- 280-1 — 왼쪽 아래 칸 = 선 공용 칸(370-3). 처음부터 암이 뜨는 까닭 = 「안 고름 + 암 지반 → 암 칸」 갈래 하나(`B06_Section_UI_Cross_Design_Pick.ts:632`). 고름 저장 · 세션 복원 탓 아님(고름 = 모듈 변수 · 새로고침이면 사라짐). +- 280-1 고침 = 그 갈래 걷음(한 줄) → 안 고르면 빈 칸 · 암 정보는 점선을 눌러 고를 때만. +- B05 우측 횡단 판(393) = 같은 카드 부품이나 발 줄을 통째 걷어 둠 → 화면 변화 없음. +- 280-6 — 드롭다운은 전부 브라우저 기본 `` | +| CAD(openwebcad · iframe · React) | 7 | `RibbonWidgets.tsx:87 · 106 · 135 · 180` · `PropertiesEditor.tsx:53 · 71 · 90` | +| M01 12 · M02 40 | 52 | createSelectField · `el("select")` | + +- 합 ≈ 110 자리 · 모두 기본 `` 는 빈 값 불가 → `data-mixed` 붙여 CSS 로 빈 견본(투명 · 점선 테두리) · 고르면 적용(결정 4-나). +- 색 끌기 중 연발(`paintNowCommitLater` `:54-62`) = 첫 연발에 사본 만들고 이후 그 사본만 칠함 · 멎으면 한 칸 실음. +- 고른 것 있을 때 = 고른 것만 바뀜 · 그리기 기본값 그대로(결정 4-가). +- 점선 판정 = `[]` · `undefined` 모두 실선. + +## 5. 우측 위 오토캐드 안내 글 줄바꿈 + +### ① 지금 동작 + +- 글 = 「오토캐드에서 WBLOCK(또는 DXFOUT)으로 저장 → 파일을 여기로 끌어다 놓기」 · 누르면 ①②③ 세 줄(`src/components/DropHint.tsx:15-21`). +- 자리 = 리본 바로 아래 오른쪽(`App.css:534-552` · `top: title + ribbon + 8px`) · B07 · M02 양식 둘 다. + +### ② 원인 + +- `max-width: 360px`(`App.css:543`) + 줄바꿈 막기 없음 · 글 폭 ≈ 390 px(11 px) → 두 줄 · ①줄도 10 px 로 ≈ 400 px → 두 줄. + +### ③ 고치는 방법 + +- `.cad-drop-hint` = `white-space: nowrap` · `max-width` 걷음(①②③ 줄도 따라감) — 두 줄. +- 안내가 말하는 길 = 끌어다 놓기 → `/api/projects/dxf-clip/file`(`src/integration/dxf-clip-paste.ts:37-43` · `:99-105`) → **M02 [파일 업로드]와 같은 서버 엔진**(아래 6장 표) · WBLOCK 은 도면 일부만 보낼 때만 필요 · 파일 통째도 됨. +- 6장 단추가 생기면 안내는 한 줄로 줄임 추천(결정 5). + +## 6. 삽입 탭 「DWG 불러오기」 + +### ① 지금 동작 — 같은 엔진 두 길 + +| | M02 구조물 조합 · 양식 [파일 업로드] | B07 CAD 끌어다 놓기(이미 있음) | +| --- | --- | --- | +| 화면 | `M02_MasterTemplete/M02_MasterTemplete_Drawing.ts:296-331`(숨은 file 입력 `.dxf,.dwg`) · 보냄 `:145-161` | `src/integration/dxf-clip-paste.ts:37-60` · 끌기 `:88-105` · 붙이기 `prepareClip`(`dxf-clip.ts:24-49`) | +| 서버 | `POST /api/m02/drawing-import`(`M02_MasterTemplete_Router_Drawing.py:57-91`) → `import_frame_file` | `POST /api/projects/dxf-clip/file`(`B07_DesignDetail_Router_Frame.py:193-219`) → `drawing_file_to_entities` | +| 변환 | 둘 다 `_read_drawing_file`(`B07_DesignDetail_Engine_Frame_Import.py:375-385`) — DWG = LibreDWG `dwg2dxf` 별도 실행(동봉 `openwebcad/tools/libredwg/dwg2dxf.exe` · `:334-364` · 180 s) → ezdxf | 같음 | +| 레이어 | 한 층 「도각」으로 몰음(`frame_document` `Engine_Template.py:179-185`) | DXF 층 이름 그대로 · 같은 이름 있으면 그 층 · 없으면 새 층(층 색) | +| 넣는 방식 | 도면 **통째 바꿈** · 저장 전 | 지금 도면에 **더함** · 원좌표 1:1 · 골라진 채 · 되돌리기 한 칸(`addEntities(…, true)`) | +| 권한 | 로그인(`main.py:681` `protected`) | 로그인 + 회사(`main.py:655` `protected_with_company`) · 프로젝트 무관 · 저장 안 함 | +| 한도 | 20 MB · 도형 20,000 · DWG R14 ~ 2018(AC1032) | 같음(`Router_Frame.py:85`) | + +- 읽는 도형 = 선 · 폴리선 · 원 · 호 · 타원 · 스플라인(선분으로) · 점 · TEXT · MTEXT · 블록 · 치수 · 지시선은 풀어서(`:208-261`). +- 버림 = 해치 · 솔리드 · 그림 · 선종류(점선) · 선 두께(모두 1). +- ByLayer 색 도형 = 흰색(`_color` `:64-73`) — 층 색은 새 층에만 실림 → B07 쪽은 오토캐드 도면이 거의 흰색으로 들어옴. +- 시험 `resources/tester/test_b07_dxf_clip.py` 에 DWG 파일 읽기(`REpyo.dwg`)까지 있음. + +### ② 원인 + +- 단추만 없음 — 끌어다 놓기를 모르면 못 씀. + +### ③ 고치는 방법 + +- `src/commands/commands.insert.ts` 에 명령 하나 — 이름 `IMPORT`(오토캐드 같은 이름) · 「DWG 불러오기」 · `run` = 기존 `pickFile('.dwg,.dxf', …)`(`helpers/pick-file.ts`) → 기존 `pasteDrawingFile` + 알림(`dxf-clip-paste.ts:62-68` `reportPaste` 를 내보냄). +- `src/ribbon/ribbon.config.ts:147-148` 삽입 탭 — 새 패널 「가져오기」 큰 단추 하나. +- `command-catalog.json` 한 줄. +- 확정 도면 막기 = `runCommand` 가 이미 막음(`run-command.ts:32`) · 서버 · 변환 · 레이어 · 되돌리기 = 그대로 재사용. +- `pickFile` 입력을 문서에 붙였다 뗌(`hidden`) — 지금은 문서 밖 입력이라 ORCA `upload` 가 못 닿음. +- (결정 6-다) ByLayer 도형 색 = 층 색 — `_entities_with_layers`(`:290-294`) 한 곳 · B07 붙이기 길만 바뀜(M02 양식 그대로). + +## ④ 코더 나눔 + +| 창 | 항목 | 파일 | 모델 | +| --- | --- | --- | --- | +| 가 | 1 · 2 | `entities/TextEntity.ts` · `integration/text-inplace-edit.ts` · 새 `helpers/text-frame.ts` · `drawControllers/screenCanvas.drawController.ts`(691줄 · 늘림 최소) · `inputController/input-controller.ts`(1-다) · `ribbon/Ribbon.tsx` · `components/QuickAccessBar.tsx` · `App.css` `.cad-tool` 줄 | opus high(회전 · 정렬 기하) | +| 나 | 3 · 4 · 5 · 6 | 3장 표 파일 전부 · `components/RibbonWidgets.tsx` · `components/PropertiesEditor.tsx` · 새 `helpers/selection-style.ts` · `components/DropHint.tsx` · `commands/commands.insert.ts` · `ribbon/ribbon.config.ts` · `integration/dxf-clip-paste.ts` · `helpers/pick-file.ts` · `commands/command-catalog.json` · `App.css` `.cad-drop-hint` · 색 견본 줄 · (6-다) `B07_DesignDetail_Engine_Frame_Import.py` | opus medium(되돌리기) | + +- 가 · 나 겹침 = `App.css` 하나(다른 규칙 덩이) — 합칠 때 충돌 적음. +- 3 · 4 를 한 창에 묶은 까닭 = `RibbonWidgets.tsx` · `PropertiesEditor.tsx` · `command-catalog.json` 같은 줄. +- **화면 배치 분석(다른 에이전트)과 겹칠 파일** — `App.css`(1102줄 · 이미 700 넘음) · `ribbon/Ribbon.tsx`(되돌리기 · 다시하기 홈 탭 오른쪽) · `ribbon/ribbon.config.ts`(`QUICK_ACCESS_COMMANDS`) · `components/QuickAccessBar.tsx`(제목 줄 지움) · `components/Toolbar.tsx` · `components/DropHint.tsx`(제목 줄 지우면 `--cad-title-height` 따라 위로 감). +- 같은 요구의 「드롭다운 고른 뒤 초점 남음」 항목 = `RibbonWidgets.tsx` 고르기 상자 · `Ribbon.tsx` 단추와 같은 뿌리(초점 남음) — 그 항목 코더와 같은 창이 좋음. +- 창 수 = 둘(가 · 나) · 배치 코더와 같은 날이면 배치 먼저 합치고 가 · 나가 받은 뒤 시작. + +## ⑤ 시험 · 화면 확인 + +기존 시험(실행 `cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad <이름>`): + +| 항목 | 기존 | 새로(같은 폴더) | +| --- | --- | --- | +| 1 | `text-inplace-edit.test.ts` · `text-acad.test.ts` | `text-frame.test.ts` — 왼 · 가운데 · 오른 · 90° · 굵게: 테두리 네 귀 = 입력 칸 left · top · width · height · 회전(배율 2 · 잰 폭 40 모의) | +| 2 | — | (화면으로) | +| 3 | `command-catalog.test.ts` | 같은 파일에 「이름 · 설명에 도면층 · 선가중치 없음」 한 줄 | +| 4 | `src/state.test.ts` | `selection-style.test.ts` — 같은 값 → 그 값 · 다름 · 목록 밖 → 빈 값 · 적용 → 폴리선 자식까지 · Ctrl+Z 한 번 → 옛 값 · 고친 것 없을 때 기본값 | +| 5 | — | (화면으로) | +| 6 | `dxf-clip.test.ts` · `test_b07_dxf_clip.py`(pytest) · `command-catalog.test.ts` | `command-catalog.test.ts` 에 「삽입 탭에 IMPORT」 한 줄 · (6-다) `test_b07_dxf_clip.py` 에 ByLayer 색 한 건 | + +- 타입 = `npm run check-types` · 빌드 = `npm run build`. + +화면(ORCA · CAD 는 iframe — `document.querySelector('iframe').contentWindow` 로 eval): + +| 항목 | 판정(수치) | +| --- | --- | +| 1 | 왼 · 가운데 · 오른 · 돈 글 각각 — 스침 뒤 `__aisloCad.highlighted()` 에 그 글 · 테두리 네 귀(디버그 창구 `debug-hook.ts` 에 글 테두리 한 칸 더함 → `screen()` 로 화면값) 와 더블클릭 뒤 `.cad-text-inplace` `getBoundingClientRect()` 차 ≤ 1 px · 여러 줄 글 = 묶음 줄 모두 강조 | +| 2 | 쉬는 중 「선」 위 마우스 — `[data-active=true]` 1개(선택) · 「선」 테두리색 ≠ 강조색 / 「선」 누름 → Esc → `document.activeElement` 가 `.cad-tool` 아님 · `:focus-visible` 0개 | +| 3 | `document.body.innerText` 에 「도면층」 · 「선가중치」 0 — 홈 탭 · 레이어 관리자 · 왼쪽 탭 · 넘침 메뉴(▾) · LAYER 명령 안내 줄 | +| 4 | 색 다른 선 둘 고름 → 두께 · 선종류 `select.value === ''` · 색 `data-mixed=true` → 두께 3 고름 → 두 선 자식까지 3 → Ctrl+Z 한 번 → 옛 값 · 방금 그린 선 남음 → 고름 풂 → 기본값 그대로 · 바꾼 값 되돌림 | +| 5 | `.cad-drop-hint` 높이 ≤ 한 줄(≈ 16 px) · `scrollWidth ≤ clientWidth` · 펼친 ①②③ 각 한 줄 | +| 6 | 삽입 탭 「DWG 불러오기」 → `orca upload`(붙은 입력) `REpyo.dwg` → 알림 「도형 N개」 · `__aisloCad.selection().length === N` · 새 레이어 · Ctrl+Z 한 번에 빠짐 · 확정 도면에선 막힘 알림 · 끝나면 Ctrl+Z 로 되돌림 | + +## ⑥ 사용자 결정 + +| 번호 | 갈래 | 선택지 | 추천 | +| --- | --- | --- | --- | +| 1-가 | 도각 자리표 칸 글 | 입력 칸도 칸 크기 / 지금처럼 글 폭 | 칸 크기 | +| 1-나 | 잡는 범위 | 글 테두리 안 어디든 스침 · 고름 · 더블클릭 / 지금처럼 기준점 10 px | 테두리 안(끝 글자에서도 강조) | +| 1-다 | 여러 줄 글 스침 | 묶음 전체 강조 / 그 줄만 | 묶음 전체 | +| 2 | 스침 모양 | 바탕색만 / 지금 그대로 | 바탕색만 | +| 4-가 | 고른 것 있을 때 바꾸면 | 고른 것만(오토캐드와 같음) / 기본값도 같이(지금) | 고른 것만 | +| 4-나 | 색 「빈 항목」 | 빈 견본(점선 테두리) / 색 목록 드롭다운으로 바꿈 | 빈 견본 | +| 4-다 | 왼쪽 특성 · 빠른 특성 | 같은 규칙 / 리본만 | 같은 규칙 | +| 4-라 | 같은 되돌리기 흠 명령(COLOR · LWEIGHT · MATCHPROP 등 8개) | 이번에 같이 / 다음 차례 | 이번에 같이(같은 부품 한 줄씩) | +| 4-마 | 글자도 고른 때 | 선 메뉴는 선만 보고 글은 문자 스타일 칸 / 글 색도 선 색으로 | 선만 | +| 5 | 안내 글 | 한 줄로 그대로 / 「DWG · DXF 파일을 끌어다 놓거나 [삽입 → DWG 불러오기]」로 줄임 / 지움 | 줄임 | +| 6-가 | 넣는 자리 | 원좌표 그대로 · 골라진 채(끌어다 놓기와 같음) / 누른 점에 놓기 | 원좌표 그대로 | +| 6-나 | 붙인 도형이 화면 밖 | 그 범위로 화면 맞춤 / 그대로 | 화면 맞춤(기존 `fitView` 재사용) | +| 6-다 | ByLayer 도형 색 | 레이어 색 / 지금처럼 흰색 | 레이어 색 | +| 3 | CAD 밖 B07 안내 글 1곳 | 같이 「레이어」 / 그대로 | 그대로(요구가 「캐드에서」) | + +## ⑦ 위험 + +- 1 — 테두리를 잡는 범위로 쓰면(1-나) 글 밑 선보다 글이 먼저 잡힘 · 줌아웃으로 글이 10 px 아래면 입력 칸만 커짐(최소 글 크기 남김). +- 1 — `getBoundingBox` 는 안 바꿈(줄맞춤 · 그립 · 창 고르기 · 공간 색인) — 바꾸면 영향 넓음. +- 2 — 「나」(초점 테두리)는 추정 — 화면에서 먼저 재현 뒤 판정. +- 3 — 토씨 22곳 · 거울 JSON 빠지면 `command-catalog.test.ts` 빨강. +- 4 — 사본 갈아 끼움 = id 그대로라 고름 유지 · 옮기기 도구와 같은 길 · 새 레이어(DWG)처럼 층 목록은 되돌리기 밖(`state.ts:591`). +- 4 — 되돌리기 흠은 이번 요구 전부터 있던 것 — 고치면 사용자가 겪던 「Ctrl+Z 가 다른 걸 지움」도 사라짐. +- 6 — DWG 단위(mm · m)와 B07 도면 단위가 다르면 크기 어긋남 — 골라진 채라 바로 SCALE · 단위 묻기는 나중. +- 6 — 서버가 파일을 다 읽은 뒤 20 MB 를 잼(M02 와 같음) · 리눅스 서버는 `dwg2dxf` 를 PATH 나 `.env` `LIBREDWG_DWG2DXF_PATH` 로. +- 공통 — `App.css` 이미 1102줄(700 넘음) · 이번 고침은 제자리 바꿈 위주로 늘림 최소 · 배치 코더와 같은 파일. From afe6e4a7993024874aa80bb8e89ef04c36bca2fc Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 21:45:52 +0900 Subject: [PATCH 05/43] =?UTF-8?q?chore(=EC=9A=94=EA=B5=AC):=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=20281=20=E2=80=94=20B07=20=ED=99=95=EC=A0=95=20?= =?UTF-8?q?=EC=9E=A0=EA=B8=88=20=EC=97=86=EC=9D=8C=20=C2=B7=20280-3=20?= =?UTF-8?q?=EB=90=98=EB=8F=8C=EB=A6=AC=EA=B8=B0=20=EC=95=88=20=EC=94=80=20?= =?UTF-8?q?=C2=B7=20=EB=B0=94=EB=A1=9C=20=EC=B0=A9=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- docs/raw/요구_1019/사용자요구_1019.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/raw/요구_1019/사용자요구_1019.md b/docs/raw/요구_1019/사용자요구_1019.md index 2c3356800..da14b7243 100644 --- a/docs/raw/요구_1019/사용자요구_1019.md +++ b/docs/raw/요구_1019/사용자요구_1019.md @@ -25,3 +25,9 @@ - 캐드에서 도면층은 레이어 라고 명칭변경 - 캐드화면 우측상단의 오토캐드 변환관련 설명이 줄바꿈 안되게 변경. 내용 포함. (추가 검토 내용은 M02의 구조물 조합에 dwg파일을 불러오는 기능이 있는데 이기능이 꽤 잘불러옴 현재 라벨에 설명이 되어 있는 방식에 적용한다면 설명이 필요없어보이는데...) - 캐드의 상단 삽입 탭에 dwg파일 불러오기 기능 추가(m02 구조물조합의 도면 영역 확인) + +## 요구 281 (10-19 · 사용자 · 280 물음 답) + +1. B07 [현재 도면 확정] 지운 뒤 확정 = 잠금 없음 — [>] · 페이지 이동 때 고친 도면 + 지금 도면 저장 · 잠기지 않음 · 원본 되돌림은 ⚙ [원본에서 다시 그리기]. +2. 280-3 노선 고칠 때 되돌리기(↶) 안 씀. +3. 계획대로 서브 창 여덟 바로 착수. From 20d0fd5e51a344530cbdedee635f55838cd27fb4 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 21:58:53 +0900 Subject: [PATCH 06/43] =?UTF-8?q?fix(483-1=C2=B72=20B05=20=EC=8A=A4?= =?UTF-8?q?=EB=83=85):=20=EC=9D=BC=EB=B0=98=EC=B8=A1=EC=A0=90=EC=9D=84=20M?= =?UTF-8?q?C=20=EB=A1=9C=20=EC=98=AE=EA=B8=B0=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=8C=20=C2=B7=20MC=20=ED=8A=B9=EB=B3=84=EC=B8=A1=EC=A0=90?= =?UTF-8?q?=20=EB=8D=94=ED=95=A8=20=C2=B7=20=EC=8A=A4=EB=83=85=20=EA=B0=84?= =?UTF-8?q?=EA=B2=A9=201=20m=20=C2=B7=200.1=20m=20=EC=A7=80=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버 snap_curve_stations ↔ TS snapCurveStations · gridIndices 지움 — 스냅 켬 = 끔과 같은 측점(20 m 그대로 + 「No.N+10 (MC)」) - 고르기 = 10 m · 측점 No. 둘 · load_station_snap_step = 10 만 · 옛 0.1 · 1 = 측점 No. - 시험 = 새 test_280_4_stations(거울 다섯 경우) · 224_2 · 344_3 · 366_3 · 390_4 · 390_5 · 407 고쳐 씀 Co-Authored-By: Claude Opus 5.5 --- A00_Common/b_page_state.ts | 2 +- .../design/logic/design_pipeline_types.ts | 4 +- B05_Profile/B05_Profile_Api_Replan.ts | 2 +- B05_Profile/B05_Profile_Engine_Sections.py | 4 +- .../B05_Profile_Engine_Sections_Core.py | 46 ++----- B05_Profile/B05_Profile_Schema.py | 2 +- .../B05_Profile_UI_RouteEdit_Chrome.ts | 4 +- .../B05_Profile_UI_RouteEdit_SnapStep.ts | 36 ++--- .../logic/B05_Profile_RouteEdit_KeyNudge.ts | 2 +- .../logic/B05_Profile_Sections_Stations.ts | 43 +----- .../logic/B05_Profile_UI_RouteEdit_Frame.ts | 30 +---- resources/tester/helper_280_4_stations.cjs | 40 ++++++ .../tester/helper_390_5_snap_step_ops.cjs | 12 +- resources/tester/test_224_2_station_snap.py | 95 ++++++-------- resources/tester/test_280_4_stations.py | 124 ++++++++++++++++++ .../test_344_3_pipeline_stations_mirror.py | 16 +-- resources/tester/test_366_3_station_fit.py | 2 +- resources/tester/test_390_4_snap_step_text.py | 15 ++- resources/tester/test_390_5_snap_step_ops.py | 35 +++-- resources/tester/test_407_curve_station.py | 13 +- 20 files changed, 292 insertions(+), 235 deletions(-) create mode 100644 resources/tester/helper_280_4_stations.cjs create mode 100644 resources/tester/test_280_4_stations.py diff --git a/A00_Common/b_page_state.ts b/A00_Common/b_page_state.ts index b7d5234e5..8d6e8082e 100644 --- a/A00_Common/b_page_state.ts +++ b/A00_Common/b_page_state.ts @@ -74,7 +74,7 @@ export const STATE_REGISTRY = { "routeedit-locks": { bucket: "pref", scope: "project" }, /** B05 계획노선 편집 창 — 분석 지형(las · sheet). 확정 지표면은 안 바꿈 */ "routeedit-terrain": { bucket: "pref", scope: "project" }, - /** B05 계획노선 편집 창 — 측점 스냅 간격(0.1 · 1 · 10 · station) */ + /** B05 계획노선 편집 창 — 측점 스냅 간격(10 · station · 280-4) */ "routeedit-snap-step": { bucket: "pref", scope: "project" }, "masshaul-open": { bucket: "pref", diff --git a/A00_Common/design/logic/design_pipeline_types.ts b/A00_Common/design/logic/design_pipeline_types.ts index f63d5fc4a..1c1e6cc14 100644 --- a/A00_Common/design/logic/design_pipeline_types.ts +++ b/A00_Common/design/logic/design_pipeline_types.ts @@ -39,7 +39,7 @@ * * 측점 열쇠(344-3 · `stationKey` · `B05_Profile/logic/B05_Profile_Sections_Stations.ts`): * | 측점 | 열쇠 | 뜻 | - * | 일반(격자) | grid:k | k = 원래 격자 번호(누가거리 ÷ 측점 간격 반올림) · 노선 모양이 바뀌어도 같은 번호 · 스냅으로 옮긴 측점은 원래 번호 | + * | 일반(격자) | grid:k | k = 원래 격자 번호(누가거리 ÷ 측점 간격 반올림) · 노선 모양이 바뀌어도 같은 번호 | * | 관 | pipe: | 관 세션 id(없으면 `pipe-{누가거리 2자리}`) | * | 구조물 | structure: | structure_id · 구간형 시작 · 끝 = `:start` · `:end` 덧붙임 | * | 시종점 | end:BP · end:EP | 그 자리 관 · 구조물이 있어도 시종점(서버 bp · ep 와 같음) | @@ -72,7 +72,7 @@ export interface RouteDraft { curveArc: Array; /** [측점 스냅](요구 224-2) — 켜면 호 안 측점 하나를 곡선 중점으로. */ stationSnap: boolean; - /** 스냅 간격(m · 0.1 · 1 · 10 · null·없음 = 측점 No.) — MC 측점 값 자릿수(390-5). */ + /** 스냅 간격(m · 10 · null·없음 = 측점 No. · 280-4) — MC 측점 값 자릿수(390-5). */ stationSnapStep?: number | null; /** 분석 지형(`las` · `sheet` · null = 확정 지표면) — ground 단계가 고를 격자. */ terrain: string | null; diff --git a/B05_Profile/B05_Profile_Api_Replan.ts b/B05_Profile/B05_Profile_Api_Replan.ts index 2b1d11a84..df97ed54a 100644 --- a/B05_Profile/B05_Profile_Api_Replan.ts +++ b/B05_Profile/B05_Profile_Api_Replan.ts @@ -117,7 +117,7 @@ export interface RoutePlanResponse { edited: boolean; /** [측점 스냅] 켬(요구 224-2) — [확인] 때 곡선표에 적은 값. 칸 없으면 끔. */ station_snap?: boolean; - /** 스냅 간격(m · 0.1 · 1 · 10) — 칸 없거나 null = 측점 No.(390-5 · MC 측점 값 자릿수). */ + /** 스냅 간격(m · 10) — 칸 없거나 null = 측점 No.(390-5 · MC 측점 값 자릿수). */ station_snap_step?: number | null; /** 현재 초기값 — 서버가 [확인] 때 프로젝트에 적어 둔 초기노선 갈래(계획서 20). */ current_initial?: { logic: string; weights: Record | null }; diff --git a/B05_Profile/B05_Profile_Engine_Sections.py b/B05_Profile/B05_Profile_Engine_Sections.py index 108484692..4960428ae 100644 --- a/B05_Profile/B05_Profile_Engine_Sections.py +++ b/B05_Profile/B05_Profile_Engine_Sections.py @@ -107,10 +107,10 @@ def load_station_snap(project_root: Path) -> bool: def load_station_snap_step(project_root: Path) -> float | None: """스냅 간격(m) — 곡선표 `"station_snap_step"`(390-5 · [확인] 때 적힘). - 칸 없으면 None(측점 No.). + 10 만 그 값 · 칸 없음 · 옛 0.1 · 1 m 등 그 밖 = None(측점 No. · 280-4). """ step = _read_design_curve_table(project_root).get("station_snap_step") - return float(step) if isinstance(step, int | float) and step > 0 else None + return 10.0 if isinstance(step, int | float) and step == 10 else None def _read_design_curve_table(project_root: Path) -> dict[str, Any]: diff --git a/B05_Profile/B05_Profile_Engine_Sections_Core.py b/B05_Profile/B05_Profile_Engine_Sections_Core.py index b1ef38200..8cc6afa33 100644 --- a/B05_Profile/B05_Profile_Engine_Sections_Core.py +++ b/B05_Profile/B05_Profile_Engine_Sections_Core.py @@ -6,10 +6,10 @@ ⚠ 파이썬·TS 짝 — `_interpolate_xy`·`_tangent_at` ↔ `B05_Profile_UI_RouteEdit_Frame.ts` (`stationFrameAt`). 거울 시험 `resources/tester/test_b05_routeedit_frame_mirror.py`. -⚠ 파이썬·TS 짝 — 측점 스냅 `_chainages` · `_design_curve_spans` · `snap_curve_stations` · +⚠ 파이썬·TS 짝 — 측점 스냅 `_chainages` · `_design_curve_spans` · `curve_stations` · `_mc_scale` ↔ `B05_Profile_UI_RouteEdit_Frame.ts` `stationsAlong` · `curveSpans` · -`snapCurveStations` · `mcScale`. 거울 시험 `resources/tester/test_224_2_station_snap.py` · -간격별 MC 값 `test_390_5_snap_step_ops.py`. +`curveStations` · `mcScale`. 거울 시험 `resources/tester/test_224_2_station_snap.py` · +`test_280_4_stations.py` · 간격별 MC 값 `test_390_5_snap_step_ops.py`. ⚠ 파이썬·TS 짝 — `generate_sections` 지반 부분 ↔ `B05_Profile/logic/B05_Profile_Sections_Ground.ts`. ⚠ 파이썬·TS 짝 — `generate_sections` 측점 부분(격자 · 스냅 · 구조물 · 곡선 측점 · 종류 · 방위각) · `_plan_radii` · `_curve_widenings` · `_design_curve_widenings` ↔ `B05_Profile/logic/B05_Profile_Sections_Stations.ts` @@ -295,35 +295,6 @@ def _mc_scale(step_m: float | None) -> int: return 10 if step_m is not None and step_m < 1 else 1 -def snap_curve_stations( - stations: np.ndarray | list[float], - spans: list[tuple[float, ...]], - step_m: float | None = None, -) -> np.ndarray: - """측점 스냅(요구 224-2) — 호(BC~EC) 안 측점 중 곡선 중점 MC 에 가장 가까운 하나를 MC - (floor(MC·s+0.5)/s · s = `_mc_scale(step_m)`)로 옮김. 측점 없는 호는 그대로 · 옮긴 자리가 다른 - 측점과 같은 값이면 하나로. - - `spans` = `_design_curve_spans` 꼴(앞 두 칸 = BC · EC 누가거리) · `step_m` = 스냅 간격 - (0.1 · 1 · 10 · None = 측점 No. · 390-5). - """ - scale = _mc_scale(step_m) - values = [float(value) for value in stations] - for span in spans: - start, end = float(span[0]), float(span[1]) - middle = (start + end) / 2.0 - inside = [ - index - for index, value in enumerate(values) - if start + CURVE_STATION_EDGE_M < value < end - CURVE_STATION_EDGE_M - ] - if not inside: - continue - nearest = min(inside, key=lambda index: abs(values[index] - middle)) - values[nearest] = math.floor(middle * scale + 0.5) / scale - return np.unique(np.asarray(values, dtype=np.float64)) - - def curve_stations( stations: np.ndarray | list[float], spans: list[tuple[float, ...]], @@ -331,7 +302,7 @@ def curve_stations( step_m: float | None = None, ) -> list[float]: """곡선 측점(요구 252-2) — MC(floor(MC·s+0.5)/s · s = `_mc_scale(step_m)`)에 측점이 - 없으면 그 값 · 0.005 m 안 측점(일반 · 스냅으로 옮긴 · 관 · 구조물)이 있거나 노선 밖이면 뺌 · + 없으면 그 값 · 0.005 m 안 측점(일반 · 관 · 구조물)이 있거나 노선 밖이면 뺌 · BC · EC 는 안 더함. `curveStations` 짝.""" scale = _mc_scale(step_m) taken = [float(value) for value in stations] @@ -393,9 +364,8 @@ def _station_layout( 곡선 측점). `generate_sections` · `planned_curve_stations`(B06 측점 맞춤 판정 · 407-3)가 같은 길.""" total = float(route_chainage[-1]) station_chainage = _chainages(total, options.station_interval_m, options.include_endpoint) + # 일반측점은 스냅이 옮기지 않음(요구 280-4) — MC 는 아래 곡선 측점으로 더함 spans = _design_curve_spans(points, route_chainage, design_curves or []) - if station_snap: - station_chainage = snap_curve_stations(station_chainage, spans, station_snap_step) # 규칙(격자) 측점 chainage 집합 — 비정규와 겹칠 때 규칙 측점을 우선 판정하는 데 쓴다. regular_rounded = {round(float(value), 6) for value in station_chainage} # 비정규 측점(구조물)을 격자 측점 배열에 병합한다. 정렬·중복 제거만 하면 이후 접선·횡단 @@ -459,9 +429,9 @@ def generate_sections( ) -> dict[str, Any]: """확정 경로로 CAD 인계 가능한 종단·횡단 원시 데이터를 생성한다. - `station_snap` — 호 안 측점 하나를 곡선 중점으로 옮김(`snap_curve_stations` · 요구 224-2) · - `station_snap_step` — 그 MC 측점 값 자릿수를 정하는 스냅 간격(390-5 · None = 측점 No.). - MC 에 측점이 없으면 곡선 측점(kind `curve` · `curve_stations` · 요구 252-2) 하나를 더함. + `station_snap` · `station_snap_step` — MC 측점 값 자릿수를 정하는 스냅 간격(390-5 · None = 측점 No.) · + 일반측점은 옮기지 않음(요구 280-4). MC 에 측점이 없으면 곡선 측점(kind `curve` · + `curve_stations` · 요구 252-2 · 「No.2+10 (MC)」) 하나를 더함. """ options = options or SectionGenerationOptions() options.validate() diff --git a/B05_Profile/B05_Profile_Schema.py b/B05_Profile/B05_Profile_Schema.py index 7ab3241d1..aaa7a3c75 100644 --- a/B05_Profile/B05_Profile_Schema.py +++ b/B05_Profile/B05_Profile_Schema.py @@ -433,4 +433,4 @@ class RouteReplanRequest(BaseModel): station_snap: bool = False """[측점 스냅] 켬(요구 224-2) — 곡선표 `"station_snap"` 으로 남아 측점 셈이 읽음.""" station_snap_step: float | None = None - """스냅 간격(m · 0.1 · 1 · 10 · None = 측점 No. · 390-5) — 곡선표 `"station_snap_step"`.""" + """스냅 간격(m · 10 · None = 측점 No. · 390-5 · 280-4) — 곡선표 `"station_snap_step"`.""" diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts index ad721cc0f..a59ecb239 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts @@ -110,12 +110,10 @@ export function createRouteEditChrome( + title="켜면 고친 곡선의 가운데(MC)를 고른 간격 배수에 맞춥니다 — 일반측점(20 m)은 그대로 · MC 가 일반측점 위가 아니면 「No.N+10 (MC)」 특별측점을 더함 · 손대지 않은 곡선은 그 자리 유지 · 고정 교각점은 그대로 · 못 맞춘 곡선은 상태줄 「측점 밖 곡선」 · [저장] 때 저장">측점 스냅