From a6badf5e9dab47f8ab812bd982517bc359118881 Mon Sep 17 00:00:00 2001 From: Brett Date: Thu, 2 May 2024 00:32:58 -0400 Subject: [PATCH] bedtime. seperating into files, cleaning code --- CMakeLists.txt | 2 +- include/force_algorithms.h | 111 +++++++ include/graph_base.h | 91 ++++++ lib/BLT-With-Graphics-Template | 2 +- res/parkerpoint.png | Bin 0 -> 69251 bytes src/force_algorithms.cpp | 91 ++++++ src/main.cpp | 534 ++++++++++----------------------- 7 files changed, 458 insertions(+), 373 deletions(-) create mode 100644 include/force_algorithms.h create mode 100644 include/graph_base.h create mode 100644 res/parkerpoint.png create mode 100644 src/force_algorithms.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e5aaf0..0a1ce6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(graphs VERSION 0.0.25) +project(graphs VERSION 0.0.26) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) diff --git a/include/force_algorithms.h b/include/force_algorithms.h new file mode 100644 index 0000000..97e22bb --- /dev/null +++ b/include/force_algorithms.h @@ -0,0 +1,111 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GRAPHS_FORCE_ALGORITHMS_H +#define GRAPHS_FORCE_ALGORITHMS_H + +#include +#include +#include +#include +#include + +class force_equation +{ + public: + using node_pair = const std::pair&; + protected: + float cooling_rate = 0.999999; + float min_cooling = 40; + float ideal_spring_length = 175.0; + float initial_temperature = 100; + + struct equation_data + { + blt::vec2 unit, unit_inv; + float mag, mag_sq; + + equation_data(blt::vec2 unit, blt::vec2 unit_inv, float mag, float mag_sq): unit(unit), unit_inv(unit_inv), mag(mag), mag_sq(mag_sq) + {} + }; + + inline static blt::vec2 dir_v(node_pair v1, node_pair v2) + { + return v2.second.getPosition() - v1.second.getPosition(); + } + + static equation_data calc_data(node_pair v1, node_pair v2); + + public: + + [[nodiscard]] virtual blt::vec2 attr(node_pair v1, node_pair v2) const = 0; + + [[nodiscard]] virtual blt::vec2 rep(node_pair v1, node_pair v2) const = 0; + + [[nodiscard]] virtual std::string name() const = 0; + + [[nodiscard]] virtual float cooling_factor(int t) const + { + return std::max(static_cast(initial_temperature * std::pow(cooling_rate, t)), min_cooling); + } + + void draw_inputs_base(); + + virtual void draw_inputs() + {} + + virtual ~force_equation() = default; +}; + +class Eades_equation : public force_equation +{ + protected: + float repulsive_constant = 24.0; + float spring_constant = 12.0; + public: + [[nodiscard]] blt::vec2 attr(node_pair v1, node_pair v2) const final; + + [[nodiscard]] blt::vec2 rep(node_pair v1, node_pair v2) const final; + + [[nodiscard]] std::string name() const final + { + return "Eades"; + } + + void draw_inputs() override; +}; + +class Fruchterman_Reingold_equation : public force_equation +{ + public: + [[nodiscard]] blt::vec2 attr(node_pair v1, node_pair v2) const final; + + [[nodiscard]] blt::vec2 rep(node_pair v1, node_pair v2) const final; + + [[nodiscard]] float cooling_factor(int t) const override + { + return force_equation::cooling_factor(t) * 0.025f; + } + + [[nodiscard]] std::string name() const final + { + return "Fruchterman & Reingold"; + } +}; + +#endif //GRAPHS_FORCE_ALGORITHMS_H diff --git a/include/graph_base.h b/include/graph_base.h new file mode 100644 index 0000000..dc464bf --- /dev/null +++ b/include/graph_base.h @@ -0,0 +1,91 @@ +#pragma once +/* + * Copyright (C) 2024 Brett Terpstra + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GRAPHS_GRAPH_BASE_H +#define GRAPHS_GRAPH_BASE_H + +#include +#include +#include + +class node +{ + private: + blt::gfx::point2d_t point; + blt::vec2 velocity; + public: + explicit node(const blt::gfx::point2d_t& point): point(point) + {} + + blt::vec2& getVelocityRef() + { + return velocity; + } + + blt::vec2& getPositionRef() + { + return point.pos; + } + + [[nodiscard]] const blt::vec2& getPosition() const + { + return point.pos; + } + + [[nodiscard]] auto& getRenderObj() const + { + return point; + } +}; + + +class edge +{ + private: + blt::u64 i1, i2; + public: + edge(blt::u64 i1, blt::u64 i2): i1(i1), i2(i2) + { + BLT_ASSERT(i1 != i2 && "Indices cannot be equal!"); + } + + inline friend bool operator==(edge e1, edge e2) + { + return (e1.i1 == e2.i1 || e1.i1 == e2.i2) && (e1.i2 == e2.i1 || e1.i2 == e2.i2); + } + + [[nodiscard]] size_t getFirst() const + { + return i1; + } + + [[nodiscard]] size_t getSecond() const + { + return i2; + } +}; + +struct edge_hash +{ + blt::u64 operator()(const edge& e) const + { + return e.getFirst() * e.getSecond(); + } +}; + +#endif //GRAPHS_GRAPH_BASE_H diff --git a/lib/BLT-With-Graphics-Template b/lib/BLT-With-Graphics-Template index a066d8f..3588dcb 160000 --- a/lib/BLT-With-Graphics-Template +++ b/lib/BLT-With-Graphics-Template @@ -1 +1 @@ -Subproject commit a066d8f6e47d63c14387ab002d30c6a87ffb1c15 +Subproject commit 3588dcbd499314284523f53cf037c290c9c62d73 diff --git a/res/parkerpoint.png b/res/parkerpoint.png new file mode 100644 index 0000000000000000000000000000000000000000..8fc59c2bac19cdbe7fabc332f05f328456ca8039 GIT binary patch literal 69251 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 srRsWO9OcO|xJtv#$6ekV0*;9P2hhAP(G+R0. + */ +#include + +/** + * -------------------------------------------------------- + * force_equation + * -------------------------------------------------------- + */ + +force_equation::equation_data force_equation::calc_data(const std::pair& v1, const std::pair& v2) +{ + auto dir = dir_v(v1, v2); + auto dir2 = dir_v(v2, v1); + auto mag = dir.magnitude(); + auto mag2 = dir2.magnitude(); + auto unit = mag == 0 ? blt::vec2() : dir / mag; + auto unit_inv = mag2 == 0 ? blt::vec2() : dir2 / mag2; + auto mag_sq = mag * mag; + return {unit, unit_inv, mag, mag_sq}; +} + +void force_equation::draw_inputs_base() +{ + namespace im = ImGui; + im::InputFloat("Ideal Spring Length", &ideal_spring_length, 2.5, 10); + im::SliderFloat("Initial Temperature", &initial_temperature, 1, 100); + im::SliderFloat("Cooling Rate", &cooling_rate, 0, 0.999999, "%.6f"); + im::InputFloat("Min Cooling", &min_cooling, 0.5, 1); +} + +/** + * -------------------------------------------------------- + * Eades_equation + * -------------------------------------------------------- + */ + +blt::vec2 Eades_equation::attr(const std::pair& v1, const std::pair& v2) const +{ + auto data = calc_data(v1, v2); + return (spring_constant * std::log(data.mag / ideal_spring_length) * data.unit) - rep(v1, v2); +} + +blt::vec2 Eades_equation::rep(const std::pair& v1, const std::pair& v2) const +{ + auto data = calc_data(v1, v2); + // scaling factor included because of the scales this algorithm is working on (large viewport) + auto scale = (repulsive_constant * 10000) / data.mag_sq; + return scale * data.unit_inv; +} + +void Eades_equation::draw_inputs() +{ + namespace im = ImGui; + im::InputFloat("Repulsive Constant", &repulsive_constant, 0.25, 10); + im::InputFloat("Spring Constant", &spring_constant, 0.25, 10); +} + +/** + * -------------------------------------------------------- + * Fruchterman_Reingold_equation + * -------------------------------------------------------- + */ + +blt::vec2 Fruchterman_Reingold_equation::attr(const std::pair& v1, const std::pair& v2) const +{ + auto data = calc_data(v1, v2); + float scale = data.mag_sq / ideal_spring_length; + return (scale * data.unit); +} + +blt::vec2 Fruchterman_Reingold_equation::rep(const std::pair& v1, const std::pair& v2) const +{ + auto data = calc_data(v1, v2); + float scale = (ideal_spring_length * ideal_spring_length) / data.mag; + return scale * data.unit_inv; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0e89069..69d379b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,10 +25,12 @@ #include #include #include -#include #include #include +#include +#include + blt::gfx::matrix_state_manager global_matrices; blt::gfx::resource_manager resources; blt::gfx::batch_renderer_2d renderer_2d(resources); @@ -40,202 +42,6 @@ int sub_ticks = 1; namespace im = ImGui; -class node -{ - private: - blt::gfx::point2d_t point; - blt::vec2 velocity; - public: - explicit node(const blt::gfx::point2d_t& point): point(point) - {} - - blt::vec2& getVelocityRef() - { - return velocity; - } - - blt::vec2& getPositionRef() - { - return point.pos; - } - - [[nodiscard]] const blt::vec2& getPosition() const - { - return point.pos; - } - - [[nodiscard]] auto& getRenderObj() const - { - return point; - } -}; - - -class edge -{ - private: - blt::u64 i1, i2; - public: - edge(blt::u64 i1, blt::u64 i2): i1(i1), i2(i2) - { - BLT_ASSERT(i1 != i2 && "Indices cannot be equal!"); - } - - inline friend bool operator==(edge e1, edge e2) - { - return (e1.i1 == e2.i1 || e1.i1 == e2.i2) && (e1.i2 == e2.i1 || e1.i2 == e2.i2); - } - - [[nodiscard]] size_t getFirst() const - { - return i1; - } - - [[nodiscard]] size_t getSecond() const - { - return i2; - } -}; - -struct edge_hash -{ - blt::u64 operator()(const edge& e) const - { - return e.getFirst() * e.getSecond(); - } -}; - -struct equation_variables -{ - float repulsive_constant = 24.0; - float spring_constant = 12.0; - float ideal_spring_length = 175.0; - float initial_temperature = 69.5; - float cooling_rate = 0.999; - float min_cooling = 0; - - equation_variables() = default; - //equation_variables(const equation_variables&) = delete; - //equation_variables& operator=(const equation_variables&) = delete; -}; - -class force_equation -{ - public: - using node_pair = const std::pair&; - protected: - const equation_variables& variables; - - struct equation_data - { - blt::vec2 unit, unit_inv; - float mag, mag_sq; - - equation_data(blt::vec2 unit, blt::vec2 unit_inv, float mag, float mag_sq): unit(unit), unit_inv(unit_inv), mag(mag), mag_sq(mag_sq) - {} - }; - - inline static blt::vec2 dir_v(node_pair v1, node_pair v2) - { - return v2.second.getPosition() - v1.second.getPosition(); - } - - inline static equation_data calc_data(node_pair v1, node_pair v2) - { - auto dir = dir_v(v1, v2); - auto dir2 = dir_v(v2, v1); - auto mag = dir.magnitude(); - auto mag2 = dir2.magnitude(); - auto unit = mag == 0 ? blt::vec2() : dir / mag; - auto unit_inv = mag2 == 0 ? blt::vec2() : dir2 / mag2; - auto mag_sq = mag * mag; - return {unit, unit_inv, mag, mag_sq}; - } - - public: - - explicit force_equation(const equation_variables& variables): variables(variables) - {} - - [[nodiscard]] virtual blt::vec2 attr(node_pair v1, node_pair v2) const = 0; - - [[nodiscard]] virtual blt::vec2 rep(node_pair v1, node_pair v2) const = 0; - - [[nodiscard]] virtual std::string name() const = 0; - - [[nodiscard]] virtual float cooling_factor(int t) const - { - return std::max(static_cast(variables.initial_temperature * std::pow(variables.cooling_rate, t)), variables.min_cooling); - } - - virtual ~force_equation() = default; -}; - -class Eades_equation : public force_equation -{ - public: - explicit Eades_equation(const equation_variables& variables): force_equation(variables) - {} - - [[nodiscard]] blt::vec2 attr(node_pair v1, node_pair v2) const final - { - auto data = calc_data(v1, v2); - - auto ideal = std::log(data.mag / variables.ideal_spring_length); - - return (variables.spring_constant * ideal * data.unit) - rep(v1, v2); - } - - [[nodiscard]] blt::vec2 rep(node_pair v1, node_pair v2) const final - { - auto data = calc_data(v1, v2); - - // scaling factor included because of the scales this algorithm is working on (large viewport) - auto scale = (variables.repulsive_constant * 10000) / data.mag_sq; - return scale * data.unit_inv; - } - - [[nodiscard]] std::string name() const final - { - return "Eades"; - } -}; - -class Fruchterman_Reingold_equation : public force_equation -{ - public: - explicit Fruchterman_Reingold_equation(const equation_variables& variables): force_equation(variables) - {} - - [[nodiscard]] blt::vec2 attr(node_pair v1, node_pair v2) const final - { - auto data = calc_data(v1, v2); - - float scale = data.mag_sq / variables.ideal_spring_length; - - return (scale * data.unit); - } - - [[nodiscard]] blt::vec2 rep(node_pair v1, node_pair v2) const final - { - auto data = calc_data(v1, v2); - - float scale = (variables.ideal_spring_length * variables.ideal_spring_length) / data.mag; - - return scale * data.unit_inv; - } - - [[nodiscard]] float cooling_factor(int t) const override - { - return force_equation::cooling_factor(t) * 0.025f; - } - - [[nodiscard]] std::string name() const final - { - return "Fruchterman & Reingold"; - } -}; - struct bounding_box { int min_x = 0; @@ -249,17 +55,16 @@ struct bounding_box bool is_screen = true; }; -class graph +class graph_t { private: - equation_variables variables; std::vector nodes; blt::hashset_t edges; blt::hashmap_t> connected_nodes; bool sim = false; bool run_infinitely = true; float sim_speed = 1; - float threshold = 0.01; + float threshold = 0; float max_force_last = 1; int current_iterations = 0; int max_iterations = 5000; @@ -268,7 +73,8 @@ class graph blt::i32 current_node = -1; - void create_random_graph(bounding_box bb, blt::size_t min_nodes, blt::size_t max_nodes, blt::f64 connectivity) + void create_random_graph(bounding_box bb, blt::size_t min_nodes, blt::size_t max_nodes, blt::f64 connectivity, blt::f64 scaling_connectivity, + blt::f64 distance_factor) { // don't allow points too close to the edges of the window. if (bb.is_screen) @@ -319,7 +125,16 @@ class graph { if (node1.first == node2.first) continue; - if (chance(dev) <= connectivity) + auto diff = node2.second.getPosition() - node1.second.getPosition(); + auto diff_sq = (diff * diff); + auto dist = distance_factor / static_cast(std::sqrt(diff_sq.x() + diff_sq.y())); + double dexp; + if (dist == 0) + dexp = 0; + else + dexp = 1 / (std::exp(dist) - dist); + auto rand = chance(dev); + if (rand <= connectivity && rand >= dexp * scaling_connectivity) connect(node1.first, node2.first); } } @@ -345,15 +160,16 @@ class graph } public: - graph() = default; + graph_t() = default; void make_new(const bounding_box& bb, blt::size_t min_nodes, blt::size_t max_nodes, blt::f64 connectivity) { - create_random_graph(bb, min_nodes, max_nodes, connectivity); + create_random_graph(bb, min_nodes, max_nodes, connectivity, 0, 25); use_Eades(); } - void reset(const bounding_box& bb, blt::size_t min_nodes, blt::size_t max_nodes, blt::f64 connectivity) + void reset(const bounding_box& bb, blt::size_t min_nodes, blt::size_t max_nodes, blt::f64 connectivity, blt::f64 scaling_connectivity, + blt::f64 distance_factor) { sim = false; current_iterations = 0; @@ -361,7 +177,7 @@ class graph nodes.clear(); edges.clear(); connected_nodes.clear(); - create_random_graph(bb, min_nodes, max_nodes, connectivity); + create_random_graph(bb, min_nodes, max_nodes, connectivity, scaling_connectivity, distance_factor); } void connect(blt::u64 n1, blt::u64 n2) @@ -410,7 +226,7 @@ class graph } for (const auto& point : nodes) - renderer_2d.drawPointInternal("parker", point.getRenderObj(), 10.0f); + renderer_2d.drawPointInternal("parker_point", point.getRenderObj(), 10.0f); for (const auto& edge : edges) { if (edge.getFirst() >= nodes.size() || edge.getSecond() >= nodes.size()) @@ -430,22 +246,10 @@ class graph current_node = -1; } - void process_mouse_drag(blt::i32, blt::i32 height) + void process_mouse_drag(blt::i32 width, blt::i32 height) { - auto mx = static_cast(blt::gfx::getMouseX()); - auto my = static_cast(height - blt::gfx::getMouseY()); - auto mv = blt::vec2(mx, my); - - const auto& ovm = global_matrices.computedOVM(); - - auto adj_mv = ovm * blt::vec4(mv.x(), mv.y(), 0, 1); - auto adj_size = ovm * blt::vec4(POINT_SIZE, POINT_SIZE, POINT_SIZE, POINT_SIZE); - float new_size = std::max(std::abs(adj_size.x()), std::abs(adj_size.y())); - - //BLT_TRACE_STREAM << "adj_mv: "; - //BLT_TRACE_STREAM << adj_mv << "\n"; - //BLT_TRACE_STREAM << "adj_size: "; - //BLT_TRACE_STREAM << adj_size << "\n"; + auto mouse_pos = blt::make_vec2(blt::gfx::calculateRay2D(width, height, global_matrices.getScale2D(), global_matrices.getView2D(), + global_matrices.getOrtho())); if (current_node < 0) { @@ -454,7 +258,7 @@ class graph { auto pos = n.second.getPosition(); - auto dist = pos - mv; + auto dist = pos - mouse_pos; auto mag = dist.magnitude(); if (mag < POINT_SIZE) @@ -465,22 +269,18 @@ class graph } } else { - auto pos = nodes[current_node].getPosition(); - auto adj_pos = ovm * blt::vec4(pos.x(), pos.y(), 0, 1); - //BLT_TRACE_STREAM << "adj_pos: "; - //BLT_TRACE_STREAM << adj_pos << "\n"; - nodes[current_node].getPositionRef() = mv; + nodes[current_node].getPositionRef() = mouse_pos; } } void use_Eades() { - equation = std::make_unique(variables); + equation = std::make_unique(); } void use_Fruchterman_Reingold() { - equation = std::make_unique(variables); + equation = std::make_unique(); } void start_sim() @@ -498,6 +298,11 @@ class graph return equation->name(); } + auto* getSimulator() + { + return equation.get(); + } + auto getCoolingFactor() { return equation->cooling_factor(current_iterations); @@ -523,11 +328,6 @@ class graph return threshold; } - auto& getVariables() - { - return variables; - } - int& getMaxIterations() { return max_iterations; @@ -539,172 +339,164 @@ class graph } }; -graph main_graph; +class engine_t +{ + private: + graph_t graph; + public: + void init(const blt::gfx::window_data& data) + { + graph.make_new({0, 0, data.width, data.height}, 5, 25, 0.2); + } + + void render(const blt::gfx::window_data& data, double ft) + { + if (im::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + static int min_nodes = 5; + static int max_nodes = 25; + + static bounding_box bb{0, 0, data.width, data.height}; + + static float connectivity = 0.12; + static float scaling_connectivity = 0.5; + static float distance_factor = 100; + + //im::SetNextItemOpen(true, ImGuiCond_Once); + im::Text("FPS: %lf Frame-time (ms): %lf Frame-time (S): %lf", fps, ft * 1000.0, ft); + im::Text("Number of Nodes: %d", graph.numberOfNodes()); + im::SetNextItemOpen(true, ImGuiCond_Once); + if (im::CollapsingHeader("Help")) + { + im::Text("You can use W/A/S/D to move the camera around"); + im::Text("Q/E can be used to zoom in/out the camera"); + } + if (im::CollapsingHeader("Graph Generation Settings")) + { + im::Checkbox("Screen Auto-Scale", &bb.is_screen); + if (im::CollapsingHeader("Spawning Area")) + { + bool result = false; + result |= im::InputInt("Min X", &bb.min_x, 5, 100); + result |= im::InputInt("Max X", &bb.max_x, 5, 100); + result |= im::InputInt("Min Y", &bb.min_y, 5, 100); + result |= im::InputInt("Max Y", &bb.max_y, 5, 100); + if (result) + bb.is_screen = false; + } + if (bb.is_screen) + { + bb.max_x = data.width; + bb.max_y = data.height; + bb.min_x = 0; + bb.min_y = 0; + } + im::SeparatorText("Node Settings"); + im::InputInt("Min Nodes", &min_nodes); + im::InputInt("Max Nodes", &max_nodes); + im::SliderFloat("Connectivity", &connectivity, 0, 1); + im::SliderFloat("Scaling Connectivity", &scaling_connectivity, 0, 1); + im::InputFloat("Distance Factor", &distance_factor, 5, 100); + if (im::Button("Reset Graph")) + { + graph.reset(bb, min_nodes, max_nodes, connectivity, scaling_connectivity, distance_factor); + } + } + im::SetNextItemOpen(true, ImGuiCond_Once); + if (im::CollapsingHeader("Simulation Settings")) + { + im::InputInt("Max Iterations", &graph.getMaxIterations()); + im::Checkbox("Run Infinitely", &graph.getIterControl()); + im::InputInt("Sub-ticks Per Frame", &sub_ticks); + im::InputFloat("Threshold", &graph.getThreshold(), 0.01, 1); + graph.getSimulator()->draw_inputs_base(); + graph.getSimulator()->draw_inputs(); + im::Text("Current Cooling Factor: %f", graph.getCoolingFactor()); + im::SliderFloat("Simulation Speed", &graph.getSimSpeed(), 0, 4); + } + im::SetNextItemOpen(true, ImGuiCond_Once); + if (im::CollapsingHeader("System Controls")) + { + if (im::Button("Start")) + graph.start_sim(); + im::SameLine(); + if (im::Button("Stop")) + graph.stop_sim(); + if (im::Button("Reset Iterations")) + graph.reset_iterations(); + im::Text("Select a system:"); + auto current_sim = graph.getSimulatorName(); + const char* items[] = {"Eades", "Fruchterman & Reingold"}; + static int item_current = 0; + ImGui::ListBox("##SillyBox", &item_current, items, 2, 2); + + if (strcmp(items[item_current], current_sim.c_str()) != 0) + { + switch (item_current) + { + case 0: + graph.use_Eades(); + BLT_INFO("Using Eades"); + break; + case 1: + graph.use_Fruchterman_Reingold(); + BLT_INFO("Using Fruchterman & Reingold"); + break; + default: + BLT_WARN("This is not a valid selection! How did we get here?"); + break; + } + } + } + im::End(); + } + + auto& io = ImGui::GetIO(); + + if (!io.WantCaptureMouse && blt::gfx::isMousePressed(0)) + graph.process_mouse_drag(data.width, data.height); + else + graph.reset_mouse_drag(); + + graph.render(ft); + } +}; -#ifdef __EMSCRIPTEN__ -std::string resource_prefix = "../"; -#else -std::string resource_prefix = "../"; -#endif +engine_t engine; void init(const blt::gfx::window_data& data) { using namespace blt::gfx; - resources.setPrefixDirectory(resource_prefix); + resources.setPrefixDirectory("../"); resources.enqueue("res/debian.png", "debian"); resources.enqueue("res/parker.png", "parker"); + resources.enqueue("res/parkerpoint.png", "parker_point"); resources.enqueue("res/parker cat ears.jpg", "parkercat"); global_matrices.create_internals(); resources.load_resources(); renderer_2d.create(); - bounding_box bb(0, 0, data.width, data.height); - main_graph.make_new(bb, 5, 25, 0.2); + engine.init(data); + lastTime = blt::system::nanoTime(); } -float x = 50, y = 50; -float sx = 0.5, sy = 0.5; -float ax = 0.05, ay = 0.05; - void update(const blt::gfx::window_data& data) { global_matrices.update_perspectives(data.width, data.height, 90, 0.1, 2000); - x += sx; - y += sx; - - sx += ax; - sy += ay; - - if (x > 256) - sx *= -1; - if (y > 256) - sy *= -1; - //im::ShowDemoWindow(); - if (im::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) - { - static int min_nodes = 5; - static int max_nodes = 25; - - static bounding_box bb{0, 0, data.width, data.height}; - - static float connectivity = 0.12; - - //im::SetNextItemOpen(true, ImGuiCond_Once); - im::Text("FPS: %lf Frame-time (ms): %lf Frame-time (S): %lf", fps, ft * 1000.0, ft); - im::Text("Number of Nodes: %d", main_graph.numberOfNodes()); - im::SetNextItemOpen(true, ImGuiCond_Once); - if (im::CollapsingHeader("Help")) - { - im::Text("You can use W/A/S/D to move the camera around"); - im::Text("Q/E can be used to zoom in/out the camera"); - } - if (im::CollapsingHeader("Graph Generation Settings")) - { - im::Checkbox("Screen Auto-Scale", &bb.is_screen); - if (im::CollapsingHeader("Spawning Area")) - { - bool result = false; - result |= im::InputInt("Min X", &bb.min_x, 5, 100); - result |= im::InputInt("Max X", &bb.max_x, 5, 100); - result |= im::InputInt("Min Y", &bb.min_y, 5, 100); - result |= im::InputInt("Max Y", &bb.max_y, 5, 100); - if (result) - bb.is_screen = false; - } - if (bb.is_screen) - { - bb.max_x = data.width; - bb.max_y = data.height; - bb.min_x = 0; - bb.min_y = 0; - } - im::SeparatorText("Node Settings"); - im::InputInt("Min Nodes", &min_nodes); - im::InputInt("Max Nodes", &max_nodes); - im::SliderFloat("Connectivity", &connectivity, 0, 1); - if (im::Button("Reset Graph")) - { - main_graph.reset(bb, min_nodes, max_nodes, connectivity); - } - } - im::SetNextItemOpen(true, ImGuiCond_Once); - if (im::CollapsingHeader("Simulation Settings")) - { - im::InputInt("Max Iterations", &main_graph.getMaxIterations()); - im::Checkbox("Run Infinitely", &main_graph.getIterControl()); - im::InputInt("Sub-ticks Per Frame", &sub_ticks); - im::InputFloat("Threshold", &main_graph.getThreshold(), 0.01, 1); - im::InputFloat("Repulsive Constant", &main_graph.getVariables().repulsive_constant, 0.25, 10); - im::InputFloat("Spring Constant", &main_graph.getVariables().spring_constant, 0.25, 10); - im::InputFloat("Ideal Spring Length", &main_graph.getVariables().ideal_spring_length, 2.5, 10); - im::SliderFloat("Initial Temperature", &main_graph.getVariables().initial_temperature, 1, 100); - im::SliderFloat("Cooling Rate", &main_graph.getVariables().cooling_rate, 0, 0.999999, "%.6f"); - im::InputFloat("Min Cooling", &main_graph.getVariables().min_cooling, 0.5, 1); - im::Text("Current Cooling Factor: %f", main_graph.getCoolingFactor()); - im::SliderFloat("Simulation Speed", &main_graph.getSimSpeed(), 0, 4); - if (im::Button("Start")) - main_graph.start_sim(); - im::SameLine(); - if (im::Button("Stop")) - main_graph.stop_sim(); - if (im::Button("Reset Iterations")) - main_graph.reset_iterations(); - } - im::SetNextItemOpen(true, ImGuiCond_Once); - if (im::CollapsingHeader("System Controls")) - { - im::Text("Select a system:"); - auto current_sim = main_graph.getSimulatorName(); - const char* items[] = {"Eades", "Fruchterman & Reingold"}; - static int item_current = 0; - ImGui::ListBox("##SillyBox", &item_current, items, 2, 2); - - if (strcmp(items[item_current], current_sim.c_str()) != 0) - { - switch (item_current) - { - case 0: - main_graph.use_Eades(); - BLT_INFO("Using Eades"); - break; - case 1: - main_graph.use_Fruchterman_Reingold(); - BLT_INFO("Using Fruchterman & Reingold"); - break; - default: - BLT_WARN("This is not a valid selection! How did we get here?"); - break; - } - } - } - im::End(); - } - auto& io = ImGui::GetIO(); - - if (!io.WantCaptureMouse && blt::gfx::isMousePressed(0)) - main_graph.process_mouse_drag(data.width, data.height); - else - main_graph.reset_mouse_drag(); - - main_graph.render(ft); + engine.render(data, ft); camera.update(); camera.update_view(global_matrices); global_matrices.update(); - //renderer_2d.drawPoint(blt::make_color(1, 0, 0), blt::vec2(0, 0), 50); - //renderer_2d.drawPoint(blt::make_color(1, 0, 0), blt::vec2(data.width, data.height), 50); renderer_2d.render(); - BLT_TRACE_STREAM << blt::gfx::calculateRay2D(static_cast(data.width), static_cast(data.height), global_matrices.getScale2D(), - global_matrices.getView2D(), global_matrices.getOrtho()) << "\n"; - auto currentTime = blt::system::nanoTime(); auto diff = currentTime - lastTime; lastTime = currentTime;