2025-07-17 22:22:07 +02:00

106 lines
3.8 KiB
TypeScript

import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
import getPreciseDistance from 'geolib/es/getPreciseDistance';
import { computeIntersection } from '../utils/computeIntersection';
/**
* @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.equal(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
let arcRad = 0;
let arcCenter = computeIntersection(
start,
crsOrthogonalOnOrigin,
intcArcOnCrsIntoEndpoint,
crsOrthogonalOnEndpoint
);
if (Math.abs(crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin) <= 0.1 && arcCenter)
arcRad = getPreciseDistance(arcCenter, start);
else {
arcRad = getPreciseDistance(start, end) / 2;
arcCenter = computeDestinationPoint(start, arcRad, crsOrthogonalOnOrigin);
}
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
// Start turn immediately
if (turnDir === 'R') {
crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
} else {
crsOrthogonalOnOrigin -= crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
}
let lastDistance = getPreciseDistance(start, end);
while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
if (turnDir === 'R') {
const delta = (crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin).normaliseDegrees();
crsOrthogonalOnOrigin += delta < 0.1 ? delta : 0.1;
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
} else {
const delta = (crsOrthogonalOnOrigin - crsOrthogonalOnEndpoint).normaliseDegrees();
crsOrthogonalOnOrigin -= delta < 0.1 ? delta : 0.1;
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.normaliseDegrees();
}
const arcFix = computeDestinationPoint(arcCenter, arcRad, crsOrthogonalOnOrigin);
const newDistance = getPreciseDistance(arcFix, end);
if (lastDistance <= newDistance && lastDistance < 25) break;
lastDistance = newDistance;
line.push([arcFix.longitude, arcFix.latitude]);
}
}
return line;
};