I would need to somehow make an interactive visualization of a 3D box contain many 3D boxes inside it.
My first crude ideas were as following. In 2D I could use
x<-matrix(runif(25),nrow=5,ncol=5);
image(x)
to color every single cell in the matrix to make it look like the big rectangle would contain small rectangles in it.
How could this be translated into 3D? Let's say in 3D the big box is of size 10x10x10. In practice I would like to choose the color of every single one of the 1000 elements in the box.I know that rgl can be used to make interactive 3D plots, but I'm having issues understanding how to color every single element in the 3D array.
If you have some suggestions for some better solutions I would gladly like to hear them.
If I understood correctly, I believe that this should work:
library(rgl)
grd <- expand.grid(x=seq(0,10,2), y=seq(0,10,2), z=seq(0,10,2))
grd$dist <- sqrt(grd$x^2 + grd$y^2 + grd$z^2) # distance to coordinate 0,0,0
grd$col <- rainbow(ceiling(max(grd$dist+1)))[ceiling(grd$dist+1)]
grd$alpha <- rep(c(0.2, 1), each=nrow(grd)/2)
open3d()
for(i in seq(nrow(grd))){
shade3d( translate3d( cube3d(col = grd$col[i]), grd$x[i], grd$y[i], grd$z[i]) , alpha=grd$alpha[i])
}
rgl.snapshot("cube.png")
This example is for a 6x6x6 cube, and colors are based on euclidean distance of their centers to the origen. Hopefully this will show you a way to adapt your colors as you like.
Related
I need to really speeding up a small piece of code that deforms about 3000 spheres in the 3d rgl space according to a 3x3 tensors list that transforms them in ellipsoids. Moreover, I would need transparent color ("alpha" argument <1). Then I want to plot them rapidly and efficiently. I would like to have the same performance of spheres3d() but of course it is not the case...(run the code below). Here below my code with necessary functions for a fully reproducible example. Could you help me in doing this? Many thanks in advance.
Paolo
library(Morpho)
library(Rvcg)
library(rgl)
rep.row<-function(x,n){ #### rep rows of a matrix
matrix(rep(x,each=n),nrow=n)
}
traslamesh<-function(mesh,c){ #### just translate a mesh in the 3d space according a position vector
newvb<-t(mesh$vb[-4,])+rep.row(c,nrow(t(mesh$vb[-4,])))
newmesh<-list(vb=t(cbind(newvb,1)),it=mesh$it)
class(newmesh)<-"mesh3d"
newmesh
}
defosph<-function(sphere,mat,after=T){#### this deforms a sphere in an ellipspoid according to a 3x3 tensor
if(after==T){newvb<-t(sphere$vb[-4,])%*%t(mat)}else{newvb<-t(mat%*%sphere$vb[-4,])}
newmesh<-list(vb=t(cbind(newvb,1)),it=sphere$it)
class(newmesh)<-"mesh3d"
newmesh
}
creasph<-function(radius=1,centroid=c(0,0,0),subdivision=1){ #### just a wrap of vcgSphere
temp_sphere<-vcgSphere(subdivision = subdivision)
temp_sphere$vb[1,]<-temp_sphere$vb[1,]+centroid[1]
temp_sphere$vb[2,]<-temp_sphere$vb[2,]+centroid[2]
temp_sphere$vb[3,]<-temp_sphere$vb[3,]+centroid[3]
final_sphere<-scalemesh(temp_sphere, radius, center = "none")
return(final_sphere)
}
positions<-matrix(rnorm(9000,2,20),ncol=3) ###### positions where we want to plot
spheres3d(positions,alpha=0.5) #### very fast to plot and reasonably fast to naviagate in the 3d rgl window
tensor1<-matrix(rnorm(9),ncol=3) #### a random tensor; let's use the same one for deforming all the 3000 spheres. In the real application each sphere will have its own tensor.
open3d()
for(i in 1:dim(positions)[1]){ #### embarrassingly slow ......
sphi<-creasph(radius=1,subdivision=2)
shade3d(traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,]),col=2,alpha=0.5)
print(i)
}
I want to deform the spheres in ellipsoids according to n tensors and plot them efficiently
There are two things you are doing that make this code slow. First, you are plotting 3000 objects, and updating the display after every one. That's easy to fix: call par3d(skipRedraw=TRUE) at the beginning, and par3d(skipRedraw=FALSE) at the end, and the drawing will be much faster.
The second thing you are doing is much harder to fix. You are constructing ellipsoids as meshes, which makes each one a collection of 320 triangles. Since you have 3000 of these, you've got nearly a million triangles to plot. rgl can handle that, but what makes it really slow is that you are declaring them all to be transparent. To plot these, it needs to sort all million triangles in order from most distant to closest to plot them. Every time it switches from plotting a triangle in one ellipsoid to a triangle in a different one, it needs to go through a fairly expensive context change.
If you set alpha = 1 you'll get a much faster display because sorting isn't needed.
The other thing you could do is to merge all 3000 ellipsoids into one huge mesh. It will still need to sort the triangles, but won't need all those context changes.
The code below illustrates the suggestions. I'll assume you keep your original code to setup the functions.
# Skip redrawing and set alpha = 1
open3d()
par3d(skipRedraw=TRUE)
for(i in 1:dim(positions)[1]){ #### reasonably fast
sphi<-creasph(radius=1,subdivision=2)
shade3d(traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,]),col=2,alpha=1)
print(i)
}
par3d(skipRedraw=FALSE)
# Keep alpha = 0.5, but plot just one big mesh
open3d()
for(i in 1:dim(positions)[1]){ #### pretty slow, but faster than the original
sphi<-creasph(radius=1,subdivision=2)
sphi <- traslamesh(scalemesh(defosph(sphi,tensor1,after=F),1,center="none"),positions[i,])
if (i == 1) result <- sphi else result <- merge(result, sphi)
print(i)
}
shade3d(result, col=2, alpha = 0.5)
The second method is still pretty slow: it does a lot of allocations doing all those merges. You could speed up the construction parts a lot by working with mesh internals. It is also pretty slow in updates, because of the sorting needed for alpha = 0.5.
The reason the built-in spheres display is so fast is that it doesn't attempt to do such a good job. It sorts the sphere centers, not all the triangles making up each sphere. If you set fastTransparency = FALSE, it will slow down a lot, because then it will sort all the triangles. It also uses the equivalent of "sprites", initializing just one sphere and redrawing it in lots of different locations. Sprites would work for your example, but not if you needed different transformations on every ellipsoid.
I am playing around with rgl and I have created a 3D rendering of the mouse brain, in which structures can be isolated and coloured separately.
The original data is a 3D array containing evenly spaced voxels.
Every voxel is coded with a structure ID.
Every structure is rendered separately as a mesh by marching cubes, and smoothed using Laplacian smoothing as implemented by Rvcg.
Some of these structures can be quite small, and it would make sense to look at them within the context of the whole brain structure.
One of the options is to create a low-threshold mesh of the whole set of voxels, so that only the outer surface of the brain is included in the mesh.
This surface can be smoothed and represented using a low alpha in rgl::shade3d colouring faces. This however seems to be quite taxing for the viewport as it slows down rotation etc especially when alpha levels are quite low.
I was wondering if there is any way to implement some sort of cel shading in rgl, e.g. outlining in solid colours the alpha hull of the 2D projection to the viewport in real time.
In case my description was not clear, here's a photoshopped example of what I'd need.
Ideally I would not render the gray transparent shell, only the outline.
Cel shading example
Does anybody know how to do that without getting deep into OpenGL?
Rendering transparent surfaces is slow because OpenGL requires the triangles making them up to be sorted from back to front. The sort order changes as you rotate, so you'll be doing a lot of sorting.
I can't think of any fast way to render the outline you want. One thing that might work given that you are starting from evenly spaced voxels is to render the outside surface using front="points", back="points", size = 1. Doing this with the ?surface3d example gives this fake transparency:
If that's not transparent enough, you might be able to improve it by getting rid of lighting (lit = FALSE), plotting in a colour close to the background (color = "gray90"), or some other thing like that. Doing both of those gives this:
You may also be able to cull your data so the surface has fewer vertices.
I have two parameters or coordinates x and y, and a quantity E(x,y,p) which is a function of those two parameters, but also depends on a state matrix p. I have a few discrete options for the state p - say, p1, p2, p3... - and for every pair (x,y) I run some calculations to find which of the possible states p minimizes E(x,y,p).
Once I have this information, I would like to make a 2D plot in which the region surrounding the point (x,y) is colored e.g. green if state p1 minimized E, blue if state p2 minimized E, etc.
There's probably a simple way to do this that I just haven't figured out yet. I didn't see a good way to do it among the specialized plots, but I was having trouble understanding a few that may have been relevant, e.g. pcolor and patch. Any advice on how to do this?
The best I've thought of is using surf with the z-value used to control the color, and then doing a top-down view of the 3D plot to get the 2D plot I want. That seems like a clunky way of doing things, though.
I was able to do the job with pcolor after all. I defined Emin(x,y)=E(x,y,p) for the p that minimized E(x,y,p) and then called pcolor(Emin). I was able to add ticks on the axes by calling pcolor(xvec,yvec,Emin) where xvec is an array of the different values of x used, etc.
pcolor works by coloring in cells based on the vertices defined by xvec and yvec, or if those are not given, based on the indices of Emin, this doesn't center the cells on the coordinates corresponding to that value. For example, if I calculate Emin(1,2)=5 and call pcolor(Emin) then the cell with the color corresponding to 5 will have its corner at coordinates (1,2). I can fix this by shifting xvec and yvec by half the width of a cell. For cells of unit width, for example, this would be xvec = xvec - 0.5. The last row and column of Emin don't show up, but I can fix this by adding a dummy row and column to it.
In case it's relevant, I was only using linearly spaced xvec and yvec, so if there could be complications I avoided with other spacings.
Hopefully this makes some sense to anyone else who has trouble reading the documentation for pcolor that I had found.
I have a set of points like this (that I have clustered using R):
180.06576696, 192.64378568
180.11529253999998, 192.62311824
180.12106092, 191.78020965999997
180.15299478, 192.56909828000002
180.2260287, 192.55455869999997
These points are dispersed around a center point or centroid.
The problem is that the points are very close together and are, thus, difficult to see.
So, how do I move the points apart so that I can distinguish each point more clearly?
Thanks,
s
Maybe I'm overlooking some intricacy here, but...multiply by 10?
EDIT
Assuming the data you listed above are Cartesian (x,y) coordinate pairs, you can visualize them as a scatter plot using Google Charts. I've rounded your data to 3 decimal places, because Google Charts doesn't appear to handle higher precision than that.
I don't know the coordinates for your central point. In the above chart, I'm assuming it is somewhere nearby and not at (0,0). If it is at (0,0), then I imagine it will be difficult to visualize all of the data at once without some kind of "zoom-in" feature, scaling the data, or a very large screen.
slotishtype, without going into code, I think you first need to add in the following tweaking parameters to be used by the visualization code.
Given an x by y display box, fill the entire box, with input parameters [0.0 to 1.0]...
overlap: the allowance for points to be placed on top of each other
completeness: how important is it to display all of your data points
centroid_display: how important is it to see the centroid in the same output
These produce the dependent parameter
scale: the ratio between display distances to numerical distances
You will need code to
calculate the distance(s) to the centroid like you said,
and also the distances between data points, affecting the output based on the chosen input parameters.
I take inspiration from the fundamentals in the GraphViz dot manual. Look at the "Drawing Orientation, Size and Spacing" on p12.
Trying to do a network plot in R.
How do I lengthen edges in a network graph using IGraph?
I actually want to use the fruchterman-reingold layout. Is there some way I can make that force-based algorithm "springier" so that my vertices are further apart?
thanks.
You can control the Fruchterman-Reingold algorithm using the layout.fruchterman.reingold function. see: help('layout.fruchterman.reingold'). A setup that I often use and gets you a little more spacing is:
l <- layout.fruchterman.reingold(g,niter=500,area=vcount(g)^2.3,repulserad=vcount(g)^2.8)
plot(g,layout=l)
where g is your graph object. Best to just test different values of these parameters for your graph and see what works. Especially repulserad influences the spacing in a graph. The default is the square of the number of nodes, so higher values should get you more spaced graphs.
If the layout.fruchterman.reingold algorithm still does not give what you want by tweaking the parameters, simply do the following. Every layout returns a set of coordinates, with the x- and y-coordinate in the first and second column respectively. You can apply any transformation you like here, and if you would just like to scale it, simply use
L = layout.fruchterman.reingold(G)*s; #Scaling factor s
More fancy transformations are of course also possible. Just for the record, you can also edit the layout manually with your mouse by using tkplot(G, layout=L). You can get the coordinates back via L = tkplot.getcoords(1).