44import com .google .common .collect .ImmutableSet ;
55import io .luna .game .model .Position ;
66
7- import java .awt .*;
8- import java .awt .geom .Rectangle2D ;
7+ import java .awt .Point ;
98import java .util .LinkedHashSet ;
109import java .util .List ;
1110import java .util .Set ;
1716public class PolygonArea extends Area {
1817
1918 /**
20- * The representation of our arbitrary polygon.
19+ * The rectangular bounding box constraints of this polygon.
2120 */
22- private final Polygon polygon ;
21+ private int southWestX ;
22+ private int southWestY ;
23+ private int northEastX ;
24+ private int northEastY ;
25+
26+ /**
27+ * The points of this polygon.
28+ */
29+ private int npoints ;
30+ private int [] xpoints ;
31+ private int [] ypoints ;
2332
2433 /**
2534 * The precomputed hashcode.
@@ -46,7 +55,7 @@ public class PolygonArea extends Area {
4655 // Precompute hashcode
4756 hashCode = Objects .hashCode (vertices );
4857
49- // Reformat to create java.awt.Polygon
58+ // Extract the points for this polygon from the vertices
5059 int index = 0 ;
5160 int totalSize = vertices .size ();
5261 int [] x = new int [totalSize ];
@@ -56,7 +65,11 @@ public class PolygonArea extends Area {
5665 y [index ] = (int ) point .getY ();
5766 index ++;
5867 }
59- polygon = new Polygon (x , y , totalSize );
68+
69+ this .npoints = totalSize ;
70+ this .xpoints = x ;
71+ this .ypoints = y ;
72+ calculateBounds (xpoints , ypoints , npoints );
6073 }
6174
6275 @ Override
@@ -74,7 +87,69 @@ public int hashCode() {
7487
7588 @ Override
7689 public boolean contains (Position position ) {
77- return polygon .contains (position .getX (), position .getY ());
90+ int x = position .getX ();
91+ int y = position .getY ();
92+
93+ if (npoints <= 2 || !inBounds (x , y )) {
94+ return false ;
95+ }
96+ int hits = 0 ;
97+
98+ int lastx = xpoints [npoints - 1 ];
99+ int lasty = ypoints [npoints - 1 ];
100+ int curx , cury ;
101+
102+ // Walk the edges of the polygon
103+ for (int i = 0 ; i < npoints ; lastx = curx , lasty = cury , i ++) {
104+ curx = xpoints [i ];
105+ cury = ypoints [i ];
106+
107+ if (cury == lasty ) {
108+ continue ;
109+ }
110+
111+ int leftx ;
112+ if (curx < lastx ) {
113+ if (x >= lastx ) {
114+ continue ;
115+ }
116+ leftx = curx ;
117+ } else {
118+ if (x >= curx ) {
119+ continue ;
120+ }
121+ leftx = lastx ;
122+ }
123+
124+ double test1 , test2 ;
125+ if (cury < lasty ) {
126+ if (y < cury || y >= lasty ) {
127+ continue ;
128+ }
129+ if (x < leftx ) {
130+ hits ++;
131+ continue ;
132+ }
133+ test1 = x - curx ;
134+ test2 = y - cury ;
135+ } else {
136+ if (y < lasty || y >= cury ) {
137+ continue ;
138+ }
139+ if (x < leftx ) {
140+ hits ++;
141+ continue ;
142+ }
143+ test1 = x - lastx ;
144+ test2 = y - lasty ;
145+ }
146+
147+ if (test1 < (test2 / (lasty - cury ) * (lastx - curx ))) {
148+ hits ++;
149+ }
150+ }
151+
152+ return ((hits & 1 ) != 0 );
78153 }
79154
80155 @ Override
@@ -97,12 +172,6 @@ public Position randomPosition() {
97172
98173 @ Override
99174 public ImmutableSet <Position > computePositionSet () {
100- // Create a rectangle that encompasses our polygon.
101- Rectangle2D outer = polygon .getBounds2D ();
102- int southWestX = (int ) outer .getMinX ();
103- int southWestY = (int ) outer .getMinY ();
104- int northEastX = (int ) outer .getMaxX ();
105- int northEastY = (int ) outer .getMaxY ();
106175
107176 // Build it into a simple box area, get every position inside.
108177 ImmutableSet <Position > outerPositionSet =
@@ -111,12 +180,37 @@ public ImmutableSet<Position> computePositionSet() {
111180 // Loop through the positions, save the ones contained within our actual polygon.
112181 ImmutableSet .Builder <Position > innerPositionSet = ImmutableSet .builder ();
113182 for (Position outerPosition : outerPositionSet ) {
114- int outerPositionX = outerPosition .getX ();
115- int outerPositionY = outerPosition .getY ();
116- if (polygon .contains (outerPositionX , outerPositionY )) {
183+ if (contains (outerPosition )) {
117184 innerPositionSet .add (outerPosition );
118185 }
119186 }
120187 return innerPositionSet .build ();
121188 }
189+
190+ private void calculateBounds (int [] xpoints , int [] ypoints , int npoints ) {
191+ int boundsMinX = Integer .MAX_VALUE ;
192+ int boundsMinY = Integer .MAX_VALUE ;
193+ int boundsMaxX = Integer .MIN_VALUE ;
194+ int boundsMaxY = Integer .MIN_VALUE ;
195+
196+ for (int i = 0 ; i < npoints ; i ++) {
197+ int x = xpoints [i ];
198+ boundsMinX = Math .min (boundsMinX , x );
199+ boundsMaxX = Math .max (boundsMaxX , x );
200+ int y = ypoints [i ];
201+ boundsMinY = Math .min (boundsMinY , y );
202+ boundsMaxY = Math .max (boundsMaxY , y );
203+ }
204+ this .southWestX = boundsMinX ;
205+ this .southWestY = boundsMinY ;
206+ this .northEastX = boundsMaxX ;
207+ this .northEastY = boundsMaxY ;
208+ }
209+
210+ /**
211+ * Checks if a point is within the bounds. This is an early check to avoid checking all the points via the logic in contains.
212+ */
213+ private boolean inBounds (int x , int y ) {
214+ return x >= this .southWestX && y >= this .southWestY && x < northEastX && y < northEastY ;
215+ }
122216}
0 commit comments