Ejemplo n.º 1
0
		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;
		}
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
		public bool Arrange(IGraph graph, SpringLayoutInfo layoutInfo)
		{
			return Arrange(graph, layoutInfo, null);
		}
Ejemplo n.º 4
0
 public bool Arrange(IGraph graph, SpringLayoutInfo layoutInfo)
 {
     return(Arrange(graph, layoutInfo, null));
 }