Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

There are several ways to specify a line. In Mathematica's graphics, it is done by specifying two points and then connecting the points with the Line command. But it is sometimes desirable to parameterize a line, and the most familiar way is the form $y = m x + b$ where $m$ is the slope and $b$ is the offset. This parameterization is particularly awkward for near-vertical lines (where the slope becomes near infinite and numerically sensitive). An alternative is the $(\rho, \theta)$ parameterization which specifies the line as an angle $\theta$ and a distance $\rho$ from the origin, as shown in the theory section of this wikipedia article. The gist is that the line consists of all points $x$ and $y$ for which $\rho = x \cos(\theta) + y \sin(\theta)$, essentially a conversion to polar coordinates.

Here is a function which takes a pair of points {p1, p2} as input in the form {{x1,y1},{x2,y2}} and outputs the corresponding $\rho$ and $\theta$:

linePolar[{p1_, p2_}] := Module[{},
    {rx, ry} = Rationalize[p1]; {sx, sy} = Rationalize[p2];
    sol = Solve[{rx Cos[t] + ry Sin[t] == rho, 
             sx Cos[t] + sy Sin[t] == rho}, {rho, t}] // N;
    First[Select[{rho, t} //. sol //. C[1] -> 0, #[[1]] >= 0 &]]];

For example:

linePolar[{{0, -1}, {-1, 0}}]
{0.707107, -2.35619}

which shows that the line connecting the two points is Sqrt[2]/2 away from the origin at an angle of -135 degrees. The almost vertical line

linePolar[{{1, -1}, {1.1, 1}}]
{1.04869, -0.0499584}

has slope near zero, and the answer appears to be nicely behaved numerically as the line crosses vertical.

I have two questions. First, it bothers me that the function is essentially re-solving the system of equations each time it is called, and this must be inefficient. Is there a nice way to make it only do the solution once? Second, I would like to have an "inverse" for this function -- something that would take a pair of $(\rho, \theta)$ values and allow it to be plotted. This would be useful because many subsequent calculations need to occur in the $(\rho, \theta)$ space, and it would be good to be able to visualize this by plotting.

share|improve this question
    
To be clear are we talking about (infinite) lines, or line segments here? –  Mr.Wizard Aug 29 '13 at 1:52
    
The question is really about (full) lines, though of course we can only ever plot portions of them. This is why I enclosed inverse in scary quotes -- you can't really get back to the particular specified endpoints, only back to the line itself. –  bill s Aug 29 '13 at 1:53
    
No time to get into this today but it seems like a variation of a relatively common rectangular to polar conversion. I presume you've already looked at such things; wherein lies the difference and difficulty? –  Mr.Wizard Aug 29 '13 at 1:58
    
Here's a decent reference I believe: mathforum.org/dr.math/faq/formulas/faq.polar.html –  Mr.Wizard Aug 29 '13 at 2:01
1  
What is the actual problem you want to solve? It's not clear why you would prefer the polar parametrization over e.g. p1 + t*(p2-p1) where p1 and p2 are the given points in R^2 and t is the parameter. Is it that you have a particular need for the distance from the origin? –  Daniel Lichtblau Aug 29 '13 at 4:20
show 7 more comments

2 Answers

up vote 8 down vote accepted

The Cross product is very useful here. It can be used with 3D vectors to get the perpendicular distance of a line to the origin, but it also works with a 2D vector as an easy way to specify its perpendicular:

This function returns the radial distance $\rho$ and the angle $\theta$ as in the question, but doesn't require resolving the coordinates of the given points explicitly:

lineToPolar[{p1_, p2_}] := 
 {
  Abs[#], ArcTan @@ Cross[Sign[#] (p2 - p1)]} &@
  Last[Apply[Cross, PadRight[#, 3] & /@ {p1, Normalize[p1 - p2]}]]    

lineToPolar[{{0, -1}, {-1, 0}}]

(* ==> {1/Sqrt[2], -((3 Pi)/4)} *)

Simplify[
 lineToPolar[{{x, y}, {u, v}}], {x, y, u, v} ∈ Reals]

$$\begin{array}{c}\{\frac{\left| v x-u y\right|}{\sqrt{(u-x)^2+(v-y)^2}},\\\tan^{-1}\left(\frac{(v-y) \text{sgn}(v x-u y)}{\sqrt{\text{sgn}\left((u-x)^2+(v-y)^2\right)}},\frac{(u-x) \text{sgn}(u y-v x)}{\sqrt{\text{sgn}\left((u-x)^2+(v-y)^2\right)}}\right)\}\end{array}$$

This is the general formula for the polar representation. You could use this directly to define the conversion function if desired.

To optimize the conversion function some more, the component form of the given vectors could be used together with this form, where the Sign function has been turned into a case distinction:

FullSimplify[
  lineToPolar[{{x, y}, {u, v}}], {x, y, u, v} ∈ Reals] /. 
 Indeterminate -> 0

$$\{\frac{\left| v x-u y\right| }{\sqrt{(u-x)^2+(v-y)^2}}, \begin{cases} 0 & v x=u y \\ \tan ^{-1}(v-y,x-u) & u y<v x \\ \tan ^{-1}(y-v,u-x) & \text{True} \\ \end{cases} \}$$

Edit:

In an earlier version, the parameter $\rho$ was allowed to be negative if the order of p1 and p2 was reversed. That means $\rho$ is the 2D impact parameter of the line, but for a real polar coordinate you may want to require $\rho>0$. In the above code, I make sure that when the impact parameter is negative we exchange the order of p1 and p2 to get back to a positive $\rho$.

End edit

The inverse is even simpler:

Clear[ρ, θ]; 
lineFromPolar[ρ_, θ_] = 
 Function[{t}, {t, ρ}.#] &[RotationMatrix[Pi/2 - θ]]

With[{ρ = 1/2, θ = 3 Pi/4},
 ParametricPlot[lineFromPolar[ρ, θ][t], {t, -10, 10}]
 ]

plot

Here I used the fact that the offset from the origin is specified by a vector perpendicular to the line, of length $\rho$. This required orthogonality is provided automatically by the rows of RotationMatrix.

share|improve this answer
    
I have never checked that Cross works that way too and that ArcTan has this handy extension. And I should have! :) +1 –  Kuba Aug 29 '13 at 6:49
3  
A fine answer, however your lineFromPolar is not written efficiently. You should evaluate the body of the function: lineFromPolar2[ρ_, θ_] := Evaluate[{#, ρ}.RotationMatrix[-θ]] &. Timing for your method: lineFromPolar[1.4, 0.3] /@ RandomReal[9, 150000] // Timing // First is 0.936 seconds; and for mine is 0.01436 seconds. –  Mr.Wizard Aug 29 '13 at 8:02
    
@Mr.Wizard You're right, I didn't think about efficiency in the inverse. The version in my edit is even faster than yours now. –  Jens Aug 29 '13 at 15:40
    
@Jens -- thanks for looking at this. I think there is still a Pi (or maybe Pi/2) confusion somewhere. If we take lineToPolar[{{1, -1}, {1, 1}}] (note that this is a vertical line that connects these two points) then you get {-1, Pi}. But this is a horizontal line, as you can see by putting it into lineFromPolar. –  bill s Aug 29 '13 at 16:20
    
@bills Oh sorry, I used different definitions of $\theta$ for the two functions. What do you prefer: is $\theta$ the angle of the actual line with the horizontal? Or is it the angle of the normal to the line with the horizontal, as in the article you linked here? I have to change one or the other, but I'll wait until you let me know which you like better... –  Jens Aug 29 '13 at 16:54
show 5 more comments

Evaluating lineToPolar[{{1,-1},{1.1,1}}] gives a negative rho, and an angle t differing by $\pi$ from the OP example. Incorporating a simple Abs[rho] and keeping the same angle t gives a different line than I think is required. I used

lineToPolarAlt[{{x1_, y1_}, {x2_, y2_}}] :=
   With[{numerator = -y1 x2 + x1 y2},
        {Abs[(numerator)/Norm[{x1, y1} - {x2, y2}]], 
         If[numerator >= 0, Arg[ I x1 - y1 - I x2 + y2], 
                            Arg[-I x1 + y1 + I x2 - y2]]}]

to check the two different cases.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.