凸包算法
-------------------------------------------- 摘要 --------------------------------------------
本文以平面上若干点为脉络,求解该若干点的凸包及过程分析。首先需对前置输入参数(平面上的若干点)作1点假设,假设
平面上的点数n>2且各点全都不在一条直线上。下文会提供2种不同的方式求解凸包,当然在最终展示的算法是必然存在优化的可
能,毕竟现有大多数的最优算法(也许是本身就不存在一个最终的最优算法)的实现不会是一蹴而就的而是一个循序渐进的过程,
不断的优化,不断地改进实现算法的思想等等,最终成型。总之写下本文的第一目的就是为了充实自己,把我的愚见与大家分享。
-------------------------------------------- 分析 --------------------------------------------
这里提供了2种方式实现凸包算法,分别简要分析2种算法实现轮廓而不会太去纠结细节上的考量。下面直接进入分析过程:
1. 第1种算法把它命名为斜率法,大体思路过程是:首先求出平面上点集在东南西北4各方位的最值点,如图1所示红点就
是最值点,其次连接2个相邻方位最值点之间线段,就以东北2方位为标准连接线段,过滤掉该线段之下的点集(不包括线上的点),
剩下候选点集。再次以某起点为基点分别计算与各候选点连线的斜率,显然斜率最大的连线的终点必然是所求凸包包含的点即凸
包点,并且需把起点换成改凸包点,如图2所示。最后重复上述步骤直到起点为终点为止,那么对应方位的凸包点就求出了,依
次求解其它方位的凸包点。
2.对于第2种算法把它暂且命名为试错法吧,思路如下:首先点集作X轴的排序,并选出各方位最值点。其次对某个
区域进行凸包点搜索,假设对东北方位相间的区域操作,先初始化-获取起点合规方向的点(合规表现为:水平与
垂直方向的合力作用的方向遇到的第一个点,即在排序好的点集中筛选),然后把该点放入候选凸包点集(以下称凸包点集)
中,并把起点替换为该点。再次以起点为基点选取合规的点,并判断该点与凸包点集中的最后一个点Dn的斜率和Dn与Dn下一
个点Dn-1的斜率,如果第一个斜率大于等于第二个斜率,则直接把该点放入凸包点集,否则剔除凸包点集中的Dn,然后又判断
该点与Dn-1的斜率和Dn-1与Dn-2的斜率,即重复上述过程直到合适后停下(该过程中的边界问题这里没有表述出来,但很好处理),
当然停下后,继续替换起点为该点。最后重复上述步骤,直到起点与终点重合为止。其他区域过程类似,就不在赘述。
-------------------------------------------- 源码 --------------------------------------------
1 package algorithm; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 /** 9 * @author Medusa 10 * @date 09/12/2021 11 * @description 凸包算法 12 */ 13 public class ConvexHullAlgorithm { 14 15 /** 16 * 通过最大斜率的方式求出凸包点 17 */ 18 public static List<int[]> slopeMethod(int[][] dotGatherArray) { 19 // 求出右 上 左 下各方位的边界点集 20 List<int[]> rightBoundaryDotList = new ArrayList<>(); 21 List<int[]> topBoundaryDotList = new ArrayList<>(); 22 List<int[]> leftBoundaryDotList = new ArrayList<>(); 23 List<int[]> belowBoundaryDotList = new ArrayList<>(); 24 getBoundaryDot(dotGatherArray, rightBoundaryDotList, topBoundaryDotList, leftBoundaryDotList, 25 belowBoundaryDotList); 26 // 定义凸包点集 27 List<int[]> convexHullDotList = new ArrayList<>(); 28 for (int i = 0; i < rightBoundaryDotList.size(); i++) 29 convexHullDotList.add(rightBoundaryDotList.get(i)); 30 // 定义局部起止点 31 int[] startDot = rightBoundaryDotList.get(rightBoundaryDotList.size() - 1); 32 int[] endDot = topBoundaryDotList.get(topBoundaryDotList.size() - 1); 33 int startIndex = topBoundaryDotList.size() - 1; // 用于判断重复点的下标 34 if (startDot[0] == endDot[0]) 35 --startIndex; 36 else { 37 // 获取所有(起止点除外)与起点连线斜率不大于起止点连线斜率的候选点 38 List<int[]> candidateDot = getCandidateDot(dotGatherArray, startDot, endDot, 0); 39 if (candidateDot.size() > 0) 40 getConvexHullDot(convexHullDotList, candidateDot, startDot, endDot, true); 41 } 42 for (int i = startIndex; i >= 0; i--) 43 convexHullDotList.add(topBoundaryDotList.get(i)); 44 startDot = topBoundaryDotList.get(0); 45 endDot = leftBoundaryDotList.get(leftBoundaryDotList.size() - 1); 46 startIndex = leftBoundaryDotList.size() - 1; 47 if (startDot[0] == endDot[0]) 48 --startIndex; 49 else { 50 List<int[]> candidateDot = getCandidateDot(dotGatherArray, startDot, endDot, 1); 51 if (candidateDot.size() > 0) 52 getConvexHullDot(convexHullDotList, candidateDot, startDot, endDot, true); 53 } 54 for (int i = startIndex; i >= 0; i--) 55 convexHullDotList.add(leftBoundaryDotList.get(i)); 56 startDot = leftBoundaryDotList.get(0); 57 endDot = belowBoundaryDotList.get(0); 58 startIndex = 0; 59 if (startDot[0] == endDot[0]) 60 ++startIndex; 61 else { 62 List<int[]> candidateDot = getCandidateDot(dotGatherArray, startDot, endDot, 2); 63 if (candidateDot.size() > 0) 64 getConvexHullDot(convexHullDotList, candidateDot, startDot, endDot, false); 65 } 66 for (int i = startIndex; i < belowBoundaryDotList.size(); i++) 67 convexHullDotList.add(belowBoundaryDotList.get(i)); 68 startDot = belowBoundaryDotList.get(belowBoundaryDotList.size() - 1); 69 endDot = rightBoundaryDotList.get(0); 70 if (startDot[0] == endDot[0]) 71 convexHullDotList.remove(convexHullDotList.size() - 1); 72 else { 73 List<int[]> candidateDot = getCandidateDot(dotGatherArray, startDot, endDot, 3); 74 if (candidateDot.size() > 0) 75 getConvexHullDot(convexHullDotList, candidateDot, startDot, endDot, false); 76 } 77 return convexHullDotList; 78 } 79 80 /** 81 * 生成非重复的点集 82 */ 83 private static int[][] generateDotGather(long xMax, long yMax, int quantity) { 84 if (xMax < 0 || yMax < 0) { 85 System.out.println("X轴或Y轴的值不能小于0"); 86 return null; 87 } 88 if ((xMax + 1) * (yMax + 1) < quantity) { 89 System.out.println("xMax,yMax所确定的最大点集数不能小于生成的点集数"); 90 return null; 91 } 92 int[][] dotGatherArray = new int[quantity][2]; 93 Map<String, Boolean> xyValueCounterMap = new HashMap<>(quantity); 94 ++xMax; ++yMax; 95 for (int i = 0; i < quantity; i++) { 96 int xValue, yValue; 97 Boolean xyValueCounter; 98 do { 99 xValue = (int) (Math.random() * xMax); 100 yValue = (int) (Math.random() * yMax); 101 xyValueCounter = xyValueCounterMap.get(xValue + "" + yValue); 102 } while (null != xyValueCounter); 103 xyValueCounterMap.put(xValue + "" + yValue, true); 104 dotGatherArray[i][0] = xValue; 105 dotGatherArray[i][1] = yValue; 106 } 107 return dotGatherArray; 108 } 109 110 private static void getBoundaryDot(int[][] dotGatherArray, List<int[]> rightBoundaryDotList, 111 List<int[]> topBoundaryDotList, List<int[]> leftBoundaryDotList, 112 List<int[]> belowBoundaryDotList) { 113 int xMax = dotGatherArray[0][0], yMax = dotGatherArray[0][1], xMin = xMax, yMin = yMax; 114 for (int i = 1; i < dotGatherArray.length; i++) { 115 int x = dotGatherArray[i][0]; 116 int y = dotGatherArray[i][1]; 117 if (x > xMax) 118 xMax = x; 119 else if (x < xMin) 120 xMin = x; 121 if (y > yMax) 122 yMax = y; 123 else if (y < yMin) 124 yMin = y; 125 } 126 for (int i = 0; i < dotGatherArray.length; i++) { 127 int x = dotGatherArray[i][0]; 128 int y = dotGatherArray[i][1]; 129 if (x == xMax) 130 rightBoundaryDotList.add(dotGatherArray[i]); 131 else if (x == xMin) 132 leftBoundaryDotList.add(dotGatherArray[i]); 133 if (y == yMax) 134 topBoundaryDotList.add(dotGatherArray[i]); 135 else if (y == yMin) 136 belowBoundaryDotList.add(dotGatherArray[i]); 137 } 138 sortBoundaryDot(rightBoundaryDotList, 1); 139 sortBoundaryDot(topBoundaryDotList, 0); 140 sortBoundaryDot(leftBoundaryDotList, 1); 141 sortBoundaryDot(belowBoundaryDotList, 0); 142 } 143 144 private static void sortBoundaryDot(List<int[]> boundaryDotList, int index) { 145 for (int i = 1; i < boundaryDotList.size(); i++) { 146 for (int j = 0; j < boundaryDotList.size() - i; j++) { 147 if (boundaryDotList.get(j)[index] > boundaryDotList.get(j + 1)[index]) { 148 int[] boundaryDot = boundaryDotList.get(j + 1); 149 boundaryDotList.set(j + 1, boundaryDotList.get(j)); 150 boundaryDotList.set(j, boundaryDot); 151 } 152 } 153 } 154 } 155 156 private static List<int[]> getCandidateDot(int[][] dotGatherArray, int[] startDot, int[] endDot, int flag) { 157 int startX = startDot[0], startY = startDot[1]; 158 int endX = endDot[0], endY = endDot[1]; 159 int yChangeRate = endY - startY, xChangeRate = endX - startX; 160 List<int[]> candidateDotList = new ArrayList<>(); 161 if (0 == flag) { 162 for (int i = 0; i < dotGatherArray.length; i++) { 163 int[] dot = dotGatherArray[i]; 164 if (dot[1] > startY && dot[0] > endX && 165 (yChangeRate * (dot[0] - startX) >= (dot[1] - startY) * xChangeRate)) 166 candidateDotList.add(dot); 167 } 168 } else if (1 == flag) { 169 for (int i = 0; i < dotGatherArray.length; i++) { 170 int[] dot = dotGatherArray[i]; 171 if (dot[0] < startX && dot[1] > endY && 172 (yChangeRate * (dot[0] - startX) >= (dot[1] - startY) * xChangeRate)) 173 candidateDotList.add(dot); 174 } 175 } else if (2 == flag) { 176 for (int i = 0; i < dotGatherArray.length; i++) { 177 int[] dot = dotGatherArray[i]; 178 if (dot[1] < startY && dot[0] < endX && 179 (yChangeRate * (dot[0] - startX) >= (dot[1] - startY) * xChangeRate)) 180 candidateDotList.add(dot); 181 } 182 } else { 183 for (int i = 0; i < dotGatherArray.length; i++) { 184 int[] dot = dotGatherArray[i]; 185 if (dot[0] > startX && dot[1] < endY && 186 (yChangeRate * (dot[0] - startX) >= (dot[1] - startY) * xChangeRate)) 187 candidateDotList.add(dot); 188 } 189 } 190 quickSort(candidateDotList, 0, candidateDotList.size() - 1); 191 return candidateDotList; 192 } 193 194 private static void quickSort(List<int[]> list, int left, int right) { 195 if (left < right) { 196 int[] temp0Array = list.get(left); 197 list.set(left, list.get((left + right) >> 1)); 198 list.set((left + right) >> 1, temp0Array); 199 int[] baseArray = list.get(left); 200 int i = left, j = right; 201 while (i != j) { 202 while (list.get(j)[0] >= baseArray[0] && i < j) j--; 203 while (list.get(i)[0] <= baseArray[0] && i < j) i++; 204 if (i < j) { 205 int[] temp1Array = list.get(i); 206 list.set(i, list.get(j)); 207 list.set(j, temp1Array); 208 } 209 } 210 list.set(left, list.get(i)); 211 list.set(i, baseArray); 212 quickSort(list, left, i - 1); 213 quickSort(list, i + 1, right); 214 } 215 } 216 217 /** 218 * 根据候选点筛选出凸包点,起点与所有候选点连线斜率的最小值即为凸包点 219 */ 220 private static void getConvexHullDot(List<int[]> convexHullDotList, List<int[]> candidateDotList, 221 int[] startDot, int[] endDot, boolean flag) { 222 if (flag) { 223 for (int i = 0; i < candidateDotList.size() >> 1; i++) { 224 int[] tempArray = candidateDotList.get(i); 225 candidateDotList.set(i, candidateDotList.get(candidateDotList.size() - 1 - i)); 226 candidateDotList.set(candidateDotList.size() - 1 - i, tempArray); 227 } 228 } 229 int startX = startDot[0], startY = startDot[1]; 230 int endX = endDot[0]; 231 candidateDotList.add(endDot); 232 List<Integer> convexHullDotIndex = new ArrayList<>(); 233 int index = 0; 234 while (startX != endX) { 235 int y1ChangeRate = candidateDotList.get(index)[1] - startY; 236 int x1ChangeRate = candidateDotList.get(index)[0] - startX; 237 convexHullDotIndex.add(index); 238 for (int i = index + 1; i < candidateDotList.size(); i++) { 239 int y2ChangeRate = candidateDotList.get(i)[1] - startY; 240 int x2ChangeRate = candidateDotList.get(i)[0] - startX; 241 int value0 = y1ChangeRate * x2ChangeRate; 242 int value1 = y2ChangeRate * x1ChangeRate; 243 if (value0 > value1) { 244 convexHullDotIndex.clear(); 245 convexHullDotIndex.add(i); 246 y1ChangeRate = y2ChangeRate; 247 x1ChangeRate = x2ChangeRate; 248 } 249 else if (value0 == value1) 250 convexHullDotIndex.add(i); 251 } 252 index = convexHullDotIndex.get(convexHullDotIndex.size() - 1); 253 startX = candidateDotList.get(index)[0]; 254 startY = candidateDotList.get(index)[1]; 255 ++index; 256 for (int i = 0; i < convexHullDotIndex.size(); i++) 257 convexHullDotList.add(candidateDotList.get(convexHullDotIndex.get(i))); 258 convexHullDotIndex.clear(); 259 } 260 convexHullDotList.remove(convexHullDotList.size() - 1); 261 } 262 263 /** 264 * 以x轴降序的方式寻找边界 265 */ 266 public static List<int[]> searchBoundary(int[][] dotGather) { 267 quickSort(dotGather, 0, dotGather.length - 1); 268 List<int[]> rightBoundaryDotList = new ArrayList<>(); 269 rightBoundaryDotList.add(dotGather[dotGather.length - 1]); 270 for (int i = dotGather.length - 2; i >= 0; i--) { 271 if (dotGather[dotGather.length - 1][0] == dotGather[i][0]) 272 rightBoundaryDotList.add(dotGather[i]); 273 else 274 break; 275 } 276 sortBoundaryDot(rightBoundaryDotList, 1); 277 List<int[]> leftBoundaryDotList = new ArrayList<>(); 278 leftBoundaryDotList.add(dotGather[0]); 279 for (int i = 1; i < dotGather.length; i++) { 280 if (dotGather[0][0] == dotGather[i][0]) 281 leftBoundaryDotList.add(dotGather[i]); 282 else 283 break; 284 } 285 sortBoundaryDot(leftBoundaryDotList, 1); 286 List<int[]> topBoundaryDotList = new ArrayList<>(); 287 List<int[]> belowBoundaryDotList = new ArrayList<>(); 288 int maxY = dotGather[0][1], minY = maxY; 289 for (int i = 1; i < dotGather.length; i++) { 290 if (maxY < dotGather[i][1]) 291 maxY = dotGather[i][1]; 292 else if (minY > dotGather[i][1]) 293 minY = dotGather[i][1]; 294 } 295 for (int i = 0; i < dotGather.length; i++) { 296 if (maxY == dotGather[i][1]) 297 topBoundaryDotList.add(dotGather[i]); 298 else if (minY == dotGather[i][1]) 299 belowBoundaryDotList.add(dotGather[i]); 300 } 301 sortBoundaryDot(topBoundaryDotList, 0); 302 sortBoundaryDot(belowBoundaryDotList, 0); 303 List<int[]> convexHullDotList = new ArrayList<>(); 304 for (int i = 0; i < rightBoundaryDotList.size(); i++) 305 convexHullDotList.add(rightBoundaryDotList.get(i)); 306 int[] startDot = rightBoundaryDotList.get(rightBoundaryDotList.size() - 1); 307 int[] endDot = topBoundaryDotList.get(topBoundaryDotList.size() - 1); 308 int startIndex = topBoundaryDotList.size() - 1; 309 if (startDot[0] == endDot[0]) 310 --startIndex; 311 else 312 search(dotGather, convexHullDotList, startDot, endDot, 0); 313 for (int i = startIndex; i >= 0; i--) 314 convexHullDotList.add(topBoundaryDotList.get(i)); 315 startDot = leftBoundaryDotList.get(leftBoundaryDotList.size() - 1); 316 endDot = topBoundaryDotList.get(0); 317 startIndex = leftBoundaryDotList.size() - 1; 318 if (startDot[0] == endDot[0]) 319 --startIndex; 320 else 321 search(dotGather, convexHullDotList, startDot, endDot, 1); 322 for (int i = startIndex; i >= 0; i--) 323 convexHullDotList.add(leftBoundaryDotList.get(i)); 324 startDot = leftBoundaryDotList.get(0); 325 endDot = belowBoundaryDotList.get(0); 326 startIndex = 0; 327 if (startDot[0] == endDot[0]) 328 ++startIndex; 329 else 330 search(dotGather, convexHullDotList, startDot, endDot, 2); 331 for (int i = startIndex; i < belowBoundaryDotList.size(); i++) 332 convexHullDotList.add(belowBoundaryDotList.get(i)); 333 startDot = rightBoundaryDotList.get(0); 334 endDot = belowBoundaryDotList.get(belowBoundaryDotList.size() - 1); 335 if (startDot[0] == endDot[0]) 336 convexHullDotList.remove(convexHullDotList.size() - 1); 337 else 338 search(dotGather, convexHullDotList, startDot, endDot, 3); 339 return convexHullDotList; 340 } 341 342 private static void quickSort(int[][] array, int left, int right) { 343 if (left < right) { 344 int[] temp0Array = array[left]; 345 array[left] = array[(left + right) >> 1]; 346 array[(left + right) >> 1] = temp0Array; 347 int[] baseArray = array[left]; 348 int i = left, j = right; 349 while (i != j) { 350 while (array[j][0] >= baseArray[0] && i < j) j--; 351 while (array[i][0] <= baseArray[0] && i < j) i++; 352 if (i < j) { 353 int[] temp1Array = array[i]; 354 array[i] = array[j]; 355 array[j] = temp1Array; 356 } 357 } 358 array[left] = array[i]; 359 array[i] = baseArray; 360 quickSort(array, left, i - 1); 361 quickSort(array, i + 1, right); 362 } 363 } 364 365 private static void search(int[][] dotGather, List<int[]> convexHullDotList, int[] startDot, 366 int[] endDot, int flag) { 367 int startX = startDot[0], startY = startDot[1]; 368 int endX = endDot[0], endY = endDot[1]; 369 if (flag == 0) { 370 // 1.dot[1] - nowConvexHullDot[1] / dot[0] - nowConvexHullDot[0] 371 // < 372 // nowConvexHullDot[1] - nextConvexHullDot[1] / nowConvexHullDot[0] - nextConvexHullDot[0] 373 // yt - y1/0 < y1 - y2/x1 - x2 374 int pointer = convexHullDotList.size() - 1, limit = pointer, index = dotGather.length - 1; 375 do { 376 int[] dot = dotGather[index--]; 377 if (dot[1] > startY && dot[0] >= endX && dot[1] <= endY) { 378 for (int i = pointer; i > limit; i--) { 379 int[] nowConvexHullDot = convexHullDotList.get(i); 380 int[] nextConvexHullDot = convexHullDotList.get(i - 1); 381 if ((dot[1] - nowConvexHullDot[1]) * (nowConvexHullDot[0] - nextConvexHullDot[0]) < 382 (nowConvexHullDot[1] - nextConvexHullDot[1]) * (dot[0] - nowConvexHullDot[0])) 383 convexHullDotList.remove(i); 384 else 385 break; 386 } 387 convexHullDotList.add(dot); 388 pointer = convexHullDotList.size() - 1; 389 startX = dot[0]; 390 startY = dot[1]; 391 } 392 } while (startX != endX && startY != endY); 393 convexHullDotList.remove(convexHullDotList.size() - 1); 394 } else if (flag == 1) { 395 // 2.dot[1] - nowConvexHullDot[1] / dot[0] - nowConvexHullDot[0] 396 // > 397 // nowConvexHullDot[1] - nextConvexHullDot[1] / nowConvexHullDot[0] - nextConvexHullDot[0] 398 // yt - y1/0 > y1 - y2/x1 - x2 399 List<int[]> tempList = new ArrayList<>(); 400 tempList.add(startDot); 401 int pointer = 0, index = 0; 402 do { 403 int[] dot = dotGather[index++]; 404 if (dot[1] > startY && dot[0] <= endX && dot[1] <= endY) { 405 for (int i = pointer; i > 0; i--) { 406 int[] nowConvexHullDot = tempList.get(i); 407 int[] nextConvexHullDot = tempList.get(i - 1); 408 if ((dot[1] - nowConvexHullDot[1]) * (nowConvexHullDot[0] - nextConvexHullDot[0]) > 409 (nowConvexHullDot[1] - nextConvexHullDot[1]) * (dot[0] - nowConvexHullDot[0])) 410 tempList.remove(i); 411 else 412 break; 413 } 414 tempList.add(dot); 415 pointer = tempList.size() - 1; 416 startX = dot[0]; 417 startY = dot[1]; 418 } 419 } while (startX != endX && startY != endY); 420 for (int i = tempList.size() - 2; i > 0; i--) 421 convexHullDotList.add(tempList.get(i)); 422 } else if (flag == 2) { 423 // 3.dot[1] - nowConvexHullDot[1] / dot[0] - nowConvexHullDot[0] 424 // < 425 // nowConvexHullDot[1] - nextConvexHullDot[1] / nowConvexHullDot[0] - nextConvexHullDot[0] 426 // yt - y1/0 < y1 - y2/x1 - x2 427 int pointer = convexHullDotList.size() - 1, limit = pointer, index = 0; 428 do { 429 int[] dot = dotGather[index++]; 430 if (dot[1] < startY && dot[0] <= endX && dot[1] >= endY) { 431 for (int i = pointer; i > limit; i--) { 432 int[] nowConvexHullDot = convexHullDotList.get(i); 433 int[] nextConvexHullDot = convexHullDotList.get(i - 1); 434 if ((dot[1] - nowConvexHullDot[1]) * (nowConvexHullDot[0] - nextConvexHullDot[0]) < 435 (nowConvexHullDot[1] - nextConvexHullDot[1]) * (dot[0] - nowConvexHullDot[0])) 436 convexHullDotList.remove(i); 437 else 438 break; 439 } 440 convexHullDotList.add(dot); 441 pointer = convexHullDotList.size() - 1; 442 startX = dot[0]; 443 startY = dot[1]; 444 } 445 } while (startX != endX && startY != endY); 446 convexHullDotList.remove(convexHullDotList.size() - 1); 447 } else { 448 // 4.dot[1] - nowConvexHullDot[1] / dot[0] - nowConvexHullDot[0] 449 // > 450 // nowConvexHullDot[1] - nextConvexHullDot[1] / nowConvexHullDot[0] - nextConvexHullDot[0] 451 // yt - y1/0 > y1 - y2/x1 - x2 452 List<int[]> tempList = new ArrayList<>(); 453 tempList.add(startDot); 454 int pointer = 0, index = dotGather.length - 1;; 455 do { 456 int[] dot = dotGather[index--]; 457 if (dot[1] < startY && dot[0] >= endX && dot[1] >= endY) { 458 for (int i = pointer; i > 0; i--) { 459 int[] nowConvexHullDot = tempList.get(i); 460 int[] nextConvexHullDot = tempList.get(i - 1); 461 if ((dot[1] - nowConvexHullDot[1]) * (nowConvexHullDot[0] - nextConvexHullDot[0]) > 462 (nowConvexHullDot[1] - nextConvexHullDot[1]) * (dot[0] - nowConvexHullDot[0])) 463 tempList.remove(i); 464 else 465 break; 466 } 467 tempList.add(dot); 468 pointer = tempList.size() - 1; 469 startX = dot[0]; 470 startY = dot[1]; 471 } 472 } while (startX != endX && startY != endY); 473 for (int i = tempList.size() - 2; i > 0; i--) 474 convexHullDotList.add(tempList.get(i)); 475 } 476 } 477 }