Commits

Mark Heath  committed 86a28f1

node control is responsible for reporting its connecting points, to allow us to introduce shapes other than circle for nodes

  • Participants
  • Parent commits e7b145f

Comments (0)

Files changed (7)

 *.suo
 packages/
 *.crunchsolution.cache
-_ReSharper.*
+_ReSharper.*
+TestResults/

File GraphPad.Tests/ConnectionDirectionUtilsTests.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace GraphPad.Tests
+{
+    [TestFixture]
+    public class ConnectionDirectionUtilsTests
+    {
+        [TestCase(0, 0, 0, 1, ConnectionDirection.East)]
+        [TestCase(0, 1, 0, 0, ConnectionDirection.West)]
+        [TestCase(1, 0, 0, 0, ConnectionDirection.North)]
+        [TestCase(0, 0, 1, 0, ConnectionDirection.South)]
+        [TestCase(0, 0, 1, 1, ConnectionDirection.SouthEast)]
+        [TestCase(1, 0, 0, 1, ConnectionDirection.NorthEast)]
+        [TestCase(0, 1, 1, 0, ConnectionDirection.SouthWest)]
+        [TestCase(1, 1, 0, 0, ConnectionDirection.NorthWest)]
+        public void CorrectConnectionDirection(int fromRow, int fromCol, int toRow, int toCol, ConnectionDirection direction)
+        {
+            var actualDirection = ConnectionDirectionUtils.GetDirection(fromRow, fromCol, toRow, toCol);
+            Assert.AreEqual(direction, actualDirection);
+        }
+    }
+}

File GraphPad.Tests/GraphPad.Tests.csproj

     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x86</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="nunit.framework, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="ConnectionDirectionUtilsTests.cs" />
     <Compile Include="GraphBuilderTests.cs" />
     <Compile Include="GraphLayoutEngineTests.cs" />
     <Compile Include="GraphTests.cs" />

File GraphPad/GraphPad.csproj

       <Generator>MSBuild:Compile</Generator>
       <SubType>Designer</SubType>
     </ApplicationDefinition>
+    <Compile Include="ConnectionDirection.cs" />
     <Compile Include="Gui\ArrowLine.cs" />
     <Compile Include="Gui\ArrowLineBase.cs" />
     <Compile Include="Gui\ArrowEnds.cs" />

File GraphPad/Logic/GraphRenderer.cs

 
         private void CreateConnections(Graph graph)
         {
-            foreach (var node in graph.Nodes)
+            foreach (var parentNode in graph.Nodes)
             {
-                var nodeControl = (UserControl)node.MetaData["Control"];
-                foreach (var connection in node.Children)
+                var parentNodeControl = (INodeControl)parentNode.MetaData["Control"];
+                foreach (var childNode in parentNode.Children)
                 {
-                    var connectedNodeControl = (UserControl)connection.MetaData["Control"];
+                    var childNodeControl = (INodeControl)childNode.MetaData["Control"];
                     UIElement connector;
-                    var midNode = GetNodeMidpoint(nodeControl);
-                    var midConnection = GetNodeMidpoint(connectedNodeControl);
-                    var gridSpacing = nodeControl.Width + nodePadding;
 
-                    if (node.GetRow() == connection.GetRow())
-                    {
-                        connector = GetStraightLine(midNode, midConnection, nodeControl.Width / 2, Brushes.Black);
-                    }
-                    else if ((Math.Abs(node.GetRow() - connection.GetRow()) <= 1) &&
-                        (Math.Abs(node.GetColumn() - node.GetColumn()) <= 1))
-                    {
-                        connector = GetStraightLine(midNode, midConnection, nodeControl.Width / 2, Brushes.Green);
-                    }
-                    else
-                    {
-                        var from = GetConnectionPoint(nodeControl, connectedNodeControl);
-                        var to = GetConnectionPoint(connectedNodeControl, nodeControl);
-                        connector = GetPolyLine(from, to, Brushes.Red);
-                    }
+                    // the line goes from child to parent in a DAG
+                    var from = childNodeControl.GetConnectionPoint(childNode.GetDirectionTo(parentNode));
+                    var to = parentNodeControl.GetConnectionPoint(parentNode.GetDirectionTo(childNode));
+                    connector = GetStraightLine(from, to, Brushes.Green);
+                    //connector = GetPolyLine(from, to, Brushes.Green);
                     canvas.Children.Add(connector);
                 }
             }
         }
 
-        private Point GetConnectionPoint(UserControl from, UserControl to)
-        {
-            var connectionPoint = new Point();
-            var fromPosition = new Point((double)from.GetValue(Canvas.LeftProperty), (double)from.GetValue(Canvas.TopProperty));
-            var toPosition = new Point((double)to.GetValue(Canvas.LeftProperty), (double)to.GetValue(Canvas.TopProperty));
-
-            if (fromPosition.Y < toPosition.Y)
-            {
-                // bottom
-                connectionPoint.Y = fromPosition.Y + from.Height;
-                connectionPoint.X = fromPosition.X + from.Width / 2;
-            }
-            else if (fromPosition.Y == toPosition.Y)
-            {
-                connectionPoint.Y = fromPosition.Y + from.Height / 2;
-                if (fromPosition.X < toPosition.X)
-                {
-                    // right edge
-                    connectionPoint.X = fromPosition.X + from.Width;
-                }
-                else
-                {
-                    connectionPoint.X = fromPosition.X;
-                }
-            }
-            else
-            {
-                connectionPoint.Y = fromPosition.Y + from.Height / 2;
-                if (toPosition.X > fromPosition.X)
-                    connectionPoint.X = fromPosition.X + from.Width;
-                else
-                    connectionPoint.X = fromPosition.X;
-                
-            }
-            return connectionPoint;
-        }
-
         private static UIElement GetPolyLine(Point from, Point to, Brush stroke)
         {
             var line = new ArrowPolyline();
             line.Stroke = stroke;
             line.StrokeThickness = 2.0;
-            line.ArrowEnds = ArrowEnds.Start;
+            line.ArrowEnds = ArrowEnds.End;
             line.ArrowLength = 8;
 
             line.Points.Add(from);
             return line;
         }
 
