Commit 0948a248 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

core: refactor external-source from app to editor

parent fe9c0ab7
......@@ -4,23 +4,21 @@
#include "application.hpp"
#include "dialog.hpp"
#include "node-editor.hpp"
#include <irritator/core.hpp>
#include <chrono>
#include "internal.hpp"
namespace irt {
bool application::init()
bool
application::init()
{
if (auto ret = editors.init(50u); is_bad(ret)) {
log_w.log(2, "Fail to initialize irritator: %s\n", status_string(ret));
std::fprintf(stderr, "Fail to initialize irritator: %s\n", status_string(ret));
std::fprintf(
stderr, "Fail to initialize irritator: %s\n", status_string(ret));
return false;
}
if (auto* ed = app.alloc_editor(); ed) {
if (auto* ed = alloc_editor(); ed) {
ed->context = imnodes::EditorContextCreate();
ed->settings.compute_colors();
}
......@@ -58,7 +56,7 @@ bool application::init()
}
bool
void application::show()
application::show()
{
bool ret = true;
......@@ -83,7 +81,6 @@ void application::show()
ImGui::MenuItem("Simulation", nullptr, &show_simulation);
ImGui::MenuItem("Plot", nullptr, &show_plot);
ImGui::MenuItem("Sources", nullptr, &show_sources_window);
ImGui::MenuItem("Settings", nullptr, &show_settings);
ImGui::MenuItem("Log", nullptr, &show_log);
......@@ -123,25 +120,17 @@ void application::show()
log_w.show(&show_log);
if (show_settings)
settings.show(&show_settings);
show_settings_window();
if (show_demo)
ImGui::ShowDemoWindow();
if (show_sources_window)
show_sources(&show_sources_window);
return ret;
}
editor*
application::alloc_editor()
{
if (srcs.binary_file_sources.capacity() == 0) {
srcs.init(50);
}
if (!editors.can_alloc(1u)) {
log_w.log(2, "Too many open editor\n");
return nullptr;
......@@ -176,7 +165,7 @@ application::show_plot_window()
}
static editor_id current = undefined<editor_id>();
if (auto* ed = make_combo_editor_name(*this, current); ed) {
if (auto* ed = make_combo_editor_name(current); ed) {
if (ImPlot::BeginPlot("simulation", "t", "s")) {
ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 1.f);
......@@ -197,8 +186,8 @@ application::show_plot_window()
ImGui::End();
}
void application::show_settings_window()
void
application::show_settings_window()
{
ImGui::SetNextWindowPos(ImVec2(300, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(350, 400), ImGuiCond_Once);
......@@ -210,7 +199,7 @@ void application::show_settings_window()
ImGui::Text("Home.......: %s", home_dir.u8string().c_str());
ImGui::Text("Executable.: %s", executable_dir.u8string().c_str());
ImGui::End();
ImGui::End();
}
void
......@@ -222,10 +211,10 @@ application::shutdown()
}
static status
simulation_run_for(simulation& sim,
long long int duration_in_microseconds,
const double end,
double& current) noexcept
run_for(editor& ed,
long long int duration_in_microseconds,
const double end,
double& current) noexcept
{
namespace stdc = std::chrono;
......@@ -233,8 +222,8 @@ simulation_run_for(simulation& sim,
long long int duration_since_start;
do {
if (sim.current_status = sim.run(current); is_bad(sim.current_status))
return ret;
if (auto ret = ed.sim.run(current); is_bad(ret))
return ret;
auto end_at = stdc::high_resolution_clock::now();
auto duration = end_at - start_at;
......@@ -245,24 +234,63 @@ simulation_run_for(simulation& sim,
return status::success;
}
void application::run_simulations()
void
application::run_simulations()
{
int running_simulation = 0;
editor* ed = nullptr;
while (editors.next(ed))
if (ed->st = editor_status::running)
if (match(ed->st,
editor_status::running_debug,
editor_status::running_thread,
editor_status::running_thread_need_join))
++running_simulation;
if (!running_simulation)
return;
auto duration = 10000 / running_simulation;
ed = nullptr;
while (editors.next(ed))
if (ed->st = editor_status::running)
ed->sim_st = run_for(ed->sim, duration, ed->simulation_end, ed->simulation_current);
if (match(ed->st,
editor_status::running_debug,
editor_status::running_thread,
editor_status::running_thread_need_join))
ed->sim_st = run_for(
*ed, duration, ed->simulation_end, ed->simulation_current);
}
editor*
application::make_combo_editor_name(editor_id& current) noexcept
{
editor* first = editors.try_to_get(current);
if (first == nullptr) {
if (!editors.next(first)) {
current = undefined<editor_id>();
return nullptr;
}
}
current = editors.get_id(first);
if (ImGui::BeginCombo("Name", first->name.c_str())) {
editor* ed = nullptr;
while (editors.next(ed)) {
const bool is_selected = current == editors.get_id(ed);
if (ImGui::Selectable(ed->name.c_str(), is_selected))
current = editors.get_id(ed);
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
return editors.try_to_get(current);
}
} // namespace irt
......@@ -9,26 +9,434 @@
#include <irritator/external_source.hpp>
#include <filesystem>
#include <optional>
#include <fstream>
#include <map>
#include <thread>
#include <variant>
#include <vector>
#include "imnodes.hpp"
#include "implot.h"
#include <imgui.h>
namespace irt {
struct application;
struct cluster;
struct editor;
struct top_cluster;
struct window_logger;
struct plot_output;
struct file_output;
struct file_discrete_output;
static inline constexpr int not_found = -1;
enum class editor_id : u64;
enum class cluster_id : u64;
enum class plot_output_id : u64;
enum class file_output_id : u64;
enum class file_discrete_output_id : u64;
using child_id = std::variant<model_id, cluster_id>;
using observation_output = std::variant<std::monostate,
plot_output_id,
file_output_id,
file_discrete_output_id>;
enum class editor_status
{
editing,
initializing,
running_debug,
running_thread,
running_thread_need_join
};
struct plot_output
{
plot_output() = default;
plot_output(std::string_view name_)
: name(name_)
{}
void operator()(const irt::observer& obs,
const irt::dynamics_type /*type*/,
const irt::time tl,
const irt::time t,
const irt::observer::status s);
editor* ed = nullptr;
std::vector<float> xs;
std::vector<float> ys;
small_string<24u> name;
double tl = 0.0;
double time_step = 0.01;
};
struct file_output
{
file_output() = default;
file_output(std::string_view name_)
: name(name_)
{}
void operator()(const irt::observer& obs,
const irt::dynamics_type type,
const irt::time tl,
const irt::time t,
const irt::observer::status s);
editor* ed = nullptr;
std::ofstream ofs;
small_string<24u> name;
};
struct file_discrete_output
{
file_discrete_output() = default;
file_discrete_output(std::string_view name_)
: name(name_)
{}
void operator()(const irt::observer& obs,
const irt::dynamics_type type,
const irt::time tl,
const irt::time t,
const irt::observer::status s);
editor* ed = nullptr;
std::ofstream ofs;
small_string<24u> name;
double tl = 0.0;
double time_step = 0.01;
};
struct cluster
{
cluster() = default;
small_string<16> name;
std::vector<child_id> children;
std::vector<int> input_ports;
std::vector<int> output_ports;
int get(const child_id id) const noexcept
{
auto it = std::find(std::begin(children), std::end(children), id);
if (it == std::end(children))
return not_found;
return static_cast<int>(std::distance(std::begin(children), it));
}
};
struct top_cluster
{
std::vector<std::pair<child_id, int>> children;
int next_node_id = 0;
static inline constexpr int not_found = -1;
status init(size_t models) noexcept
{
try {
children.reserve(models);
} catch (const std::bad_alloc&) {
std::vector<std::pair<child_id, int>>().swap(children);
irt_bad_return(status::gui_not_enough_memory);
}
return status::success;
}
int get_index(const child_id id) const noexcept
{
for (int i = 0, e = length(children); i != e; ++i)
if (children[i].first == id)
return i;
return not_found;
}
int get_index(const int node) const noexcept
{
for (int i = 0, e = length(children); i != e; ++i)
if (children[i].second == node)
return i;
return not_found;
}
void clear() noexcept
{
children.clear();
}
void pop(const int index) noexcept
{
std::swap(children[index], children.back());
children.pop_back();
}
int emplace_back(const child_id id)
{
int ret = next_node_id++;
children.emplace_back(id, ret);
return ret;
}
};
inline int
make_input_node_id(const irt::model_id mdl, const int port) noexcept;
inline int
make_output_node_id(const irt::model_id mdl, const int port) noexcept;
inline std::pair<irt::u32, irt::u32>
get_model_input_port(const int node_id) noexcept;
inline std::pair<irt::u32, irt::u32>
get_model_output_port(const int node_id) noexcept;
struct editor
{
small_string<16> name;
std::filesystem::path path;
imnodes::EditorContext* context = nullptr;
bool initialized = false;
bool show = true;
simulation sim;
external_source srcs;
double simulation_begin = 0.0;
double simulation_end = 10.0;
double simulation_current = 10.0;
double simulation_next_time = 0.0;
long simulation_bag_id = 0;
double simulation_during_date;
int simulation_during_bag;
std::thread simulation_thread;
editor_status st = editor_status::editing;
status sim_st = status::success;
bool simulation_show_value = false;
bool stop = false;
data_array<plot_output, plot_output_id> plot_outs;
data_array<file_output, file_output_id> file_outs;
data_array<file_discrete_output, file_discrete_output_id>
file_discrete_outs;
std::vector<observation_output> observation_outputs;
void show_sources_window(bool* is_show);
void show_menu_sources(const char* title, source& src);
template<typename Function, typename... Args>
constexpr void observation_dispatch(const u32 index,
Function&& f,
Args... args) noexcept
{
switch (observation_outputs[index].index()) {
case 1:
f(plot_outs,
std::get<plot_output_id>(observation_outputs[index]),
args...);
break;
case 2:
f(file_outs,
std::get<file_output_id>(observation_outputs[index]),
args...);
break;
case 3:
f(file_discrete_outs,
std::get<file_discrete_output_id>(observation_outputs[index]),
args...);
break;
default:
break;
}
}
void observation_outputs_free(const u32 index) noexcept
{
observation_dispatch(
index, [](auto& outs, auto out_id) { outs.free(out_id); });
observation_outputs[index] = std::monostate{};
}
std::filesystem::path observation_directory;
data_array<cluster, cluster_id> clusters;
std::vector<cluster_id> clusters_mapper; /* group per cluster_id */
std::vector<cluster_id> models_mapper; /* group per model_id */
std::vector<bool> models_make_transition;
ImVector<ImVec2> positions;
ImVector<ImVec2> displacements;
bool use_real_time;
bool starting = true;
double synchronize_timestep;
top_cluster top;
std::string tooltip;
bool show_load_file_dialog = false;
bool show_save_file_dialog = false;
bool show_select_directory_dialog = false;
bool show_settings = false;
bool show_sources = false;
struct settings_manager
{
int kernel_model_cache = 1024;
int kernel_message_cache = 32768;
int gui_node_cache = 1024;
ImVec4 gui_model_color{ .27f, .27f, .54f, 1.f };
ImVec4 gui_model_transition_color{ .27f, .54f, .54f, 1.f };
ImVec4 gui_cluster_color{ .27f, .54f, .27f, 1.f };
ImU32 gui_hovered_model_color;
ImU32 gui_selected_model_color;
ImU32 gui_hovered_model_transition_color;
ImU32 gui_selected_model_transition_color;
ImU32 gui_hovered_cluster_color;
ImU32 gui_selected_cluster_color;
int automatic_layout_iteration_limit = 200;
float automatic_layout_x_distance = 350.f;
float automatic_layout_y_distance = 350.f;
float grid_layout_x_distance = 250.f;
float grid_layout_y_distance = 250.f;
bool show_dynamics_inputs_in_editor = false;
void compute_colors() noexcept;
void show(bool* is_open);
} settings;
status initialize(u32 id) noexcept;
void clear() noexcept;
void group(const ImVector<int>& nodes) noexcept;
void ungroup(const int node) noexcept;
void free_group(cluster& group) noexcept;
void free_children(const ImVector<int>& nodes) noexcept;
status copy(const ImVector<int>& nodes) noexcept;
void compute_grid_layout() noexcept;
void compute_automatic_layout() noexcept;
bool is_in_hierarchy(const cluster& group,
const cluster_id group_to_search) const noexcept;
cluster_id ancestor(const child_id child) const noexcept;
int get_top_group_ref(const child_id child) const noexcept;
cluster_id parent(cluster_id child) const noexcept
{
return clusters_mapper[get_index(child)];
}
cluster_id parent(model_id child) const noexcept
{
return models_mapper[get_index(child)];
}
void parent(const cluster_id child, const cluster_id parent) noexcept
{
clusters_mapper[get_index(child)] = parent;
}
void parent(const model_id child, const cluster_id parent) noexcept
{
models_mapper[get_index(child)] = parent;
}
struct gport
{
gport() noexcept = default;
gport(irt::model* model_, const int port_index_) noexcept
: model(model_)
, port_index(port_index_)
{}
irt::model* model = nullptr;
int port_index = 0;
};
gport get_in(const int index) noexcept
{
const auto model_index_port = get_model_input_port(index);
auto* mdl = sim.models.try_to_get(model_index_port.first);
return { mdl, static_cast<int>(model_index_port.second) };
}
gport get_out(const int index) noexcept
{
const auto model_index_port = get_model_output_port(index);
auto* mdl = sim.models.try_to_get(model_index_port.first);
return { mdl, static_cast<int>(model_index_port.second) };
}
status add_lotka_volterra() noexcept;
status add_izhikevitch() noexcept;
void show_connections() noexcept;
void show_model_dynamics(model& mdl) noexcept;
void show_model_cluster(cluster& mdl) noexcept;
void show_top() noexcept;
bool show_editor() noexcept;
};
struct window_logger
{
ImGuiTextBuffer buffer;
ImGuiTextFilter filter;
ImVector<int> line_offsets;
bool auto_scroll = true;
bool scroll_to_bottom = false;
window_logger() = default;
void clear() noexcept;
void log(const int level, const char* fmt, ...) IM_FMTARGS(3);
void log(const int level, const char* fmt, va_list args) IM_FMTLIST(3);
void show(bool* is_show);
};
struct application
{
data_array<editor, editor_id> editors;
std::filesystem::path home_dir;
std::filesystem::path ex