Skip to content

Instantly share code, notes, and snippets.

@matheusmv
Last active July 1, 2023 20:08
Show Gist options
  • Save matheusmv/6804e47f27002e5f0ed616632b4ccf06 to your computer and use it in GitHub Desktop.
Save matheusmv/6804e47f27002e5f0ed616632b4ccf06 to your computer and use it in GitHub Desktop.
Bézier curve
// g++ bezier-casteljau-opengl.cpp -lSDL2 -lGL -lglut
#include <iostream>
#include <sstream>
#include <vector>
#include <SDL2/SDL.h>
#include <GL/gl.h>
#include <GL/glut.h>
struct Vec2 {
float x;
float y;
};
std::ostream& operator<<(std::ostream& out, const Vec2& vec2) {
return out << "Vec2 { x: " << vec2.x << ", y: " << vec2.y << " }";
}
float lerp(float a, float b, float t) {
return a * (1.0f - t) + b * t;
}
Vec2 lerp2(const Vec2& a, const Vec2& b, float t) {
return {
lerp(a.x, b.x, t),
lerp(a.y, b.y, t)
};
}
Vec2 bezier(const std::vector<Vec2>& points, float t) {
if (points.size() == 1) {
return points[0];
}
std::vector<Vec2> results;
for(std::size_t i = 0; i < points.size() - 1; i++) {
results.emplace_back(lerp2(points[i], points[i + 1], t));
}
return bezier(results, t);
}
std::vector<Vec2> get_all_points(const std::vector<Vec2>& points, float t, float update_factor) {
std::vector<Vec2> result;
for (float i = t; i <= 1.0f; i += update_factor) {
result.emplace_back(bezier(points, i));
}
return result;
}
void render_text(const std::string& text, int x, int y) {
glRasterPos2i(x, y);
for (auto& c : text) {
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, c);
}
}
void render_lines(const std::vector<Vec2>& points, int x, int y) {
glColor3f(0.0f, 1.0f, 0.0f);
glLineWidth(2.0f);
glBegin(GL_LINE_STRIP);
for (const auto& point : points) {
glVertex2f(x + point.x, y + point.y);
}
glEnd();
}
void render_points(const std::vector<Vec2>& points, int x, int y) {
glColor3f(1.0f, 0.0f, 0.0f);
glPointSize(5.0f);
glBegin(GL_POINTS);
for (const auto& point : points) {
glVertex2f(x + point.x, y + point.y);
}
glEnd();
}
void render_values(const std::vector<Vec2>& points, int x, int y, float zoom_factor) {
glColor3f(1.0f, 1.0f, 1.0f);
int text_x = x;
int text_y = y;
std::stringstream ss;
for (auto& point : points) {
ss << point;
int adjusted_text_x = static_cast<int>(text_x / zoom_factor);
int adjusted_text_y = static_cast<int>(text_y / zoom_factor);
render_text(ss.str(), adjusted_text_x, adjusted_text_y);
ss.str("");
text_y -= 20;
}
}
void render_curve(const std::vector<Vec2>& curve, int x, int y) {
glColor3f(1.0f, 1.0f, 1.0f);
glLineWidth(2.0f);
glBegin(GL_LINE_STRIP);
for (auto& point : curve) {
glVertex2f(x + point.x, y + point.y);
}
glEnd();
}
void render_point(const Vec2& point, int x, int y) {
glColor3f(0.0f, 0.0f, 1.0f);
glPointSize(8.0f);
glBegin(GL_POINTS);
glVertex2f(x + point.x, y + point.y);
glEnd();
}
void render(
const std::vector<Vec2>& points,
const std::vector<Vec2>& curve,
int curve_position,
int window_width, int window_height,
float zoom_factor
) {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glOrtho(0, window_width / zoom_factor, 0, window_height / zoom_factor, -1, 1);
float center_x = window_width / (2.0f * zoom_factor);
float center_y = window_height / (2.0f * zoom_factor);
render_lines(points, center_x, center_y);
render_points(points, center_x, center_y);
render_values(points, 10, window_height - 20, zoom_factor);
render_curve(curve, center_x, center_y);
render_point(curve.at(curve_position), center_x, center_y);
}
int main(int argc, char **argv) {
// Initialize SDL2
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
return 1;
}
// Create SDL window
int window_width = 800;
int window_height = 600;
SDL_Window* window = SDL_CreateWindow("Bezier Curve",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
window_width, window_height,
SDL_WINDOW_OPENGL
);
if (!window) {
std::cerr << "Failed to create SDL window: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
// Create SDL OpenGL context
SDL_GLContext glContext = SDL_GL_CreateContext(window);
if (!glContext) {
std::cerr << "Failed to create OpenGL context: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
// Initialize GLUT
glutInit(&argc, argv);
// Set up OpenGL
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
std::vector<Vec2> points = {
{ -200.0f, -100.0f },
{ 0.0f, 200.0f },
{ 200.0f, -100.0f },
{ 0.0f, -100.0f }
};
int curve_point = 0;
float t = 0.0f;
float update_factor = 0.01f;
float zoom_factor = 1.0f;
float zoom_step = 0.1f;
bool running = true;
std::vector<Vec2> curve = get_all_points(points, t, update_factor);
SDL_Event event;
while (running) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
running = false;
}
if (event.type == SDL_MOUSEWHEEL) {
if (event.wheel.y > 0) {
zoom_factor -= zoom_step;
} else {
zoom_factor += zoom_step;
}
if (zoom_factor < 0.1f) {
zoom_factor = 0.1f;
} else if (zoom_factor > 10.0f) {
zoom_factor = 10.0f;
}
}
}
SDL_GetWindowSize(window, &window_width, &window_height);
render(points, curve, curve_point, window_width, window_height, zoom_factor);
curve_point = (curve_point + 1) % curve.size();
SDL_Delay(10);
SDL_GL_SwapWindow(window);
}
// Cleanup
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
struct Vec2 {
float x;
float y;
};
std::ostream& operator<<(std::ostream& out, const Vec2& vec2) {
return out << "Vec2 { x: " << vec2.x << ", y: " << vec2.y << " }";
}
float lerp(float a, float b, float t) {
return a * (1.0f - t) + b * t;
}
Vec2 lerp2(const Vec2& a, const Vec2& b, float t) {
return {
lerp(a.x, b.x, t),
lerp(a.y, b.y, t)
};
}
Vec2 bezier(const std::vector<Vec2>& points, float t) {
if (points.size() == 1) {
return points[0];
}
std::vector<Vec2> results;
for(std::size_t i = 0; i < points.size() - 1; i++) {
results.emplace_back(lerp2(points[i], points[i + 1], t));
}
return bezier(results, t);
}
int main() {
std::vector<Vec2> points = {
{2.0f, 8.3f},
{0.5f, 6.5f},
{5.1f, 4.7f},
{3.3f, 3.1f},
};
for (std::size_t i = 0; i < points.size(); i++) {
float t = static_cast<float>(i) / (points.size() - 1);
Vec2 point = bezier(points, t);
std::cout << point << " t = " << t << std::endl;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment