81 lines
3.4 KiB
TypeScript
81 lines
3.4 KiB
TypeScript
|
|
import { describe, expect, it } from "vitest"
|
||
|
|
import { midpoint, projectOntoAxis } from "./hole-geometry"
|
||
|
|
|
||
|
|
// Testrigg: en rein nord-sør-akse (tee i sør, green 200 m rett nord) --
|
||
|
|
// lett å regne fasit for. 111_320 m per breddegrad er samme tilnærming
|
||
|
|
// som geo.test.ts allerede bruker.
|
||
|
|
const METERS_PER_DEGREE_LAT = 111_320
|
||
|
|
const tee = { lat: 59.9, lng: 10.75 }
|
||
|
|
const green = { lat: 59.9 + 200 / METERS_PER_DEGREE_LAT, lng: 10.75 }
|
||
|
|
|
||
|
|
describe("projectOntoAxis", () => {
|
||
|
|
it("punktet på start (tee) gir alongPercent=0, crossMeters≈0", () => {
|
||
|
|
const { alongPercent, crossMeters } = projectOntoAxis(tee, green, tee)
|
||
|
|
expect(alongPercent).toBeCloseTo(0, 3)
|
||
|
|
expect(crossMeters).toBeCloseTo(0, 3)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("punktet på slutt (green) gir alongPercent=100, crossMeters≈0", () => {
|
||
|
|
const { alongPercent, crossMeters } = projectOntoAxis(tee, green, green)
|
||
|
|
expect(alongPercent).toBeCloseTo(100, 0)
|
||
|
|
expect(crossMeters).toBeCloseTo(0, 1)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("midtpunktet på aksen gir alongPercent≈50, crossMeters≈0", () => {
|
||
|
|
const mid = midpoint(tee, green)
|
||
|
|
const { alongPercent, crossMeters } = projectOntoAxis(tee, green, mid)
|
||
|
|
expect(alongPercent).toBeCloseTo(50, 0)
|
||
|
|
expect(crossMeters).toBeCloseTo(0, 1)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("et punkt rett øst for midtpunktet gir riktig crossMeters mot høyre", () => {
|
||
|
|
// Aksen peker rett nord (bearing 0) -- et punkt rett øst for et punkt
|
||
|
|
// PÅ aksen skal derfor ligge til HØYRE (positiv cross), på samme
|
||
|
|
// langs-posisjon (alongPercent uendret).
|
||
|
|
const mid = midpoint(tee, green)
|
||
|
|
const offsetEast = { lat: mid.lat, lng: mid.lng + 20 / (METERS_PER_DEGREE_LAT * Math.cos((mid.lat * Math.PI) / 180)) }
|
||
|
|
const { alongPercent, crossMeters } = projectOntoAxis(tee, green, offsetEast)
|
||
|
|
expect(alongPercent).toBeCloseTo(50, 0)
|
||
|
|
expect(crossMeters).toBeGreaterThan(19)
|
||
|
|
expect(crossMeters).toBeLessThan(21)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("et punkt rett vest for midtpunktet gir negativ crossMeters (venstre)", () => {
|
||
|
|
const mid = midpoint(tee, green)
|
||
|
|
const offsetWest = { lat: mid.lat, lng: mid.lng - 20 / (METERS_PER_DEGREE_LAT * Math.cos((mid.lat * Math.PI) / 180)) }
|
||
|
|
const { crossMeters } = projectOntoAxis(tee, green, offsetWest)
|
||
|
|
expect(crossMeters).toBeLessThan(-19)
|
||
|
|
expect(crossMeters).toBeGreaterThan(-21)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("et punkt bak tee (før start) gir negativ alongPercent", () => {
|
||
|
|
const behindTee = { lat: tee.lat - 50 / METERS_PER_DEGREE_LAT, lng: tee.lng }
|
||
|
|
const { alongPercent } = projectOntoAxis(tee, green, behindTee)
|
||
|
|
expect(alongPercent).toBeLessThan(0)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("et punkt forbi green gir alongPercent > 100", () => {
|
||
|
|
const beyondGreen = { lat: green.lat + 50 / METERS_PER_DEGREE_LAT, lng: green.lng }
|
||
|
|
const { alongPercent } = projectOntoAxis(tee, green, beyondGreen)
|
||
|
|
expect(alongPercent).toBeGreaterThan(100)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("axisStart == axisEnd degraderer grasiøst (ingen NaN/deling på null)", () => {
|
||
|
|
const { alongPercent, crossMeters } = projectOntoAxis(tee, tee, green)
|
||
|
|
expect(Number.isFinite(alongPercent)).toBe(true)
|
||
|
|
expect(Number.isFinite(crossMeters)).toBe(true)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe("midpoint", () => {
|
||
|
|
it("midtpunktet av et punkt med seg selv er punktet selv", () => {
|
||
|
|
expect(midpoint(tee, tee)).toEqual(tee)
|
||
|
|
})
|
||
|
|
|
||
|
|
it("midtpunktet ligger halvveis mellom to punkt", () => {
|
||
|
|
const m = midpoint(tee, green)
|
||
|
|
expect(m.lat).toBeCloseTo((tee.lat + green.lat) / 2, 10)
|
||
|
|
expect(m.lng).toBeCloseTo((tee.lng + green.lng) / 2, 10)
|
||
|
|
})
|
||
|
|
})
|