GhiVideo 소스 복제 — v4 작업 시작 기준
기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제. (node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+62
@@ -0,0 +1,62 @@
|
||||
import { ROUTE_LEGS } from '../mocks/route';
|
||||
import type { RouteLeg } from '../types/timeline';
|
||||
|
||||
const MILEAGE_ROUND_M = 10;
|
||||
const METERS_PER_KM = 1000;
|
||||
|
||||
/** Mileage in meters at a cursor px, via piecewise-linear leg anchors. */
|
||||
export function mileageAtPx(px: number, legs: RouteLeg[] = ROUTE_LEGS): number {
|
||||
const leg =
|
||||
legs.find((l) => px >= l.startPx && px <= l.endPx) ?? legs[legs.length - 1];
|
||||
const anchors = leg.anchors;
|
||||
for (let i = 0; i < anchors.length - 1; i++) {
|
||||
const a = anchors[i];
|
||||
const b = anchors[i + 1];
|
||||
if (px <= b.px || i === anchors.length - 2) {
|
||||
const t = Math.min(1, Math.max(0, (px - a.px) / (b.px - a.px)));
|
||||
return a.mileage + (b.mileage - a.mileage) * t;
|
||||
}
|
||||
}
|
||||
return anchors[0].mileage;
|
||||
}
|
||||
|
||||
/**
|
||||
* First px at which the given mileage occurs. Leg start anchors win so a jump
|
||||
* to a turn mileage lands on the start of that leg.
|
||||
*/
|
||||
export function pxForMileage(
|
||||
mileage: number,
|
||||
legs: RouteLeg[] = ROUTE_LEGS,
|
||||
): number | null {
|
||||
for (const leg of legs) {
|
||||
if (leg.anchors[0].mileage === mileage) return leg.startPx;
|
||||
}
|
||||
for (const leg of legs) {
|
||||
const anchors = leg.anchors;
|
||||
for (let i = 0; i < anchors.length - 1; i++) {
|
||||
const a = anchors[i];
|
||||
const b = anchors[i + 1];
|
||||
const lo = Math.min(a.mileage, b.mileage);
|
||||
const hi = Math.max(a.mileage, b.mileage);
|
||||
if (mileage >= lo && mileage <= hi) {
|
||||
return a.px + (b.px - a.px) * ((mileage - a.mileage) / (b.mileage - a.mileage));
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 158204 → "158k200". */
|
||||
export function formatMileage(mileage: number): string {
|
||||
const rounded = Math.round(mileage / MILEAGE_ROUND_M) * MILEAGE_ROUND_M;
|
||||
const km = Math.floor(rounded / METERS_PER_KM);
|
||||
const m = rounded % METERS_PER_KM;
|
||||
return `${km}k${String(m).padStart(3, '0')}`;
|
||||
}
|
||||
|
||||
/** Accepts "158k200", "158200", "161800"… Returns meters or null. */
|
||||
export function parseMileageQuery(raw: string): number | null {
|
||||
const digits = raw.toLowerCase().replace(/k/g, '').replace(/[^0-9]/g, '');
|
||||
const mileage = parseInt(digits, 10);
|
||||
return Number.isFinite(mileage) && mileage > 0 ? mileage : null;
|
||||
}
|
||||
Reference in New Issue
Block a user