I would like to know whether there exists in Julia something which would be rougly equivalent to the rgl package in R language; i.e., a library allowing for a dynamic/interactive representation of 3D plots, 3D surfaces, etc.
A bit of context: if you're studying, for example, morphometrics, you regularly end up with files in PLY format, or another format produced by 3D scanners. In R, you can for instance visualize (in an interactive way) easily a 3D surface acquired by such a scanner (here, a set of molars):
Do we have currently a similar feature in Julia? If so, which library should I use?
Thanks!
Makie.jl, specifically via either the GLMakie.jl or WebGLMakie.jl backends, is a good option for interactive plots. For instance, the following example from the BeautifulMakie gallery
using GLMakie
let
x = y = LinRange(-2, 2, 51)
z = (-x .* exp.(-x .^ 2 .- (y') .^ 2)) .* 4
zmin, zmax = minimum(z), maximum(z)
cmap = :viridis
fig = Figure(resolution = (900,900))
ax = Axis3(fig, aspect = :data, perspectiveness = 0.5, elevation = π/9,
xzpanelcolor= (:black, 0.75), yzpanelcolor= (:black,0.75),
zgridcolor = :grey, ygridcolor = :grey,xgridcolor = :grey)
surface!(ax, x, y, z, colormap = cmap, colorrange = (zmin, zmax))
xm, ym, zm = minimum(ax.finallimits[])
contour!(ax, x, y, z, levels = 20, colormap = cmap, linewidth = 2,
colorrange=(zmin, zmax), transformation = (:xy, zm))
wireframe!(ax, x, y, z, overdraw = true, transparency = true,
color = (:black, 0.1))
fig[1,1] = ax
fig
end
opens an interactive window that can be rotated at will with the cursor.
I am not familiar with the PLY format however, so cannot comment on that aspect of the question.
Related
I am able to plot a 3D surface plot with a 2D contour plot just fine
but I would also like to share the x axis of the 2D plot using ax.twinx() like I've done in a separate plot here:
However, when I add this to the ax including the 3D contour plot (e.g."ax2 = ax.twinx()"), I get an error: AttributeError: 'YAxis' object has no attribute 'tick_left'. Do you have any ideas for a workaround for this? Thank you. Here are the relevant parts of my code.
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(projection='3d')
X,Y = np.meshgrid(time_Raw1[t1:t],list(reversed(range(100))))
ax.plot_surface(X,Y, csd_matrix,cmap =cm.seismic, alpha = 0.5)
ax.contourf(X, Y, csd_matrix, offset=np.min(csd_matrix), levels=levels, cmap=cmap)
ax2 = ax.twinx()
ax2.plot(time_Raw1[t1:t],channel_data, color='k', clip_on=False)
plt.show()
I've figured it out, here are the relevant bits of code:
fig = plt.figure(figsize = (10,10))
ax = fig.add_subplot(projection='3d')
X,Y = np.meshgrid(time_Raw1[t1:t],list(reversed(range(100))))
ax.plot_surface(X,Y, csd_matrix,cmap =cm.seismic, alpha = 0.5)
ax.contourf(X, Y, csd_matrix, offset=np.min(csd_matrix), levels=levels, cmap=cmap)
ax2 = fig.add_subplot(projection='3d',sharex=ax)
Z = np.full((102),np.min(csd_matrix))
ax2.plot3D(time_Raw1[t1:t],channel_data,Z, color='k', clip_on=False)
plt.show()
I want to plot 3D HeatMap for 3D function f(x,y,z).
For 2D function f(x,y), I know the below code works.
using Plots
x = 1:L # coordinate range
y = 1:L
F = Float64[f(ix,iy) for ix in x, iy in y]' #convert f(x,y) to an array
plot(F,st=:heatmap,color= cgrad(:blues))
plot!(xlabel="x",ylabel="y",aspect_ratio=:equal)
plot!(xlims=(1,L),ylims=(1,L))
For 3D function, where should I change?
using Plots
x = 1:L # coordinate range
y = 1:L
z = 1:L
F = Float64[f(ix,iy,iz) for ix in x, iy in y,iz in z] #convert f(x,y,z) to an array
plot(F,st=:heatmap,color = cgrad(:blues),alpha=0.1)
plot!(xlabel="x",ylabel="y",zlabel="z",aspect_ratio=:equal)
plot!(xlims=(1,L),ylims=(1,L),zlims=(1,L))
This code passes, but something is wrong.
color = cgrad(:blues),alpha=0.1,xlabel="x",ylabel="y" are not reflected.
In addition, the figure does not seem to be f(x,y,z). For example, f(x,y,z) = x^2 + y^2 +z^2 gives a spherical gradation, but the result is not.
The above approach is slow for more data points. However, I think you don't want heatmaps as the heatmaps in the previous link are just projections from 2D into 3D planes.
I think you need something like this.
See code here.
https://lazarusa.github.io/BeautifulMakie/surfWireLines/volume/
See image
And for convenience also here:
using GLMakie
let
x = 1:10
y = 1:10
z = 1:10
f(x,y,z) = x^2 + y^2 + z^2
vol = [f(ix,iy,iz) for ix in x, iy in y, iz in z]
fig, ax, _ = volume(x, y, z, vol, colormap = :plasma,colorrange = (minimum(vol), maximum(vol)),
figure = (; resolution = (800,800)),
axis=(; type=Axis3, perspectiveness = 0.5, azimuth = 7.19, elevation = 0.57,
aspect = (1,1,1)))
fig
end
3D HeatMap by Makie.jl
I don't know how to plot 3D HeatMap by Plots.jl yet, but I found the another way by Makie.jl : https://lazarusa.github.io/BeautifulMakie/surfWireLines/RGBcube/ .
With the help of this sample code, I got the following code.
using GLMakie, GeometryBasics, Colors
positions = vec([(i, j, k) for i=1:L,j=1:L,k=1:L]) #3D coordinate
F = zeros(Float64,length(positions)
for i = 1:length(positions) #convert f(x,y,z) to an array
x = positions[i][1]
y = positions[i][2]
z = positions[i][3]
F[i] = f(x,y,z)
end
fig, ax = mesh(HyperRectangle(Vec3f0(positions[1]...),Vec3f0(0.8)), color = RGBA(0,0,F[1],0.5), transparency = false) #HyperRectangle(::position,::length),color=(::red,::green,::blue,::alpha)
wireframe!(ax,HyperRectangle(Vec3f0(positions[1]...), Vec3f0(0.8)), linewidth = 0.1, overdraw = false)
for i in 2:length(positions)
mesh!(ax, HyperRectangle(Vec3f0(positions[i]...), Vec3f0(0.8)), color = RGBA(0,0,F[i],0.5))
wireframe!(ax, HyperRectangle(Vec3f0(positions[i]...), Vec3f0(0.8)), linewidth = 0.1, overdraw = false)
end
fig
I plotted a 3d scatter plot in R using the scatter3d function.
Now, I want to plot the labels on every dot in the 3d scatter, such as every point has its ID next to it i.e., "1", "2" etc..
Here is what I tried:
library("car")
library("rgl")
scatter3d(geometry[,1],geometry[,2],geometry[,3] , surface=FALSE, labels = rownames(geometry), id.n=nrow(geometry))
This tutorial says that adding arguments labels=rownames(geometry), id.n=nrow(geometry) should display the labels on every dot but that did not work.
EDIT:
I uploaded the coordinate file here, you can read it like this
geometry = read.csv("geometry.txt",sep = " ")
colnames(geometry) = c("x","y","z")
EDIT:
Actually, even the example from the tutorial does not label the points and does not produce the plot displayed. There is probably something wrong with the package.
scatter3d(x = sep.l, y = pet.l, z = sep.w,
surface=FALSE, labels = rownames(iris), id.n=nrow(iris))
I can give you a quick fix if you want to use any other function other than scatter3d. This can be achieved using plot3d and text3d function. I have provided the basic code block of how it can be implemented. You can customize it to your needs.
plot3d(geometry[,1],geometry[,2],geometry[,3])
text3d(geometry[,1],geometry[,2],geometry[,3],rownames(geometry))
points3d(geometry[,1],geometry[,2],geometry[,3], size = 5)
After much messing around I got it (I also have the method for plot_ly if you,re interested)
test2 <- cbind(dataSet[,paste(d)],set.final$Groups,test)
X <- test2[,1]
Y <- test2[,2]
Z <- test2[,3]
# 3D plot with the regression plane
scatter3d(x = X, y = Y, z = Z, groups = test2$`set.final$Groups`,
grid = FALSE, fit = "linear",ellipsoid = FALSE, surface=FALSE,
surface.col = c("green", "blue", "red"),
#showLabels(x = x, y = y, z = z, labels=test2$test, method="identify",n = nrow(test2), cex=1, col=carPalette()[1], location=c("lr"))
#labels = test2$test,
id=list(method = "mahal", n = length(test2$test), labels = test2$test)
#id.n=nrow(test2$test)
)
#identify3d(x = X, y = Y, z = Z, labels = test2$test, n = length(test2$test), plot = TRUE, adj = c(-0.1, 0.5), tolerance = 20, buttons = c("right"))
rglwidget()
I've plotted up a series of points using the rgl package in R. I've plotted them in two dimensions for simplicity, but the issue still exists in three dimensions. The code snippet and plot below show a basic line of points plotted in the xy-plane:
library(rgl)
seq <- seq(1, 10, by = 0.1)
df <- data.frame(x = seq, y = seq / 10)
clear3d("all")
bg3d(color = "white")
points3d(x = df$x, y = df$y, z = 0)
axes3d()
rgl.viewpoint(theta = 0, phi = 0)
The points plot as expected. However, if I take these same points and translate them by a significant amount, the graphics device does not seem to be able to handle the points:
library(rgl)
seq <- seq(1, 10, by = 0.1)
df <- data.frame(x = seq, y = seq / 10)
# Translate points
df <- df + 1000000
clear3d("all")
bg3d(color = "white")
points3d(x = df$x, y = df$y, z = 0)
axes3d()
rgl.viewpoint(theta = 0, phi = 0)
Is this a known limitation? Is the problem with OpenGL, or with the package? I'm working with some points and surfaces that have an associated coordinate system, so I'd prefer not to translate my data back to the origin.
#derhass had the right idea. From the rgl manual:
Note that many of these calculations are done on the graphics card using single precision; you will likely see signs of rounding error if your scene requires more than 4 or 5 digit precision to distinguish values in any coordinate.
I want to plot a matrix of z values with x rows and y columns as a surface similar to this graph from MATLAB.
Surface plot:
Code to generate matrix:
# Parameters
shape<-1.849241
scale<-38.87986
x<-seq(from = -241.440, to = 241.440, by = 0.240)# 2013 length
y<-seq(from = -241.440, to = 241.440, by = 0.240)
matrix_fun<-matrix(data = 0, nrow = length(x), ncol = length(y))
# Generate two dimensional travel distance probability density function
for (i in 1:length(x)) {
for (j in 1:length(y)){
dxy<-sqrt(x[i]^2+y[j]^2)
prob<-1/(scale^(shape)*gamma(shape))*dxy^(shape-1)*exp(-(dxy/scale))
matrix_fun[i,j]<-prob
}}
# Rescale 2-d pdf to sum to 1
a<-sum(matrix_fun)
matrix_scale<-matrix_fun/a
I am able to generate surface plots using a couple methods (persp(), persp3d(), surface3d()) but the colors aren't displaying the z values (the probabilities held within the matrix). The z values only seem to display as heights not as differentiated colors as in the MATLAB figure.
Example of graph code and graphs:
library(rgl)
persp3d(x=x, y=y, z=matrix_scale, color=rainbow(25, start=min(matrix_scale), end=max(matrix_scale)))
surface3d(x=x, y=y, z=matrix_scale, color=rainbow(25, start=min(matrix_scale), end=max(matrix_scale)))
persp(x=x, y=y, z=matrix_scale, theta=30, phi=30, col=rainbow(25, start=min(matrix_scale), end=max(matrix_scale)), border=NA)
Image of the last graph
Any other tips to recreate the image in R would be most appreciated (i.e. legend bar, axis tick marks, etc.)
So here's a ggplot solution which seems to come a little bit closer to the MATLAB plot
# Parameters
shape<-1.849241
scale<-38.87986
x<-seq(from = -241.440, to = 241.440, by = 2.40)
y<-seq(from = -241.440, to = 241.440, by = 2.40)
df <- expand.grid(x=x,y=y)
df$dxy <- with(df,sqrt(x^2+y^2))
df$prob <- dgamma(df$dxy,shape=shape,scale=scale)
df$prob <- df$prob/sum(df$prob)
library(ggplot2)
library(colorRamps) # for matlab.like(...)
library(scales) # for labels=scientific
ggplot(df, aes(x,y))+
geom_tile(aes(fill=prob))+
scale_fill_gradientn(colours=matlab.like(10), labels=scientific)
BTW: You can generate your data frame of probabilities much more efficiently using the built-in dgamma(...) function, rather than calculating it yourself.
In line with alexis_laz's comment, here is an example using filled.contour. You might want to increase your by to 2.40 since the finer granularity increases the time it takes to generate the plot by a lot but doesn't improve quality.
filled.contour(x = x, y = y, z = matrix_scale, color = terrain.colors)
# terrain.colors is in the base grDevices package
If you want something closer to your color scheme above, you can fiddle with the rainbow function:
filled.contour(x = x, y = y, z = matrix_scale,
color = (function(n, ...) rep(rev(rainbow(n/2, ...)[1:9]), each = 3)))
Finer granularity:
filled.contour(x = x, y = y, z = matrix_scale, nlevels = 150,
color = (function(n, ...)
rev(rep(rainbow(50, start = 0, end = 0.75, ...), each = 3))[5:150]))