/** * Tests whether this polygon is empty, i.e. it has no vertices. * * @return {@code true} if this polygon is empty, {@code false} otherwise. */ default boolean isEmpty() { return getNumberOfVertices() == 0; }
/** * Returns the index in the middle of the range from firstIndex to secondIndex moving counter clockwise. * E.g. in a polygon with 6 vertices given indices 0 and 2 (in this order) the method will return the * middle of the range [0 5 4 3 2]: 4 */ public static int getMiddleIndexCounterClockwise(int firstIndex, int secondIndex, ConvexPolygon2DReadOnly polygon) { int numberOfVertices = polygon.getNumberOfVertices(); if (secondIndex >= firstIndex) return (secondIndex + (firstIndex + numberOfVertices - secondIndex + 1) / 2) % numberOfVertices; else return (secondIndex + firstIndex + 1) / 2; }
/** * Checks if the given index is contained in the range [0, {@link #getNumberOfVertices()}[. * * @param index the index to check. * @throws IndexOutOfBoundsException if the given index is either negative or greater or equal * than the polygon's number of vertices. */ default void checkIndexInBoundaries(int index) { if (index < 0) throw new IndexOutOfBoundsException("vertexIndex < 0"); if (index >= getNumberOfVertices()) throw new IndexOutOfBoundsException("vertexIndex >= numberOfVertices. numberOfVertices = " + getNumberOfVertices()); }
/** * Gets a read-only view of this polygon vertices. * <p> * WARNING: This method generates garbage. * </p> * <p> * The returned list is an unmodifiable list created with * {@link Collections#unmodifiableList(List)}. * </p> * * @return a read-only view of this polygon vertices. */ default List<? extends Point2DReadOnly> getPolygonVerticesView() { return getVertexBufferView().subList(0, getNumberOfVertices()); }
/** * Gets the read-only reference to the {@code index}<sup>th</sup> vertex of this polygon. * <p> * This method calculates a new index to emulate a counter-clockwise ordering of this polygon's * vertices. The first vertex has the lowest x-coordinate. * </p> * * @param index the index of the vertex in the counter-clockwise ordered list. * @return the read-only reference to the vertex. * @throws OutdatedPolygonException if {@link ConvexPolygon2DBasics#update()} has not been called * since last time this polygon's vertices were edited. * @throws IndexOutOfBoundsException if the given {@code index} is negative or greater or equal * than this polygon's number of vertices. * @throws EmptyPolygonException if this polygon is empty when calling this method. */ default Point2DReadOnly getVertexCCW(int index) { return getVertex(getNumberOfVertices() - 1 - index); }
/** * Create a triangle mesh for the given polygon 2d and extrude it along the z-axis. * <p> * TODO: Figure out how to texture an extruded polygon! * </p> * * @param convexPolygon2d the polygon to create a mesh from. * @param extrusionHeight thickness of the extrusion. If {@code extrusionHeight < 0}, the polygon is extruded toward z negative. * @return the created triangle mesh. */ public static MeshDataHolder ExtrudedPolygon(ConvexPolygon2DReadOnly convexPolygon2d, double extrusionHeight) { Point2D[] points = new Point2D[convexPolygon2d.getNumberOfVertices()]; int reverseIndex = convexPolygon2d.getNumberOfVertices(); for (int i = 0; i < convexPolygon2d.getNumberOfVertices(); i++) { points[i] = new Point2D(convexPolygon2d.getVertexBufferView().get(--reverseIndex)); } return ExtrudedPolygon(points, extrusionHeight); }
/** * Tests on a per vertex and per component basis, if this polygon is exactly equal to * {@code other}. * * @param other the other polygon to compare against this. Not modified. * @return {@code true} if the two polygons are exactly equal component-wise, {@code false} * otherwise. */ default boolean equals(ConvexPolygon2DReadOnly other) { if (other == null) return false; if (getNumberOfVertices() != other.getNumberOfVertices()) return false; for (int i = 0; i < getNumberOfVertices(); i++) { Point2DReadOnly thisVertex = getVertexBufferView().get(i); Point2DReadOnly otherVertex = other.getVertexBufferView().get(i); if (!thisVertex.equals(otherVertex)) return false; } return true; }
/** * Create a triangle mesh for the given polygon. * * @param convexPolygon the polygon to create a mesh from. * @return the created triangle mesh. */ public static MeshDataHolder Polygon(ConvexPolygon2DReadOnly convexPolygon) { Point3D32[] points = new Point3D32[convexPolygon.getNumberOfVertices()]; int reverseIndex = convexPolygon.getNumberOfVertices(); for (int i = 0; i < convexPolygon.getNumberOfVertices(); i++) { Point2DReadOnly vertex = convexPolygon.getVertexBufferView().get(--reverseIndex); points[i] = new Point3D32((float) vertex.getX(), (float) vertex.getY(), 0.0f); } return Polygon(points); }
/** * Determines if the polygonToTest is inside the convex polygon. */ public static boolean isPolygonInside(ConvexPolygon2DReadOnly polygonToTest, double epsilon, ConvexPolygon2DReadOnly polygon) { for (int i = 0; i < polygonToTest.getNumberOfVertices(); i++) { if (!polygon.isPointInside(polygonToTest.getVertex(i), epsilon)) return false; } return true; }
/** * Tests if any of this polygon's vertices contains a {@link Double#NaN}. * * @return {@code true} if at least one vertex contains {@link Double#NaN}, {@code false} * otherwise. */ default boolean containsNaN() { for (int i = 0; i < getNumberOfVertices(); i++) { if (getVertexBufferView().get(i).containsNaN()) return true; } return false; }
/** * Create a triangle mesh for the given polygon. * * @param polygonTransformToWorld transform to use to obtain polygon 3D coordinates in world. * @param convexPolygon the polygon to create a mesh from. * @return the created triangle mesh. */ public static MeshDataHolder Polygon(RigidBodyTransform polygonTransformToWorld, ConvexPolygon2DReadOnly convexPolygon) { Point3D32[] points = new Point3D32[convexPolygon.getNumberOfVertices()]; int reverseIndex = convexPolygon.getNumberOfVertices(); for (int i = 0; i < convexPolygon.getNumberOfVertices(); i++) { Point2DReadOnly vertex = convexPolygon.getVertexBufferView().get(--reverseIndex); points[i] = new Point3D32((float) vertex.getX(), (float) vertex.getY(), 0.0f); polygonTransformToWorld.transform(points[i]); } return Polygon(points); }
/** * Gets a representative {@code String} of {@code convexPolygon2D} given a specific format to * use. * <p> * Using the default format {@link #DEFAULT_FORMAT}, this provides a {@code String} as follows: * * <pre> * Convex Polygon 2D: vertices = [ * ( 0.174, -0.452 ), * (-0.052, -0.173 ) ] * </pre> * </p> * * @param format the format to use for each number. * @param convexPolygon2D the object to get the {@code String} of. Not modified. * @return the representative {@code String}. */ public static String getConvexPolygon2DString(String format, ConvexPolygon2DReadOnly convexPolygon2D) { if (convexPolygon2D == null) return "null"; else return getConvexPolygon2DString(format, convexPolygon2D.getPolygonVerticesView(), convexPolygon2D.getNumberOfVertices()); }
/** * Packs the matrices A and b such that any point x is inside the polygon if it satisfies the equation A*x <= b. * * @param polygon * @param A * @param b */ public static void convertToInequalityConstraints(ConvexPolygon2DReadOnly polygon, DenseMatrix64F A, DenseMatrix64F b, double deltaInside) { int constraints = polygon.getNumberOfVertices(); if (constraints > 2) convertToInequalityConstraintsPolygon(polygon, A, b, deltaInside); else if (constraints > 1) convertToInequalityConstraintsLine(polygon, A, b, deltaInside); else convertToInequalityConstraintsPoint(polygon, A, b); }
/** * Finds the index of the closest vertex to the query. * <p> * Edge cases: * <ul> * <li>If the polygon has no vertices, this method fails and returns {@code -1}. * </ul> * </p> * * @param point the coordinates of the query. Not modified. * @return the index of the closest vertex to the query. * @throws OutdatedPolygonException if {@link ConvexPolygon2DBasics#update()} has not been called * since last time this polygon's vertices were edited. */ default int getClosestVertexIndex(Point2DReadOnly point) { checkIfUpToDate(); return EuclidGeometryPolygonTools.closestVertexIndexToPoint2D(point, getVertexBufferView(), getNumberOfVertices()); }
/** * Determines whether an observer can see the outside of the given edge of this convex polygon. * <p> * The edge is defined by its start {@code this.getVertex(edgeIndex)} and its end * {@code this.getNextVertex(edgeIndex)}. * </p> * * @param edgeIndex the vertex index of the start of the edge. * @param observer the coordinates of the observer. Not modified. * @return {@code true} if the observer can see the outside of the edge, {@code false} if the * observer cannot see the outside or is lying on the edge. * @throws OutdatedPolygonException if {@link ConvexPolygon2DBasics#update()} has not been called * since last time this polygon's vertices were edited. */ default boolean canObserverSeeEdge(int edgeIndex, Point2DReadOnly observer) { checkIfUpToDate(); return EuclidGeometryPolygonTools.canObserverSeeEdge(edgeIndex, observer, getVertexBufferView(), getNumberOfVertices(), isClockwiseOrdered()); }
/** * Finds the index of the closest edge to the query. * <p> * Edge cases: * <ul> * <li>If the polygon has one or no vertices, this method fails and returns {@code -1}. * </ul> * </p> * * @param point the coordinates of the query. Not modified. * @return the index of the closest edge to the query. * @throws OutdatedPolygonException if {@link ConvexPolygon2DBasics#update()} has not been called * since last time this polygon's vertices were edited. */ default int getClosestEdgeIndex(Point2DReadOnly point) { checkIfUpToDate(); return EuclidGeometryPolygonTools.closestEdgeIndexToPoint2D(point, getVertexBufferView(), getNumberOfVertices(), isClockwiseOrdered()); }
private double computeDistanceToClosestEdge(Point2D pointInsideConvexPolygon, ConvexPolygon2DReadOnly convexPolygon) { double minDistanceToEdge = Double.POSITIVE_INFINITY; double distanceToEdge = 0.0; int numberOfVertices = convexPolygon.getNumberOfVertices(); for (int i = 0; i < numberOfVertices - 1; i++) { Point2DReadOnly vertex = convexPolygon.getVertex(i); Point2DReadOnly vertex2 = convexPolygon.getVertex(i + 1); Point2D projectedPoint = projectPointOntoEdge(vertex, vertex2, pointInsideConvexPolygon); distanceToEdge = pointInsideConvexPolygon.distance(projectedPoint); if (distanceToEdge < minDistanceToEdge) { minDistanceToEdge = distanceToEdge; } } return minDistanceToEdge; }
/** * Finds the index of the closest vertex to the given line. * <p> * Edge cases: * <ul> * <li>If the polygon has no vertices, this method fails and returns {@code -1}. * </ul> * </p> * * @param line the query. Not modified. * @return the index of the closest vertex to the query. * @throws OutdatedPolygonException if {@link ConvexPolygon2DBasics#update()} has not been called * since last time this polygon's vertices were edited. */ default int getClosestVertexIndex(Line2DReadOnly line) { checkIfUpToDate(); return EuclidGeometryPolygonTools.closestVertexIndexToLine2D(line.getPoint(), line.getDirection(), getVertexBufferView(), getNumberOfVertices()); }
public static void convertToInequalityConstraintsPolygon(ConvexPolygon2DReadOnly polygon, DenseMatrix64F A, DenseMatrix64F b, double deltaInside) { int constraints = polygon.getNumberOfVertices(); A.reshape(constraints, 2); b.reshape(constraints, 1); for (int i = 0; i < constraints; i++) { Point2DReadOnly firstPoint = polygon.getVertex(i); Point2DReadOnly secondPoint = polygon.getNextVertex(i); double x = secondPoint.getX() - firstPoint.getX(); double y = secondPoint.getY() - firstPoint.getY(); double norm = Math.sqrt(x * x + y * y); x = x / norm; y = y / norm; A.set(i, 0, -y); A.set(i, 1, x); b.set(i, -deltaInside + firstPoint.getY() * x - firstPoint.getX() * y); // A.set(i, 0, firstPoint.y - secondPoint.y); // A.set(i, 1, -firstPoint.x + secondPoint.x); // b.set(i, firstPoint.y * (secondPoint.x - firstPoint.x) - firstPoint.x * (secondPoint.y - firstPoint.y)); } }
/** * Checks if a line intersects the edge with the given index. */ public static boolean doesLineIntersectEdge(Line2DReadOnly line, int edgeIndex, ConvexPolygon2DReadOnly polygon) { if (polygon.getNumberOfVertices() < 2) return false; Point2DReadOnly edgeStart = polygon.getVertex(edgeIndex); Point2DReadOnly edgeEnd = polygon.getNextVertex(edgeIndex); double lineDirectionX = line.getDirectionX(); double lineDirectionY = line.getDirectionY(); double edgeDirectionX = edgeEnd.getX() - edgeStart.getX(); double edgeDirectionY = edgeEnd.getY() - edgeStart.getY(); if (EuclidGeometryTools.areVector2DsParallel(lineDirectionX, lineDirectionY, edgeDirectionX, edgeDirectionY, EuclidGeometryTools.ONE_TEN_MILLIONTH)) return false; else return EuclidGeometryTools.doLine2DAndLineSegment2DIntersect(line.getPoint(), line.getDirection(), edgeStart, edgeEnd); }