gráficos vectoriales con visual basic

21

Click here to load reader

Upload: carlos-vazquez

Post on 09-Aug-2015

27 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Gráficos vectoriales con Visual Basic

Gráficos vectoriales con Visual Basic .NETMarzo 2003Cipriano Valdezate Sayalero y Manuel Valdezate Sayalero

Matrices

Introducción

Transformar figuras consiste, como hemos visto, en rotar, trasladar y/o escalar su eje de coordenadas. Intuitivamente, sin embargo, no operamos con ejes de coordenadas, sino con los dibujos mismos. Podríamos, por tanto, definir transformación como un mapeado de puntos, tal que a cada punto de la figura original le correspondiera un punto en la figura transformada o final. El punto de la figura final se obtendría aplicando una operación al de la figura inicial, de tal modo que, si realizáramos una transformación determinada a una figura, todos los puntos de la figura final se obtendrían aplicando la misma operación a sus correspondientes puntos de la figura inicial. Esta operación es una multiplicación y/o suma de matrices, lo cual es lógico, pues podemos considerar un punto como una matriz [x,y] compuesta de una fila y dos columnas.

No nos vamos a adentrar en teoría de matrices ni pretendemos ser rigurosos en las definiciones, ni mucho menos nos preocuparemos aquí de demostrar nada algebráicamente, sino que nos limitaremos a exponer la mínima teoría imprescindible para entender el funcionamiento del objeto Matrix, que nos será de gran utilizad para personalizar nuestras transformaciones.

Una matriz es un conjunto de valores dispuestos en filas y columnas. En esto, una matriz es lo mismo que un array. Igual que los arrays, las matrices tienen dimensones. Las transformaciones de figuras bidimensionales utilizan matrices de dos dimensiones, es decir, compuestas de filas y columnas. He aquí un ejemplo de una matriz de dos filas y dos columnas:

Esta es, precisamente, una de las matrices que se utilizan para obtener los puntos de la figura transformada. Cada elemento de la matriz se define por su posición, así, por ejemplo, m(1,2) es el elemento situado en la primera fila y segunda columna. Ésta es una matriz de orden (2,2), que significa que tiene dos filas y dos columnas. Las matrices cuyo número de filas es igual al número de columnas se llaman matrices cuadradas de orden n, por consiguiente nos podemos referir a esta matriz como una matriz cuadrada de orden 2.

La diferencia entre los arrays y las matrices radica en que sobre la matriz se definen determinadas propiedades y operaciones que la convierten en un array especial. Expondremos a continuación las operaciones que se aplican en el mapeado de puntos, que, como ya hemos dicho, son la suma y el producto de matrices.

Operaciones con matrices

Page 2: Gráficos vectoriales con Visual Basic

Suma de matrices

Sólo se pueden sumar matrices del mismo orden. La matriz suma es otra matriz del mismo orden que se obtiene sumando los elementos de las dos matrices situados en la misma posición. Por ejemplo:

Aunque, como veremos más adelante, el objeto Matrix realiza todas las operaciones que necesitemos por nosotros, vamos a escribir, a título meramente ilustrativo, una función que halle la suma de dos matrices. Le entregaremos un par de arrays de tipo Decimal y de la misma dimensión que harán de matrices, y devolverá otro array cuyos elementos serán la matriz suma:

Public Function Suma(ByVal Matriz1(,) As Decimal, ByVal Matriz2(,) As Decimal) As Decimal(,)'Comprobamos que las dos matrices son del mismo orden, 'es decir, que las dos tienen igual número de filas'e igual número de columnasIf Not Matriz1.GetUpperBound(0) = Matriz2.GetUpperBound(0) AndAlso _Matriz1.GetUpperBound(1) = Matriz2.GetUpperBound(1) Then Exit Function

