Developer's FAQ

Here you'll find answers to commonly asked questions about all three versions of Piccolo2D. If you don't find your answer here, you can always post it to the piccolo2d-users mailing list.

Is Piccolo2D thread-safe?

No. Piccolo2D is not thread-safe. Piccolo2D has been designed from the start to run only in a single thread (usually the event dispatch thread). This constrains the way Piccolo2D applications can be built, but we feel that overall, it increases usability by simplifying code, and eliminating thread-debugging issues.

Of course, you can still build multi-threaded applications.  The key is to invoke any updates on the main event dispatch thread.  For java, use the invokeLater() method and for .NET use BeginInvoke().

Why is my app so slow?

The best way to see what's taking so long is to run your code in a profiler. Often the results will surprise you. If you can't get a profiler, then you might try commenting out parts of you application and seeing how that affects performance.

If you are drawing a large number of nodes, rendering may be the bottleneck. There two ways to tackle this problem: make the individual nodes draw faster, or draw fewer nodes. Drawing strokes that have a width of more then one pixel seems to be a fairly expensive operation. If you are using PPath, try setting the stroke paint or pen to null and see how that affects performance. If you need to render the strokes, you could try setting the stroke width to zero, which has the effect of always drawing a one-pixel wide stroke. For PImage nodes, you might try using lower resolution images during animated transitions. Drawing fewer nodes is more difficult. You could try a semantic zooming pattern, where you decide what to draw based on the scale that a node is being viewed at. For instance, you might have one large parent node that contains many smaller child "detail" nodes. To improve performance, you could only draw those detail nodes when you are zoomed in on them.

If you try the above approaches and things are still slow, your application may be caught in a soft infinite loop. This loop is created when you change an attribute in your scene that requires a repaint, while you are in the process of painting your scene. It will create a problem where you CPU will be active even when your not interacting with the scene graph. You can check if you are in this situation by setting the PDebug.debugPaintCalls flag.  It will print a warning to System.err in Java and System.Console.Error in .NET if repaint is called when the canvas is getting painted.  Note this might happen occasionally when the application is loaded and initialized, but if it turns into a recursive process something is going wrong.

Why are the bounds of my nodes incorrect?

You may be confused between bounds and transforms.  There are two ways to modify the position and size of a node. You can modify the node's bounds or you can modify the affine transform that is applied to that node.

Each node has an affine transform that defines a local coordinate system for that node.  If you're not sure what that means, check out the Geometric Transformations section of the Graphics Primer.  If you position a node by calling a method like TranslateBy(), RotateBy(), ScaleBy(), OffsetBy(), and many others, you are modifying the node's transform.  Each transformation you apply defines a new coordinate system, with a new origin.  For example, if you set a node's bounds to (0, 0) and then translate node by (10, 10) you will define a new coordinate system for that node with an origin at (10, 10).  See the Coordinate Systems section of the Graphics Primer.  The bounds however will still be at location (0, 0) because they are stored in the local coordinate system of the node.  That is (0, 0) now refers to the new origin at (10, 10) in global coordinates.

Local versus global coodinates.

Transforms are also hierarchical.  That means if you modify a node's transform, it will affect that node and all of its children.  Let's suppose you add a child to the node described above and set its bounds to (0, 0).  The new node will appear at (10, 10) because the transform of the parent is applied down the hierarchy.  Now, let's suppose you translate the new node by (10, 10).  Where will it appear?  At (20, 20).  You have now defined a new local coordinate system for the child node with an origin at (20, 20).  Remember the bounds will still be at (0, 0) with respect to the new local coordinate system.  Check out the Matrix Transformations section of the Graphics Primer to see what's really going on here.

So, what exactly are bounds then?  You can think of a node's bounds as a rectangular bounding box that is guaranteed to be large enough to contain whatever a node renders, which also happen to be stored in the local coordinate system of the node.  If you position a node by setting the X, Y, Width, Height, or Bounds properties, you are setting the node's bounds.  Bounds are used by framework for efficient rendering and picking and can be useful to Piccolo2D users as well.  Bounds are not hierarchical.  In the above example if we set the parent node's bounds to (10, 10), the parent will move to (10, 10), but the child will not be affected since modifying a node's bounds will not change it's coordinate system.

Setting a node's bounds will have different effects on different types of nodes, depending on how they are implemented.  But, in general, nodes will change how the render to accommodate the new bounds.  For example, setting the bounds of a PImage will simply scale the rendered image to the new bounds.  Setting the bounds of a PText will clip the text.  But, if the ConstrainWidthToTextWidth or ConstrainHeightToTextHeight flags are set, setting the width or height respectively will have no effect.  Finally, setting the bounds of a PPath will scale the points to fit within the new bounds.

Why don't PPath's bounds match the points in the path?

