{"id":1276,"date":"2015-08-11T11:38:41","date_gmt":"2015-08-11T11:38:41","guid":{"rendered":"http:\/\/mindfusion.eu\/blog\/?p=1276"},"modified":"2021-01-20T16:50:02","modified_gmt":"2021-01-20T16:50:02","slug":"combine-layout-algorithms-3","status":"publish","type":"post","link":"https:\/\/mindfusion.dev\/blog\/combine-layout-algorithms-3\/","title":{"rendered":"Combine layout algorithms"},"content":{"rendered":"<p><strong>Apply FractalLayout and SpringLayout to generate a tag cloud<\/strong><\/p>\n<p>In a series of posts we&#8217;ll explore ways to combine graph layout algorithms for various purposes, such as improving layout speed or achieving specific layout constraints.<\/p>\n<p>In this topic we&#8217;ll show how to create a tag cloud using FractalLayout and SpringLayout algorithms from MindFusion diagramming API. You can download the complete project here:<\/p>\n<p><a href=\"https:\/\/mindfusion.dev\/_samples\/TagCloud.zip\">TagCloud.zip<\/a><\/p>\n<p>The sample code will show several features of the Diagram control:<\/p>\n<ul>\n<li>FractalLayout<\/li>\n<li>SpringLayout<\/li>\n<li>custom node placement<\/li>\n<li>text-only nodes<\/li>\n<\/ul>\n<p>We assume words frequencies are already counted and listed as &#8220;word: frequency&#8221; entries in a sorted file. The example uses tags extracted from the contents of Wikipedia&#8217;s <a href=\"https:\/\/en.wikipedia.org\/wiki\/Tag_cloud\" target=\"_blank\" rel=\"noopener noreferrer\">Tag cloud<\/a> page. Let&#8217;s start by parsing the file and creating a node for each word. We&#8217;ll assign the word frequency to the Weight property of nodes for future reference. Weight is also used by some layout algorithms (such as TreeMapLayout for creating tree maps) that could visually represent word frequencies as well:<\/p>\n<pre>RectangleF defaultBounds = new RectangleF(0, 0, 10, 20);\nShapeNode root;\n\t\t\nprivate void MainForm_Load(object sender, EventArgs e)\n{\n\t\/\/ read the tags file\n\tvar reader = new StreamReader(\"words.txt\");\n\tstring line;\n\twhile ((line = reader.ReadLine()) != null)\n\t{\n\t\t\/\/ each line contains \"word: frequency\" entries\n\t\tvar parts = line.Split(new[] { ':' });\n\t\tvar word = parts[0];\n\t\tvar frequency = int.Parse(parts[1]);\n\n\t\t\/\/ create a diagram node for each word in the file\n\t\tvar node = diagram.Factory.CreateShapeNode(defaultBounds);\n\t\tnode.Weight = frequency;\n\t\tnode.Text = word;\n\n\t\t\/\/ set font size corresponding to frequency\n\t\tnode.Font = new Font(\n\t\t\t\"Arial\",\n\t\t\t8 + (float)Math.Log(node.Weight, 1.15),\n\t\t\tGraphicsUnit.Point);\n\n\t\t\/\/ resize the node to fit text\n\t\tvar size = TextRenderer.MeasureText(node.Text, node.Font);\n\t\tnode.Resize(\n\t\t\t2 + (float)MeasureUnit.Pixel.Convert(size.Width, diagram.MeasureUnit, null),\n\t\t\t2 + (float)MeasureUnit.Pixel.Convert(size.Height, diagram.MeasureUnit, null));\n\n\t\t\/\/ show only text, hide geometry\n\t\tnode.Transparent = true;\n\t}\n}\n<\/pre>\n<p>Next, let&#8217;s build a tree that will distribute largest nodes roughly uniformly when arranged by FractalLayout, where larger parent nodes are circled by their smaller child nodes:<\/p>\n<pre>while ((line = reader.ReadLine()) != null)\n{\n\t\/\/ ...\n\tif (diagram.Nodes.Count == 1)\n\t{\n\t\t\/\/ save reference to the first node\n\t\troot = node;\n\t}\n\telse\n\t{\n\t\t\/\/ build a tree where each node has up to six children\n\t\tdiagram.Factory.CreateDiagramLink(\n\t\t\tdiagram.Nodes[diagram.Nodes.Count \/ 6],\n\t\t\tnode);\n\t}\n}\n\n\/\/ use FractalLayout for initial placement. if the file entries are sorted, each circular\n\/\/ branch will contain larger parent node centered between its smaller children\nnew FractalLayout().Arrange(diagram);\n<\/pre>\n<p>If you run the application now, you should see the following layout:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/mindfusion.dev\/_samples\/fractal_layout3.png\" alt=\"a tree arranged using fractal layout\" \/><\/p>\n<p>FractalLayout allocates some space for links, and we&#8217;ll reclaim it by deleting the links and compressing the initial layout of nodes:<\/p>\n<pre>\/\/ remove the links\nwhile (diagram.Links.Count &gt; 0)\n\tdiagram.Links.RemoveAt(diagram.Links.Count - 1);\n\n\/\/ pull all nodes towards the root to eliminate empty space that was occupied by links\nvar center = root.GetCenter();\nforeach (var node in diagram.Nodes.Where(n =&gt; n != root))\n{\n\tvar relativePos = new Vector(center, node.GetCenter());\n\tvar newPos = center + relativePos \/ 10;\n\tnode.Move(\n\t\tnewPos.X - node.Bounds.Width \/ 2,\n\t\tnewPos.Y - node.Bounds.Height \/ 2);\n}\n<\/pre>\n<p>Now apply SpringLayout to make distances between closely placed nodes more uniform, running only a few iterations to complete faster:<\/p>\n<pre>\/\/ run SpringLayout to distribute nodes more uniformly\nvar sl = new SpringLayout();\nsl.Randomize = false;\nsl.SplitGraph = false;\nsl.NodeDistance = 3;\nsl.IterationCount = 40;\nsl.Arrange(diagram);\n<\/pre>\n<p>Run the following method to remove any overlaps remaining after SpringLayout. RemoveOverlaps works by starting from specified node and offsetting any nodes that overlap it, continuing by spiraling away while processing other nodes. This method could also be useful in an interactive application if you want to disperse overlapping nodes introduced by the user when they move a node:<\/p>\n<pre>void RemoveOverlaps(DiagramNode modifiedNode, float minDist)\n{\n\tvar queue = new Queue();\n\tqueue.Enqueue(modifiedNode);\n\n\twhile (queue.Count &gt; 0)\n\t{\n\t\tvar node = queue.Dequeue();\n\t\tvar nodeCenter = node.GetCenter();\n\t\tvar overlaps = FindOverlaps(node, minDist);\n\t\tforeach (var overlap in overlaps)\n\t\t{\n\t\t\tvar ovrCenter = overlap.GetCenter();\n\t\t\tvar ovrBounds = overlap.Bounds;\n\t\t\tvar dx = ovrCenter.X - nodeCenter.X;\n\t\t\tvar dy = ovrCenter.Y - nodeCenter.Y;\n\t\t\tif (Math.Abs(dx) &gt; Math.Abs(dy))\n\t\t\t{\n\t\t\t\t\/\/ offset horizontally\n\t\t\t\tif (dx &lt; 0)\n\t\t\t\t\tovrBounds.X = node.Bounds.Left - ovrBounds.Width - minDist;\n\t\t\t\telse\n\t\t\t\t\tovrBounds.X = node.Bounds.Right + minDist;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t\/\/ offset vertically\n\t\t\t\tif (dy &lt; 0)\n\t\t\t\t\tovrBounds.Y = node.Bounds.Top - ovrBounds.Height - minDist;\n\t\t\t\telse\n\t\t\t\t\tovrBounds.Y = node.Bounds.Bottom + minDist;\n\t\t\t}\n\n\t\t\t\/\/ shifting the node might introduce new overlaps, continue processing\n\t\t\toverlap.Bounds = ovrBounds;\n\t\t\tqueue.Enqueue(overlap);\n\t\t}\n\t}\n}\n\nList FindOverlaps(DiagramNode modifiedNode, float minDist)\n{\n\tvar bounds = modifiedNode.Bounds;\n\tbounds.Inflate(minDist - 1, minDist - 1);\n\n\tvar overlaps = new List();\n\tforeach (var node in diagram.Nodes)\n\t{\n\t\tif (modifiedNode == node)\n\t\t\tcontinue;\n\t\tif (bounds.IntersectsWith(node.Bounds))\n\t\t\toverlaps.Add(node);\n\t}\n\treturn overlaps;\n} \n<\/pre>\n<p>Finally run a few more iterations of SpringLayout to equalize distances again and zoom the diagram to show the whole tag cloud:<\/p>\n<pre>\/\/ remove any remaining overlaps\nRemoveOverlaps(root, 0.1f);\nsl.Arrange(diagram);\n\n\/\/ show everything inside view\ndiagram.ResizeToFitItems(5);\ndiagramView.ZoomToFit();\n<\/pre>\n<p>If you run the application now, you should see the following image:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/mindfusion.dev\/_samples\/tag_cloud.png\" alt=\"tag cloud generated using MindFusion diagram control\" \/><\/p>\n<p>The code above uses MindFusion\u2019s .NET API and can be used with Windows Forms, WPF, Silverlight and ASP.NET diagramming components. The Java API for Android and desktop Swing application will look similar, with setter method calls instead of property assignments.<\/p>\n<p>You can download the trial version of any MindFusion.Diagramming component from <a href=\"http:\/\/mindfusion.dev\/download-diagramming-pack.html\">this page.<\/a><\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Apply FractalLayout and SpringLayout to generate a tag cloud In a series of posts we&#8217;ll explore ways to combine graph layout algorithms for various purposes, such as improving layout speed or achieving specific layout constraints. In this topic we&#8217;ll show &hellip; <a href=\"https:\/\/mindfusion.dev\/blog\/combine-layout-algorithms-3\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[95,74],"tags":[110,264,341,3,340],"class_list":["post-1276","post","type-post","status-publish","format-standard","hentry","category-diagramming-2","category-sample-code","tag-net","tag-c","tag-cloud","tag-diagram","tag-tag"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3RlKs-kA","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/comments?post=1276"}],"version-history":[{"count":2,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1276\/revisions"}],"predecessor-version":[{"id":2555,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1276\/revisions\/2555"}],"wp:attachment":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/media?parent=1276"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/categories?post=1276"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/tags?post=1276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}