/// <summary> /// Calculates visibility coefficient of the two edges. /// </summary> /// /// <param name="ed1"> /// First edge to be used in calculation /// </param> /// /// <param name="ed2"> /// Second edge to be used in calculation /// </param> /// /// <returns> /// Compatibility coefficient ranging from 0 to 1 /// </returns> private float VisibilityCoefficient(EdgeGroupData ed1, EdgeGroupData ed2) { float c; var p1 = ed1.V1; var p2 = ed1.V2; var q1 = ed2.V1; var q2 = ed2.V2; var pn = new Point(); pn.X = p1.Y - p2.Y; pn.Y = p2.X - p1.X; var pn1 = VectorTools.Plus(pn, p1); var pn2 = VectorTools.Plus(pn, p2); var i1 = new Point(); var i2 = new Point(); float r1 = 0, r2 = 0; if (!Intersects(q1, q2, p1, pn1, ref i1)) { return(0); } Intersects(q1, q2, p2, pn2, ref i2); if ((r1 < 0 && r2 < 0) || (r1 > 1 && r2 > 1)) { return(0); } var im = VectorTools.MidPoint(i1, i2); var qm = ed2.Middle; var i = VectorTools.Distance(i1, i2); var m = VectorTools.Distance(qm, im); if (i == 0) { return(0); } c = 1f - 2f * m / i; if (c < 0) { return(0); } else { return(c); } }
/// <summary> /// Calculates position compatibility of the two edges /// </summary> /// /// <param name="ed1"> /// First edge to be used in calculation /// </param> /// /// <param name="ed2"> /// Second edge to be used in calculation /// </param> /// /// <returns> /// Position compatibility coefficient ranging from 0 to 1 /// </returns> private float PositionCompatibility(EdgeGroupData ed1, EdgeGroupData ed2) { var avg = (ed1.Length + ed2.Length) / 2; var dis = VectorTools.Distance(ed1.Middle, ed2.Middle); if ((avg + dis) == 0) { return(0); } return(avg / (avg + dis)); }
/// <summary> /// Calculates directedness of the two edges. /// </summary> /// /// <param name="ed1"> /// First edge to be used in calculation /// </param> /// /// <param name="ed2"> /// Second edge to be used in calculation /// </param> /// /// <returns> /// True if edges have roughly the same direction, false otherwise /// </returns> private bool CalculateDirectedness(EdgeGroupData ed1, EdgeGroupData ed2) { if ((VectorTools.Distance(ed1.V1, ed2.V1) + VectorTools.Distance(ed1.V2, ed2.V2)) < (VectorTools.Distance(ed1.V1, ed2.V2) + VectorTools.Distance(ed1.V2, ed2.V1))) { return(true); } else { return(false); } }
/// <summary> /// Straightens the edges using internal data sturctures /// </summary> /// /// <param name="groupsToStraighten"> /// Groups of edges that should be straightened /// </param> /// /// <param name="s"> /// Specifies the amount of straightening, from 0 to 1 /// </param> private void StraightenEdgesInternally(Dictionary <KeyPair, EdgeGroupData> groupsToStraighten, float s) { foreach (var ed in groupsToStraighten.Values) { for (var i = 0; i < _subdivisionPoints; i++) { //STRONG CHANGE var p = ed.ControlPoints[i]; p = VectorTools.Plus(VectorTools.Multiply(p, 1 - s), VectorTools.Multiply(VectorTools.Plus(ed.V1, VectorTools.Multiply(VectorTools.Minus(ed.V2, ed.V1), 1.0f * (i + 1) / (_subdivisionPoints + 1))), s)); ed.ControlPoints[i].X = p.X; ed.ControlPoints[i].Y = p.Y; } } }
/// <summary> /// Finds an intersection point of the two lines /// </summary> /// /// <param name="p1"> /// First point of the first line /// </param> /// /// <param name="p2"> /// Second point of the first line /// </param> /// /// <param name="q1"> /// First point of the second line /// </param> /// /// <param name="q2"> /// Second point of the second line /// </param> /// /// <param name="intersection"> /// Point of intersection /// </param> /// <returns> /// True if lines are not parallel, false otherwise /// </returns> private bool Intersects(Point p1, Point p2, Point q1, Point q2, ref Point intersection) { var q = (p1.Y - q1.Y) * (q2.X - q1.X) - (p1.X - q1.X) * (q2.Y - q1.Y); var d = (p2.X - p1.X) * (q2.Y - q1.Y) - (p2.Y - p1.Y) * (q2.X - q1.X); if (d == 0) // parallel lines { return(false); } var r = q / d; /*q = (p1.Y - q1.Y) * (p2.X - p1.X) - (p1.X - q1.X) * (p2.Y - p1.Y); * var s = q / d;*/ intersection = VectorTools.Plus(p1, VectorTools.Multiply(VectorTools.Minus(p2, p1), r)); return(true); }
/// <summary> /// Divides an edge into segments by adding subdivision points to it /// </summary> /// /// <param name="ed"> /// Edge data that is used for creating new subdivision points /// </param> /// /// <param name="subdivisionPointsNum"> /// Number of subdivision points that should be created /// </param> private void DivideEdge(EdgeGroupData ed, int subdivisionPointsNum) { var r = ed.Length / (subdivisionPointsNum + 1); var sPoints = new Point[subdivisionPointsNum]; ed.NewControlPoints = new Point[subdivisionPointsNum]; Point move; move = ed.Length == 0 ? new Point(0, 0) : VectorTools.Multiply(VectorTools.Minus(ed.V2, ed.V1), 1f / ed.Length); for (var i = 0; i < subdivisionPointsNum; i++) { sPoints[i] = VectorTools.Plus(ed.V1, VectorTools.Multiply(move, r * (i + 1))); } ed.ControlPoints = sPoints; ed.K = _springConstant * (subdivisionPointsNum + 1) / ed.Length; if (ed.K > 0.5f) { ed.K = 0.5f; } }
/// <summary> /// Collects data from the specified edge. /// Used for edges that already have control points metadata. /// </summary> /// /// <param name="e"> /// Edge to collect data from /// </param> private void AddExistingData(TEdge e) { EdgeGroupData ed; var key = new KeyPair(e.Source.ID, e.Target.ID); _edgeGroupData.TryGetValue(key, out ed); if (ed == null) { var p1 = VertexPositions[e.Source]; // e.Vertices[0].Location; var p2 = VertexPositions[e.Target]; //e.Vertices[1].Location; ed = new EdgeGroupData { V1 = p1, V2 = p2, ID = key }; var mid = VectorTools.MidPoint(p1, p2); ed.Middle = mid; ed.Length = VectorTools.Distance(p1, p2); ed.ControlPoints = e.RoutingPoints; //e.GetValue(ReservedMetadataKeys.PerEdgeIntermediateCurvePoints); if (_subdivisionPoints == 0) { _subdivisionPoints = ed.ControlPoints.Length; } ed.NewControlPoints = new Point[_subdivisionPoints]; ed.K = _springConstant * (_subdivisionPoints + 1) / ed.Length; if (ed.K > 0.5f) { ed.K = 0.5f; } //ed.edges = new HashSet<int>(); ed.EdgeCount = 0; ed.CompatibleGroups = new Dictionary <KeyPair, GroupPairData>(); _edgeGroupData.Add(key, ed); } //ed.edges.Add(e.ID); ed.EdgeCount++; }
/* * /// <summary> * /// Doubles subdivision points for an edge by adding one new subdivision point between each two * /// </summary> * /// * /// <param name="ed"> * /// Edge data that contains subdivision points to be doubled * /// </param> * private void DoubleSubdivisionPoints(EdgeGroupData ed) * { * if (_subdivisionPoints == 0) //make one subdivision point * { * ed.K = _springConstant * 2 / ed.Length; * if (ed.K > 0.5f) ed.K = 0.5f; * ed.ControlPoints = new Point[1]; * ed.NewControlPoints = new Point[1]; * ed.ControlPoints[0] = ed.Middle; * * return; * } * * var sPoints = ed.ControlPoints; * var sPointsDoubled = new Point[_subdivisionPoints * 2 + 1]; * ed.NewControlPoints = new Point[_subdivisionPoints * 2 + 1]; * for (var i = 0; i < _subdivisionPoints; i++) * sPointsDoubled[i * 2 + 1] = sPoints[i]; * * * for (var i = 0; i < _subdivisionPoints - 1; i++) * sPointsDoubled[i * 2 + 2] = VectorTools.MidPoint(sPoints[i], sPoints[i + 1]); * * * sPointsDoubled[0] = VectorTools.MidPoint(ed.V1, sPoints[0]); * sPointsDoubled[_subdivisionPoints * 2] = VectorTools.MidPoint(sPoints[_subdivisionPoints - 1], ed.V2); * //ed.K = springConstant * (subdivisionPoints * 2 + 2) / ed.Length; * ed.K *= 2f; * if (ed.K > 0.5f) ed.K = 0.5f; * ed.ControlPoints = sPointsDoubled; * } */ /* * /// <summary> * /// Doubles subdivision points for all edges * /// </summary> * private void DoubleSubdivisionPointsForAllEdges() * { * foreach (var ed in _edgeGroupData.Values) DoubleSubdivisionPoints(ed); * _subdivisionPoints = _subdivisionPoints * 2 + 1; * } */ /// <summary> /// Calculates new positions for the control points of an edge by applying elastic and electrostatic forces to them /// </summary> /// /// <param name="o"> /// Edge data that contains subdivision points to be moved /// </param> private void CalculateNewControlPoints(Object o) { var ed = (EdgeGroupData)o; for (var i = 0; i < _subdivisionPoints; i++) { var p = ed.ControlPoints[i]; Point p1, p2; p1 = i == 0 ? ed.V1 : ed.ControlPoints[i - 1]; p2 = i == (_subdivisionPoints - 1) ? ed.V2 : ed.ControlPoints[i + 1]; //SizeF sp = new SizeF(p); var f = VectorTools.Multiply(VectorTools.Plus(VectorTools.Minus(p1, p), VectorTools.Minus(p2, p)), ed.K); var r = new Point(0, 0); foreach (var epd in ed.CompatibleGroups.Values) { Point q; var j = 1f; EdgeGroupData ed2; if ((epd.Ed1.ID.K1 == ed.ID.K1) && (epd.Ed1.ID.K2 == ed.ID.K2)) { ed2 = epd.Ed2; } else { ed2 = epd.Ed1; } if (epd.D) { q = ed2.ControlPoints[i]; } else { q = ed2.ControlPoints[_subdivisionPoints - i - 1]; if (_directed && _repulseOpposite) { j = _repulsionCoefficient; } } var fs = VectorTools.Minus(q, p); //PointF fs = new PointF(q.X - p.X, q.Y - p.Y); var l = VectorTools.Length(fs); if (l > 0)//??? { fs = VectorTools.Multiply(fs, epd.C / (l)); //fs = VectorTools.Multiply(fs, VectorTools.Length(fs) * ed2.edges.Count); fs = VectorTools.Multiply(fs, VectorTools.Length(fs) * ed2.EdgeCount); r.X += (j * fs.X); r.Y += (j * fs.Y); } } var rl = VectorTools.Length(r); if (rl > 0) { r = VectorTools.Multiply(r, (float)(1.0 / Math.Sqrt(rl))); } var move = new Point(f.X + r.X, f.Y + r.Y); VectorTools.Length(move); //float len = ed.Length / (subdivisionPoints + 1); //if (moveL > (len)) move = VectorTools.Multiply(move, len*cooldown / moveL); //if (moveL != 0) move = VectorTools.Multiply(move, cooldown / moveL); move = VectorTools.Multiply(move, _cooldown * 0.5f); ed.NewControlPoints[i] = VectorTools.Plus(move, p); if (ed.NewControlPoints[i].X < AreaRectangle.Left) { ed.NewControlPoints[i].X = AreaRectangle.Left; } else if (ed.NewControlPoints[i].X > AreaRectangle.Right) { ed.NewControlPoints[i].X = AreaRectangle.Right; } if (ed.NewControlPoints[i].Y < AreaRectangle.Top) { ed.NewControlPoints[i].Y = AreaRectangle.Top; } else if (ed.NewControlPoints[i].Y > AreaRectangle.Bottom) { ed.NewControlPoints[i].Y = AreaRectangle.Bottom; } } if (_useThreading) { _sem.Release(); } }
/// <summary> /// Calculates angle compatibility of the two edges /// </summary> /// /// <param name="ed1"> /// First edge to be used in calculation /// </param> /// /// <param name="ed2"> /// Second edge to be used in calculation /// </param> /// /// <returns> /// Angle compatibility coefficient ranging from 0 to 1 /// </returns> private float AngleCompatibility(EdgeGroupData ed1, EdgeGroupData ed2) { var a = VectorTools.Angle(ed1.V1, ed1.V2, ed2.V1, ed2.V2); return((float)Math.Abs(Math.Cos(a))); }
/// <summary> /// Moves the control points of all the edges of the graph closer to their original position on the straight edge /// </summary> /// /// <param name="graph"> /// Graph whose edges should be straightened /// </param> /// /// <param name="s"> /// Specifies the amount of straightening, from 0 to 1 /// </param> public void StraightenEdges(TGraph graph, float s) { foreach (var e in graph.Edges) { if (e.IsSelfLoop) { continue; } var controlPoints = e.RoutingPoints;//(PointF[])e.GetValue(ReservedMetadataKeys.PerEdgeIntermediateCurvePoints); var newControlPoints = new Point[controlPoints.Length]; for (var i = 0; i < controlPoints.Length; i++) { //STRONG CHANGE var p = controlPoints[i]; p = VectorTools.Plus(VectorTools.Multiply(p, 1 - s), VectorTools.Multiply(VectorTools.Plus(VertexPositions[e.Source], VectorTools.Multiply(VectorTools.Minus(VertexPositions[e.Target], VertexPositions[e.Source]), 1.0f * (i + 1) / (controlPoints.Length + 1))), s)); newControlPoints[i].X = p.X; newControlPoints[i].Y = p.Y; } //e.SetValue(ReservedMetadataKeys.PerEdgeIntermediateCurvePoints, newControlPoints); e.RoutingPoints = newControlPoints; } }