'Creamos la matriz sumaDim MatrizSuma(Matriz1.GetUpperBound(0), Matriz1.GetUpperBound(1)) As DecimalDim i, j As Short'Y sumamos las matricesFor i = 0 To Matriz1.GetUpperBound(0)For j = 0 To Matriz1.GetUpperBound(1)MatrizSuma(i, j) = Matriz1(i, j) + Matriz2(i, j)NextNextReturn MatrizSumaEnd FunctionPodemos utilizar este procedimiento para porbar la función:Private Sub Prueba_de_Suma()Dim A As Array = New Decimal(,) {{2, 3}, {4, 1}}Dim B As Array = New Decimal(,) {{1, 2}, {3, 4}}Dim R As Decimal(,) = Suma(A, B)MessageBox.Show(String.Format("{0} {1}" _& ControlChars.CrLf & "{2} {3}", _R(0, 0), R(0, 1), R(1, 0), R(1, 1)))End Sub

El resultado es la misma matriz que obtuvimos arriba.

Producto de matrices

Page 3: Gráficos vectoriales con Visual Basic

Sólo se pueden multiplicar dos matrices si el número de columnas de la primera coincide con el número de filas de la segunda. El resultado es una matriz de tantas filas como tiene la primera y tantas columnas la segunda. Además, esta operación no es conmutativa, es decir, el orden de los factores sí altera el producto. Expresado algebraicamente:

A(m,n) x B(n,p) = R(m,p)

De ello se deduce que para que dos matrices se puedan multiplicar en los dos sentidos, es decir, para que podamos efectuar A x B y B x A, las matrices deben ser cuadradas. Ahora bien, la matriz resultante en cada caso sería diferente, porque no es lo mismo A x B que B x A. Esto, como veremos más adelante, tiene una importancia crucial en GDI+, y veremos que variando el orden en que multipliquemos las matrices obtendremos transformaciones diferentes.

Los elementos de la matriz producto R no se calculan multiplicando los correspondientes de las matrices A y B, como es el caso de la suma. Requiere alguna cuenta más. Hemos preparado un pequeño truco fácil de seguir que, además de calcular el producto de matrices, nos servirá de guía para escribir, al igual que hemos hecho en el apartado anterior, una función en Visual Basic que, dadas dos matrices cualesquiera que cumplan los requisitos arriba expuestos, nos calculará su matriz producto.

Vamos a multiplicar las siguientes matrices A x B y obtendremos la matriz R, cuyo número de filas es igual al número de filas de A (2) y cuyo número de columnas es igual al número de columnas de B (2). Asímismo, el número de filas de A coincide con el de columnas de B (2). Puesto que vamos a basarnos en este truco para escribir una función en Visual Basic, numeraremos los índices de cada elemento en base 0.

El truco siguiente halla uno a uno los elementos de la matriz producto R. Hallaremos, para ejemplificarlo, el elemento R(1,0).

Primero escribirmos:

A( , ) * B( , )

Y lo repetimos tantas veces como el número de columnas de la matriz A = número de filas de la matriz B = 3

A( , ) * B( , )A( , ) * B( , )A( , ) * B( , )

El primer índice del elemento que estamos hallando R(1,0) será el primer índice de todos los elementos de A, y el segundo índice R(1,0) será el segundo índice de todos los elementos B:

Page 4: Gráficos vectoriales con Visual Basic

A(1, ) * B( ,0)A(1, ) * B( ,0)A(1, ) * B( ,0)

Añadimos los índices que faltan empezando por cero (o por 1, si hubiésemos definido los índices en base 1) e incrementándolos en una unidad de arriba a abajo:

A(1,0) * B(0 ,0)A(1,1) * B(1 ,0)A(1,2) * B(2 ,0)

Localizamos el valor de cada elemento en las matrices A y B, efectuamos las multiplicaciones y sumamos los resultados:

A(1,0) * B(0 ,0) = 3 * 1 = 3A(1,1) * B(1 ,0) = 2 * 3 = 6A(1,2) * B(2 ,0) = 1 * 2 = 2

R(1,0) = 3 + 6 + 2 = 11

