diff --git a/B05_Profile/B05_Profile_Engine_Grade_Profile.py b/B05_Profile/B05_Profile_Engine_Grade_Profile.py index c38a6a8f..fc7e51db 100644 --- a/B05_Profile/B05_Profile_Engine_Grade_Profile.py +++ b/B05_Profile/B05_Profile_Engine_Grade_Profile.py @@ -153,11 +153,13 @@ def design_ground_following_profile( ) -> tuple[dict[str, Any], dict[str, Any]]: """전체 측점을 변화점으로 삼고 계획고를 원지반고에 맞추는 1차 계획선. - 규칙(2026-09-02 사용자 확정): - - **모든 측점이 변화점**이고 그 표고는 그 자리 지반고 그대로다. 절·성토가 거의 0이다. - - **종단곡선(라운드)은 자동으로 넣지 않는다.** 사용자가 [직선화]로 만든 직선을 - 틸팅할 때 그 변화점에만 R이 지정되고, 그때 곡선이 생긴다 - (`build_alignment(only_explicit_curves=True)`). + 규칙(2026-09-02 사용자 확정 + 2026-09-03 종단곡선 복원): + - **모든 측점이 변화점**이고 그 자리 계획고는 지반고 그대로다. 절·성토가 거의 0이다. + - **변화점마다 종단곡선을 넣는다.** 직선과 직선 사이에는 반드시 호가 있고, 호는 + 변화점 대칭이라 **호의 중심이 측점 세로선 위**에 놓이며 좌우 직선과 접선을 이룬다. + - 대칭 종단곡선은 꼭짓점을 지나지 않으므로 **호와 측점 세로선의 교점이 지반고**가 + 되도록 꼭짓점 표고를 중앙종거만큼 밀어내며 반복 보정한다 + (`design_pipe_anchored_profile` 과 같은 방식). - 기울기는 지형 그대로라 법정 상한을 넘길 수 있다 — 막지 않고 경고만 남긴다 (기존 정책과 같다). @@ -204,6 +206,19 @@ def design_ground_following_profile( if note: warnings.append(note) + # 대칭 종단곡선은 꼭짓점을 지나지 않는다(중앙종거 |A|·L/8) — 보정 없이 곡선을 넣으면 + # 계획고가 지반고에서 뜬다. 호가 측점 세로선과 만나는 점이 지반고가 되도록 꼭짓점을 + # 밀어내며, 표고가 움직이면 대수차도 변하므로 수렴할 때까지 되풀이한다. + target = base_z.copy() + for _ in range(12): + curves, _curve_warnings = build_curves(base_s, base_z, policy) + error = target - evaluate(base_s, base_z, curves, base_s) + error[0] = 0.0 + error[-1] = 0.0 + if float(np.max(np.abs(error))) < 1e-4: + break + base_z = base_z + error + alignment = build_alignment( base_s=base_s, base_z=base_z, @@ -212,7 +227,6 @@ def design_ground_following_profile( stations=stations, policy=policy, edits=dict(edits or {}), - only_explicit_curves=True, ) warnings.extend(alignment["warnings"]) balanced = bool(alignment["balance"]["within_tolerance"]) @@ -449,10 +463,6 @@ def rebuild_alignment_profile( policy = AlignmentPolicy.from_dict(stored.get("policy") or {}) previous = (longitudinal.get("design_profiles") or [{}])[0] - # 전체 측점 폴리라인은 곡선을 자동으로 넣지 않는다 — 재구성도 같은 규칙이어야 - # 편집 델타를 지웠을 때 초기 폴리라인으로 정확히 돌아간다(2026-09-02). - only_explicit = str(previous.get("basis") or "") == GROUND_POLYLINE_BASIS - alignment = build_alignment( base_s=base_s, base_z=base_z, @@ -461,7 +471,6 @@ def rebuild_alignment_profile( stations=list(longitudinal.get("stations") or []), policy=policy, edits=edits, - only_explicit_curves=only_explicit, ) criteria = previous.get("criteria") or {} options = GradeDesignOptions(