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).
Related
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.
The input is an array of 2n integers which defines line segments by saying how the points located on a circle are paired
and connected. (Each point has its own pair that's connected to it.)
Array [2,3,0,1] reads:
Point 0 is connected to point 2 Point 1 is connected to point 3
Point 2 is connected to point 0 Point 3 is connected to
point 1
Meaning we have line segments (0,2) and (1,3).
The points are located on a circle, in the same order that they are
located in the array. (Their exact coordinates are irrelevant.)
Here is the picture of my array example. (1 intersection occured.)
The output is the number of intersections. (Number of points where 2 line segments touch.)
What would be the best (fastest) way to calculate this?
What I've tried:
public static int count(int[] world) {
int i = 0;
int intersections = 0;
int endpoint = 0;
// run trought all points in order, find their pairs and check if the line they make is intersected
while (i < world.length - 1) {
if (world[i] == i+1) { // if 2 neighbouring points are connected, there are no intersections with the line they make
i++;
} else if (world[i] > i) { // don't need to check previously checked pairs
endpoint = world[i];
// check if any intersections with the line L(i,world[i]):
// This goes through all points that are located before the endpoint of the line defined by point i and its pair world[i]
// And checks if their pair is located after the endpoint, which means that the line they make intersects the line L(i,world[i])
for (int j = i+1; j < endpoint; j++) {
if (world[j] > endpoint) {
intersections++;
}
}
}
i++;
}
return intersections;
}
Thanks to sabys answer, I also coded this:
public static int countIntersections(int[] world) {
int intersections = 0;
for (int i = 1; i < world.length; i++) {
for (int j = 0; j < i; j++) {
if (!C(i,world[i],i-j,world[i-j])) {
intersections++;
}
}
}
return intersections;
}
public static boolean C(int a, int b, int x, int y) {
return ((a <= x && b <= x) || (a >= x && b <= y) || (a >= x && b >= x));
}
Which gives the same results as my initial code meaning they both work! But my initial code is faster than this one.
I'll be accepting his answer since both codes work and optimization questions are better suited for Codereview.
Observation: Given two segments L1: [a, b] and L2: [x, y], they DONT intersect if and only if (a < x && b > y) || (x < a && y > b).
Call this Condition C(L1, l2).
Outline of Algorithm:
Iterate over the line segments one by one
Take first line segment L1
Take segment line segment L2 and calculate Condition C(L1, L2). If it is false add 1 to your intersection count.
Take segment L3 and calculate C(L3, L1) and C(L3, L2). Add the 1s accordingly.
For segment Ln, you have to calculate C(Ln, Ln-1), C(Ln, Ln-2)...
Time Complexity: O(n^2). n being the number of segments
Having some trouble building an equals method that compares two dimensional coordinate points in a list based on distance from point zero (0,0) -equation included.
public double distanceToOrigin() {
return distance(zero);
}
public double distance(Point that) {
return Math.sqrt(Math.pow((x - that.getX()), 2) + Math.pow((y - that.getY()), 2));
}
boolean equals(List<Point> lst){
boolean eq = true;
for (int i=0; i<lst.size(); i++)//accounts for first element-to-compare.
{
for (int q = 1; q < lst.size(); q++)//accounts for second element-to-compare.
{
if(lst.distanceToOrigin(i) == (lst).distanceToOrigin(q)))
{
eq = false;
}
}
}
return eq;
}
I may be over-interpreting the if statement: is there a more efficient way to compare both elements (in a single line of code)?
For reference:
static Point zero = new Point(0, 0);
public int getX(){
return x;
}
public int getY(){
return y;
}
Assistance heartily appreciated.
Examples of lists:
List<Point> lst = new ArrayList<Point>();
The corrected equals method would appear similar to the following (somewhat clumsy implementation currently):
boolean equals(List<Point> lst){
boolean eq = true;
for (int i=0; i<lst.size(); i++)//accounts for first element-to-compare.
{
for (int q = 1; q < lst.size(); q++)//accounts for second element-to-compare.
{
if(lst.get(i).distanceToOrigin() == lst.get(q).distanceToOrigin()){
eq = false;
}
}
}
return eq;
}
The equals method should return boolean true or false based on whether or not element-to-compare(1) is identical to element-to-compare(2).
If you are looking for equal distances of two points you are likely better off just comparing the sum of the squares of the coordinates. That avoids comparing floats and is more efficient:
class Point {
public boolean isSameDistanceFromOrigin(Point other) {
return x * x + y * y == other.x * other.x + other.y * other.y;
}
}
If I'm interpreting your loop correctly you want to return false if any two points in a list are the same distance from the origin. Here's an algorithm for doing that in one line (sort of) using Java 8:
public boolean areAllDifferentDistancesFromOrigin(List<Point> points) {
return points.stream().noneMatch(point ->
points.stream().filter(p -> p != point)
.anyMatch(p-> point.isSameDistanceFromOrigin(p)));
}
I am trying to implement Douglas-Peucker Algorithm with point count tolerance. I mean that i specifies that i want 50% compression. I found this algorithm on this page http://psimpl.sourceforge.net/douglas-peucker.html under Douglas-Peucker N. But i am not sure how this algorithm is working. Is there any implementation of this in java or some good specification about this version of algorithm?
What i dont understand from psimpl explanation is what will happend after we choose fist point into simplification? We will broke the edge into two new edges and rank all points and choose best point from both edges?
DP searches the polyline for the farthest vertex from the baseline. If this vertex is farther than the tolerance, the polyline is split there and the procedure applied recursively.
Unfortunately, there is no relation between this distance and the number of points to keep. Usually the "compression" is better than 50%, so you may try to continue the recursion deeper. But achieving a good balance of point density looks challenging.
Combine the Douglas-peucker algorithm with iteration, and consider the remained points as a judge criteria.
Here is my algorithm, array 'points' stores the points of the trajectory.Integer 'd' is the threshold.
public static Point[] divi(Point[] points,double d)
{
System.out.println("threshold"+d);
System.out.println("the nth divi iteration");
int i = 0;
Point[] p1 = new Point[points.length];
for (i = 0;i<points.length;i++)
p1[i] = points[i];
compress(p1, 0,p1.length - 1,d); //first compression
int size = 0;
for (Point p : p1) //trajectory after compression
if(p != null)
size ++;
System.out.println("size of points"+size);
if(size<=200 && size>=100)
return p1;
else if(size>200)
return divi(p1,d + d/2.0);
else
return divi(points,d/2.0);
}
public static void compress(Point[] points,int m, int n,double D)
{
System.out.println("threshold"+D);
System.out.println("startIndex"+m);
System.out.println("endIndex"+n);
while (points[m] == null)
m ++;
Point from = points[m];
while(points[n] == null)
n--;
Point to = points[n];
double A = (from.x() - to.x()) /(from.y() - to.y());
/** -
* 由起始点和终止点构成的直线方程一般式的系数
*/
double B = -1;
double C = from.x() - A *from.y();
double d = 0;
double dmax = 0;
if (n == m + 1)
return;
List<Double> distance = new ArrayList<Double>();
for (int i = m + 1; i < n; i++) {
if (points[i] ==null)
{
distance.add(0.0);
continue;
}
else
{
Point p = points[i];
d = Math.abs(A * (p.y()) + B * (p.x()) + C) / Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2));
distance.add(d);
}
}
dmax= Collections.max(distance);
if (dmax < D)
for(int i = n-1;i > m;i--)
points[i] = null;
else
{
int middle = distance.indexOf(dmax) + m + 1;
compress(points,m, middle,D);
compress(points,middle, n,D);
}
}
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.