PPath contains a set of points that are rendered with a pen.  But, the bounds will not be exactly equal to the bounding box of the points in the path.  Remember, the bounds are at least as big as the object, they are not a representation of the object.  Since paths have a pen with a default width of 1, the bounds will be the bounding box of the points in the path, plus half the pen width on all sides (.5).  So, what happens when you set the bounds of a path?  Piccolo2D will scale the points to fit within the new bounds.  So, if you set the width to 60, the rectangle will have a width of 59, with the pen extending .5 on either side.

How do I compare bounds in different coordinate systems?

With potentially thousands of different coordinate systems in a single piccolo2d interface, how do you compare the bounds of two nodes?  Luckily there are several methods to help you convert between coordinate systems.  See the Coordinate Systems section of Piccolo2D Patterns for details.

How do I deploy Piccolo2D on the Web?

In Piccolo2D.Java,

You can deploy applets that use Piccolo2D.  However, the Java Runtime will need to be installed on the client machine and the applets are subject to the browser's security settings.  To create a Java applet that uses piccolo2d do the following:

  1. Extend the PApplet class, located in the extras package, and add your piccolo2d code to the initialize method.
  2. Compile your applet with piccolo2d.
  3. Copy the Piccolo2D JARs and class files to a location on the webserver.
  4. Finally create an html file with an applet tag to load the applet.  If your applet class was called MyApplet, the applet tag might look something like this:
    <applet archive="piccolo.jar, piccolox.jar" code="MyApplet.class" width=200 height=100></applet>
    

In Piccolo2D.NET,

You can embed a .NET control, that uses piccolo2d, in Internet Explorer.  The .NET Runtime will need to be installed on the client machine.  In practice, this will limit you to windows-based clients.  And, this approach will not work with browsers other than Internet Explorer.

The .NET security model places additional restrictions on embedding controls in Internet Explorer.  If you are deploying your control locally, you won't have too much trouble.  But, if you are deploying it over the web, you might run into some roadblocks.  The default Internet Zone security settings on most machines will not allow you to load external dlls, which will be a problem if your applet references Piccolo2D.  The default security settings also restrict some of Piccolo2D's keyboard input code from running.  To solve this problem, you could have the client fully trust your site, or decrease their security settings.  But, that might not be desirable.  Alternatively, you can do a couple of hacks to get your control to work for most of the world.  This approach is described below.

  1. Uncomment the line "#define WEB_DEPLOY" from PCanvas and recompile Piccolo2D.NET.  This will exclude any code that violates the default Internet Zone settings.
  2. Create a new class library project that references the DLLs created in step 1.
  3. Extend PCanvas to create a new control.
  4. Compile your project to create a new DLL that contains your control.
  5. Merge your DLL with the Piccolo2D DLLs using a tool like ILMerge.
  6. Copy the DLL created in step 5 to a location on the webserver.
  7. Finally, create an html file with an object tag to load the control.  If your control class was called MyControl and you named the merged DLL Lib.dll, then the object tag might look something like this:
    <object classid="Lib.dll#MyControl" height="400" width="300"></object>
    

Why do I get unpredictable results when I create two activities that act on the same object?

Each activity has a start time, which by default is set to the current time.  Suppose you call one of PNode's animate methods multiple times:

node.AnimateToPositionScaleRotation(0, 150, 1, 0, 1000);
node.AnimateToPositionScaleRotation(150, 0, 1, 0, 2000);

You might think the node will first animate to (0, 150) and then animate to (150, 0).  Instead, both activities occur simultaneously.  Each time piccolo2d processes activities, it will give both animations a chance to step.  It will move the node a bit in one direction and then a bit in the other direction.  But, since updates are merged, it will appear as if only the first activity is executing.  Then, since the second activity has a longer duration, the node will jump to the middle of the second animation and continue.  This is probably not the intended behavior.  The solution is to set the start time.

PActivity a1 = n.AnimateToPositionScaleRotation(0, 150, 1, 0, 1400);
PActivity a2 = n.AnimateToPositionScaleRotation(150, 0, 1, 0, 2000);
a2.StartTime = a1.StartTime + a1.Duration;

Now, the node will animate all the way to (0, 150) and then to (150, 0), as expected.

How do I control the order of activities?

You can control the order of activities by setting each activity's start time.  And, you can use the root node's global time as the current time.  So, if you want an activity to start four seconds from now, you could do the following:

activity.StartTime = Canvas.Root.GlobalTime + 4000;

PActivity also has a convenience method to schedule an activity to start after another activity:

secondActivity.StartAfter(firstActivity);

Internally, this just sets the start time of secondActivity to be firstActivity's start time plus its duration.

Why is it safe to modify an activity after I create it?

You might wonder why you can schedule an activity and then set its start time, or any of its properties for that matter.  The reason is because the activity does not start running "immediately," even if its start time is set to the current time.  When you schedule an activity with the root or call an animate method, the activity is added to an internal list stored in the PActivityScheduler class.  PActivityScheduler also stores a windows timer.  The scheduled activity will be given its first chance to step the next time the timer fires, which will be some time after the current method ends.