зеркало из https://github.com/stride3d/GraphX.git
Parallel edge modifications for connection points (#77)
* Modified the grouping mechanism used for parallel edges. This is in preparation for some other changes related to the way edges with connection points are handled. These code changes will alter the sequence of edges and does not attempt to preserve the exact ordering of the old code. In the old code, the groups were built simply as the edges were encountered and did not appear to have a defined order, so I think this should be fine, but it could change the appearance of existing graphs. The new code also excludes edges that have both ends tied to connection points because those will not be spaced within the group anyway. There is a small performance penalty for very small numbers of edges, but at 50-100 edges a 3-10x speed improvement can be observed. * Modified the parallel edge logic so that it takes connection points into account. If an edge uses a connection point at only one end and parallel line grouping is in effect, the edge will be rendered parallel, but aligned to the connection point. * Restored the use of parallel edge groups with just one edge. This way, parallel edge behavior is applied to single edges with one connection point. * Edge Drag (#75) * + SetEdgesDrag * + Edge가 Drag 가능하도록 설정 * + Edge Drag * Fix * + PrepareEdgePathFromMousePointer Target Position Edit
This commit is contained in:
Родитель
5bb92f961c
Коммит
11d1ddd1c4
|
@ -242,6 +242,15 @@ namespace GraphX.Controls
|
|||
protected EdgeControlBase()
|
||||
{
|
||||
_updateLabelPosition = true;
|
||||
Loaded += EdgeControlBase_Loaded;
|
||||
}
|
||||
|
||||
private bool _isInDesignMode = false;
|
||||
|
||||
private void EdgeControlBase_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Loaded -= EdgeControlBase_Loaded;
|
||||
_isInDesignMode = CustomHelper.IsInDesignMode(this);
|
||||
}
|
||||
|
||||
private bool _updateLabelPosition;
|
||||
|
@ -595,16 +604,13 @@ namespace GraphX.Controls
|
|||
/// <param name="source">Source vertex</param>
|
||||
/// <param name="target">Target vertex</param>
|
||||
/// <param name="sideDistance">Distance between edges</param>
|
||||
internal virtual Point GetParallelOffset(VertexControl source, VertexControl target, int sideDistance)
|
||||
internal virtual Point GetParallelOffset(Point sourceCenter, Point targetCenter, int sideDistance)
|
||||
{
|
||||
var sourcepos = source.GetPosition();
|
||||
var targetpos = target.GetPosition();
|
||||
|
||||
var mainVector = new Vector(targetpos.X - sourcepos.X, targetpos.Y - sourcepos.Y);
|
||||
var mainVector = new Vector(targetCenter.X - sourceCenter.X, targetCenter.Y - sourceCenter.Y);
|
||||
//get new point coordinate
|
||||
var joint = new Point(
|
||||
sourcepos.X + source.DesiredSize.Width * .5 + sideDistance * (mainVector.Y / mainVector.Length),
|
||||
sourcepos.Y + source.DesiredSize.Height * .5 - sideDistance * (mainVector.X / mainVector.Length));
|
||||
sourceCenter.X + sideDistance * (mainVector.Y / mainVector.Length),
|
||||
sourceCenter.Y - sideDistance * (mainVector.X / mainVector.Length));
|
||||
return joint;
|
||||
}
|
||||
|
||||
|
@ -892,37 +898,42 @@ namespace GraphX.Controls
|
|||
if ((Visibility != Visibility.Visible && !IsHiddenEdgesUpdated) && this.Source == null || this.Target == null || ManualDrawing || !IsTemplateLoaded) return;
|
||||
|
||||
#region Get the inputs
|
||||
// Get the TopLeft position of the Source Vertex.
|
||||
var sourceTopLeft = new Point(
|
||||
(useCurrentCoords ? GraphAreaBase.GetX(Source) : GraphAreaBase.GetFinalX(Source)),
|
||||
(useCurrentCoords ? GraphAreaBase.GetY(Source) : GraphAreaBase.GetFinalY(Source)));
|
||||
|
||||
// Get the TopLeft position of the Target Vertex.
|
||||
var targetTopLeft = new Point(
|
||||
(useCurrentCoords ? GraphAreaBase.GetX(Target) : GraphAreaBase.GetFinalX(Target)),
|
||||
(useCurrentCoords ? GraphAreaBase.GetY(Target) : GraphAreaBase.GetFinalY(Target)));
|
||||
|
||||
//get the size of the source
|
||||
var sourceSize = new Size
|
||||
{
|
||||
Width = this.Source.ActualWidth,
|
||||
Height = this.Source.ActualHeight
|
||||
};
|
||||
if (CustomHelper.IsInDesignMode(this)) sourceSize = new Size(80, 20);
|
||||
|
||||
//get the position center of the source
|
||||
var sourcePos = new Point
|
||||
{
|
||||
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)) + sourceSize.Width * .5,
|
||||
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source)) + sourceSize.Height * .5
|
||||
};
|
||||
Size sourceSize;
|
||||
if (_isInDesignMode)
|
||||
sourceSize = new Size(80, 20);
|
||||
else
|
||||
sourceSize = new Size(Source.ActualWidth, Source.ActualHeight);
|
||||
|
||||
//get the size of the target
|
||||
var targetSize = new Size
|
||||
{
|
||||
Width = this.Target.ActualWidth,
|
||||
Height = this.Target.ActualHeight
|
||||
};
|
||||
if (CustomHelper.IsInDesignMode(this))
|
||||
Size targetSize;
|
||||
if (_isInDesignMode)
|
||||
|
||||
targetSize = new Size(80, 20);
|
||||
else
|
||||
targetSize = new Size(Target.ActualWidth, Target.ActualHeight);
|
||||
|
||||
//get the position center of the source
|
||||
var sourceCenter = new Point(
|
||||
sourceTopLeft.X + sourceSize.Width * .5,
|
||||
sourceTopLeft.Y + sourceSize.Height * .5);
|
||||
|
||||
//get the position center of the target
|
||||
var targetPos = new Point
|
||||
{
|
||||
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Target) : GraphAreaBase.GetFinalX(this.Target)) + targetSize.Width * .5,
|
||||
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Target) : GraphAreaBase.GetFinalY(this.Target)) + targetSize.Height * .5
|
||||
};
|
||||
|
||||
var targetCenter = new Point(
|
||||
targetTopLeft.X + targetSize.Width * .5,
|
||||
targetTopLeft.Y + targetSize.Height * .5);
|
||||
|
||||
|
||||
var routedEdge = Edge as IRoutingInfo;
|
||||
if (routedEdge == null)
|
||||
|
@ -931,19 +942,6 @@ namespace GraphX.Controls
|
|||
//get the route informations
|
||||
var routeInformation = externalRoutingPoints ?? routedEdge.RoutingPoints;
|
||||
|
||||
// Get the TopLeft position of the Source Vertex.
|
||||
var sourcePos1 = new Point
|
||||
{
|
||||
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)),
|
||||
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source))
|
||||
};
|
||||
// Get the TopLeft position of the Target Vertex.
|
||||
var targetPos1 = new Point
|
||||
{
|
||||
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Target) : GraphAreaBase.GetFinalX(this.Target)),
|
||||
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Target) : GraphAreaBase.GetFinalY(this.Target))
|
||||
};
|
||||
|
||||
var hasEpSource = EdgePointerForSource != null;
|
||||
var hasEpTarget = EdgePointerForTarget != null;
|
||||
|
||||
|
@ -952,59 +950,114 @@ namespace GraphX.Controls
|
|||
//if self looped edge
|
||||
if (IsSelfLooped)
|
||||
{
|
||||
PrepareSelfLoopedEdge(sourcePos1);
|
||||
PrepareSelfLoopedEdge(sourceTopLeft);
|
||||
return;
|
||||
}
|
||||
|
||||
//check if we have some edge route data
|
||||
var hasRouteInfo = routeInformation != null && routeInformation.Length > 1;
|
||||
|
||||
//calculate source and target edge attach points
|
||||
if (RootArea != null && !hasRouteInfo && RootArea.EnableParallelEdges && ParallelEdgeOffset != 0)
|
||||
{
|
||||
sourcePos = GetParallelOffset(this.Source, this.Target, ParallelEdgeOffset);
|
||||
targetPos = GetParallelOffset(this.Target, this.Source, -ParallelEdgeOffset);
|
||||
}
|
||||
|
||||
/* Rectangular shapes implementation by bleibold */
|
||||
|
||||
var gEdge = Edge as IGraphXCommonEdge;
|
||||
Point p1;
|
||||
Point p2;
|
||||
|
||||
#region Helper lambda expressions
|
||||
Func<IVertexConnectionPoint> getSourceCpOrThrow = () =>
|
||||
{
|
||||
var cp = Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
|
||||
if (cp == null)
|
||||
throw new GX_ObjectNotFoundException(string.Format("Can't find source vertex VCP by edge source connection point Id({1}) : {0}", Source, gEdge.SourceConnectionPointId));
|
||||
return cp;
|
||||
};
|
||||
Func<IVertexConnectionPoint> getTargetCpOrThrow = () =>
|
||||
{
|
||||
var cp = Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
|
||||
if (cp == null)
|
||||
throw new GX_ObjectNotFoundException(string.Format("Can't find target vertex VCP by edge target connection point Id({1}) : {0}", Target, gEdge.TargetConnectionPointId));
|
||||
return cp;
|
||||
};
|
||||
Func<IVertexConnectionPoint, Point, Point, Point> getCpEndPoint = (cp, cpCenter, distantEnd) =>
|
||||
{
|
||||
// If the connection point (cp) doesn't have any shape, the edge comes from its center, otherwise find the location
|
||||
// on its perimeter that the edge should come from.
|
||||
Point calculatedCp;
|
||||
if (cp.Shape == VertexShape.None)
|
||||
calculatedCp = cpCenter;
|
||||
else
|
||||
calculatedCp = GeometryHelper.GetEdgeEndpoint(cpCenter, cp.RectangularSize, distantEnd, cp.Shape);
|
||||
return calculatedCp;
|
||||
};
|
||||
Func<bool> needParallelCalc = () => RootArea != null && !hasRouteInfo && RootArea.EnableParallelEdges && IsParallel;
|
||||
#endregion
|
||||
|
||||
//calculate edge source (p1) and target (p2) endpoints based on different settings
|
||||
if (gEdge?.SourceConnectionPointId != null)
|
||||
if (gEdge?.SourceConnectionPointId != null && gEdge?.TargetConnectionPointId != null)
|
||||
{
|
||||
var sourceCp = this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
|
||||
if (sourceCp == null)
|
||||
throw new GX_ObjectNotFoundException(string.Format("Can't find source vertex VCP by edge source connection point Id({1}) : {0}", this.Source, gEdge.SourceConnectionPointId));
|
||||
if (sourceCp.Shape == VertexShape.None) p1 = sourceCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var targetCpPos = gEdge.TargetConnectionPointId.HasValue ? this.Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true).RectangularSize.Center() : (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos));
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourceCp.RectangularSize.Center(), sourceCp.RectangularSize, targetCpPos, sourceCp.Shape);
|
||||
}
|
||||
}
|
||||
else
|
||||
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new SysRect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), this.Source.VertexShape);
|
||||
// Get the connection points and their centers
|
||||
var sourceCp = getSourceCpOrThrow();
|
||||
var targetCp = getTargetCpOrThrow();
|
||||
var sourceCpCenter = sourceCp.RectangularSize.Center();
|
||||
var targetCpCenter = targetCp.RectangularSize.Center();
|
||||
|
||||
if (gEdge?.TargetConnectionPointId != null)
|
||||
{
|
||||
var targetCp = this.Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
|
||||
if (targetCp == null)
|
||||
throw new GX_ObjectNotFoundException(string.Format("Can't find target vertex VCP by edge target connection point Id({1}) : {0}", this.Target, gEdge.TargetConnectionPointId));
|
||||
if (targetCp.Shape == VertexShape.None) p2 = targetCp.RectangularSize.Center();
|
||||
else
|
||||
{
|
||||
var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetCp.RectangularSize.Center(), targetCp.RectangularSize, sourceCpPos, targetCp.Shape);
|
||||
SourceConnectionPoint = getCpEndPoint(sourceCp, sourceCpCenter, targetCpCenter);
|
||||
TargetConnectionPoint = getCpEndPoint(targetCp, targetCpCenter, sourceCpCenter);
|
||||
}
|
||||
}
|
||||
else
|
||||
p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new SysRect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), this.Target.VertexShape);
|
||||
else if (gEdge?.SourceConnectionPointId != null)
|
||||
{
|
||||
var sourceCp = getSourceCpOrThrow();
|
||||
var sourceCpCenter = sourceCp.RectangularSize.Center();
|
||||
|
||||
SourceConnectionPoint = p1;
|
||||
TargetConnectionPoint = p2;
|
||||
// In the case of parallel edges, the target direction needs to be found and the correct offset calculated. Otherwise, fall back
|
||||
// to route information or simply the center of the target vertex.
|
||||
if (needParallelCalc())
|
||||
{
|
||||
targetCenter = sourceCpCenter + (targetCenter - sourceCenter);
|
||||
}
|
||||
else if (hasRouteInfo)
|
||||
{
|
||||
targetCenter = routeInformation[1].ToWindows();
|
||||
}
|
||||
|
||||
SourceConnectionPoint = getCpEndPoint(sourceCp, sourceCpCenter, targetCenter);
|
||||
TargetConnectionPoint = GeometryHelper.GetEdgeEndpoint(targetCenter, new SysRect(targetTopLeft, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : sourceCpCenter, Target.VertexShape);
|
||||
}
|
||||
else if (gEdge?.TargetConnectionPointId != null)
|
||||
{
|
||||
var targetCp = getTargetCpOrThrow();
|
||||
var targetCpCenter = targetCp.RectangularSize.Center();
|
||||
|
||||
// In the case of parallel edges, the source direction needs to be found and the correct offset calculated. Otherwise, fall back
|
||||
// to route information or simply the center of the source vertex.
|
||||
if (needParallelCalc())
|
||||
{
|
||||
sourceCenter = targetCpCenter + (sourceCenter - targetCenter);
|
||||
}
|
||||
else if (hasRouteInfo)
|
||||
{
|
||||
sourceCenter = routeInformation[routeInformation.Length - 2].ToWindows();
|
||||
}
|
||||
|
||||
SourceConnectionPoint = GeometryHelper.GetEdgeEndpoint(sourceCenter, new SysRect(sourceTopLeft, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : targetCpCenter), Source.VertexShape);
|
||||
TargetConnectionPoint = getCpEndPoint(targetCp, targetCpCenter, sourceCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
//calculate source and target edge attach points
|
||||
if (needParallelCalc())
|
||||
{
|
||||
var origSC = sourceCenter;
|
||||
var origTC = targetCenter;
|
||||
sourceCenter = GetParallelOffset(origSC, origTC, ParallelEdgeOffset);
|
||||
targetCenter = GetParallelOffset(origTC, origSC, -ParallelEdgeOffset);
|
||||
}
|
||||
SourceConnectionPoint = GeometryHelper.GetEdgeEndpoint(sourceCenter, new SysRect(sourceTopLeft, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetCenter)), Source.VertexShape);
|
||||
TargetConnectionPoint = GeometryHelper.GetEdgeEndpoint(targetCenter, new SysRect(targetTopLeft, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourceCenter), Target.VertexShape);
|
||||
}
|
||||
|
||||
// If the logic above is working correctly, both the source and target connection points will exist.
|
||||
if (!SourceConnectionPoint.HasValue || !TargetConnectionPoint.HasValue)
|
||||
throw new GX_GeneralException("One or both connection points was not found due to an internal error.");
|
||||
|
||||
var p1 = SourceConnectionPoint.Value;
|
||||
var p2 = TargetConnectionPoint.Value;
|
||||
|
||||
Linegeometry = new PathGeometry();
|
||||
PathFigure lineFigure;
|
||||
|
|
|
@ -1546,12 +1546,16 @@ namespace GraphX.Controls
|
|||
//setup path
|
||||
if (_svShowEdgeLabels == true)
|
||||
edgectrl.SetCurrentValue(EdgeControlBase.ShowLabelProperty, true);
|
||||
edgectrl.PrepareEdgePath();
|
||||
}
|
||||
|
||||
if (LogicCore.EnableParallelEdges)
|
||||
UpdateParallelEdgesData();
|
||||
|
||||
foreach (var item in _edgeslist)
|
||||
{
|
||||
item.Value.PrepareEdgePath();
|
||||
}
|
||||
|
||||
GenerateEdgeLabels();
|
||||
}
|
||||
|
||||
|
@ -1574,77 +1578,51 @@ namespace GraphX.Controls
|
|||
public virtual void UpdateParallelEdgesData(Dictionary<TEdge, EdgeControl> edgeList = null)
|
||||
{
|
||||
edgeList = edgeList ?? _edgeslist;
|
||||
var usedIds = edgeList.Count > 20 ? new HashSet<long>() as ICollection<long> : new List<long>();
|
||||
|
||||
//clear IsParallel flag
|
||||
edgeList.Values.ForEach(a => a.IsParallel = false);
|
||||
|
||||
foreach (var item in edgeList)
|
||||
{
|
||||
if (usedIds.Contains(item.Key.ID) || !item.Value.CanBeParallel) continue;
|
||||
var list = new List<EdgeControl> { item.Value };
|
||||
//that list will contain checks for edges that goes form target to source
|
||||
var cList = new List<bool> { false };
|
||||
foreach (var edge in edgeList)
|
||||
{
|
||||
//skip the same edge
|
||||
if (item.Key.ID == edge.Key.ID) continue;
|
||||
//add source to target edge
|
||||
if (edge.Value.CanBeParallel && ((item.Key.Source.ID == edge.Key.Source.ID && item.Key.Target.ID == edge.Key.Target.ID)))
|
||||
{
|
||||
list.Add(edge.Value);
|
||||
cList.Add(false);
|
||||
}
|
||||
//add target to source edge and remember the check
|
||||
if (item.Key.Source.ID == edge.Key.Target.ID && item.Key.Target.ID == edge.Key.Source.ID)
|
||||
{
|
||||
cList.Add(true);
|
||||
list.Add(edge.Value);
|
||||
}
|
||||
//else cList.Add(false);
|
||||
}
|
||||
// Group edges together that share the same source and target. Edges that have both a source and target connection point defined are excluded. Self
|
||||
// looped edges are excluded. Edges marked with CanBeParallel == false are excluded. Edges with a connection point are pushed to the end of the group
|
||||
// and will be marked as parallel, but their offsets end up overridden during rendering.
|
||||
var edgeGroups =
|
||||
(from edge in edgeList
|
||||
where edge.Value.CanBeParallel && !edge.Key.IsSelfLoop && (!edge.Key.SourceConnectionPointId.HasValue || !edge.Key.TargetConnectionPointId.HasValue)
|
||||
group edge by new Tuple<long, long>(Math.Min(edge.Key.Source.ID, edge.Key.Target.ID), Math.Max(edge.Key.Source.ID, edge.Key.Target.ID)) into edgeGroup
|
||||
select edgeGroup.OrderBy(e => e.Key.SourceConnectionPointId.HasValue || e.Key.TargetConnectionPointId.HasValue ? 1 : 0).ToList())
|
||||
.ToList();
|
||||
|
||||
//do stuff
|
||||
if (list.Count > 1)
|
||||
foreach (var list in edgeGroups)
|
||||
{
|
||||
//trigger to show in which side to step distance
|
||||
bool viceversa = false;
|
||||
//check if total number of edges is even or not
|
||||
bool even = (list.Count % 2) == 0;
|
||||
//get the resulting step distance for the case
|
||||
int distance = even ? (int)(LogicCore.ParallelEdgeDistance * .5) : LogicCore.ParallelEdgeDistance;
|
||||
var first = list[0];
|
||||
|
||||
//leave first edge intact if we have not even edges count
|
||||
for (int i = even ? 0 : 1; i < list.Count; i++)
|
||||
// Alternate sides with each step
|
||||
int viceversa = 1;
|
||||
// Check if total number of edges without connection points is even or not
|
||||
bool even = (list.TakeWhile(e => !e.Key.SourceConnectionPointId.HasValue && !e.Key.TargetConnectionPointId.HasValue).Count() % 2) == 0;
|
||||
// For even numbers of edges, initial offset is a half step from the center
|
||||
int initialOffset = even ? LogicCore.ParallelEdgeDistance / 2 : 0;
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var kvp = list[i];
|
||||
kvp.Value.IsParallel = true;
|
||||
|
||||
var offset = viceversa * (initialOffset + LogicCore.ParallelEdgeDistance * ((i + (even ? 0 : 1)) / 2));
|
||||
//if source to target edge
|
||||
if (!cList[i])
|
||||
if (kvp.Key.Source == first.Key.Source)
|
||||
{
|
||||
list[i].ParallelEdgeOffset = (viceversa ? -distance : distance) * (1 + ((even ? i : i - 1) / 2));
|
||||
//list[i].TargetOffset = -list[i].ParallelEdgeOffset;
|
||||
kvp.Value.ParallelEdgeOffset = offset;
|
||||
}
|
||||
else //if target to source edge - just switch offsets
|
||||
{
|
||||
list[i].ParallelEdgeOffset = -((viceversa ? -distance : distance) * (1 + ((even ? i : i - 1) / 2)));
|
||||
//list[i].ParallelEdgeOffset = -list[i].TargetOffset;
|
||||
kvp.Value.ParallelEdgeOffset = -offset;
|
||||
}
|
||||
//change trigger to opposite
|
||||
viceversa = !viceversa;
|
||||
list[i].IsParallel = true;
|
||||
viceversa = -viceversa;
|
||||
}
|
||||
}
|
||||
|
||||
//remember used edges IDs
|
||||
list.ForEach(a =>
|
||||
{
|
||||
var edge = a.Edge as TEdge;
|
||||
if (edge != null) usedIds.Add(edge.ID);
|
||||
});
|
||||
list.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace GraphX.Controls
|
|||
var position = TranslatePoint(new Point(), VertexControl);
|
||||
var vPos = VertexControl.GetPosition();
|
||||
position = new Point(position.X + vPos.X, position.Y + vPos.Y);
|
||||
RectangularSize = new Rect(position, DesiredSize);
|
||||
RectangularSize = new Rect(position, new Size(ActualWidth, ActualHeight));
|
||||
}
|
||||
|
||||
#elif METRO
|
||||
|
|
|
@ -200,7 +200,9 @@ namespace GraphX.Controls
|
|||
/// <param name="round"></param>
|
||||
public Point GetPosition(bool final = false, bool round = false)
|
||||
{
|
||||
return round ? new Point(final ? (int)GraphAreaBase.GetFinalX(this) : (int)GraphAreaBase.GetX(this), final ? (int)GraphAreaBase.GetFinalY(this) : (int)GraphAreaBase.GetY(this)) : new Point(final ? GraphAreaBase.GetFinalX(this) : GraphAreaBase.GetX(this), final ? GraphAreaBase.GetFinalY(this) : GraphAreaBase.GetY(this));
|
||||
return round ?
|
||||
new Point(final ? (int)GraphAreaBase.GetFinalX(this) : (int)GraphAreaBase.GetX(this), final ? (int)GraphAreaBase.GetFinalY(this) : (int)GraphAreaBase.GetY(this)) :
|
||||
new Point(final ? GraphAreaBase.GetFinalX(this) : GraphAreaBase.GetX(this), final ? GraphAreaBase.GetFinalY(this) : GraphAreaBase.GetY(this));
|
||||
}
|
||||
/// <summary>
|
||||
/// Get control position on the GraphArea panel in attached coords X and Y (GraphX type version)
|
||||
|
|
|
@ -33,28 +33,29 @@ namespace GraphX.Controls
|
|||
/// <returns></returns>
|
||||
public static Vector? Intersects(Vector a1, Vector a2, Vector b1, Vector b2)
|
||||
{
|
||||
var b = a2 - a1;
|
||||
var d = b2 - b1;
|
||||
var bDotDPerp = b.X * d.Y - b.Y * d.X;
|
||||
var a = a2 - a1;
|
||||
var b = b2 - b1;
|
||||
var aDotBPerp = a.X * b.Y - a.Y * b.X;
|
||||
|
||||
// if b dot d == 0, it means the lines are parallel so have infinite intersection points
|
||||
if (bDotDPerp == 0)
|
||||
// if a dot b == 0, it means the lines are parallel so have infinite intersection points
|
||||
if (aDotBPerp == 0)
|
||||
return null;
|
||||
|
||||
var c = b1 - a1;
|
||||
var t = (c.X * d.Y - c.Y * d.X) / bDotDPerp;
|
||||
if (t < 0 || t > 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var u = (c.X * b.Y - c.Y * b.X) / bDotDPerp;
|
||||
// The intersection must fall within the line segment defined by the b1 and b2 endpoints.
|
||||
var u = (c.X * a.Y - c.Y * a.X) / aDotBPerp;
|
||||
if (u < 0 || u > 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return a1 + t * b;
|
||||
// The intersection point IS allowed to fall outside of the line segment defined by the a1 and a2
|
||||
// endpoints, anywhere along the infinite line. When this is used to find the intersection of an
|
||||
// Edge as line a and Vertex side as line b, it allows the Edge to be elongated to the intersection.
|
||||
var t = (c.X * b.Y - c.Y * b.X) / aDotBPerp;
|
||||
|
||||
return a1 + t * a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -376,30 +377,45 @@ namespace GraphX.Controls
|
|||
|
||||
public static Point GetEdgeEndpointOnRectangle(Point sourcePos, Rect sourceBounds, Point targetPos, double angle = 0)
|
||||
{
|
||||
var tgt_pt = targetPos;
|
||||
if (angle != 0)
|
||||
tgt_pt = MathHelper.RotatePoint(targetPos, sourceBounds.Center(), -angle);
|
||||
Func<Point, double, Point> rotate = (p, a) => angle == 0.0 ? p : MathHelper.RotatePoint(p, sourceBounds.Center(), a);
|
||||
|
||||
var tgt_pt = rotate(targetPos, -angle);
|
||||
|
||||
if (tgt_pt.X <= sourcePos.X)
|
||||
{
|
||||
var leftSide = Intersects(sourcePos.ToVector(), tgt_pt.ToVector(), sourceBounds.TopLeft().ToVector(), sourceBounds.BottomLeft().ToVector());
|
||||
var bottomSide = Intersects(sourcePos.ToVector(), tgt_pt.ToVector(), sourceBounds.BottomLeft().ToVector(), sourceBounds.BottomRight().ToVector());
|
||||
if (leftSide.HasValue)
|
||||
{
|
||||
return rotate(new Point(leftSide.Value.X, leftSide.Value.Y), angle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var rightSide = Intersects(sourcePos.ToVector(), tgt_pt.ToVector(), sourceBounds.TopRight().ToVector(), sourceBounds.BottomRight().ToVector());
|
||||
if (rightSide.HasValue)
|
||||
{
|
||||
return rotate(new Point(rightSide.Value.X, rightSide.Value.Y), angle);
|
||||
}
|
||||
}
|
||||
|
||||
if (tgt_pt.Y <= sourcePos.Y)
|
||||
{
|
||||
var topSide = Intersects(sourcePos.ToVector(), tgt_pt.ToVector(), sourceBounds.TopLeft().ToVector(), sourceBounds.TopRight().ToVector());
|
||||
if (topSide.HasValue)
|
||||
{
|
||||
return rotate(new Point(topSide.Value.X, topSide.Value.Y), angle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var bottomSide = Intersects(sourcePos.ToVector(), tgt_pt.ToVector(), sourceBounds.BottomLeft().ToVector(), sourceBounds.BottomRight().ToVector());
|
||||
if (bottomSide.HasValue)
|
||||
{
|
||||
return rotate(new Point(bottomSide.Value.X, bottomSide.Value.Y), angle);
|
||||
}
|
||||
}
|
||||
|
||||
var pt = new Point(sourcePos.X, sourcePos.Y);
|
||||
|
||||
// Get the rectangle side where intersection of the proposed Edge path occurred.
|
||||
if (leftSide != null)
|
||||
pt = new Point(leftSide.Value.X, leftSide.Value.Y);
|
||||
else if (bottomSide != null)
|
||||
pt = new Point(bottomSide.Value.X, bottomSide.Value.Y);
|
||||
else if (rightSide != null)
|
||||
pt = new Point(rightSide.Value.X, rightSide.Value.Y);
|
||||
else if (topSide != null)
|
||||
pt = new Point(topSide.Value.X, topSide.Value.Y);
|
||||
|
||||
if ((leftSide != null || bottomSide != null || rightSide != null || topSide != null) && angle != 0)
|
||||
pt = MathHelper.RotatePoint(pt, sourceBounds.Center(), angle);
|
||||
return pt;
|
||||
return rotate(new Point(sourcePos.X, sourcePos.Y), angle);
|
||||
}
|
||||
|
||||
public static PathFigure GenerateOldArrow(Point ip1, Point ip2)
|
||||
|
|
Загрузка…
Ссылка в новой задаче