PGraphics + noSmooth() + alpha = drawing artifacts - processing

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.

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 optimize an animation in Processing, and keep it from leaving a trail of images?

I am creating a model of a solar system in processing, and after removing the background I noticed the planets were leaving a trail of their image behind them. The program runs fine when the background is back in, but I want to add a lot more and I am sure this is inefficient and will bog things down.
I am very new to processing, and I am really not sure how to solve this. Maybe delete previous images after a delay to create a shortened trail?
These are just the parts I think are important cherry picked from the code, this is just the example of one planet. Sorry if the code is clunky, any suggestions are happily accepted.
Planet p1;
void setup() {
mercury = loadImage("mercury.png")
p1 = new Planet(40, random(TWO_PI), 0.05);
}
void draw() {
//background(0)
translate(width / 2, height / 2);
p1.display1();
p1.orbit();
}
class Planet {
float radius;
float angle;
float distance;
float orbitSpeed;
Planet(float r, float d, float o) {
radius = r;
distance = d;
orbitSpeed = o;
angle = random(TWO_PI);
}
void orbit() {
angle = angle + orbitSpeed;
}
void display1() {
pushMatrix();
rotate(angle);
translate(distance, 0);
imageMode(CENTER);
image(mercury, radius, radius, 10, 10);
popMatrix();
}
}
I realized that this would probably happen, and I am not sure how to stop it.
The behavior you describe is simply the nature of computer graphics; it's how games, operating systems, and hardware displays all work – they clear and redraw everything every frame.
In Processing, graphic objects that are pushed to a buffer remain there indefinitely until the buffer is cleared or something is pushed on top of them (this is why the planets are leaving a trail without calling background() – previous frames remain in the buffer).
You are worried about the background() being inefficient. Don't be, as it's one of the fastest operations (simply sets the value of each pixel, as given by the user).
Processing does provide a clear() function, but this is equivalent to background(0).
If you're are still concerned about efficiency and speed, one way to speed up Processing is to use the FX2D renderer rather than default AWT renderer. Another way is cache drawn objects into PGraphics objects to prevent successive rasterization (since your planets are image files and not drawn with processing, you needn't worry about this).
Your code is simple enough that it doesn't need optimisations at this stage.
As micycle mentions, you are are drawing an image at a translated position, pretty similar to blitting.
In terms of the trails, one common trick you could use is not clear the screen completely, but draw a transparent rectangle as the background. The more transparency, the longer the trails.
Here's a tweaked version of your code:
// planet object
Planet p1;
// planet texture
PImage mercury;
void setup() {
size(300, 300);
// draw image from center
imageMode(CENTER);
// clear to black one
background(0);
// remove strokes (we'll use rect() later)
noStroke();
// set the fill to black but with 9/255 transparency (~3.5% transparent)
fill(0,9);
// init texture
mercury = loadImage("mercury.png");
// init planet
p1 = new Planet(40, random(TWO_PI), 0.05);
}
void draw() {
// draw a transparent rectangle instead of completely clearing the screen
rect(0,0,width,height);
// render planet
translate(width / 2, height / 2);
p1.display1();
p1.orbit();
}
class Planet {
float radius;
float angle;
float distance;
float orbitSpeed;
Planet(float r, float d, float o) {
radius = r;
distance = d;
orbitSpeed = o;
angle = random(TWO_PI);
}
void orbit() {
angle = angle + orbitSpeed;
}
void display1() {
pushMatrix();
rotate(angle);
translate(distance, 0);
image(mercury, radius, radius, 10, 10);
popMatrix();
}
}
It's an efficient quick'n'dirty hack as you won't need to store previous position and redraw multiple times, however it has it limitations in terms of the flexibility of the trails. Hopefully tweaking the fill() alpha parameter will get you the desired effect.
Later on if you're drawing many many many planets it things start running slow have a peak at VisualVM. Profile the CPU and see the methods that take the longest to complete and focus on those. Don't need to optimise everything, just the slowest calls. Remember that Processing have multiple renderers: JAVA2D is the default one, but there's also FX2D and P2D/P3D and they will behave differently. I strongly recommend optimising at the last moment (otherwise code might be less flexible and readable and will slow down development/iteration).

Natural Color Mixing in Processing

I am trying to naturally mix green tint on one photo (elephant) and red tint on another photo (ship) by placing the two opaque and tinted photos on top of one another. While both photos appear with equal prominence, when the program is run and both colors can be observed, the red tint (because it is the last tint applied) is more dominant and there is no discernable yellow color in the image no matter which tint is applied first. If anyone knows how to naturally mix colors in processing I would love to hear your advice. I also do not want to simply apply a yellow tint as the last tint. Thanks!
PImage elephant;
PImage ship;
void setup(){
size(695,473);
elephant = loadImage("elephantRider.png");
ship = loadImage("ship.jpg");
ship.filter(OPAQUE);
elephant.filter(OPAQUE);
}
void draw(){
background(255);
tint(0,255,0, 127);
image(elephant, 0,0);
tint(255,0,0,127);
image(ship, 0,0);
}
Think of Processing as painting. What you're doing now is a little bit like drawing one color to the screen, and then drawing another color on top of the first color.
Instead, what you want to do is mix the two colors together before you draw the result to the screen. Think of this as mixing two paints together to make a new color.
You can do this by taking the average of the two colors, something like this:
PImage img;
void setup() {
size(695, 473);
img = loadImage("image.jpg");
}
void draw() {
background(255);
//green
tint(0, 255, 0, 127);
image(img, width/2, height/2, 50, 50);
float blendedRed = (0 + 255) / 2;
float blendedGreen = (255 + 0) / 2;
float blendedBlue = (0 + 0) / 2;
// blended
tint(blendedRed, blendedGreen, blendedBlue, 127);
image(img, mouseX, mouseY, 50, 50);
}
This example is a little contrived because the averages are pretty trivial, but this approach should work for any color.
If you want something more advanced, note that color averaging is a pretty advanced topic. Googling "color averaging" is probably a good start.

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

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);
}

Resources