none
Representación gráfica de grafos en C# RRS feed

  • Pregunta

  • Buenas tardes.

    Estoy realizando un programa en el cual representar grafos de forma grafica usando GDI/GDI+. La cuestion es que aun no consigo dos cosas:

    1- Agregar el numero de vertice a un vertice ya creado.

    2- Insertar una arista que conecte a dos vertices, tambien deseo eliminar una arista seleccionada usando un boton, ya hice lo propio con los vertices.

    Para dar una prueba mas clara, dejo aqui una imagen de lo que deseo lograr:

    Ademas, quiero que las aristas puedan moverse junto con los vertices cada que muevo un vertice de posicion en el panel de dibujo, cosa que logré con los circulos que representarán los vertices.

    Les dejo el codigo que segun yo es mas relevante para este problema (y de paso, mostrar los avances que he podido realizar):

    // Parte donde se creará el vertice o la arista dentro del pictureBox al presionar el boton del mouse

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {
                if(e.Button != MouseButtons.Left)
                {
                    return;
                }
    
                verticeSelec = revisaVerticeSelec(e.Location);
                if(verticeSelec == null && insertaVertice == true)
                {
                    numVer++;
                    cantVert++;
                    textBox4.Text = cantVert.ToString();
                    verticeSelec = new Vertice(e.Location, 20, numVer);
                    dibujandoVer = true;
                }
                else if(verticeSelec != null && insertaVertice == true)
                {
                    arrastrandoVer = true;
                    movido = new Point(verticeSelec.punto.X - e.Location.X, verticeSelec.punto.Y - e.Location.Y);
                }
                
                if(verticeSelec != null && eliminaVer == true)
                {
                    cantVert--;
                    textBox4.Text = cantVert.ToString();
                }
    
                if(insertaAristND == true)
                {
                    ancho2 = e.Location;
                }
            }

    // Aqui, si se mueve el mouse sobre un vertice seleccionado, dicho vertice cambiara de lugar a donde lo movamos dentro del panel cuando se mantenga presionado el boton del mouse

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
            {
                if(dibujandoVer)
                {
                    pictureBox1.Invalidate();
                }
                else if(arrastrandoVer)
                {
                    verticeSelec.punto = new Point(e.Location.X + movido.X, e.Location.Y + movido.Y);
                    pictureBox1.Invalidate();
                }
                else if(dibujandoAristND)
                {
                    //ancho2 
                }
            }

    // Aqui es donde se le dice al vertice que se quede en las coordenadas donde lo hemos arrastrado o se elimine el vertice seleccionado

     private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
            {
                if(e.Button != MouseButtons.Left)
                {
                    return;
                }
    
                if(verticeSelec == null)
                {
                    return;
                }
    
                if(arrastrandoVer)
                {
                    verticeSelec.punto = new Point(e.Location.X + movido.X, e.Location.Y + movido.Y);
                }
                else if(dibujandoVer)
                {
                    vertices.Add(verticeSelec);
                }
                else if(eliminaVer)
                {
                    vertices.Remove(verticeSelec);
                }
                arrastrandoVer = false;
                dibujandoVer = false;
                pictureBox1.Invalidate();
            }

    // Se dibujarán los vertices dentro del pictureBox

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                g.Clear(Color.White);
    
                foreach(var circulo in vertices.Where(c=>c!= verticeSelec))
                {
                    g.DrawEllipse(p, (float)(circulo.punto.X - circulo.radio), (float)(circulo.punto.Y - circulo.radio), (float)(circulo.radio*2), (float)(circulo.radio*2));
                }
                
                if(verticeSelec != null)
                {
                    g.DrawEllipse(p, (float)(verticeSelec.punto.X - verticeSelec.radio), (float)(verticeSelec.punto.Y - verticeSelec.radio), (float)(verticeSelec.radio*2), (float)(verticeSelec.radio*2));
                }
            }
    Muchas gracias por sus respuestas, espero y puedan ayudarme con esto :)

    martes, 8 de septiembre de 2015 2:36

