Mark Heath avatar Mark Heath committed 86a28f1

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

Comments (0)

Files changed (7)

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

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);
+        }
+    }
+}

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" />

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" />

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();

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");
+            }
+        }
     }
 }

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;
+            }
+        }
+    }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.