Created
July 10, 2013 18:53
-
-
Save ynurmahomed/5969035 to your computer and use it in GitHub Desktop.
Programa openGL para desenhar curvas de bezier
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <GL/gl.h> | |
#include <GL/glut.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <math.h> | |
#define WINDOW_WIDTH 1080 | |
#define WINDOW_HEIGHT 520 | |
#define CTRL_POINT_DIAMETER 10.0f | |
typedef struct { float x; float y; } point_t; | |
int N_POINTS; | |
int DEGREE; | |
int clickedCtrlPoint; | |
point_t *userCtrlPoints; | |
point_t *realCtrlPoints; | |
// Retorna um ponto da curva de bezier de acordo com o valor de t | |
point_t deCasteljau (point_t ctrlPoints[], float t, int n) { | |
point_t *aux = malloc (sizeof(point_t) * n); | |
memcpy(aux, ctrlPoints, sizeof(point_t) * n); | |
for (int k = 1; k < n; ++k) | |
for (int i = 0; i < n - k; ++i) { | |
aux[i].x = (1 - t) * aux[i].x + t * aux[i + 1].x; | |
aux[i].y = (1 - t) * aux[i].y + t * aux[i + 1].y; | |
} | |
point_t p = aux[0]; | |
free(aux); | |
return p; | |
} | |
// Desenha a curva de grau igual ao numero de pontos de controle inseridos pelo usuário | |
void drawOriginalCurve () { | |
point_t p; | |
float t, incr = 0.001f; | |
glColor3f(0.0f, 0.5f, 0.0f); | |
// A curva | |
glBegin(GL_LINE_STRIP); | |
t = 0.0f; | |
while (t < 1.0f) { | |
p = deCasteljau(userCtrlPoints, t, N_POINTS); | |
glVertex2f(p.x, p.y); | |
t += incr; | |
} | |
glVertex2f(userCtrlPoints[N_POINTS-1].x, userCtrlPoints[N_POINTS-1].y); | |
glEnd(); | |
glFlush(); | |
} | |
// Desenha uma sequencia de curvas de grau 'DEGREE' de acordo com 'realCtrlPoints' | |
void drawCurve () { | |
point_t p; | |
float t, incr = 0.001f; | |
int expandedSize = 2 * N_POINTS - 3; | |
glColor3f(0.208f, 0.0f, 0.0f); | |
glLineWidth(2); | |
// A curva | |
glBegin(GL_LINE_STRIP); | |
for (int i = 0; i < expandedSize - DEGREE; i += DEGREE) { | |
t = 0.0f; | |
while (t < 1.0f) { | |
p = deCasteljau(&realCtrlPoints[i], t, DEGREE + 1); | |
glVertex2f(p.x, p.y); | |
t += incr; | |
} | |
} | |
glVertex2f(realCtrlPoints[expandedSize-1].x, realCtrlPoints[expandedSize-1].y); | |
glEnd(); | |
glFlush(); | |
} | |
// Desenha os pontos de controle (userCtrlPoints) | |
void drawCtrlPoints () { | |
glEnable(GL_POINT_SMOOTH); | |
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); | |
glPointSize(CTRL_POINT_DIAMETER); | |
glColor3f(0.0f, 0.0f, 0.5f); | |
glBegin(GL_POINTS); | |
for (int i = 0; i < N_POINTS; ++i) | |
glVertex2f(userCtrlPoints[i].x, userCtrlPoints[i].y); | |
glEnd(); | |
glFlush(); | |
} | |
void drawBezier () { | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
glClear(GL_COLOR_BUFFER_BIT); | |
drawCurve(); | |
drawOriginalCurve(); | |
drawCtrlPoints(); | |
} | |
void onWindowReshape (GLsizei w, GLsizei h) { | |
glViewport(0, 0, w, h); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
// Faz com que (0,0) corresponda ao canto inferior esquerdo e (WINDOW_WIDTH, WINDOW_HEIGHT) | |
// ao canto superior direito | |
gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT); | |
} | |
// Retorna o quadrado da distância entre dois pontos | |
float distanceSquare (point_t p1, point_t p2) { | |
return pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2); | |
} | |
// Verifica se a distância euclidiana entre um ponto e o centro de um circulo | |
// é menor que o raio do circulo (se o ponto está dentro do circulo :p) | |
int inCircle (point_t point, point_t center, float radius) { | |
//double distance_square = pow(point.x - center.x , 2) + pow(point.y - center.y, 2); | |
return (distanceSquare(point, center) < pow(radius,2)) ? 1 : 0; | |
} | |
// Encontra o índice do ponto de controle que foi clicado | |
int findClickedCtrlPoint (point_t point) { | |
// Queremos o ultimo ponto adicionado nessa posição | |
for (int i = N_POINTS - 1; i >= 0; --i) | |
if (inCircle(point, userCtrlPoints[i], CTRL_POINT_DIAMETER / 2)) | |
return i; | |
return -1; | |
} | |
// Adiciona um novo ponto de controle em 'userCtrlPoints' | |
void addCtrlPoint (point_t p) { | |
N_POINTS++; | |
// o array de pontos de controle sempre começa com uma posição disponível | |
// se o numero de pontos for maior que 1 temos que alocar mais espaço | |
if (N_POINTS > 1) | |
userCtrlPoints = realloc(userCtrlPoints, N_POINTS * sizeof(point_t)); | |
userCtrlPoints[N_POINTS - 1] = p; | |
} | |
// Retorna o ponto médio entre dois pontos | |
point_t midPoint (point_t p1, point_t p2) { | |
point_t p = { .x = (p1.x + p2.x) / 2, .y = (p1.y + p2.y) / 2 }; | |
return p; | |
} | |
void cloneUserCtrlPoints () { | |
realCtrlPoints = realloc(realCtrlPoints, N_POINTS * sizeof(point_t)); | |
memcpy(realCtrlPoints, userCtrlPoints, sizeof(point_t) * N_POINTS); | |
} | |
// Insere os pontos de controle adicionais necessários para desenhar as curvas | |
// de grau 2 | |
void expandRealCtrlPoints () { | |
int expandedSize = 2 * N_POINTS - 3; | |
realCtrlPoints = realloc(realCtrlPoints, expandedSize * sizeof(point_t)); | |
realCtrlPoints[0] = userCtrlPoints[0]; | |
realCtrlPoints[1] = userCtrlPoints[1]; | |
for (int i = 2, j = 0; i < N_POINTS - 1; ++i, ++j) { | |
realCtrlPoints[i + j] = midPoint(userCtrlPoints[i - 1], userCtrlPoints[i]); | |
realCtrlPoints[i + j + 1] = userCtrlPoints[i]; | |
} | |
realCtrlPoints[expandedSize - 1] = userCtrlPoints[N_POINTS - 1]; | |
} | |
// Trata eventos do mouse | |
void onMouseEvent (int button, int state, int x, int y) { | |
point_t p = { .x = x, .y = WINDOW_HEIGHT - y }; | |
// Botão esquerdo em baixo, adicionamos um novo ponto de controle se não | |
// foi clicado um ponto de controle já existente | |
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { | |
//printf("\nmouse x:%d, y:%d", x, WINDOW_HEIGHT - y); | |
clickedCtrlPoint = findClickedCtrlPoint(p); | |
if (clickedCtrlPoint == -1) { | |
addCtrlPoint(p); | |
// se existirem mais pontos que o necessário para desenhar curvas | |
// de grau 'DEGREE' | |
if (N_POINTS > DEGREE + 1) | |
expandRealCtrlPoints(); | |
else | |
cloneUserCtrlPoints(); | |
} | |
} | |
glutPostRedisplay(); | |
} | |
void onMouseMotion (int x, int y) { | |
point_t p = { .x = x, .y = WINDOW_HEIGHT - y }; | |
if (clickedCtrlPoint != -1) { | |
userCtrlPoints[clickedCtrlPoint] = p; | |
if (N_POINTS > DEGREE + 1) | |
expandRealCtrlPoints(); | |
else | |
cloneUserCtrlPoints(); | |
} | |
glutPostRedisplay(); | |
} | |
void init (void) { | |
N_POINTS = 0; | |
DEGREE = 2; | |
userCtrlPoints = (point_t*) malloc(sizeof(point_t)); | |
realCtrlPoints = (point_t*) malloc(sizeof(point_t)); | |
if (userCtrlPoints == NULL) { | |
printf("Erro não foi possível inicializar os pontos de controle"); | |
exit(1); | |
} | |
clickedCtrlPoint = -1; | |
/* | |
point_t p[5] = { | |
{.x = 10.0f, .y = 10.0f}, | |
{.x = 300.0f, .y = 300.0f}, | |
{.x = 450.0f, .y = 145.0f}, | |
{.x = 600.0f, .y = 10.0f}, | |
{.x = 900.0f, .y = 300.0f}, | |
}; | |
userCtrlPoints[0] = p[0]; | |
userCtrlPoints[1] = p[1]; | |
userCtrlPoints[2] = p[2]; | |
userCtrlPoints[3] = p[3]; | |
userCtrlPoints[4] = p[4]; | |
*/ | |
glClearColor(0.9f, 0.9f, 0.9f, 1.0f); | |
//glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | |
} | |
int main(int argc, char *argv[]) { | |
glutInit(&argc, argv); | |
//glutInitDisplayMode(); | |
glutInitWindowSize(WINDOW_WIDTH,WINDOW_HEIGHT); | |
glutInitWindowPosition((glutGet(GLUT_SCREEN_WIDTH)-WINDOW_WIDTH)/2,(glutGet(GLUT_SCREEN_HEIGHT)-WINDOW_HEIGHT)/2); | |
glutCreateWindow("Curva de Bezier"); | |
glutDisplayFunc(drawBezier); | |
glutReshapeFunc(onWindowReshape); | |
glutMouseFunc(onMouseEvent); | |
glutMotionFunc(onMouseMotion); | |
init(); | |
glutMainLoop(); | |
free(userCtrlPoints); | |
return 0; | |
} | |
// gcc -Wall bezier.c -std=c99 -o bezier -lm -lGLU -lGL -lglut |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment