fix(B05): 한 번 편집하면 맞춰 둔 반지름이 하한으로 눌리던 것
실화면에서 잡음 — 곡선 하나만 지워도 나머지 반지름이 **R 12~199m → 전부 12m** 로 되돌아갔음. 원인은 편집 모델과 저장 모델이 다른 것이었음. 서버는 처음 만들 때 이어진 꺾임을 한 곡선으로 묶는데, 그 곡선의 교각점은 앞뒤 직선을 늘려 만나는 자리라 **원본 꺾임점 중 어느 것도 아님**. 편집은 「꺾임점 하나 = 곡선 하나」로 표현되므로, 묶인 채로 두면 한 번만 손대도 묶음이 낱개로 흩어지고 각 자리가 하한으로 떨어졌음. 모달이 자료를 읽을 때 **묶인 구간을 그 교각점 하나로 갈아 끼우게** 함 — 안쪽 꺾임점은 그 곡선이 대신하므로 뺌. 앞뒤 직선과 반지름이 그대로라 그려지는 선은 똑같고, 이제 손대도 안 흩어짐. 서버가 고른 반지름도 함께 들고 가 [확인] 때 되돌려 보냄. 시험 tmp/tests/test_route_polyline_apex_roundtrip.py 2건 — 갈아 끼워도 곡선 수·반지름이 같고 선이 **1mm 안**에서 일치, 그리고 안 갈아 끼우면 실제로 하한으로 눌리는 것(왜 필요한지)도 못박음. 실화면 확인(용화) — 되돌린 뒤 곡선 26곳 R 최대 198.9m → 곡선 하나 지우고 [확인] → 곡선 25곳, **R 42.5·51·198.9m 그대로**. tsc --noEmit 통과 · pytest 432 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -667,17 +667,46 @@ export async function openRouteEditModal(
|
||||
// 잡는 것은 **노드**다 — 폴리라인 정점에는 원호 위 점이 섞여 있어 편집 대상이 아니다
|
||||
// (2026-09-06 사용자 지시: 노드를 제어해 계획노선을 고친다).
|
||||
const nodes = plan.nodes ?? [];
|
||||
planned = nodes.map((node) => [node.x, node.y] as Vertex);
|
||||
nodeInfo = nodes.map((node) => ({
|
||||
radius_m: node.radius_m,
|
||||
inner_angle_deg: node.inner_angle_deg,
|
||||
violations: node.violations ?? [],
|
||||
}));
|
||||
minRadiusM = plan.min_radius_m ?? 0;
|
||||
curveInfo = plan.curves ?? [];
|
||||
// 편집값은 「서버가 준 그대로」에서 시작한다 — 곡선이 있는 자리는 켬, 반지름은 자동.
|
||||
curveOn = planned.map(() => true);
|
||||
curveRadius = planned.map(() => null);
|
||||
// 곡선 성분을 **편집할 수 있는 꼴로 펴 둔다**(2026-09-07).
|
||||
//
|
||||
// 서버는 처음 만들 때 이어진 꺾임을 한 곡선으로 묶는다 — 그 곡선의 교각점은 앞뒤 직선을
|
||||
// 늘려 만나는 자리라 **원본 꺾임점 중 어느 것도 아니다**. 그런데 편집은 「꺾임점 하나 =
|
||||
// 곡선 하나」로 표현되므로, 묶인 곡선을 그대로 두면 한 번만 손대도 그 묶음이 낱개로
|
||||
// 흩어지고 **맞춰 둔 반지름이 전부 법정 하한으로 되돌아간다**(실측: R 12~199m → 전부 12m).
|
||||
//
|
||||
// 그래서 **묶인 구간을 그 교각점 하나로 갈아 끼운다** — 안쪽 꺾임점은 그 곡선이 대신하므로
|
||||
// 뺀다. 앞뒤 직선과 반지름이 그대로라 **그려지는 선은 똑같고**, 이제 손대도 안 흩어진다.
|
||||
const curves = plan.curves ?? [];
|
||||
const replaced = new Map<number, (typeof curves)[number]>();
|
||||
const dropped = new Set<number>();
|
||||
curves.forEach((curve) => {
|
||||
replaced.set(curve.node_first, curve);
|
||||
for (let index = curve.node_first + 1; index <= curve.node_last; index += 1) {
|
||||
dropped.add(index);
|
||||
}
|
||||
});
|
||||
planned = [];
|
||||
nodeInfo = [];
|
||||
curveOn = [];
|
||||
curveRadius = [];
|
||||
curveInfo = [];
|
||||
nodes.forEach((node, index) => {
|
||||
if (dropped.has(index)) return;
|
||||
const curve = replaced.get(index);
|
||||
const at: Vertex = curve ? [curve.apex[0], curve.apex[1]] : [node.x, node.y];
|
||||
const seat = planned.length;
|
||||
planned.push(at);
|
||||
nodeInfo.push({
|
||||
radius_m: curve ? curve.radius_m : node.radius_m,
|
||||
inner_angle_deg: curve ? curve.inner_angle_deg : node.inner_angle_deg,
|
||||
violations: curve ? (curve.violations ?? []) : (node.violations ?? []),
|
||||
});
|
||||
curveOn.push(true);
|
||||
// 서버가 고른 반지름을 **그대로 들고 간다** — 안 그러면 편집 한 번에 하한으로 눌린다.
|
||||
curveRadius.push(curve ? Math.round(curve.radius_m * 100) / 100 : null);
|
||||
if (curve) curveInfo.push({ ...curve, node_first: seat, node_last: seat });
|
||||
});
|
||||
picked = -1;
|
||||
syncCurveBar();
|
||||
if (!planned.length) planned = plannedLine.map((vertex) => [vertex[0], vertex[1]]);
|
||||
|
||||
Reference in New Issue
Block a user