{"id":1370,"date":"2015-11-24T11:21:51","date_gmt":"2015-11-24T11:21:51","guid":{"rendered":"http:\/\/mindfusion.eu\/blog\/?p=1370"},"modified":"2021-01-20T17:01:44","modified_gmt":"2021-01-20T17:01:44","slug":"display-petri-nets-using-mindfusion-diagram-component","status":"publish","type":"post","link":"https:\/\/mindfusion.dev\/blog\/display-petri-nets-using-mindfusion-diagram-component\/","title":{"rendered":"Display Petri nets using MindFusion diagram component."},"content":{"rendered":"<p>In this post we show how to build a Petri net using MindFusion.Diagramming for WinForms. Petri nets are used to model and study distributed systems. A net contains places, transitions and arcs. A place represents possible state of the system, and a transition represents the change from one state to another. Arcs connect places to transitions and show the flow direction.<\/p>\n<p>First, create a new .NET Windows Forms project and add a Model.cs file to it where we&#8217;ll define Petri net model classes<\/p>\n<pre>public class Net\n{\n\tpublic List Places { get; set; }\n\tpublic List Transitions { get; set; }\n\tpublic List Arcs { get; set; }\n\n\tpublic Net()\n\t{\n\t\tPlaces = new List();\n\t\tTransitions = new List();\n\t\tArcs = new List();\n\t}\n}\n\npublic class Node\n{\n\tpublic string Label { get; set; }\n}\n\npublic class Place : Node\n{\n\tpublic int Tokens { get; set; }\n}\n\npublic class Transition : Node\n{\n}\n\npublic class Arc\n{\n\t\/\/ Arcs run from a place to a transition or vice versa,\n\t\/\/ never between places or between transitions.\n\n\tpublic Arc(Place input, Transition output)\n\t{\n\t\tInput = input;\n\t\tOutput = output;\n\t}\n\n\tpublic Arc(Transition input, Place output)\n\t{\n\t\tInput = input;\n\t\tOutput = output;\n\t}\n\n\tpublic Node Input { get; private set; }\n\tpublic Node Output { get; private set; }\n\n\tpublic int Multiplicity { get; set; }\n}\n<\/pre>\n<p>Now we can create a simple Petri net:<\/p>\n<pre>Net CreateSampleNet()\n{\n\tvar net = new Net();\n\n\tvar p1 = new Place { Label = \"P1\", Tokens = 1 };\n\tvar p2 = new Place { Label = \"P2\", Tokens = 0 };\n\tvar p3 = new Place { Label = \"P3\", Tokens = 2 };\n\tvar p4 = new Place { Label = \"P4\", Tokens = 1 };\n\n\tnet.Places.AddRange(new[] { p1, p2, p3, p4 });\n\n\tvar t1 = new Transition { Label = \"T1\" };\n\tvar t2 = new Transition { Label = \"T2\" };\n\n\tnet.Transitions.AddRange(new[] { t1, t2 });\n\n\tvar a1 = new Arc(p1, t1);\n\tvar a2 = new Arc(t1, p2);\n\tvar a3 = new Arc(t1, p3);\n\tvar a4 = new Arc(p2, t2);\n\tvar a5 = new Arc(p3, t2);\n\tvar a6 = new Arc(t2, p4);\n\tvar a7 = new Arc(t2, p1);\n\n\tnet.Arcs.AddRange(new[] { a1, a2, a3, a4, a5, a6, a7 });\n\n\treturn net;\n}<\/pre>\n<p>Next, drop a DiagramView and Diagram objects on the form which we&#8217;ll use to visualize the net. Add the following method to create diagram elements representing the model objects, and run LayeredLayout to arrange them:<\/p>\n<pre>void BuildDiagram(Net net)\n{\n\tvar nodeMap = new Dictionary&lt;node, diagramnode=\"\"&gt;();\n\n\tvar placeBounds = new RectangleF(0, 0, 16, 16);\n\tvar transBounds = new RectangleF(0, 0, 6, 20);\n\n\tforeach (var place in net.Places)\n\t{\n\t\tvar node = diagram.Factory.CreateShapeNode(placeBounds);\n\t\tnode.Text = place.Label;\n\t\tnode.TextFormat.LineAlignment = StringAlignment.Far;\n\t\tnode.Shape = Shapes.Ellipse;\n\t\tnode.Tag = place.Tokens;\n\t\tnode.CustomDraw = CustomDraw.Additional;\n\t\tnodeMap[place] = node;\n\t}\n\n\tforeach (var trans in net.Transitions)\n\t{\n\t\tvar node = diagram.Factory.CreateShapeNode(transBounds);\n\t\tnode.Text = trans.Label;\n\t\tnode.TextFormat.LineAlignment = StringAlignment.Far;\n\t\tnode.Shape = Shapes.Rectangle;\n\t\tnodeMap[trans] = node;\n\t}\n\n\tforeach (var arc in net.Arcs)\n\t{\n\t\tvar link = diagram.Factory.CreateDiagramLink(\n\t\t\tnodeMap[arc.Input], nodeMap[arc.Output]);\n\t\tlink.Tag = arc.Multiplicity;\n\t\tlink.HeadShape = ArrowHeads.PointerArrow;\n\t}\n\n\tvar layout = new LayeredLayout();\n\tlayout.Orientation = Orientation.Horizontal;\n\tlayout.StraightenLongLinks = true;\n\tlayout.Arrange(diagram);\n}&lt;\/node,&gt;<\/pre>\n<p>We will use the DrawNode custom draw event to render marks associated with each place. Another possibility is to create a custom node class and override its Draw method.<\/p>\n<pre>void OnDrawNode(object sender, DrawNodeEventArgs e)\n{\n\tvar node = e.Node;\n\tvar g = e.Graphics;\n\n\tif (node.Tag is int)\n\t{\n\t\tvar tokens = (int)node.Tag;\n\t\tvar cx = node.Bounds.Width \/ 2;\n\t\tvar cy = node.Bounds.Height \/ 2;\n\n\t\tif (tokens == 1)\n\t\t{\n\t\t\tfloat r = cx \/ 2;\n\t\t\tDrawMark(cx, cy, r, g);\n\t\t}\n\t\telse if (tokens == 2)\n\t\t{\n\t\t\tfloat r = 2 * cx \/ 5;\n\t\t\tDrawMark(cx \/ 2, cy, r, g);\n\t\t\tDrawMark(3 * cx \/ 2, cy, r, g);\n\t\t}\n\t\telse if (tokens == 3)\n\t\t{\n\t\t\tfloat r = cx \/ 3;\n\t\t\tfloat y2 = 4 * cy \/ 3;\n\t\t\tDrawMark(cx, 2 * cy \/ 5, r, g);\n\t\t\tDrawMark(cx \/ 2, y2, r, g);\n\t\t\tDrawMark(3 * cx \/ 2, y2, r, g);\n\t\t}\n\t}\n}\n\nvoid DrawMark(float x, float y, float r, IGraphics g)\n{\n\tg.FillEllipse(Brushes.Black, x - r, y - r, r * 2, r * 2);\n}<\/pre>\n<p>Finally, set some appearance properties and call the methods above to build the diagram:<\/p>\n<pre>public MainForm()\n{\n\tInitializeComponent();\n\n\tdiagram.ShadowsStyle = ShadowsStyle.None;\n\tdiagram.DiagramLinkStyle.Brush = new MindFusion.Drawing.SolidBrush(Color.Black);\n\tdiagram.ShapeNodeStyle.Brush = new MindFusion.Drawing.SolidBrush(Color.White);\n\tdiagram.ShapeNodeStyle.FontSize = 10f;\n\tdiagram.ShapeNodeStyle.FontStyle = FontStyle.Bold;\n\n\tvar textAbove = new[]\n\t{\n\t\tnew LineTemplate(-100, -100, 200, -100),\n\t\tnew LineTemplate(200, -100, 200, 0),\n\t\tnew LineTemplate(200, 0, -100, 0),\n\t\tnew LineTemplate(-100, 0, -100, -100)\n\t};\n\tShapes.Ellipse.TextArea = textAbove;\n\tShapes.Rectangle.TextArea = textAbove;\n\n\tvar net = CreateSampleNet();\n\tBuildDiagram(net);\n}<\/pre>\n<p>The final result is displayed below.<br \/>\n<img decoding=\"async\" src=\"http:\/\/mindfusion.dev\/_samples\/petri_net.png\" alt=\"Petri net diagram\" \/><\/p>\n<p>The complete sample project is available for download here:<br \/>\n<a href=\"https:\/\/mindfusion.dev\/_samples\/PetriNet.zip\">PetriNet.zip<\/a><\/p>\n<p>For more information on Petri nets, see this Wikipedia article:<br \/>\n<a href=\"http:\/\/en.wikipedia.org\/wiki\/Petri_net\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/en.wikipedia.org\/wiki\/Petri_net<\/a><\/p>\n<p>All MindFusion.Diagramming libraries expose the same programming interface, so most of the sample code shown above will work with only a few modifications in WPF, ASP.NET, Silverlight and Java versions of the control.<\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post we show how to build a Petri net using MindFusion.Diagramming for WinForms. Petri nets are used to model and study distributed systems. A net contains places, transitions and arcs. A place represents possible state of the system, &hellip; <a href=\"https:\/\/mindfusion.dev\/blog\/display-petri-nets-using-mindfusion-diagram-component\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","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,3,356],"class_list":["post-1370","post","type-post","status-publish","format-standard","hentry","category-diagramming-2","category-sample-code","tag-net","tag-c","tag-diagram","tag-petri"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3RlKs-m6","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1370","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=1370"}],"version-history":[{"count":2,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1370\/revisions"}],"predecessor-version":[{"id":2564,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1370\/revisions\/2564"}],"wp:attachment":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/media?parent=1370"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/categories?post=1370"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/tags?post=1370"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}