none
Adding non-directed edges without changing the layout RRS feed

  • Question

  • Hi,

    Following my previous post, I have managed to add edges to a graph without changing its layout with the following code:

                        var viewerEdge = MsaglEditor.Viewer.CreateEdge(edge.SourceNode, edge.TargetNode);
                        viewerEdge.Edge.Attr.Color = Color.Aqua;
                        viewerEdge.Edge.Attr.ArrowheadAtTarget = ArrowStyle.None;
                        MsaglEditor.Viewer.AddEdge(viewerEdge, true);

    Note that I'm adding the edges undirected. This causes the area where the arrows would have been to remain blank (instead of having the edge continue up to its destination).

    Before: http://img851.imageshack.us/img851/7987/beforev.png

    After: http://img696.imageshack.us/img696/266/afterut.png

    (For some reason I can't upload images directly to this post)

    Note how the selected new edge appears fine, as well as the existing edge whose color I changed. It is only the new (unselected) edges that are "cut short" of their destination.

    I've tried calling GViewer.Refresh() as well as GViewer.Invalidate() to no avail. Recalculating the layout fixes the issue but, of course, defeats the purpose

    • Edited by yosi142 Thursday, October 13, 2011 6:54 PM
    Thursday, October 13, 2011 6:39 PM

Answers

  • Hi Yosi,

    Please replace CreateEdge in IViewer.cs by  

    IViewerEdge CreateEdge(Node source, Node target, bool arrowheadAtSource, bool arrowheadAtTarget);

    Accordingly replace CreateEdge in GViewer.cs by

          public IViewerEdge CreateEdge(Microsoft.Msagl.Drawing.Node source, Microsoft.Msagl.Drawing.Node target, bool arrowheadAtSource, bool arrowheadAtTarget) {
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(source.Id) == source);
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(target.Id) == target);

                Microsoft.Msagl.Drawing.Edge edge = new DrawingEdge(source, target,Connection.Disconnected);
                edge.Label = new Microsoft.Msagl.Drawing.Label();
                Microsoft.Msagl.Edge geometryEdge = edge.Attr.GeometryEdge = new Microsoft.Msagl.Edge();
                geometryEdge.ArrowheadAtSource = arrowheadAtSource;
                geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;
    ...the rest is the same

    In your scenario you need to call CreateEdge(source, target, false, false).

    Happy Sukkot!

     


    Lev Nachmanson
    • Edited by Lev Nachmanson Friday, October 14, 2011 5:48 AM type
    • Marked as answer by Lev Nachmanson Friday, October 14, 2011 5:49 AM
    • Unmarked as answer by yosi142 Friday, October 14, 2011 8:26 PM
    • Marked as answer by yosi142 Saturday, October 15, 2011 2:51 AM
    Friday, October 14, 2011 5:47 AM
  • OK, I think I got it !

    The following seems to work (additions in bold):

            public IViewerEdge CreateEdge(DrawingNode source, DrawingNode target, bool arrowheadAtSource,
                                          bool arrowheadAtTarget)
            {
                Debug.Assert(Graph.FindNode(source.Id) == source);
                Debug.Assert(Graph.FindNode(target.Id) == target);

                var edge = new DrawingEdge(source, target, Connection.Disconnected);
                edge.Label = new Drawing.Label();
                Edge geometryEdge = edge.Attr.GeometryEdge = new Edge();
                geometryEdge.ArrowheadAtSource = arrowheadAtSource;
                geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;
                geometryEdge.Parent = Graph.GeometryGraph;

                Point a = source.Attr.GeometryNode.Center;
                Point b = target.Attr.GeometryNode.Center;
                if (source == target)
                {
                    var start = new Site(a);
                    var end = new Site(b);
                    Point mid1 = source.Attr.GeometryNode.Center;
                    mid1.X += (source.Attr.GeometryNode.BoundingBox.Width/3*2);
                    Point mid2 = mid1;
                    mid1.Y -= source.Attr.GeometryNode.BoundingBox.Height/2;
                    mid2.Y += source.Attr.GeometryNode.BoundingBox.Height/2;
                    var mid1s = new Site(mid1);
                    var mid2s = new Site(mid2);
                    start.Next = mid1s;
                    mid1s.Previous = start;
                    mid1s.Next = mid2s;
                    mid2s.Previous = mid1s;
                    mid2s.Next = end;
                    end.Previous = mid2s;
                    geometryEdge.UnderlyingPolyline = new UnderlyingPolyline(start);
                    geometryEdge.Curve = geometryEdge.UnderlyingPolyline.CreateCurve();
                }
                else
                {
                    var start = new Site(a);
                    var end = new Site(b);
                    var mids = new Site(a*0.5 + b*0.5);
                    start.Next = mids;
                    mids.Previous = start;
                    mids.Next = end;
                    end.Previous = mids;
                    geometryEdge.UnderlyingPolyline = new UnderlyingPolyline(start);
                    geometryEdge.Curve = geometryEdge.UnderlyingPolyline.CreateCurve();
                }

                geometryEdge.Source = edge.SourceNode.Attr.GeometryNode;
                geometryEdge.Target = edge.TargetNode.Attr.GeometryNode;
                geometryEdge.ArrowheadLength = edge.Attr.ArrowheadLength;
                if (!Curve.TrimSplineAndCalculateArrowheads(geometryEdge, geometryEdge.Curve, true))
                    Curve.CreateBigEnoughSpline(geometryEdge);

                var dEdge = new DEdge(DGraph.NodeMap[edge.SourceNode.Id], DGraph.NodeMap[edge.TargetNode.Id], edge,
                                      Connection.Disconnected);
                if (edge.Label != null)
                    dEdge.Label = new DLabel(dEdge, new Drawing.Label());

                dEdge.Edge.Attr.ArrowheadAtSource = arrowheadAtSource ? ArrowStyle.Normal : ArrowStyle.None;
                dEdge.Edge.Attr.ArrowheadAtTarget = arrowheadAtTarget ? ArrowStyle.Normal : ArrowStyle.None;

                return dEdge;
            }

    • Marked as answer by yosi142 Saturday, October 15, 2011 2:51 AM
    Saturday, October 15, 2011 2:35 AM
  • I think I see the problem. The black rectangle probably appears when you try to draw an arrowhead but the arrowhead position in not calculated, so it is Point(0,0) by default. As a result you get a huge arrowhead, with the base at (0,0). The correct code for an edge with arrowhead at target

       IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node, targetNode.DrawingObject as Node, false, true);
      viewer.AddEdge(edge, true);

    and without the arrowhead

       IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node,      targetNode.DrawingObject as Node, false, false);
      edge.Edge.Attr.ArrowheadAtTarget=ArrowStyle.None;
      viewer.AddEdge(edge, true);

     

     

     


    Lev Nachmanson
    • Marked as answer by yosi142 Saturday, October 15, 2011 4:51 AM
    Saturday, October 15, 2011 3:08 AM

