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