Anda di halaman 1dari 7

'*****

'* Un algoritmo para la creacin de polgonos a partir de estructuras de lneas sin topologa.
'* Gabriel Ortiz, Agosto 2003.
'* Explicacin del algoritmo y cdigo fuente y ejecutable demostrativo descargables en http://recursos.gabrielortiz.com
'*****
Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
'Prcticamente todo el cdigo sucede cuando se produce el evento MouseDown del
'formulario principal. Es decir, cuando el usuario hace click en un punto.
Refresh
px = X 'Recogemos las coordenadas del punto sealado
py = Y
'Iniciamos clculo de la distancia
d_minima = -1
For i = LBound(Lineas) To UBound(Lineas)
Call Separar((Lineas(i)))
Cos_Ang_Extremo1 = (((X2 - X1) * (px - X1)) + ((Y2 - Y1) * (py - Y1))) / ((Sqr((X2 - X1) ^ 2 + (Y2 - Y1) ^ 2)) * (Sqr((px - X1)
^ 2 + (py - Y1) ^ 2)))
Cos_Ang_Extremo2 = (((X1 - X2) * (px - X2)) + ((Y1 - Y2) * (py - Y2))) / ((Sqr((X2 - X1) ^ 2 + (Y2 - Y1) ^ 2)) * (Sqr((px - X1)
^ 2 + (py - Y1) ^ 2)))
If Cos_Ang_Extremo1 <= 0 Then
'Angulo obtuso con el Extremo1, luego cogemos la distancia al Punto1
d = Sqr((X1 - px) ^ 2 + (Y1 - py) ^ 2)
ElseIf Cos_Ang_Extremo2 <= 0 Then
'Angulo obtuso con el Extremo2, luego cogemos la distancia al Punto2
d = Sqr((X2 - px) ^ 2 + (Y2 - py) ^ 2)
Else
'Ambos ngulos son agudos, luego podemos calcular la distancia sobre la recta infinita,
'ya que el punto ms cercano de la recta al punto'cae entre los dos extremos
d = Abs((Y1 - Y2) * px + (X2 - X1) * py + (X1 * Y2 - X2 * Y1)) / Sqr((X2 - X1) ^ 2 + (Y2 - Y1) ^ 2)
End If
If d < d_minima Or d_minima = -1 Then
d_minima = d
x1def = X1: y1def = Y1: x2def = X2: y2def = Y2
End If
Next i
'Calculamos el vrtice medio de la lnea base
xmed = x1def / 2 + x2def / 2: ymed = y1def / 2 + y2def / 2
'Ahora, hay que ver qu vertice es el de la izquierda del punto medio
Call Selec_Vertice(xmed, ymed, px, py, x1def, y1def, x2def, y2def)
'Guardamos el registro del vrtice de inicio y fin del polgono as como la primera lnea del polgono en una nueva matriz
X_Inicio = No_Vx: Y_Inicio = No_Vy 'Extremo derecho de la primera lnea base
ReDim Poligono(0 To 0)
Poligono(0) = "(" & No_Vx & "," & No_Vy & ")-(" & VX & "," & VY & ")"
Do While Not ((x2_selec = X_Inicio And y2_selec = Y_Inicio))
'Ahora miramos qu lneas comparten el vrtice obtenido y seleccionamos la que realiza mayor giro en el sentido contrario
del horario.
j=j+1
If j > UBound(Lineas) + 1 Then Exit Do
angulo_mayor = -1
For i = LBound(Lineas) To UBound(Lineas)
Call Separar((Lineas(i)))
If ((X1 = VX And Y1 = VY) And (X2 <> No_Vx Or Y2 <> No_Vy)) Or ((X2 = VX And Y2 = VY) And (X1 <> No_Vx Or Y1 <>
No_Vy)) Then
If X1 <> VX Then
no_vx_linea = X1
Else

