@@ -9,11 +9,16 @@ public class Parser
99 /// Detects sprite regions in a given image represented as a 2D array of pixels.
1010 /// </summary>
1111 /// <param name="image">The 2D array of pixels to detect sprites in.</param>
12+ /// <param name="yTolerance">The variance in the Y axis to account for when grouping
13+ /// sprites together in a single row. (Plus/Minus N amount of pixels.)</param>
1214 /// <param name="transparencyMask">Optional; if provided, this pixel value will be taken
1315 /// as the transparency color for non-transparent images.</param>
1416 /// <returns>A list of <see cref="SpriteRegion"/>s with the locations and dimensions
1517 /// of all sprites found in the image.</returns>
16- public static List < SpriteRegion > DetectSpritesInImage ( Pixel [ , ] image , Pixel ? transparencyMask = null )
18+ public static List < SpriteRegion > DetectSpritesInImage (
19+ Pixel [ , ] image ,
20+ int yTolerance = 3 ,
21+ Pixel ? transparencyMask = null )
1722 {
1823 var detectedSprites = new List < SpriteRegion > ( ) ; // return value
1924 int width = image . GetLength ( 0 ) ;
@@ -40,9 +45,75 @@ public static List<SpriteRegion> DetectSpritesInImage(Pixel[,] image, Pixel? tra
4045 else { visited [ x , y ] = true ; }
4146 }
4247 }
43- return detectedSprites . OrderBy ( r => r . Y ) . ThenBy ( r => r . X ) . ToList ( ) ;
48+ detectedSprites = [ .. detectedSprites . OrderBy ( r => r . Y ) . ThenBy ( r => r . X ) ] ;
49+
50+ var groupedRows = GroupByRow ( detectedSprites , yTolerance ) ;
51+
52+ NormalizeSpriteRows ( groupedRows ) ;
53+
54+ return [ .. groupedRows . SelectMany ( row => row ) ] ;
4455 }
4556
57+
58+ /// <summary>
59+ /// Groups sprite regions by their Y coordinate, allowing for a specified tolerance.
60+ /// </summary>
61+ /// <param name="regions">The full list of detected sprite regions in an image.</param>
62+ /// <param name="yTolerance">The Y-axis potential variance to account for when grouping
63+ /// sprite groups.</param>
64+ /// <returns>A matrix of lists of <see cref="SpriteRegion"/> representing each row of
65+ /// sprites.</returns>
66+ private static List < List < SpriteRegion > > GroupByRow ( List < SpriteRegion > regions , int yTolerance )
67+ {
68+ var rows = new List < List < SpriteRegion > > ( ) ;
69+ foreach ( var region in regions )
70+ {
71+ bool added = false ;
72+ foreach ( var row in rows )
73+ {
74+ // Instead of comparing to just row[0], compare to all regions in the row
75+ if ( row . Any ( r => Math . Abs ( r . Y - region . Y ) <= yTolerance ) )
76+ {
77+ row . Add ( region ) ;
78+ added = true ;
79+ break ;
80+ }
81+ }
82+ if ( ! added ) { rows . Add ( [ region ] ) ; }
83+ }
84+ return rows ;
85+ }
86+
87+
88+ /// <summary>
89+ /// Normalizes the sprite regions in each row to have the same width and height, based
90+ /// on the dimensions of the largest sprite in the row.
91+ /// </summary>
92+ /// <param name="groupedRows">The list of sprite rows (represented as lists of
93+ /// <see cref="SpriteRegion"/>) that forms the spritesheet.</param>
94+ private static void NormalizeSpriteRows ( List < List < SpriteRegion > > groupedRows )
95+ {
96+ foreach ( var row in groupedRows )
97+ {
98+ int maxWidth = row . Max ( r => r . Width ) ;
99+ int maxHeight = row . Max ( r => r . Height ) ;
100+ int maxBottom = row . Max ( r => r . Y + r . Height ) ;
101+ for ( int i = 0 ; i < row . Count ; i ++ )
102+ {
103+ var sprite = row [ i ] ;
104+ int newY = maxBottom - maxHeight ;
105+ int yOffset = sprite . Y - newY ;
106+ int xOffset = ( maxWidth - sprite . Width ) / 2 ;
107+ sprite . Y -= yOffset ; // move up or down to align bottom
108+ sprite . X -= xOffset ; // move left or right to center
109+ sprite . Width = maxWidth ;
110+ sprite . Height = maxHeight ;
111+ row [ i ] = sprite ; // reassign the modified struct
112+ }
113+ }
114+ }
115+
116+
46117 /// <summary>
47118 /// Performs a flood fill algorithm to find the bounding box of a sprite region.
48119 /// </summary>
@@ -92,13 +163,13 @@ private static SpriteRegion FloodFill(Pixel[,] image, int startX, int startY, bo
92163 }
93164 return new SpriteRegion
94165 {
95- X = minX ,
96- Y = minY ,
166+ X = minX , Y = minY ,
97167 Width = maxX - minX + 1 ,
98168 Height = maxY - minY + 1
99169 } ;
100170 }
101171
172+
102173 /// <summary>
103174 /// Gets the neighboring pixels of a given pixel in an image.
104175 /// </summary>
0 commit comments