Respuestas

  • using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Drawing.Drawing2D;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            List<Nodo> nodos;
    
            const int RADIO = 20;
    
            Nodo selOrig;
            Nodo selNodo;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                DoubleBuffered = true;
                nodos = new List<Nodo>();
            }
    
            private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
            {
                nodos.Add(new Nodo(e.X, e.Y, RADIO));
                Refresh();
            }
    
            private void Form1_Paint(object sender, PaintEventArgs e)
            {
                foreach (Nodo item in nodos)
                {
                    item.DibujaArista(e.Graphics);
                }
                foreach (Nodo item in nodos)
                {
                    item.DibujaNodo(e.Graphics);
                }
            }
    
            private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                //detecta
                Nodo n = null;
                foreach (Nodo item in nodos)
                {
                    if (item.Adentro(e.Location))
                    {
                        n = item;
                        break;
                    }
                }
    
                // conecta dos nodos
                if (e.Button == System.Windows.Forms.MouseButtons.Right)
                {
    
                    if (selOrig == null)
                    {
                        selOrig = n;
                    }
                    else
                    {
                        if (n != null) selOrig.ConectarA(n);
                        selOrig = null;
                        Refresh();
                    }
                }
    
                // inicia movimiento del nodo
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    selNodo = n;
                }
            }
    
            private void Form1_MouseMove(object sender, MouseEventArgs e)
            {
                if (selNodo == null) return;
    
                // mueve nodo
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    selNodo.Posicion(e.Location);
                    Refresh();
                }
    
            }
    
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                //elimina el nodos seleccionado
                if (e.KeyCode == Keys.Delete)
                {
                    if (selNodo != null)
                    {
                        nodos.Remove(selNodo);
                        foreach (Nodo item in nodos)
                        {
                            item.Desconectar(selNodo);
                        }
                        Refresh();
                        selNodo = null;
                    }
                }
            }
     
        }
    
        class Nodo
        {
            static int orden = 0;
    
            int num;
            Point centro;
            int radio;
            Rectangle rec;
    
            GraphicsPath gp;
            Pen circ;
            Pen lin;
            Font letra;
            List<Nodo> conec;
    
            public Nodo(int x, int y, int r)
            {
                centro = new Point(x, y);
                radio = r;
                rec = new Rectangle(x - r, y - r, 2 * r, 2 * r);
    
                gp = new GraphicsPath();
                gp.AddEllipse(rec);
    
                circ = new Pen(Brushes.Black, 3);
                letra = new Font("arial", 12);
    
                num = ++orden;
                conec = new List<Nodo>();
    
                lin = new Pen(Brushes.DimGray, 3);
                GraphicsPath gpLin = new GraphicsPath();
                gpLin.AddLine(new Point(0, 0), new Point(3, -3));
                gpLin.AddLine(new Point(0, 0), new Point(-3, -3));
                lin.CustomEndCap = new CustomLineCap(null, gpLin);
            }
    
            public virtual void DibujaNodo(Graphics g)
            {
                g.FillPath(Brushes.Goldenrod, gp);
                g.DrawPath(circ, gp);
                g.DrawString(num.ToString(), letra, Brushes.Black, rec, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            }
    
            public virtual void DibujaArista(Graphics g)
            {
                foreach (Nodo item in conec)
                {
                    double tg = (double)(centro.Y - item.centro.Y) / (item.centro.X - centro.X);
                    double atg = Math.Atan(tg);
                    
                    int a = (int)(radio * Math.Cos(atg));
                    int b = (int)(radio * Math.Sin(atg));
    
                    if ((centro.X < item.centro.X))
                    {
                        a *= -1;
                        b *= -1;
                    }
    
                    Point p = new Point(item.centro.X + a, item.centro.Y - b);
                    g.DrawLine(lin, centro, p);
                }
            }
    
            public bool Adentro(Point pt)
            {
                return gp.IsVisible(pt);
            }
    
            public void Posicion(Point pt)
            {
                gp.Transform(new Matrix(1, 0, 0, 1, pt.X - centro.X, pt.Y - centro.Y)); 
                centro = pt;
                rec = Rectangle.Round(gp.GetBounds()); 
            }
    
            public void ConectarA(Nodo n)
            {
                conec.Add(n);
            }
    
            public void Desconectar(Nodo n)
            {
                conec.Remove(n);
            }
        }
    }

    Acá te dejo un ejemplo breve y sencillo. 
    Doble clic derecho para agregar un nodo.
    clic izquierdo sobre el nodo y luego Delete, para eliminarlo.
    Al nodo se lo arrastra con el botón izquierdo.
    Las aristas se crean picando con el botón derecho en un nodo y luego en otro. Esto también determina la dirección con la que se conectan los vértices.
    Digamos que el api de windows ha dejado atrás al gdi/gdi+. Existen nuevas apis para esto (en net, wpf es un wapper parcial de esas nuevas apis), pero no están disponibles para winforms.
    Aqui se pinta sobre un form. Vos usarías como canvas, el client area de tu propia clase derivada de Control. O sea, crearías tu propio control.


    martes, 8 de septiembre de 2015 19:17

