Last active
July 1, 2023 20:08
-
-
Save matheusmv/6804e47f27002e5f0ed616632b4ccf06 to your computer and use it in GitHub Desktop.
Bézier curve
This file contains 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
// 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; | |
} |
This file contains 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 <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