Connector.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / Common / AuthoringOM / Design / Connector.cs / 1305376 / Connector.cs

                            using System; 
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections; 
using System.Collections.ObjectModel;
using System.Collections.Generic; 
using System.ComponentModel; 
using System.ComponentModel.Design;
using System.Windows.Forms; 
using System.Diagnostics;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel.Serialization;
 
namespace System.Workflow.ComponentModel.Design
{ 
    #region Class ConnectionPointHitTestInfo 
    internal sealed class ConnectionPointHitTestInfo : HitTestInfo
    { 
        private ConnectionPoint connectionPoint;

        internal ConnectionPointHitTestInfo(ConnectionPoint connectionPoint)
            : base(connectionPoint.AssociatedDesigner, HitTestLocations.Designer | HitTestLocations.Connector) 
        {
            this.connectionPoint = connectionPoint; 
        } 

        internal ConnectionPoint ConnectionPoint 
        {
            get
            {
                return this.connectionPoint; 
            }
        } 
    } 
    #endregion
 
    #region ConnectorEvent
    public delegate void ConnectorEventHandler(object sender, ConnectorEventArgs e);

    #region Class ConnectorEventArgs 
    public class ConnectorEventArgs : EventArgs
    { 
        private Connector connector; 

        internal ConnectorEventArgs(Connector connector) 
        {
            this.connector = connector;
        }
 
        public Connector Connector
        { 
            get 
            {
                return this.connector; 
            }
        }
    }
    #endregion 
    #endregion
 
    #region Class ConnectionPoint 
    public class ConnectionPoint
    { 
        private ActivityDesigner associatedDesigner;
        private DesignerEdges designerEdge;
        private int connectionIndex;
 
        public ConnectionPoint(ActivityDesigner associatedDesigner, DesignerEdges designerEdge, int connectionIndex)
        { 
            if (associatedDesigner == null) 
                throw new ArgumentNullException("associatedDesigner");
 
            if (connectionIndex < 0 || connectionIndex >= associatedDesigner.GetConnections(designerEdge).Count)
                throw new ArgumentException(DR.GetString(DR.Error_ConnectionPoint), "connectionIndex");

            this.associatedDesigner = associatedDesigner; 
            this.designerEdge = designerEdge;
            this.connectionIndex = connectionIndex; 
        } 

        public ActivityDesigner AssociatedDesigner 
        {
            get
            {
                return this.associatedDesigner; 
            }
        } 
 
        public int ConnectionIndex
        { 
            get
            {
                return this.connectionIndex;
            } 
        }
 
        public DesignerEdges ConnectionEdge 
        {
            get 
            {
                DesignerEdges designerEdge = this.designerEdge;

                if (designerEdge != DesignerEdges.Left && designerEdge != DesignerEdges.Right && 
                    designerEdge != DesignerEdges.Top && designerEdge != DesignerEdges.Bottom)
                { 
                    designerEdge = DesignerGeometryHelper.ClosestEdgeToPoint(Location, this.associatedDesigner.Bounds, designerEdge); 
                }
 
                return designerEdge;
            }
        }
 
        public virtual Point Location
        { 
            get 
            {
                IList connections = this.associatedDesigner.GetConnections(this.designerEdge); 
                if (this.connectionIndex < connections.Count)
                    return connections[this.connectionIndex];
                else
                    return Point.Empty; 
            }
        } 
 
        public virtual Rectangle Bounds
        { 
            get
            {
                IList connections = this.associatedDesigner.GetConnections(DesignerEdges.All);
                if (connections.Count > 0) 
                {
                    Point location = Location; 
                    Size size = DefaultSize; 
                    Rectangle enclosingBounds = new Rectangle(new Point(location.X - size.Width / 2, location.Y - size.Height / 2), size);
                    return enclosingBounds; 
                }
                else
                {
                    return Rectangle.Empty; 
                }
            } 
        } 

        public override bool Equals(object obj) 
        {
            ConnectionPoint connectionPoint = obj as ConnectionPoint;
            if (connectionPoint == null)
                return false; 

            if (connectionPoint.AssociatedDesigner == this.associatedDesigner && 
                connectionPoint.designerEdge == this.designerEdge && 
                connectionPoint.ConnectionIndex == this.connectionIndex)
                return true; 
            else
                return false;
        }
 
        public override int GetHashCode()
        { 
            return (this.associatedDesigner.GetHashCode() ^ this.designerEdge.GetHashCode() ^ this.connectionIndex.GetHashCode()); 
        }
 
        public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawHighlighted)
        {
            Draw(e, Bounds);
        } 

