1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.geometry.euclidean.twod.hull; 23 24 import java.util.ArrayList; 25 import java.util.Collection; 26 import java.util.List; 27 28 import org.hipparchus.geometry.euclidean.twod.Vector2D; 29 30 /** 31 * A simple heuristic to improve the performance of convex hull algorithms. 32 * <p> 33 * The heuristic is based on the idea of a convex quadrilateral, which is formed by 34 * four points with the lowest and highest x / y coordinates. Any point that lies inside 35 * this quadrilateral can not be part of the convex hull and can thus be safely discarded 36 * before generating the convex hull itself. 37 * <p> 38 * The complexity of the operation is O(n), and may greatly improve the time it takes to 39 * construct the convex hull afterwards, depending on the point distribution. 40 * 41 * @see <a href="http://en.wikipedia.org/wiki/Convex_hull_algorithms#Akl-Toussaint_heuristic"> 42 * Akl-Toussaint heuristic (Wikipedia)</a> 43 */ 44 public final class AklToussaintHeuristic { 45 46 /** Hide utility constructor. */ 47 private AklToussaintHeuristic() { 48 } 49 50 /** 51 * Returns a point set that is reduced by all points for which it is safe to assume 52 * that they are not part of the convex hull. 53 * 54 * @param points the original point set 55 * @return a reduced point set, useful as input for convex hull algorithms 56 */ 57 public static Collection<Vector2D> reducePoints(final Collection<Vector2D> points) { 58 59 // find the leftmost point 60 int size = 0; 61 Vector2D minX = null; 62 Vector2D maxX = null; 63 Vector2D minY = null; 64 Vector2D maxY = null; 65 for (Vector2D p : points) { 66 if (minX == null || p.getX() < minX.getX()) { 67 minX = p; 68 } 69 if (maxX == null || p.getX() > maxX.getX()) { 70 maxX = p; 71 } 72 if (minY == null || p.getY() < minY.getY()) { 73 minY = p; 74 } 75 if (maxY == null || p.getY() > maxY.getY()) { 76 maxY = p; 77 } 78 size++; 79 } 80 81 if (size < 4) { 82 return points; 83 } 84 85 final List<Vector2D> quadrilateral = buildQuadrilateral(minY, maxX, maxY, minX); 86 // if the quadrilateral is not well formed, e.g. only 2 points, do not attempt to reduce 87 if (quadrilateral.size() < 3) { 88 return points; 89 } 90 91 final List<Vector2D> reducedPoints = new ArrayList<>(quadrilateral); 92 for (final Vector2D p : points) { 93 // check all points if they are within the quadrilateral 94 // in which case they can not be part of the convex hull 95 if (!insideQuadrilateral(p, quadrilateral)) { 96 reducedPoints.add(p); 97 } 98 } 99 100 return reducedPoints; 101 } 102 103 /** 104 * Build the convex quadrilateral with the found corner points (with min/max x/y coordinates). 105 * 106 * @param points the respective points with min/max x/y coordinate 107 * @return the quadrilateral 108 */ 109 private static List<Vector2D> buildQuadrilateral(final Vector2D... points) { 110 List<Vector2D> quadrilateral = new ArrayList<>(); 111 for (Vector2D p : points) { 112 if (!quadrilateral.contains(p)) { 113 quadrilateral.add(p); 114 } 115 } 116 return quadrilateral; 117 } 118 119 /** 120 * Checks if the given point is located within the convex quadrilateral. 121 * @param point the point to check 122 * @param quadrilateralPoints the convex quadrilateral, represented by 4 points 123 * @return {@code true} if the point is inside the quadrilateral, {@code false} otherwise 124 */ 125 private static boolean insideQuadrilateral(final Vector2D point, 126 final List<Vector2D> quadrilateralPoints) { 127 128 Vector2D p1 = quadrilateralPoints.get(0); 129 Vector2D p2 = quadrilateralPoints.get(1); 130 131 if (point.equals(p1) || point.equals(p2)) { 132 return true; 133 } 134 135 // get the location of the point relative to the first two vertices 136 final double last = point.crossProduct(p1, p2); 137 final int size = quadrilateralPoints.size(); 138 // loop through the rest of the vertices 139 for (int i = 1; i < size; i++) { 140 p1 = p2; 141 p2 = quadrilateralPoints.get((i + 1) == size ? 0 : i + 1); 142 143 if (point.equals(p1) || point.equals(p2)) { 144 return true; 145 } 146 147 // do side of line test: multiply the last location with this location 148 // if they are the same sign then the operation will yield a positive result 149 // -x * -y = +xy, x * y = +xy, -x * y = -xy, x * -y = -xy 150 if (last * point.crossProduct(p1, p2) < 0) { 151 return false; 152 } 153 } 154 return true; 155 } 156 157 }