node-editor.hpp 9.62 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
63
64
65
66
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_adder_empty_init_message",
        "model_adder_bad_init_message",
        "model_mult_empty_init_message",
        "model_mult_bad_init_message",
        "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",
67
68
        "model_flow_bad_samplerate",
        "model_flow_bad_data",
Gauthier Quesnel's avatar
Gauthier Quesnel committed
69
70
71
72
73
74
75
76
77
78
        "gui_not_enough_memory",
        "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"
    };

79
80
    static_assert(std::size(str) == status_size());

Gauthier Quesnel's avatar
Gauthier Quesnel committed
81
82
83
    return str[static_cast<int>(s)];
}

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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);
}

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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>;

116
enum class editor_status
117
{
118
119
120
121
122
    editing,
    initializing,
    running_debug,
    running_thread,
    running_thread_need_join
123
124
125
126
127
128
};

static inline constexpr int not_found = -1;

struct top_cluster
{
129
    std::vector<std::pair<child_id, int>> children;
130
131
132
133
    int next_node_id = 0;

    static inline constexpr int not_found = -1;

134
135
136
137
138
139
140
141
142
143
144
145
    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;
    }

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

152
        return not_found;
153
154
155
156
    }

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

161
        return not_found;
162
163
164
165
166
167
168
169
170
171
172
173
174
    }

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

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

175
    int emplace_back(const child_id id)
176
    {
177
178
179
180
181
        int ret = next_node_id++;

        children.emplace_back(id, ret);

        return ret;
182
183
184
185
186
187
188
189
190
191
192
193
194
195
    }
};

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
    {
196
197
198
        auto it = std::find(std::begin(children), std::end(children), id);
        if (it == std::end(children))
            return not_found;
199

200
        return static_cast<int>(std::distance(std::begin(children), it));
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
    }
};

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 observation_output
{
    enum class type
    {
        none,
Gauthier Quesnel's avatar
Gauthier Quesnel committed
225
        multiplot,
226
227
228
229
230
231
232
233
234
235
236
        file,
        both
    };

    observation_output() = default;

    observation_output(const char* name_)
      : name(name_)
    {}

    std::ofstream ofs;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
237
    std::string name;
238
239
    array<float> xs;
    array<float> ys;
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    double tl = 0.0;
    float min = -1.f;
    float max = +1.f;
    int id = 0;
    type observation_type = type::none;
};

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
256

257
258
259
    double simulation_begin = 0.0;
    double simulation_end = 10.0;
    double simulation_current = 10.0;
260
    double simulation_next_time = 0.0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
261
    long simulation_bag_id = 0;
262

Gauthier Quesnel's avatar
Gauthier Quesnel committed
263
264
    double simulation_during_date;
    int simulation_during_bag;
265

266
    std::thread simulation_thread;
267
268
    editor_status st = editor_status::editing;
    status sim_st = status::success;
269

270
271
272
273
274
275
276
277
278
279
280
    bool simulation_show_value = false;
    bool stop = false;

    vector<observation_output> observation_outputs;
    array<observation_output::type> observation_types;
    std::filesystem::path observation_directory;

    data_array<cluster, cluster_id> clusters;
    array<cluster_id> clusters_mapper; /* group per cluster_id */
    array<cluster_id> models_mapper;   /* group per model_id */

Gauthier Quesnel's avatar
Gauthier Quesnel committed
281
282
    array<bool> models_make_transition;

283
284
285
    ImVector<ImVec2> positions;
    ImVector<ImVec2> displacements;

286
287
288
289
290
291
292
293
294
    top_cluster top;

    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;
295
296
    status copy(const ImVector<int>& nodes) noexcept;

297
298
    void compute_grid_layout() noexcept;
    void compute_automatic_layout() noexcept;
299
300
301
302
303

    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;
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324

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

325
    static int get_in(input_port_id id) noexcept
326
327
328
329
330
331
332
333
334
335
336
    {
        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>();
    }

337
    static int get_out(output_port_id id) noexcept
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
    {
        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;
};

368
369
370
371
372
373
editor*
make_combo_editor_name(editor_id& current) noexcept;

void
show_simulation_box(window_logger& log_w, bool* show_simulation);

374
375
376
void
initialize_observation(irt::editor* ed);

377
378
} // namespace irt

379
#endif