node-editor.hpp 12.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef ORG_VLEPROJECT_IRRITATOR_APP_NODE_EDITOR_2020
#define ORG_VLEPROJECT_IRRITATOR_APP_NODE_EDITOR_2020

#include <irritator/core.hpp>

#include <filesystem>
#include <fstream>
#include <thread>
#include <variant>
#include <vector>

#include "imnodes.hpp"
#include <imgui.h>

namespace irt {

Gauthier Quesnel's avatar
Gauthier Quesnel committed
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
inline const char*
status_string(const status s) noexcept
{
    static const char* str[] = {
        "success",
        "unknown_dynamics",
        "block_allocator_bad_capacity",
        "block_allocator_not_enough_memory",
        "head_allocator_bad_capacity",
        "head_allocator_not_enough_memory",
        "simulation_not_enough_model",
        "simulation_not_enough_memory_message_list_allocator",
        "simulation_not_enough_memory_input_port_list_allocator",
        "simulation_not_enough_memory_output_port_list_allocator",
        "data_array_init_capacity_error",
        "data_array_not_enough_memory",
        "data_array_archive_init_capacity_error",
        "data_array_archive_not_enough_memory",
        "array_init_capacity_zero",
        "array_init_capacity_too_big",
        "array_init_not_enough_memory",
        "vector_init_capacity_zero",
        "vector_init_capacity_too_big",
        "vector_init_not_enough_memory",
        "dynamics_unknown_id",
        "dynamics_unknown_port_id",
        "dynamics_not_enough_memory",
        "model_connect_output_port_unknown",
        "model_connect_input_port_unknown",
        "model_connect_already_exist",
        "model_connect_bad_dynamics",
        "model_integrator_dq_error",
        "model_integrator_X_error",
        "model_integrator_internal_error",
        "model_integrator_output_error",
        "model_integrator_running_without_x_dot",
        "model_integrator_ta_with_bad_x_dot",
        "model_quantifier_bad_quantum_parameter",
        "model_quantifier_bad_archive_length_parameter",
        "model_quantifier_shifting_value_neg",
        "model_quantifier_shifting_value_less_1",
        "model_time_func_bad_init_message",
63
64
        "model_flow_bad_samplerate",
        "model_flow_bad_data",
Gauthier Quesnel's avatar
Gauthier Quesnel committed
65
        "gui_not_enough_memory",
66
        "io_not_enough_memory",
Gauthier Quesnel's avatar
Gauthier Quesnel committed
67
68
69
70
71
72
73
74
75
        "io_file_format_error",
        "io_file_format_model_error",
        "io_file_format_model_number_error",
        "io_file_format_model_unknown",
        "io_file_format_dynamics_unknown",
        "io_file_format_dynamics_limit_reach",
        "io_file_format_dynamics_init_error"
    };

76
77
    static_assert(std::size(str) == status_size());

Gauthier Quesnel's avatar
Gauthier Quesnel committed
78
79
80
    return str[static_cast<int>(s)];
}

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
template<class C>
constexpr int
length(const C& c) noexcept
{
    return static_cast<int>(c.size());
}

template<class T, size_t N>
constexpr int
length(const T (&array)[N]) noexcept
{
    (void)array;

    return static_cast<int>(N);
}

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
template<typename Identifier>
constexpr Identifier
undefined() noexcept
{
    static_assert(
      std::is_enum<Identifier>::value,
      "Identifier must be a enumeration: enum class id : unsigned {};");

    return static_cast<Identifier>(0);
}

enum class editor_id : u64;
enum class cluster_id : u64;

using child_id = std::variant<model_id, cluster_id>;

113
enum class editor_status
114
{
115
116
117
118
119
    editing,
    initializing,
    running_debug,
    running_thread,
    running_thread_need_join
120
121
122
123
124
125
};

static inline constexpr int not_found = -1;

struct top_cluster
{
126
    std::vector<std::pair<child_id, int>> children;
127
128
129
130
    int next_node_id = 0;

    static inline constexpr int not_found = -1;

131
132
133
134
135
136
137
138
139
140
141
142
    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;
    }

143
144
    int get_index(const child_id id) const noexcept
    {
145
146
147
        for (int i = 0, e = length(children); i != e; ++i)
            if (children[i].first == id)
                return i;
148

149
        return not_found;
150
151
152
153
    }

    int get_index(const int node) const noexcept
    {
154
155
156
        for (int i = 0, e = length(children); i != e; ++i)
            if (children[i].second == node)
                return i;
157

158
        return not_found;
159
160
161
162
163
164
165
166
167
168
169
170
171
    }

    void clear() noexcept
    {
        children.clear();
    }

    void pop(const int index) noexcept
    {
        std::swap(children[index], children.back());
        children.pop_back();
    }

172
    int emplace_back(const child_id id)
173
    {
174
175
176
177
178
        int ret = next_node_id++;

        children.emplace_back(id, ret);

        return ret;
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    }
};

struct cluster
{
    cluster() = default;

    small_string<16> name;
    std::vector<child_id> children;
    std::vector<input_port_id> input_ports;
    std::vector<output_port_id> output_ports;

    int get(const child_id id) const noexcept
    {
193
194
195
        auto it = std::find(std::begin(children), std::end(children), id);
        if (it == std::end(children))
            return not_found;
196

197
        return static_cast<int>(std::distance(std::begin(children), it));
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
    }
};

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);
};