        internal static void Draw(ActivityDesignerPaintEventArgs e, Rectangle bounds) 
        { 
            bounds.Inflate(-1, -1);
            e.Graphics.FillEllipse(Brushes.White, bounds); 
            e.Graphics.DrawEllipse(e.AmbientTheme.SelectionForegroundPen, bounds);

            bounds.Inflate(-1, -1);
            e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds); 
        }
 
        private Size DefaultSize 
        {
            get 
            {
                Size defaultSize = WorkflowTheme.CurrentTheme.AmbientTheme.Margin;
                defaultSize.Width += defaultSize.Width / 2;
                defaultSize.Height += defaultSize.Height / 2; 
                if (this.associatedDesigner != null)
                    defaultSize = new Size(Math.Max(defaultSize.Width, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4), Math.Max(defaultSize.Height, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4)); 
                return defaultSize; 
            }
        } 
    }
    #endregion

    #region Class Connector 
    [DesignerSerializer(typeof(ConnectorLayoutSerializer), typeof(WorkflowMarkupSerializer))]
    public class Connector : IDisposable 
    { 
        #region Members, Construction, Dispose
        private AccessibleObject accessibilityObject; 
        private FreeformActivityDesigner parentDesigner;
        private ConnectionPoint source = null;
        private ConnectionPoint target = null;
        private List segments = new List(); 
        private bool connectorModified = false;
 
        public Connector(ConnectionPoint source, ConnectionPoint target) 
        {
            if (source == null) 
                throw new ArgumentNullException("source");

            if (target == null)
                throw new ArgumentNullException("target"); 

            if (ConnectionManager.GetConnectorContainer(source.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(target.AssociatedDesigner)) 
                throw new ArgumentException(DR.GetString(DR.Error_Connector1)); 

            this.source = source; 
            this.target = target;
        }

        void IDisposable.Dispose() 
        {
        } 
        #endregion 

        #region Properties and Methods 

        public virtual AccessibleObject AccessibilityObject
        {
            get 
            {
                if (this.accessibilityObject == null) 
                    this.accessibilityObject = new ConnectorAccessibleObject(this); 
                return this.accessibilityObject;
            } 
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ConnectionPoint Source 
        {
            get 
            { 
                return this.source;
            } 

            set
            {
                if (value == null) 
                    throw new ArgumentNullException("value");
 
                if (this.source.Equals(value)) 
                    return;
 
                if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.target.AssociatedDesigner))
                    throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");

                this.source = value; 
                PerformLayout();
            } 
        } 

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public ConnectionPoint Target
        {
            get
            { 
                return this.target;
            } 
 
            set
            { 
                if (value == null)
                    throw new ArgumentNullException("value");

                if (this.target.Equals(value)) 
                    return;
 
                if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.source.AssociatedDesigner)) 
                    throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");
 
                this.target = value;
                PerformLayout();
            }
        } 

        public virtual ReadOnlyCollection ConnectorSegments 
        { 
            get
            { 
                List connectorSegments = new List();

                if (this.source != null && this.target != null)
                { 
                    if (this.segments.Count == 0 || this.segments[0] != this.source.Location)
                        connectorSegments.Add(this.source.Location); 
 
                    connectorSegments.AddRange(this.segments);
 
                    if (this.segments.Count == 0 || this.segments[this.segments.Count - 1] != this.target.Location)
                        connectorSegments.Add(this.target.Location);
                }
 
                return connectorSegments.AsReadOnly();
            } 
        } 

        public Rectangle Bounds 
        {
            get
            {
                Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(new List(ConnectorSegments).ToArray()); 
                bounds.Inflate(1, 1);
                return bounds; 
            } 
        }
 
        public FreeformActivityDesigner ParentDesigner
        {
            get
            { 
                return this.parentDesigner;
            } 
        } 

        public virtual bool HitTest(Point point) 
        {
            Size selectionSize = WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize;

            //We go thru the line seagments and hittest 
            ReadOnlyCollection segments = ConnectorSegments;
            for (int i = 1; i < segments.Count; i++) 
            { 
                if (DesignerGeometryHelper.PointOnLineSegment(point, new Point[] { segments[i - 1], segments[i] }, selectionSize))
                    return true; 
            }

            return false;
        } 

        public virtual void Offset(Size size) 
        { 
            for (int i = 0; i < this.segments.Count; i++)
                this.segments[i] = new Point(this.segments[i].X + size.Width, this.segments[i].Y + size.Height); 
        }

        public override bool Equals(object obj)
        { 
            Connector connector = obj as Connector;
            if (connector == null) 
                return false; 

            return (connector.Source == this.source && connector.target == this.target); 
        }

        public override int GetHashCode()
        { 
            if (this.source != null && this.target != null)
                return this.source.GetHashCode() ^ this.target.GetHashCode(); 
            else 
                return base.GetHashCode();
        } 

        public void Invalidate()
        {
            WorkflowView workflowView = ParentView; 
            if (workflowView != null)
                workflowView.InvalidateLogicalRectangle(Bounds); 
        } 

        public bool ConnectorModified 
        {
            get
            {
                return this.connectorModified; 
            }
        } 
 
        protected WorkflowView ParentView
        { 
            get
            {
                return GetService(typeof(WorkflowView)) as WorkflowView;
            } 
        }
 
        protected void PerformLayout() 
        {
            WorkflowView workflowView = ParentView; 
            if (workflowView != null)
                workflowView.PerformLayout(false);
        }
 
        protected internal virtual ICollection ExcludedRoutingRectangles
        { 
            get 
            {
                return new Rectangle[] { }; 
            }
        }

        //We want to allow framework or the derived classes only to set the connector segments 
        //We do not want any outside entity to set the connector segments randomly
        protected internal void SetConnectorSegments(ICollection segments) 
        { 
            //Called by derived classes when they want to change connector routing
            // 
            if (segments == null)
                throw new ArgumentNullException("segments");

            this.connectorModified = (this.parentDesigner != null && segments.Count > 0); 
            if (this.connectorModified)
                Invalidate(); 
 
            this.segments.Clear();
            this.segments.AddRange(segments); 

            if (this.connectorModified)
                Invalidate();
        } 

        protected internal virtual void OnLayout(ActivityDesignerLayoutEventArgs e) 
        { 
            if (this.segments.Count > 0 &&
                (this.segments[0] != Source.Location || this.segments[this.segments.Count - 1] != Target.Location)) 
                this.connectorModified = false;

            if (!connectorModified && ParentDesigner != null)
            { 
                Point[] newSegments = ActivityDesignerConnectorRouter.Route(Source.AssociatedDesigner.Activity.Site, Source, Target, ExcludedRoutingRectangles);
                this.segments.Clear(); 
                this.segments.AddRange(newSegments); 
            }
        } 

        protected internal virtual void OnPaint(ActivityDesignerPaintEventArgs e)
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme != null)
            { 
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5); 
                Size maxCapSize = theme.ConnectorSize;
                ActivityDesignerPaint.DrawConnectors(e.Graphics, e.DesignerTheme.ForegroundPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap); 
            }
        }

        protected internal virtual void OnPaintSelected(ActivityDesignerPaintEventArgs e, bool primarySelection, Point[] segmentEditPoints) 
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme == null) 
                return;
 
            using (Pen lineSelectionPen = new Pen(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionForeColor, 1))
            {
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5);
                Size maxCapSize = theme.ConnectorSize; 
                ActivityDesignerPaint.DrawConnectors(e.Graphics, lineSelectionPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap);
            } 
 
            if (this.source != null)
                this.source.OnPaint(e, false); 

            ReadOnlyCollection endSegmentEditPoints = ConnectorSegments;
            for (int i = 1; i < endSegmentEditPoints.Count - 1; i++)
                PaintEditPoints(e, endSegmentEditPoints[i], false); 

            for (int i = 0; i < segmentEditPoints.Length; i++) 
                PaintEditPoints(e, segmentEditPoints[i], true); 

            if (this.target != null) 
                this.target.OnPaint(e, false);
        }

        protected internal virtual void OnPaintEdited(ActivityDesignerPaintEventArgs e, Point[] segments, Point[] segmentEditPoints) 
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme == null) 
                return;
 
            using (Pen editableConnectorPen = new Pen(e.AmbientTheme.SelectionForegroundPen.Color, e.AmbientTheme.SelectionForegroundPen.Width))
            {
                editableConnectorPen.DashStyle = DashStyle.Dash;
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5); 
                Size maxCapSize = theme.ConnectorSize;
                ActivityDesignerPaint.DrawConnectors(e.Graphics, editableConnectorPen, segments, arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap); 
            } 

            if (this.source != null) 
                this.source.OnPaint(e, false);

            for (int i = 1; i < segments.Length - 1; i++)
                PaintEditPoints(e, segments[i], false); 

            for (int i = 0; i < segmentEditPoints.Length; i++) 
                PaintEditPoints(e, segmentEditPoints[i], true); 

            if (this.target != null) 
                this.target.OnPaint(e, false);
        }

        protected virtual object GetService(Type serviceType) 
        {
            object service = null; 
            if (this.parentDesigner != null && this.parentDesigner.Activity != null && this.parentDesigner.Activity.Site != null) 
                service = this.parentDesigner.Activity.Site.GetService(serviceType);
            return service; 
        }
        #endregion

        #region Helpers 
        private void PaintEditPoints(ActivityDesignerPaintEventArgs e, Point point, bool drawMidSegmentEditPoint)
        { 
            Size size = (this.source != null) ? this.source.Bounds.Size : Size.Empty; 
            if (!size.IsEmpty)
            { 
                Rectangle bounds = new Rectangle(point.X - size.Width / 2, point.Y - size.Height / 2, size.Width, size.Height);
                if (drawMidSegmentEditPoint)
                {
                    using (GraphicsPath path = new GraphicsPath()) 
                    {
                        path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Top), new Point(bounds.Right, bounds.Top + bounds.Height / 2)); 
                        path.AddLine(new Point(bounds.Right, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Bottom)); 
                        path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Bottom), new Point(bounds.Left, bounds.Top + bounds.Height / 2));
                        path.AddLine(new Point(bounds.Left, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Top)); 

                        e.Graphics.FillPath(Brushes.White, path);
                        e.Graphics.DrawPath(e.AmbientTheme.SelectionForegroundPen, path);
                    } 
                }
                else 
                { 
                    bounds.Inflate(-1, -1);
                    e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds); 
                }
            }
        }
 
        internal void SetConnectorModified(bool modified)
        { 
            this.connectorModified = modified; 
        }
 
        internal FreeformActivityDesigner RenderingOwner
        {
            get
            { 
                if (this.source == null || this.target == null)
                    return null; 
 
                //
 



                List targetParents = new List(); 
                ActivityDesigner designer = this.target.AssociatedDesigner;
                while (designer != null) 
                { 
                    FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
                    if (parentFreeFormDesigner != null) 
                        targetParents.Add(parentFreeFormDesigner);
                    designer = designer.ParentDesigner;
                }
 
                //Go through the parent freeforms of source and match it with target, the first common
                //parent freeform is the rendering designer 
                designer = this.source.AssociatedDesigner; 
                while (designer != null)
                { 
                    FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
                    if (parentFreeFormDesigner != null && targetParents.Contains(parentFreeFormDesigner))
                        break;
                    designer = designer.ParentDesigner; 
                }
 
                return designer as FreeformActivityDesigner; 
            }
        } 

        internal void SetParent(FreeformActivityDesigner parentDesigner)
        {
            //Make sure that this is in parent chain of both source and the target connection points 
            WorkflowView workflowView = ParentView;
            if (this.parentDesigner != null && workflowView != null) 
                workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds); 

            this.parentDesigner = parentDesigner; 

            if (this.parentDesigner != null && workflowView != null)
                workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds);
        } 

        internal static Connector GetConnectorFromSelectedObject(object selectedObject) 
        { 
            Connector connector = null;
            ConnectorHitTestInfo connectorHitTestInfo = selectedObject as ConnectorHitTestInfo; 
            if (connectorHitTestInfo != null)
            {
                FreeformActivityDesigner connectorContainer = connectorHitTestInfo.AssociatedDesigner as FreeformActivityDesigner;
                int index = connectorHitTestInfo.MapToIndex(); 
                if (connectorContainer != null && index >= 0 && index < connectorContainer.Connectors.Count)
                    connector = connectorContainer.Connectors[index]; 
            } 

            return connector; 
        }

        #region Properties used during serialization only
        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal string SourceActivity 
        {
            get 
            {
                string activityName = String.Empty;
                if (this.source != null)
                    activityName = this.source.AssociatedDesigner.Activity.QualifiedName; 
                return activityName;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal int SourceConnectionIndex 
        {
            get 
            {
                int connectionIndex = 0;
                if (this.source != null)
                    connectionIndex = this.source.ConnectionIndex; 
                return connectionIndex;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal DesignerEdges SourceConnectionEdge 
        {
            get 
            {
                DesignerEdges connectionEdge = DesignerEdges.None;
                if (this.source != null)
                    connectionEdge = this.source.ConnectionEdge; 
                return connectionEdge;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal string TargetActivity 
        {
            get 
            {
                string activityName = String.Empty;
                if (this.target != null)
                    activityName = this.target.AssociatedDesigner.Activity.QualifiedName; 
                return activityName;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal int TargetConnectionIndex 
        {
            get 
            {
                int connectionIndex = 0;
                if (this.target != null)
                    connectionIndex = this.target.ConnectionIndex; 
                return connectionIndex;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal DesignerEdges TargetConnectionEdge 
        {
            get 
            {
                DesignerEdges connectionEdge = DesignerEdges.None;
                if (this.target != null)
                    connectionEdge = this.target.ConnectionEdge; 
                return connectionEdge;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal List Segments 
        {
            get 
            {
                return this.segments;
            }
        } 
        #endregion
 
        #endregion 
    }
    #endregion 

    #region Class ConnectorAccessibleObject

    public class ConnectorAccessibleObject : AccessibleObject 
    {
        private Connector connector; 
 
        public ConnectorAccessibleObject(Connector connector)
        { 
            if (connector == null)
                throw new ArgumentNullException("connector");
            this.connector = connector;
        } 

        public override Rectangle Bounds 
        { 
            get
            { 
                WorkflowView parentView = this.connector.ParentDesigner.ParentView;
                Rectangle bounds = this.connector.Bounds;
                return new Rectangle(parentView.LogicalPointToScreen(bounds.Location), parentView.LogicalSizeToClient(bounds.Size));
            } 
        }
 
        public override AccessibleObject HitTest(int x, int y) 
        {
            WorkflowView parentView = this.connector.ParentDesigner.ParentView; 
            if (this.connector.HitTest(parentView.ScreenPointToLogical(new Point(x, y))))
                return this;
            else
                return null; 
        }
 
        public override string Name 
        {
            get 
            {
                return this.connector.GetType().Name;
            }
            set 
            {
            } 
        } 

        public override AccessibleObject Parent 
        {
            get
            {
                return connector.ParentDesigner.AccessibilityObject; 
            }
        } 
 
        public override AccessibleRole Role
        { 
            get
            {
                return AccessibleRole.Diagram;
            } 
        }
    } 
 
    #endregion Class ConnectorAccessibleObject
 
    #region Class ConnectorEditor
    internal sealed class ConnectorEditor
    {
        private IServiceProvider serviceProvider; 
        private Connector editedConnector;
        private EditPoint activeEditPoint; 
        private List editPoints = new List(); 

        public ConnectorEditor(Connector connectorEdited) 
        {
            this.editedConnector = connectorEdited;
            this.serviceProvider = this.editedConnector.Source.AssociatedDesigner.Activity.Site;
            CreateEditPoints(); 
        }
 
        public ConnectionPoint EditedConectionPoint 
        {
            get 
            {
                if (this.activeEditPoint != null)
                    return this.activeEditPoint.EditedConnectionPoint;
                else 
                    return null;
            } 
        } 

        public Connector EditedConnector 
        {
            get
            {
                return this.editedConnector; 
            }
        } 
 
        public Cursor GetCursor(Point cursorPoint)
        { 
            Cursor cursor = Cursors.Default;
            if (this.activeEditPoint != null)
            {
                cursor = ConnectionManager.NewConnectorCursor; 
            }
            else 
            { 
                foreach (EditPoint editPoint in this.editPoints)
                { 
                    if (editPoint.Bounds.Contains(cursorPoint))
                    {
                        cursor = ConnectionManager.SnappedConnectionCursor;
                        break; 
                    }
                } 
            } 

            return cursor; 
        }

        public bool OnBeginEditing(Point point)
        { 
            //Get all the editable points
            CreateEditPoints(); 
 
            EditPoint editPointHit = null;
            for (int i = this.editPoints.Count - 1; i >= 0; i--) 
            {
                if (this.editPoints[i].Bounds.Contains(point))
                {
                    editPointHit = this.editPoints[i]; 
                    break;
                } 
            } 

            if (editPointHit != null && 
                (editPointHit.EditedConnectionPoint == null ||
                ConnectionManager.GetConnectorContainer(editPointHit.EditedConnectionPoint.AssociatedDesigner) != null))
            {
                editPointHit.Location = point; 
                this.activeEditPoint = editPointHit;
            } 
 
            Invalidate();
            return (this.activeEditPoint != null); 
        }

        public void OnContinueEditing(Point point)
        { 
            if (this.activeEditPoint == null)
                return; 
 
            Invalidate();
            UpdateEditPoints(point); 
            Invalidate();

#if DISPLAYESCAPEREGIONS
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView; 
            if (workflowView != null)
                workflowView.InvalidateClientRectangle(Rectangle.Empty); 
#endif 
        }
 
        public void OnEndEditing(Point point, bool commitChanges)
        {
            if (this.activeEditPoint == null)
                return; 

            Invalidate(); 
            if (commitChanges) 
            {
                //This is to update the edit points based on the activepoint 
                UpdateEditPoints(point);

                EditPoint activeEditPoint = this.activeEditPoint;
                this.activeEditPoint = null; 

                //This call is to optimize the segments 
                UpdateEditPoints(point); 

                //If we were creating a new connector or modifying the connection end points 
                bool updateSegments = false;
                if (activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                {
                    ConnectionManager connectionManager = GetService(typeof(ConnectionManager)) as ConnectionManager; 
                    FreeformActivityDesigner connectorContainer = ConnectionManager.GetConnectorContainer(activeEditPoint.EditedConnectionPoint.AssociatedDesigner);
                    if (connectionManager != null && connectionManager.SnappedConnectionPoint != null && connectorContainer != null) 
                    { 
                        //Get the original source and targets
                        ConnectionPoint source = this.editedConnector.Source; 
                        ConnectionPoint target = this.editedConnector.Target;

                        //Make sure that we set the source and target correctly
                        if (target.Equals(activeEditPoint.EditedConnectionPoint)) 
                            target = connectionManager.SnappedConnectionPoint;
                        else if (source.Equals(activeEditPoint.EditedConnectionPoint)) 
                            source = connectionManager.SnappedConnectionPoint; 

                        //Check if it is a valid connection ie source can be connected to target 
                        if (connectorContainer == ConnectionManager.GetConnectorContainer(target.AssociatedDesigner) &&
                            connectorContainer.CanConnectContainedDesigners(source, target))
                        {
                            this.editedConnector.Source = source; 
                            this.editedConnector.Target = target;
 
                            if (this.editedConnector.ParentDesigner == null) 
                            {
                                this.editedConnector = connectorContainer.AddConnector(source, target); 

                                WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
                                if (loader != null)
                                    loader.SetModified(true); 
                            }
 
                            connectorContainer.OnContainedDesignersConnected(source, target); 
                        }
 
                        updateSegments = true;
                    }
                }
                else 
                {
                    updateSegments = true; 
                } 

                //Make sure that we apply the edit points to the connector 
                if (updateSegments)
                {
                    this.editedConnector.SetConnectorSegments(GetPointsFromEditPoints(this.editPoints));
                    if (this.editedConnector.ParentDesigner != null) 
                    {
                        this.editedConnector.ParentDesigner.OnConnectorChanged(new ConnectorEventArgs(this.editedConnector)); 
 
                        WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
                        if (loader != null) 
                            loader.SetModified(true);
                    }
                }
 
                PerformLayout();
            } 
 
            Invalidate();
        } 

        public bool HitTest(Point point)
        {
 			for (int i = 0; i < this.editPoints.Count; i++) 
			{
				EditPoint editPoint = this.editPoints[i]; 
                if (editPoint.Bounds.Contains(point)) 
                    return true;
            } 

            return false;
        }
 
        public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawSelected, bool drawPrimarySelection)
        { 
            List segments = new List(); 
            List segmentEditPoints = new List();
			for (int i = 0; i < this.editPoints.Count; i++) 
 			{
				EditPoint editPoint = this.editPoints[i];
                if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
                    editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                    segments.Add(editPoint.Location);
                else 
                    segmentEditPoints.Add(editPoint.Location); 
            }
 
            //If the editing is in progress then we want to draw the dashed lines
            if (drawSelected)
                this.editedConnector.OnPaintSelected(e, drawPrimarySelection, segmentEditPoints.ToArray());
 
            if (this.activeEditPoint != null)
                this.editedConnector.OnPaintEdited(e, segments.ToArray(), segmentEditPoints.ToArray()); 
 
            //
#if DISPLAYESCAPEREGIONS 
            if (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
            {
                object source = null, target = null;
                if (this.activeEditPoint.EditedConnectionPoint.Equals(Target)) 
                {
                    target = this.activeEditPoint.Location; 
                    source = Source; 
                }
                else 
                {
                    source = this.activeEditPoint.Location;
                    target = Target;
                } 

                List rectanglesToExclude; 
                List linesToExclude, pointsToExclude; 
                ActivityDesignerConnectorRouter.GetRoutingObstacles(this.serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);
 
                ICollection userDefinedObstacles = this.editedConnector.ExcludedRoutingRectangles;
                if (userDefinedObstacles != null)
                {
                    foreach (Rectangle rectangle in userDefinedObstacles) 
                        e.Graphics.DrawRectangle(Pens.DarkGreen, rectangle);
                } 
 
                foreach (Rectangle rectangle in rectanglesToExclude)
                    e.Graphics.DrawRectangle(Pens.Red, rectangle); 

                for (int i = 0; i < linesToExclude.Count / 2; i++)
                    e.Graphics.DrawLine(Pens.Red, linesToExclude[i * 2], linesToExclude[(i * 2) + 1]);
            } 
#endif
        } 
        #region Helpers 

        #region EditPointUpdation Logic 
        private ConnectionPoint Source
        {
            get
            { 
                return this.editedConnector.Source;
            } 
        } 

        private ConnectionPoint Target 
        {
            get
            {
                return this.editedConnector.Target; 
            }
        } 
 
        private void PerformLayout()
        { 
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
            if (workflowView != null)
                workflowView.PerformLayout(false);
        } 

        private void Invalidate() 
        { 
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
            if (workflowView != null) 
            {
                Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(GetPointsFromEditPoints(this.editPoints).ToArray());
                bounds.Inflate(1, 1);
                workflowView.InvalidateLogicalRectangle(bounds); 
            }
        } 
 
        private object GetService(Type serviceType)
        { 
            object service = null;
            if (this.serviceProvider != null)
                service = this.serviceProvider.GetService(serviceType);
            return service; 
        }
 
        private void CreateEditPoints() 
        {
            this.editPoints.Clear(); 

            AddEditPoints(EditPoint.EditPointTypes.ConnectionEditPoint);
            AddEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
            AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint); 

            bool validEditPoints = ValidateEditPoints(); 
            Debug.Assert(validEditPoints); 
        }
 
        private void UpdateEditPoints(Point newPoint)
        {
            if (this.editPoints.Count < 2 ||
                this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint)
            { 
                Debug.Assert(false); 
                return;
            } 

            //STEP1: First we delete all the midsegmentpoints except the one which is being edited for simplicity
            RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
 
            //STEP2: Update points as per the type of edit point
            if (this.activeEditPoint != null) 
            { 
                int activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
                EditPoint previous = (activeEditPointIndex > 0) ? this.editPoints[activeEditPointIndex - 1] : null; 
                EditPoint next = (activeEditPointIndex < this.editPoints.Count - 1) ? this.editPoints[activeEditPointIndex + 1] : null;

                //Note that extra edit points are only added if we are connected to connection point
                if (previous != null && previous.Type == EditPoint.EditPointTypes.ConnectionEditPoint) 
                {
                    float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location); 
                    Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 

                    int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints(previous.Location, (next != null) ? next.Location : this.activeEditPoint.Location)) / 4; 
                    if (orientation == Orientation.Horizontal)
                        editPointOffset *= (previous.Location.X < this.activeEditPoint.Location.X) ? 1 : -1;
                    else
                        editPointOffset *= (previous.Location.Y < this.activeEditPoint.Location.X) ? 1 : -1; 

                    activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint); 
                    Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X + editPointOffset, previous.Location.Y) : new Point(previous.Location.X, previous.Location.Y + editPointOffset); 
                    previous = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
                    this.editPoints.InsertRange(activeEditPointIndex, new EditPoint[] { new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation), previous }); 
                }

                if (next != null && next.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                { 
                    float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
                    Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 
 
                    int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints((previous != null) ? previous.Location : this.activeEditPoint.Location, next.Location)) / 4;
                    if (orientation == Orientation.Horizontal) 
                        editPointOffset *= (this.activeEditPoint.Location.X < next.Location.X) ? -1 : 1;
                    else
                        editPointOffset *= (this.activeEditPoint.Location.Y < next.Location.Y) ? -1 : 1;
 
                    activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
                    Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(next.Location.X + editPointOffset, next.Location.Y) : new Point(next.Location.X, next.Location.Y + editPointOffset); 
                    next = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation); 
                    this.editPoints.InsertRange(activeEditPointIndex + 1, new EditPoint[] { next, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation) });
                } 

                //STEP2: UPDATE THE EDIT POINTS
                if (this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                { 
                    this.activeEditPoint.Location = newPoint;
 
                    //When we start editing the end point we need to clear the slate and start over 
                    RemoveEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
 
                    object source = null, target = null;
                    if (this.activeEditPoint.EditedConnectionPoint.Equals(Target))
                    {
                        target = newPoint; 
                        source = Source;
                    } 
                    else 
                    {
                        source = newPoint; 
                        target = Target;
                    }

                    int newEditPointIndex = (this.editPoints.Count == 2) ? 1 : 0; 
                    List newEditPoints = new List();
                    Point[] points = ActivityDesignerConnectorRouter.Route(this.serviceProvider, source, target, this.editedConnector.ExcludedRoutingRectangles); 
                    for (int i = newEditPointIndex; i < points.Length - newEditPointIndex; i++ ) 
                        newEditPoints.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, points[i]));
                    this.editPoints.InsertRange(1, newEditPoints.ToArray()); 
                }
                else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
                {
                    if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint) 
                    {
                        //Update the previous point 
                        float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location); 
                        Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
                        previous.Location = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X, newPoint.Y) : new Point(newPoint.X, previous.Location.Y); 

                        //Update the next point
                        slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
                        orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 
                        next.Location = (orientation == Orientation.Horizontal) ? new Point(next.Location.X, newPoint.Y) : new Point(newPoint.X, next.Location.Y);
 
                        //Update the current point 
                        this.activeEditPoint.Location = newPoint;
                    } 
                    else
                    {
                        Debug.Assert(false);
                    } 
                }
                else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MidSegmentEditPoint) 
                { 
                    if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint)
                    { 
                        float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, next.Location);
                        Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;

                        //If the orientation is horizontal then we need to move the points vertically else we need to move the points horizontally 
                        if (orientation == Orientation.Horizontal)
                        { 
                            previous.Location = new Point(previous.Location.X, newPoint.Y); 
                            next.Location = new Point(next.Location.X, newPoint.Y);
                            this.activeEditPoint.Location = new Point(this.activeEditPoint.Location.X, newPoint.Y); 
                        }
                        else
                        {
                            previous.Location = new Point(newPoint.X, previous.Location.Y); 
                            next.Location = new Point(newPoint.X, next.Location.Y);
                            this.activeEditPoint.Location = new Point(newPoint.X, this.activeEditPoint.Location.Y); 
                        } 
                    }
                    else 
                    {
                        Debug.Assert(false);
                    }
                } 
            }
 
            //STEP3: Remove all the redundant edit points 
            RemoveCoincidingEditPoints();
 
            //STEP4: Add back the segment mid points
            AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);

            bool validEditPoints = ValidateEditPoints(); 
            Debug.Assert(validEditPoints);
        } 
 
        //Add edit points of specified type
        private void AddEditPoints(EditPoint.EditPointTypes editPointType) 
        {
            if (editPointType == EditPoint.EditPointTypes.ConnectionEditPoint)
            {
                if (this.editPoints.Count == 0 || !this.editPoints[0].EditedConnectionPoint.Equals(Source)) 
                    this.editPoints.Insert(0, new EditPoint(this, Source));
 
                if (this.editPoints.Count < 2 || !this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint.Equals(Target)) 
                    editPoints.Add(new EditPoint(this, Target));
            } 
            else if (editPointType == EditPoint.EditPointTypes.MidSegmentEditPoint)
            {
                int minLengthForSegmentEditPoint = Source.Bounds.Width * 4;
                for (int i = 0; i < this.editPoints.Count - 1; i++) 
                {
                    if (this.editPoints[i].Type != EditPoint.EditPointTypes.MidSegmentEditPoint && 
                        this.editPoints[i + 1].Type != EditPoint.EditPointTypes.MidSegmentEditPoint && 
                        DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { this.editPoints[i].Location, this.editPoints[i + 1].Location }) > minLengthForSegmentEditPoint)
                    { 
                        Point midPoint = DesignerGeometryHelper.MidPointOfLineSegment(this.editPoints[i].Location, this.editPoints[i + 1].Location);
                        this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MidSegmentEditPoint, midPoint));
                    }
                } 
            }
            else if (editPointType == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
            { 
                if (this.editPoints.Count == 2)
                { 
                    List segments = new List(this.editedConnector.ConnectorSegments);
                    if (segments.Count > 0 && segments[0] == Source.Location)
                        segments.RemoveAt(0);
                    if (segments.Count > 0 && segments[segments.Count - 1] == Target.Location) 
                        segments.RemoveAt(segments.Count - 1);
 
                    List editPointsToAdd = new List(); 
                    for (int i = 0; i < segments.Count; i++)
                        editPointsToAdd.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, segments[i])); 
                    this.editPoints.InsertRange(this.editPoints.Count - 1, editPointsToAdd.ToArray());
                }
                else
                { 
                    Debug.Assert(false);
                } 
            } 
        }
 
        //Remove edit points of specified type
        private void RemoveEditPoints(EditPoint.EditPointTypes editPointType)
        {
            List editPointsToRemove = new List(); 
 			for (int i = 0; i < this.editPoints.Count; i++)
 			{ 
				EditPoint editPoint = this.editPoints[i]; 
                if (editPoint.Type == editPointType)
                    editPointsToRemove.Add(editPoint); 
            }

 			for (int i = 0; i < editPointsToRemove.Count; i++)
			{ 
				EditPoint editPoint = editPointsToRemove[i];
                if (editPoint != this.activeEditPoint) 
                    this.editPoints.Remove(editPoint); 
            }
        } 

        //Remove points with same slope
        private void RemoveCoincidingEditPoints()
        { 
            if (this.editPoints.Count < 2 ||
                this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint))
            { 
                return;
            }

            //Just make sure that there are no mid segmment edit points or algorithm will fail 
            RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
 
            //Create list of points to retain 
            List editPointsToRetain = new List();
            for (int i = 0; i < this.editPoints.Count; i++) 
            {
                if (this.editPoints[i].Type != EditPoint.EditPointTypes.MultiSegmentEditPoint ||
                    this.editPoints[i] == this.activeEditPoint ||
                    (i > 0 && this.editPoints[i - 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint) || 
                    (i < this.editPoints.Count - 1 && this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint))
                { 
                    editPointsToRetain.Add(this.editPoints[i]); 
                }
            } 

            //Step1: Get rid of all the line segments which are within tolerance range
            for (int i = 1; i < this.editPoints.Count - 1; i++)
            { 
                EditPoint previous = this.editPoints[i - 1];
                EditPoint current = this.editPoints[i]; 
                EditPoint next = this.editPoints[i + 1]; 

                if (!editPointsToRetain.Contains(current)) 
                {
                    double distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { previous.Location, current.Location });
                    if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && next.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
                    { 
                        float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
                        next.Location = (slope < 1) ? new Point(next.Location.X, previous.Location.Y) : new Point(previous.Location.X, next.Location.Y); 
                        this.editPoints.Remove(current); 
                        i -= 1;
                    } 
                    else
                    {
                        distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { current.Location, next.Location });
                        if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && previous.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                        {
                            float slope = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location); 
                            previous.Location = (slope < 1) ? new Point(previous.Location.X, next.Location.Y) : new Point(next.Location.X, previous.Location.Y); 
                            this.editPoints.Remove(current);
                            i -= 1; 
                        }
                    }
                }
            } 

            //Step2: We should make sure that the active edit point is always retained but those points which are coincidental are always removed 
            for (int i = 1; i < this.editPoints.Count - 1; i++) 
            {
                EditPoint current = this.editPoints[i]; 
                EditPoint previous = this.editPoints[i - 1];
                EditPoint next = this.editPoints[i + 1];

                if (!editPointsToRetain.Contains(current)) 
                {
                    float slope1 = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location); 
                    float slope2 = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location); 
                    if (Math.Abs(slope1) == Math.Abs(slope2))
                    { 
                        this.editPoints.Remove(current);
                        i -= 1;
                    }
                } 
            }
 
            //Step3: Go thorugh each segment and ensure that there all the segments are either vertical or horizontal 
            for (int i = 0; i < this.editPoints.Count - 1; i++)
            { 
                EditPoint current = this.editPoints[i];
                EditPoint next = this.editPoints[i + 1];

                float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location); 
                if (slope != 0 && slope != float.MaxValue)
                { 
                    Point location = (slope < 1) ? new Point(next.Location.X, current.Location.Y) : new Point(current.Location.X, next.Location.Y); 
                    this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, location));
                } 
            }
        }

        private bool ValidateEditPoints() 
        {
            if (this.editPoints.Count < 2) 
                return false; 

            ConnectionPoint sourceConnection = this.editPoints[0].EditedConnectionPoint; 
            if (sourceConnection == null || !sourceConnection.Equals(Source))
                return false;

            ConnectionPoint targetConnection = this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint; 
            if (targetConnection == null || !targetConnection.Equals(Target))
                return false; 
 
            for (int i = 0; i < this.editPoints.Count - 1; i++)
            { 
                if (this.editPoints[i].Type == EditPoint.EditPointTypes.MidSegmentEditPoint &&
                    this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint)
                    return false;
            } 

            return true; 
        } 
        #endregion
 
        private List GetPointsFromEditPoints(List editPoints)
        {
            List segments = new List();
 
			for (int i = 0; i < editPoints.Count; i++)
 			{ 
				EditPoint editPoint = editPoints[i]; 
                if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
                    editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                    segments.Add(editPoint.Location);
            }

            return segments; 
        }
        #endregion 
 
        #region Class EditPoint
        private sealed class EditPoint 
        {
            public enum EditPointTypes { ConnectionEditPoint = 1, MultiSegmentEditPoint, MidSegmentEditPoint };

            private ConnectorEditor owner; 
            private EditPointTypes editPointType;
            private Point point; 
            private ConnectionPoint connectionPoint; 

            public EditPoint(ConnectorEditor owner, EditPointTypes editPointType, Point point) 
            {
                this.owner = owner;
                this.editPointType = editPointType;
                this.point = point; 
            }
 
            public EditPoint(ConnectorEditor owner, ConnectionPoint connectionPoint) 
            {
                this.owner = owner; 
                this.editPointType = EditPointTypes.ConnectionEditPoint;
                this.connectionPoint = connectionPoint;
                this.point = connectionPoint.Location;
            } 

            public EditPointTypes Type 
            { 
                get
                { 
                    return this.editPointType;
                }
            }
 
            public Point Location
            { 
                get 
                {
                    return this.point; 
                }

                set
                { 
                    this.point = value;
                } 
            } 

            public Rectangle Bounds 
            {
                get
                {
                    Size controlPointSize = this.owner.Source.Bounds.Size; 
                    return new Rectangle(this.point.X - controlPointSize.Width / 2, this.point.Y - controlPointSize.Height / 2, controlPointSize.Width, controlPointSize.Height);
                } 
            } 

            public ConnectionPoint EditedConnectionPoint 
            {
                get
                {
                    return this.connectionPoint; 
                }
            } 
        } 
        #endregion
    } 
    #endregion

    #region Class ActivityDesignerConnectorRouter
    internal static class ActivityDesignerConnectorRouter 
    {
        public static Point[] Route(IServiceProvider serviceProvider, object source, object target, ICollection userDefinedRoutingObstacles) 
        { 
            //Now call routing algorithm
            List rectanglesToExclude; 
            List linesToExclude, pointsToExclude;
            ActivityDesignerConnectorRouter.GetRoutingObstacles(serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);

            if (userDefinedRoutingObstacles != null) 
                rectanglesToExclude.AddRange(userDefinedRoutingObstacles);
 
            ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider); 
            AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
            Point sourcePoint = (source is ConnectionPoint) ? ((ConnectionPoint)source).Location : (Point)source; 
            Point targetPoint = (target is ConnectionPoint) ? ((ConnectionPoint)target).Location : (Point)target;

            Point[] routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, new Size(2 * ambientTheme.Margin.Width, 2 * ambientTheme.Margin.Height), rootDesigner.Bounds, rectanglesToExclude.ToArray(), linesToExclude.ToArray(), pointsToExclude.ToArray());
 
            //
 
 

            if (!AreAllSegmentsVerticalOrHorizontal(routedPoints)) 
                routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, ambientTheme.Margin, rootDesigner.Bounds, new Rectangle[] { }, linesToExclude.ToArray(), new Point[] { });

            //FALLBACK1
            if (!AreAllSegmentsVerticalOrHorizontal(routedPoints)) 
            {
                float slope = DesignerGeometryHelper.SlopeOfLineSegment(sourcePoint, targetPoint); 
                Point intermediatePoint = (slope < 1) ? new Point(targetPoint.X, sourcePoint.Y) : new Point(sourcePoint.X, targetPoint.Y); 
                routedPoints = new Point[] { sourcePoint, intermediatePoint, targetPoint };
            } 

            return routedPoints;
        }
 
        public static void GetRoutingObstacles(IServiceProvider serviceProvider, object source, object target, out List rectanglesToExclude, out List linesToExclude, out List pointsToExclude)
        { 
            //Source or Targets can be either ConnectionPoint or a Point 
            AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
            ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider); 

            ConnectionPoint sourceConnectionPoint = source as ConnectionPoint;
            Point sourcePoint = (sourceConnectionPoint != null) ? sourceConnectionPoint.Location : (Point)source;
            ActivityDesigner sourceDesigner = (sourceConnectionPoint != null) ? sourceConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(sourcePoint).AssociatedDesigner; 

            ConnectionPoint targetConnectionPoint = target as ConnectionPoint; 
            Point targetPoint = (targetConnectionPoint != null) ? targetConnectionPoint.Location : (Point)target; 
            ActivityDesigner targetDesigner = (targetConnectionPoint != null) ? targetConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(targetPoint).AssociatedDesigner;
 
            //Collect the common parent chain of source and target
            Dictionary parentDesignerDictionary = new Dictionary();
            if (sourceDesigner != null)
            { 
                //Collect designers in the source parent chain
                CompositeActivityDesigner parentDesigner = sourceDesigner.ParentDesigner; 
                while (parentDesigner != null) 
                {
                    if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode())) 
                        parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner);
                    else
                        break;
                    parentDesigner = parentDesigner.ParentDesigner; 
                }
            } 
 
            if (targetDesigner != null)
            { 
                //Collect designer from target chain which are not in source chain
                CompositeActivityDesigner parentDesigner = targetDesigner.ParentDesigner;
                while (parentDesigner != null)
                { 
                    if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode()))
                        parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner); 
                    else 
                        break;
                    parentDesigner = parentDesigner.ParentDesigner; 
                }
            }

            //Now go through the dictionary and add all the children that are not in the chain 
            rectanglesToExclude = new List();
            pointsToExclude = new List(); 
            foreach (CompositeActivityDesigner parentDesigner in parentDesignerDictionary.Values) 
            {
 				ReadOnlyCollection containedDesigners = parentDesigner.ContainedDesigners; 
 				for (int j = 0; j < containedDesigners.Count; j++)
				{
 					ActivityDesigner activityDesigner = containedDesigners[j];
                    if (activityDesigner.IsVisible && 
                        !parentDesignerDictionary.ContainsKey(activityDesigner.GetHashCode()) &&
                        activityDesigner != sourceDesigner && 
                        activityDesigner != targetDesigner) 
                    {
                        Rectangle rectangleToExclude = activityDesigner.Bounds; 
                        rectangleToExclude.Inflate(ambientTheme.Margin);
                        rectanglesToExclude.Add(rectangleToExclude);
                    }
                } 

                // 
 

 



 
            }
 
            //Now get the exclusion paths for source designer and target designer 
            linesToExclude = new List();
            if (sourceDesigner != null && sourceDesigner == targetDesigner && !sourceDesigner.IsRootDesigner) 
            {
                linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source, target }));
            }
            else 
            {
                if (sourceDesigner != null && !sourceDesigner.IsRootDesigner) 
                    linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source })); 

				if (targetDesigner != null && !targetDesigner.IsRootDesigner) 
				{
					bool needToEscapeTargetDesigner = true;

 					CompositeActivityDesigner sourceParentDesigner = (sourceDesigner != null) ? sourceDesigner.ParentDesigner : null; 
					while (sourceParentDesigner != null)
 					{ 
 						if (targetDesigner == sourceParentDesigner) 
						{
 							needToEscapeTargetDesigner = false; 
							break;
						}
						sourceParentDesigner = (sourceDesigner != null) ? sourceParentDesigner.ParentDesigner : null;
 					} 

					//need to escape the target designer only if the source is not inside the parent 
 					if(needToEscapeTargetDesigner) 
 						linesToExclude.AddRange(GetDesignerEscapeCover(targetDesigner, new object[] { target }));
				} 
            }
        }

        private static IList GetDesignerEscapeCover(ActivityDesigner designer, ICollection escapeLocations) 
        {
            Rectangle bounds = designer.Bounds; 
 
            //Create dictionary of escape points
            Dictionary> escapeEdges = new Dictionary>(); 
            foreach (object escapeLocation in escapeLocations)
            {
                DesignerEdges escapeEdge = DesignerEdges.None;
                Point escapePoint = Point.Empty; 
                if (escapeLocation is ConnectionPoint)
                { 
                    escapeEdge = ((ConnectionPoint)escapeLocation).ConnectionEdge; 
                    escapePoint = ((ConnectionPoint)escapeLocation).Location;
                } 
                else if (escapeLocation is Point)
                {
                    escapePoint = (Point)escapeLocation;
                    escapeEdge = DesignerGeometryHelper.ClosestEdgeToPoint((Point)escapeLocation, bounds, DesignerEdges.All); 
                }
 
                if (escapeEdge != DesignerEdges.None) 
                {
                    List escapePoints = null; 
                    if (!escapeEdges.ContainsKey(escapeEdge))
                    {
                        escapePoints = new List();
                        escapeEdges.Add(escapeEdge, escapePoints); 
                    }
                    else 
                    { 
                        escapePoints = escapeEdges[escapeEdge];
                    } 

                    escapePoints.Add(escapePoint);
                }
            } 

            //Create a dictionary of four edges of the designer which will form cover which need to be escaped 
            Size margin = WorkflowTheme.CurrentTheme.AmbientTheme.Margin; 
            bounds.Inflate(margin);
            Dictionary designerEdgeCover = new Dictionary(); 
            designerEdgeCover.Add(DesignerEdges.Left, new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Left, bounds.Bottom) });
            designerEdgeCover.Add(DesignerEdges.Top, new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Right, bounds.Top) });
            designerEdgeCover.Add(DesignerEdges.Right, new Point[] { new Point(bounds.Right, bounds.Top), new Point(bounds.Right, bounds.Bottom) });
            designerEdgeCover.Add(DesignerEdges.Bottom, new Point[] { new Point(bounds.Left, bounds.Bottom), new Point(bounds.Right, bounds.Bottom) }); 

            List linesToExclude = new List(); 
            foreach (DesignerEdges designerEdge in designerEdgeCover.Keys) 
            {
                if (escapeEdges.ContainsKey(designerEdge)) 
                {
                    Point[] edge = designerEdgeCover[designerEdge];
                    List escapePoints = escapeEdges[designerEdge];
                    List escapeCover = new List(); 
                    if (designerEdge == DesignerEdges.Left)
                    { 
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y)); 
 						for (int k = 0; k < escapePoints.Count; k++)
						{ 
							Point escapePoint = escapePoints[k];
                            if (escapePoint.X > edge[0].X && escapePoint.Y > edge[0].Y && escapePoint.Y < edge[1].Y)
                            {
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y - 1)); 
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y + 1)); 
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y + 1)); 
                            }
                        } 
                        escapeCover.Add(new Point(edge[0].X, edge[1].Y));
                    }
                    else if (designerEdge == DesignerEdges.Right)
                    { 
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
						for (int k = 0; k < escapePoints.Count; k++) 
                        { 
                        	Point escapePoint = escapePoints[k];
                            if (escapePoint.X < edge[0].X && escapePoint.Y > edge[0].Y && escapePoint.Y < edge[1].Y) 
                            {
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y + 1)); 
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y + 1));
                            } 
                        } 
                        escapeCover.Add(new Point(edge[0].X, edge[1].Y));
                    } 
                    else if (designerEdge == DesignerEdges.Top)
                    {
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
 						for (int k = 0; k < escapePoints.Count; k++) 
						{
 							Point escapePoint = escapePoints[k]; 
                            if (escapePoint.Y > edge[0].Y && escapePoint.X > edge[0].X && escapePoint.X < edge[1].X) 
                            {
                                escapeCover.Add(new Point(escapePoint.X - 1, edge[0].Y)); 
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y + 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y + 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, edge[0].Y));
                            } 
                        }
                        escapeCover.Add(new Point(edge[1].X, edge[0].Y)); 
                    } 
                    else if (designerEdge == DesignerEdges.Bottom)
                    { 
                        //Add edge so that the connector does not pass through
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
 						for (int k = 0; k < escapePoints.Count; k++)
						{ 
 							Point escapePoint = escapePoints[k];
                            if (escapePoint.Y < edge[0].Y && escapePoint.X > edge[0].X && escapePoint.X < edge[1].X) 
                            { 
                                escapeCover.Add(new Point(escapePoint.X - 1, edge[0].Y));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y - 1)); 
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, edge[0].Y));
                            }
                        } 
                        escapeCover.Add(new Point(edge[1].X, edge[0].Y));
                    } 
 
                    for (int i = 1; i < escapeCover.Count; i++)
                    { 
                        linesToExclude.Add(escapeCover[i - 1]);
                        linesToExclude.Add(escapeCover[i]);
                    }
                } 
                else
                { 
                    linesToExclude.AddRange(designerEdgeCover[designerEdge]); 
                }
            } 

            return linesToExclude.AsReadOnly();
        }
 
        private static bool AreAllSegmentsVerticalOrHorizontal(Point[] segments)
        { 
            if (segments == null || segments.Length == 0) 
                return false;
 
            for (int i = 1; i < segments.Length; i++)
            {
                if (segments[i - 1].X != segments[i].X && segments[i - 1].Y != segments[i].Y)
                    return false; 
            }
 
            return true; 
        }
    } 
    #endregion

    #region Class ConnectorRouter
    internal static class ConnectorRouter 
    {
        private static readonly Size DefaultSize = new Size(4, 4); 
 
        public static Point[] Route(Point begin, Point end, Size margin, Rectangle enclosingRectangle, Rectangle[] rectanglesToExclude, Point[] linesToExclude, Point[] pointsToExclude)
        { 
            List excludedRectangles = new List(rectanglesToExclude);
            if (!enclosingRectangle.IsEmpty)
            {
                begin.X = Math.Min(Math.Max(begin.X, enclosingRectangle.Left + 1), enclosingRectangle.Right - 1); 
                begin.Y = Math.Min(Math.Max(begin.Y, enclosingRectangle.Top + 1), enclosingRectangle.Bottom - 1);
 
                excludedRectangles.Insert(0, enclosingRectangle); 
            }
 
            List excludedLines = new List(linesToExclude);
            int width = Math.Max(margin.Width / 2, 1), height = Math.Max(margin.Height / 2, 1);
            foreach (Point point in pointsToExclude)
            { 
                excludedLines.Add(new Point(point.X - width, point.Y));
                excludedLines.Add(new Point(point.X + width, point.Y)); 
 
                excludedLines.Add(new Point(point.X, point.Y - height));
                excludedLines.Add(new Point(point.X, point.Y + height)); 
            }

            return GetRoutedLineSegments(begin, end, margin, excludedRectangles.ToArray(), excludedLines.ToArray());
        } 

        private static Point[] GetRoutedLineSegments(Point begin, Point end, Size margin, Rectangle[] rectanglesToExclude, Point[] linesToExclude) 
        { 
            if (rectanglesToExclude == null)
                throw new ArgumentNullException("rectanglesToExclude"); 

            if (linesToExclude == null)
                throw new ArgumentNullException("linesToExclude");
 
            if ((linesToExclude.Length % 2) > 0)
                throw new ArgumentException(DR.GetString(DR.Error_Connector2)); 
 
            CoverSet coverSet = new CoverSet(rectanglesToExclude, linesToExclude);
            coverSet.ClearUsedLines(); 

            Point A = begin;
            Point B = end;
 
            //escape points
            List LeA = new List();//escape points from begin to end 
            List LeB = new List();//escape points from end to begin 

            //horizontal/vertical escape segments from A 
            List LhA = new List();
            List LvA = new List();

            //horizontal/vertical escape segments from B 
            List LhB = new List();
            List LvB = new List(); 
 
            Orientation orientationA = Orientation.Horizontal;
            Orientation orientationB = Orientation.Horizontal; 

            //P0
            LeA.Add(begin);
            LeB.Add(end); 

            bool noEscapeA = false; 
            bool noEscapeB = false; 

            Nullable intersection = null; 
            ConnectorSegment intersectionSegmentA = null;
            ConnectorSegment intersectionSegmentB = null;

            try 
            {
                do 
                { 
                    //P1
                    if (noEscapeA) 
                    {
                        if (noEscapeB)
                        {
                            //we failed to find the point 
                            break;
                        } 
                        else 
                        {
                            //P2 
                            #region swap A and B (with all appropriate lists and flags...)
                            List tempList = LeA;
                            LeA = LeB;
                            LeB = tempList; 

                            Point tempPoint = A; 
                            A = B; 
                            B = tempPoint;
 
                            bool tempBool = noEscapeA;
                            noEscapeA = noEscapeB;
                            noEscapeB = tempBool;
 
                            Orientation tempOrientation = orientationA;
                            orientationA = orientationB; 
                            orientationB = tempOrientation; 

                            List tempListSegm = LhA; 
                            LhA = LhB;
                            LhB = tempListSegm;

                            tempListSegm = LvA; 
                            LvA = LvB;
                            LvB = tempListSegm; 
                            #endregion 

                            continue; 
                        }
                    }

                    Point objectPoint = LeA[LeA.Count - 1]; 
                    Point targetPoint = B;
 
                    intersection = EscapeAlgorithm(coverSet, objectPoint, targetPoint, 
                        ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, ref orientationA,
                        out intersectionSegmentA, out intersectionSegmentB, margin, ref noEscapeA); 
                    if (intersection != null)
                    {
                        break;
                    } 
                    else
                    { 
                        #region swap A and B (with all appropriate lists and flags...) 
                        List tempList = LeA;
                        LeA = LeB; 
                        LeB = tempList;

                        Point tempPoint = A;
                        A = B; 
                        B = tempPoint;
 
                        bool tempBool = noEscapeA; 
                        noEscapeA = noEscapeB;
                        noEscapeB = tempBool; 

                        Orientation tempOrientation = orientationA;
                        orientationA = orientationB;
                        orientationB = tempOrientation; 

                        List tempListSegm = LhA; 
                        LhA = LhB; 
                        LhB = tempListSegm;
 
                        tempListSegm = LvA;
                        LvA = LvB;
                        LvB = tempListSegm;
                        #endregion 
                    }
 
                } while (true); 

                //we failed 
                if (intersection == null)
                    return null;
                //{
                //    // 

 
 

 

                //    return newPath.ToArray();
                //}
 
                List refinedPath = new List();
 
                #region P3 apply refinement algorithms 
                //first refinement algorithm
                LeA = FirstRefinementAlgorithm(LeA, (Point)intersection, intersectionSegmentA); 
                LeB = FirstRefinementAlgorithm(LeB, (Point)intersection, intersectionSegmentB);

                //before going into the second refinement, construct the full path
                for (int j = LeA.Count - 1; j >= 0; j--) 
                    refinedPath.Add(LeA[j]);
                refinedPath.Add((Point)intersection); 
                for (int j = 0; j < LeB.Count; j++) 
                    refinedPath.Add(LeB[j]);
 
                //perform second refinement algorithm on the full path
                SecondRefinementAlgorithm(coverSet, ref refinedPath, margin);
                #endregion
 
                if (refinedPath.Count > 1 && refinedPath[refinedPath.Count - 1] == begin)
                    refinedPath.Reverse(); 
 
                return refinedPath.ToArray();
            } 
            catch
            {
                //
 
                return null;
            } 
        } 

        #region Escape Algorithm 
        private static Nullable EscapeAlgorithm(CoverSet coverSet, Point Z, Point targetPoint,
            ref List LeA, ref List LhA, ref List LvA, ref List LhB, ref List LvB,
            ref Orientation orientationA, out ConnectorSegment intersectionSegmentA, out ConnectorSegment intersectionSegmentB, Size margin, ref bool noEscapeA)
        { 
            Nullable intersection = null;
            intersectionSegmentA = null; 
            intersectionSegmentB = null; 

            ConnectorSegment leftCover = coverSet.GetCover(Z, DesignerEdges.Left); 
            ConnectorSegment rightCover = coverSet.GetCover(Z, DesignerEdges.Right);
            ConnectorSegment bottomCover = coverSet.GetCover(Z, DesignerEdges.Bottom);
            ConnectorSegment topCover = coverSet.GetCover(Z, DesignerEdges.Top);
 
            #region P1: construct escape line(s) - on the beginning of the algorithm it will create two lines
            ConnectorSegment h = ConnectorSegment.SegmentFromLeftToRightCover(coverSet, Z); 
            LhA.Add(h); 

            ConnectorSegment v = ConnectorSegment.SegmentFromBottomToTopCover(coverSet, Z); 
            LvA.Add(v);
            #endregion

            #region P2 check if the new escape line(s) intersect with the existing ones 
			for (int i = 0; i < LvB.Count; i++)
			{ 
				ConnectorSegment segment = LvB[i]; 
                intersection = h.Intersect(segment);
                if (intersection != null) 
                {
                    intersectionSegmentA = h;
                    intersectionSegmentB = segment;
                    return intersection; 
                }
            } 
 
 			for (int i = 0; i < LhB.Count; i++)
			{ 
 				ConnectorSegment segment = LhB[i];
                intersection = v.Intersect(segment);
                if (intersection != null)
                { 
                    intersectionSegmentA = v;
                    intersectionSegmentB = segment; 
                    return intersection; 
                }
            } 
            #endregion

            //there was no intersection found above, continue on
 
            //P3 find an escape point
 
            #region Call Escape Process I 
            //Escape process I P1
            Nullable escapePoint = EscapeProcessI(coverSet, Z, v, Orientation.Horizontal, margin); 
            if (escapePoint != null)
            {
                orientationA = Orientation.Vertical;
                LeA.Add((Point)escapePoint); 
                return null;
            } 
 
            //Escape process I P2
            escapePoint = EscapeProcessI(coverSet, Z, h, Orientation.Vertical, margin); 
            if (escapePoint != null)
            {
                orientationA = Orientation.Horizontal;
                LeA.Add((Point)escapePoint); 
                return null;
            } 
            #endregion 

            #region Call Escape process II 
            bool intersectionFlag = false;
            //flags indicating if we can still continue in the given directions
            bool continue1, continue2, continue3, continue4;
            Point r1 = Point.Empty, r2 = Point.Empty, r3 = Point.Empty, r4 = Point.Empty; 

            if (topCover != null) 
                r1 = new Point(Z.X, topCover.A.Y); 
            if (rightCover != null)
                r2 = new Point(rightCover.A.X, Z.Y); 
            if (bottomCover != null)
                r3 = new Point(Z.X, bottomCover.A.Y);
            if (leftCover != null)
                r4 = new Point(leftCover.A.X, Z.Y); 

            do 
            { 
                continue1 = continue2 = continue3 = continue4 = false;
                if (topCover != null) 
                {
                    r1.Y -= margin.Height;
                    if (r1.Y > Z.Y)
                    { 
                        continue1 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Vertical, 
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r1, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB); 
                        if (escape != null)
                        { 
                            LvA.Add(v);
                            if (intersectionFlag)
                                return escape;
 
                            orientationA = Orientation.Horizontal;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r1)); 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r1, (Point)escape)); 
                            LeA.Add((Point)escape);
                            return null; 
                        }
                    }
                }
 
                if (rightCover != null)
                { 
                    r2.X -= margin.Width; 
                    if (r2.X > Z.X)
                    { 
                        continue2 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Horizontal,
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r2, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB);
                        if (escape != null) 
                        {
                            LhA.Add(h); 
                            if (intersectionFlag) 
                                return escape;
 
                            orientationA = Orientation.Vertical;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r2));
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r2, (Point)escape));
                            LeA.Add((Point)escape); 
                            return null;
                        } 
                    } 
                }
 
                if (bottomCover != null)
                {
                    r3.Y += margin.Height;
                    if (r3.Y < Z.Y) 
                    {
                        continue3 = true; 
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Vertical, 
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r3, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB);
                        if (escape != null) 
                        {
                            LvA.Add(v);
                            if (intersectionFlag)
                                return escape; 

                            orientationA = Orientation.Horizontal; 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r3)); 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r3, (Point)escape));
                            LeA.Add((Point)escape); 
                            return null;
                        }
                    }
                } 

                if (leftCover != null) 
                { 
                    r4.X += margin.Width;
                    if (r4.X < Z.X) 
                    {
                        continue4 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Horizontal,
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r4, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB); 
                        if (escape != null)
                        { 
                            LhA.Add(h); 
                            if (intersectionFlag)
                                return escape; 

                            orientationA = Orientation.Vertical;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r4));
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r4, (Point)escape)); 
                            LeA.Add((Point)escape);
                            return null; 
                        } 
                    }
                } 
                //continue the loop until there is a place to go in either of the directions
            } while (continue1 || continue2 || continue3 || continue4);
            #endregion
 
            noEscapeA = true;
            return null; 
        } 

        #region Escape Process I 
        private static Nullable EscapeProcessI(CoverSet coverSet, Point Z,
            ConnectorSegment escapeLine, Orientation orientation, Size margin)
        {
            //perform extremity point permutation by the distance from the object point 
            //when sorting points store to which segments they belong (this is needed further when
            //deciding to side of the point to move if the extremity coincide with the object point 
            //on abscissa or ordinata axis) 
            List extremitiesList = new List(4);//at most four points
 
            ConnectorSegment lesserCover = coverSet.GetCover(Z, (orientation == Orientation.Horizontal) ? DesignerEdges.Left : DesignerEdges.Bottom);
            if (lesserCover != null)
            {
                AddBoundPoint(ref extremitiesList, lesserCover.A, lesserCover, Z); 
                AddBoundPoint(ref extremitiesList, lesserCover.B, lesserCover, Z);
            } 
 
            ConnectorSegment higherCover = coverSet.GetCover(Z, (orientation == Orientation.Horizontal) ? DesignerEdges.Right : DesignerEdges.Top);
            if (higherCover != null) 
            {
                AddBoundPoint(ref extremitiesList, higherCover.A, higherCover, Z);
                AddBoundPoint(ref extremitiesList, higherCover.B, higherCover, Z);
            } 

            if (extremitiesList.Count == 0) 
                return null; 

            DistanceSorter.Sort(ref extremitiesList); 
            for (int i = 0; i < extremitiesList.Count; i++)
            {
                Point p = extremitiesList[i].P;
                Point direction = new Point(Math.Sign(p.X - Z.X), Math.Sign(p.Y - Z.Y)); 
                if (((orientation == Orientation.Vertical) ? direction.X : direction.Y) == 0)
                { 
                    //if extremity is on the same abscissa(ordinata) line with the object point, need to be more careful 
                    ConnectorSegment segment = extremitiesList[i].ConnectorSegment;
                    p = segment.ExtendPointOutwards(p); 
                    direction = new Point(Math.Sign(p.X - Z.X), Math.Sign(p.Y - Z.Y));
                    p = extremitiesList[i].P;
                }
 
                DesignerEdges side;//which side is the cover we are trying to escape
                if (orientation == Orientation.Vertical) 
                    //we are escaping either top or bottom 
                    side = (direction.Y < 0) ? DesignerEdges.Bottom : DesignerEdges.Top;
                else 
                    //we are escaping either left or right
                    side = (direction.X < 0) ? DesignerEdges.Left : DesignerEdges.Right;

                Point escapePoint; 
                if ((orientation == Orientation.Vertical))
                    escapePoint = new Point(p.X + direction.X * margin.Width, Z.Y); 
                else 
                    escapePoint = new Point(Z.X, p.Y + direction.Y * margin.Height);
 
                //the new escape point should
                //1) lay on the given escape line (except the bounding points since they belong to covers)
                //2) not lay on any of the already tested escape segments - all points belonging to them are already worthless
                ConnectorSegment newEscapeLine = new ConnectorSegment(Z, escapePoint); 
                if (!coverSet.EscapeLineHasBeenUsed(newEscapeLine, escapePoint) &&
                    escapeLine.IsPointOnSegment(escapePoint) && escapeLine.A != escapePoint && escapeLine.B != escapePoint && 
                    coverSet.IsEscapePoint(Z, escapePoint, side)) 
                {
                    coverSet.AddUsedEscapeLine(newEscapeLine); 
                    return escapePoint;
                }
            }
 
            return null;
        } 
 
        private static void AddBoundPoint(ref List extremitiesList, Point p, ConnectorSegment segment, Point Z)
        { 
            if (p.X != int.MinValue && p.X != int.MaxValue && p.Y != int.MinValue && p.Y != int.MaxValue)
                extremitiesList.Add(new DistanceFromPoint(segment, Z, p));
        }
        #endregion 

        #region Escape Process II 
        private static Nullable EscapeProcessII(CoverSet coverSet, Orientation orientation, ref List LeA, 
            ref List LhA, ref List LvA, ref List LhB, ref List LvB,
            Point Z, Point R, Size margin, out bool intersectionFlag, out ConnectorSegment intersectionSegmentA, out ConnectorSegment intersectionSegmentB) 
        {
            intersectionFlag = false;
            intersectionSegmentA = null;
            intersectionSegmentB = null; 

            //rebuild invalidated covers 
            ConnectorSegment h = ConnectorSegment.SegmentFromLeftToRightCover(coverSet, R); 
            ConnectorSegment v = ConnectorSegment.SegmentFromBottomToTopCover(coverSet, R);
 
            //check intersections
 			for (int i = 0; i < LvB.Count; i++)
			{
 				ConnectorSegment segment = LvB[i]; 
                Nullable itersection = h.Intersect(segment);
                if (itersection != null) 
                { 
                    intersectionFlag = true;
                    intersectionSegmentA = h; 
                    intersectionSegmentB = segment;
                    LeA.Add(R);
                    return itersection;
                } 
            }
			for (int i = 0; i < LhB.Count; i++) 
			{ 
				ConnectorSegment segment = LhB[i];
                Nullable itersection = v.Intersect(segment); 
                if (itersection != null)
                {
                    intersectionFlag = true;
                    intersectionSegmentA = v; 
                    intersectionSegmentB = segment;
                    LeA.Add(R); 
                    return itersection; 
                }
            } 

            Nullable escapePointI = null;

            //now do both horizontal and vertical escape processes I from that point... 
            //the order is important based on 'orientation' argument
            if (orientation == Orientation.Horizontal) 
            { 
                escapePointI = EscapeProcessI(coverSet, R, v, Orientation.Horizontal, margin);
                if (escapePointI != null) 
                {
                    LvA.Add(v);
                    LeA.Add(R);
                    return escapePointI; 
                }
 
                escapePointI = EscapeProcessI(coverSet, R, h, Orientation.Vertical, margin); 
                if (escapePointI != null)
                { 
                    LhA.Add(h);
                    LeA.Add(R);
                    return escapePointI;
                } 
            }
            else 
            { 
                escapePointI = EscapeProcessI(coverSet, R, h, Orientation.Vertical, margin);
                if (escapePointI != null) 
                {
                    LhA.Add(h);
                    LeA.Add(R);
                    return escapePointI; 
                }
 
                escapePointI = EscapeProcessI(coverSet, R, v, Orientation.Horizontal, margin); 
                if (escapePointI != null)
                { 
                    LvA.Add(v);
                    LeA.Add(R);
                    return escapePointI;
                } 
            }
 
            return null; 
        }
        #endregion 

        #endregion

        #region ConnectorSegment Refinement Algorithms 
        //remove all non-corner points
        private static List FirstRefinementAlgorithm(List Le, Point intersection, ConnectorSegment intersectionSegment) 
        { 
            List refinedSet = new List();
            ConnectorSegment k = intersectionSegment; 

            while (Le.Count > 0)
            {
                Point pi; 
                int i = Le.Count - 1;
 
                //find the first point that lies on k 
                while (!k.PointLiesOnThisLine(Le[i]) && i > 0)
                    i--; 

                //find the least point that lies on k
                while (i > 0 && k.PointLiesOnThisLine(Le[i - 1]))
                    i--; 

                //save the point 
                pi = Le[i]; 
                refinedSet.Add(pi);
 
                //remove all the points up to the found one from the original list
                while (Le.Count > i)
                    Le.RemoveAt(i);
 
                //continue with the points lying on the line perpendecular to the previous one and passing through the
                //found point 
                k = k.PeprendecularThroughPoint(pi); 
            }
 
            return refinedSet;
        }

        //remove superflous parts from the path 
        private static void SecondRefinementAlgorithm(CoverSet coverSet, ref List refinedPath, Size margin)
        { 
            List newPath = new List(); 

            #region Part I: extend every segment in the path to see if it intersects any other segment 
            int currentSegment = 0;
            while (currentSegment < refinedPath.Count - 1)
            {
                Point a1 = refinedPath[currentSegment]; 
                Point a2 = refinedPath[currentSegment + 1];
 
                //need to construct a segment through the points that is limited by the covers 
                ConnectorSegment a = ConnectorSegment.ConstructBoundSegment(coverSet, a1, a2);
 
                //try to intersect it with every segment after the current one
                //and the next one (which we know does intersect with the current one)
                int intersectingSegment = currentSegment + 2;
                while (intersectingSegment < refinedPath.Count - 1) 
                {
                    Point b1 = refinedPath[intersectingSegment]; 
                    Point b2 = refinedPath[intersectingSegment + 1]; 
                    ConnectorSegment b = ConnectorSegment.ConstructBoundSegment(coverSet, b1, b2);
 
                    Nullable intersection = a.Intersect(b);
                    if (intersection != null)
                    {
                        newPath.Clear(); 
                        //need to remove all points between a1 and b2 and insert the point of intersectin there
                        for (int i = 0; i <= currentSegment; i++) 
                            newPath.Add(refinedPath[i]); 
                        newPath.Add((Point)intersection);
                        for (int i = intersectingSegment + 1; i < refinedPath.Count; i++) 
                            newPath.Add(refinedPath[i]);

                        List temp = refinedPath;
                        refinedPath = newPath; 
                        newPath = temp;
                        newPath.Clear(); 
 
                        //reset the second segment number and go through all segments once more
                        //until there are no intersections left 
                        intersectingSegment = currentSegment + 2;
                    }
                    else
                    { 
                        intersectingSegment++;
                    } 
                } 

                //we need to keep looking for intersections until we reach the very last one 
                currentSegment++;
            }
            #endregion
 
            #region Part II: construct segments perpendecular to every segment and see if they intersect any other segment
            currentSegment = 0; 
            while (currentSegment < refinedPath.Count - 1) 
            {
                Point a1 = refinedPath[currentSegment]; 
                Point a2 = refinedPath[currentSegment + 1];

                //need to construct a segment through the points that is limited by the covers
                bool intersected = false; 
                ConnectorSegment a = ConnectorSegment.ConstructBoundSegment(coverSet, a1, a2);
                if (a != null) 
                { 
                    Point direction = new Point(a2.X - a1.X, a2.Y - a1.Y);
 
                    //see how many intemediate points we can construct
                    int steps = Math.Max(Math.Abs(direction.X / margin.Width), Math.Abs(direction.Y / margin.Height));//one of the values will be null
                    direction.X = Math.Sign(direction.X);
                    direction.Y = Math.Sign(direction.Y); 

                    for (int i = 1; i <= steps; i++) 
                    { 
                        Point k = new Point(a1.X + i * margin.Width * direction.X, a1.Y + i * margin.Height * direction.Y);
                        if (k == a2) 
                            break;

                        ConnectorSegment b = ConnectorSegment.ConstructBoundSegment(coverSet, k, (a.Orientation == Orientation.Horizontal) ? Orientation.Vertical : Orientation.Horizontal);
                        //now try to intersect that segment with every segment after the current one and the one after it 
                        int intersectingSegment = currentSegment + 2;
                        while (intersectingSegment < refinedPath.Count - 1 && !intersected) 
                        { 
                            Point c1 = refinedPath[intersectingSegment];
                            Point c2 = refinedPath[intersectingSegment + 1]; 
                            ConnectorSegment c = new ConnectorSegment(c1, c2);

                            Nullable intersection = b.Intersect(c);
                            if (intersection != null && c.IsPointOnSegment((Point)intersection)) 
                            {
                                intersected = true; 
 
                                newPath.Clear();
                                //need to remove all points between a1 and b2 and insert k and the point of intersection there 
                                for (int j = 0; j <= currentSegment; j++)
                                    newPath.Add(refinedPath[j]);
                                newPath.Add(k);
                                newPath.Add((Point)intersection); 
                                for (int j = intersectingSegment + 1; j < refinedPath.Count; j++)
                                    newPath.Add(refinedPath[j]); 
 
                                List temp = refinedPath;
                                refinedPath = newPath; 
                                newPath = temp;
                                newPath.Clear();
                                break;
                            } 

                            intersectingSegment++; 
                        } 

                        //if there was an intersection, exit the for loop 
                        if (intersected)
                            break;
                    }
                } 

                //if there was an intersection, run the same segment again to see if there are other intersecting segments 
                if (!intersected) 
                    currentSegment++;
            } 
            #endregion
        }
        #endregion
 
        #region Struct DistanceFromPoint
        //sorting of extremities by closedness to point Z 
        private struct DistanceFromPoint 
        {
            public ConnectorSegment ConnectorSegment; 
            public Point P;
            public double Distance;

            public DistanceFromPoint(ConnectorSegment segment, Point z, Point p) 
            {
                this.ConnectorSegment = segment; 
                this.P = p; 
                this.Distance = ConnectorSegment.DistanceBetweenPoints(z, p);
            } 
        }
        #endregion

        #region Class DistanceSorter 
        private sealed class DistanceSorter : IComparer
        { 
            private DistanceSorter() { } 

            public static void Sort(ref List distances) 
            {
                DistanceSorter sorter = new DistanceSorter();
                distances.Sort(sorter);
            } 

            int IComparer.Compare(DistanceFromPoint lhs, DistanceFromPoint rhs) 
            { 
                if (lhs.Distance == rhs.Distance)
                    return 0; 
                else if (lhs.Distance > rhs.Distance)
                    return 1;
                else return -1;
            } 
        }
        #endregion 
 
        #region Class ConnectorSegment
        // represents a segment - the main entity in the routing algorithm 
 		[DebuggerDisplay("Segment ( {A.X}, {A.Y} ) - ( {B.X},{B.Y} ), {Orientation}")]
		private sealed class ConnectorSegment
        {
            private Point point1; 
            private Point point2;
 			private Orientation orientation; 
 
            public ConnectorSegment(Point point1, Point point2)
            { 
                if (point1.X != point2.X && point1.Y != point2.Y)
                    throw new InvalidOperationException(SR.GetString(SR.Error_InvalidConnectorSegment));

                this.point1 = point1; 
                this.point2 = point2;
 				this.orientation = ((this.point1.X == this.point2.X) ? Orientation.Vertical : Orientation.Horizontal); 
            } 

            public Point A 
            {
                get
                {
                    return this.point1; 
                }
            } 
 
            public Point B
            { 
                get
                {
                    return this.point2;
                } 
            }
 
            public Orientation Orientation 
            {
                get 
                {
                    return this.orientation;
                }
            } 

            //"segment l covers a point p, if the perpendicular from p to the line of which l is a segment intersects l" 
            //since a) we only have horizotal or vertical segments and b) some segments will be unbound, we have to store and use orientation 
            //flag to do analyzis
            public bool Covers(Point p) 
            {
				return (this.orientation == Orientation.Horizontal) ? (p.X >= Math.Min(this.point1.X, this.point2.X) && p.X <= Math.Max(this.point1.X, this.point2.X)) : (p.Y >= Math.Min(this.point1.Y, this.point2.Y) && p.Y <= Math.Max(this.point1.Y, this.point2.Y));
            }
 
            //see if the two segments intersect
            //greatly simplified by the fact that we only have vertical or horizontal segments 
            //should work fine with {Max, Min}Value values 
            public Nullable Intersect(ConnectorSegment segment)
            { 
 				if (this.orientation == segment.Orientation)
                    return null;

				ConnectorSegment vertical = (this.orientation == Orientation.Vertical) ? this : segment; 
				ConnectorSegment horizontal = (this.orientation == Orientation.Vertical) ? segment : this;
 
                if (vertical.A.X < Math.Min(horizontal.A.X, horizontal.B.X) || vertical.A.X > Math.Max(horizontal.A.X, horizontal.B.X)) 
                    return null;
 
                if (horizontal.A.Y < Math.Min(vertical.A.Y, vertical.B.Y) || horizontal.A.Y > Math.Max(vertical.A.Y, vertical.B.Y))
                    return null;

                return new Point(vertical.A.X, horizontal.A.Y); 
            }
 
            //we consider the whole line to which this segment belongs for this test 
            public bool PointLiesOnThisLine(Point p)
            { 
				return (this.orientation == Orientation.Horizontal) ? p.Y == this.point1.Y : p.X == this.point1.X;
            }

            //we consider just the segment for this test 
            public bool IsPointOnSegment(Point p)
            { 
 				if ((this.orientation == Orientation.Horizontal && p.Y != this.point1.Y) || (this.orientation == Orientation.Vertical && p.X != this.point1.X)) 
                    return false;
 
				int k = (this.orientation == Orientation.Horizontal) ? p.X : p.Y;
 				int k1 = (this.orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
 				int k2 = (this.orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y;
                return k >= Math.Min(k1, k2) && k <= Math.Max(k1, k2); 
            }
 
            //does the given segment overlap us? 
            //public bool Overlaps(ConnectorSegment segment)
            //{ 
            //    if (Orientation != segment.Orientation)
            //        return false;

            //    int k0 = (this.Orientation == Orientation.Horizontal) ? this.point1.Y : this.point1.X; 
            //    int k1 = (this.Orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
            //    int k2 = (this.Orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y; 
 
            //    int l0 = (this.Orientation == Orientation.Horizontal) ? segment.A.Y : segment.A.X;
            //    int l1 = (this.Orientation == Orientation.Horizontal) ? segment.A.X : segment.A.Y; 
            //    int l2 = (this.Orientation == Orientation.Horizontal) ? segment.B.X : segment.B.Y;

            //    if (k0 != l0)
            //        return false; 

            //    int min = Math.Min(k1, k2); 
            //    k2 = Math.Max(k1, k2); 
            //    k1 = min;
 
            //    min = Math.Min(l1, l2);
            //    l2 = Math.Max(l1, l2);
            //    l1 = min;
 
            //    if (l1 >= k2 || k1 >= l2)
            //        return false; 
 
            //    return true;
            //} 

            public ConnectorSegment PeprendecularThroughPoint(Point p)
            {
				Orientation newOrientation = (this.orientation == Orientation.Horizontal) ? Orientation.Vertical : Orientation.Horizontal; 
                Point newPoint = new Point(p.X, p.Y);
                if (newOrientation == Orientation.Horizontal) 
                    newPoint.X = int.MaxValue; 
                else
                    newPoint.Y = int.MaxValue; 

                return new ConnectorSegment(p, newPoint);
            }
 
            public Point ExtendPointOutwards(Point p)
            { 
                Debug.Assert(!(p != this.point1 && p != this.point2), "wrong extension requested"); 
                if (p != this.point1 && p != this.point2)
                    return p; 

 				int k = (this.orientation == Orientation.Horizontal) ? p.X : p.Y;
				int k1 = (this.orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
				int k2 = (this.orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y; 

                if (k == Math.Min(k1, k2)) 
                    k--; 
                else
                    k++; 

				return new Point((this.orientation == Orientation.Horizontal) ? k : p.X, (this.orientation == Orientation.Horizontal) ? p.Y : k);
            }
 
            public static double DistanceBetweenPoints(Point p, Point q)
            { 
                return Math.Sqrt((double)(p.X - q.X) * (p.X - q.X) + (double)(p.Y - q.Y) * (p.Y - q.Y)); 
            }
 
            //given two points construct a segment through them from lesser cover to higher
            public static ConnectorSegment ConstructBoundSegment(CoverSet coverSet, Point a, Point b)
            {
                // 

                if (a.X != b.X && a.Y != b.Y) 
                    return null; 

                return ConstructBoundSegment(coverSet, a, (a.X == b.X) ? Orientation.Vertical : Orientation.Horizontal); 
            }

            public static ConnectorSegment SegmentFromLeftToRightCover(CoverSet coverSet, Point p)
            { 
                ConnectorSegment leftCover = coverSet.GetCover(p, DesignerEdges.Left);
                ConnectorSegment rightCover = coverSet.GetCover(p, DesignerEdges.Right); 
 
                //construct horizontal escape segment
                Point left = new Point((leftCover != null) ? leftCover.A.X : int.MinValue, p.Y); 
                Point right = new Point((rightCover != null) ? rightCover.A.X : int.MaxValue, p.Y);
                ConnectorSegment h = new ConnectorSegment(left, right);
                return h;
            } 

            public static ConnectorSegment SegmentFromBottomToTopCover(CoverSet coverSet, Point p) 
            { 
                ConnectorSegment bottomCover = coverSet.GetCover(p, DesignerEdges.Bottom);
                ConnectorSegment topCover = coverSet.GetCover(p, DesignerEdges.Top); 

                //construct vertical escape segment
                Point bottom = new Point(p.X, (bottomCover != null) ? bottomCover.A.Y : int.MinValue);
                Point top = new Point(p.X, (topCover != null) ? topCover.A.Y : int.MaxValue); 
                ConnectorSegment v = new ConnectorSegment(bottom, top);
                return v; 
            } 

            public static ConnectorSegment ConstructBoundSegment(CoverSet coverSet, Point a, Orientation orientation) 
            {
                return (orientation == Orientation.Horizontal) ? SegmentFromLeftToRightCover(coverSet, a) : SegmentFromBottomToTopCover(coverSet, a);
            }
 
            public override bool Equals(object obj)
            { 
                ConnectorSegment segment = obj as ConnectorSegment; 
                if (segment == null)
                    return false; 
                return (this.point1 == segment.A && this.point2 == segment.B && Orientation == segment.Orientation);
            }

            public override int GetHashCode() 
            {
                return this.point1.GetHashCode() ^ this.point2.GetHashCode() ^ Orientation.GetHashCode(); 
            } 
        }
        #endregion 

        #region Class CoverSet (Set of vertical and horizontal covers)
        private sealed class CoverSet
        { 
            private List verticalCovers = new List();
            private List horizontalCovers = new List(); 
            private List usedEscapeLine = new List(); 

            public CoverSet(Rectangle[] rectanglesToExclude, Point[] linesToExclude) 
            {
                foreach (Rectangle rectangle in rectanglesToExclude)
                {
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Top), new Point(rectangle.Left, rectangle.Bottom))); 
                    AddCover(new ConnectorSegment(new Point(rectangle.Right, rectangle.Top), new Point(rectangle.Right, rectangle.Bottom)));
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Top), new Point(rectangle.Right, rectangle.Top))); 
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Bottom), new Point(rectangle.Right, rectangle.Bottom))); 
                }
 
                //Add the linesegments to cover
                for (int i = 0; i < linesToExclude.Length / 2; i++)
                    AddCover(new ConnectorSegment(linesToExclude[i * 2], linesToExclude[(i * 2) + 1]));
            } 

            public void ClearUsedLines() 
            { 
                this.usedEscapeLine.Clear();
            } 

            public void AddCover(ConnectorSegment cover)
            {
                List covers = (cover.Orientation == Orientation.Vertical) ? this.verticalCovers : this.horizontalCovers; 

                for (int i = 0; i < covers.Count; i++) 
                { 
                    ConnectorSegment existingCover = covers[i];
                    if (cover.IsPointOnSegment(existingCover.A) && cover.IsPointOnSegment(existingCover.B)) 
                    {
                        //both points of vertical are on the new segment, delete the vertical and add the new one instead
                        covers.RemoveAt(i);
                        break; 
                    }
                    else if (existingCover.IsPointOnSegment(cover.A) && existingCover.IsPointOnSegment(cover.B)) 
                    { 
                        //both points of the new segment are on an existing segment already - skip this one
                        return; 
                    }
                }

                covers.Add(cover); 
            }
 
            //public ReadOnlyCollection VerticalCovers 
            //{
            //    get 
            //    {
            //        return this.verticalCovers.AsReadOnly();
            //    }
            //} 

            //public ReadOnlyCollection HorizontalCovers 
            //{ 
            //    get
            //    { 
            //        return this.horizontalCovers.AsReadOnly();
            //    }
            //}
 
            public void AddUsedEscapeLine(ConnectorSegment segment)
            { 
                this.usedEscapeLine.Add(segment); 
            }
 
            //the new escape point should not lay on any of the already used escape segments
            public bool EscapeLineHasBeenUsed(ConnectorSegment segment, Point escapePoint)
            {
 				for (int i = 0; i < this.usedEscapeLine.Count; i++) 
				{
 					ConnectorSegment usedSegment = this.usedEscapeLine[i]; 
                    if (usedSegment.IsPointOnSegment(escapePoint)) 
                        return true;
 				} 
                return false;
            }

            //get just the vertical covers for the given point out of all stored segments 
            //public List GetVerticalCovers(Point p)
            //{ 
            //    List covers = new List(); 
            //    foreach (ConnectorSegment segment in this.verticalCovers)
            //    { 
            //        if (segment.Covers(p))
            //            covers.Add(segment);
            //    }
            //    return covers; 
            //}
 
            //get just the horizontal covers for the given point out of all stored segments 
            //public List GetHorizontalCovers(Point p)
            //{ 
            //    List covers = new List();
            //    foreach (ConnectorSegment segment in this.horizontalCovers)
            //    {
            //        if (segment.Covers(p)) 
            //            covers.Add(segment);
            //    } 
            //    return covers; 
            //}
 
            //get the cover on the given side (closest cover to the given side) for the point out of all stored segments
            public ConnectorSegment GetCover(Point p, DesignerEdges side)
            {
                ConnectorSegment cover = null; 
                int distance = 0;
 
                if (side == DesignerEdges.Left || side == DesignerEdges.Right) 
                {
					for (int i = 0; i < this.verticalCovers.Count; i++) 
 					{
						ConnectorSegment segment = this.verticalCovers[i];
                        int currentDistance = (side == DesignerEdges.Left) ? p.X - segment.A.X : segment.A.X - p.X;
                        if (currentDistance > 0 && segment.Covers(p)) 
                        {
                            if (cover == null || distance > currentDistance) 
                            { 
                                cover = segment;
                                distance = currentDistance; 
                            }
                        }
                    }
                } 
                else
                { 
					for (int i = 0; i < this.horizontalCovers.Count; i++) 
					{
 						ConnectorSegment segment = this.horizontalCovers[i]; 
                        int currentDistance = (side == DesignerEdges.Bottom) ? p.Y - segment.A.Y : segment.A.Y - p.Y;
                        if (currentDistance > 0 && segment.Covers(p))
                        {
                            if (cover == null || distance > currentDistance) 
                            {
                                cover = segment; 
                                distance = currentDistance; 
                            }
                        } 
                    }
                }

                return cover; 
            }
 
            //get the cover on the given side (closest cover to the given side) for the point out of all stored segments 
            public List GetCovers(Point p, DesignerEdges side)
            { 
                List covers = new List();

                if (side == DesignerEdges.Left || side == DesignerEdges.Right)
                { 
					for (int i = 0; i < this.verticalCovers.Count; i++)
 					{ 
 						ConnectorSegment segment = this.verticalCovers[i]; 
                        int currentDistance = (side == DesignerEdges.Left) ? p.X - segment.A.X : segment.A.X - p.X;
                        if (currentDistance > 0 && segment.Covers(p)) 
                            covers.Add(segment);
                    }
                }
                else 
                {
					for (int i = 0; i < this.horizontalCovers.Count; i++) 
 					{ 
						ConnectorSegment segment = this.horizontalCovers[i];
                        int currentDistance = (side == DesignerEdges.Bottom) ? p.Y - segment.A.Y : segment.A.Y - p.Y; 
                        if (currentDistance > 0 && segment.Covers(p))
                            covers.Add(segment);
                    }
                } 

                return covers; 
            } 

            public bool IsEscapePoint(Point origin, Point escape, DesignerEdges side) 
            {
                //get the original cover
                ConnectorSegment originalCover = this.GetCover(origin, side);
                int originalDistance; 
                if (side == DesignerEdges.Left || side == DesignerEdges.Right)
                    originalDistance = originalCover.A.X - escape.X; 
                else 
                    originalDistance = originalCover.A.Y - escape.Y;
 
                // the escape point should not be covered by the the original cover
                if (originalCover.Covers(escape))
                    return false;
 
                //it should not also be covered by any member of other segments between the original cover and the original point
                List newCovers = this.GetCovers(escape, side); 
				for (int i = 0; i < newCovers.Count; i++) 
				{
 					ConnectorSegment newCover = newCovers[i]; 
                    //should never happen, just in case...
                    if (newCover == originalCover)
                        return false;
 
                    int newDistance;
                    if (side == DesignerEdges.Left || side == DesignerEdges.Right) 
                        newDistance = Math.Abs(newCover.A.X - escape.X); 
                    else
                        newDistance = Math.Abs(newCover.A.Y - escape.Y); 

                    if (Math.Sign(newDistance) == Math.Sign(originalDistance) && Math.Abs(newDistance) < Math.Abs(originalDistance))
                        return false;
                } 

                return true; 
            } 
        }
        #endregion 
    }
    #endregion
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
using System; 
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections; 
using System.Collections.ObjectModel;
using System.Collections.Generic; 
using System.ComponentModel; 
using System.ComponentModel.Design;
using System.Windows.Forms; 
using System.Diagnostics;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel.Serialization;
 
