[piccolo2d-dev] Re: invalidatePaint does not cause a repaint unless other events are occurring

2009-11-11 Thread Nigel

Thanks everyone for their help with this.

I finally figured out what was going on.

I wrote my example to call invalidatePaint from the correct thread
(the EDT) for one node and the incorrect thread for the other node.  I
expected one node to update and the other not to update, however
neither node updated.


When calling invalidatePaint() on a node the call propogates up all
the parents towards PRoot, setting the childPaintInvalid on every
parent on the way.  The call stops if a parents childPaint is already
flagged as invalid - as it knows from this that a previous call has
already scheduled a processInputs for execution on the EDT.

If you call invalidatePaint from the wrong thread it is ignored when
it finally reaches PRoot, however in getting to PRoot
childPaintInvalid is set for every parent on the way.  As PRoot
ignores the request a processInputs Runnable is never put in the event
queue and the children remain with invalid paint.

A subsequent call to invalidatePaint, even from the correct thread,
never gets as far as PRoot as it stops (as it should) when it reaches
a parent whose child paint is already invalid, however with no
processInputs scheduled this and all future calls to invalidatePaint
get ignored.

This is exactly what was happening in my example.  I included updates
from the wrong thread so I could see the difference between the wrong
thread and the correct thread, not realising that these very updates
from the wrong thread were breaking updates from the correct thread.


The situation only arises after calling invalidatePaint from the wrong
thread.

Furthermore if you set

debugThread = true

in PDebug

the scene graph manipulated on wrong thread warning is never
displayed as PRoot.scheduleProcessInputsIfNeeded() returns if being
called from the wrong thread before getting as far as calling
PDebug.scheduleProcessInputs();

Nigel

