package br.com.etyllica.core.collision; import br.com.etyllica.core.linear.Point2D; import br.com.etyllica.layer.GeometricLayer; import br.com.etyllica.layer.Layer; import br.com.etyllica.linear.Ellipse; import br.com.etyllica.util.math.EtyllicaMath; public class CollisionDetector { public static boolean colideCirclePoint(float cx, float cy, float radius, int px, int py) { float dx = cx - px; float dy = cy - py; if ( ( dx * dx ) + ( dy * dy ) < radius * radius ) { return true; } return false; } /** * Found at: http://stackoverflow.com/a/402010 * @param circle - the circular layer * @param rect - the rectangular layer * @return collision detected */ public static boolean colideCircleRect(Layer circle, Layer rect) { int centerX = circle.getX()+circle.getW()/2; int centerY = circle.getY()+circle.getH()/2; int radius = circle.getW(); double circleDistanceX = Math.abs(centerX - rect.getX()); double circleDistanceY = Math.abs(centerY - rect.getY()); if (circleDistanceX > (rect.getW()/2 + radius)) { return false; } if (circleDistanceY > (rect.getH()/2 + radius)) { return false; } if (circleDistanceX <= (rect.getW()/2)) { return true; } if (circleDistanceY <= (rect.getH()/2)) { return true; } double cornerDistance = Math.pow((circleDistanceX - rect.getW()/2),2) + Math.pow((circleDistanceY - rect.getH()/2),2); return (cornerDistance <= (radius*radius)); } public static boolean colideIsometricPoint(Layer cam, int px, int py) { float mx = cam.utilWidth()/2; float my = cam.utilHeight()/2; float x = px-cam.getX(); float y = py-cam.getY(); if(y > my) { y = my-(y-my); } if ((x > mx+1+(2*y))||(x < mx-1-(2*y))) { return false; } return true; } //Checks collision with a vertical hexagon public static boolean colideHexagonPoint(Layer cam, int px, int py) { float mx = cam.utilWidth()/4; float my = cam.utilHeight()/2; float x = px-cam.getX(); float y = py-cam.getY(); if(x > mx*3) { x = mx-(x-mx*3); } else if(x > mx) { return py >= cam.getY() && py <= cam.getY()+cam.utilHeight(); } if ((y > my + 1 + (2*x)) || (y < my - 1 - (2*x))) { return false; } return true; } public static boolean colideRectPoint(GeometricLayer layer, double px, double py) { int rectWidth = layer.utilWidth(); int rectHeight = layer.utilHeight(); int rectCenterX = layer.getX()+rectWidth/2; int rectCenterY = layer.getY()+rectHeight/2; return Math.abs(rectCenterX-px) <= rectWidth/2 && Math.abs(rectCenterY-py) <= rectHeight/2; } /** * Code found at: http://stackoverflow.com/questions/5650032/collision-detection-with-rotated-rectangles */ public static boolean colideRectPoint(Layer layer, double px, double py) { int rectWidth = layer.utilWidth(); int rectHeight = layer.utilHeight(); int offsetX = 0; int offsetY = 0; if (layer.getScaleX() != 1) { rectWidth *= layer.getScaleX(); offsetX = (int)(layer.utilWidth()*(1-layer.getScaleX()))/2; } if (layer.getScaleY() != 1) { rectHeight *= layer.getScaleY(); offsetY = (int)(layer.utilHeight()*(1-layer.getScaleY()))/2; } int rectCenterX = layer.getX()+offsetX+rectWidth/2; int rectCenterY = layer.getY()+offsetY+rectHeight/2; double rectRotation = Math.toRadians(-layer.getAngle()); return colideRectPoint(rectWidth, rectHeight, rectRotation, rectCenterX, rectCenterY, px, py); } public static boolean colideRectPoint(Layer layer, double px, double py, double scaleX, double scaleY) { int rectWidth = layer.utilWidth(); int rectHeight = layer.utilHeight(); int offsetX = 0; int offsetY = 0; if(layer.getScaleX()!=1) { rectWidth *= layer.getScaleX(); offsetX = (int)(layer.utilWidth()*(1-layer.getScaleX()))/2; } if(layer.getScaleY()!=1) { rectHeight *= layer.getScaleY(); offsetY = (int)(layer.utilHeight()*(1-layer.getScaleY()))/2; } int rectCenterX = layer.getX()+offsetX+rectWidth/2; int rectCenterY = layer.getY()+offsetY+rectHeight/2; double rectRotation = Math.toRadians(-layer.getAngle()); return colideRectPoint(rectWidth, rectHeight, rectRotation, rectCenterX, rectCenterY, px, py); } public static boolean colideRectPoint(Layer layer, Point2D point) { int rectCenterX = layer.getX()+layer.utilWidth()/2; int rectCenterY = layer.getY()+layer.utilHeight()/2; int rectWidth = layer.utilWidth(); int rectHeight = layer.utilHeight(); double rectRotation = Math.toRadians(-layer.getAngle()); return colideRectPoint(rectWidth, rectHeight, rectRotation, rectCenterX, rectCenterY, point.getX(), point.getY()); } /** Rectangle To Point. */ private static boolean colideRectPoint(double rectWidth, double rectHeight, double rectRotation, double rectCenterX, double rectCenterY, double pointX, double pointY) { if(rectRotation == 0) //High speed for rectangles without rotation return Math.abs(rectCenterX-pointX) < rectWidth/2 && Math.abs(rectCenterY-pointY) < rectHeight/2; final double cos = Math.cos(rectRotation); final double sin = Math.cos(rectRotation); double tx = cos*pointX - sin*pointY; double ty = cos*pointY + sin*pointX; double cx = cos*rectCenterX - sin*rectCenterY; double cy = cos*rectCenterY + sin*rectCenterX; return Math.abs(cx-tx) < rectWidth/2 && Math.abs(cy-ty) < rectHeight/2; } public static boolean colideRectPoint(int x, int y, int w, int h, int pointX, int pointY) { int rectCenterX = x + w / 2; int rectCenterY = y + h / 2; int rectWidth = w; int rectHeight = h; return Math.abs(rectCenterX-pointX) < rectWidth/2 && Math.abs(rectCenterY-pointY) < rectHeight/2; } /** Circle To Segment. */ private static boolean colideCircleLine(double circleCenterX, double circleCenterY, double circleRadius, double lineAX, double lineAY, double lineBX, double lineBY) { double lineSize = Math.sqrt(Math.pow(lineAX-lineBX, 2) + Math.pow(lineAY-lineBY, 2)); double distance; if (lineSize == 0) { distance = Math.sqrt(Math.pow(circleCenterX-lineAX, 2) + Math.pow(circleCenterY-lineAY, 2)); return distance < circleRadius; } double u = ((circleCenterX - lineAX) * (lineBX - lineAX) + (circleCenterY - lineAY) * (lineBY - lineAY)) / (lineSize * lineSize); if (u < 0) { distance = Math.sqrt(Math.pow(circleCenterX-lineAX, 2) + Math.pow(circleCenterY-lineAY, 2)); } else if (u > 1) { distance = Math.sqrt(Math.pow(circleCenterX-lineBX, 2) + Math.pow(circleCenterY-lineBY, 2)); } else { double ix = lineAX + u * (lineBX - lineAX); double iy = lineAY + u * (lineBY - lineAY); distance = Math.sqrt(Math.pow(circleCenterX-ix, 2) + Math.pow(circleCenterY-iy, 2)); } return distance < circleRadius; } /** Rectangle To Circle. */ public static boolean colideRectCircle(double rectWidth, double rectHeight, double rectRotation, double rectCenterX, double rectCenterY, double circleCenterX, double circleCenterY, double circleRadius) { double tx, ty, cx, cy; if(rectRotation == 0) { //High speed for rectangles without rotation tx = circleCenterX; ty = circleCenterY; cx = rectCenterX; cy = rectCenterY; } else { tx = Math.cos(rectRotation)*circleCenterX - Math.sin(rectRotation)*circleCenterY; ty = Math.cos(rectRotation)*circleCenterY + Math.sin(rectRotation)*circleCenterX; cx = Math.cos(rectRotation)*rectCenterX - Math.sin(rectRotation)*rectCenterY; cy = Math.cos(rectRotation)*rectCenterY + Math.sin(rectRotation)*rectCenterX; } return colideRectPoint(rectWidth, rectHeight, rectRotation, rectCenterX, rectCenterY, circleCenterX, circleCenterY) || colideCircleLine(tx, ty, circleRadius, cx-rectWidth/2, cy+rectHeight/2, cx+rectWidth/2, cy+rectHeight/2) || colideCircleLine(tx, ty, circleRadius, cx+rectWidth/2, cy+rectHeight/2, cx+rectWidth/2, cy-rectHeight/2) || colideCircleLine(tx, ty, circleRadius, cx+rectWidth/2, cy-rectHeight/2, cx-rectWidth/2, cy-rectHeight/2) || colideCircleLine(tx, ty, circleRadius, cx-rectWidth/2, cy-rectHeight/2, cx-rectWidth/2, cy+rectHeight/2); } public static boolean colideRectRect(Layer a, Layer b) { if(a.getAngle() == 0 && b.getAngle() == 0) { return ((a.getX() + a.utilWidth()/2 - b.getX()+b.utilWidth()/2) * 2 < (a.utilWidth() + b.utilWidth())) && ((a.getY() + a.utilHeight()/2 - b.getY()+b.utilHeight()/2) * 2 < (a.utilHeight() + b.utilHeight())); } else { return colidePolygon(a, b); } } /** * Code found at: http://stackoverflow.com/a/21647567 */ public static boolean colidePolygon(Layer a, Layer b) { Point2D[] pointsA = getBounds(a); Point2D[] pointsB = getBounds(b); return colidePoints(pointsA, pointsB); } public static Point2D[] getBounds(Layer layer) { Point2D[] points = new Point2D[4]; points[0] = new Point2D(layer.getX(), layer.getY()); points[1] = new Point2D(layer.getX()+layer.utilWidth(), layer.getY()); points[2] = new Point2D(layer.getX()+layer.utilWidth(), layer.getY()+layer.utilHeight()); points[3] = new Point2D(layer.getX(), layer.getY()+layer.utilHeight()); if(layer.getAngle() != 0) { for(Point2D point: points) { point.rotate(layer.getX()+layer.utilWidth()/2, layer.getY()+layer.utilHeight()/2, layer.getAngle()); } } return points; } private static double[] e2p(double x, double y) { return new double[] { x, y, 1 }; } // standard vector maths private static double[] getCrossProduct(double[] u, double[] v) { return new double[] { u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0] }; } private static double getDotProduct(double[] u, double[] v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; } // collision check public static boolean colidePoints(Point2D[] pointsA, Point2D[] pointsB) { return !(isSeparate(pointsA, pointsB) || isSeparate(pointsB, pointsA)); } // the following implementation expects the convex polygon's vertices to be in counter clockwise order private static boolean isSeparate(Point2D[] coordsA, Point2D[] coordsB) { edges: for (int i = 0; i < coordsA.length; i++) { double[] u = e2p(coordsA[i].getX(), coordsA[i].getY()); int ni = i + 1 < coordsA.length ? i + 1 : 0; double[] v = e2p(coordsA[ni].getX(), coordsA[ni].getY()); double[] pedge = getCrossProduct(u, v); for (Point2D p : coordsB) { double d = getDotProduct(pedge, e2p(p.getX(), p.getY())); if (d > -0.001) { continue edges; } } return true; } return false; } public static boolean colideEllipsePoint(Ellipse ellipse, double px, double py) { double cx = ellipse.getCenter().getX(); double cy = ellipse.getCenter().getY(); double angle = Math.toRadians(ellipse.getAngle()); double a = ellipse.getW(); double b = ellipse.getH(); return colideEllipsePoint(cx, cy, angle, a, b, px, py); } public static boolean colideEllipsePoint(double cx, double cy, double angle, double a, double b, double px, double py) { final double cos = Math.cos(angle); final double sin = Math.sin(angle); double p = EtyllicaMath.sqr(cos*(px-cx)+sin*(py-cy))/(a*a); double q = EtyllicaMath.sqr(sin*(px-cx)-cos*(py-cy))/(b*b); return (p + q <= 1); } }