diff --git a/B05_Profile/B05_Profile_UI_Profile_Panel.ts b/B05_Profile/B05_Profile_UI_Profile_Panel.ts index 76ed148d..3ea62def 100644 --- a/B05_Profile/B05_Profile_UI_Profile_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Profile_Panel.ts @@ -85,6 +85,9 @@ const MAX_PANEL_HEIGHT_RATIO = 0.9; * 계산이 브라우저 안에서 끝나면서(2026-09-03) 서버 왕복이 사라져 250→60ms 로 줄였다 — * 실측 전 측점(128) 재계산 7.5ms 라 손을 떼는 즉시 유토곡선이 따라온다. */ const CROSS_PREVIEW_DEBOUNCE_MS = 60; +/** 구조물 측점을 종단 정본 측점으로 맞춰 주는 허용 오차(m). 층마다 반올림이 달라 생기는 + * mm~cm 어긋남만 흡수할 크기다 — 이보다 멀면 서로 다른 측점이다. */ +const STATION_SNAP_TOLERANCE_M = 0.1; /** * 저장된 계획선 선형을 읽되 **모양을 먼저 검증한다**. @@ -341,6 +344,36 @@ export function createRouteProfilePanel( 세로는 보이는 구간에 맞춰 자동이라 사람이 맞출 것이 없다(2026-09-04 사용자 확정). */ const profileZoom = createProfileZoom(() => draw()); + /** + * 구조물 측점의 누가거리를 **종단 정본 값으로 갈아 끼운다**(2026-09-04). + * + * 같은 구조물이 층마다 다른 누가거리를 들고 있었다 — 횡단 측점 `264.054626`, + * 종단 정본 변화점 `264.055`, 배수 정본 파일 `264.06`(cm 반올림). 사이드 목록이 든 + * 값으로 계획고를 편집하면 정본 옆 mm 자리에 **가짜 변화점**이 하나 더 서고, 둘 사이 + * 종단곡선이 mm 로 쭈그러들어 기울기가 155,791% 로 튀었다(2026-09-04 사용자 보고). + * + * 2026-09-03 에 ▲▼ 버튼 경로만 막았는데 측점 테이블·도구 경로가 남아 다시 터졌다. + * 값이 화면에 들어오는 **이 한 자리**에서 정본으로 맞춰 모든 경로를 함께 막는다. + */ + function snapToMasterStations(stations: IrregularStation[]): IrregularStation[] { + const master = alignment?.stations; + if (!master?.length) return stations; + return stations.map((station) => { + let best: number | null = null; + let gap = STATION_SNAP_TOLERANCE_M; + for (const node of master) { + const distance = Math.abs(node.chainage_m - station.chainage_m); + if (distance <= gap) { + gap = distance; + best = node.chainage_m; + } + } + return best === null || best === station.chainage_m + ? station + : { ...station, chainage_m: best }; + }); + } + /** 세로 자동 맞춤이 지금 쓰는 Y 창. 계획고를 끄는 동안에는 이 값을 붙잡는다. */ let elevationWindow: { min: number; max: number } | undefined; /** 세로 창을 **다시 그리지 않고** 옮기는 갱신기 — 그릴 때마다 렌더러가 새로 준다. */ @@ -652,7 +685,7 @@ export function createRouteProfilePanel( }, /** 비정규 측점 목록을 반영해 그래프(세로선+라벨)·테이블(주석)을 다시 그린다. */ setIrregularStations(stations: IrregularStation[]) { - irregularStations = stations; + irregularStations = snapToMasterStations(stations); draw(); }, /** 구조물 타입 레지스트리를 받아 마크 색·약호·우클릭 메뉴에 쓴다(최초 1회). */ diff --git a/common_util/common_util_drainage_pipes.py b/common_util/common_util_drainage_pipes.py index 07a7edb7..b27ac82a 100644 --- a/common_util/common_util_drainage_pipes.py +++ b/common_util/common_util_drainage_pipes.py @@ -90,7 +90,11 @@ class PipePoint: def as_dict(self) -> dict[str, Any]: # 구 형식 저장분이 확장 필드 없이 그대로 다시 저장되도록 기본값은 생략한다. payload: dict[str, Any] = { - "chainage_m": round(float(self.chainage_m), 2), + # 누가거리는 좌표(x·y)와 **같은 밀리미터 기준**으로 남긴다. cm 로 반올림하던 + # 옛 규칙은 종단 정본 변화점(3자리)과 최대 5mm 어긋났고, 그 값으로 계획고를 + # 편집하면 정본 옆 mm 자리에 가짜 변화점이 하나 더 서서 종단곡선이 mm 로 + # 쭈그러들었다 — 그 구간 기울기가 155,791% 로 튀었다(2026-09-04 사용자 보고). + "chainage_m": round(float(self.chainage_m), 3), "source": self.source, } if self.facility != PIPE_FACILITY_PIPE: