Initial commit

This commit is contained in:
Robert 2020-09-22 21:56:44 +02:00
parent b8564d1d94
commit 7570369ea4
12 changed files with 412 additions and 1 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.vs
out
*.json

16
CMakeLists.txt Normal file
View file

@ -0,0 +1,16 @@
# CMakeList.txt : Top-level CMake project file, do global configuration
# and include sub-projects here.
#
cmake_minimum_required (VERSION 3.8)
project ("Bezier Curves")
set(CMAKE_CXX_STANDARD 17)
find_package(SDL2 REQUIRED)
if(NOT ${SDL2_FOUND})
message(FATAL_ERROR "Could not locate SDL2. Aborting.")
endif()
# Include sub-projects.
add_subdirectory ("src")

View file

@ -1 +1,6 @@
# BezierCurves # Bezier Curves
I was trying to understand how Bézier Curves are created/calculated,
so I created this program to draw them.
Graphics done in SDL2

92
src/BezierCurve.cpp Normal file
View file

@ -0,0 +1,92 @@
#include "BezierCurve.hpp"
#include <math.h>
#include <iostream>
#include <SDL.h>
Uint32 BinomCoeff(int n, int k)
{
return (tgamma(n+1) / (tgamma(k+1) * tgammal(n-k+1)));
}
BezierCurve::BezierCurve()
{
vertices.fill(nullptr);
lines.fill(nullptr);
}
void BezierCurve::AddVertex(int x, int y)
{
vertices[numVertices++] = new Vertex(x, y);
if (numVertices > 1)
lines[numLines++] = new Line(vertices[numVertices - 2], vertices[numVertices - 1]);
}
void BezierCurve::Confirm()
{
if (numLines > 1)
{
lines[numLines] = new Line(vertices[numLines], vertices[0]);
numLines++;
}
inProgress = false;
ConstructBezier(0.05);
}
void BezierCurve::SetPreviewVertex(int x, int y)
{
if (vertices[numVertices] == nullptr)
vertices[numVertices] = new Vertex(x, y, Vertex::Cursor);
else
vertices[numVertices]->SetPos(x, y);
}
void BezierCurve::ConstructBezier(float resolution)
{
int grade = numVertices - 1;
if (vertices[grade] != nullptr && inProgress)
grade++;
if (grade < 1)
return;
bezier.clear();
std::vector<Uint32> binoms;
for (int i = 0; i <= grade; i++)
binoms.push_back(BinomCoeff(grade, i));
SDL_FPoint vert;
for (float t = 0; t <= 1.f; t += resolution)
{
vert = { 0, 0 };
for (int i = 0; i <= grade; i++)
{
float f = binoms[i] * pow(t, i) * pow(1 - t, grade - i);
vert.x += f * vertices[i]->x;
vert.y += f * vertices[i]->y;
}
bezier.push_back(vert);
}
vert = { (float)vertices[grade]->x, (float)vertices[grade]->y };
bezier.push_back(vert);
}
void BezierCurve::Draw(SDL_Renderer* renderer)
{
for (int i = 0; i < numVertices; i++)
vertices[i]->Draw(renderer);
for (int i = 0; i < numLines; i++)
lines[i]->Draw(renderer);
if (bezier.size() != 0)
{
SDL_SetRenderDrawColor(renderer, Color[0], Color[1], Color[2], Color[3]);
SDL_RenderDrawLinesF(renderer, &bezier[0], bezier.size());
}
}

39
src/BezierCurve.hpp Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include <array>
#include <vector>
#include "UI/Vertex.hpp"
#include "UI/Line.hpp"
struct SDL_Renderer;
struct SDL_FPoint;
class BezierCurve : public IDrawable
{
public:
inline static unsigned char Color[4] = { 20, 230, 20, 255 };
public:
BezierCurve();
void AddVertex(int x, int y);
void Confirm();
void SetPreviewVertex(int x, int y);
uint8_t GetVertexCount() const { return numVertices; }
void ConstructBezier(float resolution);
void Draw(SDL_Renderer* renderer) override;
private:
uint8_t numVertices = 0;
uint8_t numLines = 0;
std::array<Vertex*, 4> vertices;
std::array<Line*, 4> lines;
std::vector<SDL_FPoint> bezier;
bool inProgress = true;
};

31
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,31 @@
add_executable(beziercurves
"main.cpp"
"UI/IDrawable.hpp" "UI/Vertex.hpp" "UI/Vertex.cpp" "BezierCurve.hpp" "BezierCurve.cpp" "UI/Line.hpp" "UI/Line.cpp")
if(WIN32)
target_include_directories(beziercurves PRIVATE
SDL2::SDL2
)
else()
target_include_directories(beziercurves PRIVATE
${SDL2_INCLUDE_DIRS}
)
endif()
if(WIN32)
target_link_libraries(beziercurves PRIVATE
SDL2::SDL2
)
else()
target_link_libraries(beziercurves PRIVATE
${SDL2_LIBRARIES} m
)
endif()
if(WIN32)
add_custom_command(TARGET beziercurves POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${SDL2_DIR}/../bin/ $<TARGET_FILE_DIR:beziercurves>
)
endif()