Aplicando este sencillo truco a cada uno de los elementos de R obtenemos la matriz producto R = A x B:

He aquí la función:Public Function Producto(ByVal A(,) As Decimal, ByVal B(,) As Decimal) As Decimal(,)'Comprobamos que las matrices cumplen los requisitosIf A.GetUpperBound(1) <> B.GetUpperBound(0) ThenExit FunctionEnd If

Dim i, j, k As Short'Creamos la matriz productoDim R(A.GetUpperBound(0), B.GetUpperBound(1)) As Decimal'Este array de dos columnas 'es meramente operativo'equivale a las dos columnas 'que hemos creado en el ejemplo:'A(1,0) * B(0 ,0)'A(1,1) * B(1 ,0)'A(1,2) * B(2 ,0)Dim T(A.GetUpperBound(1), 1) As Decimal

'Los dos primeros bucles For'sirven para ir visitando 'todas las posiciones de la matriz RFor i = 0 To R.GetUpperBound(0)For j = 0 To R.GetUpperBound(1)

Page 5: Gráficos vectoriales con Visual Basic

'i contiene el primer índice de R'que colocamos en todas las posiciones'A(i, ) de los elementos de la primera matriz'A la vez, en la segunda posción A( ,k)'vamos aumentando el índice desde cero'hasta el número de columnas de A, 'que coincide con el de filas de B'igual que hemos hecho en el ejemploFor k = 0 To A.GetUpperBound(1)T(k, 0) = A(i, k)Next'j contiene el segundo índice de R'que colocamos en todas las posiciones'B(,j) de los elementos de la segunda matriz'A la vez, en la primera posción B(k, )'vamos aumentando el índice desde cero'hasta el número de filas de B, 'que coincide con el de columnas de A'igual que hemos hecho en el ejemploFor k = 0 To B.GetUpperBound(0)T(k, 1) = B(k, j)Next'Ya tenemos definidos todos los elementos. 'Multiplicamos cada pareja del array T'y vamos acumulando los resultados.'El resultado final lo colocamos en su posición'en la matriz producto R'exactamente igual que en el ejemploFor k = 0 To T.GetUpperBound(0)R(i, j) += T(k, 0) * T(k, 1)Next

NextNext

Return R

End Function

Pruebe el lector a multiplicar las matrices del ejemplo:Private Sub Prueba_de_producto()Dim A(,) As Decimal = {{1, 2, 3}, {3, 2, 1}}Dim B(,) As Decimal = {{1, 2}, {3, 1}, {2, 1}}Dim R(,) As Decimal = Producto(A, B)MessageBox.Show(String.Format("{0} {1}" _& ControlChars.CrLf & "{2} {3}", _R(0, 0), R(0, 1), R(1, 0), R(1, 1)))End Sub

Tipos de matrices

Ahora que sabemos operar con matrices, vamos a tipificar las matrices que generan las tres transformaciones básicas (traslación, rotación y escalado) y otras para las que VSNET no ofrece implementación ad hoc.

Page 6: Gráficos vectoriales con Visual Basic

El método que genera transformaciones multiplicando todos los puntos de la figura origen por la matriz que recibe como parámetro se llama Transform(Matrix). El constructor de la clase Matrix espera 6 parámetros: cuatro de ellos representan la matriz cuadrada que multiplicada por todos los puntos de la figura origen genera diversos tipos de transformaciones, incluida la rotación que acabamos de ver, y los otros dos representan la matriz de una fila y dos columnas que sumada a todos los puntos de la figura origen genera, como también hemos visto más arriba, la figura trasladada. Incluimos en su lugar dentro de las matrices los nombres de los parámetros para que el lector los identifique

Matriz generadora de transformaciones multiplicando por ella los puntos de la figura

origen:

Matriz que sumada a cada punto de la figura origen genera su traslación: [dx, dy]

La sintáxis del constructor del objeto Matrix es la siguiente:

New Matrix(m11, m12, m21, m22, dx, dy)

