How to draw a crystal ball with two-color particles inside
Solution 1:
In R, using the rgl
package (R-to-OpenGL interface):
library(rgl)
n <- 100
set.seed(101)
randcoord <- function(n=100,r=1) {
d <- data.frame(rho=runif(n)*r,phi=runif(n)*2*pi,psi=runif(n)*2*pi)
with(d,data.frame(x=rho*sin(phi)*cos(psi),
y=rho*sin(phi)*sin(psi),
z=rho*cos(phi)))
}
## http://en.wikipedia.org/wiki/List_of_common_coordinate_transformations
with(randcoord(50,r=0.95),spheres3d(x,y,z,radius=0.02,col="red"))
with(randcoord(50,r=0.95),spheres3d(x,y,z,radius=0.02,col="blue"))
spheres3d(0,0,0,radius=1,col="white",alpha=0.5,shininess=128)
rgl.bg(col="black")
rgl.snapshot("crystalball.png")
Solution 2:
This is very similar to Ben Bolker's answer, but I'm demonstrating how one might add a bit of an aura to the crystal ball by using some mystical coloring:
library(rgl)
lapply(seq(0.01, 1, by=0.01), function(x) rgl.spheres(0,0,0, rad=1.1*x, alpha=.01,
col=colorRampPalette(c("orange","blue"))(100)[100*x]))
rgl.spheres(0,0,0, radius=1.11, col="red", alpha=.1)
rgl.spheres(0,0,0, radius=1.12, col="black", alpha=.1)
rgl.spheres(0,0,0, radius=1.13, col="white", alpha=.1)
xyz <- matrix(rnorm(3*100), ncol=3)
xyz <- xyz * runif(100)^(1/3) / sqrt(rowSums(xyz^2))
rgl.spheres(xyz[1:50,], rad=.02, col="blue")
rgl.spheres(xyz[51:100,], rad=.02, col="red")
rgl.bg(col="black")
rgl.viewpoint(zoom=.75)
rgl.snapshot("crystalball.png")
The only difference between the two is in the lapply
call. You can see that just by changing the colors in colorRampPalette
you can change the look of the crystal ball significantly. The one on the left uses the lapply
code above, the one on the right uses this instead:
lapply(seq(0.01, 1, by=0.01), function(x) rgl.spheres(0,0,0,rad=1.1*x, alpha=.01,
col=colorRampPalette(c("orange","yellow"))(100)[100*x]))
...code from above
Here is a different approach where you can define your own texture file and use that to color the crystal ball:
# create a texture file, get as creative as you want:
png("texture.png")
x <- seq(1,870)
y <- seq(1,610)
z <- matrix(rnorm(870*610), nrow=870)
z <- t(apply(z,1,cumsum))/100
# Swirly texture options:
# Use the Simon O'Hanlon's roll function from this answer:
# http://stackoverflow.com/questions/18791212/equivalent-to-numpy-roll-in-r/18791252#18791252
# roll <- function( x , n ){
# if( n == 0 )
# return( x )
# c( tail(x,n) , head(x,-n) )
# }
# One option
# z <- mapply(function(x,y) roll(z[,x], y), x = 1:ncol(z), y=1:ncol(z))
#
# Another option
# z <- mapply(function(x,y) roll(z[,x], y), x = 1:ncol(z), y=rep(c(1:50,51:2), 10))[1:870, 1:610]
#
# One more
# z <- mapply(function(x,y) roll(z[,x], y), x = 1:ncol(z), y=rep(seq(0, 100, by=10), each=5))[1:870, 1:610]
par(mar=c(0,0,0,0))
image(x, y, z, col = colorRampPalette(c("cyan","black"))(100), axes = FALSE)
dev.off()
xyz <- matrix(rnorm(3*100), ncol=3)
xyz <- xyz * runif(100)^(1/3) / sqrt(rowSums(xyz^2))
rgl.spheres(xyz[1:50,], rad=.02, col="blue")
rgl.spheres(xyz[51:100,], rad=.02, col="red")
rgl.spheres(0,0,0, rad=1.1, texture="texture.png", alpha=0.4, back="cull")
rgl.viewpoint(phi=90, zoom=.75) # change the view if need be
rgl.bg(color="black")
!
The first image on the top left is what you get if you just run the code above, the other three are the results of using the different options in the commented out code.
Solution 3:
As the question is
I wonder if there is any way to program with R, matlab, or any other language.
and TeX is Turing complete and can be considered a programming language, I took some time and created an example in LaTeX using TikZ. As the OP writes it is for a research paper, this comes with the advantage that it can directly be integrated into the paper, assuming it is also written in LaTeX.
So, here goes:
\documentclass[tikz]{standalone}
\usetikzlibrary{positioning, backgrounds}
\usepackage{pgf}
\pgfmathsetseed{\number\pdfrandomseed}
\begin{document}
\begin{tikzpicture}[background rectangle/.style={fill=black},
show background rectangle,
]
% Definitions
\def\ballRadius{5}
\def\pointRadius{0.1}
\def\nRed{30}
\def\nBlue{30}
% Draw all red points
\foreach \i in {1,...,\nRed}
{
% Get random coordinates
\pgfmathparse{0.9*\ballRadius*rand}\let\mrho\pgfmathresult
\pgfmathparse{360*rand}\let\mpsi\pgfmathresult
\pgfmathparse{360*rand}\let\mphi\pgfmathresult
% Convert to x/y/z
\pgfmathparse{\mrho*sin(\mphi)*cos(\mpsi)}\let\mx\pgfmathresult
\pgfmathparse{\mrho*sin(\mphi)*sin(\mpsi)}\let\my\pgfmathresult
\pgfmathparse{\mrho*cos(\mphi)}\let\mz\pgfmathresult
\fill[ball color=blue] (\mz,\mx,\my) circle (\pointRadius);
}
% Draw all blue points
\foreach \i in {1,...,\nBlue}
{
% Get random coordinates
\pgfmathparse{0.9*\ballRadius*rand}\let\mrho\pgfmathresult
\pgfmathparse{360*rand}\let\mpsi\pgfmathresult
\pgfmathparse{360*rand}\let\mphi\pgfmathresult
% Convert to x/y/z
\pgfmathparse{\mrho*sin(\mphi)*cos(\mpsi)}\let\mx\pgfmathresult
\pgfmathparse{\mrho*sin(\mphi)*sin(\mpsi)}\let\my\pgfmathresult
\pgfmathparse{\mrho*cos(\mphi)}\let\mz\pgfmathresult
\fill[ball color=red] (\mz,\mx,\my) circle (\pointRadius);
}
% Draw ball
\shade[ball color=blue!10!white,opacity=0.65] (0,0) circle (\ballRadius);
\end{tikzpicture}
\end{document}
And the result:
Solution 4:
I just had to generate something as shiny as the R-answer in Matlab :) So, here is my late-night, overly complicated, super-slow solution, but my it's pretty ain't it? :)
figure(1), clf, hold on
whitebg('k')
light(...
'Color','w',...
'Position',[-3 -1 0],...
'Style','infinite')
colormap cool
brighten(0.2)
[x,y,z] = sphere(50);
surf(x,y,z);
lighting phong
alpha(.2)
shading interp
grid off
blues = 2*rand(15,3)-1;
reds = 2*rand(15,3)-1;
R = linspace(0.001, 0.02, 20);
done = false;
while ~done
indsB = sum(blues.^2,2)>1-0.02;
if any(indsB)
done = false;
blues(indsB,:) = 2*rand(sum(indsB),3)-1;
else
done = true;
end
indsR = sum( reds.^2,2)>1-0.02;
if any(indsR)
done = false;
reds(indsR,:) = 2*rand(sum(indsR),3)-1;
else
done = done && true;
end
end
nR = numel(R);
[x,y,z] = sphere(15);
for ii = 1:size(blues,1)
for jj = 1:nR
surf(x*R(jj)-blues(ii,1), y*R(jj)-blues(ii,2), z*R(jj)-blues(ii,3), ...
'edgecolor', 'none', ...
'facecolor', [1-jj/nR 1-jj/nR 1],...
'facealpha', exp(-(jj-1)/5));
end
end
nR = numel(R);
[x,y,z] = sphere(15);
for ii = 1:size(reds,1)
for jj = 1:nR
surf(x*R(jj)-reds(ii,1), y*R(jj)-reds(ii,2), z*R(jj)-reds(ii,3), ...
'edgecolor', 'none', ...
'facecolor', [1 1-jj/nR 1-jj/nR],...
'facealpha', exp(-(jj-1)/5));
end
end
set(findobj(gca,'type','surface'),...
'FaceLighting','phong',...
'SpecularStrength',1,...
'DiffuseStrength',0.6,...
'AmbientStrength',0.9,...
'SpecularExponent',200,...
'SpecularColorReflectance',0.4 ,...
'BackFaceLighting','lit');
axis equal
view(30,60)