no_vx_linea = X2
End If
If Y1 <> VY Then
no_vy_linea = Y1
Else
no_vy_linea = Y2
End If
'Construimos vectores a partir de lneas restando el vrtice comn, y calculamos sus mdulos
Mod_Lb = Sqr((No_Vx - VX) ^ 2 + (No_Vy - VY) ^ 2)
Mod_L2 = Sqr((no_vx_linea - VX) ^ 2 + (no_vy_linea - VY) ^ 2)
'Calculamos el Coseno del ngulo entre los dos vectores aplicando el producto escalar
cos_ang_lb = ((((No_Vx - VX) * (no_vx_linea - VX)) + ((No_Vy - VY) * (no_vy_linea - VY))) / (Mod_Lb * Mod_L2))
'Aplicamos el Inverso del Coseno, que en visual basic 6 no es una funcin implementada directamente,
'por lo que hay que hacerlo a partir del resto de otras funciones intrnsecas (Atn, Cos y Sqr)
'Con Visual Basic hay que tener en cuenta 3 excepciones para evitar errores.
If cos_ang_lb = "-1" Then 'Si el cos=-1 el ngulo es 180 (-pi Radianes)
ang_lb_Rad = -pi
ElseIf cos_ang_lb = "1" Then 'Si el cos=1 el ngulo es 0
ang_lb_Rad = 0
ElseIf cos_ang_lb = "0" Then 'Si el cos=0 el ngulo es 90 (pi/2 Radianes)
ang_lb_Rad = pi / 2
Else
ang_lb_Rad = Atn(-cos_ang_lb / Sqr(-cos_ang_lb * cos_ang_lb + 1)) + 2 * Atn(1)
End If
'Una vez obtenido el inverso del coseno, lo tenemos en radianes y ahora lo pasamos a grados sexagesimales
ang_lb_grados = ((ang_lb_Rad * 206265) / 60) / 60
'En caso de que dos lneas estn contguas, interpretamos un ngulo de 180
'en vez de ausencia de ngulo (0) que es lo que nos devuelve la frmula
If ang_lb_grados = 0 Then ang_lb_grados = 180
'Vemos a qu lado de la lnea base queda el vrtice de la otra lnea, para saber si hay que restarle de 360
If ((No_Vx - VX) * VY - (No_Vy - VY) * VX) - ((No_Vx - VX) * no_vy_linea - (No_Vy - VY) * no_vx_linea) >= 0 Then
angulo_final = 360 - ang_lb_grados
Else
angulo_final = ang_lb_grados
End If
'Ya tenemos el ngulo final. Ahora vamos seleccionando la lnea que presente un giro mayor
'en sentido contrario de las agujas del reloj
If angulo_final > angulo_mayor Then
angulo_mayor = angulo_final
x1_selec = VX: y1_selec = VY: x2_selec = no_vx_linea: y2_selec = no_vy_linea
End If
End If
Next i
ReDim Preserve Poligono(LBound(Poligono) To UBound(Poligono) + 1)
Poligono(UBound(Poligono)) = "(" & x1_selec & "," & y1_selec & ")-(" & x2_selec & "," & y2_selec & ")"
'Configuro las variables del bucle para la nueva lnea base
VX = x2_selec: VY = y2_selec
No_Vx = x1_selec: No_Vy = y1_selec
Loop
'Pintamos el polgono en el caso de que no est abierto
Separar (Poligono(UBound(Poligono))): x_final = X2: y_final = Y2
If X_Inicio <> x_final Or Y_Inicio <> y_final Then
Label6.Caption = "Haz click en un punto para ver su polgono"
MsgBox "El polgono est abierto por alguno de sus lados, seale uno cerrado", vbExclamation, "Atencin"
Exit Sub
End If

'Redibujamos toda la forma del polgono con un trazo ms grueso


