Commit 31dec45d authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

gui: add application class to move static variables

parent e35da6e7
Pipeline #27843 passed with stage
in 1 minute and 40 seconds
...@@ -120,31 +120,39 @@ get_executable_directory() ...@@ -120,31 +120,39 @@ get_executable_directory()
} }
#endif #endif
path_manager::path_manager(window_logger& logger) application::settings_manager::settings_manager() noexcept
{ {
if (auto home = get_home_directory(); home) { try {
home_dir = home.value(); if (auto home = get_home_directory(); home) {
home_dir /= "irritator"; home_dir = home.value();
} else { home_dir /= "irritator";
logger.log( } else {
3, "Fail to retrieve home directory. Use current directory instead"); log_w.log(
home_dir = std::filesystem::current_path(); 3,
} "Fail to retrieve home directory. Use current directory instead");
home_dir = std::filesystem::current_path();
}
if (auto install = get_executable_directory(); install) { if (auto install = get_executable_directory(); install) {
install_dir = install.value(); executable_dir = install.value();
} else { } else {
logger.log( log_w.log(
3, 3,
"Fail to retrieve executable directory. Use current directory " "Fail to retrieve executable directory. Use current directory "
"instead"); "instead");
install_dir = std::filesystem::current_path(); executable_dir = std::filesystem::current_path();
} }
logger.log(5, log_w.log(5,
"home: %s\ninstall: %s\n", "home: %s\ninstall: %s\n",
home_dir.u8string().c_str(), home_dir.u8string().c_str(),
install_dir.u8string().c_str()); executable_dir.u8string().c_str());
// TODO Fill the libraries vectors with users and systems directory.
} catch (const std::exception& /*e*/) {
log_w.log(2, "Fail to initialize application");
}
} }
struct file_dialog struct file_dialog
......
...@@ -11,13 +11,13 @@ ...@@ -11,13 +11,13 @@
namespace irt { namespace irt {
void void
node_editor_initialize(); application_initialize();
bool bool
node_editor_show(); application_show();
void void
node_editor_shutdown(); application_shutdown();
/* Move into internal API */ /* Move into internal API */
......
...@@ -120,7 +120,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) ...@@ -120,7 +120,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version); ImGui_ImplOpenGL3_Init(glsl_version);
imnodes::Initialize(); imnodes::Initialize();
irt::node_editor_initialize(); irt::application_initialize();
// Load Fonts // Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can // - If no fonts are loaded, dear imgui will use the default font. You can
...@@ -169,7 +169,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) ...@@ -169,7 +169,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
if (!irt::node_editor_show()) if (!irt::application_show())
glfwSetWindowShouldClose(window, GLFW_TRUE); glfwSetWindowShouldClose(window, GLFW_TRUE);
// Rendering // Rendering
...@@ -189,7 +189,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) ...@@ -189,7 +189,7 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
// Cleanup // Cleanup
irt::node_editor_shutdown(); irt::application_shutdown();
imnodes::Shutdown(); imnodes::Shutdown();
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
......
...@@ -97,7 +97,7 @@ int main(int, char**) ...@@ -97,7 +97,7 @@ int main(int, char**)
g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart()); g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
imnodes::Initialize(); imnodes::Initialize();
irt::node_editor_initialize(); irt::application_initialize();
// Load Fonts // Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
...@@ -141,7 +141,7 @@ int main(int, char**) ...@@ -141,7 +141,7 @@ int main(int, char**)
ImGui_ImplWin32_NewFrame(); ImGui_ImplWin32_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
if (!irt::node_editor_show()) if (!irt::application_show())
::PostMessage(hwnd, WM_CLOSE, 0, 0); ::PostMessage(hwnd, WM_CLOSE, 0, 0);
// Rendering // Rendering
...@@ -180,7 +180,7 @@ int main(int, char**) ...@@ -180,7 +180,7 @@ int main(int, char**)
frameCtxt->FenceValue = fenceValue; frameCtxt->FenceValue = fenceValue;
} }
irt::node_editor_shutdown(); irt::application_shutdown();
imnodes::Shutdown(); imnodes::Shutdown();
......
...@@ -23,37 +23,14 @@ ...@@ -23,37 +23,14 @@
namespace irt { namespace irt {
static int kernel_model_cache = 1024;
static int kernel_message_cache = 32768;
static int gui_node_cache = 1024;
static ImVec4 gui_model_color{ .27f, .27f, .54f, 1.f };
static ImVec4 gui_model_transition_color{ .27f, .54f, .54f, 1.f };
static ImVec4 gui_cluster_color{ .27f, .54f, .27f, 1.f };
static ImU32 gui_hovered_model_color;
static ImU32 gui_selected_model_color;
static ImU32 gui_hovered_model_transition_color;
static ImU32 gui_selected_model_transition_color;
static ImU32 gui_hovered_cluster_color;
static ImU32 gui_selected_cluster_color;
static int automatic_layout_iteration_limit = 200;
static auto automatic_layout_x_distance = 350.f;
static auto automatic_layout_y_distance = 350.f;
static auto grid_layout_x_distance = 250.f;
static auto grid_layout_y_distance = 250.f;
static bool show_dynamics_inputs_in_editor = false;
static ImVec4 static ImVec4
operator*(const ImVec4& lhs, const float rhs) noexcept operator*(const ImVec4& lhs, const float rhs) noexcept
{ {
return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs);
} }
static void void
compute_color() noexcept editor::settings_manager::compute_colors() noexcept
{ {
gui_hovered_model_color = gui_hovered_model_color =
ImGui::ColorConvertFloat4ToU32(gui_model_color * 1.25f); ImGui::ColorConvertFloat4ToU32(gui_model_color * 1.25f);
...@@ -130,9 +107,6 @@ for_each(DataArray& d_array, Container& container, Function f) noexcept ...@@ -130,9 +107,6 @@ for_each(DataArray& d_array, Container& container, Function f) noexcept
} }
} }
static window_logger log_w;
static data_array<editor, editor_id> editors;
void void
editor::clear() noexcept editor::clear() noexcept
{ {
...@@ -766,10 +740,11 @@ editor::compute_grid_layout() noexcept ...@@ -766,10 +740,11 @@ editor::compute_grid_layout() noexcept
int elem = 0; int elem = 0;
for (int i = 0; i < column; ++i) { for (int i = 0; i < column; ++i) {
new_pos.y = panning.y + static_cast<float>(i) * grid_layout_y_distance; new_pos.y =
panning.y + static_cast<float>(i) * settings.grid_layout_y_distance;
for (int j = 0; j < line; ++j) { for (int j = 0; j < line; ++j) {
new_pos.x = new_pos.x = panning.x +
panning.x + static_cast<float>(j) * grid_layout_x_distance; static_cast<float>(j) * settings.grid_layout_x_distance;
imnodes::SetNodeGridSpacePos(top.children[elem].second, new_pos); imnodes::SetNodeGridSpacePos(top.children[elem].second, new_pos);
positions[elem].x = new_pos.x; positions[elem].x = new_pos.x;
positions[elem].y = new_pos.y; positions[elem].y = new_pos.y;
...@@ -778,9 +753,11 @@ editor::compute_grid_layout() noexcept ...@@ -778,9 +753,11 @@ editor::compute_grid_layout() noexcept
} }
new_pos.x = panning.x; new_pos.x = panning.x;
new_pos.y = panning.y + static_cast<float>(column) * grid_layout_y_distance; new_pos.y =
panning.y + static_cast<float>(column) * settings.grid_layout_y_distance;
for (int j = 0; j < remaining; ++j) { for (int j = 0; j < remaining; ++j) {
new_pos.x = panning.x + static_cast<float>(j) * grid_layout_x_distance; new_pos.x =
panning.x + static_cast<float>(j) * settings.grid_layout_x_distance;
imnodes::SetNodeGridSpacePos(top.children[elem].second, new_pos); imnodes::SetNodeGridSpacePos(top.children[elem].second, new_pos);
positions[elem].x = new_pos.x; positions[elem].x = new_pos.x;
positions[elem].y = new_pos.y; positions[elem].y = new_pos.y;
...@@ -809,8 +786,10 @@ editor::compute_automatic_layout() noexcept ...@@ -809,8 +786,10 @@ editor::compute_automatic_layout() noexcept
remaining -= column; remaining -= column;
} }
const float W = static_cast<float>(column) * automatic_layout_x_distance; const float W =
const float L = line + (remaining > 0) ? automatic_layout_y_distance : 0.f; static_cast<float>(column) * settings.automatic_layout_x_distance;
const float L =
line + (remaining > 0) ? settings.automatic_layout_y_distance : 0.f;
const float area = W * L; const float area = W * L;
const float k_square = area / static_cast<float>(top.children.size()); const float k_square = area / static_cast<float>(top.children.size());
const float k = std::sqrt(k_square); const float k = std::sqrt(k_square);
...@@ -819,9 +798,11 @@ editor::compute_automatic_layout() noexcept ...@@ -819,9 +798,11 @@ editor::compute_automatic_layout() noexcept
// static_cast<float>(automatic_layout_iteration_limit); // static_cast<float>(automatic_layout_iteration_limit);
// t *= t; // t *= t;
float t = 1.f - 1.f / static_cast<float>(automatic_layout_iteration_limit); float t =
1.f - 1.f / static_cast<float>(settings.automatic_layout_iteration_limit);
for (int iteration = 0; iteration < automatic_layout_iteration_limit; for (int iteration = 0;
iteration < settings.automatic_layout_iteration_limit;
++iteration) { ++iteration) {
for (int i_v = 0; i_v < size; ++i_v) { for (int i_v = 0; i_v < size; ++i_v) {
const int v = i_v; const int v = i_v;
...@@ -953,12 +934,12 @@ editor::copy(const ImVector<int>& nodes) noexcept ...@@ -953,12 +934,12 @@ editor::copy(const ImVector<int>& nodes) noexcept
status status
editor::initialize(u32 id) noexcept editor::initialize(u32 id) noexcept
{ {
irt_return_if_bad(sim.init(to_unsigned(kernel_model_cache), irt_return_if_bad(sim.init(to_unsigned(settings.kernel_model_cache),
to_unsigned(kernel_message_cache))); to_unsigned(settings.kernel_message_cache)));
irt_return_if_bad(clusters.init(sim.models.capacity())); irt_return_if_bad(clusters.init(sim.models.capacity()));
irt_return_if_bad(top.init(to_unsigned(gui_node_cache))); irt_return_if_bad(top.init(to_unsigned(settings.gui_node_cache)));
irt_return_if_bad(plot_outs.init(to_unsigned(kernel_model_cache))); irt_return_if_bad(plot_outs.init(to_unsigned(settings.kernel_model_cache)));
irt_return_if_bad(file_outs.init(to_unsigned(kernel_model_cache))); irt_return_if_bad(file_outs.init(to_unsigned(settings.kernel_model_cache)));
try { try {
observation_outputs.resize(sim.models.capacity()); observation_outputs.resize(sim.models.capacity());
...@@ -2026,7 +2007,7 @@ editor::show_model_dynamics(model& mdl) noexcept ...@@ -2026,7 +2007,7 @@ editor::show_model_dynamics(model& mdl) noexcept
add_input_attribute(*this, dyn); add_input_attribute(*this, dyn);
ImGui::PushItemWidth(120.0f); ImGui::PushItemWidth(120.0f);
if (show_dynamics_inputs_in_editor) if (settings.show_dynamics_inputs_in_editor)
show_dynamics_inputs(dyn); show_dynamics_inputs(dyn);
ImGui::PopItemWidth(); ImGui::PopItemWidth();
add_output_attribute(*this, dyn); add_output_attribute(*this, dyn);
...@@ -2121,25 +2102,27 @@ editor::show_top() noexcept ...@@ -2121,25 +2102,27 @@ editor::show_top() noexcept
if (st != editor_status::editing && if (st != editor_status::editing &&
models_make_transition[get_index(id)]) { models_make_transition[get_index(id)]) {
imnodes::PushColorStyle(imnodes::ColorStyle_TitleBar, imnodes::PushColorStyle(
ImGui::ColorConvertFloat4ToU32( imnodes::ColorStyle_TitleBar,
gui_model_transition_color)); ImGui::ColorConvertFloat4ToU32(
settings.gui_model_transition_color));
imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarHovered, imnodes::PushColorStyle(
gui_hovered_model_transition_color); imnodes::ColorStyle_TitleBarHovered,
settings.gui_hovered_model_transition_color);
imnodes::PushColorStyle( imnodes::PushColorStyle(
imnodes::ColorStyle_TitleBarSelected, imnodes::ColorStyle_TitleBarSelected,
gui_selected_model_transition_color); settings.gui_selected_model_transition_color);
} else { } else {
imnodes::PushColorStyle( imnodes::PushColorStyle(
imnodes::ColorStyle_TitleBar, imnodes::ColorStyle_TitleBar,
ImGui::ColorConvertFloat4ToU32(gui_model_color)); ImGui::ColorConvertFloat4ToU32(settings.gui_model_color));
imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarHovered, imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarHovered,
gui_hovered_model_color); settings.gui_hovered_model_color);
imnodes::PushColorStyle( imnodes::PushColorStyle(
imnodes::ColorStyle_TitleBarSelected, imnodes::ColorStyle_TitleBarSelected,
gui_selected_model_color); settings.gui_selected_model_color);
} }
imnodes::BeginNode(top.children[i].second); imnodes::BeginNode(top.children[i].second);
...@@ -2173,11 +2156,11 @@ editor::show_top() noexcept ...@@ -2173,11 +2156,11 @@ editor::show_top() noexcept
if (auto* gp = clusters.try_to_get(id); gp) { if (auto* gp = clusters.try_to_get(id); gp) {
imnodes::PushColorStyle( imnodes::PushColorStyle(
imnodes::ColorStyle_TitleBar, imnodes::ColorStyle_TitleBar,
ImGui::ColorConvertFloat4ToU32(gui_cluster_color)); ImGui::ColorConvertFloat4ToU32(settings.gui_cluster_color));
imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarHovered, imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarHovered,
gui_hovered_cluster_color); settings.gui_hovered_cluster_color);
imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarSelected, imnodes::PushColorStyle(imnodes::ColorStyle_TitleBarSelected,
gui_selected_cluster_color); settings.gui_selected_cluster_color);
imnodes::BeginNode(top.children[i].second); imnodes::BeginNode(top.children[i].second);
imnodes::BeginNodeTitleBar(); imnodes::BeginNodeTitleBar();
...@@ -2205,6 +2188,46 @@ editor::show_top() noexcept ...@@ -2205,6 +2188,46 @@ editor::show_top() noexcept
} }
} }
void
editor::settings_manager::show(bool* is_open)
{
ImGui::SetNextWindowPos(ImVec2(300, 300), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(350, 400), ImGuiCond_Once);
if (!ImGui::Begin("Settings", is_open)) {
ImGui::End();
return;
}
ImGui::Text("Kernel");
ImGui::DragInt("model cache", &kernel_model_cache, 1.f, 1024, 1024 * 1024);
ImGui::DragInt("msg cache", &kernel_message_cache, 1.f, 1024, 1024 * 1024);
ImGui::Text("Graphics");
ImGui::DragInt("node cache", &gui_node_cache, 1.f, 1024, 1024 * 1024);
if (ImGui::ColorEdit3(
"model", (float*)&gui_model_color, ImGuiColorEditFlags_NoOptions))
compute_colors();
if (ImGui::ColorEdit3(
"cluster", (float*)&gui_cluster_color, ImGuiColorEditFlags_NoOptions))
compute_colors();
ImGui::Text("Automatic layout parameters");
ImGui::DragInt(
"max iteration", &automatic_layout_iteration_limit, 1.f, 0, 1000);
ImGui::DragFloat(
"a-x-distance", &automatic_layout_x_distance, 1.f, 150.f, 500.f);
ImGui::DragFloat(
"a-y-distance", &automatic_layout_y_distance, 1.f, 150.f, 500.f);
ImGui::Text("Grid layout parameters");
ImGui::DragFloat(
"g-x-distance", &grid_layout_x_distance, 1.f, 150.f, 500.f);
ImGui::DragFloat(
"g-y-distance", &grid_layout_y_distance, 1.f, 150.f, 500.f);
ImGui::End();
}
void void
add_popup_menuitem(editor& ed, dynamics_type type, model_id* new_model) add_popup_menuitem(editor& ed, dynamics_type type, model_id* new_model)
{ {
...@@ -2280,13 +2303,15 @@ editor::show_editor() noexcept ...@@ -2280,13 +2303,15 @@ editor::show_editor() noexcept
if (ImGui::BeginMenu("Edition")) { if (ImGui::BeginMenu("Edition")) {
ImGui::MenuItem("Show parameter in models", ImGui::MenuItem("Show parameter in models",
nullptr, nullptr,
&show_dynamics_inputs_in_editor); &settings.show_dynamics_inputs_in_editor);
if (ImGui::MenuItem("Clear")) if (ImGui::MenuItem("Clear"))
clear(); clear();
if (ImGui::MenuItem("Grid Reorder")) if (ImGui::MenuItem("Grid Reorder"))
compute_grid_layout(); compute_grid_layout();
if (ImGui::MenuItem("Automatic Layout")) if (ImGui::MenuItem("Automatic Layout"))
compute_automatic_layout(); compute_automatic_layout();
if (ImGui::MenuItem("Settings"))
show_settings = true;
ImGui::EndMenu(); ImGui::EndMenu();
} }
...@@ -2599,15 +2624,15 @@ editor::show_editor() noexcept ...@@ -2599,15 +2624,15 @@ editor::show_editor() noexcept
if (num_selected_nodes > 0) { if (num_selected_nodes > 0) {
selected_nodes.resize(num_selected_nodes, -1); selected_nodes.resize(num_selected_nodes, -1);
if (ImGui::IsKeyReleased('X')) { if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyReleased('X')) {
imnodes::GetSelectedNodes(selected_nodes.begin()); imnodes::GetSelectedNodes(selected_nodes.begin());
log_w.log(7, "%d model(s) to delete\n", num_selected_nodes); log_w.log(7, "%d model(s) to delete\n", num_selected_nodes);
free_children(selected_nodes); free_children(selected_nodes);
} else if (ImGui::IsKeyReleased('D')) { } else if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyReleased('D')) {
imnodes::GetSelectedNodes(selected_nodes.begin()); imnodes::GetSelectedNodes(selected_nodes.begin());
log_w.log(7, "%d model(s)/group(s) to copy\n", num_selected_nodes); log_w.log(7, "%d model(s)/group(s) to copy\n", num_selected_nodes);
copy(selected_nodes); copy(selected_nodes);
} else if (ImGui::IsKeyReleased('G')) { } else if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyReleased('G')) {
if (num_selected_nodes > 1) { if (num_selected_nodes > 1) {
imnodes::GetSelectedNodes(selected_nodes.begin()); imnodes::GetSelectedNodes(selected_nodes.begin());
log_w.log(7, "%d model(s) to group\n", num_selected_nodes); log_w.log(7, "%d model(s) to group\n", num_selected_nodes);
...@@ -2622,7 +2647,7 @@ editor::show_editor() noexcept ...@@ -2622,7 +2647,7 @@ editor::show_editor() noexcept
} else if (num_selected_links > 0) { } else if (num_selected_links > 0) {
selected_links.resize(static_cast<size_t>(num_selected_links)); selected_links.resize(static_cast<size_t>(num_selected_links));
if (ImGui::IsKeyReleased('X')) { if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyReleased('X')) {
std::fill_n(selected_links.begin(), selected_links.size(), -1); std::fill_n(selected_links.begin(), selected_links.size(), -1);
imnodes::GetSelectedLinks(selected_links.begin()); imnodes::GetSelectedLinks(selected_links.begin());
std::sort( std::sort(
...@@ -2669,6 +2694,13 @@ editor::show_editor() noexcept ...@@ -2669,6 +2694,13 @@ editor::show_editor() noexcept
selected_nodes.resize(num_selected_nodes, -1); selected_nodes.resize(num_selected_nodes, -1);
imnodes::GetSelectedNodes(selected_nodes.begin()); imnodes::GetSelectedNodes(selected_nodes.begin());
static std::vector<std::string> names;
names.clear();
names.resize(selected_nodes.size());
for (int i = 0, e = selected_nodes.size(); i != e; ++i)
names[i] = fmt::format("{}", selected_nodes[i]);
for (int i = 0, e = selected_nodes.size(); i != e; ++i) { for (int i = 0, e = selected_nodes.size(); i != e; ++i) {
const auto index = top.get_index(selected_nodes[i]); const auto index = top.get_index(selected_nodes[i]);
...@@ -2684,9 +2716,8 @@ editor::show_editor() noexcept ...@@ -2684,9 +2716,8 @@ editor::show_editor() noexcept
if (!mdl) if (!mdl)
continue; continue;
// TODO use the dynamic or model name. if (ImGui::TreeNodeEx(names[i].c_str(),
const auto& name = dynamics_type_names[static_cast<int>(mdl->type)]; ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::TreeNodeEx(name, ImGuiTreeNodeFlags_DefaultOpen)) {
auto& out = observation_outputs[get_index(id)]; auto& out = observation_outputs[get_index(id)];
auto* plot = plot_outs.try_to_get(out.plot_id); auto* plot = plot_outs.try_to_get(out.plot_id);
...@@ -2695,6 +2726,9 @@ editor::show_editor() noexcept ...@@ -2695,6 +2726,9 @@ editor::show_editor() noexcept
irt_assert(!(file && plot)); irt_assert(!(file && plot));
int choose = plot ? 1 : file ? 2 : 0; int choose = plot ? 1 : file ? 2 : 0;
ImGui::Text("%s",
dynamics_type_names[static_cast<int>(mdl->type)]);
ImGui::RadioButton("none", &choose, 0); ImGui::RadioButton("none", &choose, 0);
ImGui::SameLine(); ImGui::SameLine();
ImGui::RadioButton("plot", &choose, 1); ImGui::RadioButton("plot", &choose, 1);
...@@ -2708,11 +2742,11 @@ editor::show_editor() noexcept ...@@ -2708,11 +2742,11 @@ editor::show_editor() noexcept
} }
if (!plot) { if (!plot) {
plot_output& tf = plot_outs.alloc("unamed"); plot_output& tf = plot_outs.alloc(names[i].c_str());
plot = &tf; plot = &tf;
out.plot_id = plot_outs.get_id(tf); out.plot_id = plot_outs.get_id(tf);
auto& o = auto& o = sim.observers.alloc(
sim.observers.alloc(0.01, "unamed", (void*)plot); 0.01, names[i].c_str(), (void*)plot);
o.initialize = &observation_plot_output_initialize; o.initialize = &observation_plot_output_initialize;
o.observe = &observation_plot_output_observe; o.observe = &observation_plot_output_observe;
o.free = &observation_plot_output_free; o.free = &observation_plot_output_free;
...@@ -2720,7 +2754,7 @@ editor::show_editor() noexcept ...@@ -2720,7 +2754,7 @@ editor::show_editor() noexcept