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; };