namespace System.Workflow.ComponentModel.Design
{ 
    #region Class ConnectionPointHitTestInfo 
    internal sealed class ConnectionPointHitTestInfo : HitTestInfo
    { 
        private ConnectionPoint connectionPoint;

        internal ConnectionPointHitTestInfo(ConnectionPoint connectionPoint)
            : base(connectionPoint.AssociatedDesigner, HitTestLocations.Designer | HitTestLocations.Connector) 
        {
            this.connectionPoint = connectionPoint; 
        } 

        internal ConnectionPoint ConnectionPoint 
        {
            get
            {
                return this.connectionPoint; 
            }
        } 
    } 
    #endregion
 
    #region ConnectorEvent
    public delegate void ConnectorEventHandler(object sender, ConnectorEventArgs e);

    #region Class ConnectorEventArgs 
    public class ConnectorEventArgs : EventArgs
    { 
        private Connector connector; 

        internal ConnectorEventArgs(Connector connector) 
        {
            this.connector = connector;
        }
 
        public Connector Connector
        { 
            get 
            {
                return this.connector; 
            }
        }
    }
    #endregion 
    #endregion
 
    #region Class ConnectionPoint 
    public class ConnectionPoint
    { 
        private ActivityDesigner associatedDesigner;
        private DesignerEdges designerEdge;
        private int connectionIndex;
 
        public ConnectionPoint(ActivityDesigner associatedDesigner, DesignerEdges designerEdge, int connectionIndex)
        { 
            if (associatedDesigner == null) 
                throw new ArgumentNullException("associatedDesigner");
 
            if (connectionIndex < 0 || connectionIndex >= associatedDesigner.GetConnections(designerEdge).Count)
                throw new ArgumentException(DR.GetString(DR.Error_ConnectionPoint), "connectionIndex");

            this.associatedDesigner = associatedDesigner; 
            this.designerEdge = designerEdge;
            this.connectionIndex = connectionIndex; 
        } 

        public ActivityDesigner AssociatedDesigner 
        {
            get
            {
                return this.associatedDesigner; 
            }
        } 
 
        public int ConnectionIndex
        { 
            get
            {
                return this.connectionIndex;
            } 
        }
 
        public DesignerEdges ConnectionEdge 
        {
            get 
            {
                DesignerEdges designerEdge = this.designerEdge;

                if (designerEdge != DesignerEdges.Left && designerEdge != DesignerEdges.Right && 
                    designerEdge != DesignerEdges.Top && designerEdge != DesignerEdges.Bottom)
                { 
                    designerEdge = DesignerGeometryHelper.ClosestEdgeToPoint(Location, this.associatedDesigner.Bounds, designerEdge); 
                }
 
                return designerEdge;
            }
        }
 
        public virtual Point Location
        { 
            get 
            {
                IList connections = this.associatedDesigner.GetConnections(this.designerEdge); 
                if (this.connectionIndex < connections.Count)
                    return connections[this.connectionIndex];
                else
                    return Point.Empty; 
            }
        } 
 
        public virtual Rectangle Bounds
        { 
            get
            {
                IList connections = this.associatedDesigner.GetConnections(DesignerEdges.All);
                if (connections.Count > 0) 
                {
                    Point location = Location; 
                    Size size = DefaultSize; 
                    Rectangle enclosingBounds = new Rectangle(new Point(location.X - size.Width / 2, location.Y - size.Height / 2), size);
                    return enclosingBounds; 
                }
                else
                {
                    return Rectangle.Empty; 
                }
            } 
        } 

        public override bool Equals(object obj) 
        {
            ConnectionPoint connectionPoint = obj as ConnectionPoint;
            if (connectionPoint == null)
                return false; 

            if (connectionPoint.AssociatedDesigner == this.associatedDesigner && 
                connectionPoint.designerEdge == this.designerEdge && 
                connectionPoint.ConnectionIndex == this.connectionIndex)
                return true; 
            else
                return false;
        }
 
        public override int GetHashCode()
        { 
            return (this.associatedDesigner.GetHashCode() ^ this.designerEdge.GetHashCode() ^ this.connectionIndex.GetHashCode()); 
        }
 
        public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawHighlighted)
        {
            Draw(e, Bounds);
        } 

        internal static void Draw(ActivityDesignerPaintEventArgs e, Rectangle bounds) 
        { 
            bounds.Inflate(-1, -1);
            e.Graphics.FillEllipse(Brushes.White, bounds); 
            e.Graphics.DrawEllipse(e.AmbientTheme.SelectionForegroundPen, bounds);

            bounds.Inflate(-1, -1);
            e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds); 
        }
 
        private Size DefaultSize 
        {
            get 
            {
                Size defaultSize = WorkflowTheme.CurrentTheme.AmbientTheme.Margin;
                defaultSize.Width += defaultSize.Width / 2;
                defaultSize.Height += defaultSize.Height / 2; 
                if (this.associatedDesigner != null)
                    defaultSize = new Size(Math.Max(defaultSize.Width, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4), Math.Max(defaultSize.Height, (int)this.associatedDesigner.DesignerTheme.ForegroundPen.Width * 4)); 
                return defaultSize; 
            }
        } 
    }
    #endregion

    #region Class Connector 
    [DesignerSerializer(typeof(ConnectorLayoutSerializer), typeof(WorkflowMarkupSerializer))]
    public class Connector : IDisposable 
    { 
        #region Members, Construction, Dispose
        private AccessibleObject accessibilityObject; 
        private FreeformActivityDesigner parentDesigner;
        private ConnectionPoint source = null;
        private ConnectionPoint target = null;
        private List segments = new List(); 
        private bool connectorModified = false;
 
        public Connector(ConnectionPoint source, ConnectionPoint target) 
        {
            if (source == null) 
                throw new ArgumentNullException("source");

            if (target == null)
                throw new ArgumentNullException("target"); 

            if (ConnectionManager.GetConnectorContainer(source.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(target.AssociatedDesigner)) 
                throw new ArgumentException(DR.GetString(DR.Error_Connector1)); 

            this.source = source; 
            this.target = target;
        }

        void IDisposable.Dispose() 
        {
        } 
        #endregion 

        #region Properties and Methods 

        public virtual AccessibleObject AccessibilityObject
        {
            get 
            {
                if (this.accessibilityObject == null) 
                    this.accessibilityObject = new ConnectorAccessibleObject(this); 
                return this.accessibilityObject;
            } 
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ConnectionPoint Source 
        {
            get 
            { 
                return this.source;
            } 

            set
            {
                if (value == null) 
                    throw new ArgumentNullException("value");
 
                if (this.source.Equals(value)) 
                    return;
 
                if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.target.AssociatedDesigner))
                    throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");

                this.source = value; 
                PerformLayout();
            } 
        } 

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 
        public ConnectionPoint Target
        {
            get
            { 
                return this.target;
            } 
 
            set
            { 
                if (value == null)
                    throw new ArgumentNullException("value");

                if (this.target.Equals(value)) 
                    return;
 
                if (ConnectionManager.GetConnectorContainer(value.AssociatedDesigner) != ConnectionManager.GetConnectorContainer(this.source.AssociatedDesigner)) 
                    throw new ArgumentException(SR.GetString(SR.Error_InvalidConnectorSource), "value");
 
                this.target = value;
                PerformLayout();
            }
        } 

        public virtual ReadOnlyCollection ConnectorSegments 
        { 
            get
            { 
                List connectorSegments = new List();

                if (this.source != null && this.target != null)
                { 
                    if (this.segments.Count == 0 || this.segments[0] != this.source.Location)
                        connectorSegments.Add(this.source.Location); 
 
                    connectorSegments.AddRange(this.segments);
 
                    if (this.segments.Count == 0 || this.segments[this.segments.Count - 1] != this.target.Location)
                        connectorSegments.Add(this.target.Location);
                }
 
                return connectorSegments.AsReadOnly();
            } 
        } 

        public Rectangle Bounds 
        {
            get
            {
                Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(new List(ConnectorSegments).ToArray()); 
                bounds.Inflate(1, 1);
                return bounds; 
            } 
        }
 
        public FreeformActivityDesigner ParentDesigner
        {
            get
            { 
                return this.parentDesigner;
            } 
        } 

        public virtual bool HitTest(Point point) 
        {
            Size selectionSize = WorkflowTheme.CurrentTheme.AmbientTheme.SelectionSize;

            //We go thru the line seagments and hittest 
            ReadOnlyCollection segments = ConnectorSegments;
            for (int i = 1; i < segments.Count; i++) 
            { 
                if (DesignerGeometryHelper.PointOnLineSegment(point, new Point[] { segments[i - 1], segments[i] }, selectionSize))
                    return true; 
            }

            return false;
        } 

        public virtual void Offset(Size size) 
        { 
            for (int i = 0; i < this.segments.Count; i++)
                this.segments[i] = new Point(this.segments[i].X + size.Width, this.segments[i].Y + size.Height); 
        }

        public override bool Equals(object obj)
        { 
            Connector connector = obj as Connector;
            if (connector == null) 
                return false; 

            return (connector.Source == this.source && connector.target == this.target); 
        }

        public override int GetHashCode()
        { 
            if (this.source != null && this.target != null)
                return this.source.GetHashCode() ^ this.target.GetHashCode(); 
            else 
                return base.GetHashCode();
        } 

        public void Invalidate()
        {
            WorkflowView workflowView = ParentView; 
            if (workflowView != null)
                workflowView.InvalidateLogicalRectangle(Bounds); 
        } 

        public bool ConnectorModified 
        {
            get
            {
                return this.connectorModified; 
            }
        } 
 
        protected WorkflowView ParentView
        { 
            get
            {
                return GetService(typeof(WorkflowView)) as WorkflowView;
            } 
        }
 
        protected void PerformLayout() 
        {
            WorkflowView workflowView = ParentView; 
            if (workflowView != null)
                workflowView.PerformLayout(false);
        }
 
        protected internal virtual ICollection ExcludedRoutingRectangles
        { 
            get 
            {
                return new Rectangle[] { }; 
            }
        }

        //We want to allow framework or the derived classes only to set the connector segments 
        //We do not want any outside entity to set the connector segments randomly
        protected internal void SetConnectorSegments(ICollection segments) 
        { 
            //Called by derived classes when they want to change connector routing
            // 
            if (segments == null)
                throw new ArgumentNullException("segments");

            this.connectorModified = (this.parentDesigner != null && segments.Count > 0); 
            if (this.connectorModified)
                Invalidate(); 
 
            this.segments.Clear();
            this.segments.AddRange(segments); 

            if (this.connectorModified)
                Invalidate();
        } 

        protected internal virtual void OnLayout(ActivityDesignerLayoutEventArgs e) 
        { 
            if (this.segments.Count > 0 &&
                (this.segments[0] != Source.Location || this.segments[this.segments.Count - 1] != Target.Location)) 
                this.connectorModified = false;

            if (!connectorModified && ParentDesigner != null)
            { 
                Point[] newSegments = ActivityDesignerConnectorRouter.Route(Source.AssociatedDesigner.Activity.Site, Source, Target, ExcludedRoutingRectangles);
                this.segments.Clear(); 
                this.segments.AddRange(newSegments); 
            }
        } 

        protected internal virtual void OnPaint(ActivityDesignerPaintEventArgs e)
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme != null)
            { 
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5); 
                Size maxCapSize = theme.ConnectorSize;
                ActivityDesignerPaint.DrawConnectors(e.Graphics, e.DesignerTheme.ForegroundPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap); 
            }
        }

        protected internal virtual void OnPaintSelected(ActivityDesignerPaintEventArgs e, bool primarySelection, Point[] segmentEditPoints) 
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme == null) 
                return;
 
            using (Pen lineSelectionPen = new Pen(WorkflowTheme.CurrentTheme.AmbientTheme.SelectionForeColor, 1))
            {
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5);
                Size maxCapSize = theme.ConnectorSize; 
                ActivityDesignerPaint.DrawConnectors(e.Graphics, lineSelectionPen, new List(ConnectorSegments).ToArray(), arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap);
            } 
 
            if (this.source != null)
                this.source.OnPaint(e, false); 

            ReadOnlyCollection endSegmentEditPoints = ConnectorSegments;
            for (int i = 1; i < endSegmentEditPoints.Count - 1; i++)
                PaintEditPoints(e, endSegmentEditPoints[i], false); 

            for (int i = 0; i < segmentEditPoints.Length; i++) 
                PaintEditPoints(e, segmentEditPoints[i], true); 

            if (this.target != null) 
                this.target.OnPaint(e, false);
        }

        protected internal virtual void OnPaintEdited(ActivityDesignerPaintEventArgs e, Point[] segments, Point[] segmentEditPoints) 
        {
            CompositeDesignerTheme theme = e.DesignerTheme as CompositeDesignerTheme; 
            if (theme == null) 
                return;
 
            using (Pen editableConnectorPen = new Pen(e.AmbientTheme.SelectionForegroundPen.Color, e.AmbientTheme.SelectionForegroundPen.Width))
            {
                editableConnectorPen.DashStyle = DashStyle.Dash;
                Size arrowCapSize = new Size(theme.ConnectorSize.Width / 5, theme.ConnectorSize.Height / 5); 
                Size maxCapSize = theme.ConnectorSize;
                ActivityDesignerPaint.DrawConnectors(e.Graphics, editableConnectorPen, segments, arrowCapSize, maxCapSize, theme.ConnectorStartCap, theme.ConnectorEndCap); 
            } 

            if (this.source != null) 
                this.source.OnPaint(e, false);

            for (int i = 1; i < segments.Length - 1; i++)
                PaintEditPoints(e, segments[i], false); 

            for (int i = 0; i < segmentEditPoints.Length; i++) 
                PaintEditPoints(e, segmentEditPoints[i], true); 

            if (this.target != null) 
                this.target.OnPaint(e, false);
        }

        protected virtual object GetService(Type serviceType) 
        {
            object service = null; 
            if (this.parentDesigner != null && this.parentDesigner.Activity != null && this.parentDesigner.Activity.Site != null) 
                service = this.parentDesigner.Activity.Site.GetService(serviceType);
            return service; 
        }
        #endregion

        #region Helpers 
        private void PaintEditPoints(ActivityDesignerPaintEventArgs e, Point point, bool drawMidSegmentEditPoint)
        { 
            Size size = (this.source != null) ? this.source.Bounds.Size : Size.Empty; 
            if (!size.IsEmpty)
            { 
                Rectangle bounds = new Rectangle(point.X - size.Width / 2, point.Y - size.Height / 2, size.Width, size.Height);
                if (drawMidSegmentEditPoint)
                {
                    using (GraphicsPath path = new GraphicsPath()) 
                    {
                        path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Top), new Point(bounds.Right, bounds.Top + bounds.Height / 2)); 
                        path.AddLine(new Point(bounds.Right, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Bottom)); 
                        path.AddLine(new Point(bounds.Left + bounds.Width / 2, bounds.Bottom), new Point(bounds.Left, bounds.Top + bounds.Height / 2));
                        path.AddLine(new Point(bounds.Left, bounds.Top + bounds.Height / 2), new Point(bounds.Left + bounds.Width / 2, bounds.Top)); 

                        e.Graphics.FillPath(Brushes.White, path);
                        e.Graphics.DrawPath(e.AmbientTheme.SelectionForegroundPen, path);
                    } 
                }
                else 
                { 
                    bounds.Inflate(-1, -1);
                    e.Graphics.FillEllipse(e.AmbientTheme.SelectionForegroundBrush, bounds); 
                }
            }
        }
 
        internal void SetConnectorModified(bool modified)
        { 
            this.connectorModified = modified; 
        }
 
        internal FreeformActivityDesigner RenderingOwner
        {
            get
            { 
                if (this.source == null || this.target == null)
                    return null; 
 
                //
 



                List targetParents = new List(); 
                ActivityDesigner designer = this.target.AssociatedDesigner;
                while (designer != null) 
                { 
                    FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
                    if (parentFreeFormDesigner != null) 
                        targetParents.Add(parentFreeFormDesigner);
                    designer = designer.ParentDesigner;
                }
 
                //Go through the parent freeforms of source and match it with target, the first common
                //parent freeform is the rendering designer 
                designer = this.source.AssociatedDesigner; 
                while (designer != null)
                { 
                    FreeformActivityDesigner parentFreeFormDesigner = designer as FreeformActivityDesigner;
                    if (parentFreeFormDesigner != null && targetParents.Contains(parentFreeFormDesigner))
                        break;
                    designer = designer.ParentDesigner; 
                }
 
                return designer as FreeformActivityDesigner; 
            }
        } 

        internal void SetParent(FreeformActivityDesigner parentDesigner)
        {
            //Make sure that this is in parent chain of both source and the target connection points 
            WorkflowView workflowView = ParentView;
            if (this.parentDesigner != null && workflowView != null) 
                workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds); 

            this.parentDesigner = parentDesigner; 

            if (this.parentDesigner != null && workflowView != null)
                workflowView.InvalidateLogicalRectangle(this.parentDesigner.Bounds);
        } 

        internal static Connector GetConnectorFromSelectedObject(object selectedObject) 
        { 
            Connector connector = null;
            ConnectorHitTestInfo connectorHitTestInfo = selectedObject as ConnectorHitTestInfo; 
            if (connectorHitTestInfo != null)
            {
                FreeformActivityDesigner connectorContainer = connectorHitTestInfo.AssociatedDesigner as FreeformActivityDesigner;
                int index = connectorHitTestInfo.MapToIndex(); 
                if (connectorContainer != null && index >= 0 && index < connectorContainer.Connectors.Count)
                    connector = connectorContainer.Connectors[index]; 
            } 

            return connector; 
        }

        #region Properties used during serialization only
        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal string SourceActivity 
        {
            get 
            {
                string activityName = String.Empty;
                if (this.source != null)
                    activityName = this.source.AssociatedDesigner.Activity.QualifiedName; 
                return activityName;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal int SourceConnectionIndex 
        {
            get 
            {
                int connectionIndex = 0;
                if (this.source != null)
                    connectionIndex = this.source.ConnectionIndex; 
                return connectionIndex;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal DesignerEdges SourceConnectionEdge 
        {
            get 
            {
                DesignerEdges connectionEdge = DesignerEdges.None;
                if (this.source != null)
                    connectionEdge = this.source.ConnectionEdge; 
                return connectionEdge;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal string TargetActivity 
        {
            get 
            {
                string activityName = String.Empty;
                if (this.target != null)
                    activityName = this.target.AssociatedDesigner.Activity.QualifiedName; 
                return activityName;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal int TargetConnectionIndex 
        {
            get 
            {
                int connectionIndex = 0;
                if (this.target != null)
                    connectionIndex = this.target.ConnectionIndex; 
                return connectionIndex;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal DesignerEdges TargetConnectionEdge 
        {
            get 
            {
                DesignerEdges connectionEdge = DesignerEdges.None;
                if (this.target != null)
                    connectionEdge = this.target.ConnectionEdge; 
                return connectionEdge;
            } 
 
            set
            { 
            }
        }

        //NOTE THAT THIS WILL ONLY BE USED FOR SERIALIZATION PURPOSES 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
        internal List Segments 
        {
            get 
            {
                return this.segments;
            }
        } 
        #endregion
 
        #endregion 
    }
    #endregion 

    #region Class ConnectorAccessibleObject

    public class ConnectorAccessibleObject : AccessibleObject 
    {
        private Connector connector; 
 
        public ConnectorAccessibleObject(Connector connector)
        { 
            if (connector == null)
                throw new ArgumentNullException("connector");
            this.connector = connector;
        } 

        public override Rectangle Bounds 
        { 
            get
            { 
                WorkflowView parentView = this.connector.ParentDesigner.ParentView;
                Rectangle bounds = this.connector.Bounds;
                return new Rectangle(parentView.LogicalPointToScreen(bounds.Location), parentView.LogicalSizeToClient(bounds.Size));
            } 
        }
 
        public override AccessibleObject HitTest(int x, int y) 
        {
            WorkflowView parentView = this.connector.ParentDesigner.ParentView; 
            if (this.connector.HitTest(parentView.ScreenPointToLogical(new Point(x, y))))
                return this;
            else
                return null; 
        }
 
        public override string Name 
        {
            get 
            {
                return this.connector.GetType().Name;
            }
            set 
            {
            } 
        } 

        public override AccessibleObject Parent 
        {
            get
            {
                return connector.ParentDesigner.AccessibilityObject; 
            }
        } 
 
        public override AccessibleRole Role
        { 
            get
            {
                return AccessibleRole.Diagram;
            } 
        }
    } 
 
    #endregion Class ConnectorAccessibleObject
 
    #region Class ConnectorEditor
    internal sealed class ConnectorEditor
    {
        private IServiceProvider serviceProvider; 
        private Connector editedConnector;
        private EditPoint activeEditPoint; 
        private List editPoints = new List(); 

        public ConnectorEditor(Connector connectorEdited) 
        {
            this.editedConnector = connectorEdited;
            this.serviceProvider = this.editedConnector.Source.AssociatedDesigner.Activity.Site;
            CreateEditPoints(); 
        }
 
        public ConnectionPoint EditedConectionPoint 
        {
            get 
            {
                if (this.activeEditPoint != null)
                    return this.activeEditPoint.EditedConnectionPoint;
                else 
                    return null;
            } 
        } 

        public Connector EditedConnector 
        {
            get
            {
                return this.editedConnector; 
            }
        } 
 
        public Cursor GetCursor(Point cursorPoint)
        { 
            Cursor cursor = Cursors.Default;
            if (this.activeEditPoint != null)
            {
                cursor = ConnectionManager.NewConnectorCursor; 
            }
            else 
            { 
                foreach (EditPoint editPoint in this.editPoints)
                { 
                    if (editPoint.Bounds.Contains(cursorPoint))
                    {
                        cursor = ConnectionManager.SnappedConnectionCursor;
                        break; 
                    }
                } 
            } 

            return cursor; 
        }

        public bool OnBeginEditing(Point point)
        { 
            //Get all the editable points
            CreateEditPoints(); 
 
            EditPoint editPointHit = null;
            for (int i = this.editPoints.Count - 1; i >= 0; i--) 
            {
                if (this.editPoints[i].Bounds.Contains(point))
                {
                    editPointHit = this.editPoints[i]; 
                    break;
                } 
            } 

            if (editPointHit != null && 
                (editPointHit.EditedConnectionPoint == null ||
                ConnectionManager.GetConnectorContainer(editPointHit.EditedConnectionPoint.AssociatedDesigner) != null))
            {
                editPointHit.Location = point; 
                this.activeEditPoint = editPointHit;
            } 
 
            Invalidate();
            return (this.activeEditPoint != null); 
        }

        public void OnContinueEditing(Point point)
        { 
            if (this.activeEditPoint == null)
                return; 
 
            Invalidate();
            UpdateEditPoints(point); 
            Invalidate();

#if DISPLAYESCAPEREGIONS
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView; 
            if (workflowView != null)
                workflowView.InvalidateClientRectangle(Rectangle.Empty); 
#endif 
        }
 
        public void OnEndEditing(Point point, bool commitChanges)
        {
            if (this.activeEditPoint == null)
                return; 

            Invalidate(); 
            if (commitChanges) 
            {
                //This is to update the edit points based on the activepoint 
                UpdateEditPoints(point);

                EditPoint activeEditPoint = this.activeEditPoint;
                this.activeEditPoint = null; 

                //This call is to optimize the segments 
                UpdateEditPoints(point); 

                //If we were creating a new connector or modifying the connection end points 
                bool updateSegments = false;
                if (activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                {
                    ConnectionManager connectionManager = GetService(typeof(ConnectionManager)) as ConnectionManager; 
                    FreeformActivityDesigner connectorContainer = ConnectionManager.GetConnectorContainer(activeEditPoint.EditedConnectionPoint.AssociatedDesigner);
                    if (connectionManager != null && connectionManager.SnappedConnectionPoint != null && connectorContainer != null) 
                    { 
                        //Get the original source and targets
                        ConnectionPoint source = this.editedConnector.Source; 
                        ConnectionPoint target = this.editedConnector.Target;

                        //Make sure that we set the source and target correctly
                        if (target.Equals(activeEditPoint.EditedConnectionPoint)) 
                            target = connectionManager.SnappedConnectionPoint;
                        else if (source.Equals(activeEditPoint.EditedConnectionPoint)) 
                            source = connectionManager.SnappedConnectionPoint; 

                        //Check if it is a valid connection ie source can be connected to target 
                        if (connectorContainer == ConnectionManager.GetConnectorContainer(target.AssociatedDesigner) &&
                            connectorContainer.CanConnectContainedDesigners(source, target))
                        {
                            this.editedConnector.Source = source; 
                            this.editedConnector.Target = target;
 
                            if (this.editedConnector.ParentDesigner == null) 
                            {
                                this.editedConnector = connectorContainer.AddConnector(source, target); 

                                WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
                                if (loader != null)
                                    loader.SetModified(true); 
                            }
 
                            connectorContainer.OnContainedDesignersConnected(source, target); 
                        }
 
                        updateSegments = true;
                    }
                }
                else 
                {
                    updateSegments = true; 
                } 

                //Make sure that we apply the edit points to the connector 
                if (updateSegments)
                {
                    this.editedConnector.SetConnectorSegments(GetPointsFromEditPoints(this.editPoints));
                    if (this.editedConnector.ParentDesigner != null) 
                    {
                        this.editedConnector.ParentDesigner.OnConnectorChanged(new ConnectorEventArgs(this.editedConnector)); 
 
                        WorkflowDesignerLoader loader = GetService(typeof(WorkflowDesignerLoader)) as WorkflowDesignerLoader;
                        if (loader != null) 
                            loader.SetModified(true);
                    }
                }
 
                PerformLayout();
            } 
 
            Invalidate();
        } 

        public bool HitTest(Point point)
        {
 			for (int i = 0; i < this.editPoints.Count; i++) 
			{
				EditPoint editPoint = this.editPoints[i]; 
                if (editPoint.Bounds.Contains(point)) 
                    return true;
            } 

            return false;
        }
 
        public void OnPaint(ActivityDesignerPaintEventArgs e, bool drawSelected, bool drawPrimarySelection)
        { 
            List segments = new List(); 
            List segmentEditPoints = new List();
			for (int i = 0; i < this.editPoints.Count; i++) 
 			{
				EditPoint editPoint = this.editPoints[i];
                if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
                    editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                    segments.Add(editPoint.Location);
                else 
                    segmentEditPoints.Add(editPoint.Location); 
            }
 
            //If the editing is in progress then we want to draw the dashed lines
            if (drawSelected)
                this.editedConnector.OnPaintSelected(e, drawPrimarySelection, segmentEditPoints.ToArray());
 
            if (this.activeEditPoint != null)
                this.editedConnector.OnPaintEdited(e, segments.ToArray(), segmentEditPoints.ToArray()); 
 
            //
#if DISPLAYESCAPEREGIONS 
            if (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
            {
                object source = null, target = null;
                if (this.activeEditPoint.EditedConnectionPoint.Equals(Target)) 
                {
                    target = this.activeEditPoint.Location; 
                    source = Source; 
                }
                else 
                {
                    source = this.activeEditPoint.Location;
                    target = Target;
                } 

                List rectanglesToExclude; 
                List linesToExclude, pointsToExclude; 
                ActivityDesignerConnectorRouter.GetRoutingObstacles(this.serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);
 
                ICollection userDefinedObstacles = this.editedConnector.ExcludedRoutingRectangles;
                if (userDefinedObstacles != null)
                {
                    foreach (Rectangle rectangle in userDefinedObstacles) 
                        e.Graphics.DrawRectangle(Pens.DarkGreen, rectangle);
                } 
 
                foreach (Rectangle rectangle in rectanglesToExclude)
                    e.Graphics.DrawRectangle(Pens.Red, rectangle); 

                for (int i = 0; i < linesToExclude.Count / 2; i++)
                    e.Graphics.DrawLine(Pens.Red, linesToExclude[i * 2], linesToExclude[(i * 2) + 1]);
            } 
#endif
        } 
        #region Helpers 

        #region EditPointUpdation Logic 
        private ConnectionPoint Source
        {
            get
            { 
                return this.editedConnector.Source;
            } 
        } 

        private ConnectionPoint Target 
        {
            get
            {
                return this.editedConnector.Target; 
            }
        } 
 
        private void PerformLayout()
        { 
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
            if (workflowView != null)
                workflowView.PerformLayout(false);
        } 

        private void Invalidate() 
        { 
            WorkflowView workflowView = GetService(typeof(WorkflowView)) as WorkflowView;
            if (workflowView != null) 
            {
                Rectangle bounds = DesignerGeometryHelper.RectangleFromLineSegments(GetPointsFromEditPoints(this.editPoints).ToArray());
                bounds.Inflate(1, 1);
                workflowView.InvalidateLogicalRectangle(bounds); 
            }
        } 
 
        private object GetService(Type serviceType)
        { 
            object service = null;
            if (this.serviceProvider != null)
                service = this.serviceProvider.GetService(serviceType);
            return service; 
        }
 
        private void CreateEditPoints() 
        {
            this.editPoints.Clear(); 

            AddEditPoints(EditPoint.EditPointTypes.ConnectionEditPoint);
            AddEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
            AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint); 

            bool validEditPoints = ValidateEditPoints(); 
            Debug.Assert(validEditPoints); 
        }
 
        private void UpdateEditPoints(Point newPoint)
        {
            if (this.editPoints.Count < 2 ||
                this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint)
            { 
                Debug.Assert(false); 
                return;
            } 

            //STEP1: First we delete all the midsegmentpoints except the one which is being edited for simplicity
            RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
 
            //STEP2: Update points as per the type of edit point
            if (this.activeEditPoint != null) 
            { 
                int activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
                EditPoint previous = (activeEditPointIndex > 0) ? this.editPoints[activeEditPointIndex - 1] : null; 
                EditPoint next = (activeEditPointIndex < this.editPoints.Count - 1) ? this.editPoints[activeEditPointIndex + 1] : null;

                //Note that extra edit points are only added if we are connected to connection point
                if (previous != null && previous.Type == EditPoint.EditPointTypes.ConnectionEditPoint) 
                {
                    float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location); 
                    Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 

                    int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints(previous.Location, (next != null) ? next.Location : this.activeEditPoint.Location)) / 4; 
                    if (orientation == Orientation.Horizontal)
                        editPointOffset *= (previous.Location.X < this.activeEditPoint.Location.X) ? 1 : -1;
                    else
                        editPointOffset *= (previous.Location.Y < this.activeEditPoint.Location.X) ? 1 : -1; 

                    activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint); 
                    Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X + editPointOffset, previous.Location.Y) : new Point(previous.Location.X, previous.Location.Y + editPointOffset); 
                    previous = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
                    this.editPoints.InsertRange(activeEditPointIndex, new EditPoint[] { new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation), previous }); 
                }

                if (next != null && next.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                { 
                    float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
                    Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 
 
                    int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints((previous != null) ? previous.Location : this.activeEditPoint.Location, next.Location)) / 4;
                    if (orientation == Orientation.Horizontal) 
                        editPointOffset *= (this.activeEditPoint.Location.X < next.Location.X) ? -1 : 1;
                    else
                        editPointOffset *= (this.activeEditPoint.Location.Y < next.Location.Y) ? -1 : 1;
 
                    activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
                    Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(next.Location.X + editPointOffset, next.Location.Y) : new Point(next.Location.X, next.Location.Y + editPointOffset); 
                    next = new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation); 
                    this.editPoints.InsertRange(activeEditPointIndex + 1, new EditPoint[] { next, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation) });
                } 

                //STEP2: UPDATE THE EDIT POINTS
                if (this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
                { 
                    this.activeEditPoint.Location = newPoint;
 
                    //When we start editing the end point we need to clear the slate and start over 
                    RemoveEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
 
                    object source = null, target = null;
                    if (this.activeEditPoint.EditedConnectionPoint.Equals(Target))
                    {
                        target = newPoint; 
                        source = Source;
                    } 
                    else 
                    {
                        source = newPoint; 
                        target = Target;
                    }

                    int newEditPointIndex = (this.editPoints.Count == 2) ? 1 : 0; 
                    List newEditPoints = new List();
                    Point[] points = ActivityDesignerConnectorRouter.Route(this.serviceProvider, source, target, this.editedConnector.ExcludedRoutingRectangles); 
                    for (int i = newEditPointIndex; i < points.Length - newEditPointIndex; i++ ) 
                        newEditPoints.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, points[i]));
                    this.editPoints.InsertRange(1, newEditPoints.ToArray()); 
                }
                else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
                {
                    if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint) 
                    {
                        //Update the previous point 
                        float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location); 
                        Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
                        previous.Location = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X, newPoint.Y) : new Point(newPoint.X, previous.Location.Y); 

                        //Update the next point
                        slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
                        orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical; 
                        next.Location = (orientation == Orientation.Horizontal) ? new Point(next.Location.X, newPoint.Y) : new Point(newPoint.X, next.Location.Y);
 
                        //Update the current point 
                        this.activeEditPoint.Location = newPoint;
                    } 
                    else
                    {
                        Debug.Assert(false);
                    } 
                }
                else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MidSegmentEditPoint) 
                { 
                    if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint)
                    { 
                        float slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, next.Location);
                        Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;

                        //If the orientation is horizontal then we need to move the points vertically else we need to move the points horizontally 
                        if (orientation == Orientation.Horizontal)
                        { 
                            previous.Location = new Point(previous.Location.X, newPoint.Y); 
                            next.Location = new Point(next.Location.X, newPoint.Y);
                            this.activeEditPoint.Location = new Point(this.activeEditPoint.Location.X, newPoint.Y); 
                        }
                        else
                        {
                            previous.Location = new Point(newPoint.X, previous.Location.Y); 
                            next.Location = new Point(newPoint.X, next.Location.Y);
                            this.activeEditPoint.Location = new Point(newPoint.X, this.activeEditPoint.Location.Y); 
                        } 
                    }
                    else 
                    {
                        Debug.Assert(false);
                    }
                } 
            }
 
            //STEP3: Remove all the redundant edit points 
            RemoveCoincidingEditPoints();
 
            //STEP4: Add back the segment mid points
            AddEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);

            bool validEditPoints = ValidateEditPoints(); 
            Debug.Assert(validEditPoints);
        } 
 
        //Add edit points of specified type
        private void AddEditPoints(EditPoint.EditPointTypes editPointType) 
        {
            if (editPointType == EditPoint.EditPointTypes.ConnectionEditPoint)
            {
                if (this.editPoints.Count == 0 || !this.editPoints[0].EditedConnectionPoint.Equals(Source)) 
                    this.editPoints.Insert(0, new EditPoint(this, Source));
 
                if (this.editPoints.Count < 2 || !this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint.Equals(Target)) 
                    editPoints.Add(new EditPoint(this, Target));
            } 
            else if (editPointType == EditPoint.EditPointTypes.MidSegmentEditPoint)
            {
                int minLengthForSegmentEditPoint = Source.Bounds.Width * 4;
                for (int i = 0; i < this.editPoints.Count - 1; i++) 
                {
                    if (this.editPoints[i].Type != EditPoint.EditPointTypes.MidSegmentEditPoint && 
                        this.editPoints[i + 1].Type != EditPoint.EditPointTypes.MidSegmentEditPoint && 
                        DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { this.editPoints[i].Location, this.editPoints[i + 1].Location }) > minLengthForSegmentEditPoint)
                    { 
                        Point midPoint = DesignerGeometryHelper.MidPointOfLineSegment(this.editPoints[i].Location, this.editPoints[i + 1].Location);
                        this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MidSegmentEditPoint, midPoint));
                    }
                } 
            }
            else if (editPointType == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
            { 
                if (this.editPoints.Count == 2)
                { 
                    List segments = new List(this.editedConnector.ConnectorSegments);
                    if (segments.Count > 0 && segments[0] == Source.Location)
                        segments.RemoveAt(0);
                    if (segments.Count > 0 && segments[segments.Count - 1] == Target.Location) 
                        segments.RemoveAt(segments.Count - 1);
 
                    List editPointsToAdd = new List(); 
                    for (int i = 0; i < segments.Count; i++)
                        editPointsToAdd.Add(new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, segments[i])); 
                    this.editPoints.InsertRange(this.editPoints.Count - 1, editPointsToAdd.ToArray());
                }
                else
                { 
                    Debug.Assert(false);
                } 
            } 
        }
 
        //Remove edit points of specified type
        private void RemoveEditPoints(EditPoint.EditPointTypes editPointType)
        {
            List editPointsToRemove = new List(); 
 			for (int i = 0; i < this.editPoints.Count; i++)
 			{ 
				EditPoint editPoint = this.editPoints[i]; 
                if (editPoint.Type == editPointType)
                    editPointsToRemove.Add(editPoint); 
            }

 			for (int i = 0; i < editPointsToRemove.Count; i++)
			{ 
				EditPoint editPoint = editPointsToRemove[i];
                if (editPoint != this.activeEditPoint) 
                    this.editPoints.Remove(editPoint); 
            }
        } 

        //Remove points with same slope
        private void RemoveCoincidingEditPoints()
        { 
            if (this.editPoints.Count < 2 ||
                this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint || 
                (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint))
            { 
                return;
            }

            //Just make sure that there are no mid segmment edit points or algorithm will fail 
            RemoveEditPoints(EditPoint.EditPointTypes.MidSegmentEditPoint);
 
            //Create list of points to retain 
            List editPointsToRetain = new List();
            for (int i = 0; i < this.editPoints.Count; i++) 
            {
                if (this.editPoints[i].Type != EditPoint.EditPointTypes.MultiSegmentEditPoint ||
                    this.editPoints[i] == this.activeEditPoint ||
                    (i > 0 && this.editPoints[i - 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint) || 
                    (i < this.editPoints.Count - 1 && this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint))
                { 
                    editPointsToRetain.Add(this.editPoints[i]); 
                }
            } 

            //Step1: Get rid of all the line segments which are within tolerance range
            for (int i = 1; i < this.editPoints.Count - 1; i++)
            { 
                EditPoint previous = this.editPoints[i - 1];
                EditPoint current = this.editPoints[i]; 
                EditPoint next = this.editPoints[i + 1]; 

                if (!editPointsToRetain.Contains(current)) 
                {
                    double distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { previous.Location, current.Location });
                    if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && next.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
                    { 
                        float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
                        next.Location = (slope < 1) ? new Point(next.Location.X, previous.Location.Y) : new Point(previous.Location.X, next.Location.Y); 
                        this.editPoints.Remove(current); 
                        i -= 1;
                    } 
                    else
                    {
                        distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { current.Location, next.Location });
                        if ((distance < current.Bounds.Width || distance < current.Bounds.Height) && previous.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                        {
                            float slope = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location); 
                            previous.Location = (slope < 1) ? new Point(previous.Location.X, next.Location.Y) : new Point(next.Location.X, previous.Location.Y); 
                            this.editPoints.Remove(current);
                            i -= 1; 
                        }
                    }
                }
            } 

            //Step2: We should make sure that the active edit point is always retained but those points which are coincidental are always removed 
            for (int i = 1; i < this.editPoints.Count - 1; i++) 
            {
                EditPoint current = this.editPoints[i]; 
                EditPoint previous = this.editPoints[i - 1];
                EditPoint next = this.editPoints[i + 1];

                if (!editPointsToRetain.Contains(current)) 
                {
                    float slope1 = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location); 
                    float slope2 = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location); 
                    if (Math.Abs(slope1) == Math.Abs(slope2))
                    { 
                        this.editPoints.Remove(current);
                        i -= 1;
                    }
                } 
            }
 
            //Step3: Go thorugh each segment and ensure that there all the segments are either vertical or horizontal 
            for (int i = 0; i < this.editPoints.Count - 1; i++)
            { 
                EditPoint current = this.editPoints[i];
                EditPoint next = this.editPoints[i + 1];

                float slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location); 
                if (slope != 0 && slope != float.MaxValue)
                { 
                    Point location = (slope < 1) ? new Point(next.Location.X, current.Location.Y) : new Point(current.Location.X, next.Location.Y); 
                    this.editPoints.Insert(i + 1, new EditPoint(this, EditPoint.EditPointTypes.MultiSegmentEditPoint, location));
                } 
            }
        }

        private bool ValidateEditPoints() 
        {
            if (this.editPoints.Count < 2) 
                return false; 

            ConnectionPoint sourceConnection = this.editPoints[0].EditedConnectionPoint; 
            if (sourceConnection == null || !sourceConnection.Equals(Source))
                return false;

            ConnectionPoint targetConnection = this.editPoints[this.editPoints.Count - 1].EditedConnectionPoint; 
            if (targetConnection == null || !targetConnection.Equals(Target))
                return false; 
 
            for (int i = 0; i < this.editPoints.Count - 1; i++)
            { 
                if (this.editPoints[i].Type == EditPoint.EditPointTypes.MidSegmentEditPoint &&
                    this.editPoints[i + 1].Type == EditPoint.EditPointTypes.MidSegmentEditPoint)
                    return false;
            } 

            return true; 
        } 
        #endregion
 
        private List GetPointsFromEditPoints(List editPoints)
        {
            List segments = new List();
 
			for (int i = 0; i < editPoints.Count; i++)
 			{ 
				EditPoint editPoint = editPoints[i]; 
                if (editPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint ||
                    editPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint) 
                    segments.Add(editPoint.Location);
            }

            return segments; 
        }
        #endregion 
 
        #region Class EditPoint
        private sealed class EditPoint 
        {
            public enum EditPointTypes { ConnectionEditPoint = 1, MultiSegmentEditPoint, MidSegmentEditPoint };

            private ConnectorEditor owner; 
            private EditPointTypes editPointType;
            private Point point; 
            private ConnectionPoint connectionPoint; 

            public EditPoint(ConnectorEditor owner, EditPointTypes editPointType, Point point) 
            {
                this.owner = owner;
                this.editPointType = editPointType;
                this.point = point; 
            }
 
            public EditPoint(ConnectorEditor owner, ConnectionPoint connectionPoint) 
            {
                this.owner = owner; 
                this.editPointType = EditPointTypes.ConnectionEditPoint;
                this.connectionPoint = connectionPoint;
                this.point = connectionPoint.Location;
            } 

            public EditPointTypes Type 
            { 
                get
                { 
                    return this.editPointType;
                }
            }
 
            public Point Location
            { 
                get 
                {
                    return this.point; 
                }

                set
                { 
                    this.point = value;
                } 
            } 

            public Rectangle Bounds 
            {
                get
                {
                    Size controlPointSize = this.owner.Source.Bounds.Size; 
                    return new Rectangle(this.point.X - controlPointSize.Width / 2, this.point.Y - controlPointSize.Height / 2, controlPointSize.Width, controlPointSize.Height);
                } 
            } 

            public ConnectionPoint EditedConnectionPoint 
            {
                get
                {
                    return this.connectionPoint; 
                }
            } 
        } 
        #endregion
    } 
    #endregion

    #region Class ActivityDesignerConnectorRouter
    internal static class ActivityDesignerConnectorRouter 
    {
        public static Point[] Route(IServiceProvider serviceProvider, object source, object target, ICollection userDefinedRoutingObstacles) 
        { 
            //Now call routing algorithm
            List rectanglesToExclude; 
            List linesToExclude, pointsToExclude;
            ActivityDesignerConnectorRouter.GetRoutingObstacles(serviceProvider, source, target, out rectanglesToExclude, out linesToExclude, out pointsToExclude);

            if (userDefinedRoutingObstacles != null) 
                rectanglesToExclude.AddRange(userDefinedRoutingObstacles);
 
            ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider); 
            AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
            Point sourcePoint = (source is ConnectionPoint) ? ((ConnectionPoint)source).Location : (Point)source; 
            Point targetPoint = (target is ConnectionPoint) ? ((ConnectionPoint)target).Location : (Point)target;

            Point[] routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, new Size(2 * ambientTheme.Margin.Width, 2 * ambientTheme.Margin.Height), rootDesigner.Bounds, rectanglesToExclude.ToArray(), linesToExclude.ToArray(), pointsToExclude.ToArray());
 
            //
 
 

            if (!AreAllSegmentsVerticalOrHorizontal(routedPoints)) 
                routedPoints = ConnectorRouter.Route(sourcePoint, targetPoint, ambientTheme.Margin, rootDesigner.Bounds, new Rectangle[] { }, linesToExclude.ToArray(), new Point[] { });

            //FALLBACK1
            if (!AreAllSegmentsVerticalOrHorizontal(routedPoints)) 
            {
                float slope = DesignerGeometryHelper.SlopeOfLineSegment(sourcePoint, targetPoint); 
                Point intermediatePoint = (slope < 1) ? new Point(targetPoint.X, sourcePoint.Y) : new Point(sourcePoint.X, targetPoint.Y); 
                routedPoints = new Point[] { sourcePoint, intermediatePoint, targetPoint };
            } 

            return routedPoints;
        }
 
        public static void GetRoutingObstacles(IServiceProvider serviceProvider, object source, object target, out List rectanglesToExclude, out List linesToExclude, out List pointsToExclude)
        { 
            //Source or Targets can be either ConnectionPoint or a Point 
            AmbientTheme ambientTheme = WorkflowTheme.CurrentTheme.AmbientTheme;
            ActivityDesigner rootDesigner = ActivityDesigner.GetSafeRootDesigner(serviceProvider); 

            ConnectionPoint sourceConnectionPoint = source as ConnectionPoint;
            Point sourcePoint = (sourceConnectionPoint != null) ? sourceConnectionPoint.Location : (Point)source;
            ActivityDesigner sourceDesigner = (sourceConnectionPoint != null) ? sourceConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(sourcePoint).AssociatedDesigner; 

            ConnectionPoint targetConnectionPoint = target as ConnectionPoint; 
            Point targetPoint = (targetConnectionPoint != null) ? targetConnectionPoint.Location : (Point)target; 
            ActivityDesigner targetDesigner = (targetConnectionPoint != null) ? targetConnectionPoint.AssociatedDesigner : rootDesigner.HitTest(targetPoint).AssociatedDesigner;
 
            //Collect the common parent chain of source and target
            Dictionary parentDesignerDictionary = new Dictionary();
            if (sourceDesigner != null)
            { 
                //Collect designers in the source parent chain
                CompositeActivityDesigner parentDesigner = sourceDesigner.ParentDesigner; 
                while (parentDesigner != null) 
                {
                    if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode())) 
                        parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner);
                    else
                        break;
                    parentDesigner = parentDesigner.ParentDesigner; 
                }
            } 
 
            if (targetDesigner != null)
            { 
                //Collect designer from target chain which are not in source chain
                CompositeActivityDesigner parentDesigner = targetDesigner.ParentDesigner;
                while (parentDesigner != null)
                { 
                    if (!parentDesignerDictionary.ContainsKey(parentDesigner.GetHashCode()))
                        parentDesignerDictionary.Add(parentDesigner.GetHashCode(), parentDesigner); 
                    else 
                        break;
                    parentDesigner = parentDesigner.ParentDesigner; 
                }
            }

            //Now go through the dictionary and add all the children that are not in the chain 
            rectanglesToExclude = new List();
            pointsToExclude = new List(); 
            foreach (CompositeActivityDesigner parentDesigner in parentDesignerDictionary.Values) 
            {
 				ReadOnlyCollection containedDesigners = parentDesigner.ContainedDesigners; 
 				for (int j = 0; j < containedDesigners.Count; j++)
				{
 					ActivityDesigner activityDesigner = containedDesigners[j];
                    if (activityDesigner.IsVisible && 
                        !parentDesignerDictionary.ContainsKey(activityDesigner.GetHashCode()) &&
                        activityDesigner != sourceDesigner && 
                        activityDesigner != targetDesigner) 
                    {
                        Rectangle rectangleToExclude = activityDesigner.Bounds; 
                        rectangleToExclude.Inflate(ambientTheme.Margin);
                        rectanglesToExclude.Add(rectangleToExclude);
                    }
                } 

                // 
 

 



 
            }
 
            //Now get the exclusion paths for source designer and target designer 
            linesToExclude = new List();
            if (sourceDesigner != null && sourceDesigner == targetDesigner && !sourceDesigner.IsRootDesigner) 
            {
                linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source, target }));
            }
            else 
            {
                if (sourceDesigner != null && !sourceDesigner.IsRootDesigner) 
                    linesToExclude.AddRange(GetDesignerEscapeCover(sourceDesigner, new object[] { source })); 

				if (targetDesigner != null && !targetDesigner.IsRootDesigner) 
				{
					bool needToEscapeTargetDesigner = true;

 					CompositeActivityDesigner sourceParentDesigner = (sourceDesigner != null) ? sourceDesigner.ParentDesigner : null; 
					while (sourceParentDesigner != null)
 					{ 
 						if (targetDesigner == sourceParentDesigner) 
						{
 							needToEscapeTargetDesigner = false; 
							break;
						}
						sourceParentDesigner = (sourceDesigner != null) ? sourceParentDesigner.ParentDesigner : null;
 					} 

					//need to escape the target designer only if the source is not inside the parent 
 					if(needToEscapeTargetDesigner) 
 						linesToExclude.AddRange(GetDesignerEscapeCover(targetDesigner, new object[] { target }));
				} 
            }
        }

        private static IList GetDesignerEscapeCover(ActivityDesigner designer, ICollection escapeLocations) 
        {
            Rectangle bounds = designer.Bounds; 
 
            //Create dictionary of escape points
            Dictionary> escapeEdges = new Dictionary>(); 
            foreach (object escapeLocation in escapeLocations)
            {
                DesignerEdges escapeEdge = DesignerEdges.None;
                Point escapePoint = Point.Empty; 
                if (escapeLocation is ConnectionPoint)
                { 
                    escapeEdge = ((ConnectionPoint)escapeLocation).ConnectionEdge; 
                    escapePoint = ((ConnectionPoint)escapeLocation).Location;
                } 
                else if (escapeLocation is Point)
                {
                    escapePoint = (Point)escapeLocation;
                    escapeEdge = DesignerGeometryHelper.ClosestEdgeToPoint((Point)escapeLocation, bounds, DesignerEdges.All); 
                }
 
                if (escapeEdge != DesignerEdges.None) 
                {
                    List escapePoints = null; 
                    if (!escapeEdges.ContainsKey(escapeEdge))
                    {
                        escapePoints = new List();
                        escapeEdges.Add(escapeEdge, escapePoints); 
                    }
                    else 
                    { 
                        escapePoints = escapeEdges[escapeEdge];
                    } 

                    escapePoints.Add(escapePoint);
                }
            } 

            //Create a dictionary of four edges of the designer which will form cover which need to be escaped 
            Size margin = WorkflowTheme.CurrentTheme.AmbientTheme.Margin; 
            bounds.Inflate(margin);
            Dictionary designerEdgeCover = new Dictionary(); 
            designerEdgeCover.Add(DesignerEdges.Left, new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Left, bounds.Bottom) });
            designerEdgeCover.Add(DesignerEdges.Top, new Point[] { new Point(bounds.Left, bounds.Top), new Point(bounds.Right, bounds.Top) });
            designerEdgeCover.Add(DesignerEdges.Right, new Point[] { new Point(bounds.Right, bounds.Top), new Point(bounds.Right, bounds.Bottom) });
            designerEdgeCover.Add(DesignerEdges.Bottom, new Point[] { new Point(bounds.Left, bounds.Bottom), new Point(bounds.Right, bounds.Bottom) }); 

            List linesToExclude = new List(); 
            foreach (DesignerEdges designerEdge in designerEdgeCover.Keys) 
            {
                if (escapeEdges.ContainsKey(designerEdge)) 
                {
                    Point[] edge = designerEdgeCover[designerEdge];
                    List escapePoints = escapeEdges[designerEdge];
                    List escapeCover = new List(); 
                    if (designerEdge == DesignerEdges.Left)
                    { 
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y)); 
 						for (int k = 0; k < escapePoints.Count; k++)
						{ 
							Point escapePoint = escapePoints[k];
                            if (escapePoint.X > edge[0].X && escapePoint.Y > edge[0].Y && escapePoint.Y < edge[1].Y)
                            {
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y - 1)); 
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y + 1)); 
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y + 1)); 
                            }
                        } 
                        escapeCover.Add(new Point(edge[0].X, edge[1].Y));
                    }
                    else if (designerEdge == DesignerEdges.Right)
                    { 
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
						for (int k = 0; k < escapePoints.Count; k++) 
                        { 
                        	Point escapePoint = escapePoints[k];
                            if (escapePoint.X < edge[0].X && escapePoint.Y > edge[0].Y && escapePoint.Y < edge[1].Y) 
                            {
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y + 1)); 
                                escapeCover.Add(new Point(edge[0].X, escapePoint.Y + 1));
                            } 
                        } 
                        escapeCover.Add(new Point(edge[0].X, edge[1].Y));
                    } 
                    else if (designerEdge == DesignerEdges.Top)
                    {
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
 						for (int k = 0; k < escapePoints.Count; k++) 
						{
 							Point escapePoint = escapePoints[k]; 
                            if (escapePoint.Y > edge[0].Y && escapePoint.X > edge[0].X && escapePoint.X < edge[1].X) 
                            {
                                escapeCover.Add(new Point(escapePoint.X - 1, edge[0].Y)); 
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y + 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y + 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, edge[0].Y));
                            } 
                        }
                        escapeCover.Add(new Point(edge[1].X, edge[0].Y)); 
                    } 
                    else if (designerEdge == DesignerEdges.Bottom)
                    { 
                        //Add edge so that the connector does not pass through
                        escapeCover.Add(new Point(edge[0].X, edge[0].Y));
 						for (int k = 0; k < escapePoints.Count; k++)
						{ 
 							Point escapePoint = escapePoints[k];
                            if (escapePoint.Y < edge[0].Y && escapePoint.X > edge[0].X && escapePoint.X < edge[1].X) 
                            { 
                                escapeCover.Add(new Point(escapePoint.X - 1, edge[0].Y));
                                escapeCover.Add(new Point(escapePoint.X - 1, escapePoint.Y - 1)); 
                                escapeCover.Add(new Point(escapePoint.X + 1, escapePoint.Y - 1));
                                escapeCover.Add(new Point(escapePoint.X + 1, edge[0].Y));
                            }
                        } 
                        escapeCover.Add(new Point(edge[1].X, edge[0].Y));
                    } 
 
                    for (int i = 1; i < escapeCover.Count; i++)
                    { 
                        linesToExclude.Add(escapeCover[i - 1]);
                        linesToExclude.Add(escapeCover[i]);
                    }
                } 
                else
                { 
                    linesToExclude.AddRange(designerEdgeCover[designerEdge]); 
                }
            } 

            return linesToExclude.AsReadOnly();
        }
 
        private static bool AreAllSegmentsVerticalOrHorizontal(Point[] segments)
        { 
            if (segments == null || segments.Length == 0) 
                return false;
 
            for (int i = 1; i < segments.Length; i++)
            {
                if (segments[i - 1].X != segments[i].X && segments[i - 1].Y != segments[i].Y)
                    return false; 
            }
 
            return true; 
        }
    } 
    #endregion

    #region Class ConnectorRouter
    internal static class ConnectorRouter 
    {
        private static readonly Size DefaultSize = new Size(4, 4); 
 
        public static Point[] Route(Point begin, Point end, Size margin, Rectangle enclosingRectangle, Rectangle[] rectanglesToExclude, Point[] linesToExclude, Point[] pointsToExclude)
        { 
            List excludedRectangles = new List(rectanglesToExclude);
            if (!enclosingRectangle.IsEmpty)
            {
                begin.X = Math.Min(Math.Max(begin.X, enclosingRectangle.Left + 1), enclosingRectangle.Right - 1); 
                begin.Y = Math.Min(Math.Max(begin.Y, enclosingRectangle.Top + 1), enclosingRectangle.Bottom - 1);
 
                excludedRectangles.Insert(0, enclosingRectangle); 
            }
 
            List excludedLines = new List(linesToExclude);
            int width = Math.Max(margin.Width / 2, 1), height = Math.Max(margin.Height / 2, 1);
            foreach (Point point in pointsToExclude)
            { 
                excludedLines.Add(new Point(point.X - width, point.Y));
                excludedLines.Add(new Point(point.X + width, point.Y)); 
 
                excludedLines.Add(new Point(point.X, point.Y - height));
                excludedLines.Add(new Point(point.X, point.Y + height)); 
            }

            return GetRoutedLineSegments(begin, end, margin, excludedRectangles.ToArray(), excludedLines.ToArray());
        } 

        private static Point[] GetRoutedLineSegments(Point begin, Point end, Size margin, Rectangle[] rectanglesToExclude, Point[] linesToExclude) 
        { 
            if (rectanglesToExclude == null)
                throw new ArgumentNullException("rectanglesToExclude"); 

            if (linesToExclude == null)
                throw new ArgumentNullException("linesToExclude");
 
            if ((linesToExclude.Length % 2) > 0)
                throw new ArgumentException(DR.GetString(DR.Error_Connector2)); 
 
            CoverSet coverSet = new CoverSet(rectanglesToExclude, linesToExclude);
            coverSet.ClearUsedLines(); 

            Point A = begin;
            Point B = end;
 
            //escape points
            List LeA = new List();//escape points from begin to end 
            List LeB = new List();//escape points from end to begin 

            //horizontal/vertical escape segments from A 
            List LhA = new List();
            List LvA = new List();

            //horizontal/vertical escape segments from B 
            List LhB = new List();
            List LvB = new List(); 
 
            Orientation orientationA = Orientation.Horizontal;
            Orientation orientationB = Orientation.Horizontal; 

            //P0
            LeA.Add(begin);
            LeB.Add(end); 

            bool noEscapeA = false; 
            bool noEscapeB = false; 

            Nullable intersection = null; 
            ConnectorSegment intersectionSegmentA = null;
            ConnectorSegment intersectionSegmentB = null;

            try 
            {
                do 
                { 
                    //P1
                    if (noEscapeA) 
                    {
                        if (noEscapeB)
                        {
                            //we failed to find the point 
                            break;
                        } 
                        else 
                        {
                            //P2 
                            #region swap A and B (with all appropriate lists and flags...)
                            List tempList = LeA;
                            LeA = LeB;
                            LeB = tempList; 

                            Point tempPoint = A; 
                            A = B; 
                            B = tempPoint;
 
                            bool tempBool = noEscapeA;
                            noEscapeA = noEscapeB;
                            noEscapeB = tempBool;
 
                            Orientation tempOrientation = orientationA;
                            orientationA = orientationB; 
                            orientationB = tempOrientation; 

                            List tempListSegm = LhA; 
                            LhA = LhB;
                            LhB = tempListSegm;

                            tempListSegm = LvA; 
                            LvA = LvB;
                            LvB = tempListSegm; 
                            #endregion 

                            continue; 
                        }
                    }

                    Point objectPoint = LeA[LeA.Count - 1]; 
                    Point targetPoint = B;
 
                    intersection = EscapeAlgorithm(coverSet, objectPoint, targetPoint, 
                        ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, ref orientationA,
                        out intersectionSegmentA, out intersectionSegmentB, margin, ref noEscapeA); 
                    if (intersection != null)
                    {
                        break;
                    } 
                    else
                    { 
                        #region swap A and B (with all appropriate lists and flags...) 
                        List tempList = LeA;
                        LeA = LeB; 
                        LeB = tempList;

                        Point tempPoint = A;
                        A = B; 
                        B = tempPoint;
 
                        bool tempBool = noEscapeA; 
                        noEscapeA = noEscapeB;
                        noEscapeB = tempBool; 

                        Orientation tempOrientation = orientationA;
                        orientationA = orientationB;
                        orientationB = tempOrientation; 

                        List tempListSegm = LhA; 
                        LhA = LhB; 
                        LhB = tempListSegm;
 
                        tempListSegm = LvA;
                        LvA = LvB;
                        LvB = tempListSegm;
                        #endregion 
                    }
 
                } while (true); 

                //we failed 
                if (intersection == null)
                    return null;
                //{
                //    // 

 
 

 

                //    return newPath.ToArray();
                //}
 
                List refinedPath = new List();
 
                #region P3 apply refinement algorithms 
                //first refinement algorithm
                LeA = FirstRefinementAlgorithm(LeA, (Point)intersection, intersectionSegmentA); 
                LeB = FirstRefinementAlgorithm(LeB, (Point)intersection, intersectionSegmentB);

                //before going into the second refinement, construct the full path
                for (int j = LeA.Count - 1; j >= 0; j--) 
                    refinedPath.Add(LeA[j]);
                refinedPath.Add((Point)intersection); 
                for (int j = 0; j < LeB.Count; j++) 
                    refinedPath.Add(LeB[j]);
 
                //perform second refinement algorithm on the full path
                SecondRefinementAlgorithm(coverSet, ref refinedPath, margin);
                #endregion
 
                if (refinedPath.Count > 1 && refinedPath[refinedPath.Count - 1] == begin)
                    refinedPath.Reverse(); 
 
                return refinedPath.ToArray();
            } 
            catch
            {
                //
 
                return null;
            } 
        } 

        #region Escape Algorithm 
        private static Nullable EscapeAlgorithm(CoverSet coverSet, Point Z, Point targetPoint,
            ref List LeA, ref List LhA, ref List LvA, ref List LhB, ref List LvB,
            ref Orientation orientationA, out ConnectorSegment intersectionSegmentA, out ConnectorSegment intersectionSegmentB, Size margin, ref bool noEscapeA)
        { 
            Nullable intersection = null;
            intersectionSegmentA = null; 
            intersectionSegmentB = null; 

            ConnectorSegment leftCover = coverSet.GetCover(Z, DesignerEdges.Left); 
            ConnectorSegment rightCover = coverSet.GetCover(Z, DesignerEdges.Right);
            ConnectorSegment bottomCover = coverSet.GetCover(Z, DesignerEdges.Bottom);
            ConnectorSegment topCover = coverSet.GetCover(Z, DesignerEdges.Top);
 
            #region P1: construct escape line(s) - on the beginning of the algorithm it will create two lines
            ConnectorSegment h = ConnectorSegment.SegmentFromLeftToRightCover(coverSet, Z); 
            LhA.Add(h); 

            ConnectorSegment v = ConnectorSegment.SegmentFromBottomToTopCover(coverSet, Z); 
            LvA.Add(v);
            #endregion

            #region P2 check if the new escape line(s) intersect with the existing ones 
			for (int i = 0; i < LvB.Count; i++)
			{ 
				ConnectorSegment segment = LvB[i]; 
                intersection = h.Intersect(segment);
                if (intersection != null) 
                {
                    intersectionSegmentA = h;
                    intersectionSegmentB = segment;
                    return intersection; 
                }
            } 
 
 			for (int i = 0; i < LhB.Count; i++)
			{ 
 				ConnectorSegment segment = LhB[i];
                intersection = v.Intersect(segment);
                if (intersection != null)
                { 
                    intersectionSegmentA = v;
                    intersectionSegmentB = segment; 
                    return intersection; 
                }
            } 
            #endregion

            //there was no intersection found above, continue on
 
            //P3 find an escape point
 
            #region Call Escape Process I 
            //Escape process I P1
            Nullable escapePoint = EscapeProcessI(coverSet, Z, v, Orientation.Horizontal, margin); 
            if (escapePoint != null)
            {
                orientationA = Orientation.Vertical;
                LeA.Add((Point)escapePoint); 
                return null;
            } 
 
            //Escape process I P2
            escapePoint = EscapeProcessI(coverSet, Z, h, Orientation.Vertical, margin); 
            if (escapePoint != null)
            {
                orientationA = Orientation.Horizontal;
                LeA.Add((Point)escapePoint); 
                return null;
            } 
            #endregion 

            #region Call Escape process II 
            bool intersectionFlag = false;
            //flags indicating if we can still continue in the given directions
            bool continue1, continue2, continue3, continue4;
            Point r1 = Point.Empty, r2 = Point.Empty, r3 = Point.Empty, r4 = Point.Empty; 

            if (topCover != null) 
                r1 = new Point(Z.X, topCover.A.Y); 
            if (rightCover != null)
                r2 = new Point(rightCover.A.X, Z.Y); 
            if (bottomCover != null)
                r3 = new Point(Z.X, bottomCover.A.Y);
            if (leftCover != null)
                r4 = new Point(leftCover.A.X, Z.Y); 

            do 
            { 
                continue1 = continue2 = continue3 = continue4 = false;
                if (topCover != null) 
                {
                    r1.Y -= margin.Height;
                    if (r1.Y > Z.Y)
                    { 
                        continue1 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Vertical, 
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r1, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB); 
                        if (escape != null)
                        { 
                            LvA.Add(v);
                            if (intersectionFlag)
                                return escape;
 
                            orientationA = Orientation.Horizontal;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r1)); 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r1, (Point)escape)); 
                            LeA.Add((Point)escape);
                            return null; 
                        }
                    }
                }
 
                if (rightCover != null)
                { 
                    r2.X -= margin.Width; 
                    if (r2.X > Z.X)
                    { 
                        continue2 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Horizontal,
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r2, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB);
                        if (escape != null) 
                        {
                            LhA.Add(h); 
                            if (intersectionFlag) 
                                return escape;
 
                            orientationA = Orientation.Vertical;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r2));
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r2, (Point)escape));
                            LeA.Add((Point)escape); 
                            return null;
                        } 
                    } 
                }
 
                if (bottomCover != null)
                {
                    r3.Y += margin.Height;
                    if (r3.Y < Z.Y) 
                    {
                        continue3 = true; 
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Vertical, 
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r3, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB);
                        if (escape != null) 
                        {
                            LvA.Add(v);
                            if (intersectionFlag)
                                return escape; 

                            orientationA = Orientation.Horizontal; 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r3)); 
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r3, (Point)escape));
                            LeA.Add((Point)escape); 
                            return null;
                        }
                    }
                } 

                if (leftCover != null) 
                { 
                    r4.X += margin.Width;
                    if (r4.X < Z.X) 
                    {
                        continue4 = true;
                        Nullable escape = EscapeProcessII(coverSet, Orientation.Horizontal,
                            ref LeA, ref LhA, ref LvA, ref LhB, ref LvB, Z, r4, margin, out intersectionFlag, out intersectionSegmentA, out intersectionSegmentB); 
                        if (escape != null)
                        { 
                            LhA.Add(h); 
                            if (intersectionFlag)
                                return escape; 

                            orientationA = Orientation.Vertical;
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(Z, r4));
                            coverSet.AddUsedEscapeLine(new ConnectorSegment(r4, (Point)escape)); 
                            LeA.Add((Point)escape);
                            return null; 
                        } 
                    }
                } 
                //continue the loop until there is a place to go in either of the directions
            } while (continue1 || continue2 || continue3 || continue4);
            #endregion
 
            noEscapeA = true;
            return null; 
        } 

        #region Escape Process I 
        private static Nullable EscapeProcessI(CoverSet coverSet, Point Z,
            ConnectorSegment escapeLine, Orientation orientation, Size margin)
        {
            //perform extremity point permutation by the distance from the object point 
            //when sorting points store to which segments they belong (this is needed further when
            //deciding to side of the point to move if the extremity coincide with the object point 
            //on abscissa or ordinata axis) 
            List extremitiesList = new List(4);//at most four points
 
            ConnectorSegment lesserCover = coverSet.GetCover(Z, (orientation == Orientation.Horizontal) ? DesignerEdges.Left : DesignerEdges.Bottom);
            if (lesserCover != null)
            {
                AddBoundPoint(ref extremitiesList, lesserCover.A, lesserCover, Z); 
                AddBoundPoint(ref extremitiesList, lesserCover.B, lesserCover, Z);
            } 
 
            ConnectorSegment higherCover = coverSet.GetCover(Z, (orientation == Orientation.Horizontal) ? DesignerEdges.Right : DesignerEdges.Top);
            if (higherCover != null) 
            {
                AddBoundPoint(ref extremitiesList, higherCover.A, higherCover, Z);
                AddBoundPoint(ref extremitiesList, higherCover.B, higherCover, Z);
            } 

            if (extremitiesList.Count == 0) 
                return null; 

            DistanceSorter.Sort(ref extremitiesList); 
            for (int i = 0; i < extremitiesList.Count; i++)
            {
                Point p = extremitiesList[i].P;
                Point direction = new Point(Math.Sign(p.X - Z.X), Math.Sign(p.Y - Z.Y)); 
                if (((orientation == Orientation.Vertical) ? direction.X : direction.Y) == 0)
                { 
                    //if extremity is on the same abscissa(ordinata) line with the object point, need to be more careful 
                    ConnectorSegment segment = extremitiesList[i].ConnectorSegment;
                    p = segment.ExtendPointOutwards(p); 
                    direction = new Point(Math.Sign(p.X - Z.X), Math.Sign(p.Y - Z.Y));
                    p = extremitiesList[i].P;
                }
 
                DesignerEdges side;//which side is the cover we are trying to escape
                if (orientation == Orientation.Vertical) 
                    //we are escaping either top or bottom 
                    side = (direction.Y < 0) ? DesignerEdges.Bottom : DesignerEdges.Top;
                else 
                    //we are escaping either left or right
                    side = (direction.X < 0) ? DesignerEdges.Left : DesignerEdges.Right;

                Point escapePoint; 
                if ((orientation == Orientation.Vertical))
                    escapePoint = new Point(p.X + direction.X * margin.Width, Z.Y); 
                else 
                    escapePoint = new Point(Z.X, p.Y + direction.Y * margin.Height);
 
                //the new escape point should
                //1) lay on the given escape line (except the bounding points since they belong to covers)
                //2) not lay on any of the already tested escape segments - all points belonging to them are already worthless
                ConnectorSegment newEscapeLine = new ConnectorSegment(Z, escapePoint); 
                if (!coverSet.EscapeLineHasBeenUsed(newEscapeLine, escapePoint) &&
                    escapeLine.IsPointOnSegment(escapePoint) && escapeLine.A != escapePoint && escapeLine.B != escapePoint && 
                    coverSet.IsEscapePoint(Z, escapePoint, side)) 
                {
                    coverSet.AddUsedEscapeLine(newEscapeLine); 
                    return escapePoint;
                }
            }
 
            return null;
        } 
 
        private static void AddBoundPoint(ref List extremitiesList, Point p, ConnectorSegment segment, Point Z)
        { 
            if (p.X != int.MinValue && p.X != int.MaxValue && p.Y != int.MinValue && p.Y != int.MaxValue)
                extremitiesList.Add(new DistanceFromPoint(segment, Z, p));
        }
        #endregion 

        #region Escape Process II 
        private static Nullable EscapeProcessII(CoverSet coverSet, Orientation orientation, ref List LeA, 
            ref List LhA, ref List LvA, ref List LhB, ref List LvB,
            Point Z, Point R, Size margin, out bool intersectionFlag, out ConnectorSegment intersectionSegmentA, out ConnectorSegment intersectionSegmentB) 
        {
            intersectionFlag = false;
            intersectionSegmentA = null;
            intersectionSegmentB = null; 

            //rebuild invalidated covers 
            ConnectorSegment h = ConnectorSegment.SegmentFromLeftToRightCover(coverSet, R); 
            ConnectorSegment v = ConnectorSegment.SegmentFromBottomToTopCover(coverSet, R);
 
            //check intersections
 			for (int i = 0; i < LvB.Count; i++)
			{
 				ConnectorSegment segment = LvB[i]; 
                Nullable itersection = h.Intersect(segment);
                if (itersection != null) 
                { 
                    intersectionFlag = true;
                    intersectionSegmentA = h; 
                    intersectionSegmentB = segment;
                    LeA.Add(R);
                    return itersection;
                } 
            }
			for (int i = 0; i < LhB.Count; i++) 
			{ 
				ConnectorSegment segment = LhB[i];
                Nullable itersection = v.Intersect(segment); 
                if (itersection != null)
                {
                    intersectionFlag = true;
                    intersectionSegmentA = v; 
                    intersectionSegmentB = segment;
                    LeA.Add(R); 
                    return itersection; 
                }
            } 

            Nullable escapePointI = null;

            //now do both horizontal and vertical escape processes I from that point... 
            //the order is important based on 'orientation' argument
            if (orientation == Orientation.Horizontal) 
            { 
                escapePointI = EscapeProcessI(coverSet, R, v, Orientation.Horizontal, margin);
                if (escapePointI != null) 
                {
                    LvA.Add(v);
                    LeA.Add(R);
                    return escapePointI; 
                }
 
                escapePointI = EscapeProcessI(coverSet, R, h, Orientation.Vertical, margin); 
                if (escapePointI != null)
                { 
                    LhA.Add(h);
                    LeA.Add(R);
                    return escapePointI;
                } 
            }
            else 
            { 
                escapePointI = EscapeProcessI(coverSet, R, h, Orientation.Vertical, margin);
                if (escapePointI != null) 
                {
                    LhA.Add(h);
                    LeA.Add(R);
                    return escapePointI; 
                }
 
                escapePointI = EscapeProcessI(coverSet, R, v, Orientation.Horizontal, margin); 
                if (escapePointI != null)
                { 
                    LvA.Add(v);
                    LeA.Add(R);
                    return escapePointI;
                } 
            }
 
            return null; 
        }
        #endregion 

        #endregion

        #region ConnectorSegment Refinement Algorithms 
        //remove all non-corner points
        private static List FirstRefinementAlgorithm(List Le, Point intersection, ConnectorSegment intersectionSegment) 
        { 
            List refinedSet = new List();
            ConnectorSegment k = intersectionSegment; 

            while (Le.Count > 0)
            {
                Point pi; 
                int i = Le.Count - 1;
 
                //find the first point that lies on k 
                while (!k.PointLiesOnThisLine(Le[i]) && i > 0)
                    i--; 

                //find the least point that lies on k
                while (i > 0 && k.PointLiesOnThisLine(Le[i - 1]))
                    i--; 

                //save the point 
                pi = Le[i]; 
                refinedSet.Add(pi);
 
                //remove all the points up to the found one from the original list
                while (Le.Count > i)
                    Le.RemoveAt(i);
 
                //continue with the points lying on the line perpendecular to the previous one and passing through the
                //found point 
                k = k.PeprendecularThroughPoint(pi); 
            }
 
            return refinedSet;
        }

        //remove superflous parts from the path 
        private static void SecondRefinementAlgorithm(CoverSet coverSet, ref List refinedPath, Size margin)
        { 
            List newPath = new List(); 

            #region Part I: extend every segment in the path to see if it intersects any other segment 
            int currentSegment = 0;
            while (currentSegment < refinedPath.Count - 1)
            {
                Point a1 = refinedPath[currentSegment]; 
                Point a2 = refinedPath[currentSegment + 1];
 
                //need to construct a segment through the points that is limited by the covers 
                ConnectorSegment a = ConnectorSegment.ConstructBoundSegment(coverSet, a1, a2);
 
                //try to intersect it with every segment after the current one
                //and the next one (which we know does intersect with the current one)
                int intersectingSegment = currentSegment + 2;
                while (intersectingSegment < refinedPath.Count - 1) 
                {
                    Point b1 = refinedPath[intersectingSegment]; 
                    Point b2 = refinedPath[intersectingSegment + 1]; 
                    ConnectorSegment b = ConnectorSegment.ConstructBoundSegment(coverSet, b1, b2);
 
                    Nullable intersection = a.Intersect(b);
                    if (intersection != null)
                    {
                        newPath.Clear(); 
                        //need to remove all points between a1 and b2 and insert the point of intersectin there
                        for (int i = 0; i <= currentSegment; i++) 
                            newPath.Add(refinedPath[i]); 
                        newPath.Add((Point)intersection);
                        for (int i = intersectingSegment + 1; i < refinedPath.Count; i++) 
                            newPath.Add(refinedPath[i]);

                        List temp = refinedPath;
                        refinedPath = newPath; 
                        newPath = temp;
                        newPath.Clear(); 
 
                        //reset the second segment number and go through all segments once more
                        //until there are no intersections left 
                        intersectingSegment = currentSegment + 2;
                    }
                    else
                    { 
                        intersectingSegment++;
                    } 
                } 

                //we need to keep looking for intersections until we reach the very last one 
                currentSegment++;
            }
            #endregion
 
            #region Part II: construct segments perpendecular to every segment and see if they intersect any other segment
            currentSegment = 0; 
            while (currentSegment < refinedPath.Count - 1) 
            {
                Point a1 = refinedPath[currentSegment]; 
                Point a2 = refinedPath[currentSegment + 1];

                //need to construct a segment through the points that is limited by the covers
                bool intersected = false; 
                ConnectorSegment a = ConnectorSegment.ConstructBoundSegment(coverSet, a1, a2);
                if (a != null) 
                { 
                    Point direction = new Point(a2.X - a1.X, a2.Y - a1.Y);
 
                    //see how many intemediate points we can construct
                    int steps = Math.Max(Math.Abs(direction.X / margin.Width), Math.Abs(direction.Y / margin.Height));//one of the values will be null
                    direction.X = Math.Sign(direction.X);
                    direction.Y = Math.Sign(direction.Y); 

                    for (int i = 1; i <= steps; i++) 
                    { 
                        Point k = new Point(a1.X + i * margin.Width * direction.X, a1.Y + i * margin.Height * direction.Y);
                        if (k == a2) 
                            break;

                        ConnectorSegment b = ConnectorSegment.ConstructBoundSegment(coverSet, k, (a.Orientation == Orientation.Horizontal) ? Orientation.Vertical : Orientation.Horizontal);
                        //now try to intersect that segment with every segment after the current one and the one after it 
                        int intersectingSegment = currentSegment + 2;
                        while (intersectingSegment < refinedPath.Count - 1 && !intersected) 
                        { 
                            Point c1 = refinedPath[intersectingSegment];
                            Point c2 = refinedPath[intersectingSegment + 1]; 
                            ConnectorSegment c = new ConnectorSegment(c1, c2);

                            Nullable intersection = b.Intersect(c);
                            if (intersection != null && c.IsPointOnSegment((Point)intersection)) 
                            {
                                intersected = true; 
 
                                newPath.Clear();
                                //need to remove all points between a1 and b2 and insert k and the point of intersection there 
                                for (int j = 0; j <= currentSegment; j++)
                                    newPath.Add(refinedPath[j]);
                                newPath.Add(k);
                                newPath.Add((Point)intersection); 
                                for (int j = intersectingSegment + 1; j < refinedPath.Count; j++)
                                    newPath.Add(refinedPath[j]); 
 
                                List temp = refinedPath;
                                refinedPath = newPath; 
                                newPath = temp;
                                newPath.Clear();
                                break;
                            } 

                            intersectingSegment++; 
                        } 

                        //if there was an intersection, exit the for loop 
                        if (intersected)
                            break;
                    }
                } 

                //if there was an intersection, run the same segment again to see if there are other intersecting segments 
                if (!intersected) 
                    currentSegment++;
            } 
            #endregion
        }
        #endregion
 
        #region Struct DistanceFromPoint
        //sorting of extremities by closedness to point Z 
        private struct DistanceFromPoint 
        {
            public ConnectorSegment ConnectorSegment; 
            public Point P;
            public double Distance;

            public DistanceFromPoint(ConnectorSegment segment, Point z, Point p) 
            {
                this.ConnectorSegment = segment; 
                this.P = p; 
                this.Distance = ConnectorSegment.DistanceBetweenPoints(z, p);
            } 
        }
        #endregion

        #region Class DistanceSorter 
        private sealed class DistanceSorter : IComparer
        { 
            private DistanceSorter() { } 

            public static void Sort(ref List distances) 
            {
                DistanceSorter sorter = new DistanceSorter();
                distances.Sort(sorter);
            } 

            int IComparer.Compare(DistanceFromPoint lhs, DistanceFromPoint rhs) 
            { 
                if (lhs.Distance == rhs.Distance)
                    return 0; 
                else if (lhs.Distance > rhs.Distance)
                    return 1;
                else return -1;
            } 
        }
        #endregion 
 
        #region Class ConnectorSegment
        // represents a segment - the main entity in the routing algorithm 
 		[DebuggerDisplay("Segment ( {A.X}, {A.Y} ) - ( {B.X},{B.Y} ), {Orientation}")]
		private sealed class ConnectorSegment
        {
            private Point point1; 
            private Point point2;
 			private Orientation orientation; 
 
            public ConnectorSegment(Point point1, Point point2)
            { 
                if (point1.X != point2.X && point1.Y != point2.Y)
                    throw new InvalidOperationException(SR.GetString(SR.Error_InvalidConnectorSegment));

                this.point1 = point1; 
                this.point2 = point2;
 				this.orientation = ((this.point1.X == this.point2.X) ? Orientation.Vertical : Orientation.Horizontal); 
            } 

            public Point A 
            {
                get
                {
                    return this.point1; 
                }
            } 
 
            public Point B
            { 
                get
                {
                    return this.point2;
                } 
            }
 
            public Orientation Orientation 
            {
                get 
                {
                    return this.orientation;
                }
            } 

            //"segment l covers a point p, if the perpendicular from p to the line of which l is a segment intersects l" 
            //since a) we only have horizotal or vertical segments and b) some segments will be unbound, we have to store and use orientation 
            //flag to do analyzis
            public bool Covers(Point p) 
            {
				return (this.orientation == Orientation.Horizontal) ? (p.X >= Math.Min(this.point1.X, this.point2.X) && p.X <= Math.Max(this.point1.X, this.point2.X)) : (p.Y >= Math.Min(this.point1.Y, this.point2.Y) && p.Y <= Math.Max(this.point1.Y, this.point2.Y));
            }
 
            //see if the two segments intersect
            //greatly simplified by the fact that we only have vertical or horizontal segments 
            //should work fine with {Max, Min}Value values 
            public Nullable Intersect(ConnectorSegment segment)
            { 
 				if (this.orientation == segment.Orientation)
                    return null;

				ConnectorSegment vertical = (this.orientation == Orientation.Vertical) ? this : segment; 
				ConnectorSegment horizontal = (this.orientation == Orientation.Vertical) ? segment : this;
 
                if (vertical.A.X < Math.Min(horizontal.A.X, horizontal.B.X) || vertical.A.X > Math.Max(horizontal.A.X, horizontal.B.X)) 
                    return null;
 
                if (horizontal.A.Y < Math.Min(vertical.A.Y, vertical.B.Y) || horizontal.A.Y > Math.Max(vertical.A.Y, vertical.B.Y))
                    return null;

                return new Point(vertical.A.X, horizontal.A.Y); 
            }
 
            //we consider the whole line to which this segment belongs for this test 
            public bool PointLiesOnThisLine(Point p)
            { 
				return (this.orientation == Orientation.Horizontal) ? p.Y == this.point1.Y : p.X == this.point1.X;
            }

            //we consider just the segment for this test 
            public bool IsPointOnSegment(Point p)
            { 
 				if ((this.orientation == Orientation.Horizontal && p.Y != this.point1.Y) || (this.orientation == Orientation.Vertical && p.X != this.point1.X)) 
                    return false;
 
				int k = (this.orientation == Orientation.Horizontal) ? p.X : p.Y;
 				int k1 = (this.orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
 				int k2 = (this.orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y;
                return k >= Math.Min(k1, k2) && k <= Math.Max(k1, k2); 
            }
 
            //does the given segment overlap us? 
            //public bool Overlaps(ConnectorSegment segment)
            //{ 
            //    if (Orientation != segment.Orientation)
            //        return false;

            //    int k0 = (this.Orientation == Orientation.Horizontal) ? this.point1.Y : this.point1.X; 
            //    int k1 = (this.Orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
            //    int k2 = (this.Orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y; 
 
            //    int l0 = (this.Orientation == Orientation.Horizontal) ? segment.A.Y : segment.A.X;
            //    int l1 = (this.Orientation == Orientation.Horizontal) ? segment.A.X : segment.A.Y; 
            //    int l2 = (this.Orientation == Orientation.Horizontal) ? segment.B.X : segment.B.Y;

            //    if (k0 != l0)
            //        return false; 

            //    int min = Math.Min(k1, k2); 
            //    k2 = Math.Max(k1, k2); 
            //    k1 = min;
 
            //    min = Math.Min(l1, l2);
            //    l2 = Math.Max(l1, l2);
            //    l1 = min;
 
            //    if (l1 >= k2 || k1 >= l2)
            //        return false; 
 
            //    return true;
            //} 

            public ConnectorSegment PeprendecularThroughPoint(Point p)
            {
				Orientation newOrientation = (this.orientation == Orientation.Horizontal) ? Orientation.Vertical : Orientation.Horizontal; 
                Point newPoint = new Point(p.X, p.Y);
                if (newOrientation == Orientation.Horizontal) 
                    newPoint.X = int.MaxValue; 
                else
                    newPoint.Y = int.MaxValue; 

                return new ConnectorSegment(p, newPoint);
            }
 
            public Point ExtendPointOutwards(Point p)
            { 
                Debug.Assert(!(p != this.point1 && p != this.point2), "wrong extension requested"); 
                if (p != this.point1 && p != this.point2)
                    return p; 

 				int k = (this.orientation == Orientation.Horizontal) ? p.X : p.Y;
				int k1 = (this.orientation == Orientation.Horizontal) ? this.point1.X : this.point1.Y;
				int k2 = (this.orientation == Orientation.Horizontal) ? this.point2.X : this.point2.Y; 

                if (k == Math.Min(k1, k2)) 
                    k--; 
                else
                    k++; 

				return new Point((this.orientation == Orientation.Horizontal) ? k : p.X, (this.orientation == Orientation.Horizontal) ? p.Y : k);
            }
 
            public static double DistanceBetweenPoints(Point p, Point q)
            { 
                return Math.Sqrt((double)(p.X - q.X) * (p.X - q.X) + (double)(p.Y - q.Y) * (p.Y - q.Y)); 
            }
 
            //given two points construct a segment through them from lesser cover to higher
            public static ConnectorSegment ConstructBoundSegment(CoverSet coverSet, Point a, Point b)
            {
                // 

                if (a.X != b.X && a.Y != b.Y) 
                    return null; 

                return ConstructBoundSegment(coverSet, a, (a.X == b.X) ? Orientation.Vertical : Orientation.Horizontal); 
            }

            public static ConnectorSegment SegmentFromLeftToRightCover(CoverSet coverSet, Point p)
            { 
                ConnectorSegment leftCover = coverSet.GetCover(p, DesignerEdges.Left);
                ConnectorSegment rightCover = coverSet.GetCover(p, DesignerEdges.Right); 
 
                //construct horizontal escape segment
                Point left = new Point((leftCover != null) ? leftCover.A.X : int.MinValue, p.Y); 
                Point right = new Point((rightCover != null) ? rightCover.A.X : int.MaxValue, p.Y);
                ConnectorSegment h = new ConnectorSegment(left, right);
                return h;
            } 

            public static ConnectorSegment SegmentFromBottomToTopCover(CoverSet coverSet, Point p) 
            { 
                ConnectorSegment bottomCover = coverSet.GetCover(p, DesignerEdges.Bottom);
                ConnectorSegment topCover = coverSet.GetCover(p, DesignerEdges.Top); 

                //construct vertical escape segment
                Point bottom = new Point(p.X, (bottomCover != null) ? bottomCover.A.Y : int.MinValue);
                Point top = new Point(p.X, (topCover != null) ? topCover.A.Y : int.MaxValue); 
                ConnectorSegment v = new ConnectorSegment(bottom, top);
                return v; 
            } 

            public static ConnectorSegment ConstructBoundSegment(CoverSet coverSet, Point a, Orientation orientation) 
            {
                return (orientation == Orientation.Horizontal) ? SegmentFromLeftToRightCover(coverSet, a) : SegmentFromBottomToTopCover(coverSet, a);
            }
 
            public override bool Equals(object obj)
            { 
                ConnectorSegment segment = obj as ConnectorSegment; 
                if (segment == null)
                    return false; 
                return (this.point1 == segment.A && this.point2 == segment.B && Orientation == segment.Orientation);
            }

            public override int GetHashCode() 
            {
                return this.point1.GetHashCode() ^ this.point2.GetHashCode() ^ Orientation.GetHashCode(); 
            } 
        }
        #endregion 

        #region Class CoverSet (Set of vertical and horizontal covers)
        private sealed class CoverSet
        { 
            private List verticalCovers = new List();
            private List horizontalCovers = new List(); 
            private List usedEscapeLine = new List(); 

            public CoverSet(Rectangle[] rectanglesToExclude, Point[] linesToExclude) 
            {
                foreach (Rectangle rectangle in rectanglesToExclude)
                {
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Top), new Point(rectangle.Left, rectangle.Bottom))); 
                    AddCover(new ConnectorSegment(new Point(rectangle.Right, rectangle.Top), new Point(rectangle.Right, rectangle.Bottom)));
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Top), new Point(rectangle.Right, rectangle.Top))); 
                    AddCover(new ConnectorSegment(new Point(rectangle.Left, rectangle.Bottom), new Point(rectangle.Right, rectangle.Bottom))); 
                }
 
                //Add the linesegments to cover
                for (int i = 0; i < linesToExclude.Length / 2; i++)
                    AddCover(new ConnectorSegment(linesToExclude[i * 2], linesToExclude[(i * 2) + 1]));
            } 

            public void ClearUsedLines() 
            { 
                this.usedEscapeLine.Clear();
            } 

            public void AddCover(ConnectorSegment cover)
            {
                List covers = (cover.Orientation == Orientation.Vertical) ? this.verticalCovers : this.horizontalCovers; 

                for (int i = 0; i < covers.Count; i++) 
                { 
                    ConnectorSegment existingCover = covers[i];
                    if (cover.IsPointOnSegment(existingCover.A) && cover.IsPointOnSegment(existingCover.B)) 
                    {
                        //both points of vertical are on the new segment, delete the vertical and add the new one instead
                        covers.RemoveAt(i);
                        break; 
                    }
                    else if (existingCover.IsPointOnSegment(cover.A) && existingCover.IsPointOnSegment(cover.B)) 
                    { 
                        //both points of the new segment are on an existing segment already - skip this one
                        return; 
                    }
                }

                covers.Add(cover); 
            }
 
            //public ReadOnlyCollection VerticalCovers 
            //{
            //    get 
            //    {
            //        return this.verticalCovers.AsReadOnly();
            //    }
            //} 

            //public ReadOnlyCollection HorizontalCovers 
            //{ 
            //    get
            //    { 
            //        return this.horizontalCovers.AsReadOnly();
            //    }
            //}
 
            public void AddUsedEscapeLine(ConnectorSegment segment)
            { 
                this.usedEscapeLine.Add(segment); 
            }
 
            //the new escape point should not lay on any of the already used escape segments
            public bool EscapeLineHasBeenUsed(ConnectorSegment segment, Point escapePoint)
            {
 				for (int i = 0; i < this.usedEscapeLine.Count; i++) 
				{
 					ConnectorSegment usedSegment = this.usedEscapeLine[i]; 
                    if (usedSegment.IsPointOnSegment(escapePoint)) 
                        return true;
 				} 
                return false;
            }

            //get just the vertical covers for the given point out of all stored segments 
            //public List GetVerticalCovers(Point p)
            //{ 
            //    List covers = new List(); 
            //    foreach (ConnectorSegment segment in this.verticalCovers)
            //    { 
            //        if (segment.Covers(p))
            //            covers.Add(segment);
            //    }
            //    return covers; 
            //}
 
            //get just the horizontal covers for the given point out of all stored segments 
            //public List GetHorizontalCovers(Point p)
            //{ 
            //    List covers = new List();
            //    foreach (ConnectorSegment segment in this.horizontalCovers)
            //    {
            //        if (segment.Covers(p)) 
            //            covers.Add(segment);
            //    } 
            //    return covers; 
            //}
 
            //get the cover on the given side (closest cover to the given side) for the point out of all stored segments
            public ConnectorSegment GetCover(Point p, DesignerEdges side)
            {
                ConnectorSegment cover = null; 
                int distance = 0;
 
                if (side == DesignerEdges.Left || side == DesignerEdges.Right) 
                {
					for (int i = 0; i < this.verticalCovers.Count; i++) 
 					{
						ConnectorSegment segment = this.verticalCovers[i];
                        int currentDistance = (side == DesignerEdges.Left) ? p.X - segment.A.X : segment.A.X - p.X;
                        if (currentDistance > 0 && segment.Covers(p)) 
                        {
                            if (cover == null || distance > currentDistance) 
                            { 
                                cover = segment;
                                distance = currentDistance; 
                            }
                        }
                    }
                } 
                else
                { 
					for (int i = 0; i < this.horizontalCovers.Count; i++) 
					{
 						ConnectorSegment segment = this.horizontalCovers[i]; 
                        int currentDistance = (side == DesignerEdges.Bottom) ? p.Y - segment.A.Y : segment.A.Y - p.Y;
                        if (currentDistance > 0 && segment.Covers(p))
                        {
                            if (cover == null || distance > currentDistance) 
                            {
                                cover = segment; 
                                distance = currentDistance; 
                            }
                        } 
                    }
                }

                return cover; 
            }
 
            //get the cover on the given side (closest cover to the given side) for the point out of all stored segments 
            public List GetCovers(Point p, DesignerEdges side)
            { 
                List covers = new List();

                if (side == DesignerEdges.Left || side == DesignerEdges.Right)
                { 
					for (int i = 0; i < this.verticalCovers.Count; i++)
 					{ 
 						ConnectorSegment segment = this.verticalCovers[i]; 
                        int currentDistance = (side == DesignerEdges.Left) ? p.X - segment.A.X : segment.A.X - p.X;
                        if (currentDistance > 0 && segment.Covers(p)) 
                            covers.Add(segment);
                    }
                }
                else 
                {
					for (int i = 0; i < this.horizontalCovers.Count; i++) 
 					{ 
						ConnectorSegment segment = this.horizontalCovers[i];
                        int currentDistance = (side == DesignerEdges.Bottom) ? p.Y - segment.A.Y : segment.A.Y - p.Y; 
                        if (currentDistance > 0 && segment.Covers(p))
                            covers.Add(segment);
                    }
                } 

                return covers; 
            } 

            public bool IsEscapePoint(Point origin, Point escape, DesignerEdges side) 
            {
                //get the original cover
                ConnectorSegment originalCover = this.GetCover(origin, side);
                int originalDistance; 
                if (side == DesignerEdges.Left || side == DesignerEdges.Right)
                    originalDistance = originalCover.A.X - escape.X; 
                else 
                    originalDistance = originalCover.A.Y - escape.Y;
 
                // the escape point should not be covered by the the original cover
                if (originalCover.Covers(escape))
                    return false;
 
                //it should not also be covered by any member of other segments between the original cover and the original point
                List newCovers = this.GetCovers(escape, side); 
				for (int i = 0; i < newCovers.Count; i++) 
				{
 					ConnectorSegment newCover = newCovers[i]; 
                    //should never happen, just in case...
                    if (newCover == originalCover)
                        return false;
 
                    int newDistance;
                    if (side == DesignerEdges.Left || side == DesignerEdges.Right) 
                        newDistance = Math.Abs(newCover.A.X - escape.X); 
                    else
                        newDistance = Math.Abs(newCover.A.Y - escape.Y); 

                    if (Math.Sign(newDistance) == Math.Sign(originalDistance) && Math.Abs(newDistance) < Math.Abs(originalDistance))
                        return false;
                } 

                return true; 
            } 
        }
        #endregion 
    }
    #endregion
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK