feat(B05/B02): 표시 정보 정비 + 설계속도 축 도입
- 종단 상단 표시줄을 법정 판정 3항목으로 축소: 최대 기울기/상한(별표2 Ⅰ.2.라),
절·성토 불균형(Ⅰ.1.나.(4)(다)), 종단곡선 필요 n곳(Ⅰ.2.마 — 대수차 5% 초과인데
곡선이 빠진 변화점). 변화점·종단곡선 개수·기본 곡선길이 L·중복 경고칩은 제거.
- [초기선 복원] → [편집 되돌리기] — 실제 동작은 화면 편집 델타 삭제이지 저장 지점
복귀가 아니다. 툴팁에 동작과 [초기화]와의 차이를 적었다.
- 3D 뷰포트 안내 라벨 삭제(로딩 완료 후 남던 조작법 문구).
- 배수유역 버튼 정비: 유역 다시 나누기 / 선택한 관 삭제 / 배관 배치 초기화 +
각 툴팁에 실제 동작 명시(선택 삭제는 툴팁 자체가 없었다).
- 유토곡선 요약 12칩 → 5칩(절토·성토·잉여|부족·운반·검산). 토질 3종 내역·다짐
환산·블록 수·장거리 운반·사토는 해당 칩 툴팁으로. B05·B06 공용 함수라 동시 반영.
- 설계속도 축 도입(임도는 속도를 낼 수 없는 노선 — 기본 20km/h):
· 임도 종류는 프로젝트 등록값(projects.road_type)을 읽어 B05가 읽기 전용 표시
(main→간선임도, fire→산불진화임도, 그 외→작업임도). 화면에서 고치지 않는다.
· 설계속도 선택(간선·산불진화 20/30/40, 작업 20 고정) 신설 — 종단기울기 상한과
종단곡선 반경이 등급이 아니라 이 값으로 정해진다(지식DB 설계제원_총괄 §6·§7).
· B02 임도 종류 선택지를 현행 3종으로 정리(지선 폐지·계류보전은 사방 구분).
· 백엔드: ROUTE_GRADE_CLASSES 3종+branch 호환, selectable_design_speeds 신설,
resolve_design_speed 신설, legal_grade_limit_pct·resolve_grade_options에
설계속도 인자, sections/context가 road_type 제공(B06도 저장값 승계).
· 계획선 정책은 그릴 때마다 현재 기준으로 동기화 — 저장분의 옛 상한이 남지 않는다.
- 검증: pytest 125 통과(설계속도 12건 신규), tsc, 헤드 브라우저 — 진입 시 상한 9%,
40km/h 전환 시 7% 즉시 반영, 원복 9%, 안내문 '간선임도 · 설계속도 20km/h ·
일반지형', 유토곡선 5칩, 유역 버튼 새 이름·툴팁, 3D 라벨 빈 문자열 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -490,7 +490,7 @@ export function appendBalanceLayer(svg: SVGSVGElement, plan: HaulPlan, box: Bala
|
||||
* 사토·토취는 요약줄이 이미 곡선 기준으로 적고 있고 계단 단차의 합이 그 값과 같으므로
|
||||
* 여기서 또 적지 않는다(같은 수치가 두 번 뜨면 서로 다른 값으로 오해한다).
|
||||
*/
|
||||
export function haulPlanChips(plan: HaulPlan | null): Array<[string, string]> {
|
||||
export function haulPlanChips(plan: HaulPlan | null): Array<[string, string, string?]> {
|
||||
if (!plan) return [];
|
||||
const bands = plan.blocks.reduce((sum, block) => sum + block.bands.length, 0);
|
||||
const moved = plan.hauled_m3 + plan.transferred_m3;
|
||||
@@ -500,16 +500,18 @@ export function haulPlanChips(plan: HaulPlan | null): Array<[string, string]> {
|
||||
// (그 노선은 절토 우세라 토취가 0이었다).
|
||||
const gap = moved + plan.borrow_m3 - plan.fill_total_m3;
|
||||
const ratio = plan.fill_total_m3 > 0 ? Math.abs(gap) / plan.fill_total_m3 : 0;
|
||||
const chips: Array<[string, string]> = [
|
||||
[L("B06_MassHaul_HaulTotal"), `${compactVolume(moved)}㎥`],
|
||||
[L("B06_MassHaul_BlockCount"), `${plan.blocks.length} / ${bands}`],
|
||||
];
|
||||
/* 요약 줄 압축(2026-08-19) — 블록·띠 수와 장거리 운반·사토는 칩으로 두지 않고
|
||||
* 운반 총량 칩의 툴팁으로 내린다. 판단에 쓰는 값은 "얼마나 옮기는가"와 검산이다. */
|
||||
const haulTip = [`${L("B06_MassHaul_BlockCount")} ${plan.blocks.length} / ${bands}`];
|
||||
if (plan.transfers.length) {
|
||||
chips.push([L("B06_MassHaul_Transfer"), `${compactVolume(plan.transferred_m3)}㎥`]);
|
||||
haulTip.push(`${L("B06_MassHaul_Transfer")} ${compactVolume(plan.transferred_m3)}㎥`);
|
||||
}
|
||||
if (plan.natural_spoil_m3 > 0.5) {
|
||||
chips.push([L("B06_MassHaul_NaturalSpoil"), `${compactVolume(plan.natural_spoil_m3)}㎥`]);
|
||||
haulTip.push(`${L("B06_MassHaul_NaturalSpoil")} ${compactVolume(plan.natural_spoil_m3)}㎥`);
|
||||
}
|
||||
const chips: Array<[string, string, string?]> = [
|
||||
[L("B06_MassHaul_HaulTotal"), `${compactVolume(moved)}㎥`, haulTip.join(" · ")],
|
||||
];
|
||||
chips.push([
|
||||
L("B06_MassHaul_Check"),
|
||||
ratio <= 0.005
|
||||
|
||||
@@ -632,16 +632,29 @@ export function createMassHaulSummary(
|
||||
const row = document.createElement("div");
|
||||
row.className = "b06-masshaul__summary";
|
||||
const cut = result.cut_natural_m3;
|
||||
const chips: Array<[string, string]> = [
|
||||
const cutTotal = cut.soil + cut.ripping_rock + cut.blasting_rock;
|
||||
/* 요약 줄이 12칩까지 늘어 한눈에 안 읽혔다(2026-08-19 사용자 지적). 판단에 쓰는
|
||||
* 값만 칩으로 남기고, 세부 내역은 그 칩의 툴팁으로 내린다.
|
||||
* 절토(자연) — 토질 3종 내역은 툴팁
|
||||
* 성토(다짐) — 절토 다짐 환산은 툴팁(토량환산계수 f 적용 결과)
|
||||
* 잉여 또는 부족 — 둘은 동시에 성립하지 않는다
|
||||
* 운반·검산 — 토량 분배를 켰을 때만(haulPlanChips)
|
||||
* B05 종단 패널과 B06 횡단 화면이 같은 함수를 쓰므로 양쪽이 함께 바뀐다. */
|
||||
const chips: Array<[string, string, string?]> = [
|
||||
[
|
||||
L("B06_MassHaul_CutNatural"),
|
||||
`${formatVolume(cut.soil + cut.ripping_rock + cut.blasting_rock)}㎥`,
|
||||
`${formatVolume(cutTotal)}㎥`,
|
||||
[
|
||||
`${L("B06_Design_Ground_Soil")} ${formatVolume(cut.soil)}㎥`,
|
||||
`${L("B06_Design_Ground_Ripping")} ${formatVolume(cut.ripping_rock)}㎥`,
|
||||
`${L("B06_Design_Ground_Blasting")} ${formatVolume(cut.blasting_rock)}㎥`,
|
||||
].join(" · "),
|
||||
],
|
||||
[
|
||||
L("B06_MassHaul_Fill"),
|
||||
`${formatVolume(result.fill_compacted_m3)}㎥`,
|
||||
`${L("B06_MassHaul_CutCompacted")} ${formatVolume(result.cut_compacted_m3)}㎥`,
|
||||
],
|
||||
[L("B06_Design_Ground_Soil"), `${formatVolume(cut.soil)}㎥`],
|
||||
[L("B06_Design_Ground_Ripping"), `${formatVolume(cut.ripping_rock)}㎥`],
|
||||
[L("B06_Design_Ground_Blasting"), `${formatVolume(cut.blasting_rock)}㎥`],
|
||||
[L("B06_MassHaul_CutCompacted"), `${formatVolume(result.cut_compacted_m3)}㎥`],
|
||||
[L("B06_MassHaul_Fill"), `${formatVolume(result.fill_compacted_m3)}㎥`],
|
||||
];
|
||||
// 잉여와 부족은 동시에 성립하지 않으므로 해당하는 쪽만 보여 준다.
|
||||
chips.push(
|
||||
@@ -649,11 +662,12 @@ export function createMassHaulSummary(
|
||||
? [L("B06_MassHaul_Shortage"), `${formatVolume(result.shortage_m3)}㎥`]
|
||||
: [L("B06_MassHaul_Surplus"), `${formatVolume(result.surplus_m3)}㎥`],
|
||||
);
|
||||
// 토량 분배를 켜 두면 운반 총량·블록 수가 뒤에 붙는다(사토·토취는 발생 지점 기준 합계).
|
||||
// 토량 분배를 켜 두면 운반 총량·검산이 뒤에 붙는다(사토·토취는 발생 지점 기준 합계).
|
||||
chips.push(...haulPlanChips(haulPlan));
|
||||
for (const [label, value] of chips) {
|
||||
for (const [label, value, tip] of chips) {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "b06-masshaul__chip";
|
||||
if (tip) chip.title = tip;
|
||||
const name = document.createElement("em");
|
||||
name.textContent = label;
|
||||
const amount = document.createElement("strong");
|
||||
|
||||
Reference in New Issue
Block a user