I need to rotate a concave polygon to minimize its height. I've thinking about finding a line which is the minimal diameter of that polygon, then rotate it so that line is parallel with Y axis.
My question is how to find such a line? Or, is there any other algorithm to rotate a polygon to so that its height is minimized?
Thanks in advance.
ARRAY points := {P1, P2, ..., PN};
points.delete(middle vertices of any collinear sequence of three points);
REAL p_a := index of vertex with minimum y-coordinate;
REAL p_b := index of vertex with maximum y-coordinate;
REAL rotated_angle := 0;
REAL min_width := INFINITY;
VECTOR caliper_a(1,0); // Caliper A points along the positive x-axis
VECTOR caliper_b(-1,0); // Caliper B points along the negative x-axis
WHILE rotated_angle < PI
// Determine the angle between each caliper and the next adjacent edge in the polygon
VECTOR edge_a(points[p_a + 1].x - points[p_a].x, points[p_a + 1].y - points[p_a].y);
VECTOR edge_b(points[p_b + 1].x - points[p_b].x, points[p_b + 1].y - points[p_b].y);
REAL angle_a := angle(edge_a, caliper_a);
REAL angle_b := angle(edge_b, caliper_b);
REAL width := 0;
// Rotate the calipers by the smaller of these angles
caliper_a.rotate(min(angle_a, angle_b));
caliper_b.rotate(min(angle_a, angle_b));
IF angle_a < angle_b
p_a++; // This index should wrap around to the beginning of the array once it hits the end
width = caliper_a.distance(points[p_b]);
ELSE
p_b++; // This index should wrap around to the beginning of the array once it hits the end
width = caliper_b.distance(points[p_a]);
END IF
rotated_angle = rotated_angle + min(angle_a, angle_b);
IF (width < min_width)
min_width = width;
END IF
END WHILE
RETURN min_width;
See http://en.wikipedia.org/wiki/Rotating_calipers
Also check out: http://www.mathworks.com/matlabcentral/fileexchange/7844-geom2d/content/geom2d/polygons2d/minimumCaliperDiameter.m
Note: Both of these problems solve the convex case. To solve the concave case, you merely transform your inputs from conave to convex by doing the following:
1. Compute convex hull of your concave polygon.
2. Run algorithm above on convex polygon.
3. Once minimum height found, rotate.
4. Transform back to concave polygon.
The convex hull is very common in Python. You can use: http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.spatial.ConvexHull.html
If you aren't comfortable using this library, you can use this algorithm to convert your concave shape to convex: (this is not meant to be used, just really bad pseudocode for you to understand how convex hull is computed)
Traverse vertices of concave polygon in clockwise order
_prev = starting vertex of traversal
_middle = second vertex in traversal
FOR _next vertex in traversal:
IF _prev -> _middle -> _next make right turn (i.e. concave part)
Connect _prev and _next and set _middle = _next
ELSE
Shift _prev to _middle and _middle to _next.
In other words, you just remove concave parts :)
Related
Let's suppose we have a sphere of radius r at a distance d from the observer
We define the following
O: observer
C: Center of the sphere
P: arbitrary visible point of the sphere (fromthe observer)
OC: line connecting the observer to the center of the sphere ( fixed length: d)
OP: Line connecting the observer and an arbitrary visible point of the sphere (variable length depending on the angle: a)
CP: Line connecting the center of the sphere and this arbitrary visible point (fixed length: r)
theta: angle between OC and OP
shi: angle between OC and CP
In case P is one of the "external" visible points of the sphere, using basic geometry we have that
theta_max = atan( r/ sqrt(d^2-r^2) )
shi_max = PI/2 - theta_max
For any other point, I got the following equations
r.cos(shi) + a.cos(theta) = d
r.sin(shi) = a.sin(theta)
I think these equations are right, but I can see no way to write them as shi=f(theta), since 'a' also varies with it.
Is it possible? Or is any of these steps wrong?
EDIT
Working with the latest two equations, we can get
tan(theta)= r.sin(shi)/(d-r.cos(shi))
but I would need to get shi=f(theta) if possible
Let's call the angle between CP and OP λ. Solving for λ is rather simple:
sin(λ) = sin(theta)*d/r
Now you know two angles within that triangle and the remaining one can be calculated from the angle sum of a triangle:
shi = Pi - theta - asin( sin(theta)*d/r )
The goal is to get a point in 3D space projected on the cameras screen (the final goal is to produce a points cloud)
This is the setup:
Camera
postion: px,py,pz
up direction: ux,uy,uz
look_at_direction: lx,ly,lz
screen_width
screen_height
screen_dist
Point in space:
p = (x,y,z)
And
init U,V,W
w = || position - look_at_direction || = || (px,py,pz) - (ux,uy,uz) ||
u = || (ux,uy,uz) cross-product w ||
v = || w cross-product u ||
This gives me the u,v,w coords of the camera.
This are the steps I'm supposed to implement, and the way I understand them. I believe something was lost in the translation.
(1) Find the ray from the camera to the point
I subtract the point with the camera's position
ray_to_point = p - position
(2) Calculate the dot product between the ray to the point and the normalized ray
to the center of the screen (the camera direction). Divide the result by sc_dist.
This will give you a ratio between the distance of the point and the distance to the camera's screen.
ratio = (ray_to_point * w)/screen_dist
Here I'm not sure I should be using w or the original look at value of the camera or the w vector which is the unit vector in the camera's space.
(3) Divide the ray to the point by the ratio found in step 2, and add it to the camera position. This will give you the projection of the point on the camera screen.
point_on_camera_screen = ray_to_point / ratio
(4) Find a vector between the center of the camera's screen and the projected point.
Am I supposed to find the center pixel of the screen? How can I do that?
Thanks.
Consider the following figure:
The first step is to calculate the difference vector from camera to p(=>diff`). That's correct.
The next step is to express the difference vector in the camera's coordinate system. Since its axes are orthogonal, this can be done with the dot product. Be sure that the axes are unit vectors:
diffInCameraSpace = (dot(diff, u), dot(diff, v), dot(diff, w))
Now comes the scaling part so that the resulting difference vector has a z-component of screen_dist. So:
diffInCameraSpace *= screenDist / diffInCameraSpace.z
You don't want to transform it back to world space. You might need some further information on how camera units are mapped to pixels, but at this step you are basically done. You might want to shift the resulting diffInCameraSpace by (screenWidth / 2, screenHeight / 2, 0). Forget the z-component and you have the position on the screen.
I have some point on a 2D grid (x, y) and I need to find all points that are n distance away from that point. The way I'm measuring distance is by using the distance formula between the two points. Anyone know how to do this?
Edit: Just for reference, what I'm trying to do is to write some AI path finding that will maintain some distance away from a target in a system that uses grid based locations. Currently I'm using A* path finding, but I'm not sure if that matters or makes a difference since I'm kind of new to this stuff.
Here's what I would do:
First filter out all points that are further than D on either x or y. These are certainly outside the circle of radius D. This is a much simpler computation, and it can quickly eliminate a lot of work. This is a outer bounding-box optimization.
You can also use an inner bounding-box optimization. If the points are closer than D * sqrt(2)/2 on either x or y, then they're certainly within the circle of radius D. This is also cheaper than calculating the distance formula.
Then you have a smaller number of candidate points that may be within the circle of radius D. For these, use the distance formula. Remember that if D = sqrt(Δx2+Δy2), then D2 = Δx2+Δy2.
So you can skip the cost of calculating square root.
So in pseudocode, you could do the following:
for each point
begin
if test 1 indicates the point is outside the outer bounding box,
then skip this point
if test 2 indicates the point is inside the inner bounding box,
then keep this point
if test 3 indicates the point is inside the radius of the circle,
then keep this point
end
This problem is known as range query. The brute force solution is just as you described: computed the distance of all points from the reference point and return those whose distance is less than the desired range value.
The brute force algorithm is O(N^2). There are, however, more efficient algorithms that employ spatial indexes to reduce algorithm complexity and the number of distance calculations. For example, you can use a R-Tree to index your points.
Its called nearest neighbor search. More at http://en.wikipedia.org/wiki/Nearest_neighbor_search
There are open libraries for that. I have used one written for C and recommend it: http://www.cs.umd.edu/~mount/ANN/. ANN stands for Approximate Nearest Neighbor, however, you can turn the approximation off and find the exact nearest neighbors.
This wouldn't use the distance formula, but if you're looking for points exactly n distance away, perhaps you could use sin/cos?
In pseudocode:
for degrees in range(360):
x = cos(degrees) * n
y = sin(degrees) * n
print x, y
That would print every point n away in 360 degree increments.
Java implementation:
public static Set<Point> findNearbyPoints(Set<Point> pts, Point centerPt, double radius) {
Set<Point> nearbyPtsSet = new HashSet<Point>();
double innerBound = radius * (Math.sqrt(2.0) / 2.0);
double radiusSq = radius * radius;
for (Point pt : pts) {
double xDist = Math.abs(centerPt.x - pt.x);
double yDist = Math.abs(centerPt.y - pt.y);
if (xDist > radius || yDist > radius)
continue;
if (xDist > innerBound || yDist > innerBound)
continue;
if (distSq(centerPt, pt) < radiusSq)
nearbyPtsSet.add(pt);
}
return nearbyPtsSet;
}
I have a line that I must do calculations on for each grid square the line passes through.
I have used the Superline algorithm to get all these grid squares. This gives me an array of X,Y coordinates to check.
Now, here is where I am stuck, I need to be able to calculate the distance traveled through each of the grid squares... As in, on a line not on either 90 degree or 45 degree angles, each grid square accommodates a different 'length' of the total line.
Image example here, need 10 reputation to post images
As you can see, some squares have much more 'line length' in them than others - this is what I need to find.
How do I work this out for each grid square? I've been at this for a while and request the help of the Stack Overflowers!
There may be some clever way to do this that is faster and easier, but you could always hack through it like this:
You know the distance formula: s=sqrt((x2-x1)^2+(y2-y1)^2). To apply this, you must find the x and y co-ordinates of the points where the line intersects the edges of each grid cell. You can do this by plugging the x and y co-ordinates of the boundaries of the cell into the equation of the line and solve for x or y as appropriate.
That is, each cell extends from some point (x0,y0) to (x0+1,y0+1). So we need to find y(x0), y(x0+1), x(y0), and x(y0+1). For each of these, the x or y value found may or may not be within the ranges for that co-ordinate for that cell. Specifically, two of them will be and two won't. The two that are correspond to the edges that the line passes through, and the two that aren't are edges that it doesn't pass through.
Okay, maybe this sounds pretty confusing, so let's work through an example.
Let's say your line has the equation x=2/3 * y. You want to know where it intersects the edges of the cell extending from (1,0) to (2,1).
Plug in x=1 and you get y=2/3. 2/3 is in the legal range for y -- 0 to 1 -- so (1,2/3) is a point on the edge where the line intersects this cell. Namely, the left edge.
Plug in x=2 and you get y=4/3. 4/3 is outside the range for y. So the line does not pass through the right edge.
Plug in y=0 and you get x=0. 0 is not in the range for x, so the line does not pass through the bottom edge.
Plug in y=1 and you get x=3/2. 3/2 is in the legal range for x, so (3/2,1) is another intersection point, on the top edge.
Thus, the two points where the line intersects the edges of the cell are (1,2/3) and (3/2,1). Plug these into the distance formula and you'll get the length of the line segement through this cell, namely sqrt((1-3/2)^2+(2/3-1)^2)=sqrt(1/4+1/9)=sqrt(13/36). You can approximate that to any desired level of precision.
To do this in a program you'd need something like: (I'll use pseudo code because I don't know what language you're using)
// Assuming y=mx+b
function y(x)
return mx+b
function x(y)
return (y-b)/m
// cellx, celly are co-ordinates of lower left corner of cell
// Upper right must therefore be cellx+1, celly+1
function segLength(cellx, celly)
// We'll create two arrays pointx and pointy to hold co-ordinates of intersect points
// n is index into these arrays
// In an object-oriented language, we'd create an array of point objects, but whatever
n=0
y1=y(cellx)
if y1>=celly and y1<=celly+1
pointx[n]=cellx
pointy[n]=y1
n=n+1
y2=y(cellx+1)
if y2>=celly and y2<=celly+1
pointx[n]=cellx+1
pointy[n]=y2
n=n+1
x1=x(celly)
if x1>=cellx and x1<=cellx+1
pointx[n]=x1
pointy[n]=celly
n=n+1
x2=x(celly+1)
if x2>=cellx and x2<=cellx+1
pointx[n]=x2
pointy[n]=celly+1
n=n+1
if n==0
return "Error: line does not intersect this cell"
else if n==2
return sqrt((pointx[0]-pointx[1])^2+(pointy[0]-pointy[1])^2)
else
return "Error: Impossible condition"
Well, I'm sure you could make the code a little cleaner, but that's the idea.
have a look at Siddon's algorithm: "Fast calculation of the exact radiological path for a three-dimensional CT array"
unfortunately you need a subscription to read the original paper, but it is fairly well described in this paper
Siddon's algorithm is an O(n) algorithm for finding the length of intersection of a line with each pixel/voxel in a regular 2d/3d grid.
Use the Euclidean Distance.
sqrt((x2-x1)^2 + (y2-y1)^2)
This gives the actual distance in units between points (x1,y1) and (x2,y2)
You can fairly simply find this for each square.
You have the slope of the line m = (y2-y1)/(x2-x1).
You have the starting point:
(x1,y2)
What is the y position at x1 + 1? (i.e. starting at the next square)
Assuming you set your starting point to 0 the equation of this line is simply:
y_n = mx_n
so y_n = (y2-y1)/(x2-x1) * x_n
Then the coordinates at the first square are (x1,y1) and at the nth point:
(1, ((y2-y1)/(x2-x1))*1)
(2, ((y2-y1)/(x2-x1))*2)
(3, ((y2-y1)/(x2-x1))*3)
...
(n, ((y2-y1)/(x2-x1))*n)
Then the distance through the nth square is:
sqrt((x_n+1 - x_n)^2 + (y_n+1 - y_n)^2)
I'm trying to make a triangle (isosceles triangle) to move around the screen and at the same time slightly rotate it when a user presses a directional key (like right or left).
I would like the nose (top point) of the triangle to lead the triangle at all times. (Like that old asteroids game).
My problem is with the maths behind this. At every X time interval, I want the triangle to move in "some direction", I need help finding this direction (x and y increments/decrements).
I can find the center point (Centroid) of the triangle, and I have the top most x an y points, so I have a line vector to work with, but not a clue as to "how" to work with it.
I think it has something to do with the old Sin and Cos methods and the amount (angle) that the triangle has been rotated, but I'm a bit rusty on that stuff.
Any help is greatly appreciated.
The arctangent (inverse tangent) of vy/vx, where vx and vy are the components of your (centroid->tip) vector, gives you the angle the vector is facing.
The classical arctangent gives you an angle normalized to -90° < r < +90° degrees, however, so you have to add or subtract 90 degrees from the result depending on the sign of the result and the sign of vx.
Luckily, your standard library should proive an atan2() function that takes vx and vy seperately as parameters, and returns you an angle between 0° and 360°, or -180° and +180° degrees. It will also deal with the special case where vx=0, which would result in a division by zero if you were not careful.
See http://www.arctangent.net/atan.html or just search for "arctangent".
Edit: I've used degrees in my post for clarity, but Java and many other languages/libraries work in radians where 180° = π.
You can also just add vx and vy to the triangle's points to make it move in the "forward" direction, but make sure that the vector is normalized (vx² + vy² = 1), else the speed will depend on your triangle's size.
#Mark:
I've tried writing a primer on vectors, coordinates, points and angles in this answer box twice, but changed my mind on both occasions because it would take too long and I'm sure there are many tutorials out there explaining stuff better than I ever can.
Your centroid and "tip" coordinates are not vectors; that is to say, there is nothing to be gained from thinking of them as vectors.
The vector you want, vForward = pTip - pCentroid, can be calculated by subtracting the coordinates of the "tip" corner from the centroid point. The atan2() of this vector, i.e. atan2(tipY-centY, tipX-centX), gives you the angle your triangle is "facing".
As for what it's relative to, it doesn't matter. Your library will probably use the convention that the increasing X axis (---> the right/east direction on presumably all the 2D graphs you've seen) is 0° or 0π. The increasing Y (top, north) direction will correspond to 90° or (1/2)π.
It seems to me that you need to store the rotation angle of the triangle and possibly it's current speed.
x' = x + speed * cos(angle)
y' = y + speed * sin(angle)
Note that angle is in radians, not degrees!
Radians = Degrees * RadiansInACircle / DegreesInACircle
RadiansInACircle = 2 * Pi
DegressInACircle = 360
For the locations of the vertices, each is located at a certain distance and angle from the center. Add the current rotation angle before doing this calculation. It's the same math as for figuring the movement.
Here's some more:
Vectors represent displacement. Displacement, translation, movement or whatever you want to call it, is meaningless without a starting point, that's why I referred to the "forward" vector above as "from the centroid," and that's why the "centroid vector," the vector with the x/y components of the centroid point doesn't make sense. Those components give you the displacement of the centroid point from the origin. In other words, pOrigin + vCentroid = pCentroid. If you start from the 0 point, then add a vector representing the centroid point's displacement, you get the centroid point.
Note that:
vector + vector = vector
(addition of two displacements gives you a third, different displacement)
point + vector = point
(moving/displacing a point gives you another point)
point + point = ???
(adding two points doesn't make sense; however:)
point - point = vector
(the difference of two points is the displacement between them)
Now, these displacements can be thought of in (at least) two different ways. The one you're already familiar with is the rectangular (x, y) system, where the two components of a vector represent the displacement in the x and y directions, respectively. However, you can also use polar coordinates, (r, Θ). Here, Θ represents the direction of the displacement (in angles relative to an arbitary zero angle) and r, the distance.
Take the (1, 1) vector, for example. It represents a movement one unit to the right and one unit upwards in the coordinate system we're all used to seeing. The polar equivalent of this vector would be (1.414, 45°); the same movement, but represented as a "displacement of 1.414 units in the 45°-angle direction. (Again, using a convenient polar coordinate system where the East direction is 0° and angles increase counter-clockwise.)
The relationship between polar and rectangular coordinates are:
Θ = atan2(y, x)
r = sqrt(x²+y²) (now do you see where the right triangle comes in?)
and conversely,
x = r * cos(Θ)
y = r * sin(Θ)
Now, since a line segment drawn from your triangle's centroid to the "tip" corner would represent the direction your triangle is "facing," if we were to obtain a vector parallel to that line (e.g. vForward = pTip - pCentroid), that vector's Θ-coordinate would correspond to the angle that your triangle is facing.
Take the (1, 1) vector again. If this was vForward, then that would have meant that your "tip" point's x and y coordinates were both 1 more than those of your centroid. Let's say the centroid is on (10, 10). That puts the "tip" corner over at (11, 11). (Remember, pTip = pCentroid + vForward by adding "+ pCentroid" to both sides of the previous equation.) Now in which direction is this triangle facing? 45°, right? That's the Θ-coordinate of our (1, 1) vector!
keep the centroid at the origin. use the vector from the centroid to the nose as the direction vector. http://en.wikipedia.org/wiki/Coordinate_rotation#Two_dimensions will rotate this vector. construct the other two points from this vector. translate the three points to where they are on the screen and draw.
double v; // velocity
double theta; // direction of travel (angle)
double dt; // time elapsed
// To compute increments
double dx = v*dt*cos(theta);
double dy = v*dt*sin(theta);
// To compute position of the top of the triangle
double size; // distance between centroid and top
double top_x = x + size*cos(theta);
double top_y = y + size*sin(theta);
I can see that I need to apply the common 2d rotation formulas to my triangle to get my result, Im just having a little bit of trouble with the relationships between the different components here.
aib, stated that:
The arctangent (inverse tangent) of
vy/vx, where vx and vy are the
components of your (centroid->tip)
vector, gives you the angle the vector
is facing.
Is vx and vy the x and y coords of the centriod or the tip? I think Im getting confused as to the terminology of a "vector" here. I was under the impression that a Vector was just a point in 2d (in this case) space that represented direction.
So in this case, how is the vector of the centroid->tip calculated? Is it just the centriod?
meyahoocomlorenpechtel stated:
It seems to me that you need to store
the rotation angle of the triangle and
possibly it's current speed.
What is the rotation angle relative to? The origin of the triangle, or the game window itself? Also, for future rotations, is the angle the angle from the last rotation or the original position of the triangle?
Thanks all for the help so far, I really appreciate it!
you will want the topmost vertex to be the centroid in order to achieve the desired effect.
First, I would start with the centroid rather than calculate it. You know the position of the centroid and the angle of rotation of the triangle, I would use this to calculate the locations of the verticies. (I apologize in advance for any syntax errors, I have just started to dabble in Java.)
//starting point
double tip_x = 10;
double tip_y = 10;
should be
double center_x = 10;
double center_y = 10;
//triangle details
int width = 6; //base
int height = 9;
should be an array of 3 angle, distance pairs.
angle = rotation_angle + vertex[1].angle;
dist = vertex[1].distance;
p1_x = center_x + math.cos(angle) * dist;
p1_y = center_y - math.sin(angle) * dist;
// and the same for the other two points
Note that I am subtracting the Y distance. You're being tripped up by the fact that screen space is inverted. In our minds Y increases as you go up--but screen coordinates don't work that way.
The math is a lot simpler if you track things as position and rotation angle rather than deriving the rotation angle.
Also, in your final piece of code you're modifying the location by the rotation angle. The result will be that your ship turns by the rotation angle every update cycle. I think the objective is something like Asteroids, not a cat chasing it's tail!