I'm new to Processing and I've been working on simulating electron motion.
Everything seems fine until I'm try to add a gradient color to each particle.The frame rate drops considerably.
Here is what I've tried so far:
float a=0;
float s;
void setup()
{
size(500,500);
smooth();
frameRate(500);
colorMode(HSB,360,100,100);
noStroke();
ellipseMode(RADIUS);
}
void draw()
{
background(200,0,100);
pushMatrix();
translate(width/2, height/2);
rotate(radians(-18));
for ( int r = width ; r >= 0; r = r - 1 )
{
s = 500*exp(-r);
fill(202, s, 100);
ellipse(100*cos(a), 50*sin(a), r, r);
}
a+=0.1;
popMatrix();
}
Your question isn't about simulating electron motion, but more about efficiently drawing gradients in Processing. I see you've started with the Examples > Basics > Color > RadialGradient sample. Notice that the sample itself runs pretty slow, probably because the focus in on how to use colours (HSB) and drawing functions and less about performance.
What you can do is cache the gradient using PGraphics or PImage, which ever you're more comfortable with.
Here's an example using PGraphics, which might be simpler if you're not used to working with pixels:
PImage e;
void setup(){
size(500,500);
e = getElectronImg(30,30,0,100,100);//create a cached drawing
}
void draw(){
background(255);
translate(width * .5, height * .5);
float a = frameCount * .1;
image(e,100*cos(a), 50*sin(a));
}
PImage getElectronImg(int w,int h,int hue,int satMax,int brightness){
PGraphics electron = createGraphics(w+1,h+1);//create a PGraphics object
electron.beginDraw();//init drawing using the same Processing drawing functions
electron.colorMode(HSB,360,100,100);
electron.background(0,0);//transparent bg
electron.noStroke();
int cx = electron.width/2;
int cy = electron.height/2;
for (int r = w; r > 0; --r) {
electron.fill(hue,map(r,0,w,satMax,0),brightness);
electron.ellipse(cx, cy, r, r);
}
electron.endDraw();
return electron;
}
It's also worth noting that PGraphics extends PImage, therefore can be displayed using the image() function like and other PImage.
And here's the same caching concept done using pixels:
PImage e;
void setup(){
size(500,500);
e = getElectronImg(30,30,0,100,100);
}
void draw(){
background(255);
translate(width * .5, height * .5);
float a = frameCount * .1;
image(e,100*cos(a), 50*sin(a));
}
PImage getElectronImg(int w,int h,int hue,int satMax,int brightness){
pushStyle();//isolate drawing styles such as color Mode
colorMode(HSB,360,100,100);
PImage electron = createImage(w,h,ARGB);//create an image with an alpha channel
int np = w * h;//total number of pixels
int cx = electron.width/2;//center on x
int cy = electron.height/2;//center on y
for(int i = 0 ; i < np; i++){//for each pixel
int x = i%electron.width;//compute x from pixel index
int y = (int)(i/electron.width);//compute y from pixel index
float d = dist(x,y,cx,cy);//compute distance from centre to current pixel
electron.pixels[i] = color(hue,map(d,0,cx,satMax,0),brightness,map(d,0,cx,255,0));//map the saturation and transparency based on the distance to centre
}
electron.updatePixels();//finally update all the pixels
popStyle();
return electron;
}
Of course, this would make it easy to use a lot more electrons.
Off topic from true electron motion, here are some fun tests by making minor tweaks to draw():
void draw(){
background(255);
translate(width * .5, height * .5);
for(int i = 0 ; i < 200 ; i++){
float a = (frameCount * .025 + (i*.1));
image(e,(100+i)*cos(a + i), (50+i)*sin(a + i));
}
}
void draw(){
background(255);
translate(width * .5, height * .5);
for(int i = 0 ; i < 1000 ; i++){
float a = (frameCount * .025 + (i*.1));
image(e,(100+(i * .25))*cos(a + i), (50+(i * .25))*sin(a + i));
}
}
void draw(){
background(255);
translate(width * .5, height * .5);
scale(.25);
for(int i = 0 ; i < 5000 ; i++){
float a = (frameCount * .025 + (i*.1));
image(e,sin(a) * (100+(i * .5))*cos(a + i), (50+(i * .25))*sin(a + i));
}
}
Have fun!
Now you can actually run the code right here (use keys 1,2,3,4 to change demo):
var e,demo = 2;
function setup(){
createCanvas(500,500);
e = getGradientImg(30,30,0,100,100);
}
function draw(){
background(255);
translate(width * .5, height * .5);
if(demo == 1){
var a = frameCount * .1;
image(e,100*cos(a), 50*sin(a));
}
if(demo == 2){
for(var i = 0 ; i < 200 ; i++){
var a = (frameCount * .025 + (i*.1));
image(e,(100+i)*cos(a + i), (50+i)*sin(a + i));
}
}
if(demo == 3){
for(var i = 0 ; i < 1000 ; i++){
var a = (frameCount * .025 + (i*.1));
image(e,(100+(i * .25))*cos(a + i), (50+(i * .25))*sin(a + i));
}
}
if(demo == 4){
scale(.2);
for(var i = 0 ; i < 5000 ; i++){
var a = (frameCount * .025 + (i*.1));
image(e,sin(a) * (100+(i * .5))*cos(a + i), (50+(i * .25))*sin(a + i));
}
}
}
function keyReleased(){
if(key === '1') demo = 1;
if(key === '2') demo = 2;
if(key === '3') demo = 3;
if(key === '4') demo = 4;
}
function getGradientImg(w,h,hue,satMax,brightness){
push();//isolate drawing styles such as color Mode
colorMode(HSB,360,100,100);
var gradient = createImage(w,h);//create an image with an alpha channel
var np = w * h;//total number of pixels
var np4 = np*4;
var cx = floor(gradient.width * 0.5);//center on x
var cy = floor(gradient.height * 0.5);//center on y
gradient.loadPixels();
for(var i = 0 ; i < np4; i+=4){//for each pixel
var id4 = floor(i * .25);
var x = id4%gradient.width;//compute x from pixel index
var y = floor(id4/gradient.width);//compute y from pixel index
var d = dist(x,y,cx,cy);//compute distance from centre to current pixel
//map the saturation and transparency based on the distance to centre
gradient.pixels[i] = hue;
gradient.pixels[i+1] = map(d,0,cx,satMax,0);
gradient.pixels[i+2] = brightness;
gradient.pixels[i+3] = map(d,0,cx,255,0);
}
gradient.updatePixels();//finally update all the pixels
pop();
return gradient;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>