@Override public boolean equals(Object object) { if (object instanceof ConcaveHull) return equals((ConcaveHull) object); else return false; }
public ConcaveHullPocket findFirstConcaveHullPocket() { return findFirstConcaveHullPocket(0); }
public ConcaveHull(ConcaveHull other) { this.hullVertices = new ArrayList<>(); other.forEach(hullVertices::add); }
/** * Removes vertices to filter short edges. Only convex vertices are removed, meaning the polygon area can only decrease when calling this method. * @param concaveAngleLimit threshold to define a concavity. 0 rad being flat, negative convex, positive concave. * @param lengthThreshold any edge shorter than that will be removed, if possible. * @param concaveHullVerticesToFilter the vertices of the concave hull to filter. * @return the number of vertices removed. */ public static int filterOutShortEdges(double lengthThreshold, ConcaveHull concaveHullToFilter) { return filterOutShortEdges(lengthThreshold, concaveHullToFilter.getConcaveHullVertices()); }
Point2D[] concaveHullsVertices = new Point2D[concaveHull.getNumberOfVertices()]; concaveHull.getConcaveHullVertices().toArray(concaveHullsVertices);
private Node createConcavePocketsGraphics(PlanarRegionSegmentationRawData rawData, ConcaveHullFactoryResult concaveHullFactoryResult) { JavaFXMeshBuilder meshBuilder = new JavaFXMeshBuilder(); ConcaveHullCollection concaveHullCollection = concaveHullFactoryResult.getConcaveHullCollection(); RigidBodyTransform transform = rawData.getTransformFromLocalToWorld(); for (ConcaveHull concaveHull : concaveHullCollection) { Set<ConcaveHullPocket> pockets = concaveHull.findConcaveHullPockets(polygonizerParameters.getDepthThreshold()); for (ConcaveHullPocket pocket : pockets) { List<Point2D> pocketVertices = ListWrappingIndexTools.subListInclusive(pocket.getStartBridgeIndex(), pocket.getEndBridgeIndex(), concaveHull.getConcaveHullVertices()); Point2D average = new Point2D(); average.interpolate(pocket.getStartBridgeVertex(), pocket.getEndBridgeVertex(), 0.5); pocketVertices.add(0, average); ConcaveHullTools.ensureClockwiseOrdering(pocketVertices); meshBuilder.addPolygon(transform, pocketVertices); } } MeshView meshView = new MeshView(meshBuilder.generateMesh()); meshView.setMaterial(new PhongMaterial(OcTreeMeshBuilder.getRegionColor(rawData.getRegionId()))); return meshView; }
public boolean epsilonEquals(ConcaveHull other, double epsilon) { if (getNumberOfVertices() != other.getNumberOfVertices()) return false; for (int vertexIndex = 0; vertexIndex <= getNumberOfVertices(); vertexIndex++) { if (!hullVertices.get(vertexIndex).epsilonEquals(other.hullVertices.get(vertexIndex), epsilon)) return false; } return true; }
public boolean add(List<Point2D> newConcaveHullVertices) { return add(new ConcaveHull(newConcaveHullVertices)); }
@Override public int hashCode() { int hashCode = 1; for (ConcaveHull hull : this) hashCode = 31 * hashCode + (hull == null ? 0 : hull.hashCode()); return hashCode; } }
public List<Point3D> toVertices3d(double zOffset) { return stream().map(vertex -> new Point3D(vertex.getX(), vertex.getY(), zOffset)).collect(Collectors.toList()); }
public static ConcaveHullFactoryResult createConcaveHull(List<Point2D> pointCloud2d, List<LineSegment2D> lineConstraints, ConcaveHullFactoryParameters parameters) { if (pointCloud2d.size() <= 3) return null; MultiPoint sites = filterAndCreateMultiPoint(pointCloud2d, lineConstraints, 0.01); MultiLineString constraintSegments = createMultiLineString(lineConstraints); ConcaveHullFactoryResult result = new ConcaveHullFactoryResult(); ConcaveHullVariables initialVariables = initializeTriangulation(sites, constraintSegments, result); List<ConcaveHullVariables> variablesList = computeConcaveHullBorderEdgesRecursive(parameters, initialVariables); result.intermediateVariables.addAll(variablesList); for (ConcaveHullVariables variables : result.intermediateVariables) { ConcaveHull concaveHull = computeConcaveHull(variables.getOrderedBorderEdges()); if (concaveHull != null) { concaveHull.ensureClockwiseOrdering(); result.concaveHullCollection.add(concaveHull); } } return result; }
/** * Filter out vertices that create "peaks" or barely stick out the line described by the previous and next vertices. * Peaks are identified by a threshold on the angle between two consecutive edges. * Only convex peaks or shallow angles are removed, meaning this filter only reduces the area of the concave hull. * @param shallowAngleThreshold should be a small positive angle in radians. 0 will not remove any vertex. * @param peakAngleThreshold should be close to {@link Math#PI}. * @param concaveHullToFilter the concave hull to filter. * @return the number of vertices removed. */ public static int filterOutPeaksAndShallowAngles(double shallowAngleThreshold, double peakAngleThreshold, ConcaveHull concaveHullToFilter) { return filterOutPeaksAndShallowAngles(shallowAngleThreshold, peakAngleThreshold, concaveHullToFilter.getConcaveHullVertices()); }
public boolean equals(ConcaveHull other) { if (getNumberOfVertices() != other.getNumberOfVertices()) return false; for (int vertexIndex = 0; vertexIndex <= getNumberOfVertices(); vertexIndex++) { if (!hullVertices.get(vertexIndex).equals(other.hullVertices.get(vertexIndex))) return false; } return true; }
private static ConcaveHull computeConcaveHull(List<QuadEdge> orderedBorderEdges) { List<Point2D> orderedConcaveHullVertices = orderedBorderEdges.stream() .map(QuadEdge::orig) .map(vertex -> new Point2D(vertex.getX(), vertex.getY())) .collect(Collectors.toList()); return new ConcaveHull(orderedConcaveHullVertices); }
/** * Removes vertices to filter short edges. Only convex vertices are removed, meaning the polygon area can only decrease when calling this method. * @param concaveAngleLimit threshold to define a concavity. 0 rad being flat, negative convex, positive concave. * @param lengthThreshold any edge shorter than that will be removed, if possible. * @param concaveHullVerticesToFilter the vertices of the concave hull to filter. * @return the number of vertices removed. */ public static int filterOutShortEdges(double lengthThreshold, ConcaveHullCollection concaveHullCollectionToFilter) { int numberOfRemovedVertices = 0; for (ConcaveHull concaveHullToFilter : concaveHullCollectionToFilter) numberOfRemovedVertices += filterOutShortEdges(lengthThreshold, concaveHullToFilter.getConcaveHullVertices()); return numberOfRemovedVertices; }
@Override public String toString() { return "Size: " + getNumberOfVertices() + "\n" + ConcaveHullTools.vertexListToString(hullVertices); } }
public ConcaveHullCollection(ConcaveHullCollection other) { other.forEach(concaveHull -> add(new ConcaveHull(concaveHull))); }
/** * Inspired from the SL-decomposition in the paper * <a href="https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjZlOab96XPAhXBQD4KHcXeB4MQFggsMAE&url=https%3A%2F%2Fparasol.tamu.edu%2Fpublications%2Fdownload.php%3Ffile_id%3D390&usg=AFQjCNF3wXvuCxXNREhu4CW-oNyd1caa0A&sig2=X-zxaHykED7EuqkYhkfUgg"> * Approximate Convex Decomposition of Polygons</a>. * @param concaveHullCollection [input] the collection of concave hulls to be decomposed into convex polygons. * @param depthThreshold [input] the algorithm determines whether the polygon is to split or not by looking at the maximum depth of concave pockets in the concave hull. * When a pocket is deeper than {@code depthThreshold} the concave hull will be split in two. * Otherwise, the pocket vertices will be removed. * @param convexPolygonsToPack [output] the convex polygons approximating the concave hull. */ public static void recursiveApproximateDecomposition(ConcaveHullCollection concaveHullCollection, double depthThreshold, List<ConvexPolygon2D> convexPolygonsToPack) { for (ConcaveHull concaveHull : concaveHullCollection) recursiveApproximateDecomposition(concaveHull.getConcaveHullVertices(), depthThreshold, convexPolygonsToPack); }
/** * Inspired from the SL-decomposition in the paper * <a href="https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjZlOab96XPAhXBQD4KHcXeB4MQFggsMAE&url=https%3A%2F%2Fparasol.tamu.edu%2Fpublications%2Fdownload.php%3Ffile_id%3D390&usg=AFQjCNF3wXvuCxXNREhu4CW-oNyd1caa0A&sig2=X-zxaHykED7EuqkYhkfUgg"> * Approximate Convex Decomposition of Polygons</a>. * @param concaveHull [input] the concave hull to be decomposed into convex polygons. * @param depthThreshold [input] the algorithm determines whether the polygon is to split or not by looking at the maximum depth of concave pockets in the concave hull. * When a pocket is deeper than {@code depthThreshold} the concave hull will be split in two. * Otherwise, the pocket vertices will be removed. * @param convexPolygonsToPack [output] the convex polygons approximating the concave hull. */ public static void recursiveApproximateDecomposition(ConcaveHull concaveHull, double depthThreshold, List<ConvexPolygon2D> convexPolygonsToPack) { recursiveApproximateDecomposition(concaveHull.getConcaveHullVertices(), depthThreshold, convexPolygonsToPack); }
/** * Filter out vertices that create "peaks" or barely stick out the line described by the previous and next vertices. * Peaks are identified by a threshold on the angle between two consecutive edges. * Only convex peaks or shallow angles are removed, meaning this filter only reduces the area of the concave hull. * @param shallowAngleThreshold should be a small positive angle in radians. 0 will not remove any vertex. * @param peakAngleThreshold should be close to {@link Math#PI}. * @param concaveHullCollectionToFilter the collection of concave hulls to filter. * @return the number of vertices removed. */ public static int filterOutPeaksAndShallowAngles(double shallowAngleThreshold, double peakAngleThreshold, ConcaveHullCollection concaveHullCollectionToFilter) { int numberOfVerticesRemoved = 0; for (ConcaveHull concaveHullToFilter : concaveHullCollectionToFilter) numberOfVerticesRemoved += filterOutPeaksAndShallowAngles(shallowAngleThreshold, peakAngleThreshold, concaveHullToFilter.getConcaveHullVertices()); return numberOfVerticesRemoved; }