@@ -264,16 +264,16 @@ const output16Dir: DirectionOutput16[] = [
264264const outputCardinalDir : DirectionOutputCardinal [ ] = [ 'dirN' , 'dirE' , 'dirS' , 'dirW' ] ;
265265const outputIntercardDir : DirectionOutputIntercard [ ] = [ 'dirNE' , 'dirSE' , 'dirSW' , 'dirNW' ] ;
266266
267- const compareDirectionOutput = ( a : DirectionOutput16 , b : DirectionOutput16 ) : number => {
268- const getIndex = ( n : DirectionOutput16 ) => {
269- const index = output16Dir . indexOf ( n ) ;
270- // Values outside of output16Dir (i.e. 'unknown') sort last
271- if ( index < 0 )
272- return output16Dir . length ;
273- return index ;
274- } ;
267+ const getDirectionIndex = ( n : DirectionOutput16 ) => {
268+ const index = output16Dir . indexOf ( n ) ;
269+ // Values outside of output16Dir (i.e. 'unknown') sort last
270+ if ( index < 0 )
271+ return output16Dir . length ;
272+ return index ;
273+ } ;
275274
276- return getIndex ( a ) - getIndex ( b ) ;
275+ const compareDirectionOutput = ( a : DirectionOutput16 , b : DirectionOutput16 ) : number => {
276+ return getDirectionIndex ( a ) - getDirectionIndex ( b ) ;
277277} ;
278278
279279const outputStrings16Dir : OutputStrings = {
@@ -356,6 +356,12 @@ const xyTo4DirIntercardNum = (x: number, y: number, centerX: number, centerY: nu
356356 return Math . round ( 2 - 2 * ( ( Math . PI / 4 ) + Math . atan2 ( x , y ) ) / Math . PI ) % 4 ;
357357} ;
358358
359+ const xyToHeading = ( x : number , y : number , centerX : number , centerY : number ) : number => {
360+ x = x - centerX ;
361+ y = y - centerY ;
362+ return Math . atan2 ( x , y ) ;
363+ } ;
364+
359365const hdgTo16DirNum = ( heading : number ) : number => {
360366 // N = 0, NNE = 1, ..., NNW = 15
361367 return ( Math . round ( 8 - 8 * heading / Math . PI ) % 16 + 16 ) % 16 ;
@@ -371,6 +377,10 @@ const hdgTo4DirNum = (heading: number): number => {
371377 return ( Math . round ( 2 - heading * 2 / Math . PI ) % 4 + 4 ) % 4 ;
372378} ;
373379
380+ const outputFrom16DirNum = ( dirNum : number ) : DirectionOutput16 => {
381+ return output16Dir [ dirNum ] ?? 'unknown' ;
382+ } ;
383+
374384const outputFrom8DirNum = ( dirNum : number ) : DirectionOutput8 => {
375385 return output8Dir [ dirNum ] ?? 'unknown' ;
376386} ;
@@ -383,6 +393,88 @@ const outputFromIntercardNum = (dirNum: number): DirectionOutputIntercard => {
383393 return outputIntercardDir [ dirNum ] ?? 'unknown' ;
384394} ;
385395
396+ export type AnyDirection =
397+ | DirectionOutputCardinal
398+ | DirectionOutputIntercard
399+ | DirectionOutput8
400+ | DirectionOutput16 ;
401+
402+ /**
403+ * Get a function to pass to Array.sort to sort an array of DirectionOutput entries
404+ *
405+ * @example
406+ * const dirs: DirectionOutputCardinal[] = ['dirN', 'dirW'];
407+ *
408+ * dirs.sort(getSortDirectionsClockwiseFunction('dirE'));
409+ *
410+ * // `dirs` should equal `['dirW', 'dirN']`
411+ *
412+ * @param from The DirectionOutput to treat as the start point for sort comparison
413+ * @returns A function to pass to the Array.sort function
414+ */
415+ export const getSortDirectionsClockwiseFunction = (
416+ from ?: AnyDirection ,
417+ ) : ( left : AnyDirection , right : AnyDirection ) => number => {
418+ // Default to dirN
419+ let offset = 0 ;
420+ if ( from !== undefined && from !== 'unknown' )
421+ offset = getDirectionIndex ( from ) ;
422+
423+ const count = output16Dir . length ;
424+
425+ return ( left : AnyDirection , right : AnyDirection ) => {
426+ if ( left === 'unknown' || right === 'unknown' ) {
427+ return left === right ? 0 : left === 'unknown' ? 1 : - 1 ;
428+ }
429+ const rightIndex = ( count + getDirectionIndex ( right ) - offset ) % count ;
430+ const leftIndex = ( count + getDirectionIndex ( left ) - offset ) % count ;
431+ return leftIndex - rightIndex ;
432+ } ;
433+ } ;
434+
435+ type Point = {
436+ x : number ;
437+ y : number ;
438+ } ;
439+
440+ /**
441+ * Get a function to pass to Array.sort to sort an array of objects with `x` and `y` properties
442+ *
443+ * @example
444+ * const points = [{ x: 101, y: 101 }, { x: 99, y: 99 }];
445+ *
446+ * points.sort(getSortPointsClockwiseFunction({x: 100, y: 100}, {x: 99, y: 101}));
447+ *
448+ * // `points` should now equal `[{ x: 99, y: 99 }, { x: 101, y: 101 }]`
449+ *
450+ * @param center The x/y point to treat as the center to calculate headings from
451+ * @param reference The heading or x/y point to treat as the start point for sort comparison
452+ * @returns A function to pass to the Array.sort function
453+ */
454+ export const getSortPointsClockwiseFunction = < T extends Point > (
455+ center : T ,
456+ reference : number | T = Math . PI , // Default to north
457+ ) : ( left : T , right : T ) => number => {
458+ // Convert point to heading if needed
459+ const offset = typeof reference === 'object'
460+ ? xyToHeading ( reference . x , reference . y , center . x , center . y )
461+ : reference ;
462+
463+ const twoPI = Math . PI * 2 ;
464+
465+ return ( left : T , right : T ) => {
466+ // Get our base headings for the two points
467+ const rightHeading = xyToHeading ( right . x , right . y , center . x , center . y ) ;
468+ const leftHeading = xyToHeading ( left . x , left . y , center . x , center . y ) ;
469+
470+ // Adjust by reference offset
471+ const rightHeadingOffset = ( twoPI + ( offset - rightHeading ) ) % twoPI ;
472+ const leftHeadingOffset = ( twoPI + ( offset - leftHeading ) ) % twoPI ;
473+
474+ return leftHeadingOffset - rightHeadingOffset ;
475+ } ;
476+ } ;
477+
386478export const Directions = {
387479 output8Dir : output8Dir ,
388480 output16Dir : output16Dir ,
@@ -397,11 +489,14 @@ export const Directions = {
397489 xyTo16DirNum : xyTo16DirNum ,
398490 xyTo8DirNum : xyTo8DirNum ,
399491 xyTo4DirNum : xyTo4DirNum ,
492+ xyToHeading : xyToHeading ,
400493 hdgTo16DirNum : hdgTo16DirNum ,
401494 hdgTo8DirNum : hdgTo8DirNum ,
402495 hdgTo4DirNum : hdgTo4DirNum ,
496+ outputFrom16DirNum : outputFrom16DirNum ,
403497 outputFrom8DirNum : outputFrom8DirNum ,
404498 outputFromCardinalNum : outputFromCardinalNum ,
499+ outputFromIntercardNum : outputFromIntercardNum ,
405500 combatantStatePosTo8Dir : (
406501 combatant : PluginCombatantState ,
407502 centerX : number ,
@@ -452,6 +547,10 @@ export const Directions = {
452547 const dirNum = hdgTo8DirNum ( heading ) ;
453548 return outputFrom8DirNum ( dirNum ) ;
454549 } ,
550+ xyTo16DirOutput : ( x : number , y : number , centerX : number , centerY : number ) : DirectionOutput16 => {
551+ const dirNum = xyTo16DirNum ( x , y , centerX , centerY ) ;
552+ return outputFrom16DirNum ( dirNum ) ;
553+ } ,
455554 xyTo8DirOutput : ( x : number , y : number , centerX : number , centerY : number ) : DirectionOutput8 => {
456555 const dirNum = xyTo8DirNum ( x , y , centerX , centerY ) ;
457556 return outputFrom8DirNum ( dirNum ) ;
0 commit comments