Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 432854

Summary: [visualizer] Add a new graphic object class supporting nesting, virtual bounds and automatic scaling
Product: [Tools] CDT Reporter: Marc Dumais <marc.dumais>
Component: cdt-otherAssignee: Marc Dumais <marc.dumais>
Status: RESOLVED FIXED QA Contact: Doug Schaefer <cdtdoug>
Severity: enhancement    
Priority: P3 CC: WilliamRSwanson, xraynaud
Version: Next   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard:
Bug Depends on:    
Bug Blocks: 432055, 432908    

Description Marc Dumais CLA 2014-04-15 14:25:32 EDT
The idea is to extend the GraphicObject class to create a new type of graphic object that can serve as a container for other graphic objects. 

In the visualizer, the bounds of a graphical object are defined as the rectangle bounding the object: (x,y,w,h). The x and y parts are the absolute coordinates of the upper left corner of the rectangle. w and h are the width and height of the rectangle.  The unit is a pixel.

The container class would add a new set of virtual bounds, of arbitrary unit (later converted to pixel through a scaling operation). Another thing is that the (x,y) part of the virtual bounds are defined relative to the parent container object, instead of being an absolute canvas coordinate. This way, graphical objects can be defined in a relatively easy, encapsulated, way. 

The proposed Epiphany Visualizer (see bug 432055) uses a similar container object, but it extends the MulticoreVisualizerGraphicObject class; the goal here is to bring this lower in the class hierarchy, so it can be used by any visualizer.
Comment 1 Marc Dumais CLA 2014-04-15 14:30:13 EDT
Here is a simple example with one container that has 2 child objects, once of which has a child itself. Let's say we create the objects using the following virtual bounds:

parent virtual bounds: (0,0,10,10)  
   child1 virtual bounds: (1,1,3,3)
      grandchild1 virtual bounds: (1,1,1,1) note: (x,y)=(1,1) are relative to parent. Relative to grand-parent, it's really (2,2)
   child2 virtual bounds: (8,8,1,1)

Let's say this is to be displayed on a canvas of pixel-dimensions 100x100, which is set as the (w,h) bounds of the parent object.

The objects will end-up with the following canvas bounds:

parent canvas bounds: (0,0,100,100)
   child1 canvas bounds: (10,10,30,30)
      grandchild1 canvas bounds: (20,20,10,10)
   child2 canvas bounds: (80,80,10,10)


If the canvas is re-sized to 200x200, which is set as the (w,h) bounds of the parent object, the objects will be re-sized to the following canvas-bounds:

parent canvas bounds: (0,0,200,200)
   child1 canvas bounds: (20,20,60,60)
      grandchild1 canvas bounds: (40,40,20,20)
   child2 canvas bounds: (160,160,20,20)
Comment 2 Marc Dumais CLA 2014-04-15 15:17:44 EDT
Gerrit review: 

https://git.eclipse.org/r/#/c/25079/
Comment 3 Marc Dumais CLA 2014-04-16 10:53:32 EDT
Xavier asked a question on the gerrit page for this bug:
"Hummm.... just a naive question: are we re-inventing draw2d ?"

I hope not. I have stumbled upon draw2d sometime last fall, before starting this effort. I did a quick investigation and came out unsure if it would be a gain to use it compared to implementing the relatively small enhancements I needed for the Epiphany visualizer. 

Xavier, do you have experience using draw2d? Do you know if it's used already anywhere in CDT?
Comment 4 Xavier Raynaud CLA 2014-04-16 11:02:38 EDT
Yes, I've some experience using drawd2. It is used in Kalray products:
https://www.flickr.com/photos/109313425@N06/12655277894/

As you can see, it's very close to the multicore visualizer.
As far as I know, draw2d is not used in CDT ("git grep draw2d" returns nothing).
Comment 5 Marc Dumais CLA 2014-04-17 06:46:44 EDT
Thanks Xavier. The screenshot does looks a lot like the Epiphany Visualizer. 

From what you know, would it be relatively easy to use draw2d in the visualizer framework? It would be interesting to see a quick and dirty proof-of-concept of this idea.
Comment 6 Xavier Raynaud CLA 2014-04-17 10:25:16 EDT
I think so. It should be relatively easy.
I can try to do a proof-of-concept. However, I'm a bit busy these days - much more than last month. I can't make any promise.
Comment 7 Marc Dumais CLA 2014-04-25 11:29:47 EDT
Working on bug 432908, I have noticed a few things to improve with the VirtualBoundsGraphicObject class. I will be pushing a new patch to address them:

