Skip to content

Instantly share code, notes, and snippets.

@ynurmahomed
Created July 10, 2013 18:53
Show Gist options
  • Save ynurmahomed/5969035 to your computer and use it in GitHub Desktop.
Save ynurmahomed/5969035 to your computer and use it in GitHub Desktop.
Programa openGL para desenhar curvas de bezier
#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