Todas las respuestas

  • hola

    me pregunto, porque te matas con GDI si existen componentes que permiten diseños graficos

    como ser

    NShape

    Diagram.NET

    por supuesto estos son de libre uso, si quieres algo mejor hay compomentes de pago

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 8 de septiembre de 2015 4:36
  • using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Drawing.Drawing2D;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            List<Nodo> nodos;
    
            const int RADIO = 20;
    
            Nodo selOrig;
            Nodo selNodo;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                DoubleBuffered = true;
                nodos = new List<Nodo>();
            }
    
            private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
            {
                nodos.Add(new Nodo(e.X, e.Y, RADIO));
                Refresh();
            }
    
            private void Form1_Paint(object sender, PaintEventArgs e)
            {
                foreach (Nodo item in nodos)
                {
                    item.DibujaArista(e.Graphics);
                }
                foreach (Nodo item in nodos)
                {
                    item.DibujaNodo(e.Graphics);
                }
            }
    
            private void Form1_MouseDown(object sender, MouseEventArgs e)
            {
                //detecta
                Nodo n = null;
                foreach (Nodo item in nodos)
                {
                    if (item.Adentro(e.Location))
                    {
                        n = item;
                        break;
                    }
                }
    
                // conecta dos nodos
                if (e.Button == System.Windows.Forms.MouseButtons.Right)
                {
    
                    if (selOrig == null)
                    {
                        selOrig = n;
                    }
                    else
                    {
                        if (n != null) selOrig.ConectarA(n);
                        selOrig = null;
                        Refresh();
                    }
                }
    
                // inicia movimiento del nodo
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    selNodo = n;
                }
            }
    
            private void Form1_MouseMove(object sender, MouseEventArgs e)
            {
                if (selNodo == null) return;
    
                // mueve nodo
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    selNodo.Posicion(e.Location);
                    Refresh();
                }
    
            }
    
            private void Form1_KeyDown(object sender, KeyEventArgs e)
            {
                //elimina el nodos seleccionado
                if (e.KeyCode == Keys.Delete)
                {
                    if (selNodo != null)
                    {
                        nodos.Remove(selNodo);
                        foreach (Nodo item in nodos)
                        {
                            item.Desconectar(selNodo);
                        }
                        Refresh();
                        selNodo = null;
                    }
                }
            }
     
        }
    
        class Nodo
        {
            static int orden = 0;
    
            int num;
            Point centro;
            int radio;
            Rectangle rec;
    
            GraphicsPath gp;
            Pen circ;
            Pen lin;
            Font letra;
            List<Nodo> conec;
    
            public Nodo(int x, int y, int r)
            {
                centro = new Point(x, y);
                radio = r;
                rec = new Rectangle(x - r, y - r, 2 * r, 2 * r);
    
                gp = new GraphicsPath();
                gp.AddEllipse(rec);
    
                circ = new Pen(Brushes.Black, 3);
                letra = new Font("arial", 12);
    
                num = ++orden;
                conec = new List<Nodo>();
    
                lin = new Pen(Brushes.DimGray, 3);
                GraphicsPath gpLin = new GraphicsPath();
                gpLin.AddLine(new Point(0, 0), new Point(3, -3));
                gpLin.AddLine(new Point(0, 0), new Point(-3, -3));
                lin.CustomEndCap = new CustomLineCap(null, gpLin);
            }
    
            public virtual void DibujaNodo(Graphics g)
            {
                g.FillPath(Brushes.Goldenrod, gp);
                g.DrawPath(circ, gp);
                g.DrawString(num.ToString(), letra, Brushes.Black, rec, new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            }
    
            public virtual void DibujaArista(Graphics g)
            {
                foreach (Nodo item in conec)
                {
                    double tg = (double)(centro.Y - item.centro.Y) / (item.centro.X - centro.X);
                    double atg = Math.Atan(tg);
                    
                    int a = (int)(radio * Math.Cos(atg));
                    int b = (int)(radio * Math.Sin(atg));
    
                    if ((centro.X < item.centro.X))
                    {
                        a *= -1;
                        b *= -1;
                    }
    
                    Point p = new Point(item.centro.X + a, item.centro.Y - b);
                    g.DrawLine(lin, centro, p);
                }
            }
    
            public bool Adentro(Point pt)
            {
                return gp.IsVisible(pt);
            }
    
            public void Posicion(Point pt)
            {
                gp.Transform(new Matrix(1, 0, 0, 1, pt.X - centro.X, pt.Y - centro.Y)); 
                centro = pt;
                rec = Rectangle.Round(gp.GetBounds()); 
            }
    
            public void ConectarA(Nodo n)
            {
                conec.Add(n);
            }
    
            public void Desconectar(Nodo n)
            {
                conec.Remove(n);
            }
        }
    }

    Acá te dejo un ejemplo breve y sencillo. 
    Doble clic derecho para agregar un nodo.
    clic izquierdo sobre el nodo y luego Delete, para eliminarlo.
    Al nodo se lo arrastra con el botón izquierdo.
    Las aristas se crean picando con el botón derecho en un nodo y luego en otro. Esto también determina la dirección con la que se conectan los vértices.
    Digamos que el api de windows ha dejado atrás al gdi/gdi+. Existen nuevas apis para esto (en net, wpf es un wapper parcial de esas nuevas apis), pero no están disponibles para winforms.
    Aqui se pinta sobre un form. Vos usarías como canvas, el client area de tu propia clase derivada de Control. O sea, crearías tu propio control.


    martes, 8 de septiembre de 2015 19:17