Как нарисовать хрустальный шар с двухцветными частицами внутри

53

Я просто бросаю идею с возможностью закрытия. Мне нужно нарисовать хрустальный шар, в котором случайным образом обнаружены красные и синие частицы. Думаю, мне нужно пойти с фотошопом и даже попытаться сделать мяч в образе, но поскольку это для исследовательской работы и не нужно быть фантазией, мне интересно, есть ли способ программирования с помощью R, matlab или любого другого другой язык.

  • 1
    Я бы использовал VMD для такой задачи, но я все равно использую его ежедневно, и это может быть трудно для новичка ...
  • 1
    Нужно ли использовать этот хрустальный шар, чтобы угадать, как должен выглядеть этот шар? Вы имеете в виду конкретную геометрическую форму / объект? Что-то, что можно продолжить, даже каракули на обратной стороне салфетки, захваченное камерой вашего телефона и загруженное сюда, поможет. Или откройте редактирование изображения, набросайте его свободной рукой, сохраните и загрузите его здесь.
Показать ещё 8 комментариев
Теги:
computer-vision
plot

9 ответов

60
Лучший ответ

В R, используя пакет rgl (интерфейс R-to-OpenGL):

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

Изображение 6296

  • 6
    это один могучий красивый хрустальный шар, но он, похоже, немного подтекает внизу. :-)
  • 0
    да, я думал, что установка максимального радиуса для расположения частиц это исправит, но, похоже, это не так. Я должен больше тренироваться.
Показать ещё 5 комментариев
34

Это очень похоже на ответ Бена Болкера, но я демонстрирую, как можно добавить немного ауры к хрустальному шару, используя какую-то мистическую окраску:

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

Изображение 6297Изображение 6298

Единственное различие между ними заключается в вызове lapply. Вы можете видеть, что просто изменив цвета в colorRampPalette, вы можете значительно изменить внешний вид хрустального шара. Тот, что слева использует код lapply выше, тот, который используется справа, использует это:

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

Вот другой подход, в котором вы можете определить свой собственный файл текстур и использовать его для цветного хрустального шара:

# 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 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")

Изображение 6299Изображение 6300Изображение 6301

Первое изображение в левом верхнем углу - это то, что вы получаете, если вы просто запускаете код выше, остальные три - это результаты использования различных параметров в коде с комментариями.

  • 1
    это действительно довольно впечатляюще!
  • 1
    Небольшой комментарий: вам, вероятно, лучше использовать spheres3d (), а не rgl.spheres (). Функции rgl. * Обрабатывают значения по умолчанию для цветов и т. Д. Странным образом.
27

Поскольку вопрос

Интересно, есть ли способ программирования с помощью R, matlab или любого другого языка.

а TeX - полный Turing и может считаться языком программирования, я потратил некоторое время и создал пример в LaTeX, используя TikZ. Поскольку OP пишет, что это для исследовательской работы, это имеет то преимущество, что оно может быть непосредственно интегрировано в бумагу, предполагая, что оно также написано в LaTeX.

Итак, вот так:

\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}

И результат:

Изображение 6302

24

Я просто имел, чтобы генерировать что-то столь же блестящее, как R-ответ в Matlab:) Итак, вот моя поздняя ночь, слишком сложное, супер-медленное решение, но мое это довольно сложно, это?:)

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)

Изображение 6303

  • 0
    Не уверен, почему вы упоминаете, что он очень медленный, запуск занимает всего около секунды, что кажется хорошим для того, кто хочет сделать 1 снимок.
  • 0
    Правда. Я просто знаю, что это может быть на порядок быстрее, если больше заботиться об этом. Но действительно, кого это волнует :)
18

Я бы порекомендовал вам взглянуть на программу трассировки лучей, povray. Я мало знаю об этом языке, но, используя некоторые примеры, мне удалось произвести это без особых усилий.

Изображение 6304

background { color rgb <1,1,1,1> }
#include "colors.inc"
#include "glass.inc" 

#declare R = 3;
#declare Rs = 0.05;
#declare Rd = R - Rs ;

camera {location <1, 10 ,1>
right <0, 4/3, 0>
 up    <0,0.1,1>
 look_at  <0.0 , 0.0 , 0.0>}

light_source { 
    z*10000
    White
    }

light_source{<15,25,-25> color  rgb <1,1,1> }

#declare T_05 = texture { pigment { color Clear } finish { F_Glass1 } } 