All replies

  • Hi Yosi,

    Please replace CreateEdge in IViewer.cs by  

    IViewerEdge CreateEdge(Node source, Node target, bool arrowheadAtSource, bool arrowheadAtTarget);

    Accordingly replace CreateEdge in GViewer.cs by

          public IViewerEdge CreateEdge(Microsoft.Msagl.Drawing.Node source, Microsoft.Msagl.Drawing.Node target, bool arrowheadAtSource, bool arrowheadAtTarget) {
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(source.Id) == source);
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(target.Id) == target);

                Microsoft.Msagl.Drawing.Edge edge = new DrawingEdge(source, target,Connection.Disconnected);
                edge.Label = new Microsoft.Msagl.Drawing.Label();
                Microsoft.Msagl.Edge geometryEdge = edge.Attr.GeometryEdge = new Microsoft.Msagl.Edge();
                geometryEdge.ArrowheadAtSource = arrowheadAtSource;
                geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;
    ...the rest is the same

    In your scenario you need to call CreateEdge(source, target, false, false).

    Happy Sukkot!

     


    Lev Nachmanson
    • Edited by Lev Nachmanson Friday, October 14, 2011 5:48 AM type
    • Marked as answer by Lev Nachmanson Friday, October 14, 2011 5:49 AM
    • Unmarked as answer by yosi142 Friday, October 14, 2011 8:26 PM
    • Marked as answer by yosi142 Saturday, October 15, 2011 2:51 AM
    Friday, October 14, 2011 5:47 AM
  • Hi Lev,

    while this solution works for edges added programmatically, when adding edges manually (via dragging the middle mouse button in the graph viewer) I now encounter the "black polygon" issue from my previous thread:

    http://social.msdn.microsoft.com/Forums/en-US/automaticgraphlayout/thread/436c2539-500d-4d9d-bba2-c90dbd6b0065

    BTW, Just out of curiosity, do you work at Microsoft Israel R&D (Herzliya?)
    • Edited by yosi142 Friday, October 14, 2011 8:32 PM
    Friday, October 14, 2011 8:31 PM
  • Yosi, 

    The old post snippet was changed, I suppose. Can you please post the problematic snippet exactly?

    Thanks  http://research.microsoft.com/en-us/um/people/levnach/


    Lev Nachmanson
    • Marked as answer by Lev Nachmanson Friday, October 14, 2011 10:21 PM
    • Unmarked as answer by yosi142 Saturday, October 15, 2011 2:51 AM
    Friday, October 14, 2011 10:21 PM
  • The problematic code is the addition you suggested above to GViewer.CreateEdge():

        geometryEdge.ArrowheadAtSource = arrowheadAtSource;
        geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;

    If I comment these 2 lines out and add an edge in the viewer (via a middle-mouse drag) everything is OK.

    With the 2 lines in place, however, adding an in the same manner produces the "black polygon" that you can see here:

    http://social.msdn.microsoft.com/Forums/en-US/automaticgraphlayout/thread/436c2539-500d-4d9d-bba2-c90dbd6b0065

    (too bad about Redmond though - I had hoped MS did graph theory stuff here, too)


    • Edited by yosi142 Saturday, October 15, 2011 1:48 AM
    Saturday, October 15, 2011 1:45 AM
  • The change that I made works for me. You also were saying that it worked when you add and edge by dragging the mouse. Obviously it creates an edge programmatically and not manually!  A snippet of your code producing the black rectangle might help. The code that creates a new edge without the arrowhead at target after mouse dragging is 

       IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node, targetNode.DrawingObject as Node, false, false);
      edge.Edge.Attr.ArrowheadAtTarget=ArrowStyle.None;
      viewer.AddEdge(edge, true); 

    Thanks

     


    Lev Nachmanson
    Saturday, October 15, 2011 2:09 AM
  • Hi Lev,

    The situation is very simple.

    Consider the sample project WindowsApplicationSample

    If I run the project, click the graph creation button and then middle-drag the mouse to create a (directed) edge, everything works fine.

    Now, I go to GViewer.cs and add the following 2 lines to GViewer.CreateEdge() (in bold):

    public IViewerEdge CreateEdge(Microsoft.Msagl.Drawing.Node source, Microsoft.Msagl.Drawing.Node target, bool arrowheadAtSource, bool arrowheadAtTarget) {
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(source.Id) == source);
                System.Diagnostics.Debug.Assert(this.Graph.FindNode(target.Id) == target);

                Microsoft.Msagl.Drawing.Edge edge = new DrawingEdge(source, target,Connection.Disconnected);
                edge.Label = new Microsoft.Msagl.Drawing.Label();
                Microsoft.Msagl.Edge geometryEdge = edge.Attr.GeometryEdge = new Microsoft.Msagl.Edge();
                geometryEdge.ArrowheadAtSource = arrowheadAtSource;
                geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;

    Then I compile and run WindowsApplicationSample again with the new Microsoft.Msagl.Drawing and Microsoft.Msagl.GraphViewerGDI.dll. The same middle-dragging edge creation now produces the black polygon !

    • Edited by yosi142 Saturday, October 15, 2011 2:31 AM
    Saturday, October 15, 2011 2:30 AM
  • OK, I think I got it !

    The following seems to work (additions in bold):

            public IViewerEdge CreateEdge(DrawingNode source, DrawingNode target, bool arrowheadAtSource,
                                          bool arrowheadAtTarget)
            {
                Debug.Assert(Graph.FindNode(source.Id) == source);
                Debug.Assert(Graph.FindNode(target.Id) == target);

                var edge = new DrawingEdge(source, target, Connection.Disconnected);
                edge.Label = new Drawing.Label();
                Edge geometryEdge = edge.Attr.GeometryEdge = new Edge();
                geometryEdge.ArrowheadAtSource = arrowheadAtSource;
                geometryEdge.ArrowheadAtTarget = arrowheadAtTarget;
                geometryEdge.Parent = Graph.GeometryGraph;

                Point a = source.Attr.GeometryNode.Center;
                Point b = target.Attr.GeometryNode.Center;
                if (source == target)
                {
                    var start = new Site(a);
                    var end = new Site(b);
                    Point mid1 = source.Attr.GeometryNode.Center;
                    mid1.X += (source.Attr.GeometryNode.BoundingBox.Width/3*2);
                    Point mid2 = mid1;
                    mid1.Y -= source.Attr.GeometryNode.BoundingBox.Height/2;
                    mid2.Y += source.Attr.GeometryNode.BoundingBox.Height/2;
                    var mid1s = new Site(mid1);
                    var mid2s = new Site(mid2);
                    start.Next = mid1s;
                    mid1s.Previous = start;
                    mid1s.Next = mid2s;
                    mid2s.Previous = mid1s;
                    mid2s.Next = end;
                    end.Previous = mid2s;
                    geometryEdge.UnderlyingPolyline = new UnderlyingPolyline(start);
                    geometryEdge.Curve = geometryEdge.UnderlyingPolyline.CreateCurve();
                }
                else
                {
                    var start = new Site(a);
                    var end = new Site(b);
                    var mids = new Site(a*0.5 + b*0.5);
                    start.Next = mids;
                    mids.Previous = start;
                    mids.Next = end;
                    end.Previous = mids;
                    geometryEdge.UnderlyingPolyline = new UnderlyingPolyline(start);
                    geometryEdge.Curve = geometryEdge.UnderlyingPolyline.CreateCurve();
                }

                geometryEdge.Source = edge.SourceNode.Attr.GeometryNode;
                geometryEdge.Target = edge.TargetNode.Attr.GeometryNode;
                geometryEdge.ArrowheadLength = edge.Attr.ArrowheadLength;
                if (!Curve.TrimSplineAndCalculateArrowheads(geometryEdge, geometryEdge.Curve, true))
                    Curve.CreateBigEnoughSpline(geometryEdge);

                var dEdge = new DEdge(DGraph.NodeMap[edge.SourceNode.Id], DGraph.NodeMap[edge.TargetNode.Id], edge,
                                      Connection.Disconnected);
                if (edge.Label != null)
                    dEdge.Label = new DLabel(dEdge, new Drawing.Label());

                dEdge.Edge.Attr.ArrowheadAtSource = arrowheadAtSource ? ArrowStyle.Normal : ArrowStyle.None;
                dEdge.Edge.Attr.ArrowheadAtTarget = arrowheadAtTarget ? ArrowStyle.Normal : ArrowStyle.None;

                return dEdge;
            }

    • Marked as answer by yosi142 Saturday, October 15, 2011 2:51 AM
    Saturday, October 15, 2011 2:35 AM
  • I think I see the problem. The black rectangle probably appears when you try to draw an arrowhead but the arrowhead position in not calculated, so it is Point(0,0) by default. As a result you get a huge arrowhead, with the base at (0,0). The correct code for an edge with arrowhead at target

       IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node, targetNode.DrawingObject as Node, false, true);
      viewer.AddEdge(edge, true);

    and without the arrowhead

       IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node,      targetNode.DrawingObject as Node, false, false);
      edge.Edge.Attr.ArrowheadAtTarget=ArrowStyle.None;
      viewer.AddEdge(edge, true);

     

     

     


    Lev Nachmanson
    • Marked as answer by yosi142 Saturday, October 15, 2011 4:51 AM
    Saturday, October 15, 2011 3:08 AM
  • Makes sense, thanks !

    BTW, I further modified the method to add a new node when middle-dragging from an existing node to a blank space, such that a new edge is created, connecting the existing node to the new one. If anyone's interested, change DrawingLayoutEditor.viewer_MouseUp() like so:

    ...

                    else if (MiddleMouseButtonWasPressed)
                    {
                        viewer.StopDrawingRubberLine();
                        var targetNode = viewer.ObjectUnderMouseCursor as IViewerNode;
                        if (targetNode == null)
                        {
                            var node = viewer.Graph.AddNode(Guid.NewGuid().ToString());
                           
                            node.Attr.GeometryNode = CreateLayoutGraph.CreateGeometryNode(
                                viewer.OriginalGraph.GeometryGraph, node, Connection.Connected); //I added OriginalGraph to IViewer + GViewer

                            node.Attr.GeometryNode.Center = viewer.MousePositionPoint; //I added this property to IViewer + GViewer with the implementation: ScreenToSource(DrawingPanel.PointToClient(MousePosition))

                            //I now create an ellipse, but any other shape should work here

                            node.Attr.GeometryNode.BoundaryCurve = new Ellipse(5, 5, viewer.MousePositionPoint);                                

                            node.Attr.Shape = Shape.Circle;
                            node.Attr.FillColor = Color.Black;
                            node.LabelText = String.Empty //you probably don't want that GUID here
                           
                            targetNode = viewer.CreateNode(node);
                        }
                        IViewerEdge edge = viewer.CreateEdge(SourceOfInsertedEdge.DrawingObject as Node,
                                                             targetNode.DrawingObject as Node, false, false);
                        viewer.OnEdgeCreated(edge);
                        viewer.AddEdge(edge, true);
                    }

    ...

    Cheers !


    • Edited by yosi142 Saturday, October 15, 2011 4:53 AM
    Saturday, October 15, 2011 4:51 AM