On Nov 4, 12:24 am, nls...@googlemail.com wrote:
 The problem seems to be caused by updating the scene graph by a thread
 other than the Swing event dispatch thread. This is not allowed, a
 solution is explained in the Piccolo2D Patterns.

 A consequence of updates by wrong threads is that PRoot discards the
 update and repaint process (it checks if the right thread invoked the
 update). Subsequent invalidatePaints possibly dont reach the root.

 Am Dienstag 03 November 2009 02:36:21 schrieb Nigel:

  The comment in PNode.java reads

      // When you do create you own nodes the only method that you
  will // normally need to call isinvalidatePaint. This method marks
  the // nodes as having invalid paint, the root node's UI cycle will
  then
      // later discover this damage and report it to the Java repaint
  manager.

  However, I find that after callinginvalidatepaint() the repaint
  manager does not discover the damage unless other events are
  occurring (such as moving the mouse around).

  I was expecting that callinginvalidatePaintwould result in the
  node being repainted some time later - am I doing something wrong
  or is Piccolo?

  Here is a contrived example (ClockNode.java) - It displays 4 custom
  nodes, each showing a clock's second hand.  The top 2 clocks are
  updated from within the event dispatch thread, the lower 2 clocks
  are updated from another thread.  The 2 clocks on the left are
  redrawn via a call to repaint and the 2 on the right via a call to
 invalidatePaint.

  if you run it you'll see the clock on the right (calling
 invalidatePaint) are only redrawn if you are moving your mouse over
  the window.

  ClockNode.java

  import java.awt.*;
  import java.awt.geom.*;
  import edu.umd.cs.piccolo.*;
  import edu.umd.cs.piccolo.util.*;

  import edu.umd.cs.piccolox.PFrame;

  public class ClockNode extends PNode {

     private GeneralPath secondHand;
     private int tseconds;   // 10ths of seconds

     public void tick() {
             tseconds++;
             if ( tseconds = 600 ) { tseconds = 0; }
             // inform Piccolo that the node needs to be redrawn
             if ( useRepaint ) {
                     repaint();
             } else {
                     invalidatePaint();
             }
     }

     private boolean useRepaint;
     public ClockNode(boolean useRepaint) {
             this.useRepaint = useRepaint;
             // create the needle shape
             secondHand = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
             secondHand.moveTo(-0.1,0);
             secondHand.lineTo(0,1);
             secondHand.lineTo(0.1,0);
             secondHand.closePath();
     }

     public void paint(PPaintContext aPaintContext) {
             Graphics2D g2 = (Graphics2D)aPaintContext.getGraphics().create();
  // create - as we mess with the transform
             g2.setPaint(Color.BLACK);
             //draw the face
             g2.draw( new Ellipse2D.Double( getX(), getY(), getWidth(),
  getHeight () ));
             //draw the second hand
             g2.translate(getX()+getWidth()/2,getY()+getHeight()/2

[piccolo2d-dev] Re: invalidatePaint does not cause a repaint unless other events are occurring

2009-11-03 Thread Nigel

I agree, my interpretation is that calling invalidatePaint should
result in a repaint sometime soon after.

This differs from Piccolos current (wrong?) behaviour whereby calling
invalidatePaint does not result in a repaint (unless some other event
triggers the repaint).

My gut feel is that there's a timer missing somewhere.

It seems the invalidatePaint() message propagates up the node
hierarchy to the PLayer, but is then ignored by the camera, whereas I
think the camera should notice the invalidatePaint and if after a
reasonable time delay (say 1/10th second) should trigger a repaint if
a repaint hasn't already occurred anyway.

This would allow Piccolo to combine multiple paints into fewer paints,
especially effective when large numbers of overlapping nodes all need
to be redrawn at about the same time.

Nigel



On Nov 3, 6:03 am, Michael Heuer heue...@gmail.com wrote:
 I think this is supposed to be analogous to the AWT
 Component.invalidate() method

 http://java.sun.com/javase/6/docs/api/java/awt/Container.html#invalid...

 A client might call invalidatePaint() a lot of times before an actual
 repaint happens.

    michael

 Allain Lalonde wrote:
  It would seem that indeed, this behavior is intended, though I can't see
  why?
  invalidatePaint() is used to flag nodes as needing a repaint, but the method
  that is primarily responsible for making use of that flag is
  validateFullPaint() which is only gets called from PRoot.processInputs().

  It seems that calling repaint does the same thing, but is correctly handled
  by Swing since it you follow the execution path ultimately, it ends up
  callingin 'component.repaint(...);'.

  Can someone shed some light on why this is the way it's written?

  It would seem to me that a call to repaint() is needed at the end of
  invalidatePaint() so that the need for repainting bubbles up to the
  underlying Component.

  2009/11/2 Allain Lalonde allain.lalo...@gmail.com

  Good eye, invalidate paint just flags the node as needing to be
  repainted. This gets picked up on the next ui cycle. I don't believe
  that it will automatically bubble up the stack, though i will need to
  re-read the code to confirm this. I will examine your code when i get
  home to see if i can repelicate your issue.

  Thank you for bringing this up.

  On 02/11/2009, Nigel nigel.tamp...@f2s.com wrote:

   The comment in PNode.java reads

       // When you do create you own nodes the only method that you will
       // normally need to call is invalidatePaint. This method marks the
       // nodes as having invalid paint, the root node's UI cycle will
   then
       // later discover this damage and report it to the Java repaint
   manager.

   However, I find that after calling invalidatepaint() the repaint
   manager does not discover the damage unless other events are occurring
   (such as moving the mouse around).

   I was expecting that calling invalidatePaint would result in the node
   being repainted some time later - am I doing something wrong or is
   Piccolo?

   Here is a contrived example (ClockNode.java) - It displays 4 custom
   nodes, each showing a clock's second hand.  The top 2 clocks are
   updated from within the event dispatch thread, the lower 2 clocks are
   updated from another thread.  The 2 clocks on the left are redrawn via
   a call to repaint and the 2 on the right via a call to
   invalidatePaint.

   if you run it you'll see the clock on the right (calling
   invalidatePaint) are only redrawn if you are moving your mouse over
   the window.

   ClockNode.java

   import java.awt.*;
   import java.awt.geom.*;
   import edu.umd.cs.piccolo.*;
   import edu.umd.cs.piccolo.util.*;

   import edu.umd.cs.piccolox.PFrame;

   public class ClockNode extends PNode {

         private GeneralPath secondHand;
         private int tseconds;   // 10ths of seconds

         public void tick() {
                 tseconds++;
                 if ( tseconds = 600 ) { tseconds = 0; }
                 // inform Piccolo that the node needs to be redrawn
                 if ( useRepaint ) {
                         repaint();
                 } else {
                         invalidatePaint();
                 }
         }

         private boolean useRepaint;
         public ClockNode(boolean useRepaint) {
                 this.useRepaint = useRepaint;
                 // create the needle shape
                 secondHand = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
                 secondHand.moveTo(-0.1,0);
                 secondHand.lineTo(0,1);
                 secondHand.lineTo(0.1,0);
                 secondHand.closePath();
         }

         public void paint(PPaintContext aPaintContext) {
                 Graphics2D g2 =
   (Graphics2D)aPaintContext.getGraphics().create(); //
   create - as we mess with the transform
                 g2.setPaint(Color.BLACK);
                 //draw the face
                 g2.draw( new Ellipse2D.Double( getX(), getY

[piccolo2d-dev] invalidatePaint does not cause a repaint unless other events are occurring

2009-11-02 Thread Nigel


The comment in PNode.java reads

// When you do create you own nodes the only method that you will
// normally need to call is invalidatePaint. This method marks the
// nodes as having invalid paint, the root node's UI cycle will
then
// later discover this damage and report it to the Java repaint
manager.


However, I find that after calling invalidatepaint() the repaint
manager does not discover the damage unless other events are occurring
(such as moving the mouse around).

I was expecting that calling invalidatePaint would result in the node
being repainted some time later - am I doing something wrong or is
Piccolo?


Here is a contrived example (ClockNode.java) - It displays 4 custom
nodes, each showing a clock's second hand.  The top 2 clocks are
updated from within the event dispatch thread, the lower 2 clocks are
updated from another thread.  The 2 clocks on the left are redrawn via
a call to repaint and the 2 on the right via a call to
invalidatePaint.

if you run it you'll see the clock on the right (calling
invalidatePaint) are only redrawn if you are moving your mouse over
the window.

ClockNode.java

import java.awt.*;
import java.awt.geom.*;
import edu.umd.cs.piccolo.*;
import edu.umd.cs.piccolo.util.*;

import edu.umd.cs.piccolox.PFrame;

public class ClockNode extends PNode {

private GeneralPath secondHand;
private int tseconds;   // 10ths of seconds

public void tick() {
tseconds++;
if ( tseconds = 600 ) { tseconds = 0; }
// inform Piccolo that the node needs to be redrawn
if ( useRepaint ) {
repaint();
} else {
invalidatePaint();
}
}

private boolean useRepaint;
public ClockNode(boolean useRepaint) {
this.useRepaint = useRepaint;
// create the needle shape
secondHand = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
secondHand.moveTo(-0.1,0);
secondHand.lineTo(0,1);
secondHand.lineTo(0.1,0);
secondHand.closePath();
}

public void paint(PPaintContext aPaintContext) {
Graphics2D g2 = 
(Graphics2D)aPaintContext.getGraphics().create(); //
create - as we mess with the transform
g2.setPaint(Color.BLACK);
//draw the face
g2.draw( new Ellipse2D.Double( getX(), getY(), getWidth(), 
getHeight
() ));
//draw the second hand
g2.translate(getX()+getWidth()/2,getY()+getHeight()/2); // 
translate
hand so 0,0 is centre of bounds
g2.scale(getWidth()/2,getHeight()/2); // scale the needle
g2.rotate ( Math.toRadians((tseconds*6)/10f+180) );
g2.fill(secondHand);
}

public static void main(String args[]) {
// top left clock - uses repaint
final ClockNode clockNode1 = new ClockNode(true);
clockNode1.setBounds(0,0,200,200);
// top right clock - uses invalidatePaint
final ClockNode clockNode2 = new ClockNode(false);
clockNode2.setBounds(210,0,200,200);
// lower left clock - uses repaint
final ClockNode clockNode3 = new ClockNode(true);
clockNode3.setBounds(0,210,200,200);
// lower right clock - uses invalidatePaint
final ClockNode clockNode4 = new ClockNode(false);
clockNode4.setBounds(210,210,200,200);

PFrame pFrame = new PFrame() {
public void initialize() {

getCanvas().getLayer().addChild(clockNode1);

getCanvas().getLayer().addChild(clockNode2);

getCanvas().getLayer().addChild(clockNode3);

getCanvas().getLayer().addChild(clockNode4);
setSize(500,500);
}
};

// tick clocks 1 and 2 in the event dispatch thread
new javax.swing.Timer(100, new java.awt.event.ActionListener() {
  public void actionPerformed
(java.awt.event.ActionEvent evt) {
  clockNode1.tick();
  clockNode2.tick();
  }
  }).start();

// tick clocks 3 and 4 in the timer thread
java.util.Timer timer = new java.util.Timer(true);
timer.schedule( new java.util.TimerTask() {
   

[piccolo2d-dev] TooltipExample.java event.getPickedNode()

2009-10-23 Thread Nigel

I notice that the following line in TooltipExample.java

   final PNode n = event.getInputManager().getMouseOver().getPickedNode
();

Could be rewritten simply as:

   final PNode n = event.getPickedNode();

--~--~-~--~~~---~--~~
Piccolo2D Developers Group: http://groups.google.com/group/piccolo2d-dev?hl=en
-~--~~~~--~~--~--~---