auto: 2026-09-06 20:58 (ESD_LAPTOP)
This commit is contained in:
@@ -41,6 +41,10 @@ import {
|
|||||||
normalizeVisibleBasis,
|
normalizeVisibleBasis,
|
||||||
} from "@util/common_util_mass_haul";
|
} from "@util/common_util_mass_haul";
|
||||||
import { computeHaulPlan } from "@util/common_util_mass_haul_balance";
|
import { computeHaulPlan } from "@util/common_util_mass_haul_balance";
|
||||||
|
import {
|
||||||
|
badgeValuesFrom,
|
||||||
|
type MassHaulBadgeValues,
|
||||||
|
} from "@util/common_util_mass_haul_badge";
|
||||||
import { resetBalloonOffsets } from "@util/common_util_mass_haul_balance_view";
|
import { resetBalloonOffsets } from "@util/common_util_mass_haul_balance_view";
|
||||||
import {
|
import {
|
||||||
createMassHaulChart,
|
createMassHaulChart,
|
||||||
@@ -185,18 +189,7 @@ function readVisible(): Set<string> {
|
|||||||
* 종단 상단줄이 **접힌 상태에서도** 보여 주는 유토곡선 총괄값(2026-09-06 사용자 지시).
|
* 종단 상단줄이 **접힌 상태에서도** 보여 주는 유토곡선 총괄값(2026-09-06 사용자 지시).
|
||||||
* 곡선 자체는 접으면 사라져도 이 값은 계획선을 만지며 계속 봐야 한다.
|
* 곡선 자체는 접으면 사라져도 이 값은 계획선을 만지며 계속 봐야 한다.
|
||||||
*/
|
*/
|
||||||
export interface MassHaulSummaryValues {
|
export type MassHaulSummaryValues = MassHaulBadgeValues;
|
||||||
/** 노선 끝 누가토량(㎥) — 양수 잉여, 음수 부족. */
|
|
||||||
finalM3: number;
|
|
||||||
/** 절토(자연상태) 합계(㎥). */
|
|
||||||
cutNaturalM3: number;
|
|
||||||
/** 절토를 다짐으로 환산한 값(㎥). */
|
|
||||||
cutCompactedM3: number;
|
|
||||||
/** 성토(다짐) 합계(㎥). */
|
|
||||||
fillCompactedM3: number;
|
|
||||||
surplusM3: number;
|
|
||||||
shortageM3: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRouteMassHaulPanel(
|
export function createRouteMassHaulPanel(
|
||||||
onChanged: () => void,
|
onChanged: () => void,
|
||||||
@@ -339,31 +332,31 @@ export function createRouteMassHaulPanel(
|
|||||||
/** 마지막으로 그린 입력 — 이동이 아직 안 끝났으면 다음 프레임에 이걸로 한 번 더 그린다. */
|
/** 마지막으로 그린 입력 — 이동이 아직 안 끝났으면 다음 프레임에 이걸로 한 번 더 그린다. */
|
||||||
let lastParams: RouteMassHaulDrawParams | null = null;
|
let lastParams: RouteMassHaulDrawParams | null = null;
|
||||||
|
|
||||||
/** 총괄값만 따로 낸다 — 패널을 접어도 상단줄이 값을 잃지 않게(2026-09-06). */
|
/** 이미 낸 계열에서 총괄값만 꺼낸다 — 계산은 하지 않는다. */
|
||||||
function reportSummary(params: RouteMassHaulDrawParams): void {
|
function emitSummary(series: MassHaulSeries[]): void {
|
||||||
if (!onSummary) return;
|
if (!onSummary) return;
|
||||||
if (!context || params.pendingRecalc) return;
|
|
||||||
const series = computeMassHaulSeries(
|
|
||||||
params.longitudinal,
|
|
||||||
params.crossSections,
|
|
||||||
context.conversion,
|
|
||||||
context.naturalSpoilMinSlope,
|
|
||||||
);
|
|
||||||
if (!series.length) return onSummary(null);
|
if (!series.length) return onSummary(null);
|
||||||
// 총괄값 기준은 **횡단 고정**이다(2026-09-06 사용자: 종단 기준 곡선은 뒤에 없앨 예정).
|
// 총괄값 기준은 **횡단 고정**이다(2026-09-06 사용자: 종단 기준 곡선은 뒤에 없앨 예정).
|
||||||
// 범례에서 무엇을 켜 두었든 배지·상단줄이 흔들리지 않는다.
|
// 범례에서 무엇을 켜 두었든 배지·상단줄이 흔들리지 않는다.
|
||||||
const picked = series.find((entry) => entry.basis === "cross") ?? series[0];
|
const picked = series.find((entry) => entry.basis === "cross") ?? series[0];
|
||||||
const result = picked.result;
|
onSummary(badgeValuesFrom(picked.result));
|
||||||
const cut = result.cut_natural_m3;
|
}
|
||||||
const points = result.points;
|
|
||||||
onSummary({
|
/** 총괄값만 따로 낸다 — 패널을 접어도 상단줄이 값을 잃지 않게(2026-09-06).
|
||||||
finalM3: points.length ? points[points.length - 1].cumulative_volume_m3 : 0,
|
* **횡단 기준만** 적분한다 — 접힌 상태에서 종단 기준까지 내던 것은 버려질 값이었다
|
||||||
cutNaturalM3: cut.soil + cut.ripping_rock + cut.blasting_rock,
|
* (2026-09-06 조작 실측: 계획고 ▲ → 라벨 갱신 중앙 130ms 중 낭비분). */
|
||||||
cutCompactedM3: result.cut_compacted_m3,
|
function reportSummary(params: RouteMassHaulDrawParams): void {
|
||||||
fillCompactedM3: result.fill_compacted_m3,
|
if (!onSummary) return;
|
||||||
surplusM3: result.surplus_m3,
|
if (!context || params.pendingRecalc) return;
|
||||||
shortageM3: result.shortage_m3,
|
emitSummary(
|
||||||
});
|
computeMassHaulSeries(
|
||||||
|
params.longitudinal,
|
||||||
|
params.crossSections,
|
||||||
|
context.conversion,
|
||||||
|
context.naturalSpoilMinSlope,
|
||||||
|
["cross"],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw(params: RouteMassHaulDrawParams): void {
|
function draw(params: RouteMassHaulDrawParams): void {
|
||||||
@@ -371,9 +364,12 @@ export function createRouteMassHaulPanel(
|
|||||||
clearSelection = params.onClearSelection;
|
clearSelection = params.onClearSelection;
|
||||||
overlay.hidden = !open;
|
overlay.hidden = !open;
|
||||||
syncHandlePosition();
|
syncHandlePosition();
|
||||||
// 곡선을 그리기 전에 총괄값부터 낸다 — 접혀 있어도 상단줄에는 값이 서야 한다.
|
// 접혀 있어도 상단줄에는 값이 서야 한다 — 이때는 **횡단 기준만** 적분한다.
|
||||||
reportSummary(params);
|
// 펼친 경우에는 아래에서 두 기준을 내므로 그 결과로 총괄값을 낸다(계산 한 번).
|
||||||
if (!open) return;
|
if (!open) {
|
||||||
|
reportSummary(params);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!context) {
|
if (!context) {
|
||||||
note("토량환산계수를 불러오지 못해 유토곡선을 계산할 수 없습니다.");
|
note("토량환산계수를 불러오지 못해 유토곡선을 계산할 수 없습니다.");
|
||||||
return;
|
return;
|
||||||
@@ -400,6 +396,8 @@ export function createRouteMassHaulPanel(
|
|||||||
context.conversion,
|
context.conversion,
|
||||||
context.naturalSpoilMinSlope,
|
context.naturalSpoilMinSlope,
|
||||||
);
|
);
|
||||||
|
// 총괄값은 방금 낸 계열에서 꺼낸다 — 접힘 경로처럼 따로 적분하지 않는다.
|
||||||
|
emitSummary(series);
|
||||||
if (!series.length) {
|
if (!series.length) {
|
||||||
note("횡단 설계가 아직 없어 유토곡선을 그릴 수 없습니다.");
|
note("횡단 설계가 아직 없어 유토곡선을 그릴 수 없습니다.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -85,8 +85,10 @@ const MAX_PANEL_HEIGHT_RATIO = 0.9;
|
|||||||
/** 계획고 편집 후 횡단을 다시 계산하기까지 기다리는 시간(ms).
|
/** 계획고 편집 후 횡단을 다시 계산하기까지 기다리는 시간(ms).
|
||||||
* ▲/▼ 길게 누르기(초당 10회)·끌기에서 매 프레임 다시 돌지 않게 마지막 값만 계산한다.
|
* ▲/▼ 길게 누르기(초당 10회)·끌기에서 매 프레임 다시 돌지 않게 마지막 값만 계산한다.
|
||||||
* 계산이 브라우저 안에서 끝나면서(2026-09-03) 서버 왕복이 사라져 250→60ms 로 줄였다 —
|
* 계산이 브라우저 안에서 끝나면서(2026-09-03) 서버 왕복이 사라져 250→60ms 로 줄였다 —
|
||||||
* 실측 전 측점(128) 재계산 7.5ms 라 손을 떼는 즉시 유토곡선이 따라온다. */
|
* 실측 전 측점(128) 재계산 7.5ms 라 손을 떼는 즉시 유토곡선이 따라온다.
|
||||||
const CROSS_PREVIEW_DEBOUNCE_MS = 60;
|
* 60→16ms(2026-09-06) — ▲ 한 번에 라벨이 서기까지 중앙 130ms 를 쟀는데 그중 60ms 가
|
||||||
|
* 이 대기였다. 한 프레임(16ms)이면 연속 입력은 여전히 마지막 값만 계산한다. */
|
||||||
|
const CROSS_PREVIEW_DEBOUNCE_MS = 16;
|
||||||
/** 구조물 측점을 종단 정본 측점으로 맞춰 주는 허용 오차(m). 층마다 반올림이 달라 생기는
|
/** 구조물 측점을 종단 정본 측점으로 맞춰 주는 허용 오차(m). 층마다 반올림이 달라 생기는
|
||||||
* mm~cm 어긋남만 흡수할 크기다 — 이보다 멀면 서로 다른 측점이다. */
|
* mm~cm 어긋남만 흡수할 크기다 — 이보다 멀면 서로 다른 측점이다. */
|
||||||
const STATION_SNAP_TOLERANCE_M = 0.1;
|
const STATION_SNAP_TOLERANCE_M = 0.1;
|
||||||
|
|||||||
@@ -445,15 +445,18 @@ export function computeMassHaulSeries(
|
|||||||
crossSections: MassHaulSection[],
|
crossSections: MassHaulSection[],
|
||||||
conversion: EarthworkConversion,
|
conversion: EarthworkConversion,
|
||||||
naturalSpoilMinSlope?: number,
|
naturalSpoilMinSlope?: number,
|
||||||
|
/** 낼 기준. 총괄값(배지)만 필요하면 `["cross"]` 로 불러 종단 기준 적분을 건너뛴다. */
|
||||||
|
bases: readonly MassHaulBasis[] = SERIES_BASES,
|
||||||
): MassHaulSeries[] {
|
): MassHaulSeries[] {
|
||||||
const sections = designedSections(crossSections);
|
const sections = designedSections(crossSections);
|
||||||
const sampleSets: Record<MassHaulBasis, AreaSample[]> = {
|
const samplesFor = (basis: MassHaulBasis): AreaSample[] =>
|
||||||
cross: crossAreaSamples(sections, naturalSpoilMinSlope),
|
basis === "cross"
|
||||||
longitudinal: longitudinalAreaSamples(longitudinal, sections),
|
? crossAreaSamples(sections, naturalSpoilMinSlope)
|
||||||
};
|
: longitudinalAreaSamples(longitudinal, sections);
|
||||||
const series: MassHaulSeries[] = [];
|
const series: MassHaulSeries[] = [];
|
||||||
for (const basis of SERIES_BASES) {
|
for (const basis of SERIES_BASES) {
|
||||||
const result = integrate(sampleSets[basis], conversion);
|
if (!bases.includes(basis)) continue;
|
||||||
|
const result = integrate(samplesFor(basis), conversion);
|
||||||
if (result) series.push({ key: basis, basis, result });
|
if (result) series.push({ key: basis, basis, result });
|
||||||
}
|
}
|
||||||
return series;
|
return series;
|
||||||
|
|||||||
Reference in New Issue
Block a user