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)」 특별측점을 더함 · 손대지 않은 곡선은 그 자리 유지 · 고정 교각점은 그대로 · 못 맞춘 곡선은 상태줄 「측점 밖 곡선」 · [저장] 때 저장">측점 스냅