Skip to content

Instantly share code, notes, and snippets.

@cppxor2arr
Last active March 20, 2018 14:38
Show Gist options
  • Save cppxor2arr/88dc6a26499604c6c28204ed2288ca2d to your computer and use it in GitHub Desktop.
Save cppxor2arr/88dc6a26499604c6c28204ed2288ca2d to your computer and use it in GitHub Desktop.
SFML Partice Pulsation
#include <SFML/Graphics.hpp>
#include <cmath>
#include <utility>
#include <vector>
#include <functional>
class prog
{
public:
prog();
prog(const float&, const float&, const sf::String&);
void open(const float&, const float&, const sf::String&);
void loop();
private:
sf::RenderWindow window;
sf::Event event;
enum class state { null, exit, move, attract, stop_attract, repel, stop_repel, up, reset, resize };
state current_state{state::null};
sf::View view;
std::pair<std::vector<sf::Vertex>, std::vector<sf::Vector2f>>
particles{std::vector<sf::Vertex>{50000}, std::vector<sf::Vector2f>{50000}};
sf::Vector2f mouse;
float angle{0};
bool attract{false}, repel{false};
sf::Clock timer;
state handle_events();
void refresh(const sf::Color& color = sf::Color::Black);
float radian(const float&);
float degree(const float&);
void reset();
float inv_tan(const sf::Vector2f&);
friend void update_particles(prog&, const std::vector<sf::Vertex>::size_type&,
const std::vector<sf::Vertex>::size_type&,
const int&);
};
prog::prog() { }
prog::prog(const float& w, const float& h, const sf::String& window_title)
{
open(w, h, window_title);
}
void prog::open(const float& w, const float& h, const sf::String& window_title)
{
window.create(sf::VideoMode{static_cast<unsigned>(w), static_cast<unsigned>(h)}, window_title);
window.setFramerateLimit(0);
}
prog::state prog::handle_events()
{
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
return state::exit;
if (event.type == sf::Event::KeyPressed)
switch (event.key.code)
{
case sf::Keyboard::Return: return state::exit;
case sf::Keyboard::R: return state::reset;
default: break;
}
if (event.type == sf::Event::Resized)
return state::resize;
if (event.type == sf::Event::MouseButtonPressed)
{
switch (event.mouseButton.button)
{
case sf::Mouse::Left: return state::attract;
case sf::Mouse::Right: return state::repel;
default: break;
}
}
if (event.type == sf::Event::MouseButtonReleased)
{
switch (event.mouseButton.button)
{
case sf::Mouse::Left: return state::stop_attract;
case sf::Mouse::Right: return state::stop_repel;
default: break;
}
}
if (event.type == sf::Event::MouseMoved)
return state::move;
}
return state::null;
}
void prog::refresh(const sf::Color& color)
{
window.clear(color);
window.setView(view);
window.draw(&particles.first[0], particles.first.size(), sf::Points);
window.display();
}
float prog::radian(const float& degree)
{
constexpr float pi{3.14159265358979323846};
return degree * pi / 180.0;
}
float prog::degree(const float& radian)
{
constexpr float pi{3.14159265358979323846};
return radian * 180 / pi;
}
void prog::reset()
{
const float side{std::sqrt(static_cast<float>(particles.first.size()))}, row_start{-side / 2};
std::vector<sf::Vertex>::size_type n{0};
for (sf::Vertex& vertex : particles.first)
{
vertex.position = sf::Vector2f{row_start + std::fmod<float>(n, side), n / side};
particles.second[n] = sf::Vector2f{0, 0};
++n;
}
}
float prog::inv_tan(const sf::Vector2f& coord)
{
if (coord.x > 0 && coord.y >= 0) // quadrant I
return degree(std::atan(coord.y / coord.x));
else if (coord.x < 0 && coord.y >= 0) // quadrant II
return degree(std::atan(coord.y / coord.x)) + 180;
else if (coord.x < 0 && coord.y <= 0) // quadrant III
return degree(std::atan(coord.y / coord.x)) + 180;
else if (coord.x > 0 && coord.y <= 0) // quadrant IV
return degree(std::atan(coord.y / coord.x)) + 360;
else if (coord.x == 0 && coord.y > 0)
return 90;
else if (coord.x == 0 && coord.y < 0)
return 270;
else
return -1;
}
void update_particles(prog& app, const std::vector<sf::Vertex>::size_type& start,
const std::vector<sf::Vertex>::size_type& limit,
const int& elapsed)
{
for (std::vector<sf::Vertex>::size_type n{start}; n != limit; ++n)
{
if (app.attract || app.repel)
{
sf::Vector2f diff{app.mouse - app.particles.first[n].position};
const float temp{app.inv_tan(diff)};
if (temp != -1) app.angle = temp;
sf::Vector2f gravity{sf::Vector2f{10 * std::cos(app.radian(app.angle)), 10 * std::sin(app.radian(app.angle))}};
float acceleration{10 / std::sqrt(diff.x * diff.x + diff.y * diff.y)};
if (acceleration > 0.1) acceleration = 0.1;
gravity *= acceleration;
if (app.attract) app.particles.second[n] += gravity;
else if (app.repel) app.particles.second[n] -= gravity;
}
sf::Vector2f& coord{app.particles.second[n]};
if (std::sqrt(coord.x * coord.x + coord.y * coord.y) > 15)
{
const float temp{app.inv_tan(coord)};
if (temp != -1) app.angle = temp;
coord = sf::Vector2f{15 * std::cos(app.radian(app.angle)), 15 * std::sin(app.radian(app.angle))};
}
sf::Vertex& vertex{app.particles.first[n]};
vertex.position += coord * (elapsed / 10.f);
bool x_left{vertex.position.x <= -(app.view.getSize().x) / 2},
y_top{vertex.position.y <= -(app.view.getSize().y) / 2},
x_right{vertex.position.x >= app.view.getSize().x / 2},
y_bottom{vertex.position.y >= app.view.getSize().y / 2};
if (x_left || y_top || x_right || y_bottom)
{
const float max{std::max(std::fabs(coord.x), std::fabs(coord.y))};
const sf::Vector2f reverse{coord.x / -max, coord.y / -max};
if (x_left || x_right)
{
coord.x *= -1;
}
if (y_top || y_bottom)
{
coord.y *= -1;
}
while (x_left || y_top || x_right || y_bottom)
{
vertex.position += reverse;
x_left = vertex.position.x <= -(app.view.getSize().x) / 2;
y_top = vertex.position.y <= -(app.view.getSize().y) / 2;
x_right = vertex.position.x >= app.view.getSize().x / 2;
y_bottom = vertex.position.y >= app.view.getSize().y / 2;
}
}
const float velocity{std::sqrt(coord.x * coord.x + coord.y * coord.y)};
vertex.color = sf::Color{0, static_cast<unsigned char>(255 - velocity * 51), static_cast<unsigned char>(velocity * 51)};
}
}
void prog::loop()
{
view.setCenter(0, 0);
view.setSize(window.getSize().x, window.getSize().y);
window.setView(view);
reset();
mouse = window.mapPixelToCoords(sf::Mouse::getPosition());
timer.restart();
while (window.isOpen())
{
current_state = handle_events();
if (current_state == state::exit)
window.close();
else if (current_state == state::attract) attract = true;
else if (current_state == state::stop_attract) attract = false;
else if (current_state == state::repel) repel = true;
else if (current_state == state::stop_repel) repel = false;
else if (current_state == state::move)
mouse = window.mapPixelToCoords(sf::Vector2i{event.mouseMove.x, event.mouseMove.y});
else if (current_state == state::reset)
reset();
else if (current_state == state::resize)
view.setSize(view.getSize().y * (static_cast<float>(event.size.width) / event.size.height),
view.getSize().y);
sf::Time elapsed{timer.getElapsedTime()};
if (elapsed.asMilliseconds() > 10)
{
update_particles(std::ref(*this), 0, 50000, elapsed.asMilliseconds());
timer.restart();
}
refresh();
}
}
int main()
{
prog app{1000, 500, "Particle Physics"};
app.loop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment