Rotate plotted points of an ellipse to give a circle - r
I have an ellipse-shaped distribution that I think is a conic section. I want to rotate the points so that the distribution becomes circular-shaped, as if I was looking at the conic section directly over the top of the cone.
Here's some example data (generated using the function here)
X_df <-
structure(list(x = c(550.685479223573, 411.808342674744, 125.337513241526,
-46.6813176776531, 54.1090479024869, 335.045593380922, 538.806846993829,
476.123346783785, 207.359201714354, -23.3704356149293, -1.06902389582398,
252.471032092766, 502.461757269191, 522.09464005165, 290.954504794153,
22.4116013413886, -37.4399705577234, 166.122770874069, 446.874779008936,
547.271442128866, 372.271299246978, 84.7905677157295, -50.074206596271,
90.757431249567, 378.201298931732, 547.145608993239, 443.947162007208,
161.476775837252, -38.5517112166543, 25.2627436553199, 296.503160027896,
524.775126009974, 500.784559912938, 245.712512645379, -4.31860487373257,
-21.9661658669887, 211.218663607589, 479.198761786515, 535.314989389215,
330.010941011427, 50.0215808044216, -46.3823119064223, 130.383487121344,
416.170638042649, 549.268852072098, 407.813005658263, 119.940919946473,
-46.978590114418, 57.7409750579334, 340.505587064792, 539.650771180236,
472.254339573695, 201.890629521184, -25.163386210777, 1.25193046435474,
256.776302252232, 506.305676724803, 520.004964534048, 284.257495593069,
18.8183745840118, -35.9075114459174, 172.662124500953, 452.343060560759,
546.468842888411, 367.193523099128, 81.9151159445705, -49.726312730029,
95.1883131124973, 382.503271032958, 548.377552998115, 439.474201456606,
157.248088356873, -39.8634174011649, 28.665422852919, 301.243788141946,
526.815879166266, 497.683488701185, 240.939374274905, -7.78381612220116,
-19.3411744866129, 217.640180353188, 483.134755325255, 534.947529479343,
324.801587123232, 45.3957868762181, -45.0069945691924, 134.781896592204,
420.833721926428, 550.278658272823, 403.464000037755, 116.273973349216,
-48.5483252399878, 62.3918399072614, 345.924165684106, 540.282415561272,
468.621672005007, 195.304995872248, -28.2738679786754, 4.25351768918281,
262.272866287766, 509.296144374104), y = c(150.522375543584,
317.792592638159, 332.783726315973, 177.890614907595, -1.30774215535761,
-41.9959828735621, 94.0557252742373, 281.491416261009, 347.931229803675,
232.411150772918, 41.2498141860971, -50.240758928064, 42.4333078345691,
233.202857825371, 347.930304275537, 279.801242902484, 93.7702821671593,
-42.9605564915062, -0.128821245055916, 179.228629398932, 331.853832274807,
317.39347890031, 147.731418574477, -19.2449921421865, -31.3298018420377,
123.222814177221, 301.499351173211, 340.662811705721, 204.270915882133,
17.5570334546183, -47.8527634953491, 69.1925023774197, 260.846781070028,
350.702299892942, 255.267980339802, 64.3048063206447, -47.7550214881633,
21.0575085383169, 209.247480999408, 342.716023607699, 298.578792586917,
118.053124236616, -33.3190320226709, -16.5618829502486, 154.097078032767,
319.940208485997, 330.141085013461, 175.063055837321, -4.17911647131882,
-40.77087978947, 96.7492568480405, 284.629419943293, 347.18869555741,
228.951107374031, 37.6230640656836, -49.9549156886126, 45.2896584936418,
236.96620488459, 349.2183672397, 277.259838492706, 88.5019845874813,
-43.1419505604449, 1.98249146234145, 183.766146834555, 334.721418603224,
314.869389642466, 144.917094343997, -21.0615467657252, -29.137753346726,
126.928148173889, 305.196445845808, 339.338402720207, 201.609402013064,
15.0779976117978, -47.1046400880924, 71.7191693530443, 263.543031729657,
350.145628648054, 252.792513384296, 61.2531942049295, -49.0697852698499,
24.2068045114243, 212.793083656477, 343.533262533366, 295.969945687212,
115.135250419503, -34.4145910896749, -14.7817206225652, 156.282366465729,
322.116360452784, 328.626788731125, 171.323808201914, -6.54021515590322,
-40.1360134761796, 101.492490333309, 286.854230399582, 347.010792855229,
226.829162028581, 35.3880363162166, -50.8418314561365, 48.893760376765
)), .Names = c("x", "y"), row.names = c(NA, -101L), class = "data.frame")
Plot the ellipse, this resembles my actual data:
ggplot(X_df, aes(x, y)) +
geom_point() +
coord_equal()
I want to apply a function to the xy coords to rotate the ellipse around its long axis, moving the top of the ellipse towards me, and the bottom of the ellipse away from me, to get something like this (with equal dimension along the x-axis as the ellipse above, but different dimension along the y-axis):
I want to rotate my data like this ellipse is rotated to produce the circle, where I see the maximum distance between the top and bottom of the shape (ie. the longest possible axis perpendicular to the long axis), as the shape rotates around it long axis.
How can I do that?
It seems like what you're saying is that if I viewed the "circle" edge on from, say, x=-1000, y=0, I would see a line rotated counterclockwise (out of the xy-plane) away from the y axis. The goal is to rotate the circle back to the y-axis.
The angle of rotation is acos(1/ratio) (0.839 radians or 48.06 degrees in this case) where ratio is diff(range(X_df$x))/diff(range(X_df$y)) (assuming the axis of rotation is in the xy-plane and parallel to the x-axis and your data includes points on the x axis on both sides of the circle).
To, in effect, rotate the circle back to the xy-plane, you could just multiply the y points by ratio and then, to maintain the same center, subtract (ratio - 1) * mean(X_df$y) (where I've assumed the data points are evenly distributed about the circle).
In other words (or in code, actually):
ratio = diff(range(X_df$x))/diff(range(X_df$y))
X_df$ynew = ratio * X_df$y - (ratio - 1) * mean(X_df$y)
ggplot(X_df, aes(x, ynew)) +
geom_point() +
coord_equal()
Compare original to "rotated":
Related
Fitting an ellipsoid given 2D contours
I have coordinates corresponding to a set of 2D contours, each corresponding to different heights. These contours do not draw out a perfect ellipsoid in 3D, and instead what I would like to do is to find the best fitting ellipsoid. I do not have any knowledge on the origin of this ellipsoid. My first thought was to incorporate some type of least squares algorithm, where I find the ellipsoid parameters that minimize the distance between points. I imagine this would be quite expensive and not too far from a brute force approach. I am convinced there is a more elegant and efficient way of doing this. If there is an existing library that handles this (preferably in Python) that would be even better. I have already seen a related question (Fitting an ellipsoid to 3D data points), but figured I would ask again as it has been over a decade since that post.
So you have a set of (x,y) values for each contour, which describe a portion of an ellipse (blue dots below). The best fit ellipse is described by the general equation A x^2 + B y^2 + 2C x y + 2D x + 2E y = 1 and once the coefficients (A,B,C,D,E) are found, the ellipse of fully described. See below in how to find the the curve coordinates (x,y) from the coefficients and a parameter t=0 .. 1. To find the coefficients of the ellipse, form 5 vectors, each a column of a n×5 matrix Q for i = 1 to n Q(i,1) = x(i)^2 Q(i,2) = y(i)^2 Q(i,3) = 2*x(i)*y(i) Q(i,4) = 2*x(i) Q(i,5) = 2*y(i) next i and a vector K filled with 1 for the right-hand side for i = 1 to n K(i) = 1.0 next i Find the coefficients using a least-squares fit with some linear algebra [A,B,C,D,E] = inv(tr(Q)*Q)*tr(Q)*K where tr(Q) is the transpose of Q and * is matrix/vector product Now we need to extract the geometric properties of the ellipse from the coefficient. I want to have a the semi-major axis, b the semi-minor axis, φ the rotation angle, xc the x-axis center, yc the y-axis center. xc = -(B*D-C*E)/(A*B-(C^2)) yc = -(A*E-C*D)/(A*B-(C^2)) φ = atan( 2*C/(A-B) )/2 a = SQRT(2*(A*(B+E^2)+B*D^2-C*(C+2*D*E))/((A*B-C^2)*(A+B-SQRT((A-B)^2+4*C^2)))) b = SQRT(2*(A*(B+E^2)+B*D^2-C*(C+2*D*E))/((A*B-C^2)*(A+B+SQRT((A-B)^2+4*C^2)))) Finally to plot the ellipse you need to generate a set of points (x,y) from the curve parameter t=0..1 using the above 5 coefficients. Generate the centered aligned coordinates (u,v) with u = a*cos(2*π*t) v = b*sin(2*π*t) Generate the centered rotated coordinates (x',y') with x' = u*cos(φ) - v*sin(φ) y' = u*sin(φ) + v*cos(φ) Generate the ellipse coordinates (x,y) with x = x' + xc y = y' + yc The result is observed above in the first picture. Now for the total solution, each 2D slice would have its own ellipse. But all the slices would not generate an ellipsoid this way. Extending the above into 3D coordinates (x,y,z) is doable, but the math is quite involved and I feel [SO] is not a good place to develop such an algorithm. You can hack it together, by finding the average center for each slice (weighted by the ellipse area π*a*b). Additionally, the rotation angle should be the same for all contours, and so another averaging is needed. Finally, the major and minor axis values would fall on an elliptical curve along the z-axis and it would require another least-fit solution. This one is driven by the equation (x/a)^2 + (y/b)^2 + (z/c)^2 = 1 but rather in the aligned coordinates (u,v,w) (u/a)^2 + (v/b)^2 + (w/c)^2 = 1
How do I calculate the yaw, pitch, and roll of a point in 3D?
Given a point in 3D space, what are the three angles (e.g. Euler angles) needed to transform a line to point to that object? Imagine I have a line (or a box) in 3D and I want to transform its heading, pitch, and bank to point to the 3D point from the origin, what values would I use for those angles? I can't figure out the math to calculate the angles to point to a location such as (1,2,3).
Note: Instead of "yaw, pitch, roll", I'm going to use the conventions "heading, pitch, bank" as defined by 3D Math Primer for Graphics and Game Development by Fletcher Dunn. Firstly, notice that in a 2D coordinate system, you only need a single angle + magnitude to "point" to any point in 2D. Similarly, in a 3D coordinate system, you only need two angles + magnitude to "point" to any point in 3D. The last angle ("bank" or "roll") does not affect the location of a point in 3D. Instead it "spins" the arrow that would point to it. If the object is 360 degrees symmetrical, you won't see spin affecting the object at all. If the object is not symmetrical (e.g. an airplane) it will affect the object (e.g. tilting one wing towards the ground and the other towards the sky). So the original question actually becomes, "how do I find the heading angle, pitch angle, and magnitude to "point" to any point in 3D space?" You can easily figure this out using trigonometry functions. Imagine we have the point (1,2,3) and we're trying to calculate the heading, pitch, magnitude. For the following example, let's use this diagram, where the left axis is X, up is Y, and right is Z. The point (1,2,3), then is represented by the blue sphere. 1. Find the magnitude First, let's find the easiest value, the magnitude. Luckily for us, the magnitude (length) between any two points is easy to find no matter how many dimensions we are in, simply by using the Pythagorean theorem. Since we are in 3D and we're calculating the distance from the origin to our point, our distance formula becomes: magnitude = sqrt(x*x + y*y + z*z) Plugging in our actual values: magnitude = sqrt(1*1 + 2*2 + 3*3) = 3.7416573868 So our magnitude (or length) is ~3.741. 2. Find the heading Next, to find the heading, notice that we just care about rotation about the XZ plane, and we don't care about the Y-axis at all. If we were to "flatten" the 3D space into 2D, it becomes trivial to find the heading. We can draw a triangle that forms a 90 degree angle with the X-axis (red triangle) and then calculate that angle. Recall from trigonometry tan(angle) = opposite / adjacent, and solving for angle, we get angle = arctan(opposite / adjacent). In this case "adjacent" is a known quantity (redAdjacent = x = 1), and "opposite" is known too (redOpposite = z = 3). Instead of using arctan to solve the equation though, we want to use atan2 since it'll handle all the different cases of x and y for us. So we have: heading = atan2(redOpposite, redAdjacent) Plugging in our actual values: heading = atan2(3, 1) = 1.249045772398 so our heading is 1.249 rad, or ~72°. 3. Find the pitch Finally we need to find the pitch. Similarly to what we did with the heading, we can flatten the the 3D space into 2D along the plane that contains these three points: (A) the origin (0,0,0), (B) our point (1,2,3), and (C) our point as it would project onto the XZ plane (1,0,3) (e.g. by setting 0 for the Y-value). If we draw a triangle between all 3 of these points, you will notice that they form a right-triangle again (green triangle). We can simply calculate the angle using arctan2 again. We already calculated the green hypotenuse in step 1 (i.e. the magnitude of our vector): greenHypotenuse = sqrt(x*x + y*y + z*z) = 3.7416573868 We also know the opposite of the green triangle is the same as the y-value: greenOpposite = y = 2 Using the pythagorean theorem, we can find the length of the adjacent angle: greenOpposite^2 + greenAdjacent^2 = greenHypotenuse^2 y*y + greenAdjacent^2 = x*x + y*y + z*z greenAdjacent^2 = x*x + z*z greenAdjacent = sqrt(x*x + z*z) Notice that another way to calculate the adjacent length of the green triangle is to notice that redHypotenuse == greenAdjacent, and we could find redHypotenuse using: redHypotenuse^2 = redAdjacent^2 + redOpposite^2 = x*x + z*z redHypotenuse = sqrt(x*x + z*z) Plugging in actual values, we get: greenAdjacent = sqrt(1*1 + 3*3) = 3.1622776602 So now that we know the adjacent and opposite lengths of the green triangle, we can use arctan2 again: pitch = atan2(greenOpposite, greenAdjacent) = atan2(2, 3.1622776602) = 0.563942641356 So our pitch is 0.5634 radians, or about 32°. Conclusion If you were to draw a line from the origin, with length 3.741, heading 1.249 rad, and pitch 0.564 rad, it would extend from (0,0,0) to (1,2,3).
How to rotate 180 degrees an mtext() in R
Apparently, mtext() in R doesn't support the srt parameter whose job is to rotate a piece of text. I need mtext() to create an axis title on side 4 of my moving plot (i.e., values to be plotted come from a function so they change and so do the plot axes values). I was wondering then, what options do I have to rotate 180 degrees this side 4 axis title? An example is BELOW: curve(dnorm(x),-3,3) mtext("Strength",side=4,srt=180)
You can use par("usr") to obtain extremes of the plot area and use it to place your text without having to explicitly specify the x and y. Try curve(dnorm(x),-3,3) corners = par("usr") #Gets the four corners of plot area (x1, x2, y1, y2) par(xpd = TRUE) #Draw outside plot area text(x = corners[2]+.5, y = mean(corners[3:4]), "Strength", srt = 270) This way it will always be on the right extreme and vertically in the middle.
Translation coordinates for a circle under a certain angle
I have 2 circles that collide in a certain collision point and under a certain collision angle which I calculate using this formula : C1(x1,y1) C2(x2,y2) and the angle between the line uniting their centre and the x axis is X = arctg (|y2 - y1| / |x2 - x1|) and what I want is to translate the circle on top under the same angle that collided with the other circle. I mean with the angle X and I don't know what translation coordinates should I give for a proper and a straight translation!
For what I think you mean, here's how to do it cleanly. Think in vectors. Suppose the centre of the bottom circle has coordinates (x1,y1), and the centre of the top circle has coordinates (x2,y2). Then define two vectors support = (x1,y1) direction = (x2,y2) - (x1,y1) now, the line between the two centres is fully described by the parametric representation line = support + k*direction with k any value in (-inf,+inf). At the initial time, substituting k=1 in the equation above indeed give the coordinates of the top circle. On some later time t, the value of k will have increased, and substituting that new value of k in the equation will give the new coordinates of the centre of the top circle. How much k increases at value t is equal to the speed of the circle, and I leave that entirely up to you :) Doing it this way, you never need to mess around with any angles and/or coordinate transformations etc. It even works in 3D (provided you add in z-coordinates everywhere).
Ellipse fit to a point by altering one axis
A graphic of this problem is here: http://dl.dropbox.com/u/13390614/Question2.jpg Take an axis aligned ellipse with a fixed minor axis, and stretch the ellipse along its major axis till the ellipse's perimiter coincides with a point (A in the graphic). What is the new major axis length? I can solve this problem when both axis are to be modified, but am stumped when only one axis is modified. Any insights would be appreciated. Gary
First, let's pretend the ellipse is at the origin to simplify things. Imagine it was a circle where the diameter is your minor axis. What would be the width of the circle along the line where y = P's y? Equivalently, what is the x of the point on the circle's diameter where y = P's y. (There are two solutions to this. Either will do, though you may need to adjust a sign later on.) You can compute this using either trig or Pythagorean theorem. Your major axis is now minor axis * ((P's x) / x).
Many thanks for the help Laurence, that does seem to work. Gary // In code Since the ellipse and point are axis aligned, the point is a vector. Translate the point P onto a circle of minor axis radius using the fixed minor axis length and the Point's constant rise. double y = fabs( P.y ); double x = sqrt( semiMnrAxLen * semiMnrAxLen - y * y ); // Calc the new Semi Major Axis length. newSemiMajAxis = fabs( semiMnrAxLen * ( P.x / x ) );