private int CountCrossings() { // Initialize foreach (GraphNode node in _graph.Nodes) { SpringVertexData data = (SpringVertexData)node.GetData(_SpringVertex); data.Crossings = 0; } foreach (GraphLink link in _graph.Links) { SpringEdgeData data = (SpringEdgeData)link.GetData(_SpringEdge); data.Crossings = 0; } foreach (GraphLink link in _graph.Links) { CountEdgeCrossings(link); } int crossings = 0; foreach (GraphNode node in _graph.Nodes) { SpringVertexData data = (SpringVertexData)node.GetData(_SpringVertex); crossings += data.Crossings; } return(crossings); }
private GraphNode NodeWithMostCrossings() { GraphNode node = null; int max = 0; int numLinks = int.MaxValue; foreach (GraphNode n in _graph.Nodes) { SpringVertexData data = (SpringVertexData)n.GetData(_SpringVertex); if (data.Crossings > max || (data.Crossings == max && n.LinkCount < numLinks)) { node = n; max = data.Crossings; numLinks = n.LinkCount; } } return(node); }
private void CountEdgeCrossings(GraphLink edge) { int crossings = 0; GraphNode v1 = edge.Origin; GraphNode v2 = edge.Destination; SpringVertexData v1D = (SpringVertexData)v1.GetData(_SpringVertex); SpringVertexData v2D = (SpringVertexData)v2.GetData(_SpringVertex); PointF pt = new PointF(0, 0), pt11, pt12, pt21, pt22; pt11 = v1.Center; pt12 = v2.Center; foreach (GraphLink test in _graph.Links) { if (edge == test) { continue; } pt21 = test.Origin.Center; pt22 = test.Destination.Center; if (pt11 != pt21 && pt11 != pt22 && pt12 != pt21 && pt12 != pt22) { if (new Line(pt11, pt12).IntersectSegment(pt21, pt22).X != float.PositiveInfinity) { crossings++; v1D.Crossings++; v2D.Crossings++; } } } SpringEdgeData data = (SpringEdgeData)edge.GetData(_SpringEdge); data.Crossings = crossings; }
public bool Arrange(IGraph graph, SpringLayoutInfo layoutInfo, LayoutProgress progress) { _info = layoutInfo; if (_info.RndSeed != 0) { // Use the randomization seed supplied by the user _r = new Random(_info.RndSeed); } // Build the internal graph _graph = new Graph(graph); RectangleF rcDoc = CalcContentRect(_graph); float minNodeX = rcDoc.Left - (50f * _info.NodeDistance); float minNodeY = rcDoc.Top - (50f * _info.NodeDistance); float maxNodeX = rcDoc.Right + (50f * _info.NodeDistance); float maxNodeY = rcDoc.Bottom + (50f * _info.NodeDistance); // Initialize foreach (GraphNode node in _graph.Nodes) { node.SetData(_SpringVertex, new SpringVertexData()); } foreach (GraphLink link in _graph.Links) { link.SetData(_SpringEdge, new SpringEdgeData()); } // Calculate vertex radiuses foreach (GraphNode node in _graph.Nodes) { RectangleF rc = node.Bounds; node.SetData(_VertexRadius, (double)(rc.Width + rc.Height) / 4); } // Update progress int total = layoutInfo.IterationCount; float ffactor = total / 100; int factor = 1; if (ffactor > 1) { factor = (int)Math.Floor((double)ffactor); total = total / factor + 1; } if (progress != null) { progress(0, total); } // An iterative layout algorithm for (int step = 1; step <= layoutInfo.IterationCount; step++) { // Update progress if (progress != null) { if (step % factor == 0) { progress(step / factor, total); } } foreach (GraphNode node in _graph.Nodes) { SpringVertexData data = (SpringVertexData) node.GetData(_SpringVertex); data.DX /= 4; data.DY /= 4; data.EdgeDX = data.EdgeDY = 0; data.RepulsionDX = data.RepulsionDY = 0; } // here the "springs" confine nodes to the desired node distance foreach (GraphLink link in _graph.Links) { // get incident nodes GraphNode v1 = link.Origin; GraphNode v2 = link.Destination; // compute current distance between nodes PointF v1Pos = v1.Center; PointF v2Pos = v2.Center; double vx = v1Pos.X - v2Pos.X; double vy = v1Pos.Y - v2Pos.Y; double dist = Math.Sqrt(vx * vx + vy * vy); dist = (dist == 0) ? 0.0001 : dist; // get desired distance accomodated for node extents double desiredDist = _info.NodeDistance; desiredDist += ((double)v1.GetData(_VertexRadius) + (double)v2.GetData(_VertexRadius)) / 2; // now handling cluster center nodes ? bool masterNodes = _info.EnableClusters && (v1.LinkCount > 4 && v2.LinkCount > 4); desiredDist *= masterNodes ? 4 : link.Link.Weight; // force factor: optimal length minus actual length, // is made smaller as the current actual length gets larger. double f = _ForceConstant * (desiredDist - dist) / dist; // nodes with few links are moved easier than ones with more double f1 = masterNodes ? f : (f * Math.Pow(_stretch / 100.0, v1.LinkCount - 1)); double f2 = masterNodes ? f : (f * Math.Pow(_stretch / 100.0, v2.LinkCount - 1)); // move first node towards the second one SpringVertexData v1D = (SpringVertexData)v1.GetData(_SpringVertex); v1D.EdgeDX += f1 * vx; v1D.EdgeDY += f1 * vy; // move second node towards the first one SpringVertexData v2D = (SpringVertexData)v2.GetData(_SpringVertex); v2D.EdgeDX += -f2 * vx; v2D.EdgeDY += -f2 * vy; } // here nodes tend to run away one from another foreach (GraphNode node in _graph.Nodes) { SpringVertexData svd = (SpringVertexData)node.GetData(_SpringVertex); double dx = 0; double dy = 0; foreach (GraphNode node2 in _graph.Nodes) { if (node2 == node) { continue; } // now handling cluster center nodes ? bool masterNodes = _info.EnableClusters && (node.LinkCount > 4 && node2.LinkCount > 4); // if distance is longer than this, ignore repulsion double intrZone = _info.NodeDistance * (masterNodes ? 4.5 : _info.RepulsionFactor); // if distance is shorter than this, randomize node positions double tooClose = _info.NodeDistance / (masterNodes ? 1 : 5); // distance to move when using random position double moveRange = _info.NodeDistance * (masterNodes ? 5 : 1); // compute current distance between nodes double vx = node.Center.X - node2.Center.X; double vy = node.Center.Y - node2.Center.Y; double distance = vx * vx + vy * vy; // kind of simmulated annealing, use random jumps for nodes // that are close one to another; longer jumps for nodes that // overlap completely and shorter for nodes that don't overlap if (distance == 0) { dx += moveRange / 2 + _r.Next() % ((int)(moveRange * 3)); dy += moveRange / 2 + _r.Next() % ((int)(moveRange * 3)); if (_r.Next() % 2 == 1) { dx = -dx; } if (_r.Next() % 2 == 1) { dy = -dy; } } else if (distance < tooClose * tooClose) { dx += moveRange / 2 + _r.Next((int)moveRange); dy += moveRange / 2 + _r.Next((int)moveRange); if (_r.Next() % 2 == 1) { dx = -dx; } if (_r.Next() % 2 == 1) { dy = -dy; } } // if nodes are not that close, calculate normal repulsion else if (distance < intrZone * intrZone) { float mf = masterNodes ? (node.LinkCount + node2.LinkCount) : 1; dx += mf * vx / (distance * distance); dy += mf * vy / (distance * distance); } } // apply the summary repulsion of this node with all other nodes double dlen = dx * dx + dy * dy; if (dlen > 0) { dlen = Math.Sqrt(dlen) / 2; svd.RepulsionDX += dx / dlen; svd.RepulsionDY += dy / dlen; } } // sum repulsion and "spring" forces and move nodes foreach (GraphNode node in _graph.Nodes) { // update node movement speed SpringVertexData vd = (SpringVertexData)node.GetData(_SpringVertex); vd.DX += vd.RepulsionDX + vd.EdgeDX; vd.DY += vd.RepulsionDY + vd.EdgeDY; // move node PointF xyd = node.Center; xyd.X += (float)vd.DX; xyd.Y += (float)vd.DY; if (layoutInfo.EnableClusters && (((xyd.X < minNodeX) || (xyd.Y < minNodeY)) || ((xyd.X > maxNodeX) || (xyd.Y > maxNodeY)))) { xyd = node.Center; vd.DX /= 4; vd.DY /= 4; } node.Center = xyd; } // try to decrease crossings if that option is enabled if (_info.MinimizeCrossings && (step % 10 == 0) && (step < _info.IterationCount * 2 / 3)) { int currentCrossings = CountCrossings(); if (currentCrossings > 0) { TryDecreaseCrossings(graph.DocRect, _info.NodeDistance, currentCrossings); } } } // offset the graph to its original location RectangleF rcDoc2 = CalcContentRect(_graph); float pdx = rcDoc2.Left - rcDoc.Left; float pdy = rcDoc2.Top - rcDoc.Top; foreach (GraphNode node in _graph.Nodes) { PointF xyd = node.Center; xyd.X -= pdx; xyd.Y -= pdy; node.Center = xyd; } // update flowchart nodes positions foreach (GraphNode node in _graph.Nodes) { node.Node.Bounds = node.Bounds; } // call progress delegate if (progress != null) { progress(total, total); } return(true); }
private void TryDecreaseCrossings(RectangleF doc, float ddist, int crossings) { GraphNode node = NodeWithMostCrossings(); if (node == null) { return; } SpringVertexData data = (SpringVertexData)node.GetData(_SpringVertex); PointF pos = node.Center; PointF minPos = pos; for (int c = 3; c >= 1; c--) { for (int h = -1; h <= 1; h++) { for (int v = -1; v <= 1; v++) { if (h == 0 && v == 0) { continue; } PointF newPos = new PointF(0, 0); newPos.X = pos.X + c * h * ddist; newPos.Y = pos.Y + c * v * ddist; if (newPos.X < doc.Left) { continue; } if (newPos.X > doc.Right) { continue; } if (newPos.Y < doc.Top) { continue; } if (newPos.Y > doc.Bottom) { continue; } node.Center = newPos; int newCrossings = CountCrossings(); if (newCrossings < crossings) { minPos = newPos; } } } } if (minPos != pos) { node.Center = minPos; } }