auto: 2026-09-30 07:36 (ESD_LAPTOP)
This commit is contained in:
@@ -51,6 +51,68 @@ export function rebalanceWeights(
|
||||
return { expected: out.expected, earthwork: out.earthwork, grade: out.grade };
|
||||
}
|
||||
|
||||
/** 비중마다 고정 표시 — 고정한 값은 다른 비중을 움직여도 안 바뀐다. */
|
||||
export type WeightLocks = Record<WeightKey, boolean>;
|
||||
|
||||
export const NO_WEIGHT_LOCKS: WeightLocks = { expected: false, earthwork: false, grade: false };
|
||||
|
||||
const lockedKeys = (locks: WeightLocks): WeightKey[] => WEIGHT_KEYS.filter((name) => locks[name]);
|
||||
|
||||
/** 나머지 둘이 고정돼 저절로 정해지는 비중 — 없으면 null. 이 비중은 잠김으로 보인다. */
|
||||
export function derivedWeightKey(locks: WeightLocks): WeightKey | null {
|
||||
const free = WEIGHT_KEYS.filter((name) => !locks[name]);
|
||||
return free.length === 1 ? free[0] : null;
|
||||
}
|
||||
|
||||
/** 이 비중의 고정 단추를 새로 누를 수 있나 — 셋 다 고정은 막는다(마지막 하나는 저절로 잠김).
|
||||
* 이미 고정한 것을 푸는 것은 늘 된다. */
|
||||
export function canLockWeight(locks: WeightLocks, key: WeightKey): boolean {
|
||||
return locks[key] || lockedKeys(locks).length < 2;
|
||||
}
|
||||
|
||||
/** 슬라이더로 한 비중을 옮긴다 — 고정한 것은 그대로, 안 고정한 것끼리 합 100 을 맞춘다.
|
||||
* 고정하지 않은 것이 셋이면 나머지 둘이 지금 비율대로(`rebalanceWeights`), 둘이면 다른 하나가 남는 몫을
|
||||
* 받고, 이 비중 혼자면(나머지 둘 고정) 값이 정해져 있어 안 움직인다. 값은 남는 몫 안으로 잘린다. */
|
||||
export function moveWeight(
|
||||
weights: RouteInitialWeights,
|
||||
locks: WeightLocks,
|
||||
key: WeightKey,
|
||||
value: number,
|
||||
): RouteInitialWeights {
|
||||
if (locks[key]) return { ...weights };
|
||||
const fixedSum = lockedKeys(locks).reduce((sum, name) => sum + weights[name], 0);
|
||||
const room = 100 - fixedSum;
|
||||
const free = WEIGHT_KEYS.filter((name) => !locks[name]);
|
||||
if (free.length === WEIGHT_KEYS.length) return rebalanceWeights(weights, key, value);
|
||||
const out = { ...weights };
|
||||
if (free.length === 1) {
|
||||
out[key] = room;
|
||||
return out;
|
||||
}
|
||||
const other = free.find((name) => name !== key)!;
|
||||
const next = Math.min(room, Math.max(0, Math.round(Number.isFinite(value) ? value : 0)));
|
||||
out[key] = next;
|
||||
out[other] = room - next;
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 값 칸에 직접 넣은 숫자 — 0~100 정수이고 고정 규칙 아래 합 100 을 맞출 수 있을 때만 받는다.
|
||||
* 못 맞추면 `ok: false`(칸에 빨간 테두리 · 서버에 안 보냄). 고정한 비중은 고칠 수 없다. */
|
||||
export function inputWeight(
|
||||
weights: RouteInitialWeights,
|
||||
locks: WeightLocks,
|
||||
key: WeightKey,
|
||||
value: number,
|
||||
): { ok: boolean; weights: RouteInitialWeights } {
|
||||
const same = { ok: false, weights: { ...weights } };
|
||||
if (locks[key] || !Number.isInteger(value) || value < 0 || value > 100) return same;
|
||||
const room = 100 - lockedKeys(locks).reduce((sum, name) => sum + weights[name], 0);
|
||||
const free = WEIGHT_KEYS.filter((name) => !locks[name]);
|
||||
if (free.length === 1) return value === room ? { ok: true, weights: { ...weights } } : same;
|
||||
if (value > room) return same;
|
||||
return { ok: true, weights: moveWeight(weights, locks, key, value) };
|
||||
}
|
||||
|
||||
/** 결과 비교값 — 서버가 셈해 준 것을 보이기만 한다. 없는 칸은 null. */
|
||||
export interface InitialMetrics {
|
||||
lengthM: number | null;
|
||||
|
||||
Reference in New Issue
Block a user