For i = LBound(Poligono) To UBound(Poligono)
DrawWidth = 2
Separar (Poligono(i))
Line (X1, Y1)-(X2, Y2), vbRed
DrawWidth = 1
Next i
'Y operaramos con dicho polgono, por ejemplo calculando su rea y permetro
For i = LBound(Poligono) To UBound(Poligono)
Separar (Poligono(i))
Area = Area + ((X1 * Y2) - (X2 * Y1))
perimetro = perimetro + (Sqr((X1 - X2) ^ 2 + (Y1 - Y2) ^ 2))
Next i
Area = Area / 2
Label6.Caption = "Area= " & Format$(Area, "#.00") & ", Permetro= " & Format$(perimetro, "#.00")
End Sub
Private Sub Separar(Coordenadas As String)
'Con este procedimiento separamos los registros de la matriz que vienen
'con el formato (x1,y1)-(x2,y2), en cuatro variables independientes: x1, y1, x2, y2
If Coordenadas = "(,)-(,)" Then Exit Sub 'Si no existen coordenadas, porque no ha habido
' seleccin de lnea alguna, no hay nada que convertir
X1 = Mid(Coordenadas, (InStr(1, Coordenadas, "(") + 1), (InStr(1, Coordenadas, ",") - InStr(1, Coordenadas, "(") - 1))
Y1 = Mid(Coordenadas, (InStr(1, Coordenadas, ",") + 1), (InStr(1, Coordenadas, ")") - InStr(1, Coordenadas, ",") - 1))
Posicion_p1 = InStr(1, Coordenadas, "(")
Posicion_P2 = InStr(Posicion_p1 + 1, Coordenadas, ")")
Posicion_p3 = InStr(Posicion_P2 + 1, Coordenadas, "(")
Posicion_P4 = InStr(Posicion_p3 + 1, Coordenadas, ")")
Posicion_C1 = InStr(1, Coordenadas, ",")
Posicion_c2 = InStr(Posicion_C1 + 1, Coordenadas, ",")
X2 = Mid(Coordenadas, Posicion_p3 + 1, (Posicion_c2 - Posicion_p3 - 1))
Y2 = Mid(Coordenadas, Posicion_c2 + 1, (Posicion_P4 - Posicion_c2 - 1))
End Sub
Private Sub Selec_Vertice(xmed, ymed, px, py, x1def, y1def, x2def, y2def)
'Procedimiento para seleccionar el vrtice que queda a la izquierda relativa al punto
Dif_Ec = ((xmed - px) * y1def - (ymed - py) * x1def) - ((xmed - px) * py - (ymed - py) * px)
If Dif_Ec > 0 Then
VX = x1def: VY = y1def: No_Vx = x2def: No_Vy = y2def
ElseIf Dif_Ec < 0 Then
VX = x2def: VY = y2def: No_Vx = x1def: No_Vy = y1def
Else
MsgBox "El punto sealado pertenece a una de las lneas. Por favor, seale otro punto que caiga en un nico polgono",
vbCritical
End If
End Sub
Private Sub form_load()
'En este ejemplo, vamos a operar con una malla de lneas almacenadas en una matriz. Se podra trabajar tambin leyendo
un fichero externo
Call Carga_lineas
End Sub
Private Sub Form_Paint()
'Dibujamos un marco donde pintar las lneas en el formulario principal
Line (38, 13)-(160, 83), vbWhite, BF
'Despus del marco con fondo blanco, pintamos los segmentos de lnea
For i = LBound(Lineas) To UBound(Lineas)
Call Separar((Lineas(i)))
Line (X1, Y1)-(X2, Y2), vbBlack

