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

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.

Related

Drawing an equiangular spiral between two known points in Julia

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]])

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().

drawing the tangent to the curve knowing the point and gradient

I writing a computer program to back up my knowledge of calculus. You can see the web page here
The next thing I want to do is display a tangent to the curve when the user hovers the mouse over the curve.
When that happens, I know exactly the coordinates of the mouse and I can get the derivative which in this case is 2x -2 so if the point is at (1, 1) then the gradient would be 0.
If I was drawing this with pen and paper then I would rearrange the equation into y2-y1 = m(x2 -x1).
I am not entirely sure how to do this with code though.
I tried getting the y intercept and x intercept but the tangent looked wrong:
function getYIntercept(vertex, slope) {
return vertex.y - (slope * vertex.x);
}
const yIntercept = getYIntercept(point, gradient);
const xIntercept = - yIntercept / (gradient);
g.append('line')
.style('stroke', 'red')
.attr('class', 'tangent')
.attr('x1', xScale(point.x))
.attr('y1', yScale(point.y))
.attr('x2', xScale(xIntercept))
.attr('y2', yScale(yIntercept));
};
How better can I plot this line with the information I have?
Finding the Tangent
Let us start with a function f(x).
Calculate f '(x) (the derivative) for future reference.
Then the user indicates some point (x1, y1).
Using f '(x), the slope at this point is m = f '(x1).
Utilizing the Point-Slope formula, the equation for tangent is y-y1 = m(x-x1)
Solve for y:
y = m(x-x1)+y1
Finding the Intercepts
For the x and y intercepts [denoted here as x0 and y0 respectively], simply use the tangent equation. It may be useful to note that the intercepts are (x0,0) and (0,y0) so plugging in zero for the correct variable allows you to find a intercept.
Find the y intercept, so x=0
Thus y = m(0-x1)+y1
Distributing the m leaves y = -m*x1+y1
So y0 = -m*x1+y1 and the y intercept is ( 0, -m*x1+y1 )
This is all that is needed to graph the tangent. But in case you're are curious about the x intercept as well.
Find the x intercept, so y=0
Thus 0 = m(x-x1)+y1
Distributing the m leaves 0 = m*x - m*x1 + y1
Subtracting the x1 and y1 terms yields m*x1-y1 = m*x
Now divide by m so that [ m*x1-y1 ]/m = x
So x0 = [ m*x1-y1 ]/m and the x intercept is ( [ m*x1-y1 ]/m, 0 )
Specifics for this Case
Here are some issues:
(1, 1) is not a point on the function f(x) = x^2 - 2*x + 1
To solve this, you could simply use only the x-value of the point the user hovers over
Alternatively, you could consider graphing the slope field
The x intercept and y intercept are two distinct points, not the x and y value of one point
Once these issues are resolved, you will be able to properly graph the tangent of any function for which you know the first derivative!

Simulating movement in 2D

I need assistance in simulating movement between 2 points in a plane. Consider two points P1:(x,y1) and P2:(x2,y2). I compute the distance between P1 and P2, say D, and I choose a random velocity, say V. Next, I compute the time required to move from P1 to P2, say T. Finally, I compute the equation of the straight line between P1 and P2 as y = mx + b.
For example, let T = 10 seconds. For the first 9 seconds, I would like to generate points per second on the straight line until I reach point P2 at the 10th second. Could you please assist me in doing so.
The best approach is to use parametric equations
x = x1 + t*(x2 - x1)
y = y1 + t*(y2 - y1)
where t is the "time" parameter going from 0 to 1 (0.5 means for example halfway).
If you also like your movement to be "soft" (starting from zero velocity, then accelerating then slowing down and stopping on the arrival point) you can use this modified equation
w = 3*t*t - 2*t*t*t
x = x1 + w*(x2 - x1)
y = y1 + w*(y2 - y1)
The following is a plot of the w curve compared to a linear distribution t with 11 points (t=0.0, 0.1, ... 0.9, 1.0):

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