Processing.js : how to draw() only parts of the canvas to reduce cpu usage - performance

Right now I have an html page that renders 20 canvases each with its own instance of a processing.js script. To decrease CPU usage, I have mouseover/out events to loop()/noLoop() only the graph the mouse is hovering over.
But I need to take this even further. The canvas right now is 300x400 where there is a 300x300 space that needs to dynamically draw all the time but there's a rectangle with a legend that is the rest of the space which really doesn't need to be redrawn using the framerate() of the rest.
Does any know of a way to essentially specify to the draw() function which pixels to redraw?

I'm pretty sure a 100x300 blank area is not going to add significantly to the computations. But you can of course pass to draw something like:
if(frameCount==1) [draw legend code];
or using if(frameCount % 30 == 0)... for a continuous but less frequent rendering.
Edit:
void setup(){
size(400,400);
noStroke();
background(255);
}
void draw(){
fill(255,255,255,50);
rect(0, 150, width, height-150);
if(frameCount%50 == 1) {
fill(255);
rect(0, 0, width, 150);
fill(0);
text("frame " + frameCount, width/2, 75);
}
fill(255,0,0);
ellipse(random(0, width), random(150, height), 10, 10);
}

Related

How do I blur/unblur the picture according to the mouseY position - going down - blur, going up - unblur

It's like the blur is in never ending loop.
This is what I've wrote so far, it's currently always blurring it up again and again, and I can't make it un-blur.
void draw() {
filter(BLUR, blurRate/60);
nextY = mouseY-blurRate;
blurRate= nextY-mouseY;
}
It will help to split the problem into smaller subproblems.
(Kevin Workman's article is a great start)
The code you shared looks like you're trying to do two things at once:
map the mouse Y position to a blur value
apply the blur value to the blur filter
Let's start with mapping the value.
If you know what the minimum / maximum blur value you need you can make your life easier using Processing's map() function. It takes a value(mouseY in your case) and maps it from one range (mouseY's min/max range: 0 to height) to another range (blur's min/max value, let's say 0 to 6)
The mapping range above (from 0, height to 0, 6) is trivial because can simply divide height / 6 and get the mapped value, but map() is pretty useful and worth getting the hang of.
Here's a minimal mapping example:
void setup(){
}
void draw(){
float blurValue = map(mouseY, 0, height, 0, 6);
background(0);
text("blurValue: " + blurValue, 5, height / 2);
}
The second part is applying the blur filter.
The catch here is avoiding this situation:
currently always blurring it up again and again,
This happens because you apply the blur filter in draw() multiple times per second to the content, so each pass blurs further.
It sounds like what you want is to apply the blur filter to the initial state of your image or graphics. The code you posted doesn't show what is it that you're trying to blur.
If using graphics rendered in processing, one option is to simply clear everything (using background()) and redrawing the graphics before applying the filter. With the filter applied in draw and nothing cleared/redrawn the effect is reapplied to the same (pre blurred) content continously.
Let's take this basic example:
void setup(){
}
void draw(){
line(mouseX, mouseY, pmouseX, pmouseY);
}
Notice that even draw we render a single tiny line, because the graphics aren't cleared (using background()) each tiny line accumulates to form a larger path.
Intuitively, adding blur will accumulate the effect:
void setup(){
}
void draw(){
line(mouseX, mouseY, pmouseX, pmouseY);
filter(BLUR, 0.6);
}
Another option, if you'd rather be more efficient and re-render the same graphics in draw(), you can get an image copy of what's been rendered so far which you could render continuously.
Let's say you're drawing something in setup().
You can easily call get()(with no arguments) to get a "snapshot" of what's been drawn so far a PImage.
Once you have that you can simply render it again and again in draw() as it was drawn in setup() using image().
Once image() is called you can then call filter() with be applied to the global Processing graphics, meaning only what's rendered (while the snapshot PImage will remain intact).
Here's an example illustrating the above:
PImage snapshot;
void setup(){
// draw something
background(0);
noFill();
strokeWeight(3);
for(int i = 0 ; i < 100; i++){
stroke(random(32, 128));
float size = random(3, 27);
ellipse(random(width), random(height), size, size);
}
// take a snapshot of the current graphics
snapshot = get();
}
void draw(){
float blurValue = map(mouseY, 0, height, 0, 6);
// render the image snapshot
image(snapshot, 0, 0);
// blur it
filter(BLUR, blurValue);
// display blur value (should be unblurred)
text("blurValue: " + blurValue, 5, height / 2);
}
Your question doesn't specify, but if you are using PImage, then you need to apply blur to a copy of the image to avoid re-applying blur to an already blurred image. Here's a PImage tweaked version of the above:
PImage snapshot;
void setup(){
// draw something
background(0);
noFill();
strokeWeight(3);
for(int i = 0 ; i < 100; i++){
stroke(random(32, 128));
float size = random(3, 27);
ellipse(random(width), random(height), size, size);
}
// take a snapshot of the current graphics
snapshot = get();
}
void draw(){
float blurValue = map(mouseY, 0, height, 0, 6);
// clone the original image via get()
PImage blurredImage = snapshot.get();
// blur the cloned image
blurredImage.filter(BLUR, blurValue);
// display the blurred image
image(blurredImage, 0, 0);
// display blur value (should be unblurred)
text("blurValue: " + blurValue, 5, height / 2);
}

