We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpSyntax Questions › draw/redraw question
Page Index Toggle Pages: 1
draw/redraw question (Read 2496 times)
draw/redraw question
Oct 26th, 2009, 1:44am
 
I'm new to Processing but very impressed. I have had more fun in the last couple of days than in a couple of years of my normal job Smiley

I have put together a little program that has a mostly static visualisation but when I run it then my CPU seems to be very active, which makes sense because it's presumably redrawing everything repeatedly.

I tried to improve things by calling noLoop() in setup and then draw() when I need something repainted but there is a problem here. My visualisation has some short-lived animations in them so I would like the draw() code to be able to trigger another frame if the animation isn't complete (by calling redraw()?). But redraw() seems has no effect if called from within draw(), so either my animation stops after the first frame if I call redraw() or skips straight to the end if I call draw(), because the intermediate images aren't being rendered.

Does anyone know a good trick to do this, or is this something that I have to solve outside the Processing framework (e.g. by defining my own 'redrawNeeded' flag)?
Re: draw/redraw question
Reply #1 - Oct 26th, 2009, 2:51am
 
This sort of question seems to crop up fairly often.  The main issue is likely to be that any code in draw has to complete before draw refreshes the screen: so you can't use things like delay to simulate separate frames within draw...

There are probably other, better, ways to optimise your code though: e.g. don't use loadImage in draw.  It would probably be easier to offer suggestions if you posted some code Wink
Re: draw/redraw question
Reply #2 - Oct 28th, 2009, 12:24am
 
I thought it would be clear enough from the explanation, but here is a test program that shows my problem. If I take away the noLoop() and the redraw() calls then it works - when you click the mouse the randomly place text slowly fades in. But once the 'animation' part is finished it still chews up 20% CPU on my dual CPU desktop (not a bad one, either!) even though nothing is happening.

As I often work on my laptop when travelling on the train this means that I have less time to have fun with Processing before my battery is empty as the fan kicks in very quickly Wink

With the commented redraw() call nothing happens. If I replace this call with a draw() then it skips the animated fade and simply shows the finished visualisation.

It would be perfect if redraw() could simply trigger a new call to draw (in the normal way) whenever the current frame has been rendered. I'm not quite sure why that's not the case, but I'm sure this must be a common problem.

At the moment the only way that I can see to make this more efficient is to leave it in 'loop' mode and implement my own check to decide whether to redraw the visualisation or not, but that seems like something that a framework like Processing should help with and as a beginner I just thought that I missed something.


PFont font;
int seed = 0;
int trans = 0;

void setup() {
 size(600,600);
 font = loadFont("Verdana-20.vlw");
 noLoop();
}

void draw() {
 background(128);
 Random rnd = new Random(seed);
 for (int i=0 ; i<200 ; i++) {
   smooth();
   textFont(font);
   fill(10, 10, 100, trans);
   text("A Test", rnd.nextInt(width), rnd.nextInt(height));
 }
 if (trans < 128) {
   trans++;
   redraw(); // what to do here?
 }
}

void mousePressed() {
 seed = mouseX ^ mouseY;
 trans = 0;
 redraw();
}
Re: draw/redraw question
Reply #3 - Oct 28th, 2009, 1:45am
 
OK - maybe my bias against using noLoop() affected my answer...  You could do this:

Code:
PFont font;
int seed = 0;
int trans = 0;

void setup() {
size(600,600);
font = createFont("Arial", 20);
noLoop();
}

void draw() {

  background(128);
  Random rnd = new Random(seed);
  for (int i=0 ; i<200 ; i++) {
    smooth();
    textFont(font);
    fill(10, 10, 100, trans);
    text("A Test", rnd.nextInt(width), rnd.nextInt(height));
  }
  if (trans < 128) {
    trans++;
  }
  else {
    println("end");
    noLoop();
  }

}

void mousePressed() {
seed = mouseX ^ mouseY;
trans = 0;
loop();
}


The solution isn't to try and work around the fact that you're not looping, but starting and stopping the loop as and when necessary; and that doesn't involve using redraw(), but instead loop();
Re: draw/redraw question
Reply #4 - Oct 28th, 2009, 1:46am
 
can't you just call noLoop() again when the animation's finished?

i had the same problem with a puzzle i wrote. when you were dragging pieces into place you had to redraw everything but if you hadn't picked up a piece or weren't moving the mouse you didn't need to. jumped through all sorts of hoops and am not sure it's done in the best way now but...

http://www.koogy.clara.co.uk/puzzle2/

source code included but is lacking comments (i tidied it up for version 3, at least i hope i did). beware the sounds as well, version 3 had visual feedback but 2 is quite noisy, especially when (if!) you finish.

edit:
actually, the noLoop thing is exactly what blindfish suggests, specifically here:

  if (trans < 128) {
    trans++;
  }
  else {
    println("end");
    noLoop(); <- animation is finished so disable looping
  }
Re: draw/redraw question
Reply #5 - Oct 28th, 2009, 3:49am
 
Thank you both for your ideas.

The loop()/noLoop() solution would work with this simple example, but my real code is a bit more complex. There are some objects 'fading in' and some 'fading out', and not all of these 'animations' are necessarily in sync. So if one animation finishes and I call noLoop() then this will stop all of the other animations as well, some of which may not be complete. I could write code to keep track of the number of running animations and call noLoop() when this reaches zero, but this seems pretty ugly as well.

Being able to explicitly trigger another frame would be a real help here. Should I raise a feature request for this?
Re: draw/redraw question
Reply #6 - Oct 28th, 2009, 4:00am
 
