Last active
March 20, 2018 14:38
-
-
Save cppxor2arr/88dc6a26499604c6c28204ed2288ca2d to your computer and use it in GitHub Desktop.
SFML Partice Pulsation
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 <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