How to draw a fading object trail? Without background manipulation

I have a simple ellipse, moving across the screen, is there any simple code I could implement to have this ellipse draw a trail behind it that has its alpha fade over time to a certain extent? I still want the trail visible in the end but less bright than the casting ellipse.
You could also do something like this:
let positions = [];
function draw(){
positions.push(mouseX);
positions.push(mouseY);
for(let i in positions){
let x = positions[i];
let y = positions[i + 1];
fill(255, 255 - i * 10); noStroke();
ellipse(mouseX, mouseY, x, y)
}
if(positions.length > 20){
positions.shift();
positions.shift();
}
}
Assuming the ellipse is the only thing being drawn then there is a simple solution which is to, instead of drawing a fully opaque background on each frame, draw a semi-transparent background:
function setup() {
createCanvas(windowWidth, windowHeight);
}
function draw() {
background(0, 35);
ellipse(mouseX, mouseY, 20, 20);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
However, there are some caveats, namely that for certain colors/transparency levels you will be left with permanent "ghost" of the trails due to some weird alpha blending math anomalies.

How can I display text always in front on a 3D plot?

I have a complex 3D plot with some text on rollover. It's already a bit cluttered so I'd like to have the text always be in front of all the plot parts. Like here:
Right now, when I hover over some points in the background, the text is sometimes hidden behind plot components, like here:
How can I force the text to always be in the front and facing the camera (which it already is right now).?
Use pushMatrix()/popMatrix() to isolate coordinate spaces: one for the 3D graphics, another for the 2D elements on top:
void setup(){
size(300, 300, P3D);
sphereDetail(9);
noFill();
stroke(255);
textSize(21);
textAlign(CENTER);
}
void draw(){
background(0);
// 3D
pushMatrix();
translate(width * 0.5, height * 0.5, 0);
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, PI, -PI));
sphere(150);
popMatrix();
// 2D
pushMatrix();
text((int)frameRate, mouseX, mouseY);
popMatrix();
}
(The indenting is not required, just visually emphasising coordinate space nesting.
The last push/pop may not be needed for such as simple example, but is useful if you have more 2D/3D things happening on top of what's already been drawn)
For more info checkout the 2D Transformations Tutorial.
Additionally you might find the PeasyCam library useful.
To draw 2D text on top you'd do something like:
camera.beginHUD();
// draw 2D overlays here
camera.endHUD(); // back to 3D

PGraphics + noSmooth() + alpha = drawing artifacts

Note: I also asked this question on the Processing forum here.
I have this sample code:
PGraphics pg;
void setup() {
size(400, 500);
pg = createGraphics(width, height);
pg.noSmooth();
pg.beginDraw();
pg.background(0, 0, 255);
pg.endDraw();
}
void draw() {
if (mousePressed) {
pg.beginDraw();
pg.stroke(255, 254);
pg.point(mouseX, mouseY);
pg.endDraw();
}
image(pg, 0, 0, width, height);
}
I would expect this code to show a point wherever the user presses the mouse. Instead, I am only able to see points in a couple rectangular areas:
If I remove the call to pg.noSmooth() or if I remove the alpha value in the pg.stroke() call, then it works fine:
If I replace the pg.point() call with pg.ellipse() or pg.rect() then it also works fine.
It seems like the combination of using a PGraphics, the noSmooth() function, the point() function, and an alpha value results in this buggy behavior. I’ve tried in Processing 3.3 and Processing 3.5.2 and I see the same behavior in both.
Am I missing something obvious?
After a wee bit of digging up turns out the JAVA2D renderer draws a point as a diagonal line(line(x, y, x + EPSILON, y + EPSILON);) with a very very very small spacing (static final float EPSILON = 0.0001f;). My guess is this particular configuration the lack aliasing might mean both points of this diagonal line land on the same pixel and end up not being rendered on the top right area which. Why that area and how come this small distance I don't know, but it sounds a bit like the headaches Jakub Valtar and Andres Colubri had to deal with.
FWIW here's a hacky workaround: using a larger distance that does get rendered with transparency and no aliasing:
PGraphics pg;
void setup() {
size(400, 500);
noSmooth();
pg = createGraphics(width/20, height/20);
pg.beginDraw();
// just for debug purposes: rectangle with edge
pg.fill(0, 0, 255);
pg.rect(0,0,pg.width-1,pg.height-1);
pg.stroke(255,255,255, 128);
pg.endDraw();
}
void pointNoSmooth(PGraphics pg, float x,float y){
pg.beginShape();
pg.vertex(x,y);
pg.vertex(x + 0.75,y);//any less than 0.75 distance between vertices and there's nothing to render with aliasing
pg.endShape();
}
void draw() {
background(255);
if (mousePressed) {
pg.beginDraw();
pointNoSmooth(pg,mouseX,mouseY);
pg.endDraw();
}
// render upscaled
image(pg, 0, 0, width, height);
// render small preview in TL corner
image(pg,0,0);
}
Notice that I've set the PGraphics resolution 20 times smaller, then drawn it upscaled so it's easier to see where the pixels land on the PGraphics. I'm not scaling the mouseX,mouseY coordinates though, so you'll need to draw in the small top left preview when testing. That 0.75 distance does the trick: from what I've tested, anything smaller than 0.7499995 starts acting buggy again.

“Rotate” and “translate” in processing give me headaches

As a small homework to get into Processing, I had to write some code to get the following:
Using
public void setup() {
size(300,200);
noFill();
rect(100, 20, 40, 80);
ellipseMode(CENTER);
fill(#000000);
ellipse(width/2, height/2, 5,5);
noFill();
translate(width/2, height/2);
rotate(radians(65));
rect(-20, -40, 40, 80);
}
public void draw() {
}
this worked very good, so far. But I don’t like that I had to change the coordinates inside of the bottom rect instruction in order to get the rotation right. I know that by rotating you don’t rotate single elements but in fact the whole coordinate system.
What I don’t know is which values to put into the translate instruction to have the output be like in the picture above while also still using the same coordinates within the rect command.
The task is already done with the code I used, I just don’t like it too much. So this isn’t mere asking for somebody else doing my homework but pure interest.
EDIT: More generalised attempt of a question: How do I know which values to pit into translate before rotate to get whatever result I want? Is there a way to calculate them? For sure, it’s not just trying out, is it?
A lot of the confusion in processing is the coordinate system. In Processing the origin (0,0) is at the top left of the screen and only positive coordinates display on the screen. The very common workaround for this is to call:
translate(width/2, height/2);
at the beginning of your void draw() method. That way 0,0 is now at the center of the sketch, and any subsequent methods called such as rotate(radians(65)) will take action from the center of the sketch.
This is also good because sketches that use the P3D or OPENGL renderer often call translate to change the coordinate system into something that is easier to use. For example an object at 0,0 or 0,0,0 is at the center and it makes it easier to orbit the camera around the object or have the object rotate around its center.
another popular way of drawing objects would be to set the origin as above and instead of giving the coordinates of each object, i.e. rect(-100, -50, 50, 50) is to use popMatrix() and pushMatrix before a translate before drawing each object at 0,0 as illustrated below:
translate(width/2, height/2);
pushMatrix();
translate(-100, -50);
rect(0,0,50,50);
popMatrix();
This is a good approach to use in a 2d renderer if you think you might move to 3d renderer eventually, because you can easily replace rect() with box() or sphere() or create your own method or object that draws geometry assuming the origin is at 0,0.
If you replace the x and y coordinates with variables or iterate through an array in a for loop it becomes very easy to draw hundreds or thousands of shapes in either 2d or 3d with minimal effort and minimal rewriting of the code.
update: added clarification for the original poster, per their comment.
I have changed the problem slightly to show you how I would approach this. I am showing the translate / rotate method I described above using push and pop matrix. I am just guessing at the values, but if you want something pixel accurate you can take measurements in an image editing program like photoshop or preview.
translate(180, 150);
rect(0, 0, 180, 80);
translate(185, 170);
rotate(radians(65));
rect(0, 0, 180, 80);
translate(180, 250);
ellipse(0, 0, 8, 8);
Putting it all together with pushMatrix() and popMatrix().
void setup(){
size(400,400);
}
void draw(){
// rect1
noFill();
pushMatrix();
translate(180, 150);
rect(0, 0, 180, 80);
popMatrix();
// rect2
pushMatrix();
translate(185, 170);
rotate(radians(65));
rect(0, 0, 180, 80);
popMatrix();
// ellipse
fill(0);
pushMatrix();
translate(180, 250);
ellipse(0, 0, 8, 8);
popMatrix();
}
in my particular example, know how to translate and rotate to get the
result in the picture without changing the rectangle coordinates?
Well, you need to draw at origin to use rotate properly, so you are just dealing with the origin of the rect... As it is in your code, the default, the origin is the upper left corner, so you made an off set (from (0,0)) to draw it's centre, not the corner, at coordinate(0,0), then rotate by the middle. Well done. It is not coincidence that the values you found was minus half rect width(-20) and minus half rect height(-40). If you change to rectMode(CENTER) than you can just draw at (0,0). The same applies to translate, you can just translate to the desired point, the center of the screen, where there is the ellipse. What is done using half width and half height...
look:
public void setup() {
size(300, 200);
smooth();
ellipseMode(CENTER);
rectMode(CENTER);
noFill();
// here converting the coordinates to work with CENTER mode
//could have changed the rect mode just after this
// but i kept as an illustration
int rwidth = 40;
int rheight = 80;
int xpos = 100 + rwidth/2;
int ypos = height/2 - rheight/2 ;
rect(xpos, ypos, rwidth, rheight);
fill(#000000);
ellipse(width/2, height/2, 5, 5);
noFill();
pushMatrix();
translate(width/2, height/2);
//draw at origin gray
stroke(150);
rect(0, 0, 40, 80);
// then rotate
rotate(radians(65));
// draw again black
stroke(0);
rect(0, 0, 40, 80);
popMatrix();
// here using the cordinates calculated before as a translate value
// we draw at origin, but it appears at same place
stroke(230,230,100);
// a bit to the left...
translate(xpos-2, ypos-2);
rect(0, 0, 40, 80);
}
Rotation is aways applied to origin point (0,0), so you want to draw your axis for rotating at origin. Or move the coordinate system origin, to your axis. It is indeed confusing. When using transformations I tend to draw at origin always. Try this to see if it makes things more clear... or less :)
Also there is push/popMatrix() to control the scope of transformations...
public void setup() {
size(300, 380);
smooth();
}
public void draw() {
background(0);
stroke(255);
//use push/pop Matrix to control scope of translate and rotate
pushMatrix();
//move coordinates system origin to center of screen
translate(width/2, height/2);
// rotate with origin as axis
rotate(radians(mouseY));
//draw at origin (now temp moved to center of screen)
// white line
line(0, 0, 150, 0);
// here all coordinate system is back to normal
popMatrix();
//draw with normal system red line at origin
stroke(255, 0, 0);
line(0, 0, 150, 0);
//draw with normal system blue line at centre
stroke(0, 0, 255);
line(width/2, height/2, width/2 + 150, height/2);
}

Resources