This commit is contained in:
2026-07-24 17:52:01 +09:00
parent 5c8c0462f9
commit 505f28a30b
10 changed files with 109 additions and 39 deletions
@@ -135,6 +135,8 @@ def generate_sections(
long_z, long_valid = sampler.sample_xy(long_xy)
station_chainage = _chainages(total, options.station_interval_m, options.include_endpoint)
# 규칙(격자) 측점 chainage 집합 — 비정규와 겹칠 때 규칙 측점을 우선 판정하는 데 쓴다.
regular_rounded = {round(float(value), 6) for value in station_chainage}
# 비정규 측점(구조물)을 격자 측점 배열에 병합한다. 정렬·중복 제거만 하면 이후 접선·횡단
# 샘플링이 전부 이 배열을 따라 자동으로 확장된다. 어느 측점이 비정규인지는 chainage로 판별.
extras = {
@@ -183,7 +185,8 @@ def generate_sections(
kind = "bp"
elif abs(float(value) - total) <= 1e-6:
kind = "ep"
elif rounded in extras:
elif rounded in extras and rounded not in regular_rounded:
# 격자와 겹치지 않는 비정규 측점만 irregular. 규칙 격자와 겹치면 규칙 측점 우선.
kind = "irregular"
else:
kind = "regular"
@@ -213,7 +216,8 @@ def generate_sections(
"azimuth_deg": round(azimuth, 6),
"frame": frame,
}
if kind == "irregular":
# 구조물은 kind와 무관하게 부착한다(규칙 격자와 겹쳐 regular가 돼도 구조물 정보는 유지).
if rounded in extras:
station["structure"] = extras[rounded]
stations.append(station)
@@ -72,8 +72,9 @@ export function createIrregularStationsSection(
callbacks: IrregularStationsCallbacks,
): IrregularStationsSection {
const root = document.createElement("section");
root.className = "b05-route__panel-section";
root.className = "b05-route__panel-section ui-collapsible";
const heading = document.createElement("h3");
heading.className = "ui-collapsible__title";
heading.textContent = "비정규 측점 (구조물)";
const body = document.createElement("div");
body.className = "b05-route__panel-body";
+6 -10
View File
@@ -5,6 +5,7 @@ import {
type IrregularStationsSection,
} from "./B05_wf2_Route_UI_IrregularStations";
import { type ButtonVariant, createButton } from "@ui/ui_template_elements";
import { attachCollapsible } from "@ui/ui_template_collapsible";
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
export interface RoutePanelValues {
@@ -71,8 +72,9 @@ function L(key: keyof typeof ui_locales): string {
function section(title: string): { root: HTMLElement; body: HTMLElement } {
const root = document.createElement("section");
root.className = "b05-route__panel-section";
root.className = "b05-route__panel-section ui-collapsible";
const heading = document.createElement("h3");
heading.className = "ui-collapsible__title";
heading.textContent = title;
const body = document.createElement("div");
body.className = "b05-route__panel-body";
@@ -355,15 +357,9 @@ export function createRoutePanel(callbacks: PanelCallbacks) {
actionRow,
);
// 컨테이너 제목(h3) 클릭 시 본문을 접거나 편다(제목은 남긴다). 본문 안의 details 등 별도
// 접힘 항목은 손대지 않으므로 열려 있으면 그대로 보인다.
root.addEventListener("click", (event) => {
const heading = (event.target as HTMLElement).closest("h3");
const container = heading?.parentElement;
if (heading && container?.classList.contains("b05-route__panel-section")) {
container.classList.toggle("is-collapsed");
}
});
// 컨테이너 제목 행 전체 클릭 시 본문을 접거나 편다(공용 collapsible). 내부 details 등 별도
// 접힘 항목은 손대지 않는다.
attachCollapsible(root);
return {
root,
@@ -292,6 +292,11 @@ function buildCurveRows(options: ProfileTableOptions, centers: number[]): HTMLEl
if (curve.omitted) cell.classList.add("is-omitted");
});
}
// 선택된 규칙 측점 열의 곡선 셀도 함께 하이라이트한다.
if (station.station_id && station.station_id === options.selectedStationId) {
lengthCell.classList.add("is-selected");
radiusCell.classList.add("is-selected");
}
placeCell(lengthRow, centers[index], lengthCell);
placeCell(radiusRow, centers[index], radiusCell);
});
@@ -458,8 +463,12 @@ export function createProfileTable(options: ProfileTableOptions): HTMLElement {
spec.unit,
);
// 값이 없는 측점(절토고/성토고 중 한쪽)도 빈 칸을 만들어야 세로 구분선이 끊기지 않는다.
alignment.stations.forEach((_station, stationIndex) => {
alignment.stations.forEach((station, stationIndex) => {
const cell = element("span", "b05-profile-table__cell", spec.cell(stationIndex));
// 선택된 규칙 측점은 그 열의 값 셀을 하이라이트한다(비정규 측점의 값 열 강조와 통일).
if (station.station_id && station.station_id === options.selectedStationId) {
cell.classList.add("is-selected");
}
placeCell(row, centers[stationIndex], cell);
});
table.append(row);
+11 -23
View File
@@ -211,33 +211,11 @@
background: var(--color-surface-raised);
}
/* 제목 행 클릭 접기/펼치기는 공용 collapsible(ui_template_theme.css)에서 처리한다. */
.b05-route__panel-section h3 {
margin: 0;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-8);
color: var(--color-text);
font-size: var(--text-body-sm);
cursor: pointer;
user-select: none;
}
/* 접기/펼침 상태를 제목 우측 캐럿으로 표시(이 기능이 있음을 알린다). 펼침 ▾ / 접힘 ▸. */
.b05-route__panel-section h3::after {
content: "▾";
flex: 0 0 auto;
color: var(--color-text-muted);
font-size: 0.8em;
}
.b05-route__panel-section.is-collapsed h3::after {
content: "▸";
}
/* 제목 클릭으로 접힌 컨테이너: 본문만 감추고 제목은 남긴다. */
.b05-route__panel-section.is-collapsed .b05-route__panel-body {
display: none;
}
.b05-route__panel-body,
@@ -577,6 +555,16 @@
border-right: 1px solid var(--color-border);
}
/* 선택된 규칙 측점 열의 값·곡선 셀 하이라이트(비정규 측점 값 열 강조와 같은 색). */
.b05-profile-table__cell.is-selected,
.b05-profile-table__curve.is-selected {
background: color-mix(
in srgb,
var(--color-royal-amethyst, rgb(109 40 217)) 14%,
var(--color-surface)
);
}
.b05-profile-table__row.is-cut .b05-profile-table__cell {
color: rgb(220 38 38);
}