static void Main(string[] args) { // Create a random network SimpleNetwork net = SimpleNetwork.ReadFromEdgeList("demo.edges"); // We use a custom coloring NetworkColorizer colors = new NetworkColorizer(); colors.DefaultBackgroundColor = Color.Black; colors.DefaultEdgeColor = Color.WhiteSmoke; colors.DefaultVertexColor = Color.SteelBlue; // Let's use curved edges instead of the default straight ones Renderer.CurvedEdges = true; // Fire up the visualizer window Renderer.Start(net, new NETVisualizer.Layouts.FruchtermanReingold.FRLayout(10), colors); // Trigger the layout Renderer.Layout.DoLayoutAsync(); // The rendering and layouting is done asynchronously in parallel, // so you can modify the network while the visualization and the layout continues Console.Write("Press ANY KEY to add another edge"); Console.ReadKey(); net.AddEdge("a", "b"); net.AddEdge("b", "c"); // Trigger the layout again. Only changed nodes will be relayouted ... Renderer.Layout.DoLayoutAsync(); Console.WriteLine("Waiting for rendering window to be closed ..."); }
private static void DrawVertex(XGraphics g, string v, LayoutProvider layout, NetworkColorizer colorizer) { OpenTK.Vector3 p = layout.GetPositionOfNode(v); double size = Renderer.ComputeNodeSize(v); if (!double.IsNaN(p.X) && !double.IsNaN(p.Y) && !double.IsNaN(p.Z)) { g.DrawEllipse(new SolidBrush(colorizer[v]), p.X - size / 2d - x_offset, p.Y - size / 2d - y_offset, size, size); } }
private static void Draw(XGraphics g, IRenderableNet n, LayoutProvider layout, NetworkColorizer colorizer) { lock (n) { if (g == null) return; g.SmoothingMode = PdfSharp.Drawing.XSmoothingMode.HighQuality; g.Clear(Color.White); foreach (var e in n.GetEdgeArray()) if (string.Compare(e.Item1, e.Item2) >= 0) DrawEdge(g, e, layout, colorizer); foreach (string v in n.GetVertexArray()) DrawVertex(g, v, layout, colorizer); } }
/// <summary> /// Creates a new instance of a Networkvisualizer which renders the specified network in real-time /// </summary> /// <param name="network">The network to render</param> /// <param name="layout">The layout algorithm to use</param> /// <param name="colorizer">The colorizer to apply. Default colors will be used if this is set to null (which is the default)</param> /// <param name="width">The width of the rendering window. 800 pixels by default</param> /// <param name="height">The height of the rendering window. 600 pixels by default</param> public static void Start(IRenderableNet network, LayoutProvider layout, NetworkColorizer colorizer = null, int width = 800, int height = 600) { // The actual rendering needs to be done in a separate thread placed in the single thread appartment state _mainThread = new Thread(new ThreadStart(new Action(delegate() { Instance = new Renderer(network, layout, colorizer, width, height); _initialized.Set(); Instance.Run(80f); }))); // Set single thread appartment _mainThread.SetApartmentState(ApartmentState.STA); _mainThread.Name = "STA Thread for NETVisualizer"; // Fire up the thread and wait until initialization has been completed _mainThread.Start(); _initialized.WaitOne(); }
public static void CreatePDF(string path, IRenderableNet n, LayoutProvider layout, NetworkColorizer colorizer = null) { PdfSharp.Pdf.PdfDocument doc = new PdfDocument(); doc.Info.Title = "Network"; doc.Info.Subject = "Created by NETVisualizer"; PdfPage page = doc.AddPage(); x_offset = Renderer.Origin.X; y_offset = Renderer.Origin.Y; page.Width = Renderer.Bottomright.X - Renderer.Origin.X; page.Height = Renderer.Bottomright.Y - Renderer.Origin.Y; if (colorizer != null) // Draw the network to the xgraphics object Draw(XGraphics.FromPdfPage(page), n, layout, colorizer); else Draw(XGraphics.FromPdfPage(page), n, layout, new NetworkColorizer()); // Save the s_document... doc.Save(path); }
/// <summary> /// Basic constructor that initializes all events, default values and fields /// </summary> /// <param name="network"></param> /// <param name="layout"></param> /// <param name="colorizer"></param> /// <param name="width"></param> /// <param name="height"></param> internal Renderer(IRenderableNet network, LayoutProvider layout, NetworkColorizer colorizer, int width, int height) : base(width, height, OpenTK.Graphics.GraphicsMode.Default, "NETVisualizer") { // Register key and mouse events Keyboard.KeyDown += new EventHandler <KeyboardKeyEventArgs>(Keyboard_KeyDown); Mouse.ButtonDown += new EventHandler <MouseButtonEventArgs>(Mouse_ButtonDown); Mouse.ButtonUp += new EventHandler <MouseButtonEventArgs>(Mouse_ButtonUp); Mouse.Move += new EventHandler <MouseMoveEventArgs>(Mouse_Move); Mouse.WheelChanged += new EventHandler <MouseWheelEventArgs>(Mouse_WheelChanged); // Set default node size ComputeNodeSize = new Func <string, float>(v => { return(2f); }); MarkerSize = 2f; // Set default edge thickness ComputeEdgeThickness = new Func <Tuple <string, string>, float>(e => { return(0.05f); }); EdgeCurvatureGamma = (float)Math.PI / 3f; EdgeCurvatureSegments = 15; // Set network and initialize layout algorithm _network = network; _layout = layout; _layout.Init(Width, Height, network); // Initialize colorizer if (colorizer == null) { _colorizer = new NetworkColorizer(); } else { _colorizer = colorizer; } }
/// <summary> /// Creates a new instance of a Networkvisualizer which renders the specified network in real-time /// </summary> /// <param name="network">The network to render</param> /// <param name="layout">The layout algorithm to use</param> /// <param name="colorizer">The colorizer to apply. Default colors will be used if this is set to null (which is the default)</param> /// <param name="width">The width of the rendering window. 800 pixels by default</param> /// <param name="height">The height of the rendering window. 600 pixels by default</param> public static void Start(IRenderableNet network, LayoutProvider layout, NetworkColorizer colorizer = null, int width=800, int height=600) { // The actual rendering needs to be done in a separate thread placed in the single thread appartment state _mainThread = new Thread(new ThreadStart(new Action(delegate() { Instance = new Renderer(network, layout, colorizer, width, height); _initialized.Set(); Instance.Run(80f); }))); // Set single thread appartment _mainThread.SetApartmentState(ApartmentState.STA); _mainThread.Name = "STA Thread for NETVisualizer"; // Fire up the thread and wait until initialization has been completed _mainThread.Start(); _initialized.WaitOne(); }
/// <summary> /// Basic constructor that initializes all events, default values and fields /// </summary> /// <param name="network"></param> /// <param name="layout"></param> /// <param name="colorizer"></param> /// <param name="width"></param> /// <param name="height"></param> internal Renderer(IRenderableNet network, LayoutProvider layout, NetworkColorizer colorizer, int width, int height) : base(width, height, OpenTK.Graphics.GraphicsMode.Default, "NETVisualizer") { // Register key and mouse events Keyboard.KeyDown += new EventHandler<KeyboardKeyEventArgs>(Keyboard_KeyDown); Mouse.ButtonDown += new EventHandler<MouseButtonEventArgs>(Mouse_ButtonDown); Mouse.ButtonUp += new EventHandler<MouseButtonEventArgs>(Mouse_ButtonUp); Mouse.Move += new EventHandler<MouseMoveEventArgs>(Mouse_Move); Mouse.WheelChanged += new EventHandler<MouseWheelEventArgs>(Mouse_WheelChanged); // Set default node size ComputeNodeSize = new Func<string, float>(v => { return 2f; }); MarkerSize = 2f; // Set default edge thickness ComputeEdgeThickness = new Func<Tuple<string,string>, float>( e => { return 0.05f; }); EdgeCurvatureGamma = (float)Math.PI / 3f; EdgeCurvatureSegments = 15; // Set network and initialize layout algorithm _network = network; _layout = layout; _layout.Init(Width, Height, network); // Initialize colorizer if (colorizer == null) _colorizer = new NetworkColorizer(); else _colorizer = colorizer; }
private static void DrawEdge(XGraphics g, Tuple<string, string> e, LayoutProvider layout, NetworkColorizer colorizer) { string v = e.Item1; string w = e.Item2; float width = Renderer.ComputeEdgeThickness(e); XColor color = colorizer[e]; List<Vector2> points = new List<Vector2>(); // Draw curved edge if (Renderer.CurvedEdges) { // The two end points of the arc Vector2 pos_v = new Vector2(Renderer.Layout.GetPositionOfNode(v).X,Renderer.Layout.GetPositionOfNode(v).Y); Vector2 pos_w = new Vector2(Renderer.Layout.GetPositionOfNode(w).X, Renderer.Layout.GetPositionOfNode(w).Y); if (pos_v == pos_w) return; // The vector vw representing one point of an equilateral triangle (v,w,x) with x being the center of the circle Vector2 vw = Vector2.Subtract(pos_w, pos_v); // In an equilateral triangle, the two angles adjacent to vw are alpha float alpha = ((float)Math.PI - Renderer.EdgeCurvatureGamma) / 2f; // Compute the radius based on the sine rule ... float radius = Math.Abs((float)(vw.Length * Math.Sin(alpha) / Math.Sin(Renderer.EdgeCurvatureGamma))); // Compute center point Vector2 center = vw; center.Normalize(); float rotated_x = (float)(center.X * Math.Cos(alpha) - center.Y * Math.Sin(alpha)); float rotated_y = (float)(center.X * Math.Sin(alpha) + center.Y * Math.Cos(alpha)); center.X = rotated_x; center.Y = rotated_y; Vector2 pos_center = Vector2.Add(pos_v, Vector2.Multiply(center, radius)); // The vector xv that will be rotatd in every step ... Vector2 xv = Vector2.Subtract(pos_v, pos_center); // The point on the circle to use for the next line segment Vector2 circle_point = xv; // Initiate the drawing ... // Gradually rotate the vector circle_point to get the points on the circle to connect to a curved edge if (Renderer.orientation(pos_center, pos_v, pos_w) > 0) for (int i = 0; i <= Renderer.EdgeCurvatureSegments; i++) { points.Add(Vector2.Add(pos_center, circle_point)); float new_x = (float)(circle_point.X * Math.Cos(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) - circle_point.Y * Math.Sin(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); float new_y = (float)(circle_point.X * Math.Sin(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) + circle_point.Y * Math.Cos(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); circle_point = new Vector2(new_x, new_y); } else for (int i = 0; i <= Renderer.EdgeCurvatureSegments; i++) { points.Add(Vector2.Add(pos_center, circle_point)); float new_x = (float)(circle_point.X * Math.Cos(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) - circle_point.Y * Math.Sin(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); float new_y = (float)(circle_point.X * Math.Sin(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) + circle_point.Y * Math.Cos(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); circle_point = new Vector2(new_x, new_y); } } // Draw straight edge as a simple line between the vertices else { points.Add(new Vector2(Renderer.Layout.GetPositionOfNode(v).X, Renderer.Layout.GetPositionOfNode(v).Y)); points.Add(new Vector2(Renderer.Layout.GetPositionOfNode(w).X, Renderer.Layout.GetPositionOfNode(w).Y)); } for (int i = 1; i < points.Count; i++) g.DrawLine(new XPen(color, width), points[i - 1].X-x_offset, points[i - 1].Y-y_offset, points[i].X-x_offset, points[i].Y-y_offset); }
private static void DrawVertex(XGraphics g, string v, LayoutProvider layout, NetworkColorizer colorizer) { OpenTK.Vector3 p = layout.GetPositionOfNode(v); double size = Renderer.ComputeNodeSize(v); if (!double.IsNaN(p.X) && !double.IsNaN(p.Y) && !double.IsNaN(p.Z)) g.DrawEllipse(new SolidBrush(colorizer[v]), p.X - size / 2d - x_offset, p.Y - size / 2d - y_offset, size, size); }
private static void Draw(XGraphics g, IRenderableNet n, LayoutProvider layout, NetworkColorizer colorizer) { lock (n) { if (g == null) { return; } g.SmoothingMode = PdfSharp.Drawing.XSmoothingMode.HighQuality; g.Clear(Color.White); foreach (var e in n.GetEdgeArray()) { if (string.Compare(e.Item1, e.Item2) >= 0) { DrawEdge(g, e, layout, colorizer); } } foreach (string v in n.GetVertexArray()) { DrawVertex(g, v, layout, colorizer); } } }
public static void CreatePDF(string path, IRenderableNet n, LayoutProvider layout, NetworkColorizer colorizer = null) { PdfSharp.Pdf.PdfDocument doc = new PdfDocument(); doc.Info.Title = "Network"; doc.Info.Subject = "Created by NETVisualizer"; PdfPage page = doc.AddPage(); x_offset = Renderer.Origin.X; y_offset = Renderer.Origin.Y; page.Width = Renderer.Bottomright.X - Renderer.Origin.X; page.Height = Renderer.Bottomright.Y - Renderer.Origin.Y; if (colorizer != null) { // Draw the network to the xgraphics object Draw(XGraphics.FromPdfPage(page), n, layout, colorizer); } else { Draw(XGraphics.FromPdfPage(page), n, layout, new NetworkColorizer()); } // Save the s_document... doc.Save(path); }
private static void DrawEdge(XGraphics g, Tuple <string, string> e, LayoutProvider layout, NetworkColorizer colorizer) { string v = e.Item1; string w = e.Item2; float width = Renderer.ComputeEdgeThickness(e); XColor color = colorizer[e]; List <Vector2> points = new List <Vector2>(); // Draw curved edge if (Renderer.CurvedEdges) { // The two end points of the arc Vector2 pos_v = new Vector2(Renderer.Layout.GetPositionOfNode(v).X, Renderer.Layout.GetPositionOfNode(v).Y); Vector2 pos_w = new Vector2(Renderer.Layout.GetPositionOfNode(w).X, Renderer.Layout.GetPositionOfNode(w).Y); if (pos_v == pos_w) { return; } // The vector vw representing one point of an equilateral triangle (v,w,x) with x being the center of the circle Vector2 vw = Vector2.Subtract(pos_w, pos_v); // In an equilateral triangle, the two angles adjacent to vw are alpha float alpha = ((float)Math.PI - Renderer.EdgeCurvatureGamma) / 2f; // Compute the radius based on the sine rule ... float radius = Math.Abs((float)(vw.Length * Math.Sin(alpha) / Math.Sin(Renderer.EdgeCurvatureGamma))); // Compute center point Vector2 center = vw; center.Normalize(); float rotated_x = (float)(center.X * Math.Cos(alpha) - center.Y * Math.Sin(alpha)); float rotated_y = (float)(center.X * Math.Sin(alpha) + center.Y * Math.Cos(alpha)); center.X = rotated_x; center.Y = rotated_y; Vector2 pos_center = Vector2.Add(pos_v, Vector2.Multiply(center, radius)); // The vector xv that will be rotatd in every step ... Vector2 xv = Vector2.Subtract(pos_v, pos_center); // The point on the circle to use for the next line segment Vector2 circle_point = xv; // Initiate the drawing ... // Gradually rotate the vector circle_point to get the points on the circle to connect to a curved edge if (Renderer.orientation(pos_center, pos_v, pos_w) > 0) { for (int i = 0; i <= Renderer.EdgeCurvatureSegments; i++) { points.Add(Vector2.Add(pos_center, circle_point)); float new_x = (float)(circle_point.X * Math.Cos(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) - circle_point.Y * Math.Sin(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); float new_y = (float)(circle_point.X * Math.Sin(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) + circle_point.Y * Math.Cos(Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); circle_point = new Vector2(new_x, new_y); } } else { for (int i = 0; i <= Renderer.EdgeCurvatureSegments; i++) { points.Add(Vector2.Add(pos_center, circle_point)); float new_x = (float)(circle_point.X * Math.Cos(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) - circle_point.Y * Math.Sin(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); float new_y = (float)(circle_point.X * Math.Sin(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments) + circle_point.Y * Math.Cos(-Renderer.EdgeCurvatureGamma / Renderer.EdgeCurvatureSegments)); circle_point = new Vector2(new_x, new_y); } } } // Draw straight edge as a simple line between the vertices else { points.Add(new Vector2(Renderer.Layout.GetPositionOfNode(v).X, Renderer.Layout.GetPositionOfNode(v).Y)); points.Add(new Vector2(Renderer.Layout.GetPositionOfNode(w).X, Renderer.Layout.GetPositionOfNode(w).Y)); } for (int i = 1; i < points.Count; i++) { g.DrawLine(new XPen(color, width), points[i - 1].X - x_offset, points[i - 1].Y - y_offset, points[i].X - x_offset, points[i].Y - y_offset); } }