node-editor.hpp 9.58 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
116
117
118
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>;

enum class simulation_status
{
    success,
119
    running_once,
Gauthier Quesnel's avatar
Gauthier Quesnel committed
120
    running_once_need_join,
121
    running_step,
122
123
124
125
126
127
};

static inline constexpr int not_found = -1;

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

    static inline constexpr int not_found = -1;

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

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

151
        return not_found;
152
153
154
155
    }

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

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

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

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

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

        children.emplace_back(id, ret);

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

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

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

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
224
        multiplot,
225
226
227
228
229
230
231
232
233
234
235
        file,
        both
    };

    observation_output() = default;

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

    std::ofstream ofs;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
236
    std::string name;
237
238
    array<float> xs;
    array<float> ys;
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    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
255

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

265
    std::thread simulation_thread;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
266
    simulation_status st = simulation_status::success;
267

268
269
270
271
272
273
274
275
276
277
278
    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
279
280
    array<bool> models_make_transition;

281
282
283
    ImVector<ImVec2> positions;
    ImVector<ImVec2> displacements;

284
285
286
287
288
289
290
291
292
    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;
293
294
    status copy(const ImVector<int>& nodes) noexcept;

295
296
    void compute_grid_layout() noexcept;
    void compute_automatic_layout() noexcept;
297
298
299
300
301

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

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

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

335
    static int get_out(output_port_id id) noexcept
336
337
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
    {
        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;
};

366
367
368
369
370
371
editor*
make_combo_editor_name(editor_id& current) noexcept;

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

372
373
374
void
initialize_observation(irt::editor* ed);

375
376
} // namespace irt

377
#endif