public interface ICollidable { // . // , . float getRadius(); Vector3 getPosition(); ArrayList<Vector2> getConvexHull(Plane plane); // ( ) ArrayList<Vector3> getNormals(); void normalizeLocation(); }
private static ArrayList<Plane> getPlanes(ICollidable firstPh, ICollidable secondPh) { ArrayList<Plane> planes = new ArrayList<>(); ArrayList<Vector3> firstNormals = firstPh.getNormals(); ArrayList<Vector3> secondNormals = secondPh.getNormals(); Plane plane = new Plane(); int size = firstNormals.size() + secondNormals.size(); for(int i = 0; i < size; i++) { setPlane(plane, firstNormals, secondNormals, i); if (!planes.contains(plane)) planes.add(new Plane(plane)); } return planes; } private static void setPlane(Plane plane, ArrayList<Vector3> firstNormals, ArrayList<Vector3> secondNormals, int num) { // ( x, y ) if (num < firstNormals.size()) plane.setFrom(firstNormals.get(num)); else { num -= firstNormals.size(); plane.setFrom(secondNormals.get(num)); } // . plane.swapZY(); }
private static CheckResult check(ICollidable firstPh, ICollidable secondPh) { // ArrayList<Plane> planes = getPlanes(firstPh, secondPh); Collision2D min = null; Plane minPlane = new Plane(); for (Plane plane : planes) { // . ArrayList<Vector2> resultOne = firstPh.getConvexHull(plane); ArrayList<Vector2> resultTwo = secondPh.getConvexHull(plane); // . ( "check") Collision2D collision = new Collision2D(resultOne, resultTwo); Vector.release(resultOne); Vector.release(resultTwo); if (!collision.isCollide()) return null; // if (min == null || collision.getMTVLength() < min.getMTVLength()) { min = collision; minPlane.setFrom(plane); } plane.release(); } return new CheckResult(min, minPlane); }
Voguntaya![]() | Convex![]() |
private ArrayList<Vector2> getDistinctProjection(Plane plane) { Vector2 vector = Vector.getInstance(2); // HashSet ( ) ArrayList<Vector2> result = new ArrayList<>(); for (Vector3 current : vertices) { plane.getProjection(vector, current); if (!result.contains(vector)) // { Vector2 copy = Vector.getInstance(2, vector); result.add(copy); } } Vector.release(vector); return result; } // Plane: public void getProjection(Vector2 result, Vector3 vector) { throwIfReleased(); float x = vector.getX() * xAxis.getX() + vector.getY() * xAxis.getY() + vector.getZ() * xAxis.getZ(); float y = vector.getX() * yAxis.getX() + vector.getY() * yAxis.getY() + vector.getZ() * yAxis.getZ(); result.setFrom(x, y); }
The convex hull of a set X is the smallest convex set containing X. “Least set” here means the smallest element with respect to the embedding of sets, that is, such a convex set containing this shape that it is contained in any other convex set containing this shape.
Imagine a board into which you have driven - but not to the bit of a hat - many nails. Take a rope, tie a sliding loop (lasso) on it and throw it on the board, and then tighten it. The rope surrounds all the nails, but it concerns only some of the most external. Those nails, which it concerns, constitute a convex shell for the entire group of nails [1].
@Override public ArrayList<Vector2> getConvexHull(Plane plane) { // ArrayList<Vector2> projection = getDistinctProjection(plane); ArrayList<Vector2> convexHull = new ArrayList<>(projection.size()); if (projection.size() < 2) throw new IllegalStateException("projection size less than 2"); // , 100% // // , . int firstIndex = getFirstPointIndex(projection); Vector2 first = projection.remove(firstIndex); convexHull.add(first); // Collections.sort(projection, new AngleComparator(first)); // , .. . Vector2 second = projection.remove(0); convexHull.add(second); Vector2 prevVector = Vector.getInstance(2); Vector2 currentVector = Vector.getInstance(2); for(Vector2 current : projection) { Vector2 firstPrevPoint = convexHull.get(convexHull.size() - 1); Vector2 secondPrevPoint = convexHull.get(convexHull.size() - 2); // prevVector.setFrom(firstPrevPoint); prevVector.subtract(secondPrevPoint); // currentVector.setFrom(current); currentVector.subtract(firstPrevPoint); // , float angle = prevVector.getAngle(currentVector); if (angle >= 180 && angle < 360) convexHull.remove(convexHull.size() - 1); // convexHull.add(current); } Vector.release(prevVector); Vector.release(currentVector); return convexHull; }
// Vector2 public float getAngle(Vector2 other) { throwIfReleased(); float scalar = getScalar(other); float lengthOne = this.getLength(); float lengthTwo = other.getLength(); float angle = (float)Math.toDegrees(Math.acos(scalar / (lengthOne * lengthTwo))); return Angle.correct(getCross(other) > 0 ? angle : 360 - angle); }
private static int getFirstPointIndex(ArrayList<Vector2> projection) { Vector2 minVector = null; int minVectorIndex = 0; int size = projection.size(); for (int i = 0; i < size; i++) { Vector2 current = projection.get(i); if (minVector == null) { minVector = current; continue; } int compareX = Float.compare(current.getX(), minVector.getX()); if (compareX < 0) { minVector = current; minVectorIndex = i; } if (compareX == 0) { int compareY = Float.compare(current.getY(), minVector.getY()); if (compareY == 0) throw new IllegalArgumentException("projection has the same points"); if (compareY > 0) { minVector = current; minVectorIndex = i; } } } return minVectorIndex; }
private static class AngleComparator implements Comparator<Vector2> { private Vector2 first; private Vector2 left; private Vector2 right; public AngleComparator(Vector2 first) { this.first = first; left = Vector.getInstance(2); right = Vector.getInstance(2); } @Override public int compare(Vector2 lhs, Vector2 rhs) { // // left.setFrom(lhs); left.subtract(first); right.setFrom(rhs); right.subtract(first); // float firstAngle = Vector2.xAxis.getAngle(left); float secondAngle = Vector2.xAxis.getAngle(right); // // : 15, 45, 315, 345 () => -45, -15, 15, 45 () if (firstAngle > 90) firstAngle -= 360; if (secondAngle > 90) secondAngle -= 360; // // // . // , // if (Math.abs(firstAngle - secondAngle) <= Vector.epsilon) { float leftLength = left.getLength(); float rightLength = right.getLength(); // 0, if (firstAngle >= 0) return Float.compare(rightLength, leftLength); return Float.compare(leftLength, rightLength); } // return Float.compare(firstAngle, secondAngle); } }
private static CheckResult check(ArrayList<Vector2> firstVertices, ArrayList<Vector2> secondVertices) { Vector2 mtv = null; Vector2 normal = Vector.getInstance(2); float minMTVLength = 0.0f; int count = firstVertices.size() + secondVertices.size(); for (int i = 0; i < count; i ++) { setNormal(normal, firstVertices, secondVertices, i); // . X - Y - . Vector2 firstProjection = normal.getProjection(firstVertices); Vector2 secondProjection = normal.getProjection(secondVertices); // , , . if (firstProjection.getX() < secondProjection.getY() || secondProjection.getX() < firstProjection.getY()) return null; // . , . if (mtv == null) { mtv = Vector.getInstance(2, normal); minMTVLength = getIntersectionLength(firstProjection, secondProjection); } else { float mtvLength = getIntersectionLength(firstProjection, secondProjection); if (Math.abs(mtvLength) < Math.abs(minMTVLength)) { mtv = Vector.getInstance(2, normal); minMTVLength = mtvLength; } } } return new CheckResult(mtv, minMTVLength); } // Vector2 public Vector2 getProjection(ArrayList<Vector2> vertices) { Vector2 result = null; for (Vector2 current : vertices) { float projection = getScalar(current); if (result == null) result = new Vector2(projection, projection); // x - max if (projection > result.getX()) result.setX(projection); // y - min if (projection < result.getY()) result.setY(projection); } return result; }
private static float getIntersectionLength(Vector2 firstProjection, Vector2 secondProjection) { return (secondProjection.getY() - firstProjection.getX() > 0) ? secondProjection.getY() - firstProjection.getX() : firstProjection.getY() - secondProjection.getX(); } private static void setNormal(Vector2 normal, ArrayList<Vector2> vertices, int num) { Vector2 firstPoint = vertices.get(num); Vector2 secondPoint = vertices.get(num + 1 == vertices.size() ? 0 : num + 1); Vector2 edge = secondPoint.getSubtract(firstPoint); normal.setX(-edge.getY()); normal.setY(edge.getX()); normal.normalize(); }
private static Vector3 getMTV(CheckResult result) { Vector2 mtv2 = result.collision.getMTV(); Vector3 mtv3 = new Vector3(mtv2.getX(), mtv2.getY(), 0); Vector3 planeX = result.plane.xAxis(); Vector3 planeY = result.plane.yAxis(); Vector3 planeZ = result.plane.zAxis(); float[] matrix = new float[16]; matrix[0] = planeX.getX(); matrix[1] = planeX.getY(); matrix[2] = planeX.getZ(); matrix[4] = planeY.getX(); matrix[5] = planeY.getY(); matrix[6] = planeY.getZ(); matrix[8] = planeZ.getX(); matrix[9] = planeZ.getY(); matrix[10] = planeZ.getZ(); matrix[15] = 1.0f; Matrix.multiplyMV(mtv3.getRaw(), 0, matrix, 0, mtv3.getRaw(), 0); mtv3.normalize(); return mtv3; }
Source: https://habr.com/ru/post/257655/
All Articles