Related
From the man page for XFillPolygon:
If shape is Complex, the path may self-intersect. Note that contiguous coincident points in the path are not treated as self-intersection.
If shape is Convex, for every pair of points inside the polygon, the line segment connecting them does not intersect the path. If known by the client, specifying Convex can improve performance. If you specify Convex for a path that is not convex, the graphics results are undefined.
If shape is Nonconvex, the path does not self-intersect, but the shape is not wholly convex. If known by the client, specifying Nonconvex instead of Complex may improve performance. If you specify Nonconvex for a self-intersecting path, the graphics results are undefined.
I am having performance problems with fill XFillPolygon and, as the man page suggests, the first step I want to take is to specify the correct shape of the polygon. I am currently using Complex to be on the safe side.
Is there an efficient algorithm to determine if a polygon (defined by a series of coordinates) is convex, non-convex or complex?
You can make things a lot easier than the Gift-Wrapping Algorithm... that's a good answer when you have a set of points w/o any particular boundary and need to find the convex hull.
In contrast, consider the case where the polygon is not self-intersecting, and it consists of a set of points in a list where the consecutive points form the boundary. In this case it is much easier to figure out whether a polygon is convex or not (and you don't have to calculate any angles, either):
For each consecutive pair of edges of the polygon (each triplet of points), compute the z-component of the cross product of the vectors defined by the edges pointing towards the points in increasing order. Take the cross product of these vectors:
given p[k], p[k+1], p[k+2] each with coordinates x, y:
dx1 = x[k+1]-x[k]
dy1 = y[k+1]-y[k]
dx2 = x[k+2]-x[k+1]
dy2 = y[k+2]-y[k+1]
zcrossproduct = dx1*dy2 - dy1*dx2
The polygon is convex if the z-components of the cross products are either all positive or all negative. Otherwise the polygon is nonconvex.
If there are N points, make sure you calculate N cross products, e.g. be sure to use the triplets (p[N-2],p[N-1],p[0]) and (p[N-1],p[0],p[1]).
If the polygon is self-intersecting, then it fails the technical definition of convexity even if its directed angles are all in the same direction, in which case the above approach would not produce the correct result.
This question is now the first item in either Bing or Google when you search for "determine convex polygon." However, none of the answers are good enough.
The (now deleted) answer by #EugeneYokota works by checking whether an unordered set of points can be made into a convex polygon, but that's not what the OP asked for. He asked for a method to check whether a given polygon is convex or not. (A "polygon" in computer science is usually defined [as in the XFillPolygon documentation] as an ordered array of 2D points, with consecutive points joined with a side as well as the last point to the first.) Also, the gift wrapping algorithm in this case would have the time-complexity of O(n^2) for n points - which is much larger than actually needed to solve this problem, while the question asks for an efficient algorithm.
#JasonS's answer, along with the other answers that follow his idea, accepts star polygons such as a pentagram or the one in #zenna's comment, but star polygons are not considered to be convex. As
#plasmacel notes in a comment, this is a good approach to use if you have prior knowledge that the polygon is not self-intersecting, but it can fail if you do not have that knowledge.
#Sekhat's answer is correct but it also has the time-complexity of O(n^2) and thus is inefficient.
#LorenPechtel's added answer after her edit is the best one here but it is vague.
A correct algorithm with optimal complexity
The algorithm I present here has the time-complexity of O(n), correctly tests whether a polygon is convex or not, and passes all the tests I have thrown at it. The idea is to traverse the sides of the polygon, noting the direction of each side and the signed change of direction between consecutive sides. "Signed" here means left-ward is positive and right-ward is negative (or the reverse) and straight-ahead is zero. Those angles are normalized to be between minus-pi (exclusive) and pi (inclusive). Summing all these direction-change angles (a.k.a the deflection angles) together will result in plus-or-minus one turn (i.e. 360 degrees) for a convex polygon, while a star-like polygon (or a self-intersecting loop) will have a different sum ( n * 360 degrees, for n turns overall, for polygons where all the deflection angles are of the same sign). So we must check that the sum of the direction-change angles is plus-or-minus one turn. We also check that the direction-change angles are all positive or all negative and not reverses (pi radians), all points are actual 2D points, and that no consecutive vertices are identical. (That last point is debatable--you may want to allow repeated vertices but I prefer to prohibit them.) The combination of those checks catches all convex and non-convex polygons.
Here is code for Python 3 that implements the algorithm and includes some minor efficiencies. The code looks longer than it really is due to the the comment lines and the bookkeeping involved in avoiding repeated point accesses.
TWO_PI = 2 * pi
def is_convex_polygon(polygon):
"""Return True if the polynomial defined by the sequence of 2D
points is 'strictly convex': points are valid, side lengths non-
zero, interior angles are strictly between zero and a straight
angle, and the polygon does not intersect itself.
NOTES: 1. Algorithm: the signed changes of the direction angles
from one side to the next side must be all positive or
all negative, and their sum must equal plus-or-minus
one full turn (2 pi radians). Also check for too few,
invalid, or repeated points.
2. No check is explicitly done for zero internal angles
(180 degree direction-change angle) as this is covered
in other ways, including the `n < 3` check.
"""
try: # needed for any bad points or direction changes
# Check for too few points
if len(polygon) < 3:
return False
# Get starting information
old_x, old_y = polygon[-2]
new_x, new_y = polygon[-1]
new_direction = atan2(new_y - old_y, new_x - old_x)
angle_sum = 0.0
# Check each point (the side ending there, its angle) and accum. angles
for ndx, newpoint in enumerate(polygon):
# Update point coordinates and side directions, check side length
old_x, old_y, old_direction = new_x, new_y, new_direction
new_x, new_y = newpoint
new_direction = atan2(new_y - old_y, new_x - old_x)
if old_x == new_x and old_y == new_y:
return False # repeated consecutive points
# Calculate & check the normalized direction-change angle
angle = new_direction - old_direction
if angle <= -pi:
angle += TWO_PI # make it in half-open interval (-Pi, Pi]
elif angle > pi:
angle -= TWO_PI
if ndx == 0: # if first time through loop, initialize orientation
if angle == 0.0:
return False
orientation = 1.0 if angle > 0.0 else -1.0
else: # if other time through loop, check orientation is stable
if orientation * angle <= 0.0: # not both pos. or both neg.
return False
# Accumulate the direction-change angle
angle_sum += angle
# Check that the total number of full turns is plus-or-minus 1
return abs(round(angle_sum / TWO_PI)) == 1
except (ArithmeticError, TypeError, ValueError):
return False # any exception means not a proper convex polygon
The following Java function/method is an implementation of the algorithm described in this answer.
public boolean isConvex()
{
if (_vertices.size() < 4)
return true;
boolean sign = false;
int n = _vertices.size();
for(int i = 0; i < n; i++)
{
double dx1 = _vertices.get((i + 2) % n).X - _vertices.get((i + 1) % n).X;
double dy1 = _vertices.get((i + 2) % n).Y - _vertices.get((i + 1) % n).Y;
double dx2 = _vertices.get(i).X - _vertices.get((i + 1) % n).X;
double dy2 = _vertices.get(i).Y - _vertices.get((i + 1) % n).Y;
double zcrossproduct = dx1 * dy2 - dy1 * dx2;
if (i == 0)
sign = zcrossproduct > 0;
else if (sign != (zcrossproduct > 0))
return false;
}
return true;
}
The algorithm is guaranteed to work as long as the vertices are ordered (either clockwise or counter-clockwise), and you don't have self-intersecting edges (i.e. it only works for simple polygons).
Here's a test to check if a polygon is convex.
Consider each set of three points along the polygon--a vertex, the vertex before, the vertex after. If every angle is 180 degrees or less you have a convex polygon. When you figure out each angle, also keep a running total of (180 - angle). For a convex polygon, this will total 360.
This test runs in O(n) time.
Note, also, that in most cases this calculation is something you can do once and save — most of the time you have a set of polygons to work with that don't go changing all the time.
To test if a polygon is convex, every point of the polygon should be level with or behind each line.
Here's an example picture:
The answer by #RoryDaulton
seems the best to me, but what if one of the angles is exactly 0?
Some may want such an edge case to return True, in which case, change "<=" to "<" in the line :
if orientation * angle < 0.0: # not both pos. or both neg.
Here are my test cases which highlight the issue :
# A square
assert is_convex_polygon( ((0,0), (1,0), (1,1), (0,1)) )
# This LOOKS like a square, but it has an extra point on one of the edges.
assert is_convex_polygon( ((0,0), (0.5,0), (1,0), (1,1), (0,1)) )
The 2nd assert fails in the original answer. Should it?
For my use case, I would prefer it didn't.
This method would work on simple polygons (no self intersecting edges) assuming that the vertices are ordered (either clockwise or counter)
For an array of vertices:
vertices = [(0,0),(1,0),(1,1),(0,1)]
The following python implementation checks whether the z component of all the cross products have the same sign
def zCrossProduct(a,b,c):
return (a[0]-b[0])*(b[1]-c[1])-(a[1]-b[1])*(b[0]-c[0])
def isConvex(vertices):
if len(vertices)<4:
return True
signs= [zCrossProduct(a,b,c)>0 for a,b,c in zip(vertices[2:],vertices[1:],vertices)]
return all(signs) or not any(signs)
I implemented both algorithms: the one posted by #UriGoren (with a small improvement - only integer math) and the one from #RoryDaulton, in Java. I had some problems because my polygon is closed, so both algorithms were considering the second as concave, when it was convex. So i changed it to prevent such situation. My methods also uses a base index (which can be or not 0).
These are my test vertices:
// concave
int []x = {0,100,200,200,100,0,0};
int []y = {50,0,50,200,50,200,50};
// convex
int []x = {0,100,200,100,0,0};
int []y = {50,0,50,200,200,50};
And now the algorithms:
private boolean isConvex1(int[] x, int[] y, int base, int n) // Rory Daulton
{
final double TWO_PI = 2 * Math.PI;
// points is 'strictly convex': points are valid, side lengths non-zero, interior angles are strictly between zero and a straight
// angle, and the polygon does not intersect itself.
// NOTES: 1. Algorithm: the signed changes of the direction angles from one side to the next side must be all positive or
// all negative, and their sum must equal plus-or-minus one full turn (2 pi radians). Also check for too few,
// invalid, or repeated points.
// 2. No check is explicitly done for zero internal angles(180 degree direction-change angle) as this is covered
// in other ways, including the `n < 3` check.
// needed for any bad points or direction changes
// Check for too few points
if (n <= 3) return true;
if (x[base] == x[n-1] && y[base] == y[n-1]) // if its a closed polygon, ignore last vertex
n--;
// Get starting information
int old_x = x[n-2], old_y = y[n-2];
int new_x = x[n-1], new_y = y[n-1];
double new_direction = Math.atan2(new_y - old_y, new_x - old_x), old_direction;
double angle_sum = 0.0, orientation=0;
// Check each point (the side ending there, its angle) and accum. angles for ndx, newpoint in enumerate(polygon):
for (int i = 0; i < n; i++)
{
// Update point coordinates and side directions, check side length
old_x = new_x; old_y = new_y; old_direction = new_direction;
int p = base++;
new_x = x[p]; new_y = y[p];
new_direction = Math.atan2(new_y - old_y, new_x - old_x);
if (old_x == new_x && old_y == new_y)
return false; // repeated consecutive points
// Calculate & check the normalized direction-change angle
double angle = new_direction - old_direction;
if (angle <= -Math.PI)
angle += TWO_PI; // make it in half-open interval (-Pi, Pi]
else if (angle > Math.PI)
angle -= TWO_PI;
if (i == 0) // if first time through loop, initialize orientation
{
if (angle == 0.0) return false;
orientation = angle > 0 ? 1 : -1;
}
else // if other time through loop, check orientation is stable
if (orientation * angle <= 0) // not both pos. or both neg.
return false;
// Accumulate the direction-change angle
angle_sum += angle;
// Check that the total number of full turns is plus-or-minus 1
}
return Math.abs(Math.round(angle_sum / TWO_PI)) == 1;
}
And now from Uri Goren
private boolean isConvex2(int[] x, int[] y, int base, int n)
{
if (n < 4)
return true;
boolean sign = false;
if (x[base] == x[n-1] && y[base] == y[n-1]) // if its a closed polygon, ignore last vertex
n--;
for(int p=0; p < n; p++)
{
int i = base++;
int i1 = i+1; if (i1 >= n) i1 = base + i1-n;
int i2 = i+2; if (i2 >= n) i2 = base + i2-n;
int dx1 = x[i1] - x[i];
int dy1 = y[i1] - y[i];
int dx2 = x[i2] - x[i1];
int dy2 = y[i2] - y[i1];
int crossproduct = dx1*dy2 - dy1*dx2;
if (i == base)
sign = crossproduct > 0;
else
if (sign != (crossproduct > 0))
return false;
}
return true;
}
For a non complex (intersecting) polygon to be convex, vector frames obtained from any two connected linearly independent lines a,b must be point-convex otherwise the polygon is concave.
For example the lines a,b are convex to the point p and concave to it below for each case i.e. above: p exists inside a,b and below: p exists outside a,b
Similarly for each polygon below, if each line pair making up a sharp edge is point-convex to the centroid c then the polygon is convex otherwise it’s concave.
blunt edges (wronged green) are to be ignored
N.B
This approach would require you compute the centroid of your polygon beforehand since it doesn’t employ angles but vector algebra/transformations
Adapted Uri's code into matlab. Hope this may help.
Be aware that Uri's algorithm only works for simple polygons! So, be sure to test if the polygon is simple first!
% M [ x1 x2 x3 ...
% y1 y2 y3 ...]
% test if a polygon is convex
function ret = isConvex(M)
N = size(M,2);
if (N<4)
ret = 1;
return;
end
x0 = M(1, 1:end);
x1 = [x0(2:end), x0(1)];
x2 = [x0(3:end), x0(1:2)];
y0 = M(2, 1:end);
y1 = [y0(2:end), y0(1)];
y2 = [y0(3:end), y0(1:2)];
dx1 = x2 - x1;
dy1 = y2 - y1;
dx2 = x0 - x1;
dy2 = y0 - y1;
zcrossproduct = dx1 .* dy2 - dy1 .* dx2;
% equality allows two consecutive edges to be parallel
t1 = sum(zcrossproduct >= 0);
t2 = sum(zcrossproduct <= 0);
ret = t1 == N || t2 == N;
end
I need to compute the following problem in code:
x0 = 2 and xi = (−1/2) * x(sub i - 1) * sqrt(x(sub i - 1))
find the result of (1/e^(x1 + x2 + x3 + ...)).
(Or as marked up text)
Write a function of an appropriate type that calculates and returns the result of:
e(x1-1 - x2-1 + x3-1 - x4-1 + ...), for n elements, defined as: x0 = 2 and xi = -½√|xi-1|
It has to be done in C but I am just trying to figure out the logistics of it.
What I have thought till now : x0 has to be a variable initialized with 2 along with x1. x2, x3... will be calculated in a recursive function n-1 times. I am not sure how the results should be stored, also a variable or maybe an array? Would an array be appropriate??
Thank you.
Would it not be simpler to do it iteratively like this? I'm not actually sure if this generates the correct answer but this seems to be what your formula would imply.
long double
compute(unsigned n)
{
long double x = 2.0L;
for (unsigned i = 0; i < n; ++i)
x = (-(1.0L/2.0L) * x) * sqrtl(fabsl(x));
return x;
}
I need to rotate a triangle (called ship) around itself.
Here is what I got so far, but it doesn't work. It keeps getting smaller and smaller until it disappears.
void RotatePoint(Point *P, float angle)
{
float theta = angle * (180/3.1415);
P->x = (P->x * cos(theta)) - (P->y * sin(theta));
P->y = (P->y * cos(theta)) + (P->x * sin(theta));
}
void RotateShip(Ship *ship)
{
Rotate(&ship->A, rotateAngle);
Rotate(&ship->B, rotateAngle);
Rotate(&ship->C, rotateAngle);
}
Point P is the Point I want to rotate, and Point C is the center of the triangle. I thought that if I rotate all three vertixes, the triangle will rotate.
In my case, I initialize this way:
void initShip(Ship *ship)
{
ship->center.x = (SCREEN_W)/2.0;
ship->center.y = (SCREEN_H)/2.0;
ship->A.x = 0;
ship->A.y = -5;
ship->B.x = 15;
ship->B.y = 25;
ship->C.x = -15;
ship->C.y = 25;
ship->color = al_map_rgb(255, 255, 255);
}
Ship A, B and C are the distance from the center of the triangle. I draw it adding the A, B and C to the center vertix.
A=-0.699857,-19.963261
A=-0.000857,-19.951065
A=-0.699001,-19.914387
A=-0.001712,-19.902250
A=-0.698147,-19.865631
A=-0.002565,-19.853554
Im pressing one key back and one key forth, making it rotate clockwise and anticlockwise. notice how A is shrinking.
I don't know what I do. I should be going back to 20.00 when it reaches the top. This way my triangle is shrinking.
I'm using cos(0.035) and sin(0.035), meaning 2 degrees.
The OP has a classic bug: using a temporary (or intermediate) value where the original/initial value should be used instead.
As a simplified example, consider a case where you have three variables, a, b, and c, and want to rotate their values one variable to the left:
a = b;
b = c;
c = a; /* Oops! Won't work! */
The last assignment is a problem, because a is no longer the original value! You cannot order the assignments in a way that would avoid this problem; the only thing that changes is which variable will suffer from the problem. To fix the problem, you need to use a new temporary variable to hold the original value:
t = a;
a = b;
b = c;
c = t;
In OP's case, the ship structure should not mix the current shape of the ship, and the true/unrotated shape of the ship, in the same variables. Even if you avoid the abovementioned problem, you'll still suffer from accumulated rounding errors; it might take hours of gameplay, but eventually your ship would end up looking different.
The solution is to describe the ship shape in separate variables, or using constants in the ship update function.)
Let's say we have a variable dir that specifies the direction in radians, rotated counterclockwise from up, 0 being up (towards negative y axis), π/2 (and -3π/2) left (towards negative x axis), π (and -π) down, 3π/2 (and -π/2) right, and so on. If deg is in degrees, dir = deg * 3.14159265358979323846 / 180.0. We can also use the atan2() function to find out dir: dir = atan2(-x, y).
When dir = 0, OP wants A = { 0, -5 }, B = { 15, 25 }, and C = { -15, 25 }. If we define Adir = 3.14159, Ar = 5, Bdir = -0.54042, Br = sqrt(15*15+25*25) = 29.15476, Cdir = 0.54042, and Cr = 29.15476, then the ship vertices are
A.x = center.x + Ar*sin(dir + Adir);
A.y = center.y + Ar*cos(dir + Adir);
B.x = center.x + Br*sin(dir + Bdir);
B.y = center.y + Br*cos(dir + Bdir);
C.x = center.x + Cr*sin(dir + Cdir);
C.y = center.y + Cr*cos(dir + Cdir);
If the OP wants to fix the ship shape in the rotateShip function, then
void rotateShip(Ship *s, double rotateAngle)
{
s->A.x = s->center.x + 5.00000 * sin(rotateAngle + 3.14159);
s->A.y = s->center.y + 5.00000 * cos(rotateAngle + 3.14159);
s->B.x = s->center.x + 29.15476 * sin(rotateAngle - 0.54042);
s->B.y = s->center.y + 29.15476 * cos(rotateAngle - 0.54042);
s->C.x = s->center.x + 29.15476 * sin(rotateAngle + 0.54042);
s->C.y = s->center.y + 29.15476 * cos(rotateAngle + 0.54042);
}
Personally, I'd define the ship shape using a variable number of vertices:
typedef struct {
double x;
double y;
} vec2d;
typedef struct {
vec2d center;
size_t vertices;
const vec2d *shape; /* Un-rotated ship vertices */
double direction; /* Ship direction, in radians */
vec2d *vertex; /* Rotated ship vertices */
} Ship;
const vec2d default_shape[] = {
{ 0.0, -5.0 },
{ -15.0, 25.0 },
{ 15.0, 25.0 },
};
void updateShip(Ship *ship)
{
const double c = cos(ship->direction);
const double s = sin(ship->direction);
size_t i;
for (i = 0; i < ship->vertices; i++) {
ship->vertex[i].x = ship->center.x + c*ship->shape[i].x - s*ship->shape[i].y;
ship->vertex[i].y = ship->center.y + s*ship->shape[i].x + c*ship->shape[i].y;
}
}
void initShip(Ship *ship, const size_t vertices, const vec2d *shape)
{
ship->center.x = 0.5 * SCREEN_W;
ship->center.y = 0.5 * SCREEN_H;
if (vertices > 2 && shape != NULL) {
ship->vertices = vertices;
ship->shape = shape;
} else {
ship->vertices = (sizeof default_shape) / (sizeof default_shape[0]);
ship->shape = default_shape;
}
ship->direction = 0;
ship->vertex = malloc(ship->vertices * sizeof ship->vertex[0]);
if (!ship->vertex) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
updateShip(ship);
}
In updateShip, we use 2D rotation by ship->direction, to rotate the ship model speficied by the vertices in shape[], saving the rotated and translated coordinates to vertex[].
x_current = x_center + x_original * cos(direction) - y_original * sin(direction);
y_current = y_center + x_original * sin(direction) + y_original * cos(direction);
as defined in e.g. the Wikipedia article on rotation. Note that the original coordinates, x_original and y_original (or the values in the shape[] array in the Ship structure) are never modified.
This way you can let the player "upgrade" their ship by just changing the shape to point to a new ship shape, and vertices to reflect that number.
I can reproduce fast shrinking (while also rotating) with coordinates in int.
(It would have been so much easier based on an MCVE....).
With coordinates in float, it shrinks much slower, but it still shrinks.
I relate that to the fact that your implementation collects all math errors (which computers always make) in a very visible way.
In order to avoid shrinking altogether:
Do not manipulate the relative coordinates in order to rotate. Instead store relative coordinates as constants, together with the ships orientation as an angle in double.
Then rotate by increasing/reducing the angle (wrapping around, to stay within -Pi ... +Pi).
Then draw by always applying the changing angle to the constant relative coordinates.
(I can only show you in detail, if you provide a MCVE.)
This way, the collected errors will only result in a slight and slowly growing misorientation,
which most likely will not be noticed by the pilot - and then be corrected by the pilot.
"Hmm, the ship has not yet completed the 360 I wanted. Oh well, I will turn a little more."
On a side note, I do not trust the way you use angles as parameters to cos() and sin().
Or to put it differently, I think
theta = angle * (180/3.1415); -> theta = angle; for a U-turn via Pi.
theta = angle * (180/3.1415); -> theta = angle * (3.1415/180); for a U-turn via 180.
For your implementation you get a U-turn for the angle (Pi*3.1415/180), which I cannot see a reason for.
I also recommend to use appropriate constants from math.h (e.g. M_PI), instead of your own constant with 4 decimal places.
I am writing a fractal explorer at the moment and I got stuck at zooming on a certain point in the set. My drawing function for the Julia set for example looks like this:
void *julia_thread(void *param)
{
int x, y, temp;
long double re, aux, im;
int start = ((int *)param)[0];
int end = ((int *)param)[1];
int iterations;
for (x = start; x < end; x++)
for (y = 0; y < WIN_SIZE; y++)
{
re = range_change(zoom_factor, x, mv_x);
im = range_change(zoom_factor, y, mv_y);
iterations = 0;
while (!blowing_up(re, im) && iterations < max_iter)
{
aux = re;
re = re * re - im * im + re_c;
im = 2 * aux * im + im_c;
iterations++;
}
put_pixel(img, x, y, color_table[iterations]);
}
return NULL;
}
The function that calculates the initial values for the real and imaginary part of Z is this:
long double range_change(long double zoom_factor, int value, long double mv)
{
long double newmax = 2 / zoom_factor;
long double newmin = -2 / zoom_factor;
return ((long double)value * (newmax - newmin)) / WIN_SIZE + newmin + mv;
}
So I get a scaled down value that is part of the interval where the fractal exists and according to the number of iterations I assign a colour to that certain pixel. Zooming works fine by making the real interval (-2, 2) smaller by dividing both of the ends with a factor. This works but I can't seem to be figure out how to zoom on a certain spot other than the centre. I can move around and reach that spot eventually by adding to the real and imaginary part (x, y) a number but I can't zoom on a point determined by the screen (x, y) given to me by the cursor position.
It is a very bad idea to do indiscriminate integer divisions:
2 / zoom_factor
will return 0 if zoom_factor is larger than 2. Replace 2 by 2.0 to force floating point division, this should be sufficient to repair the code.
If I interpret this correctly, you want that the screen window represents a square in the coordinate or fractal plane with the width and height 4.0/zoom_factor around the point (mv_x, mv_y).
mv is situated at WIN_SIZE/2, so that
coord = mv + ( 4*value/WIN_SIZE - 2 )/zoom_factor
which can be implemented exactly as this
return mv + ( (4.0*value)/WIN_SIZE - 2.0 )/zoom_factor;
and with the factor 4.0 the denominator gets type double and division is carried out in double.
Slow derivation
What the function range_change wants to achieve is a linear change of coordinates
coord = A*screen + B
where screen is the input screen coordinate and coord is the coordinate in the Cartesian plane playing host for the Julia fractal. The endpoint mapping is
screen=0 --> coord = center - 2.0/zoom
screen=WIN_SIZE --> coord = center + 2.0/zoom
From the first we read B=center - 2.0/zoom and from the second formula
A*WIN_SIZE + center - 2.0/zoom = center + 2.0/zoom
A*WIN_SIZE = 4.0/zoom
A = 4.0/(zoom*WIN_SIZE)
which gives the transformation formula
coord = (4.0*value)/(zoom*WIN_SIZE) + center - 2.0/zoom
= ( (4.0*value)/WIN_SIZE - 2.0 )/zoom + center
Given n points in a plane , how many squares can be formed ...??
I tried this by calculating the distances between each 2 points , then sort them , and look for the squares in the points with four or more equal distances after verifying the points and slopes.
But this looks like an approach with very high complexity . Any other ideas ...??
I thought dynamic programming for checking for line segments of equal distances might work ... but could not get the idea quite right ....
Any better ideas???
P.S : The squares can be in any manner . They can overlap , have a common side, one square inside another ...
If possible please give a sample code to perform the above...
Let d[i][j] = distances between points i and j. We are interested in a function count(i, j) that returns, as fast as possible, the number of squares that we can draw by using points i and j.
Basically, count(i, j) will have to find two points x and y such that d[i][j] = d[x][y] and check if these 4 points really define a square.
You can use a hash table to solve the problem in O(n^2) on average. Let H[x] = list of all points (p, q) that have d[p][q] = x.
Now, for each pair of points (i, j), count(i, j) will have to iterate H[ d[i][j] ] and count the points in that list that form a square with points i and j.
This should run very fast in practice, and I don't think it can ever get worse than O(n^3) (I'm not even sure it can ever get that bad).
This problem can be solved in O(n^1.5) time with O(n) space.
The basic idea is to group the points by X or Y coordinate, being careful to avoid making groups that are too large. The details are in the paper Finding squares and rectangles in sets of points. The paper also covers lots of other cases (allowing rotated squares, allowing rectangles, and working in higher dimensions).
I've paraphrased their 2d axis-aligned square finding algorithm below. Note that I changed their tree set to a hash set, which is why the time bound I gave is not O(n^1.5 log(n)):
Make a hash set of all the points. Something you can use to quickly check if a point is present.
Group the points by their X coordinate. Break any groups with more than sqrt(n) points apart, and re-group those now-free points by their Y coordinate. This guarantees the groups have at most sqrt(n) points and guarantees that for each square there's a group that has two of the square's corner points.
For every group g, for every pair of points p,q in g, check whether the other two points of the two possible squares containing p and q are present. Keep track of how many you find. Watch out for duplicates (are the two opposite points also in a group?).
Why does it work? Well, the only tricky thing is the regrouping. If either the left or right columns of a square are in groups that are not too large, the square will get found when that column group gets iterated. Otherwise both its top-left and top-right corners get regrouped, placed into the same row group, and the square will be found when that row group gets iterated.
I have a O(N^2) time, O(N) space solution:
Assume given points is an array of object Point, each Point has x,y.
First iterate through the array and add each item into an HashSet: This action de-duplicate and give us an O(1) access time. The whole process takes O(N) time
Using Math, Say vertices A, B, C, D can form a square, AC is known and it's a diagonal line, then the corresponding B, D is unique. We could write a function to calculate that. This process is O(1) time
Now Let's get back to our thing. write a for-i-loop and a for-j-inner-loop. Say input[i] and input[j] form a diagonal line, find its anti-diagonal line in the set or not: If exist, counter ++; This process take O(N^2) time.
My code in C#:
public int SquareCount(Point[] input)
{
int count = 0;
HashSet<Point> set = new HashSet<Point>();
foreach (var point in input)
set.Add(point);
for (int i = 0; i < input.Length; i++)
{
for (int j = 0; j < input.Length; j++)
{
if (i == j)
continue;
//For each Point i, Point j, check if b&d exist in set.
Point[] DiagVertex = GetRestPints(input[i], input[j]);
if (set.Contains(DiagVertex[0]) && set.Contains(DiagVertex[1]))
{
count++;
}
}
}
return count;
}
public Point[] GetRestPints(Point a, Point c)
{
Point[] res = new Point[2];
int midX = (a.x + c.y) / 2;
int midY = (a.y + c.y) / 2;
int Ax = a.x - midX;
int Ay = a.y - midY;
int bX = midX - Ay;
int bY = midY + Ax;
Point b = new Point(bX,bY);
int cX = (c.x - midX);
int cY = (c.y - midY);
int dX = midX - cY;
int dY = midY + cX;
Point d = new Point(dX,dY);
res[0] = b;
res[1] = d;
return res;
}
It looks like O(n^3) to me. A simple algo might be something like:
for each pair of points
for each of 3 possible squares which might be formed from these two points
test remaining points to see if they coincide with the other two vertices
Runtime: O(nlog(n)^2), Space: θ(n), where n is the number of points.
For each point p
Add it to the existing arrays sorted in the x and y-axis respectively.
For every pair of points that collide with p in the x and y-axis respectively
If there exists another point on the opposite side of p, increment square count by one.
The intuition is counting how many squares a new point creates. All squares are created on the creation of its fourth point. A new point creates a new square if it has any colliding points on concerned axes and there exists the "fourth" point on the opposite side that completes the square. This exhausts all the possible distinct squares.
The insertion into the arrays can be done binary, and checking for the opposite point can be done by accessing a hashtable hashing the points' coordinates.
This algorithm is optimal for sparse points since there will be very little collision points to check. It is pessimal for dense-squares points for the opposite of the reason for that of optimal.
This algorithm can be further optimized by tracking if points in the axis array have a collision in the complementary axis.
Just a thought: if a vertex A is one corner of a square, then there must be vertices B, C, D at the other corners with AB = AD and AC = sqrt(2)AB and AC must bisect BD. Assuming every vertex has unique coordinates, I think you can solve this in O(n^2) with a hash table keying on (distance, angle).
This is just an example implementation in Java - any comments welcome.
import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
public class SweepingLine {
public static void main(String[] args) {
Point[] points = {
new Point(1,1),
new Point(1,4),
new Point(4,1),
new Point(4,4),
new Point(7,1),
new Point(7,4)
};
int max = Arrays.stream(points).mapToInt(p -> p.x).max().orElseThrow(NoSuchElementException::new);
int count = countSquares(points, max);
System.out.println(String.format("Found %d squares in %d x %d plane", count, max, max));
}
private static int countSquares(Point[] points, int max) {
int count = 0;
Map<Integer, List<Integer>> map = new HashMap<>();
for (int x=0; x<max; x++) {
for (int y=0; y<max; y++) {
for(Point p: points) {
if (p.x == x && p.y == y) {
List<Integer> ys = map.computeIfAbsent(x, _u -> new ArrayList<Integer>());
ys.add(y);
Integer ley = null;
for (Integer ey: ys) {
if (ley != null) {
int d = ey - ley;
for (Point p2: points) {
if (x + d == p2.x && p2.y == ey){
count++;
}
}
}
ley = ey;
}
}
}
}
}
return count;
}
private static class Point {
public final int x;
public final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
}
Here is a complete implemention of finding the diagonal points in C++!
Given points a and c, return b and d, which lie on the opposite diagonal
If b or d are not integer points, dicard them (optional)
To find all squares generated by n points, can check out this C++ implementation
Idea credited to Kevman. Hope it can help!
vector<vector<int>> createDiag(vector<int>& a, vector<int>& c){
double midX = (a[0] + c[0])/2.0;
double midY = (a[1] + c[1])/2.0;
double bx = midX - (a[1] - midY);
double by = midY + (a[0] - midX);
double dx = midX - (c[1] - midY);
double dy = midY + (c[0] - midX);
// discard the non-integer points
double intpart;
if(modf(bx, &intpart) != 0 or modf(by, &intpart) != 0 or modf(dx, &intpart) != 0 or modf(dy, &intpart) != 0){
return {{}};
}
return {{(int)bx, (int)by}, {(int)dx, (int)dy}};
}