Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 14 de octubre de 2020

Dibujando la alfombra de Sierpinski en C#

Otra entrada migrada de la vieja web, pero con los enlaces actualizados.

Me topé con esta web https://lodev.org/cgtutor/sierpinski.html que muestra cómo dibujar el fractal de Sierpinski usando C++ y recursividad. El código es bastante simple, así que decidí implementarlo en .Net.

La idea principal para dibujar el triángulo de Sierpinski está en este párrafo:

En realidad el código fuente ya está hecho. La idea era pasarlo a C# y, como valor añadido por pura diversión, meterle punteros.

Primero creé una ventana "Form1" con un PictureBox llamado "PicS":

 

 
Aparte, creé una clase llamada Sierpinski, donde está el código para dibujar el fractal. Form1 sólo posee este código:

  

 
La variable "unaVez" se encarga de que el fractal sólo se dibuje una vez al ejecutar el programa.
El código de la clase Sierpinski es:
 
 
using System;
using System.Drawing;

namespace Sierpinsky
{
    unsafe class Sierpinski
    {
        public Graphics Draw(Graphics g, int ancho, int alto)
        {
            Graphics g0 = g;
            Point* esq = stackalloc Point[3];         
            esq[0].X = 50;
            esq[0].Y = alto - 10;
            esq[1].X = ancho - 10;
            esq[1].Y = alto - 10;
            esq[2].X = ancho/2;
            esq[2].Y = 10;
            Sierp(g0, esq, 0);
            g = g0;                                 
            return g;
        }      

        private void Sierp(Graphics g0, Point* p, int n)
        {
            Pen pen = new Pen(Color.Black, 1);
            g0.DrawLine(pen, p[0], p[1]);
            g0.DrawLine(pen, p[1], p[2]);
            g0.DrawLine(pen, p[2], p[0]);
            Point* p2 = stackalloc Point[3];

            if (n<7)
            {
                p2[0].X = (p[0].X + p[1].X)/2 + (p[1].X - p[2].X)/2;
                p2[0].Y = (p[0].Y + p[1].Y)/2 + (p[1].Y - p[2].Y)/2;
                p2[1].X = (p[0].X + p[1].X)/2 + (p[0].X - p[2].X)/2;
                p2[1].Y = (p[0].Y + p[1].Y)/2 + (p[0].Y - p[2].Y)/2;
                p2[2].X = (p[0].X + p[1].X)/2;

                p2[2].Y = (p[0].Y + p[1].Y)/2;

                Sierp(g0, p2, n+1);

                p2[0].X = (p[1].X + p[2].X)/2 + (p[1].X - p[0].X)/2;
                p2[0].Y = (p[1].Y + p[2].Y)/2 + (p[1].Y - p[0].Y)/2;
                p2[1].X = (p[1].X + p[2].X)/2 + (p[2].X - p[0].X)/2;
                p2[1].Y = (p[1].Y + p[2].Y)/2 + (p[2].Y - p[0].Y)/2;
                p2[2].X = (p[1].X + p[2].X)/2;
                p2[2].Y = (p[1].Y + p[2].Y)/2;

                Sierp(g0, p2, n+1);

                p2[0].X = (p[0].X + p[2].X)/2 + (p[2].X - p[1].X)/2;
                p2[0].Y = (p[0].Y + p[2].Y)/2 + (p[2].Y - p[1].Y)/2;
                p2[1].X = (p[0].X + p[2].X)/2 + (p[0].X - p[1].X)/2;
                p2[1].Y = (p[0].Y + p[2].Y)/2 + (p[0].Y - p[1].Y)/2;
                p2[2].X = (p[0].X + p[2].X)/2;
                p2[2].Y = (p[0].Y + p[2].Y)/2;
                Sierp(g0, p2, n + 1);     
            }
        }
    }
}

 
 
Y el resultado es éste: 

 
 Por ahí debo haber puesto al revés una suma, pero de salir, salió.

 
Si a la función "Sierp" le hago la siguiente modificación:
 
private void Sierp(Graphics g0, Point* p, int n)
{
    Pen pen = new Pen(Color.Black, 1);
    g0.DrawLine(pen, p[0], p[1]);
    g0.DrawLine(pen, p[1], p[2]);
    g0.DrawLine(pen, p[2], p[0]);
    Point* p2 = stackalloc Point[3];

    if (n<7)
    {
        p2[0].X = (p[0].X + p[1].X) / 2 + (p[1].X - p[2].X) / 2;
        p2[0].Y = (p[0].Y + p[1].Y) / 2 + (p[1].Y - p[2].Y) / 2;
        p2[1].X = (p[0].X + p[1].X) / 2 + (p[0].X - p[2].X) / 2;
        p2[1].Y = (p[0].Y + p[1].Y) / 2 + (p[0].Y - p[2].Y) / 2;
        p2[2].X = (p[0].X + p[1].X) / 2;
        p2[2].Y = (p[0].Y + p[1].Y) / 2;

        Sierp(g0, p2, n+1);

        p2[0].X = (p[0].X + p[2].X) / 2 + (p[1].X - p[0].X) / 2;
        p2[0].Y = (p[0].Y + p[2].Y) / 2 + (p[1].Y - p[0].Y) / 2;
        p2[1].X = (p[0].X + p[2].X) / 2 + (p[2].X - p[0].X) / 2;
        p2[1].Y = (p[0].Y + p[2].Y) / 2 + (p[2].Y - p[0].Y) / 2;
        p2[2].X = (p[0].X + p[2].X) / 2;
        p2[2].Y = (p[0].Y + p[2].Y) / 2;

        Sierp(g0, p2, n+1);

        p2[0].X = (p[1].X + p[2].X) / 2 + (p[2].X - p[1].X) / 2;
        p2[0].Y = (p[1].Y + p[2].Y) / 2 + (p[2].Y - p[1].Y) / 2;
        p2[1].X = (p[1].X + p[2].X) / 2 + (p[0].X - p[1].X) / 2;
        p2[1].Y = (p[1].Y + p[2].Y) / 2 + (p[0].Y - p[1].Y) / 2;
        p2[2].X = (p[1].X + p[2].X) / 2;
        p2[2].Y = (p[1].Y + p[2].Y) / 2;

        Sierp(g0, p2, n + 1);              
    }
}

 
El resultado será:
 
 No está mal :)


Lo mismo se puede conseguir modificando un poquito la función Sierp, simplemente añadiendo un puntero:
 



El proyecto completo hecho en Visual Studio Express 2008 se puede descargar de aquí. A diferencia del código en C++, mi código usa una sola función (la función recursiva) para dibujar el fractal.

No hay comentarios: