feat(B05): 종단 테이블 12행 → 6행 정리

화면 자리 확보 목적 (2026-09-06 사용자 지시). 뺀 6행 = 구배 L·H, 절토고,
성토고, 누가거리, 거리. 남긴 6행 = 구배 S, 계획고, 지반고, 측점, 곡선 L·R.
뺀 값은 사라지지 않음 — 구배 L·H 는 블록 툴팁, 절·성토고와 누가거리는 선택
측점 값 열 툴팁에 남김. B07 도면 종단도는 별도 코드라 12행 그대로.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-06 11:28:23 +09:00
co-authored by Claude Opus 5
parent c3dc1f4395
commit 4cbfb7e690
2 changed files with 26 additions and 52 deletions
+3 -2
View File
@@ -17,8 +17,9 @@ import {
type IrregularStation,
} from "./B05_Profile_UI_IrregularStations";
/** 테이블 12행. 행 높이와 셀 폭에서 글자 크기를 정하는 데 쓴다. */
export const TABLE_ROW_COUNT = 12;
/** 테이블 6행(구배 S · 계획고 · 지반고 · 측점 · 곡선 L · 곡선 R). 행 높이와 셀 폭에서
* 글자 크기를 정하는 데 쓴다. 2026-09-06 사용자 지시로 12행에서 줄였다. */
export const TABLE_ROW_COUNT = 6;
/** 측점 사이 여백 — 이웃 셀끼리 붙어 보이지 않게 띄운다. */
export const CELL_GAP_PX = 2;
+23 -50
View File
@@ -1,10 +1,11 @@
/* =============================================================================
* B05_Profile_UI_Profile_Table.ts
* 종단면도 하단 도면 테이블 (구배 3행 · 측점값 7행 · 곡선 2행 = 12행).
* 종단면도 하단 도면 테이블 (구배 1행 · 측점값 3행 · 곡선 2행 = 6행).
*
* 실무 종단면도 좌측 하단 표를 그대로 옮긴 구성이다. 구배와 곡선은 단일값 행이 아니라
* 도면에서도 여러 줄로 찍히므로 각각 물리적인 행으로 분리했다.
* - 구배: 연장 / 고저차 / 기울기 3행. 블록은 곡선 구간을 뺀 실제 직선부만 덮는다.
* 실무 종단면도 좌측 하단 표를 옮긴 구성이되, **화면에서는 판단에 쓰는 행만** 둔다
* (2026-09-06 사용자 지시 — 표가 그래프 자리를 너무 먹었다). 뺀 6행은 구배 L·H,
* 절토고, 성토고, 누가거리, 거리이며 **B07 도면 종단도는 12행 그대로**다(별도 코드).
* - 구배: 기울기(S) 1행. 블록은 곡선 구간을 뺀 실제 직선부만 덮는다.
* - 곡선: 곡선길이 / 반경 2행. 반경 R만 입력 가능하고 L = R × |대수차| 로 따라온다.
*
* 값에는 `L=` 같은 접두를 붙이지 않는다 — 행 이름표가 이미 항목과 단위를 말해준다.
@@ -173,10 +174,9 @@ function createRow(className: string, label: string, unit: string): HTMLElement
return row;
}
/** 도면의 구배 블록 3행: 구간 연장(m) / 고저차(m) / 기울기(%). 단위는 행 이름표가 대신한다. */
/** 화면 구배 블록 1행: 기울기(%). 연장·고저차 두 행은 2026-09-06 사용자 지시로 뺐다 —
* 두 값은 블록 툴팁에 그대로 남아 있어 필요할 때 짚어 볼 수 있다. */
const SEGMENT_ROWS: SegmentRowSpec[] = [
{ label: "구배 L", unit: "m 구간 연장", cell: (segment) => segment.length_m.toFixed(2) },
{ label: "구배 H", unit: "m 구간 고저차", cell: (segment) => segment.height_m.toFixed(2) },
{ label: "구배 S", unit: "% 구간 기울기", cell: (segment) => segment.grade_percent.toFixed(2) },
];
@@ -194,19 +194,9 @@ function buildStationRows(
display: { station: number; cumulative: number },
): StationRowSpec[] {
const stations = alignment.stations;
// 절토고·성토고·누가거리·거리 네 행은 2026-09-06 사용자 지시로 뺐다 — 절·성토는
// 그래프 음영과 유토곡선이, 누가거리는 측점 라벨이 이미 말해 준다.
return [
{
label: "절토고",
unit: "m",
modifier: "cut",
cell: (index) => (stations[index].cut_m > 0.005 ? stations[index].cut_m.toFixed(2) : ""),
},
{
label: "성토고",
unit: "m",
modifier: "fill",
cell: (index) => (stations[index].fill_m > 0.005 ? stations[index].fill_m.toFixed(2) : ""),
},
{
label: "계획고",
unit: "m",
@@ -214,16 +204,6 @@ function buildStationRows(
cell: (index) => stations[index].plan_elevation_m.toFixed(2),
},
{ label: "지반고", unit: "m", cell: (index) => stations[index].ground_elevation_m.toFixed(2) },
{
label: "누가거리",
unit: "m",
cell: (index) => (stations[index].chainage_m + display.cumulative).toFixed(2),
},
{
label: "거리",
unit: "m 전 측점 대비",
cell: (index) => (index ? stations[index].distance_m.toFixed(2) : ""),
},
{
label: "측점",
unit: "측점번호+잔여거리",
@@ -233,7 +213,7 @@ function buildStationRows(
}
/**
* 구배 3행. 각 블록은 **변화점에서 변화점까지**를 덮는다.
* 구배 행. 각 블록은 **변화점에서 변화점까지**를 덮는다.
*
* 구분선을 곡선의 접선점(BVC/EVC)에 두면 곡선 길이만큼 블록 사이에 틈이 생기고,
* 양옆 블록의 테두리가 그 틈을 감싸 "빈 셀"처럼 보인다. 변화점은 곧 **생성된 R의
@@ -268,7 +248,7 @@ function buildSegmentRows(
const span = x(segment.to_m) - left;
const text = spec.cell(segment);
const node = element("span", "b05-profile-table__segment", "");
// 구배 3행(L·H·S)은 **늘 보인다** — 구조물로 갈라진 좁은 구간도 값을 적는다
// 구배 은 **늘 보인다** — 구조물로 갈라진 좁은 구간도 값을 적는다
// (2026-09-04 사용자 확정: 값 열을 접는 규칙에서 구배 행은 제외).
// 옛 규칙(2026-08-03: 구조물 구간은 고를 때만 값 표시)은 여기서 걷어냈고,
// 구조물 구간 표시(색·하이라이트)와 툴팁은 그대로 둔다.
@@ -499,27 +479,13 @@ function buildSelectedColumn(
const curveAt = alignment.curves.find((entry) => Math.abs(entry.chainage_m - chainage) < 0.01);
// 값 열 오버레이도 같은 규칙 — 직선화된 자리는 곡선 L·R 을 비운다.
const curve = curveAt && !isStraightCurve(curveAt) ? curveAt : undefined;
// 거리: 바로 앞 측점(계획선 측점)까지의 간격.
const previous = alignment.stations
.filter((row) => row.chainage_m < chainage - 1e-6)
.reduce<number | null>(
(acc, row) => (acc === null ? row.chainage_m : Math.max(acc, row.chainage_m)),
null,
);
const distance = previous !== null ? chainage - previous : null;
// 테이블 행 순서(구배 3 · 측점값 7 · 곡선 2)와 정확히 같게 채운다. 없는 값은 공백.
// `plan: true`인 계획고 행만 직접 입력 셀로 만든다(값 열은 오버레이라 실제 12행 격자는 불변).
// 테이블 행 순서(구배 1 · 측점값 3 · 곡선 2)와 정확히 같게 채운다. 없는 값은 공백.
// `plan: true`인 계획고 행만 직접 입력 셀로 만든다(값 열은 오버레이라 실제 6행 격자는 불변).
// 절토·성토고는 행이 빠졌어도 값 자체는 툴팁에 남긴다 — 고른 측점에서 흔히 찾는 값이다.
const rows: Array<{ value: CellValue; modifier: string; plan?: boolean }> = [
{ value: gradeCell((segment) => segment.length_m, 2), modifier: "" }, // 구배 L
{ value: gradeCell((segment) => segment.height_m, 2), modifier: "" }, // 구배 H
{ value: gradeCell((segment) => segment.grade_percent, 2), modifier: "grade" }, // 구배 S
{ value: { text: cut !== null ? cut.toFixed(2) : "" }, modifier: "cut" }, // 절토고
{ value: { text: fill !== null ? fill.toFixed(2) : "" }, modifier: "fill" }, // 성토고
{ value: { text: plan !== null ? plan.toFixed(2) : "" }, modifier: "plan", plan: true }, // 계획고
{ value: { text: ground !== null ? ground.toFixed(2) : "" }, modifier: "" }, // 지반고
{ value: { text: (chainage + display.cumulative).toFixed(2) }, modifier: "" }, // 누가거리
{ value: { text: distance !== null ? distance.toFixed(2) : "" }, modifier: "" }, // 거리
{ value: { text: displayStationLabel(chainage, interval, display.station) }, modifier: "" }, // 측점
{ value: { text: curve ? curve.l_m.toFixed(2) : "" }, modifier: "" }, // 곡선 L
{ value: { text: curve ? curve.r_m.toFixed(1) : "" }, modifier: "" }, // 곡선 R
@@ -560,9 +526,16 @@ function buildSelectedColumn(
}
column.append(cell);
});
column.title = `${displayStationLabel(chainage, interval, display.station)} · ${(
// 행에서 뺀 값(절토고·성토고·누가거리)은 툴팁에 남긴다 — 고른 측점에서 흔히 찾는 값이다.
const cutFillText =
cut !== null
? ` · 절토 ${cut.toFixed(2)}m`
: fill !== null
? ` · 성토 ${fill.toFixed(2)}m`
: "";
column.title = `${displayStationLabel(chainage, interval, display.station)} · 누가거리 ${(
chainage + display.cumulative
).toFixed(2)}m`;
).toFixed(2)}m${cutFillText}`;
return column;
}