PaulC wrote on Oct 28th, 2009, 3:49am:
Being able to explicitly trigger another frame would be a real help here.

Well, that's redraw()
I haven't followed closely this thread, so I am not sure what is your issue with it.

[EDIT] I re-read your first post, and I see a problem: you are not supposed to call draw(), it is called by Processing. That's what redraw() is made for.
You should change your logic, so there is a separate function which call redraw() and find out if another is needed after this one.

You can also just let the drawing calls to be called at normal rate (which you set, of course), and if there is nothing to do, just exit draw() early, not changing anything in the display (no background() call, etc.). Thus you have a timer ticking, and you can control the sequence of events and draw calls.
Re: draw/redraw question
Reply #7 - Oct 28th, 2009, 4:12am
 
..and that's why I don't use noLoop().  More often than not you need the draw loop to be running continuously.  If an animation completes but something still needs to be displayed, as in your example, it needs to be included in draw, or in the hypothetical case when you explicitly 'trigger another frame'.  Basically I'd question how often this type of optimisation would actually be active; though I do like the idea of saving energy whenever possible...
Re: draw/redraw question
Reply #8 - Oct 28th, 2009, 4:39am
 
PhiLho  wrote on Oct 28th, 2009, 4:00am:
you are not supposed to call draw(), it is called by Processing. That's what redraw() is made for.


I realised that I am not supposed to call draw() (that's just plain ol' recursion) but redraw() doesn't work from within the draw() method. Where else can I call it from Putting it in another method that is then called by my draw() code obviously won't make any difference.

So the conclusion is that only way that I can achieve this is to code it myself by having my own 'redrawNeeded' flag and using this either to short-circuit the draw() implementation or enable/disable the loop That seems a bit untidy to me. Couldn't redraw() just be changed to work from within draw() as well (i.e. trigger the next frame)
Re: draw/redraw question
Reply #9 - Oct 28th, 2009, 5:24am
 
The idea is that draw() is designed to be called at regular intervals (the frameRate) by Processing. So there is no need to call redraw() from draw(), since you know it will be called soon...

I don't get why short-circuiting draw() seems untidy: somehow, it is just an indicator "if there is nothing new to draw, don't draw".
I wouldn't play with noLoop()/loop() either.

Or just call noLoop() in setup(), and call from there a function which will call redraw() when it sees fit. But it would need to call redraw() at regular intervals (or after a while if a pause is needed). So you will end in using a Timer or similar thread code.
Re: draw/redraw question
Reply #10 - Oct 28th, 2009, 5:55am
 
I tried out both solutions using a RedrawManager class to manage the state. Here is the version with the loop control:

 private static boolean redrawNeeded = false;
 private static boolean loopRunning = true;

 static void reset() {
   redrawNeeded = false;
 }

 static void redraw(PApplet p) {
   redrawNeeded = true;
   if (!loopRunning) {
     loopRunning = true;
     p.loop();
   }
 }

 static void schedule(PApplet p) {
   if (loopRunning && !redrawNeeded) {
     loopRunning = false;
     p.noLoop();
   }
 }


My draw loop then looks like this:

 void draw() {
   System.out.println("Drawing frame " + frameCount);
   RedrawManager.reset();
   // ... drawing code ...
   RedrawManager.schedule(this);
 }


If I need a redraw any time then I now call RedrawManager.redraw() instead of redraw(). This way my CPU usage is zero when the application isn't changing. The equivalent for a flag to short-circuit the drawing code when it's not needed is simpler, but still uses about 5% CPU in the 'idle' state.

This seems untidy because it needs this extra code to make the best of 'noLoop' mode (unless the visualisation really is static, but then why use Processing?). If redraw() triggered another frame even if it was called within draw() then I could have saved this extra code and a fair amount of head scratching.

I realise that as a software engineer I may be more inclined to look for optimisations than the average user, but software engineers are users too  Wink
Re: draw/redraw question
Reply #11 - Oct 28th, 2009, 6:14am
 
> redraw() doesn't work from within the draw() method

you know, i never realised this. could explain a few of the bugs i was seeing in my puzzle (things not appearing, other things not disappearing)
Re: draw/redraw question
Reply #12 - Oct 28th, 2009, 1:15pm
 
I took a look at the code and it should be very easy to change to allow redraw to work from within draw() as well. In the PApplet.handleDraw() method you could just move the line that clears the redraw flag to the top of the enclosing block instead of the end.
Re: draw/redraw question
Reply #13 - Apr 26th, 2010, 2:19pm
 
In case anyone is still interested, here is the patch for the change that I suggested:

--- a/core/src/processing/core/PApplet.java     Mon Apr 26 21:06:08 2010 +0200
+++ b/core/src/processing/core/PApplet.java     Mon Apr 26 21:25:28 2010 +0200
@@ -1579,6 +1579,11 @@
        this.defaultSize = false;

      } else {  // frameCount > 0, meaning an actual draw()
+        // unset 'redraw' flag in case it was set.
+        // doing this here allows the draw() code to request a new
+        // frame by calling redraw() if necessary.
+        redraw = false;
+
        // update the current frameRate
        double rate = 1000000.0 / ((now - frameRateLastNanos) / 1000000.0);
        float instantaneousRate = (float) rate / 1000.0f;
@@ -1607,10 +1612,6 @@
        dequeueKeyEvents();

        drawMethods.handle();
-
-        redraw = false;  // unset 'redraw' flag in case it was set
-        // (only do this once draw() has run, not just setup())
-
      }

      g.endDraw();
Page Index Toggle Pages: 1