GCC Code Coverage Report


Directory: ./
File: src/tide/node.cpp
Date: 2023-04-27 00:55:30
Exec Total Coverage
Lines: 0 513 0.0%
Functions: 0 19 0.0%
Branches: 0 407 0.0%

Line Branch Exec Source
1 #include "node.h"
2 #include "bezier.h"
3
4 #include "imgui_internal.h"
5
6 #include <algorithm>
7
8 void GraphEditor::draw() {
9 ✗ hovered_link = nullptr;
10 ✗ hovered_pin = nullptr;
11
12 ✗ ImGuiIO& io = ImGui::GetIO();
13
14 // ImGui::SetNextWindowSize(ImVec2(700, 600), ImGuiCond_FirstUseEver);
15 #ifdef IMGUI_HAS_VIEWPORT
16 ImGuiViewport* viewport = ImGui::GetMainViewport();
17 ImGui::SetNextWindowPos(viewport->GetWorkPos());
18 ImGui::SetNextWindowSize(viewport->GetWorkSize());
19 ImGui::SetNextWindowViewport(viewport->ID);
20 #else
21 ✗ ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
22 ✗ ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize);
23 #endif
24 ✗ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
25 ✗ ImGui::Begin("GraphEditor", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize);
26
27 ✗ const ImVec2 offset = ImGui::GetCursorScreenPos() + scrolling;
28 ✗ _offset = offset;
29
30 ✗ ImGui::BeginGroup();
31 ✗ ImGui::BeginChild("scrolling_region",
32 ✗ ImVec2(0, 0),
33 true,
34 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove);
35
36 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
37
38 ✗ drawgrid();
39
40 ✗ ImGui::PushItemWidth(120.0f);
41 ✗ draw_list->ChannelsSplit(3);
42 ✗ for (Forest& forest: forests) {
43 ✗ for (Tree& tree: forest.trees) {
44 ✗ current_tree = &tree;
45
46 ✗ for (Node& node: tree.nodes) {
47 ✗ draw(&node, offset);
48 }
49
50 ✗ draw_list->ChannelsSetCurrent(0);
51 ✗ for (Link& link: tree.links) {
52 ✗ draw(&link, offset);
53 }
54
55 ✗ current_tree = nullptr;
56 }
57 }
58 ✗ draw_list->ChannelsMerge();
59
60 ✗ if (ImGui::IsWindowHovered() && !ImGui::IsAnyItemActive() &&
61 ✗ ImGui::IsMouseDragging(ImGuiMouseButton_Middle, 0.0f))
62 ✗ scrolling = scrolling + io.MouseDelta;
63
64 ✗ handle_events(offset);
65
66 ✗ ImGui::PopItemWidth();
67 ✗ ImGui::EndChild();
68 ✗ ImGui::EndGroup();
69 ✗ ImGui::End();
70
71 ✗ ImGui::PopStyleVar(1);
72 ✗ }
73
74 void GraphEditor::handle_events(ImVec2 offset) {
75 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
76
77 // REMOVE LINK
78 ✗ if (ImGui::IsKeyDown(ImGuiKey_LeftAlt) && ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
79 ✗ if (hovered_link != nullptr) {
80 ✗ auto it = std::remove_if(std::begin(selected_tree->links),
81 ✗ std::end(selected_tree->links),
82 [=](Link const& link) {
83 ✗ Link const* l = &link;
84 ✗ return l == hovered_link;
85 });
86
87 ✗ it->from->connected = false;
88 ✗ it->to->connected = false;
89
90 ✗ selected_tree->links.erase(it);
91
92 ✗ hovered_link = nullptr;
93 ✗ selected_tree = nullptr;
94 }
95
96 ✗ return;
97 }
98
99 // CREATE LINK
100 ✗ if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
101 ✗ if (selected_pin != nullptr && hovered_pin != nullptr) {
102 ✗ selected_tree->links.emplace_back(selected_pin, hovered_pin);
103 }
104 ✗ printf("Selected: %d, hovered %d, selected_tree %d\n",
105 ✗ selected_pin != nullptr,
106 ✗ hovered_pin != nullptr,
107 ✗ selected_tree != nullptr);
108 ✗ selected_pin = nullptr;
109 ✗ hovered_pin = nullptr;
110 ✗ selected_tree = nullptr;
111
112 ✗ selected_node = nullptr;
113 ✗ rectangle_select = false;
114 ✗ rectangle_selection = ImRect();
115 ✗ return;
116 }
117
118 // PENDING LINK
119 ✗ if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
120 ✗ if (selected_pin != nullptr) {
121 ✗ auto color = _colors[selected_pin->type];
122 ✗ draw_bezier(draw_list,
123 ✗ with_scroll(selected_pin->pos),
124 ImGui::GetMousePos(),
125 color,
126 bezier_segments,
127 tickness);
128 ✗ return;
129 }
130
131 ✗ if (selected_node == nullptr) {
132 ✗ if (!rectangle_select) {
133 ✗ rectangle_select = true;
134 ✗ for (auto& item: selected) {
135 ✗ item.first->selected = false;
136 }
137 ✗ selected.clear();
138 ✗ rectangle_start = ImGui::GetMousePos();
139 }
140
141 // ImVec2 mp = ImGui::GetMousePos();
142 // ImVec2 mn = ImVec2(std::min(rectangle_start.x, mp.x), std::min(rectangle_start.y,
143 // mp.y)); ImVec2 mx = ImVec2(std::max(rectangle_start.x, mp.x),
144 // std::max(rectangle_start.y, mp.y)); rectangle_selection = ImRect(mn, mx);
145
146 ✗ rectangle_selection = ImRect(rectangle_start, rectangle_start);
147 ✗ rectangle_selection.Add(ImGui::GetMousePos());
148
149 ✗ draw_list->AddRectFilled(
150 ✗ rectangle_selection.GetTR(), rectangle_selection.GetBL(), rectangle_color);
151 ✗ return;
152 }
153 }
154 }
155
156 void GraphEditor::drawgrid() {
157 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
158
159 // Display grid
160 ✗ if (show_grid) {
161 ✗ ImU32 GRID_COLOR = IM_COL32(200, 200, 200, 40);
162 ✗ float GRID_SZ = 64.0f;
163 ✗ ImVec2 win_pos = ImGui::GetCursorScreenPos();
164 ✗ ImVec2 canvas_sz = ImGui::GetWindowSize();
165
166 ✗ for (float x = fmodf(scrolling.x, GRID_SZ); x < canvas_sz.x; x += GRID_SZ)
167 ✗ draw_list->AddLine(
168 ✗ ImVec2(x, 0.0f) + win_pos, ImVec2(x, canvas_sz.y) + win_pos, GRID_COLOR);
169
170 ✗ for (float y = fmodf(scrolling.y, GRID_SZ); y < canvas_sz.y; y += GRID_SZ)
171 ✗ draw_list->AddLine(
172 ✗ ImVec2(0.0f, y) + win_pos, ImVec2(canvas_sz.x, y) + win_pos, GRID_COLOR);
173 }
174 ✗ }
175
176 uint64_t nextid() {
177 static uint64_t counter = 0;
178 ✗ return counter++;
179 }
180
181 bool draw_bezier(ImDrawList* draw_list,
182 ImVec2 p1,
183 ImVec2 p2,
184 ImU32 color,
185 int segments,
186 float tickness,
187 float eps) {
188 ✗ if (p1.x > p2.x) {
189 ✗ std::swap(p1, p2);
190 }
191
192 auto sgn = [](float v) -> int { return int(v > 0); };
193
194 ✗ auto offset = ImVec2((p2.x - p1.x + 1) / 2.f, 0);
195 ✗ auto y_offset = sgn(p2.y - p1.y) * (p2.y - p1.y) / 2.f;
196
197 ✗ if (p2.y - p1.y > p2.x - p1.x) {
198 ✗ offset = ImVec2(0, y_offset);
199 }
200
201 ✗ ImVec2 P0 = p1;
202 ✗ ImVec2 P1 = p1 + offset;
203 ✗ ImVec2 P2 = p2 - offset;
204 ✗ ImVec2 P3 = p2;
205
206 const ImProjectResult result =
207 ✗ ImProjectOnCubicBezier(ImGui::GetMousePos(), P0, P1, P2, P3, segments);
208
209 ✗ bool hovered = result.Distance <= tickness + eps;
210
211 ✗ draw_list->AddBezierCubic(P0, P1, P2, P3, color, tickness + float(hovered), segments);
212
213 ✗ return hovered;
214 }
215
216 void GraphEditor::draw(Link* link, ImVec2 offset) {
217 ✗ auto color = _colors[link->from->type];
218
219 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
220 ✗ bool hovered = draw_bezier(draw_list,
221 ✗ with_scroll(link->from->pos),
222 ✗ with_scroll(link->to->pos),
223 color,
224 bezier_segments,
225 tickness);
226
227 ✗ if (hovered) {
228 ✗ hovered_link = link;
229 ✗ selected_tree = current_tree;
230 }
231 ✗ }
232
233 ImVec2 GraphEditor::estimate_size(Node& node, ImVec2 font_size) {
234 ✗ ImRect size;
235 ✗ float height = 0;
236 ✗ for (int i = 0; i < node.inputs.size(); i++) {
237 ✗ if (node.inputs[i].name.empty())
238 ✗ continue;
239
240 ✗ ImVec2 v = font_size * ImVec2(node.inputs[i].name.size(), 1);
241
242 ✗ height += v.y;
243 ✗ size.Add(v + ImVec2(pin_radius * 4, height));
244 }
245
246 ✗ ImVec2 v = font_size * ImVec2(node.name.size(), 1);
247 ✗ size.Add(v + ImVec2(pin_radius * 4, height));
248
249 ✗ ImVec2 col1 = size.GetSize();
250
251 ✗ height = 0;
252 ✗ for (int i = 0; i < node.outputs.size(); i++) {
253 ✗ if (node.outputs[i].name.empty())
254 ✗ continue;
255
256 ✗ ImVec2 v = font_size * ImVec2(node.outputs[i].name.size(), 1);
257 ✗ height += v.y;
258 ✗ size.Add(v + ImVec2(col1.x, height) + ImVec2(pin_radius * 2, 0));
259 }
260
261 ✗ return size.GetSize();
262 }
263
264 void GraphEditor::draw(Node* node, ImVec2 offset) {
265 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
266 ✗ ImGuiIO& io = ImGui::GetIO();
267
268 ✗ ImGui::PushID(int(node->id));
269
270 ✗ ImVec2 node_rect_min = offset + node->pos; // Parent offset
271 static float value;
272 ✗ bool old_any_active = ImGui::IsAnyItemActive();
273
274 // Content
275 ✗ draw_list->ChannelsSetCurrent(2);
276 // ImGui::SetCursorScreenPos(node_rect_min + ImVec2(1, 1) * 0* node_padding);
277
278
279 ✗ ImGui::BeginGroup();
280
281 // Argument Column
282 ✗ ImGui::BeginGroup();
283 ✗ ImVec2 txt = ImGui::CalcTextSize("T");
284 ✗ float line_height = node->layout.input.y;
285 ✗ float line_width = node->layout.input.x;
286 ✗ ImVec2 start = node->pos + ImVec2(1, 1) * pin_radius + ImVec2(1, 1) * node_padding;
287 ✗ float new_width = pin_radius / 2;
288 ✗ float new_height = std::max(pin_radius * 2, txt.y);
289
290 ✗ ImGui::SetCursorPos(start);
291 ✗ _size = ImRect();
292
293 // Inputs
294 ✗ for (int slot_idx = 0; slot_idx < node->inputs.size(); slot_idx++) {
295 ✗ Pin& pin = node->inputs[slot_idx];
296
297 ✗ pin.pos = start;
298
299 ✗ const char* name = pin.name.c_str();
300 ✗ if (pin.kind == PinKind::Flow) {
301 ✗ name = node->name.c_str();
302 }
303 ✗ ImVec2 v = ImGui::CalcTextSize(name);
304
305 ✗ draw(&pin, with_scroll(pin.pos));
306 ✗ ImGui::SetCursorPos(
307 ✗ with_scroll(pin.pos + ImVec2(pin_radius / 2 + pin_label_margin, -(pin_radius + v.y / 4))));
308
309 ✗ ImGui::Text("%s", name);
310 ✗ ImVec2 s(0, 0);
311 ✗ if (pin.type == PinType::Float && !pin.connected) {
312 ✗ ImGui::SameLine();
313 ✗ ImGui::PushItemWidth(txt.x * 5);
314 ✗ ImGui::InputFloat("", pin.as_float(), 0, 0, "%.f");
315 ✗ ImGui::PopItemWidth();
316
317 ✗ s = ImGui::GetItemRectSize();
318 }
319
320 ✗ new_height = std::max(new_height, v.y);
321 ✗ new_width = std::max(new_width, v.x + s.x);
322 ✗ start.y += line_height;
323 }
324 ✗ node->layout.input.y = new_height;
325 ✗ node->layout.input.x = new_width + pin_radius;
326
327 ✗ ImGui::EndGroup();
328 ✗ ImVec2 s = ImGui::GetItemRectSize();
329
330 //
331 ✗ if (node->exec_in() == nullptr) {
332 ✗ const char* name = node->name.c_str();
333
334 ✗ ImVec2 sz = ImGui::CalcTextSize(name) + ImVec2(txt.x, 0);
335 // ImVec2 sz(txt.x * (node->name.size() + 2), txt.y);
336
337 // ImVec2 center = ImVec2(
338 // node->layout.input.x,
339 // (node->size.y - sz.y )/ 2.f
340 // );
341
342 ✗ ImVec2 center = node->size / 2 + ImVec2(-sz.x, -sz.y - node_padding);
343 ✗ ImGui::SetCursorPos(with_scroll(node->pos + center));
344 ✗ ImGui::Text("%s", name);
345
346 ✗ node->layout.input.x = new_width + sz.x + 2 * pin_radius;
347 }
348
349 // Ouputs
350 ✗ ImGui::SameLine();
351 ✗ ImGui::BeginGroup();
352 // Output Column
353 // ImGui::SetCursorScreenPos(node_rect_min + ImVec2(node->layout.input.x, 0));
354
355 ✗ ImGui::SetCursorPos(node->pos + ImVec2(1, 0) * pin_radius + ImVec2(node->layout.input.x, 0) +
356 ✗ ImVec2(0, 1) * node_padding);
357 ✗ start = ImGui::GetCursorPos();
358
359 ✗ line_width = node->layout.output.x;
360 ✗ new_width = pin_radius;
361 ✗ new_height = pin_radius * 2;
362
363 ✗ for (int slot_idx = 0; slot_idx < node->outputs.size(); slot_idx++) {
364 ✗ Pin& pin = node->outputs[slot_idx];
365 ✗ pin.pos = start + ImVec2(pin_radius / 2, 0);
366 ✗ const char* name = pin.name.c_str();
367
368 ✗ ImVec2 v = ImGui::CalcTextSize(name);
369 ✗ if (pin.name.empty())
370 ✗ v.y = 0;
371
372 ✗ new_width = std::max(new_width, v.x + pin_radius);
373 ✗ new_height = std::max(new_height, v.y);
374 ✗ float s = line_width - v.x - pin_radius / 2;
375
376 ✗ ImGui::SetCursorPos(with_scroll(start + ImVec2(s, -v.y / 4)));
377 ✗ ImGui::Text("%s", name);
378
379 ✗ pin.pos = start + ImVec2(line_width + pin_label_margin, 0) + ImVec2(1, 1) * pin_radius +
380 ✗ ImVec2(0, 0);
381 ✗ draw(&pin, with_scroll(pin.pos));
382
383 ✗ start.y += line_height;
384 }
385 ✗ node->layout.output.x = new_width + pin_label_margin;
386 ✗ node->layout.output.y = new_height;
387 ✗ ImGui::EndGroup();
388 ✗ ImGui::EndGroup();
389 // ---
390
391 ✗ node->size = (_size.GetSize() - node->pos) + ImVec2(1, 1) * node_padding;
392 ✗ ImVec2 node_rect_max = node_rect_min + node->size;
393
394 // Background
395 ✗ draw_list->ChannelsSetCurrent(1);
396 // ImGui::SetCursorScreenPos(node_rect_min);
397
398 ✗ ImGui::SetCursorPos(with_scroll(node->pos));
399 ✗ ImGui::InvisibleButton("node", node->size);
400 /// ---
401
402 // Events
403 ✗ if (ImGui::IsItemHovered()) {
404 ✗ hovered_node = node;
405 ✗ open_context_menu |= ImGui::IsMouseClicked(1);
406 }
407
408 ✗ bool node_widgets_active = (!old_any_active && ImGui::IsAnyItemActive());
409 ✗ bool node_moving_active = ImGui::IsItemActive();
410 ✗ if (node_widgets_active || node_moving_active)
411 ✗ selected_node = node;
412
413 ✗ if (node_moving_active && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
414
415 ✗ if (!has_selection()) {
416 ✗ node->pos = node->pos + io.MouseDelta;
417 } else {
418 ✗ for (auto& item: selected) {
419 ✗ item.first->pos = item.first->pos + io.MouseDelta;
420 }
421 }
422 }
423
424 // ----
425
426 ✗ draw_list->AddRectFilled(node_rect_min, node_rect_max, node_bg_color, 4.0f);
427 ✗ draw_list->AddRect(node_rect_min,
428 node_rect_max,
429 ✗ node->selected ? node_selected_color : node_outline_color,
430 4.0f,
431 0,
432 ✗ node->selected ? 4.0f : 1.0f);
433 ✗ check_selected(node);
434
435 ✗ ImGui::PopID();
436 ✗ }
437
438 void GraphEditor::draw(Pin* pin, ImVec2 center) {
439 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
440 ✗ draw_list->ChannelsSetCurrent(2);
441
442 ✗ ImGui::PushID(int(pin->id));
443
444 ✗ ImVec2 radius = ImVec2(1, 1) * pin_radius;
445 ✗ bool hovered = false;
446 ✗ bool held = false;
447 ✗ auto flags = ImGuiButtonFlags_NoHoldingActiveId;
448
449 ✗ ImVec2 pos = center - radius - get_offset();
450 ✗ ImVec2 size = radius * 2;
451 ✗ ImRect bb(pos, pos + size);
452
453 ✗ ImGui::SetCursorPos(with_scroll(pos));
454 ✗ ImGui::ItemSize(size, 10);
455 ✗ ImGui::ItemAdd(bb, int(pin->id));
456 ✗ ImGui::ButtonBehavior( //
457 ✗ ImRect(center - radius, center + radius), //
458 ✗ int(pin->id), //
459 &hovered, //
460 &held, //
461 flags //
462 );
463
464 // ButtonBehavior hovered does not work well
465 ✗ hovered = bb.Contains(ImGui::GetMousePos());
466
467 // ImGui::SetCursorPos(with_scroll(pos));
468 // ImGui::Button("", size);
469
470 ✗ _size.Add(pos);
471 ✗ _size.Add(pos + size);
472
473 ✗ if (held) {
474 ✗ selected_pin = pin;
475 ✗ selected_tree = current_tree;
476 }
477
478 ✗ if (hovered) {
479 ✗ hovered_pin = pin;
480 }
481
482 ✗ ImU32 color = _colors[pin->type];
483
484 auto style = PinStyle{
485 //
486 ✗ pin->kind, //
487 ✗ pin->connected, //
488 color, //
489 color //
490 ✗ };
491
492 ✗ draw(style, center - radius, radius * 2.0);
493 ✗ ImGui::PopID();
494 ✗ }
495
496 void GraphEditor::draw(PinStyle& style, ImVec2 pos, ImVec2 size) {
497
498 ✗ switch (style.kind) {
499 ✗ case PinKind::Flow: return draw_flow(style, pos, size);
500 ✗ case PinKind::Circle: return draw_circle(style, pos, size);
501 ✗ case PinKind::Square: return draw_square(style, pos, size);
502 ✗ case PinKind::Grid: return draw_grid(style, pos, size);
503 ✗ case PinKind::RoundSquare: return draw_round_square(style, pos, size);
504 ✗ case PinKind::Diamond: return draw_diamond(style, pos, size);
505 }
506
507 ✗ return draw_circle(style, pos, size);
508 }
509
510 void GraphEditor::draw_flow(PinStyle& style, ImVec2 pos, ImVec2 size) {
511 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
512
513 ✗ auto rect = ImRect(pos, pos + size);
514 ✗ auto rect_x = rect.Min.x;
515 ✗ auto rect_y = rect.Min.y;
516 ✗ auto rect_w = rect.Max.x - rect.Min.x;
517 ✗ auto rect_h = rect.Max.y - rect.Min.y;
518 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
519 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
520 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
521 ✗ const auto outline_scale = rect_w / 24.0f;
522 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
523
524 ✗ const auto origin_scale = rect_w / 24.0f;
525 ✗ const auto offset_x = 1.0f * origin_scale;
526 ✗ const auto offset_y = 0.0f * origin_scale;
527 ✗ const auto margin = (style.filled ? 2.0f : 2.0f) * origin_scale;
528 ✗ const auto rounding = 0.1f * origin_scale;
529 ✗ const auto tip_round = 0.7f; // percentage of triangle edge (for tip)
530 // const auto edge_round = 0.7f; // percentage of triangle edge (for corner)
531 ✗ const auto canvas = ImRect(rect.Min.x + margin + offset_x,
532 ✗ rect.Min.y + margin + offset_y,
533 ✗ rect.Max.x - margin + offset_x,
534 ✗ rect.Max.y - margin + offset_y);
535 ✗ const auto canvas_x = canvas.Min.x;
536 ✗ const auto canvas_y = canvas.Min.y;
537 ✗ const auto canvas_w = canvas.Max.x - canvas.Min.x;
538 ✗ const auto canvas_h = canvas.Max.y - canvas.Min.y;
539
540 ✗ const auto left = canvas_x + canvas_w * 0.5f * 0.3f;
541 ✗ const auto right = canvas_x + canvas_w - canvas_w * 0.5f * 0.3f;
542 ✗ const auto top = canvas_y + canvas_h * 0.5f * 0.2f;
543 ✗ const auto bottom = canvas_y + canvas_h - canvas_h * 0.5f * 0.2f;
544 ✗ const auto center_y = (top + bottom) * 0.5f;
545 // const auto angle = AX_PI * 0.5f * 0.5f * 0.5f;
546
547 ✗ const auto tip_top = ImVec2(canvas_x + canvas_w * 0.5f, top);
548 ✗ const auto tip_right = ImVec2(right, center_y);
549 ✗ const auto tip_bottom = ImVec2(canvas_x + canvas_w * 0.5f, bottom);
550
551 ✗ draw_list->PathLineTo(ImVec2(left, top) + ImVec2(0, rounding));
552 ✗ draw_list->PathBezierCubicCurveTo(
553 ✗ ImVec2(left, top), ImVec2(left, top), ImVec2(left, top) + ImVec2(rounding, 0));
554 ✗ draw_list->PathLineTo(tip_top);
555 ✗ draw_list->PathLineTo(tip_top + (tip_right - tip_top) * tip_round);
556 ✗ draw_list->PathBezierCubicCurveTo(
557 ✗ tip_right, tip_right, tip_bottom + (tip_right - tip_bottom) * tip_round);
558 ✗ draw_list->PathLineTo(tip_bottom);
559 ✗ draw_list->PathLineTo(ImVec2(left, bottom) + ImVec2(rounding, 0));
560 ✗ draw_list->PathBezierCubicCurveTo(
561 ✗ ImVec2(left, bottom), ImVec2(left, bottom), ImVec2(left, bottom) - ImVec2(0, rounding));
562
563 ✗ if (style.filled) {
564 ✗ if (style.fill & 0xFF000000) {
565 ✗ draw_list->AddConvexPolyFilled(
566 ✗ draw_list->_Path.Data, draw_list->_Path.Size, style.fill);
567 }
568 }
569 ✗ draw_list->PathStroke(style.color, true, 2.0f * outline_scale);
570 ✗ }
571
572 void GraphEditor::draw_circle(PinStyle& style, ImVec2 pos, ImVec2 size) {
573 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
574
575 ✗ auto rect = ImRect(pos, pos + size);
576 ✗ auto rect_x = rect.Min.x;
577 ✗ auto rect_y = rect.Min.y;
578 ✗ auto rect_w = rect.Max.x - rect.Min.x;
579 ✗ auto rect_h = rect.Max.y - rect.Min.y;
580 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
581 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
582 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
583 ✗ const auto outline_scale = rect_w / 24.0f;
584 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
585
586 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
587 ✗ auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
588 ✗ rect.Min.x += rect_offset;
589 ✗ rect.Max.x += rect_offset;
590 ✗ rect_x += rect_offset;
591 ✗ rect_center_x += rect_offset * 0.5f;
592 ✗ rect_center.x += rect_offset * 0.5f;
593
594 ✗ const auto c = rect_center;
595 ✗ if (style.filled) {
596 ✗ const auto r = 0.5f * rect_w / 2.0f - 0.5f;
597
598 ✗ if (style.fill & 0xFF000000) {
599 ✗ draw_list->AddCircleFilled(c, r, style.fill, 12 + extra_segments);
600 }
601
602 ✗ draw_list->AddCircle(c, r, style.color, 12 + extra_segments, 2.0f * outline_scale);
603 } else {
604 ✗ draw_list->AddCircle(c, 0.5f * rect_w / 2.0f, style.color, 12 + extra_segments);
605 }
606
607 ✗ const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
608
609 ✗ draw_list->AddTriangleFilled(ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
610 ✗ ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
611 ✗ ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
612 style.color);
613 ✗ }
614
615 void GraphEditor::draw_square(PinStyle& style, ImVec2 pos, ImVec2 size) {
616
617 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
618
619 ✗ auto rect = ImRect(pos, pos + size);
620 ✗ auto rect_x = rect.Min.x;
621 ✗ auto rect_y = rect.Min.y;
622 ✗ auto rect_w = rect.Max.x - rect.Min.x;
623 ✗ auto rect_h = rect.Max.y - rect.Min.y;
624 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
625 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
626 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
627 ✗ const auto outline_scale = rect_w / 24.0f;
628 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
629
630 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
631 ✗ auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
632 ✗ rect.Min.x += rect_offset;
633 ✗ rect.Max.x += rect_offset;
634 ✗ rect_x += rect_offset;
635 ✗ rect_center_x += rect_offset * 0.5f;
636 ✗ rect_center.x += rect_offset * 0.5f;
637
638 ✗ if (style.filled) {
639 ✗ const auto r = 0.5f * rect_w / 2.0f;
640 ✗ const auto p0 = rect_center - ImVec2(r, r);
641 ✗ const auto p1 = rect_center + ImVec2(r, r);
642
643 ✗ draw_list->AddRectFilled(p0, p1, style.color, 0, ImDrawFlags_RoundCornersAll);
644 } else {
645 ✗ const auto r = 0.5f * rect_w / 2.0f - 0.5f;
646 ✗ const auto p0 = rect_center - ImVec2(r, r);
647 ✗ const auto p1 = rect_center + ImVec2(r, r);
648
649 ✗ if (style.fill & 0xFF000000) {
650 ✗ draw_list->AddRectFilled(p0, p1, style.fill, 0, ImDrawFlags_RoundCornersAll);
651 }
652
653 ✗ draw_list->AddRect(
654 p0, p1, style.color, 0, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
655 }
656 ✗ }
657
658 void GraphEditor::draw_grid(PinStyle& style, ImVec2 pos, ImVec2 size) {
659 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
660
661 ✗ auto rect = ImRect(pos, pos + size);
662 ✗ auto rect_x = rect.Min.x;
663 ✗ auto rect_y = rect.Min.y;
664 ✗ auto rect_w = rect.Max.x - rect.Min.x;
665 ✗ auto rect_h = rect.Max.y - rect.Min.y;
666 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
667 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
668 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
669 ✗ const auto outline_scale = rect_w / 24.0f;
670 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
671
672 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
673
674 ✗ auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
675
676 ✗ rect.Min.x += rect_offset;
677 ✗ rect.Max.x += rect_offset;
678 ✗ rect_x += rect_offset;
679 ✗ rect_center_x += rect_offset * 0.5f;
680 ✗ rect_center.x += rect_offset * 0.5f;
681
682 ✗ const auto r = 0.5f * rect_w / 2.0f;
683 ✗ const auto w = ceilf(r / 3.0f);
684
685 ✗ const auto baseTl = ImVec2(floorf(rect_center_x - w * 2.5f), floorf(rect_center_y - w * 2.5f));
686 ✗ const auto baseBr = ImVec2(floorf(baseTl.x + w), floorf(baseTl.y + w));
687
688 ✗ auto tl = baseTl;
689 ✗ auto br = baseBr;
690 ✗ for (int i = 0; i < 3; ++i) {
691 ✗ tl.x = baseTl.x;
692 ✗ br.x = baseBr.x;
693 ✗ draw_list->AddRectFilled(tl, br, style.color);
694 ✗ tl.x += w * 2;
695 ✗ br.x += w * 2;
696
697 ✗ if (i != 1 || style.filled)
698 ✗ draw_list->AddRectFilled(tl, br, style.color);
699
700 ✗ tl.x += w * 2;
701 ✗ br.x += w * 2;
702 ✗ draw_list->AddRectFilled(tl, br, style.color);
703
704 ✗ tl.y += w * 2;
705 ✗ br.y += w * 2;
706 }
707
708 // ---
709 ✗ triangleStart = br.x + w + 1.0f / 24.0f * rect_w;
710 ✗ const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
711 ✗ draw_list->AddTriangleFilled(ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
712 ✗ ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
713 ✗ ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
714 style.color);
715 ✗ }
716
717 void GraphEditor::draw_round_square(PinStyle& style, ImVec2 pos, ImVec2 size) {
718 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
719
720 ✗ auto rect = ImRect(pos, pos + size);
721 ✗ auto rect_x = rect.Min.x;
722 ✗ auto rect_y = rect.Min.y;
723 ✗ auto rect_w = rect.Max.x - rect.Min.x;
724 ✗ auto rect_h = rect.Max.y - rect.Min.y;
725 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
726 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
727 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
728 ✗ const auto outline_scale = rect_w / 24.0f;
729 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
730
731 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
732 ✗ auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
733 ✗ rect.Min.x += rect_offset;
734 ✗ rect.Max.x += rect_offset;
735 ✗ rect_x += rect_offset;
736 ✗ rect_center_x += rect_offset * 0.5f;
737 ✗ rect_center.x += rect_offset * 0.5f;
738
739 ✗ if (style.filled) {
740 ✗ const auto r = 0.5f * rect_w / 2.0f;
741 ✗ const auto cr = r * 0.5f;
742 ✗ const auto p0 = rect_center - ImVec2(r, r);
743 ✗ const auto p1 = rect_center + ImVec2(r, r);
744
745 ✗ draw_list->AddRectFilled(p0, p1, style.color, cr, ImDrawFlags_RoundCornersAll);
746 } else {
747 ✗ const auto r = 0.5f * rect_w / 2.0f - 0.5f;
748 ✗ const auto cr = r * 0.5f;
749 ✗ const auto p0 = rect_center - ImVec2(r, r);
750 ✗ const auto p1 = rect_center + ImVec2(r, r);
751
752 ✗ if (style.fill & 0xFF000000) {
753 ✗ draw_list->AddRectFilled(p0, p1, style.fill, cr, ImDrawFlags_RoundCornersAll);
754 }
755
756 ✗ draw_list->AddRect(
757 p0, p1, style.color, cr, ImDrawFlags_RoundCornersAll, 2.0f * outline_scale);
758 }
759 ✗ }
760 void GraphEditor::draw_diamond(PinStyle& style, ImVec2 pos, ImVec2 size) {
761 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
762
763 ✗ auto rect = ImRect(pos, pos + size);
764 ✗ auto rect_x = rect.Min.x;
765 ✗ auto rect_y = rect.Min.y;
766 ✗ auto rect_w = rect.Max.x - rect.Min.x;
767 ✗ auto rect_h = rect.Max.y - rect.Min.y;
768 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
769 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
770 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
771 ✗ const auto outline_scale = rect_w / 24.0f;
772 ✗ const auto extra_segments = static_cast<int>(2 * outline_scale); // for full circle
773
774 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
775 ✗ auto rect_offset = -static_cast<int>(rect_w * 0.25f * 0.25f);
776 ✗ rect.Min.x += rect_offset;
777 ✗ rect.Max.x += rect_offset;
778 ✗ rect_x += rect_offset;
779 ✗ rect_center_x += rect_offset * 0.5f;
780 ✗ rect_center.x += rect_offset * 0.5f;
781
782 ✗ if (style.filled) {
783 ✗ const auto r = 0.607f * rect_w / 2.0f;
784 ✗ const auto c = rect_center;
785
786 ✗ draw_list->PathLineTo(c + ImVec2(0, -r));
787 ✗ draw_list->PathLineTo(c + ImVec2(r, 0));
788 ✗ draw_list->PathLineTo(c + ImVec2(0, r));
789 ✗ draw_list->PathLineTo(c + ImVec2(-r, 0));
790 ✗ draw_list->PathFillConvex(style.color);
791 } else {
792 ✗ const auto r = 0.607f * rect_w / 2.0f - 0.5f;
793 ✗ const auto c = rect_center;
794
795 ✗ draw_list->PathLineTo(c + ImVec2(0, -r));
796 ✗ draw_list->PathLineTo(c + ImVec2(r, 0));
797 ✗ draw_list->PathLineTo(c + ImVec2(0, r));
798 ✗ draw_list->PathLineTo(c + ImVec2(-r, 0));
799
800 ✗ if (style.fill & 0xFF000000)
801 ✗ draw_list->AddConvexPolyFilled(
802 ✗ draw_list->_Path.Data, draw_list->_Path.Size, style.fill);
803
804 ✗ draw_list->PathStroke(style.color, true, 2.0f * outline_scale);
805 }
806 ✗ }
807
808 void GraphEditor::draw_triangle(PinStyle& style, ImVec2 pos, ImVec2 size) {
809 ✗ ImDrawList* draw_list = ImGui::GetWindowDrawList();
810
811 ✗ auto rect = ImRect(pos, pos + size);
812 ✗ auto rect_x = rect.Min.x;
813 ✗ auto rect_y = rect.Min.y;
814 ✗ auto rect_w = rect.Max.x - rect.Min.x;
815 ✗ auto rect_h = rect.Max.y - rect.Min.y;
816 ✗ auto rect_center_x = (rect.Min.x + rect.Max.x) * 0.5f;
817 ✗ auto rect_center_y = (rect.Min.y + rect.Max.y) * 0.5f;
818 ✗ auto rect_center = ImVec2(rect_center_x, rect_center_y);
819 ✗ auto triangleStart = rect_center_x + 0.32f * rect_w;
820 ✗ const auto triangleTip = triangleStart + rect_w * (0.45f - 0.32f);
821
822 ✗ draw_list->AddTriangleFilled(ImVec2(ceilf(triangleTip), rect_y + rect_h * 0.5f),
823 ✗ ImVec2(triangleStart, rect_center_y + 0.15f * rect_h),
824 ✗ ImVec2(triangleStart, rect_center_y - 0.15f * rect_h),
825 style.color);
826 ✗ }
827