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 0000000..8fc59c2
Binary files /dev/null and b/res/parkerpoint.png differ
diff --git a/src/force_algorithms.cpp b/src/force_algorithms.cpp
new file mode 100644
index 0000000..853de1c
--- /dev/null
+++ b/src/force_algorithms.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 .
+ */
+#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;