diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..f63addd
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..fde0314
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,580 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 64f799a..0349dd3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.25)
-project(COSC-4P80-Assignment-3 VERSION 0.0.14)
+project(COSC-4P80-Assignment-3 VERSION 0.0.15)
include(FetchContent)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
diff --git a/include/assign3/manager.h b/include/assign3/manager.h
new file mode 100644
index 0000000..062369b
--- /dev/null
+++ b/include/assign3/manager.h
@@ -0,0 +1,93 @@
+#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 COSC_4P80_ASSIGNMENT_3_UI_H
+#define COSC_4P80_ASSIGNMENT_3_UI_H
+
+#include "blt/gfx/renderer/batch_2d_renderer.h"
+#include "blt/gfx/renderer/font_renderer.h"
+#include
+#include
+#include
+
+namespace assign3
+{
+
+ struct render_info_t
+ {
+ blt::vec2 base_pos;
+ blt::vec2 neuron_padding;
+ float neuron_scale = 0;
+ };
+
+ class motor_data_t
+ {
+ public:
+ std::vector files;
+ std::vector map_files_names;
+
+ void update();
+ };
+
+
+ class renderer_t
+ {
+ friend motor_data_t;
+ public:
+ explicit renderer_t(motor_data_t& data, blt::gfx::resource_manager& resources, blt::gfx::matrix_state_manager& state):
+ motor_data(data), br2d{resources, state}
+ {}
+
+ void create();
+
+ void cleanup();
+
+ void draw_som(const std::function& color_func, bool debug);
+
+ void render();
+
+ void update_graphics();
+
+ void generate_network(int selection)
+ {
+ som = std::make_unique(motor_data.files[selection].normalize(), som_width, som_height, max_epochs);
+ }
+
+ private:
+ motor_data_t& motor_data;
+ std::unique_ptr som;
+ std::unique_ptr topology_function;
+
+ blt::gfx::font_renderer_t fr2d{};
+ blt::gfx::batch_renderer_2d br2d;
+
+ float draw_width = 0;
+ float draw_height = 0;
+ float neuron_scale = 35;
+
+ blt::size_t som_width = 5;
+ blt::size_t som_height = 5;
+ blt::size_t max_epochs = 100;
+ Scalar initial_learn_rate = 0.1;
+
+ int currently_selected_network = 0;
+ };
+
+}
+
+#endif //COSC_4P80_ASSIGNMENT_3_UI_H
diff --git a/include/assign3/ui.h b/include/assign3/ui.h
deleted file mode 100644
index 163929e..0000000
--- a/include/assign3/ui.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#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 COSC_4P80_ASSIGNMENT_3_UI_H
-#define COSC_4P80_ASSIGNMENT_3_UI_H
-
-namespace assign3 {
-
-}
-
-#endif //COSC_4P80_ASSIGNMENT_3_UI_H
diff --git a/lib/blt-with-graphics b/lib/blt-with-graphics
index f75cbbb..0a0b87f 160000
--- a/lib/blt-with-graphics
+++ b/lib/blt-with-graphics
@@ -1 +1 @@
-Subproject commit f75cbbb10c3c5c482182def61365e6b7fe6da3af
+Subproject commit 0a0b87fc5566211cb7375d9c1b361ba3e91a3545
diff --git a/src/main.cpp b/src/main.cpp
index ddea90a..8a55f33 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3,67 +3,17 @@
#include
#include
#include "blt/gfx/renderer/resource_manager.h"
-#include "blt/gfx/renderer/batch_2d_renderer.h"
-#include "blt/gfx/renderer/font_renderer.h"
#include "blt/gfx/renderer/camera.h"
-#include "blt/std/random.h"
#include
-#include
-#include
+#include
using namespace assign3;
-std::vector files;
-std::unique_ptr som;
-
blt::gfx::matrix_state_manager global_matrices;
blt::gfx::resource_manager resources;
-blt::gfx::batch_renderer_2d renderer_2d(resources, global_matrices);
blt::gfx::first_person_camera_2d camera;
-blt::gfx::font_renderer_t font_renderer;
-
-blt::size_t som_width = 5;
-blt::size_t som_height = 5;
-blt::size_t max_epochs = 100;
-Scalar initial_learn_rate = 0.1;
-
-int currently_selected_network = 0;
-std::vector map_files_names;
-std::unique_ptr topology_function;
-
-float neuron_scale = 35;
-float draw_width;
-float draw_height;
-
-void update_graphics()
-{
- // find the min x / y for the currently drawn som as positions may depend on type.
- const auto x_comparator = [](const auto& a, const auto& b) {
- return a.get_x() < b.get_x();
- };
- const auto y_comparator = [](const auto& a, const auto& b) {
- return a.get_y() < b.get_y();
- };
- const auto& som_neurons = som->get_array().get_map();
- auto min_x = std::min_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
- auto max_x = std::max_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
- auto min_y = std::min_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
- auto max_y = std::max_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
- draw_width = (max_x - min_x) * neuron_scale;
- draw_height = (max_y - min_y) * neuron_scale;
-
- topology_function = std::make_unique();
-}
-
-void generate_network(int selection)
-{
- som = std::make_unique(files[selection].normalize(), som_width, som_height, max_epochs);
-}
-
-const char* get_selection_string(void*, int selection)
-{
- return map_files_names[selection].c_str();
-}
+assign3::motor_data_t data{};
+assign3::renderer_t renderer{data, resources, global_matrices};
void init(const blt::gfx::window_data&)
{
@@ -72,15 +22,7 @@ void init(const blt::gfx::window_data&)
global_matrices.create_internals();
resources.load_resources();
- renderer_2d.create();
- font_renderer.create_default(250, 2048);
-
- font_renderer.create_text("Hello There \"I'm a boy", 13)->setPosition(50, 500);
-
- for (const auto& data : files)
- map_files_names.emplace_back(std::to_string(data.data_points.begin()->bins.size()));
-
- generate_network(currently_selected_network);
+ renderer.create();
}
void update(const blt::gfx::window_data& window_data)
@@ -92,150 +34,14 @@ void update(const blt::gfx::window_data& window_data)
camera.update_view(global_matrices);
global_matrices.update();
- update_graphics();
-
- if (ImGui::Begin("Controls"))
- {
- ImGui::Text("Network Select");
- if (ImGui::ListBox("##Network Select", ¤tly_selected_network, get_selection_string, nullptr, static_cast(map_files_names.size())))
- generate_network(currently_selected_network);
-
- if (ImGui::Button("Run Epoch"))
- {
- som->train_epoch(initial_learn_rate, topology_function.get());
- }
- static bool run;
- ImGui::Checkbox("Run to completion", &run);
- if (run)
- {
- if (som->get_current_epoch() < som->get_max_epochs())
- som->train_epoch(initial_learn_rate, topology_function.get());
- }
- ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
- }
- ImGui::End();
-
- static std::vector activations;
-
- activations.clear();
- activations.resize(som->get_array().get_map().size());
-
- auto current_data_file = files[currently_selected_network].normalize();
- for (auto& v : current_data_file.data_points)
- {
- auto nearest = som->get_closest_neuron(v.bins);
- activations[nearest] += v.is_bad ? -1 : 1;
- }
-
- blt::i64 max = *std::max_element(activations.begin(), activations.end());
- blt::i64 min = *std::min_element(activations.begin(), activations.end());
-
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- auto activation = activations[i];
-
- blt::vec4 color = blt::make_color(1, 1, 1);
- if (activation > 0)
- color = blt::make_color(0, static_cast(activation) / static_cast(max), 0);
- else if (activation < 0)
- color = blt::make_color(std::abs(static_cast(activation) / static_cast(min)), 0, 0);
-
- renderer_2d.drawPointInternal(color,
- point2d_t{v.get_x() * neuron_scale + neuron_scale, v.get_y() * neuron_scale + neuron_scale, neuron_scale});
- }
-
- static std::vector closest_type;
- closest_type.clear();
- closest_type.resize(som->get_array().get_map().size());
-
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- Scalar lowest_distance = std::numeric_limits::max();
- bool is_bad = false;
- for (const auto& data : current_data_file.data_points)
- {
- auto dist = v.dist(data.bins);
- if (dist < lowest_distance)
- {
- lowest_distance = dist;
- is_bad = data.is_bad;
- }
- }
-// BLT_TRACE(is_bad ? -lowest_distance : lowest_distance);
- closest_type[i] = is_bad ? -lowest_distance : lowest_distance;
- }
-
- auto min_dist = *std::min_element(closest_type.begin(), closest_type.end());
- auto max_dist = *std::max_element(closest_type.begin(), closest_type.end());
-
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- auto type = closest_type[i];
-
- blt::vec4 color = blt::make_color(1, 1, 1);
- if (type >= 0)
- color = blt::make_color(0, 1 - (type / max_dist) + 0.1f, 0);
- else if (type < 0)
- color = blt::make_color(1 - (type / min_dist) + 0.1f, 0, 0);
-
- renderer_2d.drawPointInternal(color,
- point2d_t{draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale,
- v.get_y() * neuron_scale + neuron_scale, neuron_scale});
- }
-
- closest_type.clear();
- closest_type.resize(som->get_array().get_map().size());
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- auto half = som->find_closest_neighbour_distance(i);
- auto scale = topology_function->scale(half * 0.5f, 0.5);
- for (const auto& data : current_data_file.data_points)
- {
- auto dist = v.dist(data.bins);
- auto ds = topology_function->call(dist, scale);
-// BLT_TRACE("%f, %f, %f", ds, dist, scale);
- if (data.is_bad)
- closest_type[i] -= ds;
- else
- closest_type[i] += ds;
- }
- }
- auto min_act = *std::min_element(closest_type.begin(), closest_type.end());
- auto max_act = *std::max_element(closest_type.begin(), closest_type.end());
- for (auto& v : closest_type)
- {
- auto n = 2 * (v - min_act) / (max_act - min_act) - 1;
- v = n;
- }
-// BLT_TRACE("Min %f Max %f", min_act, max_act);
-
- for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
- {
- auto type = closest_type[i];
-
- blt::vec4 color;
- if (type >= 0)
- color = blt::make_color(0, type, 0);
- else
- color = blt::make_color(-type, 0, 0);
-
- renderer_2d.drawPointInternal(color,
- point2d_t{draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale,
- draw_height + neuron_scale * 2 + v.get_y() * neuron_scale + neuron_scale, neuron_scale});
- }
-
- font_renderer.render_text("Wow I hate you too", 32)->setPosition(100, 300);
-
- renderer_2d.render(window_data.width, window_data.height);
- font_renderer.render();
+ renderer.render();
}
void destroy(const blt::gfx::window_data&)
{
global_matrices.cleanup();
resources.cleanup();
- renderer_2d.cleanup();
- font_renderer.cleanup();
+ renderer.cleanup();
blt::gfx::cleanup();
BLT_INFO("Goodbye World!");
}
@@ -248,7 +54,8 @@ int main(int argc, const char** argv)
auto args = parser.parse_args(argc, argv);
- files = assign3::data_file_t::load_data_files_from_path(args.get("file"));
+ data.files = assign3::data_file_t::load_data_files_from_path(args.get("file"));
+ data.update();
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true));
}
diff --git a/src/manager.cpp b/src/manager.cpp
new file mode 100644
index 0000000..9ec1e61
--- /dev/null
+++ b/src/manager.cpp
@@ -0,0 +1,217 @@
+/*
+ *
+ * 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
+#include
+#include
+
+const char* get_selection_string(void* user_data, int selection)
+{
+ return (*reinterpret_cast*>(user_data))[selection].c_str();
+}
+
+namespace assign3
+{
+
+ void motor_data_t::update()
+ {
+ for (const auto& data : files)
+ map_files_names.emplace_back(std::to_string(data.data_points.begin()->bins.size()));
+ }
+
+ void renderer_t::create()
+ {
+ fr2d.create(250, 2048);
+ br2d.create();
+
+ generate_network(currently_selected_network);
+ update_graphics();
+ }
+
+ void renderer_t::cleanup()
+ {
+ fr2d.cleanup();
+ br2d.cleanup();
+ }
+
+ void renderer_t::draw_som(const std::function& color_func, bool debug)
+ {
+
+ }
+
+ void renderer_t::render()
+ {
+ using namespace blt::gfx;
+
+ if (ImGui::Begin("Controls"))
+ {
+ ImGui::Text("Network Select");
+ if (ImGui::ListBox("##Network Select", ¤tly_selected_network, get_selection_string, &motor_data.map_files_names,
+ static_cast(motor_data.map_files_names.size())))
+ generate_network(currently_selected_network);
+
+ if (ImGui::Button("Run Epoch"))
+ {
+ som->train_epoch(initial_learn_rate, topology_function.get());
+ }
+ static bool run;
+ ImGui::Checkbox("Run to completion", &run);
+ if (run)
+ {
+ if (som->get_current_epoch() < som->get_max_epochs())
+ som->train_epoch(initial_learn_rate, topology_function.get());
+ }
+ ImGui::Text("Epoch %ld / %ld", som->get_current_epoch(), som->get_max_epochs());
+ }
+ ImGui::End();
+
+ static std::vector activations;
+
+ activations.clear();
+ activations.resize(som->get_array().get_map().size());
+
+ auto current_data_file = motor_data.files[currently_selected_network].normalize();
+ for (auto& v : current_data_file.data_points)
+ {
+ const auto nearest = som->get_closest_neuron(v.bins);
+ activations[nearest] += v.is_bad ? -1 : 1;
+ }
+
+ const blt::i64 max = *std::max_element(activations.begin(), activations.end());
+ const blt::i64 min = *std::min_element(activations.begin(), activations.end());
+
+ for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
+ {
+ const auto activation = activations[i];
+
+ blt::vec4 color = blt::make_color(1, 1, 1);
+ if (activation > 0)
+ color = blt::make_color(0, static_cast(activation) / static_cast(max), 0);
+ else if (activation < 0)
+ color = blt::make_color(std::abs(static_cast(activation) / static_cast(min)), 0, 0);
+
+ br2d.drawPointInternal(color, point2d_t{v.get_x() * neuron_scale + neuron_scale, v.get_y() * neuron_scale + neuron_scale, neuron_scale});
+ }
+
+ static std::vector closest_type;
+ closest_type.clear();
+ closest_type.resize(som->get_array().get_map().size());
+
+ for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
+ {
+ Scalar lowest_distance = std::numeric_limits::max();
+ bool is_bad = false;
+ for (const auto& [is_bins_bad, bins] : current_data_file.data_points)
+ {
+ if (const auto dist = v.dist(bins); dist < lowest_distance)
+ {
+ lowest_distance = dist;
+ is_bad = is_bins_bad;
+ }
+ }
+ // BLT_TRACE(is_bad ? -lowest_distance : lowest_distance);
+ closest_type[i] = is_bad ? -lowest_distance : lowest_distance;
+ }
+
+ auto min_dist = *std::min_element(closest_type.begin(), closest_type.end());
+ auto max_dist = *std::max_element(closest_type.begin(), closest_type.end());
+
+ for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
+ {
+ auto type = closest_type[i];
+
+ blt::vec4 color = blt::make_color(1, 1, 1);
+ if (type >= 0)
+ color = blt::make_color(0, 1 - (type / max_dist) + 0.1f, 0);
+ else if (type < 0)
+ color = blt::make_color(1 - (type / min_dist) + 0.1f, 0, 0);
+
+ br2d.drawPointInternal(color,
+ point2d_t{
+ draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale,
+ v.get_y() * neuron_scale + neuron_scale, neuron_scale
+ });
+ }
+
+ closest_type.clear();
+ closest_type.resize(som->get_array().get_map().size());
+ for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
+ {
+ auto half = som->find_closest_neighbour_distance(i);
+ auto scale = topology_function->scale(half * 0.5f, 0.5);
+ for (const auto& data : current_data_file.data_points)
+ {
+ auto dist = v.dist(data.bins);
+ auto ds = topology_function->call(dist, scale);
+ // BLT_TRACE("%f, %f, %f", ds, dist, scale);
+ if (data.is_bad)
+ closest_type[i] -= ds;
+ else
+ closest_type[i] += ds;
+ }
+ }
+ auto min_act = *std::min_element(closest_type.begin(), closest_type.end());
+ auto max_act = *std::max_element(closest_type.begin(), closest_type.end());
+ for (auto& v : closest_type)
+ {
+ auto n = 2 * (v - min_act) / (max_act - min_act) - 1;
+ v = n;
+ }
+ // BLT_TRACE("Min %f Max %f", min_act, max_act);
+
+ for (auto [i, v] : blt::enumerate(som->get_array().get_map()))
+ {
+ auto type = closest_type[i];
+
+ blt::vec4 color;
+ if (type >= 0)
+ color = blt::make_color(0, type, 0);
+ else
+ color = blt::make_color(-type, 0, 0);
+
+ br2d.drawPointInternal(color,
+ point2d_t{
+ draw_width + neuron_scale * 2 + v.get_x() * neuron_scale + neuron_scale,
+ draw_height + neuron_scale * 2 + v.get_y() * neuron_scale + neuron_scale, neuron_scale
+ });
+ }
+
+
+ br2d.render(0,0);
+ fr2d.render();
+ }
+
+ void renderer_t::update_graphics()
+ {
+ // find the min x / y for the currently drawn som as positions may depend on type.
+ const auto x_comparator = [](const auto& a, const auto& b) {
+ return a.get_x() < b.get_x();
+ };
+ const auto y_comparator = [](const auto& a, const auto& b) {
+ return a.get_y() < b.get_y();
+ };
+ const auto& som_neurons = som->get_array().get_map();
+ auto min_x = std::min_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
+ auto max_x = std::max_element(som_neurons.begin(), som_neurons.end(), x_comparator)->get_x();
+ auto min_y = std::min_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
+ auto max_y = std::max_element(som_neurons.begin(), som_neurons.end(), y_comparator)->get_y();
+ draw_width = (max_x - min_x) * neuron_scale;
+ draw_height = (max_y - min_y) * neuron_scale;
+
+ topology_function = std::make_unique();
+ }
+}
\ No newline at end of file