Algorithm that determines a relationship between a square and rectangle - java

I need to find an algorithm which determines a relationship between a square and rectangle. It must be able to determine if:
The square is completely inside the rectangle
The square is partially inside (overlaps) the rectangle
Square's corner only touches a rectangle's corner
Square's edge is on the rectangle's edge
And here are the inputs (given values) that will help us to extract a mathematical formula for each case:
x coordinate of the center of the square = squareX
y coordinate of the center of the square = squareY
width of the square = squareW
x coordinate of the center of the rectangle = recX
y coordinate of the center of the rectangle = recY
width of the rectangle = recW
length of the rectangle = recL
P.S: Rectangle's sizes are always bigger than the square's width.
I will write the code in Java once we can extract an algorithm using mathematical operations.
Edit:
For the case of corners in touch, here is the code I wrote, and it works (Math.abs means the absolute value):
((Math.abs(Math.abs(recX-squareX)-(recW+squareW)/2))<=0.001) && ((Math.abs(Math.abs(recY-squareY)-(recL+squareW)/2))<=0.001)

updated for doubles
double dx = Math.abs(rectX - squareX);
double dy = Math.abs(rectY - squarey);
double dw2 = (rectW + squareW) / 2;
double dh2 = (rectL + squareW) / 2;
if (Double.compare(dx, dw2) == 0 && Double.compare(dy, dh2) == 0)
return CORNER_TOUCH;
else if (Double.compare(dx, dw2) > 0 || Double.compare(dy, dh2) > 0)
return OUTSIDE;
else if (Double.compare(dx, dw2) == 0 || Double.compare(dy, dh2) == 0)
return EDGE_TOUCH;
else if (Double.compare(dx, rectW - dw2) <= 0 &&
Double.compare(dy, rectL - dh2) <= 0)
return INSIDE;
else
return OVERLAPS;

squareX1 = squareX - squareW/2
squareY1 = squareY - squareW/2
squareX2 = squareX + squareW/2
squareY2 = squareY + squareW/2
recX1 = recX - recW/2
recY1 = recY - recL/2
recX2 = recX + recW/2
recY2 = recY + recL/2
inside = squareX1 > recX1 && squareX2 < recX2 && squareY1 > recY1 && squareY2 < recY2
overlaps = squareX1 < recX2 && squareX2 > recX1 && squareY1 < recY2 && squareY2 > recY1
the last two one should be trivial

Related

Collision detection between circles and rectangles knowing collision side