-        private static ArrowLine GetStraightLine(Point from, Point to, double radius, Brush stroke)
+        private static ArrowLine GetStraightLine(Point from, Point to, Brush stroke)
         {
             var line = new ArrowLine();
             line.Stroke = stroke;
             line.StrokeThickness = 2.0;
-            line.ArrowEnds = ArrowEnds.Start;
+            line.ArrowEnds = ArrowEnds.End;
             line.ArrowLength = 8;
 
-            // trim the line
-            var angle = Math.Atan((to.Y - from.Y) / (to.X - from.X));
-            var xOffset = radius * Math.Cos(angle);
-            var yOffset = radius * Math.Sin(angle);
-            from.X += xOffset;
-            from.Y += yOffset;
-
-            to.X -= xOffset;
-            to.Y -= yOffset;
-
             line.X1 = from.X;
             line.Y1 = from.Y;
             line.X2 = to.X;
             return line;
         }
 
-        private static Point GetNodeMidpoint(UserControl node)
-        {
-            var radius = node.Width / 2;
-            return new Point((double)node.GetValue(Canvas.LeftProperty) + radius, (double)node.GetValue(Canvas.TopProperty) + radius);
-        }
-
         private static UserControl CreateNodeControl(double left, double top, string name)
         {
             var nodeControl = new NodeControl();

File GraphPad/NodeControl.xaml.cs

 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Windows;
 
 namespace GraphPad
 {
+    public interface INodeControl
+    {
+        Point GetConnectionPoint(ConnectionDirection connectionDirection);
+    }
+
     /// <summary>
     /// Interaction logic for Node.xaml
     /// </summary>
-    public partial class NodeControl : UserControl
+    public partial class NodeControl : UserControl, INodeControl
     {
-        private List<NodeControl> connections = new List<NodeControl>();
-        
         public NodeControl()
         {
             InitializeComponent();
         }
 
-        public List<NodeControl> Connections
-        {
-            get { return connections; }
-        }
-
         public string NodeName
         {
             get { return (string)GetValue(NodeNameProperty); }
             node.nodeNameTextBlock.Text = (string)args.NewValue;
         }
 
+        public Point GetConnectionPoint(ConnectionDirection connectionDirection)
+        {
+            var from = this;
+            var fromPosition = new Point((double)from.GetValue(Canvas.LeftProperty), (double)from.GetValue(Canvas.TopProperty));
+            
+            var radius = this.Width/2;
+            var corner = radius - Math.Sqrt((radius*radius)/2);
+
+            switch (connectionDirection)
+            {
+                case ConnectionDirection.West:
+                    return new Point(fromPosition.X, fromPosition.Y + from.Height / 2);
+                case ConnectionDirection.East:
+                    return new Point(fromPosition.X + from.Width, fromPosition.Y + from.Height/2);
+                case ConnectionDirection.North:
+                    return new Point(fromPosition.X + from.Width / 2, fromPosition.Y);
+                case ConnectionDirection.South:
+                    return new Point(fromPosition.X + from.Width / 2, fromPosition.Y + from.Height);
+                case ConnectionDirection.NorthWest:
+                    return new Point(fromPosition.X + corner, fromPosition.Y + corner);
+                case ConnectionDirection.SouthWest:
+                    return new Point(fromPosition.X + corner, fromPosition.Y + from.Height - corner);
+                case ConnectionDirection.NorthEast:
+                    return new Point(fromPosition.X + from.Width - corner, fromPosition.Y + corner);
+                case ConnectionDirection.SouthEast:
+                    return new Point(fromPosition.X + from.Width - corner, fromPosition.Y + from.Height - corner);
+                default:
+                    throw new ArgumentException("Invalid connection direction");
+            }
+        }
     }
 }

File graphpad/ConnectionDirection.cs

+using GraphPad.Logic;
+
+namespace GraphPad
+{
+    public enum ConnectionDirection
+    {
+        North,
+        NorthEast,
+        East,
+        SouthEast,
+        South,
+        SouthWest,
+        West,
+        NorthWest
+    }
+
+    public static class ConnectionDirectionUtils
+    {
+        public static ConnectionDirection GetDirectionTo(this Node fromNode, Node toNode)
+        {
+            return GetDirection(fromNode.GetRow(), fromNode.GetColumn(), toNode.GetRow(), toNode.GetColumn());
+        }
+
+        public static ConnectionDirection GetDirection(int fromRow, int fromColumn, int toRow, int toColumn)
+        {
+            if (fromRow == toRow)
+            {
+                return fromColumn > toColumn ? ConnectionDirection.West : ConnectionDirection.East;
+            }
+            else if (fromRow > toRow)
+            {
+                if (fromColumn == toColumn) return ConnectionDirection.North;
+                return fromColumn > toColumn ? ConnectionDirection.NorthWest : ConnectionDirection.NorthEast;
+            }
+            else
+            {
+                if (fromColumn == toColumn) return ConnectionDirection.South;
+                return fromColumn > toColumn ? ConnectionDirection.SouthWest : ConnectionDirection.SouthEast;
+            }
+        }
+    }
+}