- I noticed an integration issue with GraphicCanvas#paintCanvas(). This class does two separate calls per graphic object: one to paint an object and a second one to paint the object's decorations, if the object reports having decorations. This doesn't work well with a VirtualBoundsGraphicObject that has no decoration, but has child object(s) that does; the decoration(s) are not be painted. 

I tried a few things to fix this, but none were totally satisfactory. The solution I ended-up with fixes the issue, but the ability to chose to paint the decorations separately of the object is lost. That seems a worthy trade-off, but open to suggestions. 

- Added a null check in VirtualBoundsGraphicObject#paint() before restoring the background/foreground color, to avoid an exception if restoring a null color. 

- It seems a bit strange that, by default, a VirtualBoundsGraphicObject is created with its bounds set to not being drawn (i.e. setDrawContainerBounds(false)). I think it feels more natural to do the opposite. 

- Added a setter to permit setting the margin to be used between a child and parent object, and renamed the field to better reflect its use.
Comment 8 William Swanson CLA 2014-04-25 12:06:19 EDT
(In reply to Marc Dumais from comment #7)
> 
> - I noticed an integration issue with GraphicCanvas#paintCanvas(). This
> class does two separate calls per graphic object: one to paint an object and
> a second one to paint the object's decorations, if the object reports having
> decorations. This doesn't work well with a VirtualBoundsGraphicObject that
> has no decoration, but has child object(s) that does; the decoration(s) are
> not be painted. 

> I tried a few things to fix this, but none were totally satisfactory. The
> solution I ended-up with fixes the issue, but the ability to chose to paint
> the decorations separately of the object is lost. That seems a worthy
> trade-off, but open to suggestions. 

Could you describe the issue(s) you encountered? My first thought is
to have the parent graphic object report the logical-OR of its own
hasDecorations() state and that of its direct children, and then,
when the decorations need to be painted, simply delegate to its direct
child objects' decoration methods even when it has no decorations
of its own. If a child has nothing to draw, the parent could optimize
by checking hasDecorations() on the child, or the child's decoration
code could simply be a no-op that doesn't mind if it's called.
Comment 9 Marc Dumais CLA 2014-04-25 13:55:31 EDT
Hi Bill,

(In reply to William Swanson from comment #8)
> 
> Could you describe the issue(s) you encountered? My first thought is
> to have the parent graphic object report the logical-OR of its own
> hasDecorations() state and that of its direct children, and then,
> when the decorations need to be painted, simply delegate to its direct
> child objects' decoration methods even when it has no decorations
> of its own. If a child has nothing to draw, the parent could optimize
> by checking hasDecorations() on the child, or the child's decoration
> code could simply be a no-op that doesn't mind if it's called.

Something very close to what you describe was my first though as well. The only change I had to do to get this to work was to override method VirtualBoundsGraphicObject#hasDecorations() so that it goes though its children; if any one of them has decorations, the parent returns true. The painting part, delegating the painting of decorations to each children object, was already working as you describe above (i.e. nothing further to do for that to work).

The downside I saw of this way of fixing the issue is that a derived class can override VirtualBoundsGraphicObject#hasDecorations() in a parent object, possibly breaking the mechanism. (only a problem I think if the overriding method returns false and has children with decorations). 

I will push a new patch with that alternative version of the fix instead so you can have a look. You can tell me if you like one better than the other or have other ideas.

Thanks!

Marc
Comment 10 William Swanson CLA 2014-04-25 15:18:28 EDT
(In reply to Marc Dumais from comment #9)
> 
> The downside I saw of this way of fixing the issue is that a derived class
> can override VirtualBoundsGraphicObject#hasDecorations() in a parent object,
> possibly breaking the mechanism. (only a problem I think if the overriding
> method returns false and has children with decorations). 

The new version (Patch #9) looks good to me.

You should add a javadoc comment to the hasDecorations() override
noting the recusive check of the children as behavior that
a derived type will want to preserve.

You might also put the child-check loop in its own method (e.g. hasChildrenWithDecorations()) just to make it blindingly obvious
to someone who wants to override the method.
Comment 11 Marc Dumais CLA 2014-04-28 14:25:48 EDT
Merged into master branch.