217
static inline window_logger log_w;
218

219
220
struct editor;

221
222
223
enum class plot_output_id : u64;
enum class file_output_id : u64;

224
struct plot_output
225
{
226
    plot_output() = default;
227

228
    plot_output(std::string_view name_)
229
230
231
      : name(name_)
    {}

232
    void operator()(const irt::observer& obs,
233
                    const irt::dynamics_type /*type*/,
234
235
236
                    const irt::time t,
                    const irt::observer::status s);

237
    editor* ed = nullptr;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
238
239
    std::vector<float> xs;
    std::vector<float> ys;
240
    small_string<24u> name;
241
    double tl = 0.0;
242
    double time_step = 0.1;
243
244
245
246
247
248
249
250
251
252
};

struct file_output
{
    file_output() = default;

    file_output(std::string_view name_)
      : name(name_)
    {}

253
    void operator()(const irt::observer& obs,
254
255
256
                    const irt::dynamics_type /*type*/,
                    const irt::time t,
                    const irt::observer::status s);
257

258
    editor* ed = nullptr;
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
    std::ofstream ofs;
    small_string<24u> name;
    double tl = 0.0;
};

struct observation_output
{
    constexpr observation_output() = default;

    constexpr void clear() noexcept
    {
        plot_id = undefined<plot_output_id>();
        file_id = undefined<file_output_id>();
    }

    plot_output_id plot_id = undefined<plot_output_id>();
    file_output_id file_id = undefined<file_output_id>();
276
277
278
279
280
281
282
283
284
285
286
};

struct editor
{
    small_string<16> name;
    std::filesystem::path path;
    imnodes::EditorContext* context = nullptr;
    bool initialized = false;
    bool show = true;

    simulation sim;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
287

288
289
290
    double simulation_begin = 0.0;
    double simulation_end = 10.0;
    double simulation_current = 10.0;
291
    double simulation_next_time = 0.0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
292
    long simulation_bag_id = 0;
293

Gauthier Quesnel's avatar
Gauthier Quesnel committed
294
295
    double simulation_during_date;
    int simulation_during_bag;
296

297
    std::thread simulation_thread;
298
299
    editor_status st = editor_status::editing;
    status sim_st = status::success;
300

301
302
303
    bool simulation_show_value = false;
    bool stop = false;

304
305
306
    data_array<plot_output, plot_output_id> plot_outs;
    data_array<file_output, file_output_id> file_outs;

Gauthier Quesnel's avatar
Gauthier Quesnel committed
307
    std::vector<observation_output> observation_outputs;
308
309
310
    std::filesystem::path observation_directory;

    data_array<cluster, cluster_id> clusters;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
311
312
    std::vector<cluster_id> clusters_mapper; /* group per cluster_id */
    std::vector<cluster_id> models_mapper;   /* group per model_id */
313

Gauthier Quesnel's avatar
Gauthier Quesnel committed
314
    std::vector<bool> models_make_transition;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
315

316
317
318
    ImVector<ImVec2> positions;
    ImVector<ImVec2> displacements;

319
    bool use_real_time;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
320
    bool starting = true;
321
322
    double synchronize_timestep;

323
324
    top_cluster top;

325
326
    std::string tooltip;

327
328
329
    bool show_load_file_dialog = false;
    bool show_save_file_dialog = false;
    bool show_select_directory_dialog = false;
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    bool show_settings = 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;
360

361
362
363
364
365
366
367
    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;
368
369
    status copy(const ImVector<int>& nodes) noexcept;

370
371
    void compute_grid_layout() noexcept;
    void compute_automatic_layout() noexcept;
372
373
374
375
376

    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;
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

    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;
    }

398
    static int get_in(input_port_id id) noexcept
399
400
401
402
403
404
405
406
407
408
409
    {
        return static_cast<int>(get_index(id));
    }

    input_port_id get_in(int index) const noexcept
    {
        auto* port = sim.input_ports.try_to_get(static_cast<u32>(index));

        return port ? sim.input_ports.get_id(port) : undefined<input_port_id>();
    }

410
    static int get_out(output_port_id id) noexcept
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
    {
        constexpr u32 is_output = 1 << 31;
        u32 index = get_index(id);
        index |= is_output;

        return static_cast<int>(index);
    }

    output_port_id get_out(int index) const noexcept
    {
        constexpr u32 mask = ~(1 << 31); /* remove the first bit */
        index &= mask;

        auto* port = sim.output_ports.try_to_get(static_cast<u32>(index));

        return port ? sim.output_ports.get_id(port)
                    : undefined<output_port_id>();
    }

    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;
};

441
void
442
443
444
445
446
447
448
449
450
451
452
453
454
455
show_simulation_box(editor& ed, bool* show_simulation);

struct application
{
    data_array<editor, editor_id> editors;

    struct settings_manager
    {
        settings_manager() noexcept;

        std::filesystem::path home_dir;
        std::filesystem::path executable_dir;
        std::vector<std::string> libraries_dir;

456
        void show(bool* is_open);
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    } settings;

    bool show_log = true;
    bool show_simulation = true;
    bool show_demo = false;
    bool show_plot = true;
    bool show_settings = false;

    editor* alloc_editor();
    void free_editor(editor& ed);
};

static inline application app;

editor*
make_combo_editor_name(application& app, editor_id& current) noexcept;
473

474
475
} // namespace irt

476
#endif