Gráficos vectoriales con Visual Basic .NET

Marzo 2003

Cipriano Valdezate Sayalero y Manuel Valdezate Sayalero

 


 

Regiones

 

Una región puede encapsular formas básicas, trayectos y otras regiones. La novedad del objeto Region es que no se limita a añadir sus componentes (de hecho carece de métodos Addxxxx) sino que los combina según una relación entre sus puntos establecida por el usuario. Veremos los diferentes métodos de combinación de formas con un sencillo ejemplo:

 

Private Sub Regiones()

 

        Dim Lienzo As Graphics = Me.CreateGraphics

 

        Dim Trayecto1 As New GraphicsPath()

        Trayecto1.AddEllipse(DameRec(New Point(250, 250), 200, 200))

 

        Dim Trayecto2 As New GraphicsPath()

        Trayecto2.AddEllipse(DameRec(New Point(350, 250), 200, 200))

 

        Dim Región As New Region(Trayecto1)’Creamos un objeto Region

‘compuesto por las formas encapsuladas en el GraphicsPath Trayecto1

 

                Región.Exclude(Trayecto2)’Al añadir el GraphicsPth Trayecto2

                Región.Union(Trayecto2)’indicamos cómo combinamos el contenido

                Región.Intersect(Trayecto2)’del nuevo GraphicsPath Trayecto2 con Trayecto1

                Región.Complement(Trayecto2)’seleccionando uno de estos 5 métodos

                Región.Xor(Trayecto2)

 

                Región.MakeEmpty()

                Región.MakeInfinite()

 

‘Finalmente plasmamos el dibujo resultante mediante

‘el método FillRegion del objeto Graphics

‘pasándole como parámetros una brocha y la región recién creada

Lienzo.FillRegion(Brushes.LightSkyBlue, Región)

 

      ‘Mediante el método DrawPath del objeto Graphics

‘dibujamos las formas encapsuladas en los GraphicPath Trayecto1 y Trayecto2

‘para apreciar mejor el comportamiento de

‘las diferentes opciones de combinación del objeto Region

  Lienzo.DrawPath(Pens.Black, Trayecto1)

        Lienzo.DrawPath(Pens.Black, Trayecto2)

 

        Trayecto1.Dispose()

Trayecto2.Dispose()

Lienzo.Dispose()

 

End Sub

 

Private Function DameRec(ByVal Punto As Point, ByVal EjeX As Integer, ByVal EjeY As Integer) As Rectangle

    Dim MiPunto As Point = New Point(Punto.X - EjeX / 2, Punto.Y - EjeY / 2)

 

‘Devuelve un rectángulo a partir del punto central y sus dos ejes

‘horizontal (EjeX) y vertical (ejeY)

 

    Return New Rectangle(MiPunto, New Size(EjeX, EjeY))

End Function

 

Se trata de pintar un círculo y añadir otro a su derecha según los cinco métodos de combinación de formas. Según qué línea seleccionemos de las cinco señalizadas con un asterisco, el resultado es el siguiente:

 

RG.Union(P2) El círculo nuevo (el de la derecha) se une formando una región compuesta por todos los puntos que pertenercen a cualquiera de las dos formas. Sea x un punto cualquiera de la región resultante, P1 el círculo inicial (el de la izquierda) y P2 el que se añade (el de la derecha), entonces:

 

 

 

RG.Intersect(P2) La región resultante contiene sólo los puntos que pertenecen a las dos formas:

 

 

RG.Xor(P2) La región final contiene los puntos que no comparten las dos formas. Es el opuesto a Intersect

 

 

 

RG.Exclude(P2) La región final contiene todos los puntos pertenecientes a la forma inicial salvo los que pertenecen a la nueva forma añadida, pertenezcan o no a la forma inicial:

 

 

 

 

 

  

RG.Complement(P2) Es el opuesto a Exclude. Se colorean los puntos que pertenecen a la forma añadida salvo los pertenecientes a la forma inicial, pertenezcan o no a la forma añadida:

 

  

 

