Plotting a set of parametric equations with several parameters in Julia - plot

To plot a circle of radius 2 centered at (1, 1) I do the following:
θ = 0:0.1:2π
x = 1 .+ 2cos.(θ)
y = 1 .+ 2sin.(θ)
plot(x, y, aspect_ratio=:equal)
However, if I want to plot a set of parametric equations with more than two parameters I cannot use this approach. How should one approach plotting parametric equations with more than one parameter in Julia? For instance, how can I plot the cone described by the parametric equations
x = r*cos(θ)
y = r*sin(θ)
z = r
where r and θ are the parameters?
I am imagining the final plot to look like in the below image that has been generated by entering ParametricPlot3D[{r*Cos[t], r*Sin[t], r}, {r, -3, 3}, {t, 0, 2*Pi}] in Mathematica.

This works with the plotly and pyplot backends to Plots but not gr:
X(r,theta) = r * cos(theta)
Y(r,theta) = r * sin(theta)
Z(r,theta) = r
rs = range(0, 2, length=50)
ts = range(0, 2pi, length=50)
surface(X.(rs',ts), Y.(rs', ts), Z.(rs', ts))

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

Simultaneous Contour and 3d Plot

I'm trying to simultaneously plot the contour lines and the surface of a function in Julia, but I can't seem to find a way to that.
Is there an easy way to achieve this?
Here is an example I worked out for Plots. The PyPlot package has something better, and something could be done with Makie:
import Contour: contours, levels, level, lines, coordinates
function surface_contour(xs, ys, f; offset=0)
p = surface(xs, ys, f, legend=false, fillalpha=0.5)
## we add to the graphic p, then plot
zs = [f(x,y) for x in xs, y in ys] # reverse order for use with Contour package
for cl in levels(contours(xs, ys, zs))
lvl = level(cl) # the z-value of this contour level
for line in lines(cl)
_xs, _ys = coordinates(line) # coordinates of this line segment
_zs = offset .+ 0 .* _xs
plot!(p, _xs, _ys, _zs, alpha=0.5) # add curve on x-y plane
end
end
p
end
xs = ys = range(-pi, stop=pi, length=100)
f(x,y) = 2 + sin(x) - cos(y)
surface_contour(xs, ys, f)

Draw a function given by a system of equations involving an implicit function

I need to draw a graph of a function like this:
What is the most elegant way to do it?
You could use the substitution x = cos(t) to parameterise your set of equations for t ∈ [0, 2π) as x = cos(t), y = sin(t), z = 2x.
In Julia, you can use Plots.jl to plot this as follows:
using Plots
t = 0:0.01:2*pi
x = cos.(t)
y = sin.(t)
z = 2 .* x
plot(x, y, z)
A slightly neater trick is to use parametric plotting, where we can just pass functions (x(t), y(t), z(t)) and specify the range of the parameter t:
plot(cos, sin, x -> 2 * cos(x), 0, 2π, xlabel="x", ylabel="y", zlabel="z", label="f")

Contour plot of an extrapolated anonymous function in Julia

I created a 64x64 random 2D array in Julia, and then interpolated it using Interpolations.jl:
using Interpolations
using Plots
gr()
x = range(-10., length=64, stop=10.)
y = range(-10., length=64, stop=10.)
v_unscaled = interpolate(potential, BSpline(Cubic(Line(OnCell()))))
v_scaled = Interpolations.scale(v_unscaled, x, y)
v = extrapolate(v_scaled, 0)
p_int = contour(x, y, v_scaled, fill=true)
display(p_int)
p = contour(x, y, v, fill=true)
display(p)
When I plot the extrapolated function v, the non-zero part of the contour is shrunk to the lower left corner which is not as expected, because in the specified domain, the two plots should look the same. What is wrong with the procedures above?

Producing a linear distribution for a "donut" shape

I am trying to produce a distribution for points within a circle where there are more towards the center, but for a donut shape
I can produce a uniformly distributed donut shape (1), or a circle where there are more points towards the center (2) but not a donut shape that has so many points towards its inside boundary.
(1)
r = sqrt(runif(250, min = 0.25, max =1))
p = runif(250)
theta = p *2*pi
a = r * cos(theta) #coordinates for circle
b = r * sin(theta)
plot(a,b)
(2)
r = runif(250)
p = runif(250)
theta = p *2*pi
a = r * cos(theta)
b = r * sin(theta)
plot(a,b)
My closest attempt is modifying program (2) where r is bound between 0.5 and 1 but this removes most of the points closest to the center and does not have as many around its inside.
As #RobertDodier said, try to use some advanced distribution for radius. What I would like to propose is Beta distribution.
First, it is naturally in the [0...1] range, no need to truncate, accept/reject etc.
Second, it has two parameters (a,b) which could be used to get single peak, zero at 0, and slope to 1. Look at graphs in the wiki page. And last, it is implemented in R. a being smaller than b means peak is on the lef of 0.5, a being larger than b means peak is on the right of 0.5.
Along the lines
N = 10000
r = rbeta(N, 7.0, 5.0)
theta = 2.0*pi*runif(N)
a = r * cos(theta)
b = r * sin(theta)
plot(a,b)
will produce plot like that
Does it look like at donut?
UPDATE
This one is with clear hole at the center and shape proposed by #RobertDodier
N = 10000
hole = 0.25
r = hole + (1.0-hole)*rbeta(N, 1.0, 3.0)
theta = 2.0*pi*runif(N)
a = r * cos(theta)
b = r * sin(theta)
plot(a,b)
Another one with clear hole in the center and symmetric shape, like a true donut
r = hole + (1.0-hole)*rbeta(N, 2.0, 2.0)

Resources