- 측구 · 측구형식 · 경사 방향 묶음 = 부품 분할 고르기 20 · 토사 · 측구 · 단경사 켜고 끄기 = 켜기 칩 20 · [상세] = 단추(고스트) 20 - ⋯ 넘침 펼침 = 아이콘 단추(선) 22 꼴(펼침 = aria-pressed) · 암 경계 ▲▼↺ · 암 절토 ↺ = 아이콘 단추 22 - 지운 자체 CSS — b06-design__btn(--active) · 펼침 보라 채움 · rockb 단추 꼴 · 사이 금 · 글 0.72rem → --text-caption - 부품 아이콘 단추의 display 가 hidden 을 덮어 선 칸 숨김 단추는 페이지에 숨김 한 줄 - 카드 높이 7 px 줄음(설계 줄 단추 24 → 20 · 지반 칩 24 → 20) · 그림 색 전후 같음(34 장면 × 102~120 선) - 시험 헬퍼 둘 — helper_318 = 단추 줄 시험이 진짜 부품 파일을 흉내 노드로 실음 · helper_472 = 켜기 칩 글을 안쪽 span 에서 읽음 - Line_Cell · CutSlope 는 흉내 DOM 시험(370-3 등)이 @ui 를 안 풀어 부품 클래스 · data-size 계약으로 붙임 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
5.9 KiB
TypeScript
122 lines
5.9 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_UI_Cross_CutSlope.ts
|
|
* 측점 하나의 **암 절토 경사** 입력칸(2026-09-07 사용자 지시).
|
|
*
|
|
* 사용자 원문 — 「개별 횡단도에는 암 절토 각도의 개별 수정 가능해야함 / 전체 변경을 위해서는
|
|
* 좌측 패널의 표준 횡단면 설정을 이용」. 그래서 **전체 기본값은 좌측 패널**, **이 칸은 그 측점
|
|
* 하나**만 바꾼다. 표준을 바꿔도 여기서 만진 측점은 그대로다(사용자 값이라 재계산이 살려 둔다).
|
|
*
|
|
* 화면도 속도 **경사비(1:n 의 n)** 로 쓴다(2026-09-12 사용자 지시). 종전에는 화면만 각도(°)로
|
|
* 바꿔 보여 줬는데, 도면·좌측 [표준 횡단면 설정]·품셈이 모두 1:n 이라 이 칸만 머리로 환산해야
|
|
* 했다. 참고: 1:0.4 = 68.2° · 1:1.0 = 45° · 1:1.5 = 33.7°.
|
|
*
|
|
* ⚠ 경사는 **나르는 값이 아니라 기하 입력**이다. 계산이 끝난 결과에 값만 베껴 붙이면 설계선은
|
|
* 옛 경사로 그려지고 숫자만 새것이 된다 — 값은 반드시 **계산 전에** 넘어가야 한다
|
|
* (`computeCrossDesign(cutSlopeRatio)` · `compute_cross_design(cut_slope_ratio=…)`).
|
|
* ========================================================================== */
|
|
|
|
import type { CrossSection } from "./B06_Section_Api_Fetch";
|
|
|
|
/** 측점별 암 절토 경사 제어기 — Page 가 세션 저장소와 연결해 구현한다. */
|
|
export interface CutSlopeControl {
|
|
/** 세션 → design 저장값 → 표준값 순으로 지금 경사비를 돌려준다. */
|
|
ratioFor: (section: CrossSection) => number;
|
|
/** 이 측점의 표준값(되돌릴 자리) — 사용자가 안 만진 상태의 값. */
|
|
standardRatioFor: (section: CrossSection) => number;
|
|
/** 경사비를 넣는다. null 이면 표준값으로 되돌린다. */
|
|
set: (chainageM: number, ratio: number | null) => void;
|
|
}
|
|
|
|
/** 경사비(1:n 의 n) → 수평에서 잰 각도(°). n 이 작을수록 급하다. */
|
|
export function ratioToDegrees(ratio: number): number {
|
|
return (Math.atan(1 / Math.max(ratio, 1e-6)) * 180) / Math.PI;
|
|
}
|
|
|
|
/** 각도(°) → 경사비(1:n 의 n). 1~89° 밖은 사면이 서지 않아 잘라 받는다. */
|
|
export function degreesToRatio(degrees: number): number {
|
|
const clamped = Math.min(Math.max(degrees, 1), 89);
|
|
return 1 / Math.tan((clamped * Math.PI) / 180);
|
|
}
|
|
|
|
/** 경사비를 화면 자릿수(0.01)로 맞춘다 — 사면이 서지 않는 각도(1~89° 밖)는 잘라 받는다. */
|
|
export function clampRatio(ratio: number): number {
|
|
return Math.round(degreesToRatio(ratioToDegrees(ratio)) * 100) / 100;
|
|
}
|
|
|
|
/** 0.01 단위로 같은 값인가 — 표준값으로 되돌아왔는지 가리는 데 쓴다. */
|
|
function sameRatio(a: number, b: number): boolean {
|
|
return Math.abs(a - b) < 0.005;
|
|
}
|
|
|
|
/**
|
|
* 카드 하단에 서는 「암 절토 1:0.40 ↺」 칸. 암 경계선 제어와 같은 자리·같은 모양이다.
|
|
*
|
|
* 되돌리기(↺)는 **표준 횡단면 설정값**으로 돌려놓는다 — 좌측 패널을 그 뒤에 바꾸면 이 측점도
|
|
* 따라간다(사용자가 만진 흔적이 지워지므로).
|
|
*/
|
|
export function buildCutSlopeControl(section: CrossSection, control: CutSlopeControl): HTMLElement {
|
|
const wrap = document.createElement("div");
|
|
wrap.className = "b06-design__seg b06-design__cutslope";
|
|
const legend = document.createElement("span");
|
|
legend.className = "b06-design__seg-legend";
|
|
legend.textContent = "암 절토";
|
|
wrap.append(legend);
|
|
|
|
const group = document.createElement("div");
|
|
group.className = "b06-design__seg-buttons";
|
|
const ratio = clampRatio(control.ratioFor(section));
|
|
const standard = clampRatio(control.standardRatioFor(section));
|
|
|
|
// 「1:」은 고정 머리말 — 사용자가 넣는 것은 뒤의 n 하나다.
|
|
const prefix = document.createElement("span");
|
|
prefix.className = "b06-design__cutslope-unit";
|
|
prefix.textContent = "1:";
|
|
|
|
const input = document.createElement("input");
|
|
input.type = "number";
|
|
input.className = "b06-design__cutslope-input";
|
|
input.step = "0.05";
|
|
input.min = "0.02";
|
|
input.max = "57.29";
|
|
input.value = ratio.toFixed(2);
|
|
input.title =
|
|
`이 측점의 암 절토 경사 — 경사비(1:n 의 n)로 넣는다. n 이 작을수록 급하다.\n` +
|
|
`지금 1:${ratio.toFixed(2)} (${ratioToDegrees(ratio).toFixed(1)}°) · 표준 1:${standard.toFixed(2)} (${ratioToDegrees(standard).toFixed(1)}°)\n` +
|
|
`전체를 바꾸려면 좌측 [표준 횡단면 설정]을 쓴다.`;
|
|
const commit = (): void => {
|
|
const entered = Number(input.value);
|
|
if (!Number.isFinite(entered) || entered <= 0) {
|
|
input.value = ratio.toFixed(2);
|
|
return;
|
|
}
|
|
// 표준값으로 되돌아온 입력은 사용자 값을 남기지 않는다 — 그래야 표준을 바꿀 때 따라간다.
|
|
const next = clampRatio(entered);
|
|
control.set(section.chainage_m, sameRatio(next, standard) ? null : next);
|
|
};
|
|
input.addEventListener("change", commit);
|
|
input.addEventListener("keydown", (event) => {
|
|
if (event.key === "Enter") {
|
|
event.preventDefault();
|
|
commit();
|
|
}
|
|
});
|
|
// 카드 클릭(선택·줌)이 입력을 뺏지 않게 한다 — 암 경계선 버튼과 같은 태도.
|
|
input.addEventListener("pointerdown", (event) => event.stopPropagation());
|
|
|
|
const reset = document.createElement("button");
|
|
reset.type = "button";
|
|
reset.className = "ui-icon-btn ui-icon-btn--ghost b06-design__rockb-btn is-reset"; // 부품 아이콘 단추 22
|
|
reset.setAttribute("data-size", "22");
|
|
reset.textContent = "↺";
|
|
reset.title = `표준값으로 되돌리기 (1:${standard.toFixed(2)} · ${ratioToDegrees(standard).toFixed(1)}°)`;
|
|
reset.disabled = sameRatio(ratio, standard);
|
|
reset.addEventListener("click", (event) => {
|
|
event.stopPropagation();
|
|
control.set(section.chainage_m, null);
|
|
});
|
|
|
|
group.append(prefix, input, reset);
|
|
wrap.append(group);
|
|
return wrap;
|
|
}
|