teecup/frontend/lib/hole-geometry.ts
Erol Haagenrud 6f79df6eab Visuelt hull-diagram for rangefinderen (tee nederst, green øverst)
Fikser to ting rapportert fra ekte produksjonsskjermbilde: (1) kun
nærmeste hindring ble vist -- target-distance.tsx viser nå ALLE
hindringer, sortert nærmest først, uavhengig av om et diagram kan
tegnes. (2) Nytt hull-diagram (hole-diagram-view.tsx) plasserer
hindringer og spiller i 2D langs en fast tee->green-akse, tee nederst/
green øverst konsistent uansett gangretning.

Ny geometri-modul frontend/lib/hole-geometry.ts (projectOntoAxis,
bygget på eksisterende haversineMeters/bearingDegrees) med 10
enhetstester som beviser matematikken riktig før noe visuelt ble
bygget. Verifisert i scratch mot presist kjente syntetiske koordinater
-- alle tall og posisjoner stemte eksakt med håndregnede fasitverdier.

Midlertidig hånd-kodet visning, venter på V0-eksport for den polerte
versjonen (samme mønster som FlagPlantSheet/PlayerImportPanel). Ingen
migrasjon, ingen backend-endring.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 16:46:50 +02:00

39 lines
1.8 KiB
TypeScript

import { type LatLng, bearingDegrees, haversineMeters } from "./geo"
/**
* Projiserer et vilkårlig punkt ned på en fast akse (typisk tee->green),
* for et hull-diagram der "langs hullet" og "til siden" skal vises
* uavhengig av spillerens gangretning -- ren klientberegning, ingen
* Mapbox-kall (samme ADR-048-kostnadskontroll-prinsipp som geo.ts).
*
* `alongPercent`: 0 ved axisStart, 100 ved axisEnd -- IKKE klippet her
* (kan bli <0 eller >100 hvis punktet ligger bak start/forbi slutt),
* klipping skjer kun ved selve visningen.
* `crossMeters`: avstand til siden av aksen. Positiv = til høyre for
* aksens retning (sett fra axisStart mot axisEnd), negativ = venstre --
* samme klokke-positive konvensjon som bearingDegrees.
*/
export function projectOntoAxis(
axisStart: LatLng,
axisEnd: LatLng,
point: LatLng,
): { alongPercent: number; crossMeters: number } {
const axisLength = haversineMeters(axisStart, axisEnd)
if (axisLength === 0) {
return { alongPercent: 0, crossMeters: haversineMeters(axisStart, point) }
}
const axisBearing = bearingDegrees(axisStart, axisEnd)
const pointDistance = haversineMeters(axisStart, point)
const pointBearing = bearingDegrees(axisStart, point)
// Normaliser vinkeldifferansen til (-180, 180] før trigonometri.
const angleDiff = (((pointBearing - axisBearing + 540) % 360) - 180) * (Math.PI / 180)
const along = pointDistance * Math.cos(angleDiff)
const cross = pointDistance * Math.sin(angleDiff)
return { alongPercent: (along / axisLength) * 100, crossMeters: cross }
}
/** Midtpunkt mellom to koordinater (enkel plan-tilnærming -- presist nok
* på hull-skala, samme antakelse som resten av geo.ts). */
export function midpoint(a: LatLng, b: LatLng): LatLng {
return { lat: (a.lat + b.lat) / 2, lng: (a.lng + b.lng) / 2 }
}