resources: add clockwise point sorting helpers#1077
Conversation
|
I really like this in concept, but the code here seems to be vastly over complicated and a bit narrow in scope at the same time, if that makes sense. I think something like the following (only minimally tested) would work much better, and includes sorting for both an array of direction outputs and for points. type AnyDirection = DirectionOutputCardinal | DirectionOutputIntercard | DirectionOutput8 | DirectionOutput16;
// Example usage:
// const dirs: DirectionOutputCardinal[] = ['dirN', 'dirW'];
// dirs.sort(getSortDirectionsClockwiseFunction('dirE'));
// `dirs` should equal `['dirW', 'dirN']`
const getSortDirectionsClockwiseFunction = (from: AnyDirection | undefined) => {
// Default to dirN
let offset = 0;
if (from !== undefined)
offset = output16Dir.indexOf(from);
const count = output16Dir.length;
return (left: AnyDirection, right: AnyDirection) => {
const rightIndex = (count + (output16Dir.indexOf(right) - offset)) % count;
const leftIndex = (count + (output16Dir.indexOf(left) - offset)) % count;
return rightIndex - leftIndex;
};
};
type Point = {
x: number;
y: number;
};
const xyToHeading = (x: number, y: number, centerX: number, centerY: number) => {
x = x - centerX;
y = y - centerY;
return Math.atan2(x, y);
};
// Example usage:
// getSortPointsClockwiseFunction
// const points = [{ x: 101, y: 101 }, { x: 99, y: 99 }];
// points.sort(getSortPointsClockwiseFunction({x: 100, y: 100}, {x: 99, y: 101}));
// `points` should now equal `[{ x: 99, y: 99 }, { x: 101, y: 101 }]`
const getSortPointsClockwiseFunction = <T extends Point>(center: T, reference: number | T = 0) => {
// Convert point to heading if needed
const offset = typeof reference === 'object' ? xyToHeading(reference.x, reference.y, center.x, center.y) : reference;
const twoPI = Math.PI * 2;
return (left: T, right: T) => {
// Get our base headings for the two points
const rightHeading = xyToHeading(right.x, right.y, center.x, center.y);
const leftHeading = xyToHeading(left.x, left.y, center.x, center.y);
// Adjust by reference offset
const rightHeadingOffset = (twoPI + (rightHeading - offset)) % twoPI;
const leftHeadingOffset = (twoPI + (leftHeading - offset)) % twoPI;
return rightHeadingOffset - leftHeadingOffset;
};
}; |
Add Direction utilities for sorting x/y points clockwise from either an explicit reference angle or the edge of a semicircle-contained cluster.
Sorry, the default reference in my example was meant to be north, so it should have been I feel that any sort of automatic attempt to determine the "correct" reference angle is doomed to failure due to edge cases. If any point in the array is controlled by the player, it becomes functionally impossible to account for all edge cases. As such, that logic should not be a generic use case, and instead it should live in the trigger set file it's used by. If I were implementing this, I would write a generic sort function like my example which can be exposed in |
|
@valarnin I've rewritten the implementation, so please review again. |

I am opening this PR because, while thinking about a Korean server strategy implementation for Forsaken, I thought these functions would be useful as shared utilities.
These are two functions that take objects containing
xandycoordinates and return them sorted in clockwise order.sortPointsClockwise
This function sorts target points clockwise around their center point when the points are clustered on one side.
If the target points are on a semicircle, it returns the sorted points; otherwise, it returns
undefined. (There may be cases where the points appear sortable even when they span more than a semicircle, but those are very limited exceptions.)sortPointsClockwise with reference point
This function sorts points clockwise from the position of a specific reference point.
Because there is a reference point, there are no constraints on the positions of the points.
2026-06-13
sortPointsClockwiseFromintosortPointsClockwisevia an optional{ reference }option.sortPointsClockwisenow always returnsT[].