Intersect es el método creador de lo que en inglés se llama Clip. El efecto obtenido es el “recorte” de una imagen. Se define una región igual a la superficie ocupada por la imagen y otra incluida en ella pero de área menor y transparente. Al intersectar las dos regiones, desaparecen los puntos que no pertenecen a las dos regiones, y como la más pequeña es transparente, permite ver la imagen, que queda “recortada” dentro de aquélla. He aquí un ejemplo:

 

Private Sub PedazoDeTarta ()

        Dim Lienzo As Graphics = Me.CreateGraphics

        Dim Brocha As TextureBrush = New TextureBrush(PictureBox1.Image)

Dim Región As Region = New Region(New Rectangle(0, 0, Me.ClientSize.Width, Me.ClientSize.Height))’La región abarca todo el área cliente

 

Dim Trayecto As New GraphicsPath()

        Dim Radio As Integer = 200

Dim Centro As Point = New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)

        Trayecto.AddPie(DameCuadrado(Centro, Radio), 0, 90 * 3)

 

‘AddPie añade un arco cuyos extremos

‘están unidos al punto central del círculo

‘Sus parámetros coinciden con los del arco

Trayecto.StartFigure()

Trayecto.AddPie(DameCuadrado(New Point(Centro.X + 20, Centro.Y - 20), Radio), 90 * 3, 90)

 

        Región.Intersect(Trayecto)’Aquí está el punto clave: la intersección

 

Lienzo.FillRegion(Brocha, Región)

 

Brocha.Dispose()

End Sub

 

Private Function DameCuadrado(ByVal Punto As Point, ByVal Radio As Integer) As Rectangle

’Devuelve un cuadrado a partir del punto central y el radio

Dim Punto As Point = New Point(Punto.X - Radio, Punto.Y - Radio)

        Return New Rectangle(Punto, New Size(Radio * 2, Radio * 2))

End Function

 

El resultado es la hija de Cipri distribuida dentro de una tarta:

 

Este método, sin embargo, no siempre es capaz de crear un “clip”. Supongamos este gráfico, formado superponiendo la versión circular de nuestro PathGradientBrush sobre las tuberías que pintamos con el LinearGradientBrush:

 

 

Queremos hacer pasar las tuberías azules sobre el círculo, y mantener todo lo demás intacto. Inténtelo el lector con Intersect: nosotros no lo hemos conseguido. Para nuestra fortuna, VSNET ha venido en nuestra ayuda: el método SetClips del objeto Graphics crea una región que recortará las regiones que se combinen con ella. Aplicándolo a nuestro ejemplo, hemos creado una región, marcada en el gráfico con trazo amarillo, que ocupa la superficie cubierta por los tubos azules:

 

 

Del mismo modo que dos regiones pueden combinarse de las cinco maneras diferentes arriba indicadas, la región que se aplique al clip también puede combinarse con él de cualquiera de esas cinco maneras. Elegiremos, por tanto, la intersección. El truco consiste en volver a pintar todas las tuberías sobre el clip. Al haber elegido la intersección, El clip permite que se pinte sólo lo que cabe dentro de él, que hemos procurado que coincida con las tuberías azules. He aquí el código y el resultado:

 

Private Sub ClipTuberías()’Aquí arranca el ejemplo

 

Dim Lienzo As Graphics = Me.CreateGraphics

        Dim Tamaño As Size = New Size(300, 150)

 

        'Primero pintamos los tubos

CreaMosaico(Lienzo, Tamaño)

 

'Hay 7 tubos más una franja de otro. Esa franja es MargenY

Dim MargenY As Decimal = Me.ClientSize.Height * 2 / 70 / 2

 

'Punto central y radio del círculo

Dim Punto As Point = New Point(Me.ClientSize.Width / 2, Me.ClientSize.Height / 2)

        Dim Radio As Decimal = Me.ClientSize.Height / 2 - MargenY

        PintaCírculo(Punto, Radio) 'Dibujamos el círculo

 

'Definimos los rectángulos que forman los tubos azules

        ‘y están señalados en el gráfico con trazo amarillo

