Consider the following sketch:
float offset = 0.0;
void setup() {
size(500, 500);
smooth(8);
}
void draw() {
stroke(0);
background(255);
strokeWeight(2);
translate(offset, 0);
for (int i = 0; i < 10; i++) {
line(i * 20, 100, i * 30, 400);
}
offset += 0.1;
}
If you run it, you will notice that the lines move in a jerky kind of way, and not smoothly as one would hope. I'm guessing this is probably due to Processing rounding coordinates. Has anyone encountered and solved these issues before? Is there anything I can do about it?
Basically: how would I make this animation be smooth while keeping the animation speed?
Processing isn't really "rounding" anything. The problem is that there is no such thing as half a pixel.
Think about it this way: you start out with an X value of 30.0, which is at pixel 30. You then increase it to 30.1, which is still at pixel 30. Then 30.2 is still at pixel 30. This continues until you finally increase to 31.0, which is at pixel 31.
In other words, you're spending 10 frames on each pixel. That's why your animation looks jerky: the line spends 10 frames on a coordinate, then moves, then spends 10 frames on a coordinate, etc.
If you increase the offset increment to .5, then you spend less time on each pixel and your animation is smoother.
Before:
After:
Edit: The above is true for the default renderer. The P3D renderer seems to be better at sub-pixel rendering:
You might also try the P2D renderer, which seems to be better at sub-pixel rendering than the default renderer as well.
Related
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).
For some reason when I draw over another rectangle in my current processing sketch, it draws over all other rectangle fills, but does not appear to draw over other rectangle strokes. when I make another sketch this problem doesn't persist, but it happens consistently in this sketch.
Overlapping rectangles picture
[]
Example
Here's a simplified example which seems to recreate the issue
void setup(){
size(1000, 600 , P3D);
surface.setResizable(true);
//frameRate(120);
}
void draw(){
if(width < 100)
size(100,height,P3D);
if(height < 100)
size(width,100,P3D);
fill(10);
stroke(10);
strokeWeight(10);
rect(100,100,100,100);
fill(255);
stroke(100);
strokeWeight(10);
rect(0,0,300,300);
}
It's because you set size with the "P3D" argument. P3D is a 3D rendering mode. This result is likely due to some eccentricity of rendering the 2D rect() function in a 3D space.
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.
The code is supposed to fade and copy the window's image to a buffer f, then draw f back onto the window but translated, rotated, and scaled. I am trying to create an effect like a feedback loop when you point a camera plugged into a TV at the TV.
I have tried everything I can think of, logged every variable I could think of, and still it just seems like image(f,0,0) is doing something wrong or unexpected.
What am I missing?
Pic of double image mirror about x-axis:
PGraphics f;
int rect_size;
int midX;
int midY;
void setup(){
size(1000, 1000, P2D);
f = createGraphics(width, height, P2D);
midX = width/2;
midY = height/2;
rect_size = 300;
imageMode(CENTER);
rectMode(CENTER);
smooth();
background(0,0,0);
fill(0,0);
stroke(255,255);
}
void draw(){
fade_and_copy_pixels(f); //fades window pixels and then copies pixels to f
background(0,0,0);//without this the corners dont get repainted.
//transform display window (instead of f)
pushMatrix();
float scaling = 0.90; // x>1 makes image bigger
float rot = 5; //angle in degrees
translate(midX,midY); //makes it so rotations are always around the center
rotate(radians(rot));
scale(scaling);
imageMode(CENTER);
image(f,0,0); //weird double image must have something not working around here
popMatrix();//returns window matrix to normal
int x = mouseX;
int y = mouseY;
rectMode(CENTER);
rect(x,y,rect_size,rect_size);
}
//fades window pixels and then copies pixels to f
void fade_and_copy_pixels(PGraphics f){
loadPixels(); //load windows pixels. dont need because I am only reading pixels?
f.loadPixels(); //loads feedback loops pixels
// Loop through every pixel in window
//it is faster to grab data from pixels[] array, so dont use get and set, use this
for (int i = 0; i < pixels.length; i++) {
//////////////FADE PIXELS in window and COPY to f:///////////////
color p = pixels[i];
//get color values, mask then shift
int r = (p & 0x00FF0000) >> 16;
int g = (p & 0x0000FF00) >> 8;
int b = p & 0x000000FF; //no need for shifting
// reduce value for each color proportional
// between fade_amount between 0-1 for 0 being totallty transparent, and 1 totally none
// min is 0.0039 (when using floor function and 255 as molorModes for colors)
float fade_percent= 0.005; //0.05 = 5%
int r_new = floor(float(r) - (float(r) * fade_percent));
int g_new = floor(float(g) - (float(g) * fade_percent));
int b_new = floor(float(b) - (float(b) * fade_percent));
//maybe later rewrite in a way to save what the difference is and round it differently, like maybe faster at first and slow later,
//round doesn't work because it never first subtracts one to get the ball rolling
//floor has a minimum of always subtracting 1 from each value each time. cant just subtract 1 ever n loops
//keep a list of all the pixel as floats? too much memory?
//ill stick with floor for now
// the lowest percent that will make a difference with floor is 0.0039?... because thats slightly more than 1/255
//shift back and or together
p = 0xFF000000 | (r_new << 16) | (g_new << 8) | b_new; // or-ing all the new hex together back into AARRGGBB
f.pixels[i] = p;
////////pixels now copied
}
f.updatePixels();
}
This is a weird one. But let's start with a simpler MCVE that isolates the problem:
PGraphics f;
void setup() {
size(500, 500, P2D);
f = createGraphics(width, height, P2D);
}
void draw() {
background(0);
rect(mouseX, mouseY, 100, 100);
copyPixels(f);
image(f, 0, 0);
}
void copyPixels(PGraphics f) {
loadPixels();
f.loadPixels();
for (int i = 0; i < pixels.length; i++) {
color p = pixels[i];
f.pixels[i] = p;
}
f.updatePixels();
}
This code exhibits the same problem as your code, without any of the extra logic. I would expect this code to show a rectangle wherever the mouse is, but instead it shows a rectangle at a position reflected over the X axis. If the mouse is on the top of the window, the rectangle is at the bottom of the window, and vice-versa.
I think this is caused by the P2D renderer being OpenGL, which has an inversed Y axis (0 is at the bottom instead of the top). So it seems like when you copy the pixels over, it's going from screen space to OpenGL space... or something. That definitely seems buggy though.
For now, there are two things that seem to fix the problem. First, you could just use the default renderer instead of P2D. That seems to fix the problem.
Or you could get rid of the for loop inside the copyPixels() function and just do f.pixels = pixels; for now. That also seems to fix the problem, but again it feels pretty buggy.
If somebody else (paging George) doesn't come along with a better explanation by tomorrow, I'd file a bug on Processing's GitHub. (I can do that for you if you want.)
Edit: I've filed an issue here, so hopefully we'll hear back from a developer in the next few days.
Edit Two: Looks like a fix has been implemented and should be available in the next release of Processing. If you need it now, you can always build Processing from source.
An easier one, and works like a charm:
add f.beginDraw(); before and f.endDraw(); after using f:
loadPixels(); //load windows pixels. dont need because I am only reading pixels?
f.loadPixels(); //loads feedback loops pixels
// Loop through every pixel in window
//it is faster to grab data from pixels[] array, so dont use get and set, use this
f.beginDraw();
and
f.updatePixels();
f.endDraw();
Processing must know when it's drawing in a buffer and when not.
In this image you can see that works
I'm a newbie who working on processing code. I'm using a variable to fade light blue into a darker blue and that's working fine, but I was wondering how I could slow the process of it fading.
Another question(hope people don't mind me asking two on one post), is how do I make a shape stop moving at a certain point? I have a ellipse there labelled the sun. I'd like it to stop when x=700.
Here's my coding:
float x = 0;
float y = 0;
float r = 0;
int gb = 0;
void setup() {
size(800, 600);
background(gb, gb, 255);
imageMode(CENTER);
noStroke();
}
void draw() {
background(0, gb, 255);
gb++;
if (gb>50) {
//the sun
fill(243, 230, 0);
ellipse(x, 60, 75, 75);
fill(243, 230, 0, 80);
ellipse(x, 60, 90, 90);
x++;
}
fill(0, 255, 0);
rect(0, 380, 800, 450);
}
I was wondering how I could slow the process of it fading.
Check out this line:
gb++;
Here you're incrementing (adding 1 to) your gb variable, which you're using to determine the color. To slow down the color changing, just add a smaller value to it. Something like this:
gb = gb + .1;
Which can be shortened to:
gb += .1;
For this to work, you'll have to change your gb variable to be a float so it can hold decimals.
You might also want to check out the lerp() and map() functions in the reference.
Another question(hope people don't mind me asking two on one post), is how do I make a shape stop moving at a certain point? I have a ellipse there labelled the sun. I'd like it to stop when x=700.
In the future, please only ask one question per post. And try to put together a MCVE for each question instead of posting your whole sketch.
But you can do this with an if statement that only increments x when it is less than 700. Like this:
if(x < 700){
x++;
}
Shameless self-promotion: I wrote a tutorial on using if statements to create animations in Processing available here.