106 lines
3.8 KiB
TypeScript
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;
|
|
};
|