Pythagoras Tree
#include "SFML/Graphics.hpp"
#include <math.h>
#include <iostream>
#include <utility>
#include <queue>
inline void pause() { std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); }
#define WIDTH 1920
#define HEIGHT 1080
namespace CONFIG {
double line_scaler = .7071067812; // sqrt(2)/2
const int total_orders = 10;
double initial_angle_seed = -90;
double rotation = 45;
double thickness = 10.f;
const double thickness_scaler = .87;
namespace GRADIENT {
const float r = .85;
const float g = .85;
const float b = .85;
}
}
class PythagorusLine : public sf::Drawable
{
public:
PythagorusLine(const sf::Vector2f& point1, const sf::Vector2f& point2,
int rotation, float thickness, sf::Color color) :
thickness(thickness), p1(point1), p2(point2),
color(color), left(nullptr), right(nullptr), rotation(rotation)
{
sf::Vector2f direction = point2 - point1;
sf::Vector2f unitDirection = direction / std::sqrt(direction.x*direction.x + direction.y*direction.y);
sf::Vector2f unitPerpendicular(-unitDirection.y, unitDirection.x);
sf::Vector2f offset = (thickness / 2.f)*unitPerpendicular;
vertices[0].position = point1 + offset;
vertices[1].position = point2 + offset;
vertices[2].position = point2 - offset;
vertices[3].position = point1 - offset;
for (int i = 0; i<4; ++i)
vertices[i].color = color;
}
~PythagorusLine() {
if (left) delete left;
if (right) delete right;
}
sf::Vector2f getBottom() { return p1; }
sf::Vector2f getTop() { return p2; }
static PythagorusLine* getRotatedCopy(PythagorusLine &line, const int rotation, const sf::Color &color) {
sf::Vector2f bottom_point = line.getTop();
const float dist = distance(line.getBottom(), line.getTop());
const float rad = radians(rotation);
float top_x = bottom_point.x + (cos(rad) * dist * CONFIG::line_scaler);
float top_y = bottom_point.y + (sin(rad) * dist * CONFIG::line_scaler);
return new PythagorusLine(bottom_point, { top_x, top_y }, rotation, line.thickness * CONFIG::thickness_scaler, color);
}
static float radians(int degrees) {
return 3.14159 / 180 * degrees;
}
void draw(sf::RenderTarget &target, sf::RenderStates states) const
{
target.draw(vertices, 4, sf::Quads);
}
void draw(sf::RenderWindow &window) const
{
window.draw(vertices, 4, sf::Quads);
}
static float distance(const sf::Vector2f &p1, const sf::Vector2f &p2) {
float sumOfSquares = pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2);
return sqrt(sumOfSquares);
}
PythagorusLine *left, *right;
int rotation;
float thickness;
sf::Color color;
private:
sf::Vector2f p1, p2;
sf::Vertex vertices[4];
};
class PythagorusTree {
public:
PythagorusTree() {}
static void draw(sf::RenderWindow &window, PythagorusLine *root, int num_orders) {
if (!root) return;
num_orders = std::min(num_orders, CONFIG::total_orders);
std::queue<PythagorusLine*> q;
q.push(root);
q.push(nullptr);
while (!q.empty()) {
PythagorusLine *parent = q.front(); q.pop();
if (parent) parent->draw(window);
if (parent && num_orders) {
if (parent->right)
q.push(parent->right);
if (parent->left)
q.push(parent->left);
}
else if (!parent && num_orders) {
num_orders--;
q.push(nullptr);
}
}
}
PythagorusLine* generatePythagorusTree(PythagorusLine *root) {
if (!root) return nullptr;
int order_count_down = CONFIG::total_orders;
std::queue<PythagorusLine*> q;
q.push(root);
q.push(nullptr);
while (!q.empty()) {
PythagorusLine *parent = q.front(); q.pop();
if (parent && order_count_down) {
sf::Color darker = parent->color;
darker.r = int(darker.r * CONFIG::GRADIENT::r);
darker.b = int(darker.b * CONFIG::GRADIENT::b);
darker.g = int(darker.g * CONFIG::GRADIENT::g);
parent->right = PythagorusLine::getRotatedCopy(*parent, int(parent->rotation + CONFIG::rotation) % 360, darker);
parent->left = PythagorusLine::getRotatedCopy(*parent, int(parent->rotation - CONFIG::rotation) % 360, darker);
q.push(parent->right); q.push(parent->left);
}
else if (!parent && order_count_down && !q.empty()) {
order_count_down--;
q.push(nullptr);
}
}
return root;
}
};
struct ControlHandler
{
ControlHandler() {}
static void processEvents(sf::RenderWindow &window, sf::View &view, int ¤t_order_count) {
sf::Event event;
while (window.pollEvent(event))
{
// close
if (event.type == sf::Event::Closed) window.close();
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) &&
sf::Keyboard::isKeyPressed(sf::Keyboard::C)) window.close();
// camera
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) view.move(-20, 0);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) view.move(+20, 0);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) view.move(0, -20);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) view.move(0, +20);
// zoom
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) view.zoom(1.05);
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) view.zoom(.95);
// click generator
else if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) current_order_count++;
window.setView(view);
}
}
};
enum ANIMATE { length, rotation, branch_angle, thickness, all, none,
l_r_b};
class Game
{
public:
Game(const ANIMATE &generator)
: window(sf::VideoMode(WIDTH, HEIGHT), "Pythagorus Tree") {
window.setFramerateLimit(60);
view.setCenter(WIDTH / 2, HEIGHT / 2);
view.setSize(WIDTH, HEIGHT);
handler = new ControlHandler();
create_new_tree(generator);
if (generator == ANIMATE::none) {
run();
}
else {
current_order_count = CONFIG::total_orders;
run(generator);
}
}
private:
PythagorusTree *tree;
PythagorusLine *root;
ControlHandler *handler;
int current_order_count = 0;
sf::RenderWindow window;
sf::View view;
sf::Color colorWindow = sf::Color(80, 49, 42);
sf::Color branch_init = sf::Color(111, 193, 185);
void render() {
window.clear(colorWindow);
tree->draw(window, this->root, current_order_count);
window.display();
}
void run(const ANIMATE &generator) {
while (window.isOpen())
{
handler->processEvents(window, view, current_order_count);
render();
create_new_tree(generator);
}
}
void run() {
while (window.isOpen())
{
handler->processEvents(window, view, current_order_count);
render();
}
}
const double line_scaler = 1.0005;
const double initial_angle_seed_scaler = 1.1;
const double rotation_scaler = 1.01;
const double thickness_scaler = 1.01;
void create_new_tree(const ANIMATE &generator) {
if (this->root) delete root;
root = nullptr;
tree = new PythagorusTree();
switch (generator) {
case length:
CONFIG::line_scaler *= line_scaler;
break;
case rotation:
CONFIG::initial_angle_seed =
fmod(CONFIG::initial_angle_seed * initial_angle_seed_scaler, 360);
break;
case branch_angle:
CONFIG::rotation =
fmod(CONFIG::rotation * rotation_scaler, 360);
break;
case thickness:
CONFIG::thickness *= thickness_scaler;
break;
case l_r_b:
CONFIG::line_scaler *= line_scaler;
CONFIG::initial_angle_seed =
fmod(CONFIG::initial_angle_seed * initial_angle_seed_scaler, 360);
CONFIG::rotation =
fmod(CONFIG::rotation * rotation_scaler, 360);
break;
case all:
CONFIG::line_scaler *= line_scaler;
CONFIG::initial_angle_seed =
fmod(CONFIG::initial_angle_seed * initial_angle_seed_scaler, 360);
CONFIG::rotation =
fmod(CONFIG::rotation * rotation_scaler, 360);
CONFIG::thickness *= thickness_scaler;
break;
case none:
break;
}
root = new PythagorusLine({ WIDTH / 2, WIDTH / 2 }, { WIDTH / 2, HEIGHT / 2 },
CONFIG::initial_angle_seed, CONFIG::thickness, branch_init);
tree->generatePythagorusTree(root);
}
};
int main()
{
//Game game(ANIMATE::none);
//Game game(ANIMATE::rotation);
//Game game(ANIMATE::thickness);
//Game game(ANIMATE::branch_angle);
//Game game(ANIMATE::length);
//Game game(ANIMATE::l_r_b);
Game game(ANIMATE::all);
}Last updated