Next i
End Sub
Private Sub Carga_lineas()
'Procedimiento para cargar las lneas, es decir, para crear la matriz con la que operamos en este ejemplo. La nica
'funcin de este procedimiento es alimentar de datos al programa para poder ver cmo funciona.
Num_Lineas = 55
'Matriz para Pares de coordenadas de las lneas
ReDim Lineas(0 To Num_Lineas - 1)
Lineas(0) = "(43,80)-(49,69)": Lineas(1) = "(44,62)-(49,69)": Lineas(2) = "(49,69)-(58,64)": Lineas(3) = "(58,64)-(67,80)"
Lineas(4) = "(58,64)-(68,55)": Lineas(5) = "(58,64)-(51,52)": Lineas(6) = "(51,52)-(43,54)": Lineas(7) = "(51,52)-(69,42)"
Lineas(8) = "(69,42)-(68,55)": Lineas(9) = "(68,55)-(98,65)": Lineas(10) = "(98,65)-(103,80)": Lineas(11) = "(98,65)(108,52)"
Lineas(12) = "(108,52)-(125,57)": Lineas(13) = "(125,57)-(126,65)": Lineas(14) = "(131,36)-(132,30)": Lineas(15) =
"(132,30)-(141,32)"
Lineas(16) = "(141,32)-(146,41)": Lineas(17) = "(146,41)-(154,32)": Lineas(18) = "(141,32)-(145,22)": Lineas(19) =
"(145,22)-(143,18)"
Lineas(20) = "(145,22)-(155,23)": Lineas(21) = "(87,26)-(98,18)": Lineas(22) = "(75,18)-(59,31)": Lineas(23) = "(50,23)(43,31)"
Lineas(24) = "(50,23)-(59,31)": Lineas(25) = "(126,65)-(143,64)": Lineas(26) = "(125,57)-(136,53)": Lineas(27) = "(136,53)(143,64)"
Lineas(28) = "(136,53)-(129,48)": Lineas(29) = "(129,48)-(108,52)": Lineas(30) = "(108,52)-(102,41)": Lineas(31) =
"(126,65)-(117,80)"
Lineas(32) = "(143,64)-(149,80)": Lineas(33) = "(143,64)-(150,51)": Lineas(34) = "(150,51)-(155,52)": Lineas(35) =
"(150,51)-(146,41)"
Lineas(36) = "(146,41)-(154,47)": Lineas(37) = "(146,41)-(136,53)": Lineas(38) = "(129,48)-(131,36)": Lineas(39) =
"(132,30)-(131,18)"
Lineas(40) = "(119,26)-(123,18)": Lineas(41) = "(119,26)-(109,18)": Lineas(42) = "(131,36)-(119,26)": Lineas(43) =
"(119,26)-(102,41)"
Lineas(44) = "(102,41)-(88,37)": Lineas(45) = "(88,37)-(87,26)": Lineas(46) = "(87,26)-(75,18)": Lineas(47) = "(49,18)(50,23)"
Lineas(48) = "(59,31)-(69,42)": Lineas(49) = "(69,42)-(88,37)": Lineas(50) = "(59,31)-(51,52)": Lineas(51) = "(98,18)(109,18)"
Lineas(52) = "(67,80)-(83,77)": Lineas(53) = "(103,80)-(83,77)": Lineas(54) = "(103,80)-(117,80)"
End Sub
'Declaracin de constantes y variables pblicas a nivel de mdulo
Const pi = 3.14159265358979
Public Lineas() As Variant 'Matriz dinmica para guardar las lneas del ejemplo
Public Poligono() As Variant 'Matriz dinmica para guardar las lneas del polgono final
Public X1 As Double 'Variables para almacenamiento temporal de coordenadas
Public Y1 As Double
Public X2 As Double
Public Y2 As Double
Public VX As Double
Public VY As Double
Public No_Vx As Double 'X del extremo que no es el vrtice de trabajo en la lnea base
Public No_Vy As Double 'Y del extremo que no es el vrtice de trabajo en la lnea base
Public X_Inicio As Double 'Y del vrtice inicio y final del polgono
Public Y_Inicio As Double 'X del vrtice inicio y final del polgono

Anda mungkin juga menyukai