Algorithm to check intersection between two objects - java

I'm programming a little game and I'm having some problems with the intersections. I need an efficient algorithm to check if two objects (which have x and y coords. and also a width and an height) are intersecting.
I tried with the following, but it doesn't always work, sometimes it doesn't recnognize an intersection.
public boolean contains(int x, int y) {
if ((x < this.x + this.width) && (x >= this.x) && (y < this.y + this.height) && (y >= this.y))
return true;
else
return false;
}
I have an ArrayList containing the objects, and I do the following:
private boolean checkIntersection(String pDirection) {
for (int i = 0; i < walls.size(); i++) {
if (pDirection.equalsIgnoreCase("right") && car.contains(walls.get(i).getX() - 1, walls.get(i).getY()))
return true;
if (pDirection.equalsIgnoreCase("left") && car.contains(walls.get(i).getX() + 30, walls.get(i).getY()))
return true;
if (pDirection.equalsIgnoreCase("top") && car.contains(walls.get(i).getX(), walls.get(i).getY() + 30))
return true;
if (pDirection.equalsIgnoreCase("down") && car.contains(walls.get(i).getX(), walls.get(i).getY() - 1))
return true;
}
return false;
}
Note that "-1" and "+30" is to avoid the car enter the "walls", there the walls have a width of 30 and an height of 30. The car also has the same dimensions.
Also note that the x and y are the top-left cords of the rectangles. The car and the walls are rectangles.
I would be thankful for your help.
INFO: It doesn't recnognize an intersection at the beginn of a row of walls if I am above the wall and I change the direction to "down" or viceversa.
See picture
EDIT 1 (I tried inverting the objects, but it also doesn't always work):
private boolean checkIntersection(String pDirection) {
for (int i = 0; i < walls.size(); i++) {
if (pDirection.equals("right") && walls.get(i).contains(car.getX() + 30, car.getY()))
return false;
if (pDirection.equals("left") && walls.get(i).contains(car.getX() - 1, car.getY()))
return false;
if (pDirection.equals("top") && walls.get(i).contains(car.getX(), car.getY() - 1))
return false;
if (pDirection.equals("down") && walls.get(i).contains(car.getX(), car.getY() + 30))
return false;
}
return true;
}

The flaw in your algorithm is, you are always checking the left-top point of the wall whether it is in the car. However, this is not equivalent to having intersection.
Instead, you should check whether any one of the objects contains (at least) one corner (not necessarily the top left one) of the other object.
Note that you should perform this check for both sides, i.e. either the car contains any corner of the wall or the wall contains any corner of the car.

I solved modifying the contains method in the following way, and it now works perfectly:
public boolean contains(int x, int y) {
if ((x < this.x + this.width) && (x > this.x-this.width) && (y < this.y + this.height) && (y > this.y-this.height))
return true;
else
return false;
}
I think that I did it involuntarily (checking for non-intersection instead of intersection), but I can optimize it using the answer/suggestion of #samgak and #Gene. So thanks, problem solved.

Related

Java change long if statement into for loop

I have an if statement that looks like this:
if (pan[x + 1][y + 1].getBackground() == TeamColor &&
pan[x + 1][y].getBackground() == TeamColor &&
pan[x + 1][y -1].getBackground() == TeamColor &&
pan[x][y - 1].getBackground() == TeamColor &&
pan[x - 1][y - 1].getBackground() == TeamColor &&
pan[x - 1][y].getBackground() == TeamColor &&
pan[x - 1][y + 1].getBackground() == TeamColor &&
pan[x][y + 1].getBackground() == TeamColor) {
// do something
}
The goal is to check every item (in a 2d array) around the current x and y values and make sure they are the correct color.
I assume there is a simple way to do such. I would assume creating a for loop would solve the problem by iterating through each item but unfortunately was not able to think of a way to do this because the items are not all in sequence.
NOTE: i found many other posts on stackoverflow that where titled "solution for very long if statement" unfortunately they were in different programing languages (such as python, android and javascript)
NOTE 2: this is Not a duplicate of this post. It was a question of strings and regex and unfortunately not the solution to my problem
Hopefully someone will have an answer!
Try something like this:
boolean match = true;
for (int dx = -1; match && (dx < 2); ++dx) {
for (int dy = -1; match && (dy < 2); ++dy) {
if (dx != 0 || dy != 0) {
match = pan[x+dx][y+dy].getBackground() == TeamColour;
}
}
}
if (match) {
// do something
}
Basically, you want to check offsets -1, 0 and 1 in each direction, so we have two for loops, each producing those offsets in one dimension. We then check the array element corresponding to each offset, and keep track using the match variable.
Note though that, like the original code, this will fail near boundaries (e.g. if x == 0). This can be fixed if necessary.
It is possible, of course, to instead have the loops run over the actual indices to check (e.g. for (int x2 = x-1; x2 < x+2; ++x)). It's much the same in the end.
for (int a = x-1;a <= x+1;a++)
{
if (a < 0 || a >= pan.length) continue;
for (int b = y-1; b <= y+1; b++)
{
if (b < 0 || b >= pan[a].length) continue;
if (a == x && b == y) continue;
if (pan[a][b].getBackground() != TeamColor)
return false;
}
}
return true;
I can propose two ways :
1) Full object way
You could introduce a custom class Coordinate that holds two values : the x and y coordinates.
Create a List of Coordinate where you had the Coordinate element you want to test and iterate on it to achieve your need.
public class Coordinate{
private final int x;
private final int y;
public Coordinate(int x, int y){
this.x = x;
this.y = y;
}
public getX(){
return x;
}
public getY(){
return y;
}
}
And you can use it :
List<Coordinate> coordinates = new ArrayList<>();
coordinates.add(new Coordinate(1,1));
coordinates.add(new Coordinate(1,0));
coordinates.add(new Coordinate(1,-1));
coordinates.add(new Coordinate(0,-1));
coordinates.add(new Coordinate(-1,-1));
coordinates.add(new Coordinate(-1,0));
coordinates.add(new Coordinate(-1,1));
coordinates.add(new Coordinate(0,1));
// you can also init them with a loop
boolean isMatched = true;
for (Coordinate coordinate : coordinates){
if (pan[x + coordinate.getX()][y + coordinate.getY()].getBackground() != TeamColor){
isMatched = false;
break;
}
}
The object way is more verbose but it has the advantage to expose rules.
So you can read and change it easily.
Suppose, the rules to check become more complex, it becomes very valuable.
2) Shorter code way
It is the same logical even by inlining values of Coordinate and by ignoring the specific case that you don't want to test (no change case).
boolean isMatched = true;
for (int xDelta = -1; xDelta <=1; xDelta++){
for (int yDelta = -1; yDelta <=1; yDelta++){
// as you don't want to test if no change
if (yDelta == 0 && xDelta ==0){
continue;
}
if (pan[x + xDelta][y + yDelta ].getBackground() != TeamColor){
isMatched = false;
break;
}
}

Comparison error for isometric sorting

So I'm implementing an isometric sorter for my sprites and I'm having some issues with the comparison of when the tiles should be rendered. I'm sorting all the isometric sprites that will be rendered by implementing them as comparable.
The problem is, when I'm implementing the following compareTo method:
// 1 = render this after
// 0 == render same
// -1 = render this before
#Override
public int compareTo(IsoSprite o) {
if(z >= o.z && maxY <= o.minY && maxX <= o.minX){
return 1;
}
if(z >= o.z && maxY >= o.minY && maxX >= o.minX){
return -1;
}
if(z > o.z){
return 1;
}
if(z < o.z){
return -1;
}
//z == o.z && maxY == o.maxY && minY == o.minY && minX == o.minX && maxX == o.maxX
return 0;
}
I get the error "Comparison method violates its general contract!" from the array.sort call in the LibGDX Array (which I use for sorting). I can't tell how I am supposed to solve this when looking at other peoples issue with this error, but those problems are mostly trivial. Anyone know how I should solve this in my isometric comparison?
My isometric world (for reference):
Edit:
Found something interesting when only sorting by Z:
//Doesn't work
public int compareTo(IsoSprite o) {
if(maxZ > o.z){
return 1;
}
if (maxZ < o.z){
return -1;
}
return 0;
}
//Works
#Override
public int compareTo(IsoSprite o) {
if(z > o.z){
return 1;
}
if(z < o.z){
return -1;
}
return 0;
}
I realised I won't be able to do the comparisons needed in a comparable. So instead I'm using my own implementation of Quicksort to sort using my own compareTo method that basically checks if a sprite is behind or infront of another one.
Thanks for all the help anyway!
This message indicates that there is something wrong
with transitive logic in comparator, for if A > B then also B < A must be true. The compiler is smart enough to point it out to the user.
The problem in code is that a different values are compared to each other. To correct it you have to compare the same values minY < o.minY, and not to use <= and >= operators.
This should work:
public int compareTo(IsoSprite o) {
if (isoDepth > o.isoDepth) return 1;
if (isoDepth < o.isoDepth) return -1;
return 0;
}
See algorithm to calculate isoDepth that could be used to sort /compare IsoSprites.

Java LWJGL - Monster not following me, runs between a bounds of 2

I have written a script to be set off whenever a player is within a distance of the monster. The script checks if the x position is greater than or less than the players x, and same for the z. (y is automatically set to terrain)
public int checkWalkX(Vector3f position) {
if (Math.floor(this.getX()) != Math.floor(position.x)) {
if(this.getX() > position.x) return 1; //Greater
if(this.getX() < position.x) return 2; //Less
}
return 0;
}
public int checkWalkZ(Vector3f position) {
if (Math.floor(this.getZ()) != Math.floor(position.z)) {
if(this.getZ() > position.z) return 1; //Greater
if(this.getZ() < position.z) return 2; //Less
}
return 0;
}
public void follow(Player player) {
walking = false;
following = true;
if(checkWalkX(player.getPosition()) == 1) this.setX(this.getX() - mobSpeed);
else if(checkWalkX(player.getPosition()) == 2) this.setX(this.getX() + mobSpeed);
if(checkWalkZ(player.getPosition()) == 1) this.setZ(this.getZ() - mobSpeed);
else if(checkWalkZ(player.getPosition()) == 2) this.setZ(this.getZ() + mobSpeed);
if(Math.floor(checkWalkX(walkToPosition)) == 0 && Math.floor(checkWalkZ(walkToPosition)) == 0) following = false;
}
For some reason when I run this script, the monster will only move within a distance of 2ish. He moves the right ways kinda, but he doesnt follow me. Would anyone know why this is?
You should leave out the Math.floor() condition.What you could get right now is that the player for example is at x=1 and the monster at x=1.99. The Floor condition would shorten this to 1 == 1 and declare that both are at the same place. In the worst case this would leave the monster at a distance of sqrt(2).
You can leave out the condition to get:
public int checkWalkX(Vector3f position) {
if(this.getX() > position.x) return 1; //Greater
if(this.getX() < position.x) return 2; //Less
return 0; // equal
}
The drawback of this is that you almost never get a zero back. The monster will always move. And here we come to the next problem. It always moves a fixed distance. This can lead to jumping around the target if the mobspeed is greater than the distance.
An approach to solve this could be to calculate the distance of the monster to the player (dist = sqrt(deltaX^2 + deltaZ^2)) and it this is greater than the desired distance move the monster along the monster-player-vector for a distance of mobspeed or monster-player-distance whichever is shorter.

Trying to draw axes for Quadrant I, getting Quadrant IV instead

Currently, I am trying to create a program that draws a square anywhere in a 15x15 Quadrant I (Coordinate plane) grid. I am stuck on trying to get the axes displaying correctly.
This is the code I have thus far:
import java.util.Scanner;
public class Question2square {
public static void main(String[] args) {
// Axis variables
int yAxismin = 0;
int yAxismax = 15;
int xAxismin = 0;
int xAxismax = 15;
//Loop through all coordinates on plane using for loops
for(int y = yAxismin; y <= yAxismax; y++)
{
for(int x = xAxismin; x <= xAxismax; x++)
{
//Draw the axis
if (!Axis(x,y).equals("")) {
System.out.print(Axis (x,y));
}
}
System.out.println("");
}
}
// This method draws the 15x15 axis
public static String Axis(int x, int y)
{
// Each if and else if statement dictates what symbol needs to go where for the axes
// If there is nothing to be drawn, there will simply be a blank space
if (x == 15 && y== 0) return ">";
else if(x == 0 && y == 15) return "^";
else if (x == 0 && y == 0 )return ".";
else if(x == 0 && y >= 0) return "|";
else if(x >= 0 && y==0) return "-";
else return "";
}
/*
// Method to be used to draw actual square
public static ... drawSquare(...) {
}
*/
}
Unfortunately, instead of drawing the 'L' shaped axes I desire, it displays an 'r' shape instead. I'm trying to figure out how to display the axis properly.
I tried flipping the for loops but that didn't help. I don't see what else could be inhibiting this.
Try reversing the y loop:
for(int y = yAxismax; y >= yAxismin; y--) ...
As your loop prints lines to the console from "top to bottom" and then "left to right", you want your greatest value of y to come first and your least value of x to come first. Therefore you only need to reverse the y loop to go from yAxismax to yAxismin. The result (for limits of 3 and not 15 is then:
^
|
|
.-->

Given four coordinates check whether it forms a square

So I am trying to write a simple method which takes in set of four coordinates and decide whether they form a square or not.My approach is start with a point and calculate the distance between the other three points and the base point.From this we can get the two sides which have same value and the one which is a diagonal.Then I use Pythagoras theorem to find if the sides square is equal to the diagonal.If it is the isSquare method return true else false.The thing I want to find out is there some cases I might be missing out on or if something is wrong with the approach.Thanks for the all the help.
public class CoordinatesSquare {
public static boolean isSquare(List<Point> listPoints) {
if (listPoints != null && listPoints.size() == 4) {
int distance1 = distance(listPoints.get(0), listPoints.get(1));
int distance2 = distance(listPoints.get(0), listPoints.get(2));
int distance3 = distance(listPoints.get(0), listPoints.get(3));
if (distance1 == distance2) {
// checking if the sides are equal to the diagonal
if (distance3 == distance1 + distance2) {
return true;
}
} else if (distance1 == distance3) {
// checking if the sides are equal to the diagonal
if (distance2 == distance1 + distance3) {
return true;
}
}
}
return false;
}
private static int distance(Point point, Point point2) {
//(x2-x1)^2+(y2-y1)^2
return (int) (Math.pow(point2.x - point.x, 2) + (Math.pow(point2.y
- point.y, 2)));
}
public static void main(String args[]) {
List<Point> pointz = new ArrayList<Point>();
pointz.add(new Point(2, 2));
pointz.add(new Point(2, 4));
pointz.add(new Point(4, 2));
pointz.add(new Point(4, 4));
System.out.println(CoordinatesSquare.isSquare(pointz));
}
}
//Point Class
public class Point {
Integer x;
Integer y;
boolean isVisited;
public Point(Integer x, Integer y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj.getClass().equals(this.getClass())){
return ((Point) obj).x.equals(this.x)&&((Point) obj).y.equals(this.y);
}
return false;
}
}
You know, you can do the same check much easier. You just have to check two things:
"four points make a parallelogram" and "one of its angles is right".
First is true when P3 = P1 + (P2-P1) + (P4-P1)
And the second when (P2-P1)*(P4-P1) = 0
Where A*B is a dot product (A.x * B.x + A.y * B.y)
The only catch here is computational error. You can't expect floats to be exactly equal, so instead of A=B you should consider using something like abs(A-B) < E where E is small enough for your case.
Here's a corner case:
What if dist1 is the diagonal distance of the square? (I'm assuming the 4 points are in arbitrary order.)
You probably need to do another check for the distances:
if(dist1 == dist2){
//do stuff
}
else if(dist1 == dist3){
//do stuff
}
else if(dist2 == dist3){
//do stuff
}
else return false;
Your function doesn't take everything into account. You're only checking one point against the others. jwpat7 mentions this, so here's an example:
Assume the points are in this order: (red, yellow, green, blue), and each block on the grid is one.
Your distance1 and distance2 will both be equal to 4, so you're essentially saying that the last point can be any point where distance3 = 8. This is the blue line. If the last point is anywhere on that line, you just approved it as square.
You can fix this easily by doing the same check , but using the next coordinate as the 'base', instead of 0. If your check passes for two points, it's definitely a square.
Alternative:
You can check if it's not a square. In a valid square, there are only two valid distances, side length(s), and diagonal length(d).
Since you're using squared distance, d = s * 2
If any distance(there are only six) does not equal either d or s, it cannot be a square. If all six do, it must be a square.
The advantage is that if you check to prove it is a square, you have to do all six distance checks. If you want to prove it's not a square, you can just stop after you find a bad one.
So, it depends on your data. If you're expecting more squares than non-squares, you might want to check for squareness. If you expect more non-squares, you should check for non-squareness. That way you get a better average case, even though the worst case is slower.
public static boolean isSquare(List<Point> points){
if(points == null || points.size() != 4)
return false;
int dist1 = sqDistance(points.get(0), points.get(1));
int dist2 = sqDistance(points.get(0), points.get(2));
if(dist1 == dist2){ //if neither are the diagonal
dist2 = sqDistance(points.get(0), points.get(3));
}
int s = Math.min(dist1, dist2);
int d = s * 2;
for(int i=0;i<points.size;i++){
for(int j=i+1;j<points.size();j++){
int dist = sqDistance(points.get(i), points.get(j));
if(dist != s && dist != d))
return false;
}
}
return true;
}
If you add an else if(dist2 == dist3){...} alternative (as suggested in another answer also) then it is true that your isSquare method will recognize a square when the four points form a square. However, your code will also report some non-squares as being squares. For example, consider the set of points {(0,0), (1,1), (0,-1), (-1,0)}. Then your distance1,2,3 values are 2, 1, 1, respectively, which will satisfy the tests in the dist2 == dist3 case.
Any non-degenerate quadrilateral has a total of six inter-corner distances. Knowing five of those distances constrains the remaining distance to either of two values; that is, it doesn't uniquely constrain it. So I imagine that a square-testing method based on inter-corner distances will have to compute and test all six of them.
You are not using the Pythagorean Theorem correctly. The Pythagorean Theorem states that the sum of the squares of two legs is the square of the diagonal, and you are interpreting it to mean that the sum of the two legs is equal to the diagonal. You should use this for the Pythagorean Theorem testing:
if (distance3 == Math.sqrt(distance1*distance1 + distance2*distance2)) {
return true;
}
Does this make sense?
<script>
function isSquare(p1,p2,p3,p4){
if ((areACorner(p1,p2,p3) && areACorner(p4,p2,p3))
|| (areACorner(p1,p2,p4) && areACorner(p3,p2,p4))
|| (areACorner(p1,p3,p4) && areACorner(p2,p3,p4))) return true
return false
}
function areACorner(p1,p2,p3){
//pivot point is p1
return Math.abs(p2.y - p1.y) == Math.abs(p3.x - p1.x)
&& Math.abs(p2.x - p1.x) == Math.abs(p3.y - p1.y)
}
</script>
Output:
console.log(isSquare({x:0,y:0},{x:1,y:1},{x:0,y:1},{x:1,y:0}))
true
console.log(isSquare({x:0,y:0},{x:1,y:1},{x:-1,y:-1},{x:1,y:0}))
false
If you use something like (my C code), where I use squared distance (to avoid sqrt):
int sqDist(Point p1, Point p2) {
int x = p1.x - p2.x;
int y = p1.y - p2.y;
return(x*x + y*y);
}
where Point is simply:
typedef struct {
int x, y;
} Point;
`
In your code, calculate the permutations of each corner to one another, find the smallest / largest edges (in squared values), then you can check that you have 4 sides and 2 diagonals:
int squares[6];
squares[0] = sqDist(p[0], p[1]);
squares[1] = sqDist(p[0], p[2]);
squares[2] = sqDist(p[0], p[3]);
squares[3] = sqDist(p[1], p[2]);
squares[4] = sqDist(p[1], p[3]);
squares[5] = sqDist(p[2], p[3]);
int side = squares[0];
int diagonal = squares[0];
int i = 0;
while((++i <= 4) && (side >= diagonal)) {
if(squares[i] < side) side = squares[i];
if(squares[i] > diagonal) diagonal = squares[i];
}
int diagonal_cnt = 0;
int side_cnt = 0;
int error = 0;
for(int i = 0; i < 6; i++) {
if(abs(side - squares[i]) <= error) side_cnt++;
if(abs(diagonal - squares[i]) <= error) diagonal_cnt++;
}
printf("Square = %s\n", ((side_cnt == 4) && (diagonal_cnt == 2)) ? "true" : "false");
You could change the error value to handle floating points errors -- if you'd like to convert this routine to handle floating point values.
Note: If all points are at the same location, I consider this a point (not a square).

Categories

Resources