Friday, October 14, 2011

WPF:Adding a custom symbol in middle of Edge of yFiles graph.

I am working on yFiles graph. I was need to create a custom symbol in yFiles Edge. But that type of symbol does not exist in yFiles default implementation. But yFiles has given option to customize the visual of Edge overriding CreateVisual or UpdateVisual. My requirement is very simple. I want to create a symbol ( double line ) in middle of Edge. We have to consider numbers of bends associated with that Edge to keep it middle of Edge. You can see the added symbol in middle of Edge. So lets see how can we add this.

image

First lets consider how arrow at Target or Source endpoint of Edge is added. Arrow is created as visual which can have any shape. it can be like “X” . But how is it added to an Edge endpoint. We know the location of Edge endpoint (edge.SourcePort.Location). So it is very easy to place that symbol at that position. But there have two problem . One when Edge is moving you need to place the Symbol at endpoint of Edge always even when node is moving. And second problem is you need to move the direction of Arrow symbol to the direction where your edge is going as Arrow symbol indicate the flow of your edge. As a matter of graphics, you need to make translation of symbol to the endpoint of Edge always when it is moving or you can say UpdateVisual is called. And you also need to add to consider rotation to the angel which direction the Edge endpoint is moving. There have a simple matrix which is used to provide the translation and rotation in xy plane. (http://cse.taylor.edu/~btoll/s99/424/res/mtu/Notes/geometry/geo-tran.htm).
Here the equation of matrix is :
          


   For that equation we will need to know the (h,k) which is middle of Edge. I also have consider number of bends within edge so that we can always put the  symbol in middle of edge. the cosA and sinA can be determined from right triangle. see equestion of determining sinA and cosA here http://en.wikipedia.org/wiki/Angle .  Here you can see the formula of determining the cosA and sinA from two adjacent bends of middle of edge.
   1: IPoint startBend = null;
   2:           IPoint endBend = null;
   3:           PointD direction = new PointD();
   4:           if (edge.Bends != null && edge.Bends.Count > 2)
   5:           {
   6:               var bendCount = edge.Bends.Count + 1;
   7:               var startBendPos = bendCount/2;
   8:               startBend = edge.Bends[startBendPos - 1].Location;
   9:               endBend = edge.Bends[startBendPos].Location;
  10:           }
  11:  
  12:           else
  13:           {
  14:               startBend = edge.SourcePort.Location;
  15:               endBend = edge.TargetPort.Location;
  16:           }
  17:           var middleX = (startBend.X > endBend.X)
  18:                             ? endBend.X + (startBend.X - endBend.X)/2
  19:                             : startBend.X + (endBend.X - startBend.X)/2;
  20:           var middleY = (startBend.Y > endBend.Y)
  21:                             ? endBend.Y + (startBend.Y - endBend.Y)/2
  22:                             : startBend.Y + (endBend.Y - startBend.Y)/2;
  23:          
  24:  
  25:           double x = startBend.X - endBend.X;
  26:           double y = startBend.Y - endBend.Y;
  27:           double r = Math.Sqrt(x*x + y*y);
  28:           if (r > 0.0)
  29:           {
  30:               double cosA = x/r;
  31:               double sinA = y/r;
  32:  
  33:               direction = new PointD(cosA, sinA);
  34:           }
  35:  
  36:           var customSymbol = new MyCustomSymbol();
  37:           Visual visual1 = customSymbol.GetPaintable(edge, true, new PointD(middleX, middleY), direction).CreateVisual(context);
  38:           container.Add(visual1);
When the symbol is created it call CreateVisual of IArrow class. there we have to transform our custom angle direction with determined sinA and cosA and (h,k) which is middle of Edge. The transformation code with the above transformation 2D matrix is
   1: frameworkElement.RenderTransform = (Transform)new MatrixTransform()
   2:            {
   3:                Matrix = new Matrix(direction.X, direction.Y, -direction.Y, direction.X, anchor.X, anchor.Y)
   4:            };

here according to the above formula direction.X is cosA, direction.Y is sinA, anchor.X is h, and anchor.Y is k. using this metrix equation when Edge is moved the symbol is moved to same direction of Edge and set to the middle position of Edge.
source code will be attached here after few refactoring.