Bézier

As curvas de Bézier têm preocupação com a continuidade. Isso é, funções e derivadas contínuas.


Ilustração 1: Curva contínua



Ilustração 2: Curva descontínua




As curvas são ditas contínuas quando são homeomorfas ao intervalo (0,1], no caso de curvas fechadas, quando são homeomorfos ao círculo unitário. Como já foi citado na introdução, a continuidade é um fator de elevada importância, e as curvas de Bézier tem essa propriedade.

As curvas de Bézier são facilmente implementadas por sua forma polinomial, ou pelo algoritmo de De Castealjau. Vamos abordar principalmente a forma polinomial que é a mais natural, e a mais matematizada.

Para um parâmetro u variando de 0, a 1, temos que as curvas de Bézier podem ser equacionadas por:

Fatores a serem percebidos:

A princípio isto é que nos é importante saber para avaliar de forma bem rústica a utilização de curvas de Bézier no trabalho.

Em contrapartida da interpolação linear, as curvas de Bézier são funções de grau elevado, sendo preciso diversas iterações e diversos cálculos para montagem de uma simples curva, gerando, maioria das vezes, pixels de acumulação1, principalmente nos intervalos de maior curvatura.

As superfícies geradas por curvas de Bézier, podem se forma simplória, serem geradas pela interpolação de curvas transversais. Uma superfície projetada numa tela, que é nosso caso, tende a ser gerada por um conjunto de curvas em direções paralelas e transversais (obviamente, isso não obriga as curvas a serem paralelas, apenas suas direções de apoio).

Para entender a direção de uma curva, pensemos no caso de uma superfície.


Ilustração 3: superfície de Bézier




Observe que o fechos dos pontos a contém completamente, e que ela e formada por diversas linhas apoiadas em projeções paralelas, ainda não completamente. A forma canônica de representação das curvas de Bézier (se baseando no espaço euclidiano, o nosso do dia-a-dia) é trivial, contudo tomando as linhas paralelas e ortogonais como independentes.

A forma polinomial de conjuntos de pontos, que é a que foi implementada, se baseia nesse argumento: as linhas podem ser dadas como independentes, independentemente da direção na qual estejam orientadas2.

A implementação realizada foi a seguinte

void micBezier(double u, struct ponto lista[], int tam){
        
        int k, n=tam-1;//sizeof(lista)-1;
        double b;
        
        struct ponto pt;
        pt.posO.x=0.0f;
        pt.posO.y=0.0f;
        pt.posO.z=0.0f;
        ///impõe a influência de cada struct ponto de controle
        
        for(k=0;k<=n;k++)
                printf("Lista[%i]=(%d,%d,%d)\n",k,lista[k].posO.x,lista[k].posO.y,lista[k].posO.z);
        
        
                for(k=0;k<=n;k++){
                        //b=micC(k,n)*powf(u,k)*powf(1-u,n-k);
                        b = micC(n,k)*potencia(u,k)*potencia(1-u,n-k);
                        
                        printf("k=%d, u=%f; pow(u,k)=%f, powf(1-u,n-k)=%f\n",k, u, potencia(u,k), potencia(1-u,n-k));
                        pt.posO.x+=lista[k].posO.x*b;
                        pt.posO.y+=lista[k].posO.y*b;
                        pt.posO.z+=lista[k].posO.z*b;
                        
                        pt.corO.x+=lista[k].corO.x*b;
                        pt.corO.y+=lista[k].corO.y*b;
                        pt.corO.z+=lista[k].corO.z*b;
                
                        ///Desenha os struct pontos
                        printf("struct ponto k=%i b=%f :(%f,%f,%f)\n",k, b, pt.posO.x, pt.posO.y, pt.posO.z);
                        micPontos(pt, ORTOGONAL);
        }
        
}

Este algoritmo recebe uma estrutura ponto (estruturas podem ser vistas mais detalhadamente na seção sobre detalhes de implementação) e um valor para o parâmetro u, e um valor para o tamanho da lista (em número de pontos). A lista é então implementada como uma curva de Bézier, para desenhar superfícies é necessário implementar o deslocamento da curva na direção transversal a esse, ou seja, implementar listas nas duas direções. Este método é simples porem ineficiente, mas atende ao requisitos do trabalho já que o foco está mais nas matemáticas características de modelagem que nos detalhes de implementação. Fora alguns problemas com estruturas de dados dinâmicas, assim sendo, isto está sendo corrigido.



A função seguinte desenha uma superfície de Bézier, ela apresenta muitos problemas com acesso à memória, mas também está sendo corrigida.

///Esta função tem problemas com a passagem de vetores bidimensionais
void micBezierSup(double u, struct ponto *lista, int tam1, int tam2){
        
        int k, q, m=tam1-1, n = tam2 - 1;//sizeof(lista)-1;
        double b;
        
        struct ponto pt;
        pt.posO.x=0.0f;
        pt.posO.y=0.0f;
        pt.posO.z=0.0f;
        ///impõe a influência de cada struct ponto de controle
        

//      for(k=0;k<=m;k++)
//              printf("Lista[%i]=(%d,%d,%d)\n",k,lista[k].posO.x,lista[k].posO.y,lista[k].posO.z);
//      for(k=0;k<=m;k++)
//              printf("Lista[%i]=(%d,%d,%d)\n",k,lista[k].posO.x,lista[n].posO.y,lista[n].posO.z);

for(q = 0; q<=m; q++){  
                for(k=0;k<=n;k++){
                                //b=micC(k,n)*powf(u,k)*powf(1-u,n-k);
                        b = micC(n,k)*potencia(u,k)*potencia(1-u,n-k);
                                
                        printf("k=%d, u=%f; pow(u,k)=%f, powf(1-u,n-k)=%f\n",k, u, potencia(u,k), potencia(1-u,n-k));
                        pt.posO.x+=lista[q][k]->posO.x*b;
                        pt.posO.y+=lista[q][k]->posO.y*b;
                        pt.posO.z+=lista[q][k]->posO.z*b;
                                
                        pt.corO.x+=lista[q][k]->corO.x*b;
                        pt.corO.y+=lista[q][k]->corO.y*b;
                        pt.corO.z+=lista[q][k]->corO.z*b;
                
                        ///Desenha os struct pontos
                        printf("struct ponto k=%i b=%f :(%f,%f,%f)\n",k, b, pt.posO.x, pt.posO.y, pt.posO.z);
                        micPontos(pt, ORTOGONAL);
                }
        }
        
                for(q = 0; q<=n; q++){  
                        for(k=0;k<=m;k++){
                                //b=micC(k,n)*powf(u,k)*powf(1-u,n-k);
                        b = micC(m,k)*potencia(u,k)*potencia(1-u,n-k);
                                
                        printf("k=%d, u=%f; pow(u,k)=%f, powf(1-u,n-k)=%f\n",k, u, potencia(u,k), potencia(1-u,n-k));
                        pt.posO.x+=*lista[k][q].posO.x*b;
                        pt.posO.y+=*lista[k][q].posO.y*b;
                        pt.posO.z+=*lista[k][q].posO.z*b;
                                
                        pt.corO.x+=*lista[k][q].corO.x*b;
                        pt.corO.y+=*lista[k][q].corO.y*b;
                        pt.corO.z+=*lista[k][q].corO.z*b;
                
                        ///Desenha os struct pontos
                        printf("struct ponto k=%i b=%f :(%f,%f,%f)\n",k, b, pt.posO.x, pt.posO.y, pt.posO.z);
                        micPontos(pt, ORTOGONAL);
                        }
                }

}
É fácil perceber que está apenas uma ampliação da função anterior para vetores bidimensionais.

Como já foi dito, há problemas com estruturas dinâmicas, mas a função seguinte faz o encadeamento de um ponto numa superfície de Bézier, observe que ela o insere no último nodo, tanto na horizontal como na vertical, criando estruturas de lista que podem ser varridas com algoritmos paralelos.

micInserepontoFim(struct ponto *lista, struct ponto *nodo){
        
        struct ponto *temp = lista;
        
        nodo->id = 1;
        
        if(temp!=NULL){
                while(temp->frente!=NULL)
                        temp = temp->frente;
                nodo->id = temp->id + 1;
                temp->frente = nodo;
                temp->frente->baixo = temp->baixo->frente ;
                temp = temp->frente;
        }
        temp = nodo;
        
        nodo->frente = NULL;
}
A função seguinte remove o ponto da lista:

int micRemovePonto(struct ponto *lista, struct ponto *nodo, double id){
        struct ponto *temp = lista;
        struct ponto *temp2;
        
        while((temp != NULL) || (temp->id != id)){
                temp2 = temp;
                temp = temp->frente;
        }
        if(temp = NULL)
                return NAO;
        else{
                temp2->frente = temp->frente;
                temp2->baixo = temp->baixo;
                apaga(temp);
        }
        return SIM;
}
Os Binomiais do polinômio de Bézier são calculados pela seguinte função:

int micC(int m, int p){
        printf("Combinação de m, p a p\n");
        int i, pfat = 1, mfat = 1, mpfat = 1;
        printf("\tIgualou a 1\n");
        ///C(m,p) = m!/p!(m-p)!
        
        if(m<p){
                int temp = m;
                m = p;
                p = temp;
                printf("\tTrocou m por P\n");
        }
        
        if(!(m==1 || m==0))
                for(i=m; i>1; i--)
                        mfat *= i;
        printf("Calculou m!\n");
        
        if(!(p==0 || p==1))
                for(i=p; i>1; i--)
                        pfat *= i;
        printf("Calculou p!\n");
        
        if(!((m-p)==0 || (m-p)==1))
                for(i=(m-p); i>1; i--)
                        mpfat *= i;
        printf("Calculou (m-p)!");
        
        printf("C(%i,%i)=%i\n",m, p, mfat/(pfat*mpfat));
        
        return (mfat/(pfat*mpfat));
        
        
        
}
E a função que eleva um numero a outro é a seguinte:

double potencia(double base, int expoente){
        int i;
        
        double res = 1.0;
        
        if(base == 0)
                return 0.0;
        
        if(expoente != 0)
                for(i=1; i<=expoente; i++)
                        res *= base;
        
        return res;
}
Até então, a parte específica sobre Bézier se encerra aqui, as mesmas funções de Base podem ser usadas para expansão em splines, já que a natureza das splines e das curvas de Bézier e muito parecida no quesito implementação. 

1Pixels que acumulam diversos pontos da imagem, isto é ineficiente, em geral não causa problemas mais complexos.

2Nos experimentos do trabalho foi deduzido que isso é bem verdade, porém é não é suficiente para a interpolação de cores