Drawing an equiangular spiral between two known points in Julia - math

I have two random points in a 2D Cartesian grid, p1 and p2. I would like to define a curve between p1 and p2 of N points such that the curve forms an equiangular spiral (similar to what is done in this paper (Fig. 8)). I've tried converting the paper into a script, but something is still off, so I'm trying to build a "dumbed down" example. My closest attempt is this (p2 can be seen on zoom-in, but not shown in script's plot):
using PyPlot
using LinearAlgebra
p1 = [5,7]
p2 = [1,2]
r = norm(p1-p2)
theta_offset = tan((p1[2]-p2[2])/(p1[1]-p2[1]));
# Number of points
rez = 500
# Number of revolutions
rev = 5
# Radius as spiral decreases
t = range(0,r,rez)
# Angle as spiral decreases
theta = range(0, 2*pi*rev, rez) .+ theta_offset
x = cos.(theta).*exp.(-t).+p2[1];
y = sin.(theta).*exp.(-t).+p2[2];
figure()
plot(x,y)
scatter(p1[1],p1[2],c="red",s=5)
scatter(p2[1],p2[2],c="red",s=10)
show(); gcf()
which produces the following plot:
While the plot is centered on p2 (at coordinate [1,2]), the endpoint does not lie near / pass through my specified point p1. My ideal outcome would be something like this:
EDIT: Solved problem using #PaSTE's suggestion. Changing my theta_offset, x, and y coordinate calculations to:
theta_offset = atan((p1[2]-p2[2])/(p1[1]-p2[1]));
x = cos.(theta).*exp.(-t).*r.+p2[1]
y = sin.(theta).*exp.(-t).*r.+p2[2]
yields the following plot, exactly what I was hoping for. In my solution, handedness and number of loops are not important.

As PaSTE said, The issue is the algebra: you need get the angle with atan, not tan, and multiply your exponential factor by r to get the right radius.
using LinearAlgebra
using Plots
p1 = [5,7]
p2 = [1,2]
r = norm(p1-p2)
theta_offset = atan((p1[2]-p2[2])/(p1[1]-p2[1]));
# Number of points
rez = 1500
# Number of revolutions
rev = 5
# Radius as spiral decreases
t = range(0,r,rez)
# Angle as spiral decreases
theta = range(0, 2*pi*rev, rez) .+ theta_offset
x = cos.(theta) .* r .* exp.(-t) .+ p2[1]
y = sin.(theta) .* r .* exp.(-t) .+ p2[2]
plot(x, y)
scatter!([p1[1], p2[1]], [p1[2], p2[2]])

Related

Connecting two points with a 3D logarithmic spiral (Julia)

I am calculating points along a three-dimensional logarithmic spiral between two points. I seem to be close, but I think I'm missing a conditional sign flip somewhere.
This code works relatively well:
using PlotlyJS
using LinearAlgebra
# Points to connect (`p2` spirals into `p1`)
p1 = [1,1,1]
p2 = [3,10,2]
# Number of curve revolutions
rev = 3
# Number of points defining the curve
rez = 500 # Number of points defining the line
r = norm(p1-p2)
t = range(0,r,rez)
theta_offset = atan((p1[2]-p2[2])/(p1[1]-p2[1]))
theta = range(0, 2*pi*rev, rez) .+ theta_offset
x = cos.(theta).*exp.(-t).*r.+p1[1];
y = sin.(theta).*exp.(-t).*r.+p1[2];
z = exp.(-t).*log.(r).+p1[3]
# Plot curve points
plot(scatter(x=x, y=y, z=z, marker=attr(size=2,color="red"),type="scatter3d"))
and produces the following plot. Values of the endpoints are shown on the plot, with an arrow from the coordinate to its respective marker. The first point is off, but it's close enough for my liking.
The problem comes when I flip p2 and p1 such that
p1 = [3,10,2]
p2 = [1,1,1]
In this case, I still get a spiral from p2 to p1, and the end point (p1) is highly accurate. However, the other endpoint (p2) is wildly off:
I think this is due to me changing the relative Z position of the two points, but I'm not sure, and I haven't been able to solve this riddle. Any help would be greatly appreciated. (Bonus points if you can help figure out why the Z value on p2 is off in the first example!)
Assuming this is a follow-up of your other question: Drawing an equiangular spiral between two known points in Julia
I assume you just want to add a third dimension to your previous 2D problem using cylindric coordinate system. This means that you need to separate the treatment of x and y coordinate on one side, and the z coordinate on the other side.
First you need to calculate your r on the first two coordinate:
r = norm(p1[1:2]-p2[1:2])
Then, when calculating z, you need to take only the third dimension in your formula (not sure why you used a log function there in the first place):
z = exp.(-t).*(p1[3]-p2[3]).+p2[3]
That will fix your z-axis.
Finally for your x and y coordinate, use the two argument atan function:
julia>?atan
help?> atan
atan(y)
atan(y, x)
Compute the inverse tangent of y or y/x, respectively.
For one argument, this is the angle in radians between the positive x-axis and the point (1, y), returning a value in the interval [-\pi/2, \pi/2].
For two arguments, this is the angle in radians between the positive x-axis and the point (x, y), returning a value in the interval [-\pi, \pi]. This corresponds to a standard atan2
(https://en.wikipedia.org/wiki/Atan2) function. Note that by convention atan(0.0,x) is defined as \pi and atan(-0.0,x) is defined as -\pi when x < 0.
like this:
theta_offset = atan( p1[2]-p2[2], p1[1]-p2[1] )
And finally, like in your previous question, add the p2 point instead of the p1 point at the end of x, y, and z:
x = cos.(theta).*exp.(-t).*r.+p2[1];
y = sin.(theta).*exp.(-t).*r.+p2[2];
z = exp.(-t).*(p1[3]-p2[3]).+p2[3]
In the end, I have this:
using PlotlyJS
using LinearAlgebra
# Points to connect (`p2` spirals into `p1`)
p2 = [1,1,1]
p1 = [3,10,2]
# Number of curve revolutions
rev = 3
# Number of points defining the curve
rez = 500 # Number of points defining the line
r = norm(p1[1:2]-p2[1:2])
t = range(0.,norm(p1-p2), length=rez)
theta_offset = atan( p1[2]-p2[2], p1[1]-p2[1] )
theta = range(0., 2*pi*rev, length=rez) .+ theta_offset
x = cos.(theta).*exp.(-t).*r.+p2[1];
y = sin.(theta).*exp.(-t).*r.+p2[2];
z = exp.(-t).*(p1[3]-p2[3]).+p2[3]
#show (x[begin], y[begin], z[begin])
#show (x[end], y[end], z[end]);
# Plot curve points
plot(scatter(x=x, y=y, z=z, marker=attr(size=2,color="red"),type="scatter3d"))
Which give the expected results:
p2 = [1,1,1]
p1 = [3,10,2]
(x[begin], y[begin], z[begin]) = (3.0, 10.0, 2.0)
(x[end], y[end], z[end]) = (1.0001877364735474, 1.0008448141309634, 1.0000938682367737)
and:
p1 = [1,1,1]
p2 = [3,10,2]
(x[begin], y[begin], z[begin]) = (0.9999999999999987, 1.0, 1.0)
(x[end], y[end], z[end]) = (2.9998122635264526, 9.999155185869036, 1.9999061317632263)
In 2D, let us assume the pole at the point C, and the spiral from P to Q, corresponding to a variation of the parameter in the interval [0, 1].
We have
X = Cx + cos(at+b).e^(ct+d)
Y = Cy + sin(at+b).e^(ct+d)
Using the known points,
Px - Cx = cos(b).e^d
Py - Cy = sin(b).e^d
Qx - Cx = cos(a+b).e^(c+d)
Qy - Cy = sin(a+b).e^(c+d)
From the first two, by a Cartesian to polar transformation (and logarithm), you can obtain b and d. From the last two, you similarly obtain a+b and c+d, and the spiral is now defined.
For the Z coordinate, I cannot answer precisely as you don't describe how you generalize the spiral to 3D. Anyway, we can assume a certain function Z(t), that you can map to [Pz, Qz] by the linear transformation
(Qz - Pz) . (Z(t) - Z(0)) / (Z(1) - Z(0)) + Pz.

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 to draw a boundary line on a scatter plot for classifier in Julia?

If I want to draw a boundary line to separate two classes which is the result of my classifier. How to draw it?
The picture is the sample, the black line is the boundary I want to draw.
the green points is the boundary points. I want to draw a curve perfectly fit those points. But when I plot those curve, the result is the purple line which is not a curve.
Here is a reproducible example how to do it:
using Plots
x = rand(1000)
y = rand(1000)
color = [3 * (b-0.5)^2 < a - 0.1 ? "red" : "blue" for (a, b) in zip(x, y)]
y_bound = 0:0.01:1
x_bound = #. 3 * (y_bound - 0.5)^2 + 0.1
scatter(x, y, color=color, legend=false)
plot!(x_bound, y_bound, color="green")
and you should get a plot like:
The crucial thing here is to make your boundary points ordered (i.e. they must be ordered in the vectors properly so that when you plot a line you connect proper points). In my example I achieved it by varying the y-dimension and calculating the x dimension.
In more complex cases it will be better to use contour plot, e.g.:
x = 1:0.1:8
y = 1:0.1:7
f(x, y) = begin
(3x + y ^ 2) * abs(sin(x) + cos(y)) - 40
end
X = repeat(reshape(x, 1, :), length(y), 1)
Y = repeat(y, 1, length(x))
Z = map(f, X, Y)
contour(x, y, Z, levels=[0], color="green", width=3)
x_s = 7 .* rand(1000) .+ 1
y_s = 6 .* rand(1000) .+ 1
color = [f(a, b) > 0 ? "red" : "blue" for (a, b) in zip(x_s, y_s)]
scatter!(x_s, y_s, color=color, legend=false)
and you should get something like:
However, as you can see this time for the best results it is best to pass scores to contour and specify the classification threshold as level.
I guess your TA asked you to conduct a grid search for this question.
The meaning of grid search is not searching over the data point you have, but searching over whole coordinate. (I.e. From (0,0), (0,1), (0,2) to (0,100), then to (1,0), (1,1) and so on.) You may change the distance between each point when you conduct a grid search.
In your case, you need to solve the equation d_1(X) = d_2(X). So what you need to do is to simulate some points (like the above example), then put those points into |d_1(X) - d_2(X)|, and pick the points that bring you to a value that is smaller than epsilon (a self-given small number like 0.05 or 0.1). Then use Plot() to connect them.
This is not the most efficient way to create the boundary but this is what you learnt in your tutorial. You may also try contour().

Uniform sampling of intersection area of two disks

Given 2D uniform variable we can generate a uniform distribution in a unit-disk as discussed here.
My problem is similar in that i wish to uniformly sample the intersection area of two intersecting disks where one disk is always the unit-disk and the other can be freely moved and resized like here
I was trying to split the area into two regions (as depicted above) and sample each region individual based on the respected disk. My approach is based on uniform disk algorithm cited above. To sample the first region right of the center line I would restrict theta to be within the two intersection points. Next r would need to be projected based on that theta
such that the points are pushed in the area between our mid line and the radius of the disk. The python sample code can be found here.
u = unifrom2D()
A;B; // Intersection points
for p in allPoints
theta = u.x * (getTheta(A) - getTheta(B)) + getTheta(B)
r = sqrt(u.y + (1- u.y)*length2(lineIntersection(theta)))
p = (r * cos(theta), r * sin(theta))
However this approach is rather expensive and further fails to preserve uniformity. Just to clarify i do not want to use rejection sampling.
I am not sure if this is better than rejection sampling, but here is a solution for uniform sampling of a circle segment (with center angle <= pi) involving the numerical computation of an inverse function. (The uniform sampling of the intersection of two circles can then be composed of the sampling of segments, sectors and triangles - depending on how the intersection can be split into simpler figures.)
First we need to know how to generate a random value Z with given distribution F, i.e. we want
P(Z < x) = F(x) <=> (x = F^-1(y))
P(Z < F^-1(y)) = F(F^-1(y)) = y <=> (F is monotonous)
P(F(Z) < y) = y
This means: if Z has the requested distribution F, then F(Z) is distributed uniformly. The other way round:
Z = F^-1(Y),
where Y is distributed uniformly in [0,1], has the requested distribution.
If F is of the form
/ 0, x < a
F(x) = | (F0(x)-F0(a)) / (F0(b)-F0(a)), a <= x <= b
\ 1, b < x
then we can choose a Y0 uniformly in [F(a),F(b)] and set Z = F0^-1(Y0).
We choose to parametrize the segment by (theta,r), where the center angle theta is measured from one segment side. When the segment's center angle is alpha, the area of the segment intersected with a sector of angle theta starting where the segment starts is (for the unit circle, theta in [0,alpha/2])
F0_theta(theta) = 0.5*(theta - d*(s - d*tan(alpha/2-theta)))
where s = AB/2 = sin(alpha/2) and d = dist(M,AB) = cos(alpha/2) (the distance of the circle center to the segment). (The case alpha/2 <= theta <= alpha is symmetric and not considered here.)
We need a random theta with P(theta < x) = F_theta(x). The inverse of F_theta cannot be computed symbolically - it must be determined by some optimization algorithm (e.g. Newton-Raphson).
Once theta is fixed we need a random radius r in the range
[r_min, 1], r_min = d/cos(alpha/2-theta).
For x in [0, 1-r_min] the distribution must be
F0_r(x) = (x+r_min)^2 - r_min^2 = x^2 + 2*x*r_min.
Here the inverse can be computed symbolically:
F0_r^-1(y) = -r_min + sqrt(r_min^2+y)
Here is an implementation in Python for proof of concept:
from math import sin,cos,tan,sqrt
from scipy.optimize import newton
# area of segment of unit circle
# alpha: center angle of segment (0 <= alpha <= pi)
def segmentArea(alpha):
return 0.5*(alpha - sin(alpha))
# generate a function that gives the area of a segment of a unit circle
# intersected with a sector of given angle, where the sector starts at one end of the segment.
# The returned function is valid for [0,alpha/2].
# For theta=alpha/2 the returned function gives half of the segment area.
# alpha: center angle of segment (0 <= alpha <= pi)
def segmentAreaByAngle_gen(alpha):
alpha_2 = 0.5*alpha
s,d = sin(alpha_2),cos(alpha_2)
return lambda theta: 0.5*(theta - d*(s - d*tan(alpha_2-theta)))
# generate derivative function generated by segmentAreaByAngle_gen
def segmentAreaByAngleDeriv_gen(alpha):
alpha_2 = 0.5*alpha
d = cos(alpha_2)
return lambda theta: (lambda dr = d/cos(alpha_2-theta): 0.5*(1 - dr*dr))()
# generate inverse of function generated by segmentAreaByAngle_gen
def segmentAreaByAngleInv_gen(alpha):
x0 = sqrt(0.5*segmentArea(alpha)) # initial guess by approximating half of segment with right-angled triangle
return lambda area: newton(lambda theta: segmentAreaByAngle_gen(alpha)(theta) - area, x0, segmentAreaByAngleDeriv_gen(alpha))
# for a segment of the unit circle in canonical position
# (i.e. symmetric to x-axis, on positive side of x-axis)
# generate uniformly distributed random point in upper half
def randomPointInSegmentHalf(alpha):
FInv = segmentAreaByAngleInv_gen(alpha)
areaRandom = random.uniform(0,0.5*segmentArea(alpha))
thetaRandom = FInv(areaRandom)
alpha_2 = 0.5*alpha
d = cos(alpha_2)
rMin = d/cos(alpha_2-thetaRandom)
secAreaRandom = random.uniform(0, 1-rMin*rMin)
rRandom = sqrt(rMin*rMin + secAreaRandom)
return rRandom*cos(alpha_2-thetaRandom), rRandom*sin(alpha_2-thetaRandom)
The visualisation seems to verify uniform distribution (of the upper half of a segment with center angle pi/2):
import matplotlib.pyplot as plot
segmentPoints = [randomPointInSegmentHalf(pi/2) for _ in range(500)]
plot.scatter(*zip(*segmentPoints))
plot.show()

3D Line - Plane intersection?

I am having two Vectors (X,Y,Z), one above Y=0 and one below Y=0.
I want to find the Vector (X,Y,Z) where the line between the two original vectors intersects with the Y=0 level.
How do I do that?
Example Point A:
X = -43.54235
Y = 95.2679138
Z = -98.2120361
Example Point B:
X = -43.54235
Y = 97.23531
Z = -96.24464
These points read from two UnProjections from a users click and I'm trying to target the unprojection to Y=0.
(I found 3D line plane intersection, with simple plane but didn't understand the accepted answer as it's for 2D)
I suspect that by two vectors, you really mean two points, and want to intersect the line connecting those two points with the plane defined by Y=0.
If that's the case, then you could use the definition of a line between two points:
<A + (D - A)*u, B + (E - B)*u, C + (F - C)*u>
Where <A,B,C> is one of your points and <D,E,F> is the other point. u is an undefined scalar that is used to calculate the points along this line.
Since you're intersecting this line with the plane Y=0, you simply need to find the point on the line where the "Y" segment is 0.
Specifically, solve for u in B + (E - B)*u = 0, and then feed that back into the original line equation to find the X and Z components.
The equation for the line is
(x–x1)/(x2–x1) = (y–y1)/(y2–y1) = (z–z1)/(z2–z1)
So making y=0 yields your coordinates for the intersection.
x = -y1 * (x2-x1)/(y2-y1) + x1
and
z = -y1 * (z2-z1) /(y2-y1) + z1

Resources