Skip to content

Instantly share code, notes, and snippets.

@kim366
Last active June 14, 2018 19:00
Show Gist options
  • Save kim366/f204a2ecd53e305d525d1866a6077bb5 to your computer and use it in GitHub Desktop.
Save kim366/f204a2ecd53e305d525d1866a6077bb5 to your computer and use it in GitHub Desktop.
#include <SFML/Graphics.hpp>
#include <cassert>
#include <array>
#include <algorithm>
// #define CATCH_CONFIG_MAIN
// #include <catch.hpp>
void draw_lines(const std::vector<sf::Vector2f>& vector_, sf::RenderTarget& target_, sf::Color color_ = sf::Color::White)
{
for (auto it{vector_.cbegin()}; it != vector_.cend();)
{
auto current_it{it};
if (++it != vector_.cend())
{
sf::Vertex line[2]
{
{*current_it, color_},
{*it, color_}
};
target_.draw(line, 2, sf::Lines);
}
}
}
float interpolate(float t, float a, float b)
{
return (b - a) * t + a;
}
sf::Vector2f vinterpolate(float t, sf::Vector2f a, sf::Vector2f b)
{
return {interpolate(t, a.x, b.x), interpolate(t, a.y, b.y)};
}
class BezierCurve : public sf::Drawable
{
public:
// template <typename... Vector2>
// BezierCurve(Vector2... points_)
// : _points{points_...}
// {
// }
BezierCurve()
{
std::fill(_points.begin(), _points.end(), sf::Vector2f{-1, -1});
}
sf::Vector2f getA()
{
return _points[2] + (_points[2] - _points[1]);
}
void setA(sf::Vector2f a_)
{
_points[2] = (a_ + _points[1]) / 2.f;
}
sf::Vector2f& operator[](size_t i_)
{
return _points[i_];
}
void makeCurve()
{
// all points filled -> draw bezier curve
if (fullyInitialized())
{
for (float n{50}, t{0}; t <= 1.01; t += 1 / n)
{
std::vector<sf::Vector2f> remaining_points;
remaining_points.resize(4);
std::copy(_points.begin(), _points.end(), remaining_points.begin());
while (remaining_points.size() > 1)
{
std::vector<sf::Vector2f> current_remaining_points;
for (auto it{remaining_points.cbegin()}; it != remaining_points.cend();)
{
auto current_it{it};
if (++it != remaining_points.cend())
current_remaining_points.push_back(vinterpolate(t, *current_it, *it));
}
remaining_points = current_remaining_points;
}
if (remaining_points.size() == 1)
_curve.push_back(remaining_points[0]);
}
}
}
void setNextPoint(sf::Vector2f point_)
{
auto found{std::find(_points.begin(), _points.end(), sf::Vector2f{-1, -1})};
assert(found != _points.end());
*found = point_;
if (fullyInitialized())
makeCurve();
}
bool fullyInitialized() const
{
return std::find(_points.begin(), _points.end(), sf::Vector2f{-1, -1}) == _points.end();
}
private:
void draw(sf::RenderTarget& target_, sf::RenderStates states_) const override
{
for (auto& point : _points)
{
sf::CircleShape circle{2};
circle.setOrigin(3, 3);
circle.setPosition(point);
circle.setFillColor({100, 100, 100});
target_.draw(circle);
}
if (fullyInitialized())
draw_lines(_curve, target_, sf::Color::Red);
}
private:
std::array<sf::Vector2f, 4> _points;
std::vector<sf::Vector2f> _curve;
};
int main()
{
const sf::ContextSettings aa_8x{0, 0, 8};
sf::RenderWindow window({350, 350}, "SFML Bezier", sf::Style::Default, aa_8x);
std::vector<BezierCurve> curves;
while (true)
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape))
return 0;
if (event.type == sf::Event::MouseButtonPressed)
{
sf::Vector2f mpos{static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y)};
if (curves.empty())
{
curves.emplace_back();
curves.back()[0] = mpos;
}
else
{
if (curves.back().fullyInitialized())
{
auto end_point{curves.back()[3]};
curves.emplace_back();
curves.back()[0] = end_point;
}
curves.back().setNextPoint(mpos);
}
}
}
window.clear();
for (const auto& curve : curves)
window.draw(curve);
window.display();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment