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

Bug 355968

Summary: Only ChopboxAnchors should compute the reference point as the center of its GA.
Product: [Modeling] Graphiti Reporter: Hernan Gonzalez <hjg.com.ar>
Component: CoreAssignee: Project Inbox <graphiti-inbox>
Status: CLOSED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: matthias.gorning, michael.wenz
Version: 0.8.0Flags: michael.wenz: juno+
Target Milestone: 0.9.0   
Hardware: All   
OS: All   
See Also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=359219
Whiteboard: Juno M4 Theme_bugs

Description Hernan Gonzalez CLA 2011-08-26 14:04:17 EDT
Build Identifier: 20110301-1815

Positioned anchors (FixPointAnchor and BoxRelativeAnchor) should assume that their "referenceLocation" is the position of the anchor, not the center of its GA or shape. The current behaviour goes against the concept as explained in the tutorial, one cannot for example define an anchor on the border of the rectangle an assign a smaller rectangle to the anchor, because the connections will assume a referencepoint in the middle of the small rectangle.

For example, currently, if one defines a BoxRelativeAnchor at (0,0.5) 

Reproducible: Always

Steps to Reproduce:
1. Create a Shape with a Rectangular GA, position (100, 100) size (40,60)
2. Add a BoxRelativeAnchor width=0, height=0.5; it would be located then at the middle of the left border, absolute coordinates (100,130) 
3. Assign to the anchor a Rectangle, position (0, -10) size (20, 20) (just to the right of the anchor, inside the bigger rectangle, on the left border)
4. Draw a connection: it's seen that it doesnt comes from the (100,130) reference point, but from the center of the small rectangle
Comment 1 Michael Wenz CLA 2011-08-29 06:57:59 EDT
Not sure if it should be the position of the anchor or rather the center of the GA assigned to the anchor? But definitely not the center of the GA of the shape.
Comment 2 Hernan Gonzalez CLA 2011-08-29 07:50:34 EDT
(In reply to comment #1)
> Not sure if it should be the position of the anchor or rather the center of the
> GA assigned to the anchor? 

I hitnk it should be the position of the anchor. That's conceptually logical (for me), it's what one thinks as the endpoint of the connection. And, elsewhere, it would be impossible to get the desired behaviour, as in the (common) tutorial scenario: an anchor on the border of the parent rectangle, with connections starting precisely from the border.
Comment 3 Matthias Gorning CLA 2011-09-28 08:30:43 EDT
The center of the anchor's GA will be furthermore the "referenceLocation" of the anchor. The "referenceLocation" is the location where connections can start or end. We think that's a good default solution for the most anchors.

But we understand your problem. Therefore with this fix you will get a new method on a FixPointAnchor/BoxRelativeAnchor:

setUseAnchorLocationAsConnectionEndpoint(boolean value); 

With this method you can achieve the anchor behaviour what you want.
Comment 4 Matthias Gorning CLA 2011-09-28 08:35:56 EDT
I've also created a bugzilla for the improvement of the anchor documentation.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=359219
Comment 5 Hernan Gonzalez CLA 2011-10-10 09:19:09 EDT
I'm getting errors on the master branch because of references to a AdvancedAnchor class that is not present - perhaps some incomplete commit?
Comment 6 Michael Wenz CLA 2011-10-10 09:51:48 EDT
I can see AdvancedAnchor in the master brand (commited 13 days ago). Could ther be a sync issue on your side?
Comment 7 Hernan Gonzalez CLA 2011-10-10 10:04:17 EDT
Mmmm I got it now. Apparently EGit "compare with" does not show differences if there are new files on the remote?  Never mind...
Comment 8 Hernan Gonzalez CLA 2011-10-18 12:24:24 EDT
The current fix, together with the ChopboxAnchorFixed.getLocation(Point) implementation, is not precise. For example, suppose a define a FixPointAnchor at coordinate (180, 72) (assume it's already translated to absolute), and assume a connection is drawn from remote reference point (15,506) The locationPoint should return (180, 72), but it returns (180,73).

The problem is that the  GFChopboxAnchor.getBox() returns (for a 
advancedAnchor with isUseAnchorLocationAsConnectionEndpoint() == true) a 1x1 rectangle. Then (in the example),  GFChopboxAnchor.getLocation() will assume the center is at (180.5, 72.5), which is not what we want: we want the center at (180, 72).

I propose this fix (works for me)

In GFChopboxAnchor.getBox() , be real, return a 0x0 rectangle:

 copy.setHeight(0);
 copy.setWidth(0);

In ChopboxAnchorFixed.getLocation(Point) alter the calculation to avoid division by zero:

	public Point getLocation(final Point reference) {
		Rectangle r = Rectangle.SINGLETON;
		r.setBounds(getBox());
		getOwner().translateToAbsolute(r);
		float centerX = r.x + 0.5f * r.width;
		float centerY = r.y + 0.5f * r.height;
		float dx = reference.x - centerX;
		float dy = reference.y - centerY;
		float tmax = Math.max(Math.abs(dx) * r.height, Math.abs(dy) * r.width);
		// CHANGED: in case of "nearly zero" (divide-by-zero or
		// rounding-problems) would happen: return just the center
		if (tmax >= 0.001f) {
			float scale = r.width * 0.5f * r.height / tmax;
			centerX += dx * scale;
			centerY += dy * scale;
		}
		return new Point(Math.round(centerX), Math.round(centerY));
	}

GefService.getChopboxLocationOnBox() and PeServiceImpl.getChopboxLocationOnBox() should be modified analogously  - we should never assume that r.width or r.height are non-zero.
Comment 9 Michael Wenz CLA 2011-10-19 07:53:42 EDT
We need to check this
Comment 10 Hernan Gonzalez CLA 2011-10-19 22:15:51 EDT
To ease the testing, and perhaps to reuse in the other methods, we could define a static helper method:

  /**
   * Computes the "location", i.e. the point at which the line that joins the
   * rectangle center with the reference point crosses the rectangle border.
   * Works with zero size rectangles.
   */
  public static Point computeLocation(Rectangle r, Point reference) {
    float centerX = r.x + 0.5f * r.width;
    float centerY = r.y + 0.5f * r.height;
    float dx = reference.x - centerX;
    float dy = reference.y - centerY;
    float tmax = Math.max(Math.abs(dx) * r.height, Math.abs(dy) * r.width);
    if (tmax >= 0.001f) {
      float scale = r.width * 0.5f * r.height / tmax;
      centerX += dx * scale;
      centerY += dy * scale;
    }
    return new Point(Math.round(centerX), Math.round(centerY));
  }

  public Point getLocation(Point reference) {
    Rectangle r = Rectangle.SINGLETON;
    r.setBounds(getBox());
    getOwner().translateToAbsolute(r);
    Point location = computeLocation(r, reference);
    return location;
  }
Comment 11 Hernan Gonzalez CLA 2011-10-22 23:54:56 EDT
There is a more basic problem here. This implementation of AdvancedAnchors seems to assume that the anchor is in the corner of its GA. In other words: the position of the Anchor is assumed to be the position of its GA. Say a want my anchor to be inside its GA (a rectangle), not in the border, nor in the center. Currently it's impossible to do that.
Comment 12 Michael Wenz CLA 2011-10-24 04:23:06 EDT
(In reply to comment #11)
> There is a more basic problem here. This implementation of AdvancedAnchors
> seems to assume that the anchor is in the corner of its GA. In other words: the
> position of the Anchor is assumed to be the position of its GA. Say a want my
> anchor to be inside its GA (a rectangle), not in the border, nor in the center.
> Currently it's impossible to do that.
That's only the default location. You could do something like this to achive what you want (squareShape is a ContainerShape that may have a Rectangle as representation):
BoxRelativeAnchor relativeAnchor = createService.createBoxRelativeAnchor(squareShape);
relativeAnchor.setRelativeHeight(0.5d);
relativeAnchor.setRelativeWidth(0.5d);
relativeAnchor.setReferencedGraphicsAlgorithm(squareRectangle);
relativeAnchor.setUseAnchorLocationAsConnectionEndpoint(true);
Ellipse anchorEllipse = createService.createEllipse(relativeAnchor);
layoutService.setLocationAndSize(anchorEllipse, 25, 25, 0, 0);

You will get a circle (radius) as connection point and the connection line will point to the center of the square rectangle. (This is taken from the newly checked-in Chess example in AddBoardFeature and defines the anchor in a chess square).
Comment 13 Michael Wenz CLA 2011-10-24 06:55:21 EDT
(In reply to comment #10)
> To ease the testing, and perhaps to reuse in the other methods, we could define
> a static helper method:
In principle agreed, but not sure if using a 0x0 rectangle is the best solution, as it introduces DivisionByZeroExceptions at various spots (also in GEF) we would need to work around. Feasibility check needed, colleague will check after his vacation.
Comment 14 Hernan Gonzalez CLA 2011-11-07 10:07:54 EST
(In reply to comment #12)
> That's only the default location. You could do something like this to achive
> what you want (squareShape is a ContainerShape that may have a Rectangle as
> representation):
> BoxRelativeAnchor relativeAnchor =
> createService.createBoxRelativeAnchor(squareShape);
> relativeAnchor.setRelativeHeight(0.5d);
> relativeAnchor.setRelativeWidth(0.5d);
> relativeAnchor.setReferencedGraphicsAlgorithm(squareRectangle);
> relativeAnchor.setUseAnchorLocationAsConnectionEndpoint(true);
> Ellipse anchorEllipse = createService.createEllipse(relativeAnchor);
> layoutService.setLocationAndSize(anchorEllipse, 25, 25, 0, 0);
> 
> You will get a circle (radius) as connection point 

Well, I once had to digest that in Graphiti "a Rectangle is not a Shape", now it seems that an circle can be a point ... ;-)

Seriously, this is utterly confusing, I can't understand the concepts related to anchors. You use a "ReferencedGraphicsAlgorithm" and there is stricly zero information in the docs (including source comments) about what a ReferencedGraphicsAlgorithm is supposed to be, I've tried to guess it and failed, I tried to ask and also failed  http://www.eclipse.org/forums/index.php/t/254053/

There are three GA involved with an anchor (the parent, the anchor own GA, and the "referenced", it's not clear the meaning of each one, it's not clear the positioning.
Comment 15 Hernan Gonzalez CLA 2011-11-07 10:39:48 EST
BTW, in your example 

layoutService.setLocationAndSize(anchorEllipse, 25, 25, 0, 0);

creates a zero sized circle, (it seems strange to me, after noting that zero sized rectangles are dangerous), so that the the "anchor area" or "anchor capture zone" 
(where the anchor gets activated when attempting a connection creation) result in a single pixel -if I'm not mistaken. 
I don't want that, I want the anchor to be a (say) 20x10 rectangle and the anchor end-point to be at (say) at the middle of a vertical side.

For example 


   -------------------------------------
  |                                     |
  |                                     |
  | ----------- RA                      |
  ||           |                        |
  ||           |                        |
  ||A          |                        |
  ||           |                        |
  ||           |                        |
  | -----------                         |
  |                                     |
  |                     RB ------------ |
  |                       |            ||
  |                       |            ||
  |                       |           B||
  |                       |            ||
  |                       |            ||
  |                        ------------ |
  |                                     |
  |                                     |
   -------------------------------------



In this example, we have a 200x200 outer rectangle,
a RA 60x60 rectangle starting at (0,30) with an anchor point
at the middle of its left side 
(coordinates (0,60) relative to the outer rect)
Similarly, the RB rectangle has an anchor point in the
middle of its right side 
I want the rectangles RA RB to act as anchors, i.e.,
that their areas accepts a connection creation when
the mouse is over them. And I want the connection
to start at the points A B
Comment 16 Michael Wenz CLA 2011-11-07 10:49:15 EST
(In reply to comment #14)
> Well, I once had to digest that in Graphiti "a Rectangle is not a Shape", now
> it seems that an circle can be a point ... ;-)
> Seriously, this is utterly confusing, I can't understand the concepts related
> to anchors. You use a "ReferencedGraphicsAlgorithm" and there is stricly zero
> information in the docs (including source comments) about what a
> ReferencedGraphicsAlgorithm is supposed to be, I've tried to guess it and
> failed, I tried to ask and also failed 
> http://www.eclipse.org/forums/index.php/t/254053/
> There are three GA involved with an anchor (the parent, the anchor own GA, and
> the "referenced", it's not clear the meaning of each one, it's not clear the
> positioning.
Yes, indeed this is confusing and lacks some documentation; Bug 359219 targets exactly that and it should clarify especially the refGA and boxes stuff.
Sorry, i somehow missed that forum entry
Comment 17 Michael Wenz CLA 2011-11-08 07:09:03 EST
(In reply to comment #15)
> BTW, in your example 
> layoutService.setLocationAndSize(anchorEllipse, 25, 25, 0, 0);
> creates a zero sized circle, (it seems strange to me, after noting that zero
> sized rectangles are dangerous), so that the the "anchor area" or "anchor
> capture zone" 
> (where the anchor gets activated when attempting a connection creation) result
> in a single pixel -if I'm not mistaken. 
> I don't want that, I want the anchor to be a (say) 20x10 rectangle and the
> anchor end-point to be at (say) at the middle of a vertical side.
> For example 
>    -------------------------------------
>   |                                     |
>   |                                     |
>   | ----------- RA                      |
>   ||           |                        |
>   ||           |                        |
>   ||A          |                        |
>   ||           |                        |
>   ||           |                        |
>   | -----------                         |
>   |                                     |
>   |                     RB ------------ |
>   |                       |            ||
>   |                       |            ||
>   |                       |           B||
>   |                       |            ||
>   |                       |            ||
>   |                        ------------ |
>   |                                     |
>   |                                     |
>    -------------------------------------
> In this example, we have a 200x200 outer rectangle,
> a RA 60x60 rectangle starting at (0,30) with an anchor point
> at the middle of its left side 
> (coordinates (0,60) relative to the outer rect)
> Similarly, the RB rectangle has an anchor point in the
> middle of its right side 
> I want the rectangles RA RB to act as anchors, i.e.,
> that their areas accepts a connection creation when
> the mouse is over them. And I want the connection
> to start at the points A B

This should be perfectly possible by setting the refGa to the outer rectangle and having rectangles of the appropriate sizes as GAs for the anchors. In the chess example we have a special situation because we have the square visualized as rectangle and the box relative anchor only serving as the anchor the connection is drawn to. For actually attaching a connection to, there is a second chopbox anchor in place. So the visualization using 0 as diameter is ok since the shape is not meant for grabbing.
The solution is not really satisfying to me as well but I could not come up with a better one under these circumstances:
1) Attaching a connection shall be possible to the complete square (or piece) shape
2) The connection shall not be clipped at the square (or piece) shape border
3) The GA of the box relative anchor must not overlay the square or piece shape for selection
Comment 18 Matthias Gorning CLA 2011-11-28 08:50:03 EST
Fixed. Location of an advanced anchor is now precise (only in cases where the location is used as a connection endpoint).

commit 55c41afcc6f8a80ad0a5a7e072f439de5401610a
Author: mgorning <matthias.gorning@sap.com> 2011-11-28 14:36:58
Committer: mgorning <matthias.gorning@sap.com> 2011-11-28 14:36:58
Parent: 8a058f973e54f8720a0b14e618d21786e92661f3 (Bug 355968 - Only ChopboxAnchors should compute the reference point as the center of its GA / returned location is more precise)
Branches: origin/master, master
Comment 19 Michael Wenz CLA 2011-11-30 04:20:04 EST
Position does no longer depend on the center of a rectangle but instead directly on the anchor position. Looks good to me.
Comment 20 Michael Wenz CLA 2012-04-11 10:44:34 EDT
Bookkeeping: Set target release
Comment 21 Michael Wenz CLA 2012-06-29 04:13:26 EDT
Part of Graphiti 0.9.0 (Eclipse Juno)