// Last edited on 2015-06-09 20:25:30 by stolfilocal // Tubos e conexoes com retalhos de bezier #macro tubo_verif_indices(i, j, di, dj) // Verifica a validade dos índices de uma borda de retalho de Bézier. // Os índices {i,j} devem ser 0 ou 3. // #if (((i != 0) & (i != 3)) | ((j != 0) & (j != 3))) #error concat("indices invalidos ", str(i,1,0), " ", str(j,1,0), "\n") #end #if ((abs(di) + abs(dj) != 1) | (i + di < 0) | (i + di > 3) | (j + dj < 0) | (j + dj > 3)) #error concat("passos invalidos ", str(di,1,0), " ", str(dj,1,0), "\n") #end #end #macro tubo_costura(A, iA, jA, diA, djA, B, iB, jB, diB, djB) // Costura dois retalhos de Bézier definidos pelas matrizes // de pontos de controle {A[4][4]} e {B[4][4]} com continuidade // de primeira ordem. Para isso, os 4 pontos de controle // ao longo de uma borda de {A} e outra borda de {B} // são identificados com a média dos pontos vizinhos, nas camadas // interiores paralelas a essas bordas. // // O primeiro par de pontos a definir é {A[iA][jA]} e {B[iB][jB]}. // Os demais pontos da borda de {A} são obtidos incrementando {iA,jA} // com {diA,djA}, e {iB,jB} com {diB,djB}. Os parâmetros // {iA} e {jA} devem ser 0 ou 3; {diA} e {djA} devem ser {+1,00}, {-1,00}, // {00,+1} ou {00,-1}. Idem para o retalho {B}. // // Especificamente, para cada {k} em {0..3}, os pontos {A[iA + k*diA][jA + k*djA]} // e {B[iB + k*diB][jB + k*djB]} são posicionados na média dos pontos // {A[iA' + k*diA][jA' + k*djA]} e {B[iB' + k*diB][jB' + k*djB]}, // onde {A[iA'][jA']} e {B[iB'][jB']} são os primeiros pontos da // camada vizinha. tubo_verif_indices(iA,jA, diA, djA) tubo_verif_indices(iB,jB, diB, djB) // Determina indices da camada vizinha de {A}: #if (diA = 0) // Borda horizontal #local jA1 = jA; #if (iA = 0) #local iA1 = 1; #else #local iA1 = 2; #end #else // Borda vertical #local iA1 = iA; #if (jA = 0) #local jA1 = 1; #else #local jA1 = 2; #end #end // Determina indices da camada vizinha de {B}: #if (diB = 0) // Borda horizontal #local jB1 = jB; #if (iB = 0) #local iB1 = 1; #else #local iB1 = 2; #end #else // Borda vertical #local iB1 = iB; #if (jB = 0) #local jB1 = 1; #else #local jB1 = 2; #end #end // Faz colagem: #local k = 0; #while (k < 4) #local p = (A[iA1 + k*diA][jA1 + k*djA] + B[iB1 + k*diB][jB1 + k*djB])/2; #local A[iA + k*diA][jA + k*djA] = p; #local B[iB + k*diB][jB + k*djB] = p; #local k = k + 1; #end #end #macro tubo_retalho_padrao(A,p) // Inicializa a matriz {A[4][4]} dos pontos de controle de um retalho de Bézier // coomo um quadrado paralelo ao plano {x,y}. Especificamente, // define {A[i][j]} como {p + }; #local i = 0; #while (i < 4) #local j = 0; #while (j < 4) #local A[i][j] = p + < j,i,0 >; #local j = j + 1; #end #local i = i + 1; #end #end #macro tubo_linha_reta(A, i,j,di,dj, p,dp) // Atribui o ponto {p + r*rp} a todos os elementos {A[i + r*di][j + r*dj]} // para {r} em {0..3} #local r = 0; #while (r < 4) #local A[i + r*di][j + r*dj] = p + r*dp; #local r = r + 1; #end #end #macro tubo_define_boca(PP,PN,NP,NN, ik, Ok,Rk,Sk,dk,tk) // Define os 8 + 8 pontos de Bézier livres numa boca de um tubo. // // A boca tem centro {Ok}. O ponto {Ok} e os vetores {Rk,Sk} definem // o plano da boca, que é quase circular e passa pelos quatro pontos // {Ok±Rk} e {Ok±Sk}. // Supõe que o tubo é formado por 4 retalhos de Bézier definids pelas // matrizes de pontos de controle {PP,PN,NP,NN}, que serão unidos // pelas laterais (elementos {[i][0]} e {[i][3]}, para {i} em {0..3}). // // Os pontos de controle da linha {ik} (0 ou 3) de cada matriz ficam // no plano da boca. Os pontos de controle da linha adjacente {mk} // ({mk=1} se {ik=0], {mk=2} se {ik=3}) são os mesmos da linha {ik}, // deslocados por uma distãncia {dk} perpendicularmente ao plano da // boca. // // Os retalhos {PP,PN,NP,NN} ficam nos quadrantes {++,+-,-+,--} // relativos aos vetores {Rk,Sk}. Os pontos {[ik][0]} dos 4 retalhos // são {Ok±Rk} e os pontos {[ik][3]} são {Ok±Sk}. // // O parâmetro {tk} determina o chanfro da boca. Se {tk} não for 0.0, // os pontos da linha {mk} são expandidos por um fator {1 + tk} nas // direções paralelas ao plano, em relação ao seu centro. // // Vetor unitário perpendicular à boca: #local Tk = vnormalize(vcross(Rk,Sk)); #local aa = 0.553; // Determine incremento {sk} do índice da linha da borda para a linha adjacente: #if (ik = 0) #local sk = +1; #else #local sk = -1; #end #local j = 0; #while (j < 2) // Índice da linha a definir: #local i = ik + sk*j; // Pontos de controle da linha {i} #local fi = 1 + j*tk; #local Ci = Ok + j*dk*Tk; #local PP[i][1] = Ci + fi*(+ 1*Rk + aa*Sk); #local PP[i][2] = Ci + fi*(+ aa*Rk + 1*Sk); #local PN[i][1] = Ci + fi*(+ 1*Rk - aa*Sk); #local PN[i][2] = Ci + fi*(+ aa*Rk - 1*Sk); #local NP[i][1] = Ci + fi*(- 1*Rk + aa*Sk); #local NP[i][2] = Ci + fi*(- aa*Rk + 1*Sk); #local NN[i][1] = Ci + fi*(- 1*Rk - aa*Sk); #local NN[i][2] = Ci + fi*(- aa*Rk - 1*Sk); #local j = j + 1; #end #end #macro tubo_2(O1,R1,S1,d1,t1, O2,R2,S2,d2,t2, rad, txg, tx0,tx1,tx2,tx3) // Gera um tubo com duas bocas dadas pelos parametros. // // As duas bocas tem centros {O1} e {O2}. O ponto {O1} e os vetores // {R1} e {S1} definem o plano da boca 1, que é quase circular e passa // pelos quatro pontos {O1±R1} e {O1±S1}. O parâmetro {d1} é a // distância entre a primeira e segunda camada de pontos de Bézier da // boca, e {t1} determina o chanfro da boca. Veja detalhes na macro // {tubo_define_boca}. Idem para a boca 2. Para que o tubo não fique // torcido, os vetores {R1} e {R2} devem ser aproximadamente // paralelos, e igualmente para {S1} e {S2}. // // Os parâmetros canônicos do tubo são {O1=<0,0,0>},{O2=<0,0,4>}, // {R1=R2=x}, {S1=S2=y}, {d1=1}, {d2=-1}, {t1=t2=0} para um tubo // cilíndrico centrado no eixo {z}. // // As texturas {tx0,tx1,tx2,tx3} são aplicadas aos quatro retalhos. Se // as quatro texturas são a mesma imagem com {uv_mapping}, ela é // aplicada ao tubo, em toda sua volta. O eixo vertical da imagem é // aplicado ao longo do tubo, no sentido da boca 1 para a boca 2. O // eixo horizontal da imagem é enrolado em torno do tubo, começando no // ponto da boca 1 que fica na direção do vetor {R1} e no ponto da // boca 2 que fica na direção do vetor {R2}. Cada retalho usa 1/4 da // imagem na direção horizontal. // O tubo é formado por 4 retalhos de Bézier unidos pelas laterais // (pontos {B[k][0]} e {B[k][3]} para {k} em {0..3}). #local PP = array[4][4]; #local PN = array[4][4]; #local NP = array[4][4]; #local NN = array[4][4]; tubo_define_boca(PP,PN,NP,NN, 0, O1,R1,S1,d1,t1) tubo_define_boca(PP,PN,NP,NN, 3, O2,R2,S2,d2,t2) tubo_costura(PP,0,0,1,0, PN,0,0,1,0) tubo_costura(NP,0,0,1,0, NN,0,0,1,0) tubo_costura(PP,0,3,1,0, NP,0,3,1,0) tubo_costura(NN,0,3,1,0, PN,0,3,1,0) union{ object{ retalho(PP, rad, txg, tx0, 0.00,1, 0.25,0) } object{ retalho(NP, rad, txg, tx1, 0.50,1, 0.25,0) } object{ retalho(NN, rad, txg, tx2, 0.50,1, 0.75,0) } object{ retalho(PN, rad, txg, tx3, 1.00,1, 0.75,0) } } #end #macro tubo_3(O0, X0, Y0, Z0, A1,O1,R1,S1,d1,t1, A2,O2,R2,S2,d2,t2, A3,O3,R3,S3,d3,t3, rad, txg, tx0,tx1,tx2,tx3) // Gera uma junta de tubos com três bocas dadas pelos parametros. // // A parte central da junção (o /miolo/) tem centro aproximado {O0} e suas // dimensões são definidas pelos vetores {X0,Y0,Z0}, tipicamente // perpendiculares entre si. As tres pernas da junçao começam saindo // do ponto {O0} em direções {u1,u2,u3} paralelas ao plano definido // pela base {O0,X0,Y0}. Mais precisamente, as direções {u1,u2,u3} // formam ângulos {A1,A2,A3} (graus) com o vetor {X0}, respectivamente, // na direção do vetor {Y0}. O vetor {Z0} é normalmente perpendicular a // {X0} e {Y0}. // // As tres bocas tem centros {O1}, {O2} e {O3}. O ponto {O1} e os // vetores {R1} e {S1} definem o plano da boca 1, que é quase circular // e passa pelos quatro pontos {O1±R1} e {O1±S1}. O parâmetro {d1} é a // distância entre a primeira e segunda camada de pontos de Bézier, e // {t1} determina o chanfro da boca. Veja detalhes na macro // {tubo_define_boca}. Idem para as bocas 2 e 3. // // Para que as pernas não fiquem torcidas, os ângulos {A1,A2,A3} // precisam estar em ordem anti-horária; os vetores {R1}, {R2} e {R3} // devem ser aproximadamente paralelos ao vetor {Z0}; e os vetores // {S1}, {S2}, e {S3} devem ser aproximadamente perpendiculares aos // vetores {u1,u2,u3}, no sentido anti-horário relativo ao vetor {Z0}. // // Os parâmetros canônicos da junta são {O0=<0,0,0>}, {X0=x}, {Y0=y}, {Z0=z}, // {A1=0}, {O1=4*vrotate(x,A1*z)}, {R1=z}, {S1=vrotate(y,A1*z)}, // {d1=1}, {t1=0}, e analogamente para {A2=120} e {A3=240}. // // As texturas {tx0,tx1,tx2,tx3} são aplicadas aos quatro retalhos de // cada perna. Se as quatro texturas são a mesma imagem com // {uv_mapping}, ela é aplicada à perna em toda sua volta. O eixo // vertical da imagem é aplicado ao longo da perna, no sentido do // miolo para a boca. O eixo horizontal da imagem é enrolado em torno // da perna, começando no ponto da boca 1 que fica na direção do vetor // {R1}, e analogamente para as outas pernas. Cada retalho usa 1/4 da // imagem na direção horizontal. // Cada perna é formada por 4 retalhos de Bézier, unidos pelas laterais e // (pontos {B[k][0]} e {B[k][3]} para {k} em {0..3}) e pela borda central // (pontos {B[0][j]} para {j} em {0..3}). #local PP1 = array[4][4]; #local PN1 = array[4][4]; #local NP1 = array[4][4]; #local NN1 = array[4][4]; #local PP2 = array[4][4]; #local PN2 = array[4][4]; #local NP2 = array[4][4]; #local NN2 = array[4][4]; #local PP3 = array[4][4]; #local PN3 = array[4][4]; #local NP3 = array[4][4]; #local NN3 = array[4][4]; tubo_define_boca(PP1,PN1,NP1,NN1, 3, O1,R1,S1,d1,t1) tubo_define_boca(PP2,PN2,NP2,NN2, 3, O2,R2,S2,d2,t2) tubo_define_boca(PP3,PN3,NP3,NN3, 3, O3,R3,S3,d3,t3) // Direções iniciais das pernas, e ortogonais a elas: // Coeficientes para pontos da camada 1: #local cu1 = 1.2; #local cv1 = 0.553; #local cz1 = 1.0; #local cu2 = 1.5; #local cv2 = 1.0; #local cz2 = 0.553; // camada 1 da perna 1: #local ar1 = radians(A1); #local u1 = + cos(ar1)*X0 + sin(ar1)*Y0; // Direção da perna. #local v1 = - sin(ar1)*X0 + cos(ar1)*Y0; // Direção transversal anti-hor. #local PP1[1][1] = O0 + cu1*u1 + cv1*v1 + cz1*Z0; #local PP1[1][2] = O0 + cu2*u1 + cv2*v1 + cz2*Z0; #local NP1[1][1] = O0 + cu1*u1 + cv1*v1 - cz1*Z0; #local NP1[1][2] = O0 + cu2*u1 + cv2*v1 - cz2*Z0; #local PN1[1][1] = O0 + cu1*u1 - cv1*v1 + cz1*Z0; #local PN1[1][2] = O0 + cu2*u1 - cv2*v1 + cz2*Z0; #local NN1[1][1] = O0 + cu1*u1 - cv1*v1 - cz1*Z0; #local NN1[1][2] = O0 + cu2*u1 - cv2*v1 - cz2*Z0; // camada 1 da perna 2 #local ar2 = radians(A2); #local u2 = + cos(ar2)*X0 + sin(ar2)*Y0; // Direção da perna. #local v2 = - sin(ar2)*X0 + cos(ar2)*Y0; // Direção transversal anti-hor. #local PP2[1][1] = O0 + cu1*u2 + cv1*v2 + cz1*Z0; #local PP2[1][2] = O0 + cu2*u2 + cv2*v2 + cz2*Z0; #local NP2[1][1] = O0 + cu1*u2 + cv1*v2 - cz1*Z0; #local NP2[1][2] = O0 + cu2*u2 + cv2*v2 - cz2*Z0; #local PN2[1][1] = O0 + cu1*u2 - cv1*v2 + cz1*Z0; #local PN2[1][2] = O0 + cu2*u2 - cv2*v2 + cz2*Z0; #local NN2[1][1] = O0 + cu1*u2 - cv1*v2 - cz1*Z0; #local NN2[1][2] = O0 + cu2*u2 - cv2*v2 - cz2*Z0; // camada 1 da perna 3 #local ar3 = radians(A3); #local u3 = + cos(ar3)*X0 + sin(ar3)*Y0; // Direção da perna. #local v3 = - sin(ar3)*X0 + cos(ar3)*Y0; // Direção transversal anti-hor. #local PP3[1][1] = O0 + cu1*u3 + cv1*v3 + cz1*Z0; #local PP3[1][2] = O0 + cu2*u3 + cv2*v3 + cz2*Z0; #local NP3[1][1] = O0 + cu1*u3 + cv1*v3 - cz1*Z0; #local NP3[1][2] = O0 + cu2*u3 + cv2*v3 - cz2*Z0; #local PN3[1][1] = O0 + cu1*u3 - cv1*v3 + cz1*Z0; #local PN3[1][2] = O0 + cu2*u3 - cv2*v3 + cz2*Z0; #local NN3[1][1] = O0 + cu1*u3 - cv1*v3 - cz1*Z0; #local NN3[1][2] = O0 + cu2*u3 - cv2*v3 - cz2*Z0; // Zera a linha 0 para evitar elem indefinido na costura: tubo_linha_reta(PP1, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(PN1, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NP1, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NN1, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(PP2, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(PN2, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NP2, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NN2, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(PP3, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(PN3, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NP3, 0,0,0,1, O0,<0,0,0>) tubo_linha_reta(NN3, 0,0,0,1, O0,<0,0,0>) // Costuras laterais das pernas: tubo_costura(PP1,0,0,1,0, PN1,0,0,1,0) tubo_costura(NP1,0,0,1,0, NN1,0,0,1,0) tubo_costura(PP1,0,3,1,0, NP1,0,3,1,0) tubo_costura(NN1,0,3,1,0, PN1,0,3,1,0) tubo_costura(PP2,0,0,1,0, PN2,0,0,1,0) tubo_costura(NP2,0,0,1,0, NN2,0,0,1,0) tubo_costura(PP2,0,3,1,0, NP2,0,3,1,0) tubo_costura(NN2,0,3,1,0, PN2,0,3,1,0) tubo_costura(PP3,0,0,1,0, PN3,0,0,1,0) tubo_costura(NP3,0,0,1,0, NN3,0,0,1,0) tubo_costura(PP3,0,3,1,0, NP3,0,3,1,0) tubo_costura(NN3,0,3,1,0, PN3,0,3,1,0) // Costuras entre as pernas: tubo_costura(PP1,0,0,0,1, PN2,0,0,0,1) tubo_costura(NP1,0,0,0,1, NN2,0,0,0,1) tubo_costura(PP2,0,0,0,1, PN3,0,0,0,1) tubo_costura(NP2,0,0,0,1, NN3,0,0,0,1) tubo_costura(PP3,0,0,0,1, PN1,0,0,0,1) tubo_costura(NP3,0,0,0,1, NN1,0,0,0,1) // Pontos centrais: #local PC = (PN1[1][1] + PP1[1][1] + PN2[1][1] + PP2[1][1] + PN3[1][1] + PP3[1][1])/6; #local PN1[0][0] = PC; #local PP1[0][0] = PC; #local PN2[0][0] = PC; #local PP2[0][0] = PC; #local PN3[0][0] = PC; #local PP3[0][0] = PC; #local NC = (NP1[1][1] + NN2[1][1] + NP2[1][1] + NN3[1][1] + NP3[1][1] + NN1[1][1])/6; #local NN1[0][0] = NC; #local NP1[0][0] = NC; #local NN2[0][0] = NC; #local NP2[0][0] = NC; #local NN3[0][0] = NC; #local NP3[0][0] = NC; union{ object{ retalho(PP1, rad, txg, tx0, 1.00,1, 0.75,0) } object{ retalho(NP1, rad, txg, tx1, 0.50,1, 0.75,0) } object{ retalho(NN1, rad, txg, tx2, 0.50,1, 0.25,0) } object{ retalho(PN1, rad, txg, tx3, 0.00,1, 0.25,0) } object{ retalho(PP2, rad, txg, tx0, 1.00,1, 0.75,0) } object{ retalho(NP2, rad, txg, tx1, 0.50,1, 0.75,0) } object{ retalho(NN2, rad, txg, tx2, 0.50,1, 0.25,0) } object{ retalho(PN2, rad, txg, tx3, 0.00,1, 0.25,0) } object{ retalho(PP3, rad, txg, tx0, 1.00,1, 0.75,0) } object{ retalho(NP3, rad, txg, tx1, 0.50,1, 0.75,0) } object{ retalho(NN3, rad, txg, tx2, 0.50,1, 0.25,0) } object{ retalho(PN3, rad, txg, tx3, 0.00,1, 0.25,0) } } #end