Dim ClipRegion As Region = BarrasCírculo(MargenY, Me.ClientSize.Height / (7 - (7 / 2) / 70))

 

       

        Lienzo.SetClip(ClipRegion, CombineMode.Intersect)

        ‘Setclip toma dos parámetros: la región que se combina

        ‘con la región que hace de clip

        ‘y el modo de combinación, enumeración CombineMode

 

  'Tras esta línea,todo lo que dibujemos será invisible

'salvo lo que ocupe la superficie definida por la región

'pasada como argumento a SetClip

 

'Pintamos otra vez el mosaico,

'pero ahora sólo son visibles los tubos azules

CreaMosaico(Lienzo, Tamaño)

 

    End Sub

 

    Private Sub CreaMosaico(ByVal Lienzo As Graphics, ByVal Tamaño As Size)

        Dim i, j As Short

        For i = 0 To Me.ClientSize.Width \ Tamaño.Width

           For j = 0 To Me.ClientSize.Height \ Tamaño.Height

             PintaMosaico(Lienzo, New Point(i * Tamaño.Width, j * Tamaño.Height), Tamaño)

           Next

        Next

End Sub

 

Private Sub PintaCírculo(ByVal Punto As Point, ByVal Radio As Decimal)Comentado más arriba

        Dim Lienzo As Graphics = Me.CreateGraphics

        Dim Trayecto As GraphicsPath = New GraphicsPath()

 

        Trayecto.AddEllipse(DameRec(Punto, Radio, Radio))

        Dim Brocha As PathGradientBrush = New PathGradientBrush(Trayecto)

        Brocha.CenterPoint = New PointF(Punto.X, Punto.Y)

        Dim c As Color = Color.Brown

 

        Dim z As Short = 15

        Dim Posiciones(z) As Single : Dim Colores(z) As Color

 

        Posiciones(0) = 0.0F : Colores(0) = Color.Firebrick

        Posiciones(1) = 0.15F : Colores(1) = Color.Firebrick

        Posiciones(2) = 0.25F : Colores(2) = Color.Goldenrod

        Posiciones(3) = 0.35F : Colores(3) = Color.Firebrick

        Posiciones(4) = 0.525F : Colores(4) = Color.Brown

        Posiciones(5) = 0.6F : Colores(5) = Color.Goldenrod

Posiciones(6) = 0.66F : Colores(6) = Color.Brown

Posiciones(7) = 0.775F : Colores(7) = Color.Brown

Posiciones(8) = 0.825F : Colores(8) = Color.Goldenrod

Posiciones(9) = 0.85F : Colores(9) = Color.Brown

Posiciones(10) = 0.935F : Colores(10) = Color.DarkRed

Posiciones(11) = 0.95F : Colores(11) = Color.Goldenrod

Posiciones(12) = 0.965F : Colores(12) = Color.DarkRed

Posiciones(13) = 0.985F : Colores(13) = Color.DarkRed

Posiciones(14) = 0.995F : Colores(14) = Color.Goldenrod

Posiciones(z) = 1.0F : Colores(z) = Color.Goldenrod

 

Dim Mezcla As New ColorBlend()

        Mezcla.Colors = Colores

Mezcla.Positions = Posiciones

Brocha.InterpolationColors = Mezcla

 

Dim Región As Region = New Region(Trayecto)

Lienzo.FillRegion(Brocha, Región)

 

Brocha.Dispose()

Lienzo.Dispose()

 

End Sub

 

Private Function BarrasCírculo(ByVal MargenY As Decimal, ByVal AnchoBarra As Decimal) As Region

‘Crea la región que servirá de clip

‘y coincide con la señalada con trazos amarillos

Dim i As Short

        Dim Trayecto As New GraphicsPath()

        For i = 1 To Me.ClientSize.Height \ AnchoBarra - 1 Step 2

           Trayecto.AddRectangle(New Rectangle(0, MargenY + i * AnchoBarra, _

           Me.ClientSize.Width, AnchoBarra))

        Next

 

        Return New Region(Trayecto)

    End Function

 

 

 


Índice del curso GDI+
 

la Luna del Guille o... el Guille que está en la Luna... tanto monta...