#declare Ball = sphere {
    <0,0,0>, R
      pigment { rgbf <0.75,0.8,1,0.9> } // A blue-tinted glass

    finish
  { phong 0.5 phong_size 40  // A highlight
    reflection 0.2  // Glass reflects a bit
  }
    interior{ior 1.5}
  }

#declare redsphere =    sphere {
    <0,0,0>, Rs
        pigment{color Red}
      texture { T_05 } interior { I_Glass4 fade_color Col_Red_01 }}

#declare bluesphere =   sphere {
    <0,0,0>, Rs
    pigment{color Blue}
      texture { T_05 } interior { I_Glass4 fade_color Col_Blue_01 }}

object{ Ball }

#declare Rnd_1 = seed (123);
 #for (Cntr, 0, 200)
#declare rr = Rd* rand( Rnd_1);
#declare theta = -pi/2 + pi * rand( Rnd_1);
#declare phi = -pi+2*pi* rand( Rnd_1);
#declare xx = rr * cos(theta) * cos(phi);
#declare yy = rr * cos(theta) * sin(phi);
#declare zz = rr * sin(theta) ;
object{ bluesphere  translate  <xx , yy , zz > }
#declare rr = Rd* rand( Rnd_1);
#declare theta = -pi/2 + pi * rand( Rnd_1);
#declare phi = -pi+2*pi* rand( Rnd_1);
#declare xx = rr * cos(theta) * cos(phi);
#declare yy = rr * cos(theta) * sin(phi);
#declare zz = rr * sin(theta) ;
object{ redsphere  translate  <xx , yy , zz > }
#end 
  • 0
    очень хороший хрустальный шар, приятные световые эффекты с поврей
  • 1
    не могли бы вы опубликовать код здесь? Это немного долго, но не кажется слишком длинным.
17

Немного поздно в игре, но вот код Matlab, который реализует scatter3sph (из FEX)

figure('Color', [0.04 0.15 0.4]);
nos = 11; % number small of spheres
S= 3; %small spheres sizes
Grid_Size=256;
%Coordinates
X= Grid_Size*(0.5+rand(2*nos,1));
Y= Grid_Size*(0.5+rand(2*nos,1));
Z= Grid_Size*(0.5+rand(2*nos,1));
%Small spheres colors: (Red & Blue)
C= ones(nos,1)*[0 0 1];
C= [C;ones(nos,1)*[1 0 0]];
% Plot big Sphere
scatter3sph(Grid_Size,Grid_Size,Grid_Size,'size',220,'color',[0.9 0.9 0.9]); hold on
light('Position',[0 0 0],'Style','local');
alpha(0.45);
material shiny 
% Plot small spheres 
scatter3sph(X,Y,Z,'size',S,'color',C);  
axis equal; axis tight; grid off
view([108 -42]);
set(gca,'Visible','off')
set(gca,'color','none')

Изображение 6305

  • 0
    отлично. Я чувствую, что в примере R тоже можно получить блеск, но мне не хотелось возиться с этим.
11

Другое решение с Matlab.

[x,y,z] = sphere(50);
[img] = imread('crystal.jpg');

figure('Color',[0 0 0]);
surf(x,y,z,img,'edgeColor','none','FaceAlpha',.6,'FaceColor','texturemap')
hold on;

i = 0;
while i<100
    px = randn();
    py = randn();
    pz = randn();
    d = pdist([0 0 0; px py pz],'euclidean');
    if d<1
        if mod(i,2)==0
            scatter3(px, py, pz,30,'ro','filled');
        else
            scatter3(px, py, pz,30,'bo','filled');
        end
        i = i+1;
    end
end

hold off;
camlight;

axis equal;
axis off;

Вывод:

Изображение 6306

11

В Javascript с d3.js: http://jsfiddle.net/jjcosare/rggn86aj/6/ или > Выполнить фрагмент кода

Полезно для публикации в Интернете.

var particleChangePerMs = 1000;
var particleTotal = 250;
var particleSizeInRelationToCircle = 75;

var svgWidth = (window.innerWidth > window.innerHeight) ? window.innerHeight : window.innerWidth;
var svgHeight = (window.innerHeight > window.innerWidth) ? window.innerWidth : window.innerHeight;

var circleX = svgWidth / 2;
var circleY = svgHeight / 2;
var circleRadius = (circleX / 4) + (circleY / 4);
var circleDiameter = circleRadius * 2;

var particleX = function() {
  return Math.floor(Math.random() * circleDiameter) + circleX - circleRadius;
};
var particleY = function() {
  return Math.floor(Math.random() * circleDiameter) + circleY - circleRadius;
};
var particleRadius = function() {
  return circleDiameter / particleSizeInRelationToCircle;
};
var particleColorList = [
  'blue',
  'red'
];
var particleColor = function() {
  return "url(#" + particleColorList[Math.floor(Math.random() * particleColorList.length)] + "Gradient)";
};

var svg = d3.select("#quantumBall")
  .append("svg")
  .attr("width", svgWidth)
  .attr("height", svgHeight);

var blackGradient = svg.append("svg:defs")
  .append("svg:radialGradient")
  .attr("id", "blackGradient")
  .attr("cx", "50%")
  .attr("cy", "50%")
  .attr("radius", "90%")

blackGradient.append("svg:stop")
  .attr("offset", "80%")
  .attr("stop-color", "black")

blackGradient.append("svg:stop")
  .attr("offset", "100%")
  .attr("stop-color", "grey")

var redGradient = svg.append("svg:defs")
  .append("svg:linearGradient")
  .attr("id", "redGradient")
  .attr("x1", "0%")
  .attr("y1", "0%")
  .attr("x2", "100%")
  .attr("y2", "100%")
  .attr("spreadMethod", "pad");

redGradient.append("svg:stop")
  .attr("offset", "0%")
  .attr("stop-color", "red")
  .attr("stop-opacity", 1);

redGradient.append("svg:stop")
  .attr("offset", "100%")
  .attr("stop-color", "pink")
  .attr("stop-opacity", 1);

var blueGradient = svg.append("svg:defs")
  .append("svg:linearGradient")
  .attr("id", "blueGradient")
  .attr("x1", "0%")
  .attr("y1", "0%")
  .attr("x2", "100%")
  .attr("y2", "100%")
  .attr("spreadMethod", "pad");

blueGradient.append("svg:stop")
  .attr("offset", "0%")
  .attr("stop-color", "blue")
  .attr("stop-opacity", 1);

blueGradient.append("svg:stop")
  .attr("offset", "100%")
  .attr("stop-color", "skyblue")
  .attr("stop-opacity", 1);

svg.append("circle")
  .attr("r", circleRadius)
  .attr("cx", circleX)
  .attr("cy", circleY)
  .attr("fill", "url(#blackGradient)");

function isParticleInQuantumBall(particle) {
  var x1 = circleX;
  var y1 = circleY;
  var r1 = circleRadius;
  var x0 = particle.x;
  var y0 = particle.y;
  var r0 = particle.radius;
  return Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)) < (r1 - r0);
};

function randomizedParticles() {
  d3.selectAll("svg > .particle").remove();
  var particle = {};
  particle.radius = particleRadius();
  for (var i = 0; i < particleTotal;) {
    particle.x = particleX();
    particle.y = particleY();
    particle.color = particleColor();
    if (isParticleInQuantumBall(particle)) {
      svg.append("circle")
        .attr("class", "particle")
        .attr("cx", particle.x)
        .attr("cy", particle.y)
        .attr("r", particle.radius)
        .attr("fill", particle.color);
      i++;
    }
  }
}

setInterval(randomizedParticles, particleChangePerMs);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="quantumBall"></div>
10

В R вы можете использовать функцию rasterImage для добавления к текущему сюжету, вы можете либо создать/скачать приятное изображение хрустального шара, либо загрузить его в R (см. png, EBImage или другие пакеты), затем сделать он полупрозрачен и использует rasterImage, чтобы добавить его к текущему сюжету. Я бы предположил, что вы сначала нарисуете свои 2 цветные точки, затем сделайте изображение шара сверху (с прозрачностью они все равно будут видны и выглядят так, как будто они внутри).

Более простой подход (хотя, вероятно, не такой красивый) состоит в том, чтобы просто нарисовать полупрозрачный серый круг, используя функцию polygon для представления шара.

Если вы хотите сделать это в трех измерениях, посмотрите на пакет rgl, вот базовый пример:

library(rgl)
open3d()
spheres3d(0,0,0, radius=1, color='lightgrey', alpha=0.2)
spheres3d(c(.3,-.3),c(-.2,.4),c(.1,.2), color=c('red','blue'),
     alpha=1, radius=0.15)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню