public ConnectionLine CalculateBestConnection(Area fromArea, Area destinationArea, Func<Area, ProximityTestResult> isOverlappingWithOtherControls) { var fromConnectors = GetAllConnectorPoints(fromArea, false); var toLineEndConnectors = GetAllConnectorPoints(destinationArea, true); var toArrowTipConnectors = GetAllConnectorPoints(destinationArea, false); // Logger.Instance.WriteEntry("Finding Best Connection Line from {0} to {1}", (fromArea.Tag ?? fromArea.TopLeft.ToString()), (destinationArea.Tag ?? destinationArea.TopLeft.ToString())); // Logger.Instance.WriteEntry(" From: {0} To: {1}", fromArea, destinationArea); double angle = fromArea.Centre.AngleToPointInDegrees(destinationArea.Centre); var idealPoint = fromArea.CalculateCircumferenceIntersectionPoint(angle); var chosenfromConnector = fromConnectors.Select(fromConnector => new Tuple<KeyValuePair<int, Point>, double>(fromConnector, fromConnector.Value.DistanceTo(idealPoint))) .OrderBy(x => x.Item2) .First(); var toConnectorsInOrderOfPreference = toLineEndConnectors.Select(toConnector => new Tuple<KeyValuePair<int, Point>, double>(toConnector, toConnector.Value.DistanceTo(idealPoint))) .OrderBy(x => x.Item2) .ToList(); // This code checks to see if the arrow head is overlapping with an existing diagram element. (not really needed) //int chosenToIndex = 0; //for (int toConnectorIndex = 0; toConnectorIndex < toConnectors.Length; toConnectorIndex++) //{ // var toConnector = toConnectorsInOrderOfPreference[toConnectorIndex]; // Area arrowheadArea = ArrowHead.GetArea(CalculateExitAngle(toConnectorIndex), toConnector.Item1); // ProximityTestResult proximityTestResult = isOverlappingWithOtherControls(arrowheadArea); // if (proximityTestResult.Proximity == Proximity.NotOverlapping || proximityTestResult.Proximity == Proximity.VeryClose) // { // chosenToIndex = toConnectorIndex; // break; // } //} return new ConnectionLine { Distance = chosenfromConnector.Item1.Value.DistanceTo(toConnectorsInOrderOfPreference[0].Item1.Value), From = chosenfromConnector.Item1.Value, To = toArrowTipConnectors[toConnectorsInOrderOfPreference[0].Item1.Key], // toConnectorsInOrderOfPreference[0].Item1.Value, // Incorrect ToLineEnd = toConnectorsInOrderOfPreference[0].Item1.Value, ExitAngle = CalculateExitAngle(toConnectorsInOrderOfPreference[0].Item1.Key) }; }
/// <summary> /// Calculate the best line connecting <see cref="fromArea"/> to <see cref="destinationArea"/> /// </summary> /// <param name="fromArea"> /// The from Area. /// </param> /// <param name="destinationArea"> /// The destination Area. /// </param> /// <param name="isOverlappingWithOtherControls"> /// The is Overlapping With Other Controls. /// </param> /// <returns> /// The <see cref="ConnectionLine"/>. /// </returns> public ConnectionLine CalculateBestConnection(Area fromArea, Area destinationArea, Func<Area, ProximityTestResult> isOverlappingWithOtherControls) { double fromAngle = fromArea.Centre.AngleToPointInDegrees(destinationArea.Centre); Point fromIdealPoint = fromArea.CalculateCircumferenceIntersectionPoint(fromAngle); double toAngle = TrigHelper.InverseAngle(fromAngle); Point toIdealPoint = destinationArea.CalculateCircumferenceIntersectionPoint(toAngle); Point toLineEnd = toIdealPoint; // Leave room for arrow head double offset1 = (ArrowHead.ArrowWidth - 2) * Math.Sin(CalculateLineAngleToPreviousAxis(toAngle)); double offset2 = (ArrowHead.ArrowWidth - 2) * Math.Cos(CalculateLineAngleToPreviousAxis(toAngle)); if (toAngle >= 0 && toAngle < 90) { toLineEnd.Offset(offset1, -offset2); } else if (toAngle >= 90 && toAngle <= 180) { toLineEnd.Offset(offset2, offset1); } else if (toAngle > 180 && toAngle < 270) { toLineEnd.Offset(-offset1, offset2); } else { toLineEnd.Offset(-offset2, -offset1); } return new ConnectionLine { Distance = fromIdealPoint.DistanceTo(toIdealPoint), ExitAngle = CalculateExitAngle(fromAngle), From = fromIdealPoint, To = toIdealPoint, ToLineEnd = toLineEnd, ToAngle = toAngle, FromAngle = fromAngle, }; }