I am trying to implement an algorithm that will align a rectangle on an ellipse when a point is dragged.
The data I have is:
I know what corner is being dragged
the starting position of it
the ending position of it
My old algorithm was to align the adjacent corners, but that only worked if the ellipse or rectangle weren't at an angle.
I am trying to implement something like Figma does:
My current idea is to take the sides that were changed on drag and match the other sides that weren't changed to the size of the changed sides. Though I'm not sure if that's correct.
Let rectangle is described by center point (CX, CY) and two unit direction vectors (WX, WY) and (HX, HY), also W is half-width, H is half-height.
As far as I understand, rectangle slope is preserved, so direction vectors remain the same.
When corner number k was shifted, it's new position is (NX, NY). Opposite vertex has number (k+2)%4 and it's position is (PX, PY) (doesn't change)
New center is
CX' = (PX + NX) / 2
CY' = (PY + NY) / 2
New half-width and half-height
W' = 0.5 * Abs(WX * (NX - PX) + WY * (NY - PY))
H' = 0.5 * Abs(HX * (NX - PX) + HY * (NY - PY))
Related
I am writing a simple graphics editor where a user can draw a triangle (in either clockwise or counterclockwise orientation) and can then select the triangle and drag it around.
My issue comes down to finding whether or not the mouse cursor coordinates intersect with the triangle. Because I am using GLFW, my window space is defined on a Cartesian coordinate system where x/y have range [-1, 1].
This causes an issue when I try to determine whether I have an intersection using barycentric coordinates (or any other method found here)
My current approach is as follows:
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
// Get the size of the window
int width, height;
glfwGetWindowSize(window, &width, &height);
// Convert screen position to world coordinates
xworld = ((xpos / double(width)) * 2) - 1;
yworld = (((height - 1 - ypos) / double(height)) * 2) - 1; // NOTE: y axis is flipped in glfw
double area = 0.5 * (-p1y * p2x + p0y * (-p1x + p2x) + p0x * (p1y - p2y) + p1x * p2y);
double s = 1 / (2 * area) * (p0y * p2x - p0x * p2y + (p2y - p0y) * xworld + (p0x - p2x) * yworld),
t = 1 / (2 * area) * (p0x * p1y - p0y * p1x + (p0y - p1y) * xworld + (p1x - p0x) * yworld);
if (s > 0 && t > 0 && 1 - s - t > 0)
return true;
return false;
Which kinda works if triangle in the first quadrant and oriented counter-clockwise, however, it also recognizes as intersection if to the left of the triangle.
Thanks for any help.
EDIT: Issue was a typo I had in my code (wrong value for a vertice in my triangle) Ended up using the calculation of area approach to detect intersection.
If you don't know the winding order of your triangle, you can check if mouse cursor position is to the left of every edge and if it is to the right of every edge. If one of this is true, so the mouse cursor is inside the triangle indeed.
Luckily, in the case of a triangle, any 2-combination of its vertices yield its edge. So the problem amounts to calculate six cross products.
But also, after the user has finished drawing of a triangle you can save triangle's vertices with a certain winding order, then you can do only three cross products.
I want to do it that a convex(consist of some line or arc) rotate around it's geometrical center(Cx,Cy). Meanwhile the convex flanked by two circle (given by radius: R and left center:(Lx,Cy), right center:(Rx,Cy) ). It means the circle center with same X axis as geometrical(Cy).
The model picture:
The algorithm aims to calculate the Lx and Rx when rotate theta(0 to 360 degree) and the convex with two circle only have one contact point separately. How can I achieve it?
Assume we can get the convex discretization points by given delta distance or delta theta around geometrical center.
To provide contact between rotated convex and circle, it is worth to define convex pieces analytically.
For example, if straight segment has parametric equation
X = X0 + t * (X1-X0)
Y = Y0 + t * (Y1-Y0)
then solve equation system
(X - Lx)^2 + (Y - Cy)^2 = R^2 //distance
(X - Lx) * (X1 - X0) + (Y - Cy) * (Y1 - Y0) = 0 //tangent perpendicularity to radius
for unknowns t and Lx and check that t lies in range 0..1. If true - circle touches this segment, Lx if valid
If curved segment is analytical curve, then normal to the curve in touch point should be collinear with radius.
For circle arc segment with radius aR and center ax, ay tangency condition is:
(ax - Lx)^2 + (ay - Cy)^2 = (aR + R)^2
again - one need to check whether tangent point is inside arc limits
Problem and nearby solution image:
Problem: A mesh equally distant from each node. Each small node of grid is a circle(fig: grey circle) of radius r, if we make any circle(fig:blue color) then how can we make a set of that circle having upper(fig:red circle) and lower bound(fig:red circle). Basically we need to find upper bound radius, and lower bound radius.
Value known are:
Radius of small circle(grey) r.
Distance between two grey circle/node_of_grid from center to center = 4 X r.
Radius of big circle (blue) rb. //this may vary but will be in multiple of r. In this figure case is diameter d = 2(20 x r).
Center of big circle can only be center of small circle.
How can i find the radius of upper circle and lower circle. Each blue small circle who comes within the big circle circumference should come under upper bound and vice versa for lower bound.
Currently i came up with this solution which is incorrect :
unpper_bound_radius = rb + (r + (rb/(3.1415926 * 2r)))
lower_bound_radius = rb - (r + (rb/(3.1415926 * 2r)))
Thanks
If you're looking for an algorithm to decide which grey dots intersect with the blue circle, and calculate the radius of the boundaries that contain the selected grey dots, you could do something like this:
Give the inner and outer boundary a radius equal to the blue circle.
The grey dot straight above the center, which intersect with the blue circle or is just above it, is the starting point.
Calculate the distance from that point to the center point; if it is between rb-r and rb+r, the blue circle intersects with this grey dot.
If it intersects, adjust the inner boundary to distance - r and the outer boundary to distance + r as necessary.
If it doesn't, and the distance is greater, try the grid point below it; if it doesn't, and the distance is smaller, try the grid point to the right.
Do this until you move out of a 45° sector; you only need to check one eigth of the circle, the other results will be symmetrical.
Example calculation:
The first grid point, at the top, is 5×4r from the center; the boundaries are at 5×4r-r and 5×4r+r.
The next grid point to the right is at 5.09902×4r from the center, so the outer boundary increases to 5.09902×4r+r.
The next grid point to the right is at 5.38516×4r from the center, which is 1.54066×r outside the blue circle (too far).
The next grid point downward is at 4.47216×4r from the center, which is 1.88854×r inside the blue circle (too close).
The next grid point to the right is at exactly 5×4r from the center, so the boundaries don't change.
The next grid point to the right is at 5.65686×4r from the center, which is 2.62742×r outside the blue circle (too far).
The next grid point down is more than 45° to the right.
Result: inner boundary is at 5×4r-r, outer boundary is at 5.09902×4r+r.
function boundaries(b) {
var i = b, o = b, x = 0, y = Math.ceil(b / 4) * 4;
while (Math.atan2(y, x) >= Math.PI / 4) {
var d = Math.sqrt(x * x + y * y);
if (d < b - 1) x += 4; // too close, go right
else if (d > b + 1) y -= 4; // too far, go down
else { // intersection
if (i > d - 1) i = d - 1; // adjust inner
if (o < d + 1) o = d + 1; // adjust outer
x += 4; // go right
}
}
return {i: i, o: o};
}
var res = boundaries(20);
document.write("inner: r×" + res.i + "<br>outer: r×" + res.o);
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