TraslaciónSe obtiene sumando a cada punto una matriz, obviamente compuesta de una fila ydos columnas:

Punto: [x, y]Matriz de traslación: [dx, dy]

Matriz final: [x, y] + [dx, dy] = [x+dx, y+dy]

El valor dx representa el movimiento por el eje de abcisas X y dy el movimiento porel eje de ordenadas Y.En el apartado anterior hemos visto que el objeto Matrix incluye la matriz de traslación [dx, dy]. Si queremos, por tanto, aplicar una sólo traslación mediante matrices, los elementos mxx han de representar la matriz identidad, que explicamos a continuación. Por ejemplo, si queremos mover una figura 15 píxeles por el eje X y 10 por el eje Y, la línea quedaría así: ("Trayecto" representa un Graphicspath)Trayecto.Transform(New Matrix(1, 0, 0, 1, 15, 10))IdentidadTransformación identidad es aquella que genera una figura exactamente igual, en lamisma posición, tamaño y orientación que la original, y se obtiene entregandocomo parámetro al método Transform la matriz identidad:

10

01

RotaciónConsiste en el giro de los ejes de coordenadas. Se genera entregando al método

Page 7: Gráficos vectoriales con Visual Basic

Transform la matriz siguiente:

)cos()sin(

)sin()cos(

donde a representa los grados de giro medidos en radianes. Los radianes miden lalongitud del arco cortado por los dos radios de longitud = 1 que distan entre sí losgrados especificados. Como la circunferencia mide 2pr y r=1, entonces 360º = 2pradiantes, o lo que es lo mismo, 180º son p radiantes, y por tanto 1 grado =p/180 radianes, de modo que si queremos expresar la matriz en grados, para asíutilizar la misma unidad de medida que entregábamos al método RotateTransform,la matriz de rotación nos queda así:

180*cos(

180*sin(

180*sin(

180*cos(

Aplicando esta matriz obtenemos una figura girada g grados en el sentido de lasagujas del reloj.

A continuación presentamos un ejemplo:

Private Sub Rotación_con_y_sin_matriz()Dim i As ShortDim Lienzo As Graphics = Me.CreateGraphics

'Movemos el eje de coordenadas a un lugar cercano al centrodel formularioLienzo.TranslateTransform(Me.ClientSize.Width / 2, _Me.ClientSize.Height / 2 - 100)

'Medidas del rectánguloDim Esquina As New Point(100, 0)Dim Tamaño As New Size(150, 50)

'Dibujamos una línea desde el origen de coordenadas'hasta el vértice superior izquierdo del rectánguloDim Trayecto1 As New GraphicsPath()Trayecto1.AddLine(New Point(0, 0), Esquina)Dim Lápiz1 As New Pen(Color.LightGreen, 8)Lápiz1.EndCap = LineCap.ArrowAnchorLienzo.DrawPath(Lápiz1, Trayecto1)

'Dibujamos el rectánguloDim Trayecto2 As New GraphicsPath()Trayecto2.AddRectangle(New Rectangle(Esquina, Tamaño))

Page 8: Gráficos vectoriales con Visual Basic

Lienzo.FillPath(Brushes.Blue, Trayecto2)

'Construimos la matriz. Queremos rotar el rectángulo 90 grados'y no queremos trasladar la figura, por eso'los valores dx y dy son cero.Dim D As Single = 90.0R * PI / 180Dim Matriz As Matrix = New Matrix(Cos(D), Sin(D), -Sin(D),Cos(D), 0.0F, 0.0F)'Rotar el objeto Graphics mediante el método RotateTransform...Lienzo.RotateTransform(90) 'Inhabilita esta línea si quieres ver'los métodos Trayecto1.Transform(M) y Trayecto2.Transform(M)en acción'... es lo mismo que entregar al método Tranform de losGraphicPath'la matriz M arriba definidaTrayecto1.Transform(Matriz) 'Inhabilita estas dos líneas siquieresTrayecto2.Transform(Matriz) 'ver el métodoLienzo.RotateTransform(90) en acción

'Dibujamos las figuras encapsuladas en los dos GraphicsPath.'No hay que perder de vista que toda transformación se ejecutasobre'el eje de coordenadas, no sobre las figuras mismas.'Por tanto, tras transformar los ejes de coordenadas,'debemos dibujar "manualmente" las figuras.Lienzo.DrawPath(Lápiz1, Trayecto1)Lienzo.FillPath(Brushes.Blue, Trayecto2)

Trayecto1.Dispose()Trayecto2.Dispose()Lienzo.Dispose()

End Sub

Page 9: Gráficos vectoriales con Visual Basic

El ejemplo siguiente genera esta misma transformación “manualmente” utilizandonuestra función de multiplicación de matrices. Multiplicaremos cada uno de loscuatro vértices del rectángulo por la matriz, y el resultado serán sido los cuatrovértices del rectángulo girado 90º que vemos en el dibujo. De hecho, todas lastranformaciones expuestas en este capítulo pueden simularse manualmenteutilizando nuestras funciones Suma y Producto de matrices.

Private Sub Rotación_manual_Click()Dim i As ShortDim Lienzo As Graphics = Me.CreateGraphics

'Movemos el eje de coordenadas a un lugar cercano al centrodel formularioLienzo.TranslateTransform(Me.ClientSize.Width / 2, _Me.ClientSize.Height / 2 - 100)

'Medidas del rectánguloDim Esquina As New Point(100, 0)Dim Tamaño As New Size(150, 50)

'Dibujamos una línea desde el origen de coordenadas'hasta el vértice superior izquierdo del rectánguloDim Lápiz1 As New Pen(Color.LightGreen, 8)Lápiz1.EndCap = LineCap.ArrowAnchorLienzo.DrawLine(Lápiz1, New Point(0, 0), Esquina)

'Dibujamos el rectángulo originalLienzo.FillRectangle(Brushes.Blue, New Rectangle(Esquina,Tamaño))

Page 10: Gráficos vectoriales con Visual Basic

'Hallamos los vértices del rectánguloDim N1 As Point = Esquina 'NoroesteDim N2 As Point = New Point(N1.X + Tamaño.Width, N1.Y)'NoresteDim N3 As Point = New Point(N2.X, N2.Y + Tamaño.Height)'SuresteDim N4 As Point = New Point(N1.X, N3.Y) 'Suroeste

'Construimos la matriz de rotación.'Queremos rotar el rectángulo 90 gradosDim D As Single = 90.0R * PI / 180Dim Matriz As Array = New Decimal(,) {{Cos(D), Sin(D)}, {-Sin(D), Cos(D)}}

'Hallamos los vértices del rectángulo transformado'Primero hallamos las matrices productoDim K1(,) As Decimal = Producto(New Decimal(,) {{N1.X, N1.Y}},Matriz)Dim K2(,) As Decimal = Producto(New Decimal(,) {{N2.X, N2.Y}},Matriz)Dim K3(,) As Decimal = Producto(New Decimal(,) {{N3.X, N3.Y}},Matriz)Dim K4(,) As Decimal = Producto(New Decimal(,) {{N4.X, N4.Y}},Matriz)

'y luego las convertimos en puntosDim T1 As Point = New Point(K1(0, 0), K1(0, 1))Dim T2 As Point = New Point(K2(0, 0), K2(0, 1))Dim T3 As Point = New Point(K3(0, 0), K3(0, 1))Dim T4 As Point = New Point(K4(0, 0), K4(0, 1))

'y por fin dibujamos el rectángulo transformado "a mano"Lienzo.FillPolygon(Brushes.Blue, New Point() {T1, T2, T3, T4})'y la flechaLienzo.DrawLine(Lápiz1, New Point(0, 0), T1)Lienzo.Dispose()‘El resultado es exactamente el mismo obtenido con la función anterior

End Sub

DeformaciónLa deformación ocurre cuando giramos el eje X k grados y el eje Y l grados y k<> l. Es decir, cada eje gira independientemente del otro. El ángulo que formacada brazo del eje con su contiguo deja de ser necesariamente de noventa grados,y por tanto los ángulos de la figura se alteran. Esta es la matriz:

Page 11: Gráficos vectoriales con Visual Basic

180*cos(

180*sin(

180*sin(

180*cos(

Donde k son los grados que gira el eje X y l los gradpos que gira el eje Y.

Veamos un ejemplo:

Private Sub Deformación()Dim Lienzo As Graphics = Me.CreateGraphics'Trasladamos el origen de coordenadas'cerca del centro del formularioLienzo.TranslateTransform(Me.ClientSize.Width / 2 - 50, _Me.ClientSize.Height / 2 - 100)

'Dibujamos los ejes de coordenadasDim h As Integer = 350Dim lápiz As New Pen(Color.Black, 2)lápiz.DashStyle = DashStyle.DotLienzo.DrawLine(lápiz, -h, 0, h, 0)Lienzo.DrawLine(lápiz, 0, h, 0, -h)

'Dibujamos un rectánguloDim Trayecto1 As New GraphicsPath()Trayecto1.AddRectangle(New Rectangle(0, 0, 250, 100))Lienzo.FillPath(Brushes.LightGreen, Trayecto1)

'Creamos la matriz de transformación'el eje X lo giramos 5 grados'y el eje Y lo giramos 60 gradosDim GiroX As Integer = 5Dim GiroY As Integer = 60Dim Radián As Decimal = PI / 180Dim R As Matrix = New _Matrix(Cos(GiroX * Rad), Sin(GiroX * Rad), _-Sin(GiroY * Radián), Cos(GiroY * Radián), _0.0F, 0.0F)

'Ejecutamos la transformación'y dibujamos el rectángulo transformadoTrayecto1.Transform(R)Lienzo.FillPath(Brushes.Blue, Trayecto1)

End Sub

En el dibujo que genera este código podemos apreciar la alteración de los ángulosde la figura causada por la diferencia de giro de cada eje de coordenadas:

Page 12: Gráficos vectoriales con Visual Basic

SimetríaSi cambiamos ceros por unos y viceversa en la matriz identidad obtenemos lafigura reflejada en un espejo colocado sobre la bisectriz de los ejes de coordenadas,es decir, sobre la línea que divide en dos partes iguales la superficie delimitada porlos ejes +X y +Y. Lo veremos más claro en el ejemplo:

01

10

Private Sub Simetría()Dim i As ShortDim Lienzo As Graphics = Me.CreateGraphics

'Medidas del rectánguloDim Esquina As New Point(250, 50)Dim Tamaño As New Size(250, 100)

'Dibujamos la bisectrizDim Lápiz As New Pen(Color.Black, 4)Lápiz.DashStyle = DashStyle.DotLienzo.DrawLine(Lápiz, 0, 0, Me.ClientSize.Height, _Me.ClientSize.Height)

Page 13: Gráficos vectoriales con Visual Basic

'Dibujamos el rectánguloDim Trayecto1 As New GraphicsPath()Trayecto1.AddRectangle(New Rectangle(Esquina, Tamaño))Lienzo.FillPath(Brushes.Blue, Trayecto1)

'Construimos la matriz de simetría.Dim Matriz As Matrix = _New Matrix(0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F)

'Ejecutamos la transformaciónTrayecto1.Transform(Matriz)

'y dibujamos el rectángulo transformadoLienzo.FillPath(Brushes.Blue, Trayecto1)

Trayecto1.Dispose()Lienzo.Dispose()

End Sub

Si unimos un vértice de un rectángulo con la bisectriz formando una línea

Page 14: Gráficos vectoriales con Visual Basic

perpendicular y hacemos lo mismo con el mismo vértice del rectángulo reflejado,entonces las dos líneas se encuentran formando una sola, y la distancia entre elvértice y la bisectriz es la misma para las dos.

EscaladoComo ya sabemos, el efecto del escalado es el aumento o disminución del tamañode la figura por los ejes X y/o Y. La matriz de escalado es la siguiente:

Y

X

0

0

X = número de veces que escalamos el eje XY = número de veces que escalamos el eje Y

Es lo mismo, por ejemplo,

ScaleTransform(3.0F, 2.0F)

Que

Transform(New Matrix(3.0F, 0.0F, 0.0F, 2.0F, 0.0F, 0.0F)

Escalado de figura simétrica

0

0

Y

X

Es decir, los valores de escalado X e Y los colocamos en el lugar que ocupan trasconvertir la matriz identidad en simétrica. El lector puede fácilmente probar estamatriz sustituyendo la línea

Dim Matriz As Matrix = New Matrix(0.0F, 1.0F, 1.0F, 0.0F, 0.0F, 0.0F)

Del ejemplo de las figuras simétricas por, por ejemplo, esta:

Dim Matriz As Matrix = New Matrix(0.0F, 0.5F, 0.5F, 0.0F, 0.0F, 0.0F)

Que reduce el tamaño del rectángulo simétrico a la mitad.

Combinación de transformaciones

Otra consecuencia del hecho de que las transformaciones se aplican al eje decoordenadas y no al gráfico es que cada transformación se acumula a la anterior.Dos son los tipos posibles de acumulación: suma y multiplicación.Traslación con el resto de transformaciones

Page 15: Gráficos vectoriales con Visual Basic

Como vimos más arriba, la traslación se calcula sumando a la matriz punto [x, y] lamatriz de traslación [dx, dy] (VSNET llama a esta matriz “Offset”), resultando=[x+dx, y+dy]. Puesto que la suma de matrices es conmutativa, es decir, M + N =N + M, no es imprescindible un objeto específico para la matriz [dx, dy]. De hecho,y dado que, además, es indiferente ejecutar primero la transformación y después latraslación o primero la traslación y después la transformación, dx y dy formanparte, como vimos más arriba, de los elementos del objeto Matrix. Así resulta queMatrix, en realidad, contiene dos matrices, y es capaz de ejecutar las dostransformaciones (traslación y cualquier otra) a la vez. Todo lo que tenemos quehacer es, como venimos haciendo en los ejemplos, dar valores a los elementosm11, m12, m21, m22 pertenecientes a la matriz de transformación y a loselementos dx y dy pertenecientes a la matriz de traslación. Se efectuarán las dostransformaciones.

Ahora bien, si queremos que la figura se mueva y no ejecuteninguna otra transformación, entonces los valores de m11, m12, m21 y m22 debenformar la matriz identidad. Por ejemplo:

Trayecto1.Transform(New Matrix(1, 0, 0, 1, 100, 50))

Mueve la figura 100 pixels por el eje de abcisas y 50 por el de ordenadas sinmodificarla. Sólo si también damos valores a mXX entonces se ejecutan las dostransformaciones a la vez.

Multiplicación de transformacionesSi ejecutamos una serie de transformaciones en la misma figura aplicando lamatrices M1, M2, M3, … Mn-1, Mn, entonces podemos evitarnos aplicarlas todas yaplicar sólo una matriz R obteniendo el mismo resultado, y la hallaríamosmultiplicando todas en el mismo orden: R = M1 x M2 x M3 x … x Mn-1 x Mn. Para elloutilizamos el método Multiply(Matrix, MatrixOrder), como vemos en el siguienteejemplo:

Private Sub Combinación()Dim Lienzo As Graphics = Me.CreateGraphics

'Este procedimiento dibuja (casi) todos los pasos'de una serie de transformaciones'aplicadas a un GraphicsPath'Movemos el eje de coordenadas al centro del formularioLienzo.TranslateTransform(Me.ClientSize.Width / 2,Me.ClientSize.Height / 2)

'Dibujamos un círculo en el centro'por pura estéticaDim Trayecto0 As New GraphicsPath()Dim Lápiz As Pen = New Pen(Color.Violet, 4)Dim Rectángulo As Rectangle = New Rectangle(-25, -25, 50, 50)Trayecto0.AddEllipse(Rectángulo)Lienzo.DrawPath(Lápiz, Trayecto0)

'Encapsulamos un cuadrado en un GraphicsPath'pero no lo dibujamos todavíaDim Trayecto1 As New GraphicsPath()Trayecto1.AddRectangle(Rec)

Page 16: Gráficos vectoriales con Visual Basic

'antes de dibujarlo lo transformamos:'multiplicamos por dos su tamañoTrayecto1.Transform(New Matrix(2, 0, 0, 2, 0, 0))

'y lo giramos 45 gradosDim Ángulo As Decimal = 45 * PI / 180Trayecto1.Transform(New _Matrix(Cos(Ángulo), Sin(Ángulo), -Sin(Ángulo), Cos(Ángulo), 0, 0))Lienzo.DrawPath(Lápiz, Trayecto1) 'ahora sí lo dibujamos

Dim i As Short : For i = 0 To 3'Aplicamos cuatro transformaciones seguidas'con la misma matrizTrayecto1.Transform(New Matrix(1.5, 0, 0, 1, 0, 0))Lienzo.DrawPath(Lápiz, Trayecto1)

Next

Trayecto0.Dispose()Trayecto1.Dispose()Lienzo.Dispose()

End Sub

Private Sub Multiplicación()'Este procedimiento ejecuta'las mismas transformaciones que el anterior'pero sólo dibuja el resultado final.'En vez de ir aplicando todas las matrices una a una'las multiplica, y sólo aplica la matriz producto

Dim Ángulo As Decimal = 45 * PI / 180Dim M1 As New Matrix(2, 0, 0, 2, 0, 0)Dim M2 As New _Matrix(Cos(Ángulo), Sin(Ángulo), -Sin(Ángulo), Cos(Ángulo), 0, 0)Dim M3 As New Matrix(1.5, 0, 0, 1, 0, 0)

M1.Multiply(M2, MatrixOrder.Append)'M1.Multiply(M2, MatrixOrder.Append) = M1 * M2'M1.Multiply(M2, MatrixOrder.Prepend) = M2 * M1

Dim i As Short : For i = 0 To 3'Multiply no es una función,'no devuleve la matriz producto'sino que sustituye los valores de M1'por el resultado de la operación'es decir: M1 = M1 * M3M1.Multiply(M3, MatrixOrder.Append)

Next

Dim Lienzo As Graphics = Me.CreateGraphics'Movemos el eje de coordenadas al centro del formularioLienzo.TranslateTransform(Me.ClientSize.Width / 2,Me.ClientSize.Height / 2)

Dim Trayecto0 As New GraphicsPath()Dim Rectángulo As Rectangle = New Rectangle(-25, -25, 50, 50)

Page 17: Gráficos vectoriales con Visual Basic

Trayecto0.AddEllipse(Rectángulo) 'dibujamos un circulito pormera estética

Dim Lápiz As Pen = New Pen(Color.Blue, 4)Lienzo.DrawPath(Lápiz, Trayecto0)Dim Trayecto1 As New GraphicsPath()Trayecto1.AddRectangle(Rectángulo)

'ejecutamos sólo una transformación'aplicando la matriz obtenida de multiplicar'M1 * M2 * M3 * M3 * M3 * M3Trayecto1.Transform(M1)Lienzo.DrawPath(Lápiz, Trayecto1)

Trayecto0.Dispose()Trayecto1.Dispose()Lienzo.Dispose()

End Sub

Mostramos en primer lugar el dibujo generado por el primer procedimiento ydespués el generado por el segundo, y por último mostramos los dos dibujossuperpuestos, donde se ve claramente que la matriz producto de toda la serie dematrices implicadas genera el mismo dibujo que la última matriz de la serie:

Índice del curso GDI+

Page 18: Gráficos vectoriales con Visual Basic