12
src/UI/IDrawable.hpp Normal file
View file

@ -0,0 +1,12 @@
#pragma once
struct SDL_Renderer;
class IDrawable
{
public:
virtual void Draw(SDL_Renderer* renderer) = 0;
protected:
IDrawable() = default;
};

14
src/UI/Line.cpp Normal file
View file

@ -0,0 +1,14 @@
#include "Line.hpp"
#include <SDL.h>
Line::Line(const Vertex* a, const Vertex* b) :
a(a), b(b)
{
}
void Line::Draw(SDL_Renderer* renderer)
{
SDL_SetRenderDrawColor(renderer, Color[0], Color[1], Color[2], Color[3]);
SDL_RenderDrawLine(renderer, a->x, a->y, b->x, b->y);
}

19
src/UI/Line.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include "IDrawable.hpp"
#include "Vertex.hpp"
class Line : public IDrawable
{
public:
inline static unsigned char Color[4] = { 200, 200, 200, 255 };
public:
Line(const Vertex* a, const Vertex* b);
void Draw(SDL_Renderer* renderer) override;
private:
const Vertex* a;
const Vertex* b;
};

27
src/UI/Vertex.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "Vertex.hpp"
#include <SDL.h>
Vertex::Vertex(unsigned int x, unsigned int y, unsigned char* color) :
rect(new SDL_Rect),
col(color)
{
SetPos(x, y);
}
void Vertex::Draw(SDL_Renderer* renderer)
{
SDL_SetRenderDrawColor(renderer, col[0], col[1], col[2], col[3]);
SDL_RenderFillRect(renderer, rect);
}
void Vertex::SetPos(int x, int y)
{
this->x = x;
this->y = y;
rect->x = x - Size / 2;
rect->y = y - Size / 2;
rect->w = Size;
rect->h = Size;
}

26
src/UI/Vertex.hpp Normal file
View file

@ -0,0 +1,26 @@
#pragma once
#include "IDrawable.hpp"
struct SDL_Rect;
class Vertex : public IDrawable
{
public:
inline static unsigned char Color[4] = { 230, 20, 20, 255 };
inline static unsigned char Cursor[4] = { 230, 20, 230, 255 };
inline static unsigned int Size = 10;
public:
Vertex(unsigned int x, unsigned int y, unsigned char* color = Color);
void Draw(SDL_Renderer* renderer) override;
void SetPos(int x, int y);
unsigned int x, y;
private:
SDL_Rect* rect;
unsigned char* col;
};

126
src/main.cpp Normal file
View file

@ -0,0 +1,126 @@
#include <iostream>
#include <vector>
#include <SDL.h>
#include "BezierCurve.hpp"
#undef main
std::vector<BezierCurve*> curves;
void AddVertex(int x, int y, BezierCurve** curve);
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Bezier Curves",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 800,
SDL_WINDOW_SHOWN);
if (window == nullptr)
{
std::cerr << "Could not create window" << std::endl;
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
if (renderer == nullptr)
{
std::cerr << "Could not create renderer" << std::endl;
return 1;
}
SDL_Event e;
int mouseX = 0, mouseY = 0;
SDL_GetMouseState(&mouseX, &mouseY);
BezierCurve* currentCurve = nullptr;
Vertex cursor(mouseX, mouseY, Vertex::Cursor);
bool isOpen = true;
while (isOpen)
{
while (SDL_PollEvent(&e))
{
if(e.type == SDL_WINDOWEVENT)
{
switch (e.window.event)
{
case SDL_WINDOWEVENT_CLOSE:
isOpen = false;
break;
}
}
switch (e.type)
{
case SDL_MOUSEMOTION:
SDL_GetMouseState(&mouseX, &mouseY);
cursor.SetPos(mouseX, mouseY);
if (currentCurve != nullptr)
{
currentCurve->SetPreviewVertex(mouseX, mouseY);
currentCurve->ConstructBezier(0.05);
}
break;
case SDL_MOUSEBUTTONDOWN:
if (e.button.button == SDL_BUTTON_LEFT)
AddVertex(mouseX, mouseY, &currentCurve);
if (e.button.button == SDL_BUTTON_RIGHT)
{
if (currentCurve == nullptr)
break;
if (currentCurve->GetVertexCount() > 1)
{
currentCurve->Confirm();
curves.push_back(currentCurve);
}
currentCurve = nullptr;
}
break;
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
for (BezierCurve* curve : curves)
curve->Draw(renderer);
if(currentCurve != nullptr)
currentCurve->Draw(renderer);
cursor.Draw(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
void AddVertex(int x, int y, BezierCurve** curve)
{
if (*curve == nullptr)
(*curve) = new BezierCurve;
(*curve)->AddVertex(x, y);
if ((*curve)->GetVertexCount() == 4)
{
(*curve)->Confirm();
curves.push_back(*curve);
*curve = nullptr;
}
}