import type { Point } from '../../utils/geometry/points.js'; interface McchOptions { /** * Whether the input points are already sorted or not (sort by column and then row). * @default `false` */ sorted?: boolean; } /** * Computes the convex hull of a binary image using Andrew's Monotone Chain Algorithm * @see {@link http://www.algorithmist.com/index.php/Monotone_Chain_Convex_Hull} * @param points - An array of points. * @param options - MCCH Algorithm options. * @returns Coordinates of the convex hull in clockwise order. */ export function monotoneChainConvexHull( points: Point[], options: McchOptions = {}, ): Point[] { const { sorted = false } = options; if (!sorted) { points = points.slice(); points.sort(byXThenY); } const n = points.length; const result = new Array(n * 2); let k = 0; for (let i = 0; i < n; i++) { const point = points[i]; while (k >= 2 && cw(result[k - 2], result[k - 1], point) <= 0) { k--; } result[k++] = point; } const t = k + 1; for (let i = n - 2; i >= 0; i--) { const point = points[i]; while (k >= t && cw(result[k - 2], result[k - 1], point) <= 0) { k--; } result[k++] = point; } return result.slice(0, k - 1); } function cw(p1: Point, p2: Point, p3: Point) { return ( (p2.row - p1.row) * (p3.column - p1.column) - (p2.column - p1.column) * (p3.row - p1.row) ); } function byXThenY(point1: Point, point2: Point) { if (point1.column === point2.column) { return point1.row - point2.row; } return point1.column - point2.column; } // 0 -> column, 1 -> row