I have a "physics ball" that can bounce around the screen off the edges which works fine. But I wanted to be able to add boxes and have my ball be able to bounce of those, too. I have tried to create something and it feels like it is quite close, but there is flaw that I understand why it exists, but am unsure on how I can get around it.
if (colliding(ball, block))
{
if (ball.velocity.x > 0)
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x - ball.radius;
}
else
{
ball.velocity.x *= -ball.restitution;
ball.vector.x = block.vector.x + block.width + ball.radius;
}
if (ball.velocity.y > 0)
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y - ball.radius;
}
else
{
ball.velocity.y *= -ball.restitution;
ball.vector.y = block.vector.y + block.height + ball.radius;
}
}
colliding():
boolean colliding(MassEntity ball, Block block)
{
return PVector.dist(ball.vector, block.vector) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x + block.width, block.vector.y + block.height)) < ball.radius
|| PVector.dist(ball.vector, new PVector(block.vector.x, block.vector.y + block.height)) < ball.radius
|| (ball.vector.x - ball.radius < block.vector.x + block.width && ball.vector.x + ball.radius > block.vector.x
&& ball.vector.y - ball.radius < block.vector.y + block.height && ball.vector.y + ball.radius > block.vector.y);
}
(I know the if statement is a little monstrous, but I don't know what happened to the formatting honestly)
The issue is that when the ball collides with the rectangle, since the ball is "teleported" to outside of the rectangle (so it doesn't stay inside the rectangle due to the velocity being flipped), it teleports on both axes so pretty much the ball will weirdly teleport to the end of one of the edges.
I just need to somehow make if statements for the respective axes to only be considered in the appropriate situation.
First, compute P, the nearest point to the ball that is in the box :
PVector P = new PVector(
max(min(ball.vector.x, box.vector.x + box.width / 2), box.vector.x - box.width / 2),
max(min(ball.vector.y, box.vector.y + box.height / 2), box.vector.y - box.height / 2)
);
To check if there is a collision, you can check if the distance between P and the center of the ball is smaller than the radius of the ball.
To update the speed of the ball, you can do this :
PVector n = normalize(ball.vector.copy().sub(P));
ball.velocity.sub(n.mult(2 * n.dot(ball.velocity)));
n is the normal vector a the collision's position and to reflect the speed on the surface you have to delete the component of the velocity that is parallel to it. Then you have to add this same component multiplied by -1. As those two operations are the same, you can just do it one time with a factor 2.
A last precision I have to make is that you may need to check if the ball is going away from the box to avoid reversing the speed in that case :
PVector n = normalize(ball.vector.copy().sub(P));
float f = n.dot(ball.velocity);
if (f < 0)
ball.velocity.sub(n.mult(2 * f));
I haven't tested my code so tell me if there is a problem.

Cannot work out how to compare 2 dimensional arrays of different sizes

So I am building a project in order to build a tetris style game, and I want to be able to test whether the shape will be able to be added to a 5 x 5 grid. The shape is modelled by a 2D array, where a 1 is considered to be a single block of the shape (the shapes are made of a few blocks). The shapes are modelled with a 3 x 3 grid. The thing I must do is check the grid for whether the shape will be able to fit on top of it. Take for example placing a line shape at the top square of the grid, the line will go out of bounds and should not work, or another example is that the grid may already have a shape on it and so the line should not be able to be put on top of it.
This is the code that I've got thus far, and it is not working, I'm just really having a tough time conceptualising what to do. Thank you in advance.
Please note that cols is the number of columns in the grid (5) and rows is the same (5). Game piece is the shape, and the co-ordinates is where the user has clicked on the 5x5 grid.
Also: The anchor point of the shape is 1,1 of the 3x3 grid (so the anchor point is right in the middle of the grid). And get(int x, int y) method is getting the value stored in the 5x5 grid.
Sorry if this was not made clear in the beginning but I am trying to basically see whether the shape stored in the 3x3 grid (made up of blocks) can be placed on top of the 5x5 grid. The 3x3 grid that contains the block has a centre anchor point, so it would be 1,1 (since arrays start with 0). If the 5x5 grid has other blocks that are at the same co-ordinate of the new shape being added, then I want it to return false or if the shape becomes out of bounds when being placed on the 5x5 grid, but if it can be added successfully then it will return true.
public boolean canPlayPiece (GamePiece piece, int x, int y) {
logger.info("canPlayPiece - Block clicked coordinates: " + x + "," + y);
// Piece co-ordinates are 3 x 3, each element that is 1 means there is a block there
int[][] pieceCoordinates = piece.getBlocks();
// For loop to iterate through the grid
// first looping through x values
for (int i = x - 1; i < cols; i++) {
System.out.println("i= " + i);
// nested for loop to find the y values stored inside the x
for (int j = y - 1; j < rows; j++) {
System.out.println("j: " + j);
if (pieceCoordinates[x][y] == 1 && get(i,j) != 0) {
logger.info("canPlayPiece: FALSE");
return false;
}
}
}
logger.info("canPlayPiece: TRUE");
return true;
}
Ok i made the following for you:
public boolean canPlayPiece(GamePiece piece, int x, int y) {
int[][] pc = piece.getBlocks();
final int w = 3, h = 3, e = w - 1;
final int offX = -1, offY = -1; // The offset of the left top corner from 'x' and 'y'
int i, si, ei, ax, ay, rx, ry;
for (ei = w * h - 1; ei >= 0 && pc[ei / w][ei % w] == 0; ei--);
for (si = 0; si <= ei && pc[si / w][si % w] == 0; si++);
for (i = si + 1, ax = si % w; ax > 0 && i <= ei; i++) if (pc[i / w][rx = i % w] != 0) { si += Math.min(rx - ax, 0); ax = rx; }
for (i = ei - 1, ax = ei % w; ax < e && i >= si; i--) if (pc[i / w][rx = i % w] != 0) { ei += Math.max(rx - ax, 0); ax = rx; }
if (si > ei) return true; // There is no block in the piece's grid
int sx = si % w, sy = si / w, ex = ei % w, ey = ei / w; // The bounds of the shape inside of pc
int asx = x + offX + sx, asy = y + offY + sy, aex = asx + ex - sx, aey = asy + ey - sy;
if ((asx | asy | aex | aey | cols - 1 - aex | rows - 1 - aey) < 0) return false; // Would be out of bounds
for (rx = sx, ax = asx; rx <= ex; rx++, ax++) {
for (ry = sy, ay = asy; ry <= ey; ry++, ay++) {
// if (grid[ay][ax] != 0 && pc[ry][rx] != 0) return false; // Block overlaps another block
if (get(ax, ay) != 0 && pc[ry][rx] != 0) return false; // Block overlaps another block
}
}
return true;
}
First it figues out the bounds of the shape inside of 'pc' grid (the grid returned by 'piece.getBlocks()')
If there is no shape inside of 'pc' it will return true, since an empty shape can be placed anywhere (change the return value to false, if you want to return false in that case)
If the inner shape would go out of bounds, when being inserted, it will return false
In the end it will walk through both the grid (using your 'get(x: int, y: int) function) and 'pc' to check whether the shape in 'pc' overlaps with any preexisting blocks inside the grid. And if it doesn't it returns true.
I really hope that this works for you. I tested it out and it worked at least for me.

Raycasting inaccurately calculating intersections

I am trying to write a raycasting algorithm described in this article. I use a coordinate system where y increases upward, x increases to the left. The variable names in the following code snippet are taken from the article, I will describe them so that the code is easier to read:
rayDirX and rayDirY are coordinates of the vector pointing from the player's position on the map in the direction where the ray is being cast, alpha is the angle between this ray and the origin.
Px, Py are precise coordinates of the player's position on the map
Ax, Ay are the precise coordinates of the first horizontal intersection, later other horizontal intersections.
Bx, By are the precise coordinates of the first vertical intersection, later other vertical intersections.
horXa, horYa, vertXa, vertYb are the step increments, which are constant after finding the first intersection. I suspect that the problem with the algorithm is that these values are not calculated correctly.
mapX, mapY are the coordinates of the lower left corner of an intersection on a map. This is used to check if there is a wall on this position in the array map[][].
Horizontal and vertical intersections are checked alternately. Ax, Ay, or Bx, By should hold the precise position of the intersection.
// Calculate the initial intersections
// Horizontal intersections
// If the ray is facing up
if (rayDirY > 0)
{
horYa = 1;
Ay = mapY+1;
}
// If the ray is facing down
else
{
horYa = -1;
Ay = mapY;
}
horXa = Math.abs(horYa)/tanAlpha;
Ax = Px + (Ay-Py)/tanAlpha;
// Vertical intersections
// If the ray is facing right
if (rayDirX > 0)
{
vertXa = 1;
Bx = mapX+1;
}
// If the ray is facing left
else
{
vertXa = -1;
Bx = mapX;
}
vertYa = Math.abs(vertXa)*tanAlpha;
By = Py + (Px-Bx)*tanAlpha;
//Loop to find where the ray hits a wall
//Number of texture to display
int texNum;
boolean horizontal = Math.abs(Ax * Math.cos(alpha)) < Math.abs(Bx*Math.cos(alpha));
//This loop runs for each ray with the setup above
while(true) {
// Check horizontal intersection
if (horizontal)
{
mapX = (int)Math.floor(Math.abs(Ax));
mapY = (int)Math.floor(Math.abs(Ay));
if(mapX > mapWidth-1) mapX = mapWidth-1;
if(mapY > mapHeight-1) mapY = mapHeight-1;
if(mapX < 0) mapX = 0;
if(mapY < 0) mapY = 0;
texNum = map[mapX][mapY];
if(texNum > 0) break;
Ax += horXa;
Ay += horYa;
horizontal = false;
}
else {
mapX = (int)Math.floor(Math.abs(Bx));
mapY = (int)Math.floor(Math.abs(By));
if(mapX > mapWidth-1) mapX = mapWidth-1;
if(mapY > mapHeight-1) mapY = mapHeight-1;
if(mapX < 0) mapX = 0;
if(mapY < 0) mapY = 0;
texNum = map[mapX][mapY];
if (texNum > 0) break;
Bx += vertXa;
By += vertYa;
horizontal = true;
}
}
Using this algorithm, the image is not rendered properly, see the screenshot. I suspect that it is because of horYa, Ay, vertXa, Bx not being calculated correctly. UPDATE: After some debugging efforts, it seems that sometimes we choose to compute horizontal intersection instead of vertical and vice versa... Really strange!
Can you please spot an error in the algorithm? Thank you for any input!

2D Vertical/Horizontal collision detection

I'm writing a 2D platformer in Java, and I've run into a problem. I have two objects: Both have bounding boxes, and four coordinates to represent the corners of the boxes.
My issue is that I am trying to find a way to simulate collision, but I just can't seem to do it. I've tried searching all over the place, but most sites just demonstrate OpenGL tactics.
Let us represent the bounding box coordinates like so:
TL: Top-left
TR: Top-right
BL: Bottom-left
BR: Bottom-right
Here is how I originally proposed testing collision:
if(TL1 > TL2 && TL1 < TR2) //X-axis
//Collision happened, TL1 corner is inside of second object
else if(BL1 < TL2 && BL1 > BL2) //Y-axis
//Collision happened, BL1 corner is inside of second object
This is a very crude way of showing it, but basically I'm checking to see if a corner intersects the other object. The problem is, it doesn't account for both axii. That is to say, an x-collision will occur even if one object is above the other.
If I check for collision on both axii, then there is no way of telling whether it was a horizontal or vertical collision. Or maybe there is and I haven't figured it out.
Can anyone point me in the right direction?
this is from java.awt.Rectangle.
you should be able to modify it to suit the coords you have.
/**
* Determines whether or not this <code>Rectangle</code> and the specified
* <code>Rectangle</code> intersect. Two rectangles intersect if
* their intersection is nonempty.
*
* #param r the specified <code>Rectangle</code>
* #return <code>true</code> if the specified <code>Rectangle</code>
* and this <code>Rectangle</code> intersect;
* <code>false</code> otherwise.
*/
public boolean intersects(Rectangle r) {
int tw = this.width;
int th = this.height;
int rw = r.width;
int rh = r.height;
if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
return false;
}
int tx = this.x;
int ty = this.y;
int rx = r.x;
int ry = r.y;
rw += rx;
rh += ry;
tw += tx;
th += ty;
// overflow || intersect
return ((rw < rx || rw > tx) &&
(rh < ry || rh > ty) &&
(tw < tx || tw > rx) &&
(th < ty || th > ry));
}

Test of Point inside polygon in Android

The other day I did a class in Java to calculate if a point(X,Y) is inside a polygon. (X and Y are double, because will be geo-coordinates).
I know that Java has the class Polygon, but I had to use Path2D and Point2D, because Polygon don't allow double's, just integers :(
Once I have the polygon done in Path2D, I used the method contains (Path2D had it), and my problem was solved.
But now, I want to import to Android, and the problem is here, because Path2D needs to import:
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
and in Android don't exist awt, so I can't use.
So, is there any class similar to Path2D that had contains method? or I have to calculate by myself?
Here is how I did in Java using Path2D:
private void ConstructPolygon(Vector<Point2D> coodinates)
{
this.polygon.moveTo(coodinates.get(0).getX(), coodinates.get(0).getY());
//System.out.println(coodinates.get(0).getX() + " " + coodinates.get(0).getY());
//System.out.println("asda");
for(int i = 1; i < this.num_points; i++)
{
//System.out.println(coodinates.get(i).getX() + " " + coodinates.get(i).getY());
this.polygon.lineTo(coodinates.get(i).getX(), coodinates.get(i).getY());
}
this.polygon.closePath();
}
public boolean InsideCity(Point2D punto)
{
return this.polygon.contains(punto);
}
You can use my simple library exactly for this: https://github.com/snatik/polygon-contains-point.
Prepare polygon:
Polygon polygon = Polygon.Builder()
.addVertex(new Point(1, 3))
.addVertex(new Point(2, 8))
.addVertex(new Point(5, 4))
.addVertex(new Point(5, 9))
.addVertex(new Point(7, 5))
.addVertex(new Point(6, 1))
.addVertex(new Point(3, 1))
.build();
And check whereas the point is inside the polygon:
Point point = new Point(4.5f, 7);
boolean contains = polygon.contains(point);
It works with float types and with polygons that contain holes :)
You can use Google Maps PolyUtil:
import com.google.maps.android.PolyUtil;
boolean inside = PolyUtil.containsLocation(new LatLng(...), poly, true);
Here is how I did it in Android.
It is based on this java program (ray casting algorithm) :
https://gis.stackexchange.com/questions/42879/check-if-lat-long-point-is-within-a-set-of-polygons-using-google-maps/46720#46720
public boolean pointInPolygon(LatLng point, Polygon polygon) {
// ray casting alogrithm http://rosettacode.org/wiki/Ray-casting_algorithm
int crossings = 0;
List<LatLng> path = polygon.getPoints();
path.remove(path.size()-1); //remove the last point that is added automatically by getPoints()
// for each edge
for (int i=0; i < path.size(); i++) {
LatLng a = path.get(i);
int j = i + 1;
//to close the last edge, you have to take the first point of your polygon
if (j >= path.size()) {
j = 0;
}
LatLng b = path.get(j);
if (rayCrossesSegment(point, a, b)) {
crossings++;
}
}
// odd number of crossings?
return (crossings % 2 == 1);
}
public boolean rayCrossesSegment(LatLng point, LatLng a,LatLng b) {
// Ray Casting algorithm checks, for each segment, if the point is 1) to the left of the segment and 2) not above nor below the segment. If these two conditions are met, it returns true
double px = point.longitude,
py = point.latitude,
ax = a.longitude,
ay = a.latitude,
bx = b.longitude,
by = b.latitude;
if (ay > by) {
ax = b.longitude;
ay = b.latitude;
bx = a.longitude;
by = a.latitude;
}
// alter longitude to cater for 180 degree crossings
if (px < 0 || ax <0 || bx <0) { px += 360; ax+=360; bx+=360; }
// if the point has the same latitude as a or b, increase slightly py
if (py == ay || py == by) py += 0.00000001;
// if the point is above, below or to the right of the segment, it returns false
if ((py > by || py < ay) || (px > Math.max(ax, bx))){
return false;
}
// if the point is not above, below or to the right and is to the left, return true
else if (px < Math.min(ax, bx)){
return true;
}
// if the two above conditions are not met, you have to compare the slope of segment [a,b] (the red one here) and segment [a,p] (the blue one here) to see if your point is to the left of segment [a,b] or not
else {
double red = (ax != bx) ? ((by - ay) / (bx - ax)) : Double.POSITIVE_INFINITY;
double blue = (ax != px) ? ((py - ay) / (px - ax)) : Double.POSITIVE_INFINITY;
return (blue >= red);
}
}
Sorry #sromku I asked my self (I never used this type of things)
That's how I solved if anyone have the same question
Builder poly2 = new Polygon.Builder();
for(int i = 0; i< xpoints.length;i++){
poly2.addVertex(new Point(xpoints[i],ypoints[i]));
}
Polygon polygon2 = poly2.build();

Categories

Resources