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)
Related
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]])
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")
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?
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))
Is there a way to draw a simple ellipse based on the following definition (instead of eigenvalue) in R?
The definition I want to use is that an ellipse is the set of points in a plane for which the sum of the distances to two fixed points F1 and F2 is a constant.
Should I just use a polar cordinate?
This may be more algorithmic question.
As #DWin suggested, there are several implementations for plotting ellipses (such as function draw.ellipse in package plotrix). To find them:
RSiteSearch("ellipse", restrict="functions")
That being said, implementing your own function is fairly simple if you know a little geometry. Here is an attempt:
ellipse <- function(xf1, yf1, xf2, yf2, k, new=TRUE,...){
# xf1 and yf1 are the coordinates of your focus F1
# xf2 and yf2 are the coordinates of your focus F2
# k is your constant (sum of distances to F1 and F2 of any points on the ellipse)
# new is a logical saying if the function needs to create a new plot or add an ellipse to an existing plot.
# ... is any arguments you can pass to functions plot or lines (col, lwd, lty, etc.)
t <- seq(0, 2*pi, by=pi/100) # Change the by parameters to change resolution
k/2 -> a # Major axis
xc <- (xf1+xf2)/2
yc <- (yf1+yf2)/2 # Coordinates of the center
dc <- sqrt((xf1-xf2)^2 + (yf1-yf2)^2)/2 # Distance of the foci to the center
b <- sqrt(a^2 - dc^2) # Minor axis
phi <- atan(abs(yf1-yf2)/abs(xf1-xf2)) # Angle between the major axis and the x-axis
xt <- xc + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
yt <- yc + a*cos(t)*sin(phi) + b*sin(t)*cos(phi)
if(new){ plot(xt,yt,type="l",...) }
if(!new){ lines(xt,yt,...) }
}
An example:
F1 <- c(2,3)
F2 <- c(1,2)
plot(rbind(F1, F2), xlim=c(-1,5), ylim=c(-1, 5), pch=19)
abline(h=0, v=0, col="grey90")
ellipse(F1[1], F1[2], F2[1], F2[2], k=2, new=FALSE, col="red", lwd=2)
points((F1[1]+F2[1])/2, (F1[2]+F2[2])/2, pch=3)