-
Notifications
You must be signed in to change notification settings - Fork 0
/
QuickHullMethods.java
319 lines (293 loc) · 18.1 KB
/
QuickHullMethods.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
package JavaLearningConvexHull;
import java.util.*;
import java.awt.Point;
//This class will hold all of our convex hull static methods
public class QuickHullMethods {
//First we will make a static helper method to find the extrema for the line `P1P2`
//It will return an array with the `Point` `min` at the index `[0]` and `Point` `max` ->
//-> at the index `[1]`
public static Point[] findExtrema(ArrayList<Point> list){
Iterator<Point> iter = list.iterator();
//Making the starting `min` and `max` points something that will always be changed ->
//-> on first iteration
Point min = new Point(51,0);
Point max = new Point(-1, 0);
while(iter.hasNext() == true){
Point curPoint = iter.next();
//This will always run only the first time
if(curPoint.getX() < min.getX() && curPoint.getX() > max.getX()){
min = curPoint;
max = curPoint;
}
else if(curPoint.getX() < min.getX()){
min = curPoint;
}
else if(curPoint.getX() > max.getX()){
max = curPoint;
}
//System.out.println(curPoint);
}
System.out.println();
// System.out.println("Min: " + min);
// System.out.println("Max: " + max);
list.remove(min);
list.remove(max);
Point[] extrema = {min,max};
return extrema;
}
//This static method finds the furthest point from the two extrema and add it to the ->
//-> the convex hull
//NOTE: A -> B is right side list (`sR`) and B -> A is left side list (`sL`)
public static ArrayList<Point> findFurthest(ArrayList<Point> finalConvexHull, ArrayList<Point> sList, Point min, Point max, Boolean isAboveOrBelow){
if(sList.size() == 0){//Base case for recursion: If the `ArrayList` `list` is empty ->
//-> then `return null`
return null;
}
else{//Iterate through the `ArrayList` of points `sList` and calculate which point ->
//-> is the furthest by adding the distance from the min to the point, to the ->
//-> distance from the point to the max
Point C = new Point();
for(int i = 0; i < sList.size(); i++){
if(i == 0){
C = sList.get(i);
}
//IMPORTANT NOTE: We are not using the "sqrt" in the "distance formula" ->
//-> because the solution is monotonically increasing, so with or without ->
//-> the "sqrt" we will get the furthest point from `min`/`max` and ->
//-> removing the `sqrt` releases the load on the machine from having ->
//-> to calculate the `sqrt` everytime
//NOTE: My distance formula was having a problem so now I am going to comment ->
//-> it out and try the built in static method `distanceSq(i,i,i,i)` ->
//-> in the `Point` class (I have no doubt it is because I messed up the ->
//-> calculation somewhere inside that MESS of parenthesis)
//else if((Math.sqrt(Math.pow(sList.get(i).getX()-min.getX(), 2)+Math.pow(sList.get(i).getY()-min.getY(), 2))+Math.sqrt(Math.pow(max.getX()-sList.get(i).getX(), 2)+Math.pow(max.getY()-sList.get(i).getY(), 2))) > Math.sqrt(Math.pow(C.getX()-min.getX(), 2)+Math.pow(C.getY()-min.getY(), 2))+Math.sqrt(Math.pow(max.getX()-C.getX(), 2)+Math.pow(max.getY()-C.getY(), 2))){
else if((Point.distance(min.getX(), min.getY(), sList.get(i).getX(), sList.get(i).getY()) + Point.distance(sList.get(i).getX(), sList.get(i).getY(), max.getX(), max.getY())) > (Point.distance(min.getX(), min.getY(), C.getX(), C.getY()) + Point.distance(C.getX(), C.getY(), max.getX(), max.getY()))){
C = sList.get(i);
}
}
//Add the furthest point `C` from `min`/`max` to the convex hull points ->
//-> `ArrayList` `finalConvexHull` between the `min` and `max`
//The additional points added to the `finalConvexHull` `ArrayList` come into ->
//-> play for painting the points on the canvas (look at `JavaFXQuickHullMain` ->
//-> for a better understanding of why)
if(isAboveOrBelow == true){
finalConvexHull.add(finalConvexHull.indexOf(min)+1, C);
finalConvexHull.add(finalConvexHull.indexOf(C), new Point(-1,-1));//Means above
}
else{
finalConvexHull.add(finalConvexHull.indexOf(min)+1, C);
finalConvexHull.add(finalConvexHull.indexOf(C), new Point(-2,-2));//Means below
}
sList.remove(C);
// System.out.println("C is: " + C);
// System.out.println("Min is: " + min);
// System.out.println("Max is: " + max);
//We are not going to have a `s0` just an `s1` and a `s2` that we will add ->
//-> points to
//Slopes from `min` to `C` and `C` to `max` so we do not have to calculate them ->
//-> everytime
double minCSlope = (C.getY()-min.getY())/(C.getX()-min.getX());
double maxCSlope = (max.getY()-C.getY())/(max.getX()-C.getX());
//1ArrayList1s to store the `s1` (`sL`) and `s2` (`sR`)
ArrayList<Point> sL = new ArrayList<Point>();
ArrayList<Point> sR = new ArrayList<Point>();
for(int i = 0; i < sList.size(); i++){
//IMPORTANT NOTE FOR BELOW: If point `C` (the furthest point) is above or ->
//-> below (left or right) the slope from `min` to `max` then the way ->
//-> to determine where all other points fall from the triangle made ->
//-> between `min`, `max`, and `C` is different which is why below we ->
//-> have an if statement to determine whether the point `C` currently ->
//-> being used is above or below (left or right) the slope from `min` to ->
//-> `max` and the condition in the if statement just checks whether the ->
//-> the slope from `min` to `C` is negative or positive
//NOTE: Have to do it a new way because I forgot that `minCSlope` is ->
//-> calculated with `C` and not `max` meaning a point above the slope ->
//-> from `min` to `max` could be determined as below when it should be ->
//-> determined as above
//if(minCSlope > 0){
//NEW CONDITION USED BELOW:
//I really did not want to resend `max` and `min` everytime and re-calculate ->
//-> the `min` to `max` slope everytime so to get around this I just used ->
//-> a boolean in the initial and recursive calls that marks it as an ->
//-> above (`true`) or below (`below`) `findFurthest()` function call
if(isAboveOrBelow == true){
//If the slope from `min` to a point is greater than `min` to `C` then we ->
//-> know that this unknown point is on the left side of the line from ->
//-> `min` to `C`
//IMPORTANT NOTE: This does not mean that all other points will be on ->
//-> the right side of the line from `C` to `max` because they could fall ->
//-> in the middle of the triangle instead (we will need another check ->
//-> to determine if a point is on the right side of the line from `C` to `max`)
if((sList.get(i).getY()-min.getY())/(sList.get(i).getX()-min.getX()) > minCSlope){
sL.add(sList.get(i));
// System.out.println("LEFT SIDE OF TRI (S1): " + sList.get(i));
}
//If the slope from some point to `max` is less than the slope from `C` to ->
//-> `max` and it already failed the check of having a greater slope than ->
//-> `min` to `C` then we know that the point is on the right side of the ->
//-> line from `C` to `max`
else if((max.getY()-sList.get(i).getY())/(max.getX()-sList.get(i).getX()) < maxCSlope){
sR.add(sList.get(i));
// System.out.println("RIGHT SIDE OF TRI (S2): " + sList.get(i));
}
else{
// System.out.println("[S0 Detected] " + sList.get(i));
}
}
else{
if((sList.get(i).getY()-min.getY())/(sList.get(i).getX()-min.getX()) < minCSlope){
sL.add(sList.get(i));
//System.out.println("LEFT SIDE OF TRI (S1): " + sList.get(i));
}
else if((max.getY()-sList.get(i).getY())/(max.getX()-sList.get(i).getX()) > maxCSlope){
sR.add(sList.get(i));
//System.out.println("RIGHT SIDE OF TRI (S2): " + sList.get(i));
}
else{
//System.out.println("[S0 Detected] " + sList.get(i));
}
}
}
//Recursively call the static function `findFurthest()` with the newly determined ->
//-> points and `C` as one of the extrema for the differing sides (`sL` or `sR`)
findFurthest(finalConvexHull, sL, min, C, isAboveOrBelow);
findFurthest(finalConvexHull, sR, C, max, isAboveOrBelow);
}
return null;
}
//Static method for actually assembling all of the `Point` points that make the ->
//-> convex hull and putting them into an `ArrayList` to be returned
public static ArrayList<Point> convexHullAssembler(ArrayList<Point> list){
ArrayList<Point> finalConvexHull = new ArrayList<Point>(2);//This `ArrayList` instance (reference/pointer variable) ->
//-> will hold all the `Point` points at the very end that make up the convex hull
Point[] extrema = findExtrema(list);
Point min = extrema[0];
Point max = extrema[1];
finalConvexHull.add(min);
finalConvexHull.add(max);
ArrayList<Point> sL = new ArrayList<Point>();//Left extrema line
ArrayList<Point> sR = new ArrayList<Point>();//Right extrema line
//Check if the slope from `min` to `max` is positive, negative, or no slope because ->
//-> it changes the conditions for determining if a point is on the left or right side
if(min.getY() < max.getY()){//Positive slope from `min` to `max`
//First: we must find what the slope between `min` and `max` is to be used later for ->
//-> which side of the slope a point is on
//NOTE: `MMS` stands for "MinMaxSlope"
double MMS = ((max.getY()-min.getY())/(max.getX()-min.getX()));
for(int i = 0; i < list.size(); i++){
// //BELOW: is if the slope from `min` to `max` is positive
// //If a point's slope to the `min` is >= 1 it will ALWAYS be on the left side
// //NOTE: a point that has a higher `Y` value than the `max` `Y` value will always ->
// //-> have a negative slope to `max` meaning it must be on its left side
// if(((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) >= 1)){
// sL.add(list.get(i));
// System.out.println("LEFT SIDE: " + list.get(i));
// }
// //*If a point's slope to the `min` is < 1 there are two options:*
// //1.) The point's slope to the `max` is also < 1 in which the point is on the ->
// //-> left side
// else if(((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) < 1) && (max.getY()-list.get(i).getY())/(max.getX()-list.get(i).getX()) < 1){
// sL.add(list.get(i));
// System.out.println("LEFT SIDE: " + list.get(i));
// }
// //2.) The point's slope to the `max` is NOT also < 1 in which the point is on ->
// //-> the right side
// else{
// sR.add(list.get(i));
// System.out.println("RIGHT SIDE: " + list.get(i));
// }
//Above is the old and worse way of finding which side the point is on ->
//-> (but I put so much work into that first method I didnt have the heart ->
//-> to delete it so now it will be eternally memorialized in commented out ->
//-> form)
//The point's slope to `min` is > `MMS` meaning it will ALWAYS be on the ->
//-> left side
if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) > MMS){
sL.add(list.get(i));
//System.out.println("LEFT SIDE: " + list.get(i));
}
//The point's slope to `min` is <= `MMS` meaning it will ALWAYS be on the ->
//-> right side
//NOTE: The `=` in the inequality `<=` means that if the point's slope to ->
//-> the `min` is the same as the slope from `min` to `max` then it will ->
//-> arbitrarily be put on the right side
//NOTE: I could just make the `else if()` statement below just an `else` ->
//-> statement but I wanted to be able to show how the other condition is ->
//-> found
else if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) <= MMS){
sR.add(list.get(i));
//System.out.println("RIGHT SIDE: " + list.get(i));
}
}
}
else if(min.getY() > max.getY()){//Negative slope from `min` to `max`
//NOTE: The left side is below and the right side is above
//First: we must find what the slope between `min` and `max` is to be used later for ->
//-> which side of the slope a point is on
//NOTE: `MMS` stands for "MinMaxSlope"
double MMS = ((max.getY()-min.getY())/(max.getX()-min.getX()));
for(int i = 0; i < list.size(); i++){
// //If the point's slope to `min` is >= 0 then it will ALWAYS be on the right ->
// //-> side
// if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) >= 0){
// sR.add(list.get(i));
// System.out.println("RIGHT SIDE: " + list.get(i));
// }
// //*If the point's slope to `min` is < 0 there are two options:*
// //1.) The point's slope to `max` is < -1 in which the point is on the right ->
// //-> side
// else if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) < 0 && ((max.getY()-list.get(i).getY())/(max.getX()-list.get(i).getX()) <= -1)){
// sR.add(list.get(i));
// System.out.println("RIGHT SIDE: " + list.get(i));
// }
// //2.) The point's slope to `max` is > -1 in which the point is on the left ->
// //-> side
// else{
// sL.add(list.get(i));
// System.out.println("LEFT SIDE: " + list.get(i));
// }
// Above is the old and worse way of finding which side the point is on ->
// -> (but I put so much work into that first method I didnt have the heart ->
// -> to delete it so now it will be eternally memorialized in commented out ->
// -> form)
//The point's slope to the `min` is < `MMS` meaning the point will ALWAYS ->
//-> be on the left side
if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) < MMS){
sR.add(list.get(i));
//System.out.println("(RIGHT) LEFT SIDE: " + list.get(i));
}
//The point's slope to the `min` is >= `MMS` meaning the point will ALWAYS ->
//-> be on the right side
//NOTE: The `=` in the inequality `>=` means that if the point's slope to ->
//-> the `min` is the same as the slope from `min` to `max` then it will ->
//-> arbitrarily be put on the right side
//NOTE: I could just make the `else if()` statement below just an `else` ->
//-> statement but I wanted to be able to show how the other condition is ->
//-> found
else if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) >= MMS){
sL.add(list.get(i));
//System.out.println("(LEFT) RIGHT SIDE: " + list.get(i));
}
}
}
else if(min.getY() == max.getY()){//No slope from `min` to `max`
for(int i = 0; i < list.size(); i++){
//The point's slope to the `min` is positive in which the point is on the ->
//-> left side (above)
if((list.get(i).getY()-min.getY())/(list.get(i).getX()-min.getX()) >= 0){
sL.add(list.get(i));
//System.out.println("LEFT SIDE (above): " + list.get(i));
}
//The point's slope to the `min` is negative in which the point is on the ->
//-> right side (below)
else{
sR.add(list.get(i));
//System.out.println("RIGHT SIDE (below): " + list.get(i));
}
}
}
findFurthest(finalConvexHull, sL, min, max, true);
findFurthest(finalConvexHull, sR, min, max, false);
return finalConvexHull;
}
}