I am aware that there are other solutions to this problem, but I am learning and for that purpose would like to understand why my algorithm doesn't work
Here is how my solution works:
It gets the coordinates for the 4 vertices of each rectangle, translates them so they are relative to 0, 0 rather than the rectangles' origins, and rotates them respectively using these formulas:
rotatedX = unrotatedX * cos( radiansOfCounterClockwiseRotation ) -
unrotatedY * sin( radians );
and
rotatedY = unrotatedX * sin( radians ) + y * cos( radians );
and translates them so they are relative to their rectangles' origins
Then it calculates x and y values of the axis to compare separation on by subtracting the upper left, and lower right vertices' coordinates from those of the upper right vertice, for a total of 4 axis (1 for each pair of parallel edges of the 2 rectangles)
Then it calculates an x value for each of the rotated vertices, which lies on the axis, using this formula:
x = -( -( axis.x / axis.y ) * vertice.x - vertice.y ) /
( axis[i].y / axis[i].x + axis[i].x / axis[i].y );
which is derived from getting the point of intersection between the axis line and the line perpendicular to the axis that passes through the vertice
It compares these values for each axis and checks whether:
maxXValueForRect0 >= minXValueForRect1 AND minXValueForRect0 <= maxXValueForRect1;
if that is true for every axis, then there is a collision
However, during debugging I discovered that the min x values area always lower than the max x values, regardless of positions and sizes of the rectangles
Can anyone tell me what is wrong here?
This works perfectly and I made an intensely minor error in my code itself that caused both groups of vertices to be translated relative to the first rectangle after their rotation. So the logic presented here is solid and will work provided you don't make a silly mistake like I did :D
Related
This question already has answers here:
Fitting largest circle in free area in image with distributed particle
(5 answers)
Closed 1 year ago.
I deal with plots of data on the order of half a million points in Octave. I am trying to find the center of empty spaces that are in the data (on purpose).
I know how many points to look for and I was thinking of feeding in starter locations and then try to expand a circle in one direction until you hit valid data point locations and keep doing that in a few directions until you have a circle that is filled with no data but touches valid data points. The center of that circle would be the center of the void space. I'm not entirely sure how to write that since I'm very green in coding.
Obviously a graphical solution probably isn't the best method, but I don't know how to find big x and y gaps in a huge matrix of x y locations.
A section of the data I deal with. Trying to write a program to automatically find the center of that hole.
A sample of the data I'm working with. Each data point is an x and y location with a z height that isn't really valuable to what I'm trying to solve here. The values do not line up in consistent intervals
Here is a large sample of what I'm working with
I know you said your data does not line-up in x or y, but it still seems suspiciously grid-like.
In this case, you can probably express each gridpoint as a 'pixel' in an image; this gives you access to excellent functions you can use from the image package, such as the imregionalmin function. This will give you connected components of 'holes', in your case. For each component you can find their centres of mass easily by finding the 'average coordinate' over the pixels within that component. You can then perform a distance transform (e.g. using bwdist) to find the exact radius for the circle you describe, as the distance from that centre of mass to the nearest pixel. Alternatively, you can start with bwdist and then use immaximas to detect the centres of mass directly. If you have multiple such regions, you can use bwconncomp to find connected components first (or over the output of imregionalmin).
If your data is not specifically grid-like, then you could probably interpolate your data to make them fit such a grid.
Example:
pkg load image
t = 0 : 0.1 : 2 * pi; % for use when plotting circles later
[X0, Y0] = ndgrid( 1:100, 1:100 ); % Create 'index' grid
X = X0 - 0.25 * Y0; Y = 0.25 * X0 + Y0; % Create transformed grid
Z = 0.5 * (X0 - 50) .^ 2 + (Y0 - 50) .^ 2 > 250; % Assign a logical value to each 'index' point on grid
M = imregionalmin ( Z ); % Find 'hole' as mask
C = { round(mean(X0(M))), round(mean(Y0(M))) }; % Find centre of mass (as index)
R = bwdist( ~M )(C{:}); % Find distance from centre of mass to nearest pixel
R = min( abs( X(C{1}+R, C{2}) - X(C{:}) ), abs( Y(C{1}, C{2}+R) - Y(C{:}) ) ); % Adjust for transformed grid
figure(1); hold on
plot( X(Z), Y(Z), '.', 'markerfacecolor', 'b' ) % Draw original transformed grid data
plot( X(C{:}), Y(C{:}), 'o', 'markerfacecolor', 'r' ); % Draw centre of mass in transformed grid
plot( X(C{:}) + R * cos(t), Y(C{:}) + R * sin(t), 'r-' ) % Draw optimal circle on top
axis equal; hold off
I need to find pixels laying on a circle centered on point (0,0). Right now I do this by using formulas:
x = round(r * cos(angle))
y = round(r * sin(angle))
and my angle takes values from 0 to 2 Pi.
However this is not producing accurate results. For example, it gives point (1,1) for diameter 1 and also for diameter 2. How to avoid it?
I'm playing a bit with D3.js and I got most things working. But I want to place my svg shapes in a circle. So I will show the difference in data with color and text. I know how to draw circles and pie charts, but I want to basically have a circle of same size circles. And not have them overlap, the order is irrelevant. I don't know where to start, to find out the x & y for each circle.
If I understand you correctly, this is a fairly standard math question:
Simply loop over some angle variable in the appropriate step size and use sin() and cos() to calculate your x and y values.
For example:
Let's say you are trying to place 3 objects. There are 360 degrees in a circle. So each object is 120 degrees away from the next. If your objects are 20x20 pixels in size, place them at the following locations:
x1 = sin( 0 * pi()/180) * r + xc - 10; y1 = cos( 0 * pi()/180) * r + yc - 10
x2 = sin(120 * pi()/180) * r + xc - 10; y2 = cos(120 * pi()/180) * r + yc - 10
x3 = sin(240 * pi()/180) * r + xc - 10; y3 = cos(240 * pi()/180) * r + yc - 10
Here, r is the radius of the circle and (xc, yc) are the coordinates of the circle's center point. The -10's make sure that the objects have their center (rather than their top left corner) on the circle. The * pi()/180 converts the degrees to radians, which is the unit most implementations of sin() and cos() require.
Note: This places the shapes equally distributed around the circle. To make sure they don't overlap, you have to pick your r big enough. If the objects have simple and identical boundaries, just lay out 10 of them and figure out the radius you need and then, if you need to place 20, make the radius twice as big, for 30 three times as big and so forth. If the objects are irregularly shaped and you want to place them in the optimal order around the circle to find the smallest circle possible, this problem will get extremely messy. Maybe there's a library for this, but I don't have one in the top of my head and since I haven't used D3.js, I'm not sure whether it will provide you with this functionality either.
Here's another approach to this, for shapes of arbitrary size, using D3's tree layout: http://jsfiddle.net/nrabinowitz/5CfGG/
The tree layout (docs, example) will figure out the x,y placement of each item for you, based on a given radius and a function returning the separation between the centers of any two items. In this example, I used circles of varying sizes, so the separation between them is a function of their radii:
var tree = d3.layout.tree()
.size([360, radius])
.separation(function(a, b) {
return radiusScale(a.size) + radiusScale(b.size);
});
Using the D3 tree layout solves the first problem, laying out the items in a circle. The second problem, as #Markus notes, is how to calculate the right radius for the circle. I've taken a slightly rough approach here, for the sake of expediency: I estimate the circumference of the circle as the sum of the diameters of the various items, with a given padding in between, then calculate radius from the circumference:
var roughCircumference = d3.sum(data.map(radiusScale)) * 2 +
padding * (data.length - 1),
radius = roughCircumference / (Math.PI * 2);
The circumference here isn't exact, and this will be less and less accurate the fewer items you have in the circle, but it's close enough for this purpose.
I have the need to determine the bounding rectangle for a polygon at an arbitrary angle. This picture illustrates what I need to do:
alt text http://kevlar.net/RotatedBoundingRectangle.png
The pink rectangle is what I need to determine at various angles for simple 2d polygons.
Any solutions are much appreciated!
Edit:
Thanks for the answers, I got it working once I got the center points correct. You guys are awesome!
To get a bounding box with a certain angle, rotate the polygon the other way round by that angle. Then you can use the min/max x/y coordinates to get a simple bounding box and rotate that by the angle to get your final result.
From your comment it seems you have problems with getting the center point of the polygon. The center of a polygon should be the average of the coordinate sums of each point. So for points P1,...,PN, calculate:
xsum = p1.x + ... + pn.x;
ysum = p1.y + ... + pn.y;
xcenter = xsum / n;
ycenter = ysum / n;
To make this complete, I also add some formulas for the rotation involved. To rotate a point (x,y) around a center point (cx, cy), do the following:
// Translate center to (0,0)
xt = x - cx;
yt = y - cy;
// Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * cos(alpha) - yt * sin(alpha);
yr = xt * sin(alpha) + yt * cos(alpha);
// Translate back to (cx, cy)
result.x = xr + cx;
result.y = yr + cx;
To get the smallest rectangle you should get the right angle. This can acomplished by an algorithm used in collision detection: oriented bounding boxes.
The basic steps:
Get all vertices cordinates
Build a covariance matrix
Find the eigenvalues
Project all the vertices in the eigenvalue space
Find max and min in every eigenvalue space.
For more information just google OBB "colision detection"
Ps: If you just project all vertices and find maximum and minimum you're making AABB (axis aligned bounding box). Its easier and requires less computational effort, but doesn't guarantee the minimum box.
I'm interpreting your question to mean "For a given 2D polygon, how do you calculate the position of a bounding rectangle for which the angle of orientation is predetermined?"
And I would do it by rotating the polygon against the angle of orientation, then use a simple search for its maximum and minimum points in the two cardinal directions using whatever search algorithm is appropriate for the structure the points of the polygon are stored in. (Simply put, you need to find the highest and lowest X values, and highest and lowest Y values.)
Then the minima and maxima define your rectangle.
You can do the same thing without rotating the polygon first, but your search for minimum and maximum points has to be more sophisticated.
To get a rectangle with minimal area enclosing a polygon, you can use a rotating calipers algorithm.
The key insight is that (unlike in your sample image, so I assume you don't actually require minimal area?), any such minimal rectangle is collinear with at least one edge of (the convex hull of) the polygon.
Here is a python implementation for the answer by #schnaader.
Given a pointset with coordinates x and y and the degree of the rectangle to bound those points, the function returns a point set with the four corners (and a repetition of the first corner).
def BoundingRectangleAnglePoints(x,y, alphadeg):
#convert to radians and reverse direction
alpha = np.radians(alphadeg)
#calculate center
cx = np.mean(x)
cy = np.mean(y)
#Translate center to (0,0)
xt = x - cx
yt = y - cy
#Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * np.cos(alpha) - yt * np.sin(alpha)
yr = xt * np.sin(alpha) + yt * np.cos(alpha)
#Find the min and max in rotated space
minx_r = np.min(xr)
miny_r = np.min(yr)
maxx_r = np.max(xr)
maxy_r = np.max(yr)
#Set up the minimum and maximum points of the bounding rectangle
xbound_r = np.asarray([minx_r, minx_r, maxx_r, maxx_r,minx_r])
ybound_r = np.asarray([miny_r, maxy_r, maxy_r, miny_r,miny_r])
#Rotate and Translate back to (cx, cy)
xbound = (xbound_r * np.cos(-alpha) - ybound_r * np.sin(-alpha))+cx
ybound = (xbound_r * np.sin(-alpha) + ybound_r * np.cos(-alpha))+cy
return xbound, ybound
I am going to develop a 2-d ball game where two balls (circles) collide. Now I have the problem with determining the colliding point (in fact, determining whether they are colliding in x-axis/y-axis). I have an idea that when the difference between the y coordinate of 2 balls is greater than the x coordinate difference then they collide in their y axis, otherwise, they collide in their x axis. Is my idea correct? I implemented this thing in my games. Normally it works well, but sometimes, it fails. Can anyone tell me whether my idea is right? If not, then why, and is any better way?
By collision in the x axis, I mean the circle's 1st, 4th, 5th, or 8th octant, y axis means the circle's 2nd, 3rd, 6th, or 7th octant.
Thanks in advance!
Collision between circles is easy. Imagine there are two circles:
C1 with center (x1,y1) and radius r1;
C2 with center (x2,y2) and radius r2.
Imagine there is a line running between those two center points. The distance from the center points to the edge of either circle is, by definition, equal to their respective radii. So:
if the edges of the circles touch, the distance between the centers is r1+r2;
any greater distance and the circles don't touch or collide; and
any less and then do collide.
So you can detect collision if:
(x2-x1)^2 + (y2-y1)^2 <= (r1+r2)^2
meaning the distance between the center points is less than the sum of the radii.
The same principle can be applied to detecting collisions between spheres in three dimensions.
Edit: if you want to calculate the point of collision, some basic trigonometry can do that. You have a triangle:
(x1,y1)
|\
| \
| \ sqrt((x2-x1)^2 + (y2-y1)^2) = r1+r2
|y2-y1| | \
| \
| X \
(x1,y2) +------+ (x2,y2)
|x2-x1|
The expressions |x2-x1| and |y2-y1| are absolute values. So for the angle X:
|y2 - y1|
sin X = -------
r1 + r2
|x2 - x1|
cos X = -------
r1 + r2
|y2 - y1|
tan X = -------
|x2 - x1|
Once you have the angle you can calculate the point of intersection by applying them to a new triangle:
+
|\
| \
b | \ r2
| \
| X \
+-----+
a
where:
a
cos X = --
r2
so
a = r2 cos X
From the previous formulae:
|x2 - x1|
a = r2 -------
r1 + r2
Once you have a and b you can calculate the collision point in terms of (x2,y2) offset by (a,b) as appropriate. You don't even need to calculate any sines, cosines or inverse sines or cosines for this. Or any square roots for that matter. So it's fast.
But if you don't need an exact angle or point of collision and just want the octant you can optimize this further by understanding something about tangents, which is:
0 <= tan X <= 1 for 0 <= X <= 45 degrees;
tan X >= 1 for 45 <= X <= 90
0 >= tan X >= -1 for 0 >= X => -45;
tan X <= -1 for -45 >= X => -90; and
tan X = tan (X+180) = tan (X-180).
Those four degree ranges correspond to four octants of the cirlce. The other four are offset by 180 degrees. As demonstrated above, the tangent can be calculated simply as:
|y2 - y1|
tan X = -------
|x2 - x1|
Lose the absolute values and this ratio will tell you which of the four octants the collision is in (by the above tangent ranges). To work out the exact octant just compare x1 and x2 to determine which is leftmost.
The octant of the collision on the other single is offset (octant 1 on C1 means octant 5 on C2, 2 and 6, 3 and 7, 4 and 8, etc).
As cletus says, you want to use the sum of the radii of the two balls. You want to compute the total distance between the centers of the balls, as follows:
Ball 1: center: p1=(x1,y1) radius: r1
Ball 2: center: p2=(x2,y2) radius: r2
collision distance: R= r1 + r2
actual distance: r12= sqrt( (x2-x1)^2 + (y2-y1)^2 )
A collision will happen whenever (r12 < R). As Artelius says, they shouldn't actually collide on the x/y axes, they collide at a particular angle. Except, you don't actually want that angle; you want the collision vector. This is the difference between the centers of the two circles when they collide:
collision vector: d12= (x2-x1,y2-y1) = (dx,dy)
actual distance: r12= sqrt( dx*dx + dy*dy )
Note that you have already computed dx and dy above when figuring the actual distance, so you might as well keep track of them for purposes like this. You can use this collision vector for determining the new velocity of the balls -- you're going to end up scaling the collision vector by some factors, and adding that to the old velocities... but, to get back to the actual collision point:
collision point: pcollision= ( (x1*r2+x2*r1)/(r1+r2), (y1*r2+y2*r1)/(r1+r2) )
To figure out how to find the new velocity of the balls (and in general to make more sense out of the whole situation), you should probably find a high school physics book, or the equivalent. Unfortunately, I don't know of a good web tutorial -- suggestions, anyone?
Oh, and if still want to stick with the x/y axis thing, I think you've got it right with:
if( abs(dx) > abs(dy) ) then { x-axis } else { y-axis }
As for why it might fail, it's hard to tell without more information, but you might have a problem with your balls moving too fast, and passing right by each other in a single timestep. There are ways to fix this problem, but the simplest way is to make sure they don't move too fast...
This site explains the physics, derives the algorithm, and provides code for collisions of 2D balls.
Calculate the octant after this function calculates the following: position of collision point relative to centre of mass of body a; position of collision point relative to centre of mass of body a
/**
This function calulates the velocities after a 2D collision vaf, vbf, waf and wbf from information about the colliding bodies
#param double e coefficient of restitution which depends on the nature of the two colliding materials
#param double ma total mass of body a
#param double mb total mass of body b
#param double Ia inertia for body a.
#param double Ib inertia for body b.
#param vector ra position of collision point relative to centre of mass of body a in absolute coordinates (if this is
known in local body coordinates it must be converted before this is called).
#param vector rb position of collision point relative to centre of mass of body b in absolute coordinates (if this is
known in local body coordinates it must be converted before this is called).
#param vector n normal to collision point, the line along which the impulse acts.
#param vector vai initial velocity of centre of mass on object a
#param vector vbi initial velocity of centre of mass on object b
#param vector wai initial angular velocity of object a
#param vector wbi initial angular velocity of object b
#param vector vaf final velocity of centre of mass on object a
#param vector vbf final velocity of centre of mass on object a
#param vector waf final angular velocity of object a
#param vector wbf final angular velocity of object b
*/
CollisionResponce(double e,double ma,double mb,matrix Ia,matrix Ib,vector ra,vector rb,vector n,
vector vai, vector vbi, vector wai, vector wbi, vector vaf, vector vbf, vector waf, vector wbf) {
double k=1/(ma*ma)+ 2/(ma*mb) +1/(mb*mb) - ra.x*ra.x/(ma*Ia) - rb.x*rb.x/(ma*Ib) - ra.y*ra.y/(ma*Ia)
- ra.y*ra.y/(mb*Ia) - ra.x*ra.x/(mb*Ia) - rb.x*rb.x/(mb*Ib) - rb.y*rb.y/(ma*Ib)
- rb.y*rb.y/(mb*Ib) + ra.y*ra.y*rb.x*rb.x/(Ia*Ib) + ra.x*ra.x*rb.y*rb.y/(Ia*Ib) - 2*ra.x*ra.y*rb.x*rb.y/(Ia*Ib);
double Jx = (e+1)/k * (Vai.x - Vbi.x)( 1/ma - ra.x*ra.x/Ia + 1/mb - rb.x*rb.x/Ib)
- (e+1)/k * (Vai.y - Vbi.y) (ra.x*ra.y / Ia + rb.x*rb.y / Ib);
double Jy = - (e+1)/k * (Vai.x - Vbi.x) (ra.x*ra.y / Ia + rb.x*rb.y / Ib)
+ (e+1)/k * (Vai.y - Vbi.y) ( 1/ma - ra.y*ra.y/Ia + 1/mb - rb.y*rb.y/Ib);
Vaf.x = Vai.x - Jx/Ma;
Vaf.y = Vai.y - Jy/Ma;
Vbf.x = Vbi.x - Jx/Mb;
Vbf.y = Vbi.y - Jy/Mb;
waf.x = wai.x - (Jx*ra.y - Jy*ra.x) /Ia;
waf.y = wai.y - (Jx*ra.y - Jy*ra.x) /Ia;
wbf.x = wbi.x - (Jx*rb.y - Jy*rb.x) /Ib;
wbf.y = wbi.y - (Jx*rb.y - Jy*rb.x) /Ib;
}
I agree with provided answers, they are very good.
I just want to point you a small pitfall: if the speed of balls is high, you can just miss the collision, because circles never intersect for given steps.
The solution is to solve the equation on the movement and to find the correct moment of the collision.
Anyway, if you would implement your solution (comparisons on X and Y axes) you'd get the good old ping pong! http://en.wikipedia.org/wiki/Pong
:)
The point at which they collide is on the line between the midpoints of the two circles, and its distance from either midpoint is the radius of that respective circle.