2025-07-14 03:31:57 +02:00

106 lines
3.4 KiB
TypeScript

import * as geolib from "geolib";
import { computeIntersection } from "../utils/computeIntersection.ts";
/**
* @param crsIntoEndpoint Course into arc endpoint
* @param crsFromOrigin Course from arc origin point
* @param start Arc origin point
* @param end Leg endpoint
* @param turnDir Turn direction
* @returns Line segments forming arc from `start`to `end`
*/
export const generateTangentArc = (
crsIntoEndpoint: number,
crsFromOrigin: number,
start: NavFix,
end: NavFix,
turnDir?: TurnDirection
) => {
const line: LineSegment[] = [[start.longitude, start.latitude]];
// Check if there even is an arc
if (crsFromOrigin !== crsIntoEndpoint) {
// Course to the end of the arc
let crsFromStartToEnd;
if (!turnDir || turnDir === "E") {
let prov = crsFromOrigin - crsIntoEndpoint;
prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov;
turnDir = prov > 0 ? "L" : "R";
}
if (turnDir === "R") {
const delta = (360 - crsFromOrigin + crsIntoEndpoint).normaliseDegrees();
crsFromStartToEnd = (crsFromOrigin + delta / 2).normaliseDegrees();
} else {
const delta = (crsFromOrigin + 360 - crsIntoEndpoint).normaliseDegrees();
crsFromStartToEnd = (crsFromOrigin - delta / 2).normaliseDegrees();
}
// Arc end
const intcArcOnCrsIntoEndpoint = computeIntersection(
start,
crsFromStartToEnd,
end,
crsIntoEndpoint.reciprocalCourse()
);
if (!intcArcOnCrsIntoEndpoint) return null;
let crsOrthogonalOnOrigin;
let crsOrthogonalOnEndpoint;
if (turnDir === "R") {
crsOrthogonalOnOrigin = (crsFromOrigin + 90).normaliseDegrees();
crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees();
} else {
crsOrthogonalOnOrigin = (crsFromOrigin - 90).normaliseDegrees();
crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees();
}
// Generate arc
const arcCenter = computeIntersection(
start,
crsOrthogonalOnOrigin,
intcArcOnCrsIntoEndpoint,
crsOrthogonalOnEndpoint
);
if (!arcCenter) return null;
const arcRad = geolib.getDistance(arcCenter, start);
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
// Start turn immediately
if (turnDir === "R") {
crsOrthogonalOnOrigin +=
crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
} else {
crsOrthogonalOnOrigin -=
crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
}
while (crsOrthogonalOnOrigin !== crsOrthogonalOnEndpoint) {
if (turnDir === "R") {
const delta = (
crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin
).normaliseDegrees();
crsOrthogonalOnOrigin += delta < 1 ? delta : 1;
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
} else {
const delta = (
crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint
).normaliseDegrees();
crsOrthogonalOnOrigin -= delta < 1 ? delta : 1;
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
}
if (crsOrthogonalOnOrigin === crsOrthogonalOnEndpoint) break;
const arcFix = geolib.computeDestinationPoint(
arcCenter,
arcRad,
crsOrthogonalOnOrigin
);
line.push([arcFix.longitude, arcFix.latitude]);
}
}
return line;
};