Blender V5.0
node_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <iomanip>
11
12#include "BKE_idprop.hh"
13#include "MEM_guardedalloc.h"
14
15#include "DNA_light_types.h"
16#include "DNA_linestyle_types.h"
17#include "DNA_material_types.h"
18#include "DNA_modifier_types.h"
19#include "DNA_node_types.h"
20#include "DNA_screen_types.h"
21#include "DNA_space_types.h"
22#include "DNA_text_types.h"
23#include "DNA_world_types.h"
24
25#include "BLI_array.hh"
26#include "BLI_bounds.hh"
27#include "BLI_convexhull_2d.hh"
28#include "BLI_function_ref.hh"
29#include "BLI_listbase.h"
30#include "BLI_map.hh"
31#include "BLI_math_color.h"
32#include "BLI_set.hh"
33#include "BLI_span.hh"
34#include "BLI_string.h"
35#include "BLI_string_ref.hh"
36#include "BLI_string_utf8.h"
37#include "BLI_vector.hh"
38
39#include "BLT_translation.hh"
40
43#include "BKE_context.hh"
44#include "BKE_curves.hh"
45#include "BKE_global.hh"
46#include "BKE_idtype.hh"
47#include "BKE_lib_id.hh"
48#include "BKE_library.hh"
49#include "BKE_main.hh"
51#include "BKE_node.hh"
53#include "BKE_node_runtime.hh"
56#include "BKE_object.hh"
57#include "BKE_scene.hh"
58#include "BKE_scene_runtime.hh"
59#include "BKE_screen.hh"
60
61#include "IMB_imbuf.hh"
62
63#include "DEG_depsgraph.hh"
64
65#include "BLF_api.hh"
66
67#include "BIF_glutil.hh"
68
69#include "GPU_framebuffer.hh"
70#include "GPU_immediate.hh"
71#include "GPU_immediate_util.hh"
72#include "GPU_matrix.hh"
73#include "GPU_state.hh"
74#include "GPU_viewport.hh"
75
76#include "WM_api.hh"
77#include "WM_types.hh"
78
79#include "ED_gpencil_legacy.hh"
80#include "ED_node.hh"
81#include "ED_node_preview.hh"
82#include "ED_screen.hh"
83#include "ED_space_api.hh"
84#include "ED_viewer_path.hh"
85
86#include "UI_interface.hh"
88#include "UI_resources.hh"
89#include "UI_view2d.hh"
90
91#include "RNA_access.hh"
92#include "RNA_prototypes.hh"
93
98#include "NOD_sync_sockets.hh"
99#include "NOD_trace_values.hh"
100
101#include "GEO_fillet_curves.hh"
102
103#include "node_intern.hh" /* own include */
104
105#include <fmt/format.h>
106#include <sstream>
107
113
114namespace blender::ed::space_node {
115
116#define NODE_ZONE_PADDING UI_UNIT_X
117#define ZONE_ZONE_PADDING 0.3f * UI_UNIT_X
118#define EXTRA_INFO_ROW_HEIGHT (20.0f * UI_SCALE_FAC)
119
180
182{
183 return NODE_GRID_STEP_SIZE;
184}
185
187{
188 SpaceNode *snode = CTX_wm_space_node(C);
189 if (snode) {
191
192 if (snode->nodetree) {
193 id_us_ensure_real(&snode->nodetree->id);
194 }
195 }
196}
197
198/* id is supposed to contain a node tree */
200{
201 if (id) {
202 if (GS(id->name) == ID_NT) {
203 return (bNodeTree *)id;
204 }
205 return bke::node_tree_from_id(id);
206 }
207
208 return nullptr;
209}
210
212{
213 bNodeTree *ntree = node_tree_from_ID(id);
214 if (id == nullptr || ntree == nullptr) {
215 return;
216 }
217
218 /* TODO(sergey): With the new dependency graph it should be just enough to only tag ntree itself.
219 * All the users of this tree will have update flushed from the tree. */
220 DEG_id_tag_update(&ntree->id, 0);
221
222 if (ntree->type == NTREE_SHADER) {
223 DEG_id_tag_update(id, 0);
224
225 if (GS(id->name) == ID_MA) {
227 }
228 else if (GS(id->name) == ID_LA) {
230 }
231 else if (GS(id->name) == ID_WO) {
233 }
234 }
235 else if (ntree->type == NTREE_COMPOSIT) {
237 }
238 else if (ntree->type == NTREE_TEXTURE) {
239 DEG_id_tag_update(id, 0);
241 }
242 else if (ntree->type == NTREE_GEOMETRY) {
244 }
245 else if (id == &ntree->id) {
246 /* Node groups. */
247 DEG_id_tag_update(id, 0);
248 }
249}
250
251static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout);
252
254static bool compare_node_depth(const bNode *a, const bNode *b)
255{
256 /* These tell if either the node or any of the parent nodes is selected.
257 * A selected parent means an unselected node is also in foreground! */
258 bool a_select = (a->flag & NODE_SELECT) != 0, b_select = (b->flag & NODE_SELECT) != 0;
259 bool a_active = (a->flag & NODE_ACTIVE) != 0, b_active = (b->flag & NODE_ACTIVE) != 0;
260
261 /* If one is an ancestor of the other. */
262 /* XXX there might be a better sorting algorithm for stable topological sort,
263 * this is O(n^2) worst case. */
264 for (bNode *parent = a->parent; parent; parent = parent->parent) {
265 /* If B is an ancestor, it is always behind A. */
266 if (parent == b) {
267 return false;
268 }
269 /* Any selected ancestor moves the node forward. */
270 if (parent->flag & NODE_ACTIVE) {
271 a_active = true;
272 }
273 if (parent->flag & NODE_SELECT) {
274 a_select = true;
275 }
276 }
277 for (bNode *parent = b->parent; parent; parent = parent->parent) {
278 /* If A is an ancestor, it is always behind B. */
279 if (parent == a) {
280 return true;
281 }
282 /* Any selected ancestor moves the node forward. */
283 if (parent->flag & NODE_ACTIVE) {
284 b_active = true;
285 }
286 if (parent->flag & NODE_SELECT) {
287 b_select = true;
288 }
289 }
290
291 /* One of the nodes is in the background and the other not. */
292 if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND)) {
293 return true;
294 }
295 if ((b->flag & NODE_BACKGROUND) && !(a->flag & NODE_BACKGROUND)) {
296 return false;
297 }
298
299 /* One has a higher selection state (active > selected > nothing). */
300 if (a_active && !b_active) {
301 return false;
302 }
303 if (b_active && !a_active) {
304 return true;
305 }
306 if (!b_select && (a_active || a_select)) {
307 return false;
308 }
309 if (!a_select && (b_active || b_select)) {
310 return true;
311 }
312
313 return false;
314}
315
317{
318 Array<bNode *> sort_nodes = ntree.all_nodes();
319 std::sort(sort_nodes.begin(), sort_nodes.end(), [](bNode *a, bNode *b) {
320 return a->ui_order < b->ui_order;
321 });
322 std::stable_sort(sort_nodes.begin(), sort_nodes.end(), compare_node_depth);
323 for (const int i : sort_nodes.index_range()) {
324 sort_nodes[i]->ui_order = i;
325 }
326}
327
329{
330 Array<bNode *> nodes = ntree.all_nodes();
331 if (nodes.is_empty()) {
332 return {};
333 }
334 std::sort(nodes.begin(), nodes.end(), [](const bNode *a, const bNode *b) {
335 return a->ui_order < b->ui_order;
336 });
337 return nodes;
338}
339
341{
342 Array<bNode *> nodes = ntree.all_nodes();
343 if (nodes.is_empty()) {
344 return {};
345 }
346 std::sort(nodes.begin(), nodes.end(), [](const bNode *a, const bNode *b) {
347 return a->ui_order > b->ui_order;
348 });
349 return nodes;
350}
351
353{
354 Array<uiBlock *> blocks(nodes.size());
355
356 /* Add node uiBlocks in drawing order - prevents events going to overlapping nodes. */
357 Scene *scene = CTX_data_scene(&C);
358 wmWindow *window = CTX_wm_window(&C);
359 ARegion *region = CTX_wm_region(&C);
360 for (const int i : nodes.index_range()) {
361 const bNode &node = *nodes[i];
362 std::string block_name = "node_" + std::string(node.name);
363 uiBlock *block = UI_block_begin(
364 &C, scene, window, region, std::move(block_name), ui::EmbossType::Emboss);
365 blocks[node.index()] = block;
366 /* This cancels events for background nodes. */
368 }
369
370 return blocks;
371}
372
374{
375 return co * UI_SCALE_FAC;
376}
377
378static rctf node_to_rect(const bNode &node)
379{
380 rctf rect{};
381 rect.xmin = node.location[0];
382 rect.ymin = node.location[1] - node.height;
383 rect.xmax = node.location[0] + node.width;
384 rect.ymax = node.location[1];
385 return rect;
386}
387
388void node_to_updated_rect(const bNode &node, rctf &r_rect)
389{
390 r_rect = node_to_rect(node);
391 BLI_rctf_mul(&r_rect, UI_SCALE_FAC);
392}
393
395{
396 return co / UI_SCALE_FAC;
397}
398
399static bool is_node_panels_supported(const bNode &node)
400{
401 return node.declaration() && node.declaration()->use_custom_socket_order;
402}
403
404/* Draw UI for options, buttons, and previews. */
406 bNodeTree &ntree,
407 bNode &node,
409 uiBlock &block,
410 int &dy)
411{
412 /* Buttons rect? */
413 const bool node_options = draw_buttons && (node.flag & NODE_OPTIONS);
414 if (!node_options) {
415 return false;
416 }
417
418 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
419
420 /* Round the node origin because text contents are always pixel-aligned. */
421 const float2 loc = math::round(node_to_view(node.location));
422
423 dy -= NODE_DYS / 4;
424
425 uiLayout &layout = ui::block_layout(&block,
428 loc.x + NODE_DYS,
429 dy,
430 NODE_WIDTH(node) - NODE_DY,
431 0,
432 0,
434
435 if (node.is_muted()) {
436 layout.active_set(false);
437 }
438 if (!ID_IS_EDITABLE(&ntree.id)) {
439 layout.enabled_set(false);
440 }
441
442 layout.context_ptr_set("node", &nodeptr);
443
444 draw_buttons(&layout, (bContext *)&C, &nodeptr);
445
446 UI_block_align_end(&block);
447 const int buty = ui::block_layout_resolve(&block).y;
448
449 dy = buty - NODE_DYS / 4;
450 return true;
451}
452
453const char *node_socket_get_label(const bNodeSocket *socket, const char *panel_label)
454{
455 /* Get the short label if possible. This is used when grouping sockets under panels,
456 * to avoid redundancy in the label. */
457 const std::optional<StringRefNull> socket_short_label = bke::node_socket_short_label(*socket);
458 const char *socket_translation_context = bke::node_socket_translation_context(*socket);
459
460 if (socket_short_label.has_value()) {
461 return CTX_IFACE_(socket_translation_context, socket_short_label->c_str());
462 }
463
464 const StringRefNull socket_label = bke::node_socket_label(*socket);
465 const char *translated_socket_label = CTX_IFACE_(socket_translation_context,
466 socket_label.c_str());
467
468 /* Shorten socket label if it begins with the panel label. */
469 if (panel_label) {
470 const int len_prefix = strlen(panel_label);
471 if (STREQLEN(translated_socket_label, panel_label, len_prefix) &&
472 translated_socket_label[len_prefix] == ' ')
473 {
474 return translated_socket_label + len_prefix + 1;
475 }
476 }
477
478 /* Full label. */
479 return translated_socket_label;
480}
481
482static void draw_socket_layout(TreeDrawContext &tree_draw_ctx,
483 const bContext &C,
484 uiLayout &layout,
485 bNodeSocket &socket,
486 bNodeTree &ntree,
487 bNode &node,
488 PointerRNA &node_ptr,
489 PointerRNA &socket_ptr,
490 const char *panel_label)
491{
492 const nodes::SocketDeclaration *socket_decl = socket.runtime->declaration;
493 const StringRefNull label = node_socket_get_label(&socket, panel_label);
495 layout,
496 ntree,
497 node,
498 socket,
499 node_ptr,
500 socket_ptr,
501 label,
503 if (socket_decl) {
504 if (socket_decl->custom_draw_fn) {
505 (*socket_decl->custom_draw_fn)(params);
506 return;
507 }
508 }
509 params.draw_standard(layout);
510}
511
512static bool node_update_basis_socket(TreeDrawContext &tree_draw_ctx,
513 const bContext &C,
514 bNodeTree &ntree,
515 bNode &node,
516 const char *panel_label,
517 bNodeSocket *input_socket,
518 bNodeSocket *output_socket,
519 uiBlock &block,
520 const int &locx,
521 int &locy)
522{
523 if ((!input_socket || !input_socket->is_visible()) &&
524 (!output_socket || !output_socket->is_visible()))
525 {
526 return false;
527 }
528
529 const int topy = locy;
530
531 /* Add the half the height of a multi-input socket to cursor Y
532 * to account for the increased height of the taller sockets. */
533 const bool is_multi_input = (input_socket ? input_socket->flag & SOCK_MULTI_INPUT : false);
534 const float multi_input_socket_offset = is_multi_input ?
535 std::max(input_socket->runtime->total_inputs - 2,
536 0) *
538 0.0f;
539 locy -= multi_input_socket_offset * 0.5f;
540
541 uiLayout &layout = ui::block_layout(&block,
544 locx + NODE_DYS,
545 locy,
546 NODE_WIDTH(node) - NODE_DY,
547 NODE_DY,
548 0,
550
551 if (node.is_muted()) {
552 layout.active_set(false);
553 }
554 if (!ID_IS_EDITABLE(&ntree.id)) {
555 layout.enabled_set(false);
556 }
557
558 uiLayout *row = &layout.row(true);
559 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
560 row->context_ptr_set("node", &nodeptr);
561
562 if (input_socket) {
563 /* Context pointers for current node and socket. */
564 PointerRNA sockptr = RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, input_socket);
565 row->context_ptr_set("socket", &sockptr);
566
568
570 tree_draw_ctx, C, *row, *input_socket, ntree, node, nodeptr, sockptr, panel_label);
571 }
572 else {
573 /* Context pointers for current node and socket. */
574 PointerRNA sockptr = RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, output_socket);
575 row->context_ptr_set("socket", &sockptr);
576
577 /* Align output buttons to the right. */
579
581 tree_draw_ctx, C, *row, *output_socket, ntree, node, nodeptr, sockptr, panel_label);
582 }
583
584 if (input_socket) {
585 /* Round the socket location to stop it from jiggling. */
586 input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
587 }
588 if (output_socket) {
589 /* Round the socket location to stop it from jiggling. */
590 output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
591 round(locy - NODE_DYS));
592 }
593
594 /* Prioritize tooltip for inputs if available. The tooltip for the output is still accessible
595 * when hovering exactly over the output socket. */
596 if (input_socket) {
597 node_socket_add_tooltip_in_node_editor(*input_socket, *row);
598 }
599 else if (output_socket) {
600 node_socket_add_tooltip_in_node_editor(*output_socket, *row);
601 }
602
603 UI_block_align_end(&block);
604
605 int buty = ui::block_layout_resolve(&block).y;
606 /* Ensure minimum socket height in case layout is empty. */
607 buty = min_ii(buty, topy - NODE_DY);
608 locy = buty - multi_input_socket_offset * 0.5;
609 return true;
610}
611
612namespace flat_item {
613
622
623struct Socket {
624 static constexpr Type type = Type::Socket;
625 bNodeSocket *input = nullptr;
626 bNodeSocket *output = nullptr;
628};
629struct Separator {
630 static constexpr Type type = Type::Separator;
631};
646struct Layout {
647 static constexpr Type type = Type::Layout;
649};
650
651} // namespace flat_item
652
654 std::variant<flat_item::Socket,
661
663 {
664 return std::visit([](auto &&item) { return item.type; }, this->item);
665 }
666};
667
669 const bNode &node, const nodes::PanelDeclaration &panel_decl, MutableSpan<bool> r_result)
670{
671 bool potentially_visible = false;
672 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
673 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
674 const bNodeSocket &socket = node.socket_by_decl(*socket_decl);
675 potentially_visible |= socket.is_visible();
676 }
677 else if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl))
678 {
679 determine_potentially_visible_panels_recursive(node, *sub_panel_decl, r_result);
680 potentially_visible |= r_result[sub_panel_decl->index];
681 }
682 }
683 r_result[panel_decl.index] = potentially_visible;
684}
685
690{
691 for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) {
692 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
693 determine_potentially_visible_panels_recursive(node, *panel_decl, r_result);
694 }
695 }
696}
697
699 const nodes::PanelDeclaration &panel_decl,
700 const Span<bool> potentially_visible_states,
701 MutableSpan<bool> r_result)
702{
703 if (!potentially_visible_states[panel_decl.index]) {
704 /* This panel does not contain any visible sockets. */
705 return;
706 }
707 r_result[panel_decl.index] = true;
708 const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index];
709 if (panel_state.is_collapsed()) {
710 /* The sub-panels can't be visible if this panel is collapsed. */
711 return;
712 }
713 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
714 if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
716 node, *sub_panel_decl, potentially_visible_states, r_result);
717 }
718 }
719}
720
721static void determine_visible_panels_impl(const bNode &node,
722 const Span<bool> potentially_visible_states,
723 MutableSpan<bool> r_result)
724{
725 for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) {
726 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
728 node, *panel_decl, potentially_visible_states, r_result);
729 }
730 }
731}
732
738static void determine_visible_panels(const bNode &node, MutableSpan<bool> r_visibility_states)
739{
740 Array<bool> potentially_visible_states(r_visibility_states.size(), false);
741 determine_potentially_visible_panels(node, potentially_visible_states);
742 determine_visible_panels_impl(node, potentially_visible_states, r_visibility_states);
743}
744
746 const nodes::SocketDeclaration &socket_decl,
747 const nodes::PanelDeclaration *panel_decl,
748 const nodes::SocketDeclaration *prev_socket_decl,
749 Vector<FlatNodeItem> &r_items)
750{
751 bNodeSocket &socket = node.socket_by_decl(socket_decl);
752 if (!socket.is_visible()) {
753 return;
754 }
755 if (socket_decl.align_with_previous_socket) {
756 if (!prev_socket_decl || !node.socket_by_decl(*prev_socket_decl).is_visible()) {
757 r_items.append({flat_item::Socket()});
758 }
759 }
760 else {
761 r_items.append({flat_item::Socket()});
762 }
763 flat_item::Socket &item = std::get<flat_item::Socket>(r_items.last().item);
764 if (socket_decl.in_out == SOCK_IN) {
765 BLI_assert(!item.input);
766 item.input = &socket;
767 }
768 else {
769 BLI_assert(!item.output);
770 item.output = &socket;
771 }
772 item.panel_decl = panel_decl;
773}
774
776{
777 r_items.append({flat_item::Separator()});
778}
779
780static void add_flat_items_for_layout(const bNode &node,
781 const nodes::LayoutDeclaration &layout_decl,
782 Vector<FlatNodeItem> &r_items)
783{
784 if (!(node.flag & NODE_OPTIONS)) {
785 return;
786 }
787 r_items.append({flat_item::Layout{&layout_decl}});
788}
789
791 const nodes::PanelDeclaration &panel_decl,
792 const Span<bool> panel_visibility,
793 Vector<FlatNodeItem> &r_items)
794{
795 if (!panel_visibility[panel_decl.index]) {
796 return;
797 }
798 flat_item::PanelHeader header_item;
799 header_item.decl = &panel_decl;
800 const nodes::SocketDeclaration *panel_input_decl = panel_decl.panel_input_decl();
801 if (panel_input_decl) {
802 header_item.input = &node.socket_by_decl(*panel_input_decl);
803 }
804 r_items.append({header_item});
805
806 const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index];
807 if (panel_state.is_collapsed()) {
808 return;
809 }
810 r_items.append({flat_item::PanelContentBegin{&panel_decl}});
811 const nodes::SocketDeclaration *prev_socket_decl = nullptr;
812 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
813 if (item_decl == panel_input_decl) {
814 continue;
815 }
816 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
817 add_flat_items_for_socket(node, *socket_decl, &panel_decl, prev_socket_decl, r_items);
818 prev_socket_decl = socket_decl;
819 }
820 else {
821 if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
822 add_flat_items_for_panel(node, *sub_panel_decl, panel_visibility, r_items);
823 }
824 else if (dynamic_cast<const nodes::SeparatorDeclaration *>(item_decl)) {
826 }
827 else if (const auto *layout_decl = dynamic_cast<const nodes::LayoutDeclaration *>(item_decl))
828 {
829 add_flat_items_for_layout(node, *layout_decl, r_items);
830 }
831 prev_socket_decl = nullptr;
832 }
833 }
834 r_items.append({flat_item::PanelContentEnd{&panel_decl}});
835}
836
841{
843 BLI_assert(node.runtime->panels.size() == node.num_panel_states);
844
845 const int panels_num = node.num_panel_states;
846 Array<bool> panel_visibility(panels_num, false);
847 determine_visible_panels(node, panel_visibility);
848
849 const nodes::SocketDeclaration *prev_socket_decl = nullptr;
850
852 for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) {
853 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
854 add_flat_items_for_socket(node, *socket_decl, nullptr, prev_socket_decl, items);
855 prev_socket_decl = socket_decl;
856 }
857 else {
858 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
859 add_flat_items_for_panel(node, *panel_decl, panel_visibility, items);
860 }
861 else if (dynamic_cast<const nodes::SeparatorDeclaration *>(item_decl)) {
863 }
864 else if (const auto *layout_decl = dynamic_cast<const nodes::LayoutDeclaration *>(item_decl))
865 {
866 add_flat_items_for_layout(node, *layout_decl, items);
867 }
868 prev_socket_decl = nullptr;
869 }
870 }
871 return items;
872}
873
875static float get_margin_empty()
876{
877 return NODE_DYS;
878}
879
882{
883 const FlatNodeItem &first_item = items[0];
884 const flat_item::Type first_item_type = first_item.type();
885 switch (first_item_type) {
887 return 2 * NODE_ITEM_SPACING_Y;
889 return NODE_ITEM_SPACING_Y / 2;
891 return 3 * NODE_ITEM_SPACING_Y;
893 return 4 * NODE_ITEM_SPACING_Y;
896 break;
897 }
899 return 0;
900}
901
904{
905 const FlatNodeItem &last_item = items.last();
906 const flat_item::Type last_item_type = last_item.type();
907 switch (last_item_type) {
909 return 2 * NODE_ITEM_SPACING_Y;
911 return NODE_ITEM_SPACING_Y;
913 return 5 * NODE_ITEM_SPACING_Y;
915 return 4 * NODE_ITEM_SPACING_Y;
917 break;
919 return 1 * NODE_ITEM_SPACING_Y;
920 }
922 return 0;
923}
924
926static float get_margin_between_elements(const Span<FlatNodeItem> items, const int next_index)
927{
928 BLI_assert(next_index >= 1);
929 const FlatNodeItem &prev = items[next_index - 1];
930 const FlatNodeItem &next = items[next_index];
931 using flat_item::Type;
932 const Type prev_type = prev.type();
933 const Type next_type = next.type();
934
935 /* Handle all cases explicitly. This simplifies modifying the margins for specific cases
936 * without breaking other cases significantly. */
937 switch (prev_type) {
938 case Type::Socket: {
939 switch (next_type) {
940 case Type::Socket:
941 return NODE_ITEM_SPACING_Y;
942 case Type::Separator:
943 return 0;
944 case Type::Layout:
945 return 2 * NODE_ITEM_SPACING_Y;
946 case Type::PanelHeader:
947 return 3 * NODE_ITEM_SPACING_Y;
948 case Type::PanelContentBegin:
949 break;
950 case Type::PanelContentEnd:
951 return 2 * NODE_ITEM_SPACING_Y;
952 }
953 break;
954 }
955 case Type::Layout: {
956 switch (next_type) {
957 case Type::Socket:
958 return 2 * NODE_ITEM_SPACING_Y;
959 case Type::Separator:
960 return 0;
961 case Type::Layout:
962 return NODE_ITEM_SPACING_Y;
963 case Type::PanelHeader:
964 return 3 * NODE_ITEM_SPACING_Y;
965 case Type::PanelContentBegin:
966 break;
967 case Type::PanelContentEnd:
968 return 2 * NODE_ITEM_SPACING_Y;
969 }
970 break;
971 }
972 case Type::Separator: {
973 switch (next_type) {
974 case Type::Socket:
975 return 2 * NODE_ITEM_SPACING_Y;
976 case Type::Separator:
977 return NODE_ITEM_SPACING_Y;
978 case Type::Layout:
979 return NODE_ITEM_SPACING_Y;
980 case Type::PanelHeader:
981 return NODE_ITEM_SPACING_Y;
982 case Type::PanelContentBegin:
983 break;
984 case Type::PanelContentEnd:
985 return NODE_ITEM_SPACING_Y;
986 }
987 break;
988 }
989 case Type::PanelHeader: {
990 switch (next_type) {
991 case Type::Socket:
992 return 4 * NODE_ITEM_SPACING_Y;
993 case Type::Separator:
994 return 3 * NODE_ITEM_SPACING_Y;
995 case Type::Layout:
996 return 3 * NODE_ITEM_SPACING_Y;
997 case Type::PanelHeader:
998 return 5 * NODE_ITEM_SPACING_Y;
999 case Type::PanelContentBegin:
1000 return 3 * NODE_ITEM_SPACING_Y;
1001 case Type::PanelContentEnd:
1002 return 3 * NODE_ITEM_SPACING_Y;
1003 }
1004 break;
1005 }
1006 case Type::PanelContentBegin: {
1007 switch (next_type) {
1008 case Type::Socket:
1009 return 2 * NODE_ITEM_SPACING_Y;
1010 case Type::Separator:
1011 return NODE_ITEM_SPACING_Y;
1012 case Type::Layout:
1013 return 2 * NODE_ITEM_SPACING_Y;
1014 case Type::PanelHeader:
1015 return 3 * NODE_ITEM_SPACING_Y;
1016 case Type::PanelContentBegin:
1017 break;
1018 case Type::PanelContentEnd:
1019 return NODE_ITEM_SPACING_Y;
1020 }
1021 break;
1022 }
1023 case Type::PanelContentEnd: {
1024 switch (next_type) {
1025 case Type::Socket:
1026 return NODE_ITEM_SPACING_Y;
1027 case Type::Separator:
1028 return NODE_ITEM_SPACING_Y;
1029 case Type::Layout:
1030 return NODE_ITEM_SPACING_Y;
1031 case Type::PanelHeader:
1032 return 3 * NODE_ITEM_SPACING_Y;
1033 case Type::PanelContentBegin:
1034 break;
1035 case Type::PanelContentEnd:
1036 return 0;
1037 }
1038 break;
1039 }
1040 }
1042 return 0.0f;
1043}
1044
1047 const int node_left_x,
1048 const nodes::PanelDeclaration &visible_panel_decl,
1049 const nodes::PanelDeclaration &panel_decl)
1050{
1051 const bke::bNodePanelRuntime &visible_panel_runtime =
1052 node.runtime->panels[visible_panel_decl.index];
1053
1054 /* If the panel runtime is not initialized, then it is not visible. */
1055 if (!visible_panel_runtime.header_center_y.has_value()) {
1056 return;
1057 }
1058
1059 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
1060 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
1061 bNodeSocket &socket = node.socket_by_decl(*socket_decl);
1062 const int socket_x = socket.in_out == SOCK_IN ? node_left_x : node_left_x + NODE_WIDTH(node);
1063 socket.runtime->location = math::round(
1064 float2(socket_x, *visible_panel_runtime.header_center_y));
1065 socket.flag |= SOCK_PANEL_COLLAPSED;
1066 }
1067 else if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl))
1068 {
1069 mark_sockets_collapsed_recursive(node, node_left_x, visible_panel_decl, *sub_panel_decl);
1070 }
1071 }
1072}
1073
1075 const int node_left_x,
1076 const nodes::PanelDeclaration &panel_decl)
1077{
1078 const bNodePanelState &panel_state = node.panel_states_array[panel_decl.index];
1079 if (panel_state.is_collapsed()) {
1080 mark_sockets_collapsed_recursive(node, node_left_x, panel_decl, panel_decl);
1081 return;
1082 }
1083 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
1084 if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
1085 update_collapsed_sockets_recursive(node, node_left_x, *sub_panel_decl);
1086 }
1087 }
1088}
1089
1094static void update_collapsed_sockets(bNode &node, const int node_left_x)
1095{
1096 for (const nodes::ItemDeclaration *item_decl : node.declaration()->root_items) {
1097 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
1098 update_collapsed_sockets_recursive(node, node_left_x, *panel_decl);
1099 }
1100 }
1101}
1102
1107static void tag_final_panel(bNode &node, const Span<FlatNodeItem> items)
1108{
1109 const flat_item::PanelContentEnd *final_panel = nullptr;
1110 for (int item_i = items.size() - 1; item_i >= 0; item_i--) {
1111 const FlatNodeItem &item = items[item_i];
1112 if (const auto *panel_item = std::get_if<flat_item::PanelContentEnd>(&item.item)) {
1113 final_panel = panel_item;
1114 }
1115 else {
1116 break;
1117 }
1118 }
1119 if (final_panel) {
1120 bke::bNodePanelRuntime &final_panel_runtime = node.runtime->panels[final_panel->decl->index];
1121 final_panel_runtime.content_extent->fill_node_end = true;
1122 }
1123}
1124
1125/* Advanced drawing with panels and arbitrary input/output ordering. */
1127 const bContext &C,
1128 bNodeTree &ntree,
1129 bNode &node,
1130 uiBlock &block,
1131 const int locx,
1132 int &locy)
1133{
1135 BLI_assert(node.runtime->panels.size() == node.num_panel_states);
1136
1137 /* Reset states. */
1138 for (bke::bNodePanelRuntime &panel_runtime : node.runtime->panels) {
1139 panel_runtime.header_center_y.reset();
1140 panel_runtime.content_extent.reset();
1141 panel_runtime.input_socket = nullptr;
1142 }
1143 for (bNodeSocket *socket : node.input_sockets()) {
1144 socket->flag &= ~SOCK_PANEL_COLLAPSED;
1145 }
1146 for (bNodeSocket *socket : node.output_sockets()) {
1147 socket->flag &= ~SOCK_PANEL_COLLAPSED;
1148 }
1149
1150 /* Gather flattened list of items in the node. */
1151 const Vector<FlatNodeItem> flat_items = make_flat_node_items(node);
1152 if (flat_items.is_empty()) {
1153 const float margin = get_margin_empty();
1154 locy -= margin;
1155 return;
1156 }
1157
1158 for (const int item_i : flat_items.index_range()) {
1159 /* Apply margins. This should be the only place that applies margins between elements so that
1160 * it is easy change later on. */
1161 if (item_i == 0) {
1162 const float margin = get_margin_from_top(flat_items);
1163 locy -= margin;
1164 }
1165 else {
1166 const float margin = get_margin_between_elements(flat_items, item_i);
1167 locy -= margin;
1168 }
1169
1170 const FlatNodeItem &item_variant = flat_items[item_i];
1171 std::visit(
1172 [&](const auto &item) {
1173 using ItemT = std::decay_t<decltype(item)>;
1174 if constexpr (std::is_same_v<ItemT, flat_item::Socket>) {
1175 bNodeSocket *input_socket = item.input;
1176 bNodeSocket *output_socket = item.output;
1177 const nodes::PanelDeclaration *panel_decl = item.panel_decl;
1178 const char *parent_label = panel_decl ? panel_decl->name.c_str() : "";
1179 node_update_basis_socket(tree_draw_ctx,
1180 C,
1181 ntree,
1182 node,
1183 parent_label,
1184 input_socket,
1185 output_socket,
1186 block,
1187 locx,
1188 locy);
1189 }
1190 else if constexpr (std::is_same_v<ItemT, flat_item::Layout>) {
1191 const nodes::LayoutDeclaration &decl = *item.decl;
1192 /* Round the node origin because text contents are always pixel-aligned. */
1193 const float2 loc = math::round(node_to_view(node.location));
1194 uiLayout &layout = ui::block_layout(&block,
1197 loc.x + NODE_DYS,
1198 locy,
1199 NODE_WIDTH(node) - NODE_DY,
1200 0,
1201 0,
1203 if (node.is_muted()) {
1204 layout.active_set(false);
1205 }
1206 if (!ID_IS_EDITABLE(&ntree.id)) {
1207 layout.enabled_set(false);
1208 }
1209 PointerRNA node_ptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
1210 layout.context_ptr_set("node", &node_ptr);
1211 decl.draw(&layout, const_cast<bContext *>(&C), &node_ptr);
1212 UI_block_align_end(&block);
1213 locy = ui::block_layout_resolve(&block).y;
1214 }
1215 else if constexpr (std::is_same_v<ItemT, flat_item::Separator>) {
1216 uiLayout &layout = ui::block_layout(&block,
1219 locx + NODE_DYS,
1220 locy,
1221 NODE_WIDTH(node) - NODE_DY,
1222 NODE_DY,
1223 0,
1227 }
1228 else if constexpr (std::is_same_v<ItemT, flat_item::PanelHeader>) {
1229 const nodes::PanelDeclaration &node_decl = *item.decl;
1230 bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index];
1231 const float panel_header_height = NODE_DYS;
1232 locy -= panel_header_height / 2;
1233 panel_runtime.header_center_y = locy;
1234 locy -= panel_header_height / 2;
1235 bNodeSocket *input_socket = item.input;
1236 if (input_socket) {
1237 panel_runtime.input_socket = input_socket;
1238 input_socket->runtime->location = float2(locx, *panel_runtime.header_center_y);
1239 }
1240 }
1241 else if constexpr (std::is_same_v<ItemT, flat_item::PanelContentBegin>) {
1242 const nodes::PanelDeclaration &node_decl = *item.decl;
1243 bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index];
1244 panel_runtime.content_extent.emplace();
1245 panel_runtime.content_extent->max_y = locy;
1246 }
1247 else if constexpr (std::is_same_v<ItemT, flat_item::PanelContentEnd>) {
1248 const nodes::PanelDeclaration &node_decl = *item.decl;
1249 bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[node_decl.index];
1250 panel_runtime.content_extent->min_y = locy;
1251 }
1252 },
1253 item_variant.item);
1254 }
1255
1256 const float bottom_margin = get_margin_to_bottom(flat_items);
1257 locy -= bottom_margin;
1258
1259 update_collapsed_sockets(node, locx);
1260 tag_final_panel(node, flat_items);
1261}
1262
1263/* Conventional drawing in outputs/buttons/inputs order. */
1265 const bContext &C,
1266 bNodeTree &ntree,
1267 bNode &node,
1268 uiBlock &block,
1269 const int locx,
1270 int &locy)
1271{
1272 /* Space at the top. */
1273 locy -= NODE_DYS / 2;
1274
1275 /* Output sockets. */
1276 bool add_output_space = false;
1277
1278 for (bNodeSocket *socket : node.output_sockets()) {
1279 /* Clear flag, conventional drawing does not support panels. */
1280 socket->flag &= ~SOCK_PANEL_COLLAPSED;
1281
1283 tree_draw_ctx, C, ntree, node, nullptr, nullptr, socket, block, locx, locy))
1284 {
1285 if (socket->next) {
1286 locy -= NODE_ITEM_SPACING_Y;
1287 }
1288 add_output_space = true;
1289 }
1290 }
1291
1292 if (add_output_space) {
1293 locy -= NODE_DY / 4;
1294 }
1295
1296 const bool add_button_space = node_update_basis_buttons(
1297 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
1298
1299 bool add_input_space = false;
1300
1301 /* Input sockets. */
1302 for (bNodeSocket *socket : node.input_sockets()) {
1303 /* Clear flag, conventional drawing does not support panels. */
1304 socket->flag &= ~SOCK_PANEL_COLLAPSED;
1305
1307 tree_draw_ctx, C, ntree, node, nullptr, socket, nullptr, block, locx, locy))
1308 {
1309 if (socket->next) {
1310 locy -= NODE_ITEM_SPACING_Y;
1311 }
1312 add_input_space = true;
1313 }
1314 }
1315
1316 /* Little bit of padding at the bottom. */
1317 if (add_input_space || add_button_space) {
1318 locy -= NODE_DYS / 2;
1319 }
1320}
1321
1325static void node_update_basis(const bContext &C,
1326 TreeDrawContext &tree_draw_ctx,
1327 bNodeTree &ntree,
1328 bNode &node,
1329 uiBlock &block)
1330{
1331 /* Round the node origin because text contents are always pixel-aligned. */
1332 const float2 loc = math::round(node_to_view(node.location));
1333
1334 int dy = loc.y;
1335
1336 /* Header. */
1337 dy -= NODE_DY;
1338
1339 if (is_node_panels_supported(node)) {
1340 node_update_basis_from_declaration(tree_draw_ctx, C, ntree, node, block, loc.x, dy);
1341 }
1342 else {
1343 node_update_basis_from_socket_lists(tree_draw_ctx, C, ntree, node, block, loc.x, dy);
1344 }
1345
1346 node.runtime->draw_bounds.xmin = loc.x;
1347 node.runtime->draw_bounds.xmax = loc.x + NODE_WIDTH(node);
1348 node.runtime->draw_bounds.ymax = loc.y;
1349 node.runtime->draw_bounds.ymin = min_ff(dy, loc.y - 2 * NODE_DY);
1350
1351 /* Set the block bounds to clip mouse events from underlying nodes.
1352 * Add a margin for sockets on each side. */
1354 node.runtime->draw_bounds.xmin - NODE_SOCKSIZE,
1355 node.runtime->draw_bounds.ymin,
1356 node.runtime->draw_bounds.xmax + NODE_SOCKSIZE,
1357 node.runtime->draw_bounds.ymax);
1358}
1359
1363static void node_update_collapsed(bNode &node, uiBlock &block)
1364{
1365 int totin = 0, totout = 0;
1366
1367 /* Round the node origin because text contents are always pixel-aligned. */
1368 const float2 loc = math::round(node_to_view(node.location));
1369
1370 /* Calculate minimal radius. */
1371 for (const bNodeSocket *socket : node.input_sockets()) {
1372 if (socket->is_visible()) {
1373 totin++;
1374 }
1375 }
1376 for (const bNodeSocket *socket : node.output_sockets()) {
1377 if (socket->is_visible()) {
1378 totout++;
1379 }
1380 }
1381
1382 const float dy = NODE_DY * 0.5f;
1383 const float height = dy * std::max({totin, totout, 2}) + BASIS_RAD * 2.0f;
1384 /* This offset for Y values keeps the text in the same spot as in non-collapsed nodes. */
1385 const float offset = NODE_DY * -0.5f;
1386
1387 node.runtime->draw_bounds.xmin = loc.x;
1388 node.runtime->draw_bounds.xmax = loc.x + NODE_WIDTH(node);
1389 node.runtime->draw_bounds.ymax = loc.y + height * 0.5f + offset;
1390 node.runtime->draw_bounds.ymin = loc.y - height * 0.5f + offset;
1391
1392 /* Output sockets. */
1393 {
1394 const float x = node.runtime->draw_bounds.xmax;
1395 float y = loc.y + dy * float(totout - 1) * 0.5f + offset;
1396 for (bNodeSocket *socket : node.output_sockets()) {
1397 if (socket->is_visible()) {
1398 socket->runtime->location = {x, y};
1399 y -= dy;
1400 }
1401 }
1402 }
1403
1404 /* Input sockets. */
1405 {
1406 const float x = node.runtime->draw_bounds.xmin;
1407 float y = loc.y + dy * float(totin - 1) * 0.5f + offset;
1408 for (bNodeSocket *socket : node.input_sockets()) {
1409 if (socket->is_visible()) {
1410 socket->runtime->location = {x, y};
1411 y -= dy;
1412 }
1413 }
1414 }
1415
1416 /* Set the block bounds to clip mouse events from underlying nodes.
1417 * Add a margin for sockets on each side. */
1419 node.runtime->draw_bounds.xmin - NODE_SOCKSIZE,
1420 node.runtime->draw_bounds.ymin,
1421 node.runtime->draw_bounds.xmax + NODE_SOCKSIZE,
1422 node.runtime->draw_bounds.ymax);
1423}
1424
1425static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
1426{
1427 const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass :
1428 node.typeinfo->ui_class(&node);
1429 switch (nclass) {
1430 case NODE_CLASS_INPUT:
1431 return TH_NODE_INPUT;
1432 case NODE_CLASS_OUTPUT: {
1433 if (node.type_legacy == GEO_NODE_VIEWER) {
1434 return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE;
1435 }
1436 const bool is_output_node = (node.flag & NODE_DO_OUTPUT) ||
1438 return is_output_node ? TH_NODE_OUTPUT : TH_NODE;
1439 }
1441 return TH_NODE_CONVERTER;
1443 return TH_NODE_COLOR;
1445 return TH_NODE_VECTOR;
1447 return TH_NODE_FILTER;
1448 case NODE_CLASS_GROUP:
1449 return TH_NODE_GROUP;
1451 return TH_NODE_INTERFACE;
1452 case NODE_CLASS_MATTE:
1453 return TH_NODE_MATTE;
1454 case NODE_CLASS_DISTORT:
1455 return TH_NODE_DISTORT;
1456 case NODE_CLASS_TEXTURE:
1457 return TH_NODE_TEXTURE;
1458 case NODE_CLASS_SHADER:
1459 return TH_NODE_SHADER;
1460 case NODE_CLASS_SCRIPT:
1461 return TH_NODE_SCRIPT;
1463 return TH_NODE_GEOMETRY;
1465 return TH_NODE_ATTRIBUTE;
1466 case NODE_CLASS_LAYOUT:
1467 return node.is_frame() ? TH_NODE_FRAME : TH_NODE;
1468 default:
1469 return TH_NODE;
1470 }
1471}
1472
1473static void node_draw_mute_line(const bContext &C,
1474 const View2D &v2d,
1475 const SpaceNode &snode,
1476 const bNode &node)
1477{
1479
1480 for (const bNodeLink &link : node.internal_links()) {
1481 if (!bke::node_link_is_hidden(link)) {
1482 node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
1483 }
1484 }
1485
1487}
1488
1490 const int socket_index_in_tree,
1491 const float2 location,
1492 const float2 size)
1493{
1494 /* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
1495 * button on top of them for the tooltip. */
1496 uiBut *but = uiDefIconBut(&block,
1498 0,
1499 ICON_NONE,
1500 location.x - size.x / 2.0f,
1501 location.y - size.y / 2.0f,
1502 size.x,
1503 size.y,
1504 nullptr,
1505 0,
1506 0,
1507 std::nullopt);
1508
1510 but,
1511 [](bContext &C, uiTooltipData &tip, uiBut *but, void *argN) {
1512 const SpaceNode &snode = *CTX_wm_space_node(&C);
1513 const bNodeTree &ntree = *snode.edittree;
1514 const int index_in_tree = POINTER_AS_INT(argN);
1515 ntree.ensure_topology_cache();
1516 const bNodeSocket &socket = *ntree.all_sockets()[index_in_tree];
1517 build_socket_tooltip(tip, C, but, ntree, socket);
1518 },
1519 POINTER_FROM_INT(socket_index_in_tree),
1520 nullptr);
1521}
1522
1523static const float virtual_node_socket_outline_color[4] = {0.5, 0.5, 0.5, 1.0};
1524
1525static void node_socket_outline_color_get(const bool selected,
1526 const int socket_type,
1527 float r_outline_color[4])
1528{
1529 /* Explicitly use the node editor theme for the outline color to ensure consistency even when
1530 * sockets are drawn in other editors.
1531 */
1532 if (selected) {
1533 UI_GetThemeColorType4fv(TH_ACTIVE, SPACE_NODE, r_outline_color);
1534 }
1535 else if (socket_type == SOCK_CUSTOM) {
1536 /* Until there is a better place for per socket color,
1537 * the outline color for virtual sockets is set here. */
1539 }
1540 else {
1541 UI_GetThemeColorType4fv(TH_WIRE, SPACE_NODE, r_outline_color);
1542 r_outline_color[3] = 1.0f;
1543 }
1544}
1545
1547 const bNodeTree &ntree,
1548 PointerRNA &node_ptr,
1549 const bNodeSocket &sock,
1550 float r_color[4])
1551{
1552 if (!sock.typeinfo->draw_color) {
1553 /* Fall back to the simple variant. If not defined either, fall back to a magenta color. */
1554 if (sock.typeinfo->draw_color_simple) {
1555 sock.typeinfo->draw_color_simple(sock.typeinfo, r_color);
1556 }
1557 else {
1558 copy_v4_v4(r_color, float4(1.0f, 0.0f, 1.0f, 1.0f));
1559 }
1560 return;
1561 }
1562
1563 BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node));
1565 &const_cast<ID &>(ntree.id), &RNA_NodeSocket, &const_cast<bNodeSocket &>(sock));
1566 sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color);
1567}
1568
1570{
1572 &layout,
1573 [](bContext &C, uiTooltipData &tip, uiBut *but, void *argN) {
1574 const SpaceNode &snode = *CTX_wm_space_node(&C);
1575 const bNodeTree &ntree = *snode.edittree;
1576 const int index_in_tree = POINTER_AS_INT(argN);
1577 ntree.ensure_topology_cache();
1578 const bNodeSocket &socket = *ntree.all_sockets()[index_in_tree];
1579 build_socket_tooltip(tip, C, but, ntree, socket);
1580 },
1581 POINTER_FROM_INT(sock.index_in_tree()),
1582 nullptr,
1583 nullptr);
1584}
1585
1586void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
1587{
1588 struct SocketTooltipData {
1589 const bNodeTree *ntree;
1590 const bNodeSocket *socket;
1591 };
1592
1593 SocketTooltipData *data = MEM_callocN<SocketTooltipData>(__func__);
1594 data->ntree = &ntree;
1595 data->socket = &sock;
1596
1598 &layout,
1599 [](bContext &C, uiTooltipData &tip, uiBut *but, void *argN) {
1600 SocketTooltipData *data = static_cast<SocketTooltipData *>(argN);
1601 build_socket_tooltip(tip, C, but, *data->ntree, *data->socket);
1602 },
1603 data,
1605 MEM_freeN);
1606}
1607
1608#define NODE_SOCKET_OUTLINE U.pixelsize
1609
1610void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
1611{
1612 const float radius = NODE_SOCKSIZE * scale;
1613 const float2 center = {BLI_rcti_cent_x_fl(rect), BLI_rcti_cent_y_fl(rect)};
1614 const rctf draw_rect = {
1615 center.x - radius,
1616 center.x + radius,
1617 center.y - radius,
1618 center.y + radius,
1619 };
1620
1621 ColorTheme4f outline_color;
1622 node_socket_outline_color_get(sock->flag & SELECT, sock->type, outline_color);
1623
1624 node_draw_nodesocket(&draw_rect,
1625 color,
1626 outline_color,
1627 NODE_SOCKET_OUTLINE * scale,
1628 sock->display_shape,
1629 1.0 / scale);
1630}
1631
1633#define NODE_TREE_SCALE_SMALL 0.2f
1634
1636static float node_tree_view_scale(const SpaceNode &snode)
1637{
1638 return (1.0f / snode.runtime->aspect) * UI_SCALE_FAC;
1639}
1640
1641/* Some elements of the node tree like labels or node sockets are hardly visible when zoomed
1642 * out and can slow down the drawing quite a bit.
1643 * This function can be used to check if it's worth to draw those details and return
1644 * early. */
1645static bool draw_node_details(const SpaceNode &snode)
1646{
1648}
1649
1651{
1653 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
1654
1656
1657 /* Drawing the checkerboard. */
1658 const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
1659 const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
1660 immUniform4f("color1", checker_dark, checker_dark, checker_dark, 1.0f);
1661 immUniform4f("color2", checker_light, checker_light, checker_light, 1.0f);
1662 immUniform1i("size", 8);
1663 immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
1665}
1666
1667/* Not a callback. */
1668static void node_draw_preview(const Scene *scene, ImBuf *preview, const rctf *prv)
1669{
1670 float xrect = BLI_rctf_size_x(prv);
1671 float yrect = BLI_rctf_size_y(prv);
1672 float xscale = xrect / float(preview->x);
1673 float yscale = yrect / float(preview->y);
1674 float scale;
1675
1676 /* Uniform scale and offset. */
1677 rctf draw_rect = *prv;
1678 if (xscale < yscale) {
1679 float offset = 0.5f * (yrect - float(preview->y) * xscale);
1680 draw_rect.ymin += offset;
1681 draw_rect.ymax -= offset;
1682 scale = xscale;
1683 }
1684 else {
1685 float offset = 0.5f * (xrect - float(preview->x) * yscale);
1686 draw_rect.xmin += offset;
1687 draw_rect.xmax -= offset;
1688 scale = yscale;
1689 }
1690
1691 node_draw_preview_background(&draw_rect);
1692
1694 /* Pre-multiply graphics. */
1696
1697 ED_draw_imbuf(preview,
1698 draw_rect.xmin,
1699 draw_rect.ymin,
1700 false,
1701 &scene->view_settings,
1702 &scene->display_settings,
1703 scale,
1704 scale);
1705
1707
1708 float black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1710 const float outline_width = 1.0f;
1711 draw_rect.xmin -= outline_width;
1712 draw_rect.xmax += outline_width;
1713 draw_rect.ymin -= outline_width;
1714 draw_rect.ymax += outline_width;
1715 UI_draw_roundbox_4fv(&draw_rect, false, BASIS_RAD / 2, black);
1716}
1717
1718/* Common handle function for operator buttons that need to select the node first. */
1719static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
1720{
1721 SpaceNode &snode = *CTX_wm_space_node(C);
1722 bNodeTree &node_tree = *snode.edittree;
1723 bNode &node = *node_tree.node_by_id(POINTER_AS_INT(node_argv));
1724 const char *opname = (const char *)op_argv;
1725
1726 /* Select & activate only the button's node. */
1727 node_select_single(*C, node);
1728
1729 WM_operator_name_call(C, opname, wm::OpCallContext::InvokeDefault, nullptr, nullptr);
1730}
1731
1732static void node_draw_shadow(const SpaceNode &snode,
1733 const bNode &node,
1734 const float radius,
1735 const float alpha)
1736{
1737 const rctf &rct = node.runtime->draw_bounds;
1739
1740 const float shadow_width = 0.4f * U.widget_unit;
1741 const float shadow_alpha = 0.2f * alpha;
1742
1743 ui_draw_dropshadow(&rct, radius, shadow_width, snode.runtime->aspect, shadow_alpha);
1744
1745 /* Outline emphasis. Slight darkening _inside_ the outline. */
1746 const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
1747 rctf rect{};
1748 rect.xmin = rct.xmin - 0.5f;
1749 rect.xmax = rct.xmax + 0.5f;
1750 rect.ymin = rct.ymin - 0.5f;
1751 rect.ymax = rct.ymax + 0.5f;
1752 UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
1753}
1754
1755/* Node groups draw two "copies" of the node body underneath, just narrower and dimmer. */
1757 const bNode &node,
1758 const rctf &rect,
1759 const float radius,
1760 const float color[4])
1761{
1762 if (node.type_legacy != NODE_GROUP) {
1763 return;
1764 }
1765
1766 /* How far it extends down and narrows. */
1767 const bool is_selected = node.flag & NODE_SELECT;
1768 const bool is_collapsed = node.flag & NODE_COLLAPSED;
1769 const float offset_x = 3.6f * UI_SCALE_FAC;
1770 const float offset_y = 2.4f * UI_SCALE_FAC;
1771 const float shadow_width = 0.2f * U.widget_unit;
1772 const float shadow_alpha = is_selected ? 0.4f : 0.2f;
1773 const float dim_collapsed = is_collapsed ? 0.2f : 0.0f;
1774
1775 const float outline_width = is_selected ? 1.0f : 0.5f;
1776 float outline_color[4];
1777 UI_GetThemeColor4fv(TH_NODE_OUTLINE, outline_color);
1778
1779 if (is_selected) {
1780 UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, outline_color);
1781 }
1782
1784
1785 /* Start with the last copy. */
1786 {
1787 const rctf rect_group_front = {
1788 rect.xmin + offset_x * 4,
1789 rect.xmax - offset_x * 4,
1790 rect.ymin - (offset_y * 2) - U.pixelsize,
1791 rect.ymin - offset_y + (U.pixelsize * 2),
1792 };
1793
1794 const rctf rect_group_front_shadow = {
1795 rect_group_front.xmin + outline_width,
1796 rect_group_front.xmax - outline_width,
1797 rect_group_front.ymin + outline_width,
1798 rect_group_front.ymax - outline_width,
1799 };
1800
1801 ui_draw_dropshadow(&rect_group_front_shadow,
1802 radius + outline_width,
1803 shadow_width,
1804 snode.runtime->aspect,
1805 shadow_alpha);
1806
1807 /* Use the node color (or header color when collapsed) but slightly darker. */
1808 float fill_color_front[4], outline_color_front[4];
1809 copy_v4_v4(fill_color_front, color);
1810 mul_v3_fl(fill_color_front, 0.8f - dim_collapsed);
1811
1812 copy_v4_v4(outline_color_front, outline_color);
1813 mul_v3_fl(outline_color_front, (is_selected ? 0.5f : 1.0f) - dim_collapsed);
1814
1815 UI_draw_roundbox_4fv_ex(&rect_group_front,
1816 fill_color_front,
1817 nullptr,
1818 0.0f,
1819 outline_color_front,
1820 outline_width,
1821 radius);
1822 }
1823
1824 /* Draw the first copy in the front. */
1825 {
1826 const rctf rect_group_back = {
1827 rect.xmin + offset_x * 2,
1828 rect.xmax - offset_x * 2,
1829 rect.ymin - offset_y - U.pixelsize,
1830 rect.ymin + (U.pixelsize * 2),
1831 };
1832
1833 const rctf rect_group_back_shadow = {
1834 rect_group_back.xmin + outline_width,
1835 rect_group_back.xmax - outline_width,
1836 rect_group_back.ymin + outline_width,
1837 rect_group_back.ymax - outline_width,
1838 };
1839
1840 ui_draw_dropshadow(&rect_group_back_shadow,
1841 radius + outline_width,
1842 shadow_width,
1843 snode.runtime->aspect,
1844 shadow_alpha);
1845
1846 float fill_color_back[4], outline_color_back[4];
1847 copy_v4_v4(fill_color_back, color);
1848 mul_v3_fl(fill_color_back, 0.9f - dim_collapsed);
1849
1850 copy_v4_v4(outline_color_back, outline_color);
1851 mul_v3_fl(outline_color_back, (is_selected ? 0.7f : 1.1f) - dim_collapsed);
1852
1853 UI_draw_roundbox_4fv_ex(&rect_group_back,
1854 fill_color_back,
1855 nullptr,
1856 0.0f,
1857 outline_color_back,
1858 outline_width,
1859 radius);
1860 }
1861}
1862
1863static void node_draw_socket(const bContext &C,
1864 const bNodeTree &ntree,
1865 const bNode &node,
1866 PointerRNA &node_ptr,
1867 uiBlock &block,
1868 const bNodeSocket &sock,
1869 const float outline_thickness,
1870 const bool selected,
1871 const float aspect)
1872{
1873 const float half_width = NODE_SOCKSIZE;
1874
1875 const bool multi_socket = (sock.flag & SOCK_MULTI_INPUT) && !(node.flag & NODE_COLLAPSED);
1876 float half_height = multi_socket ? node_socket_calculate_height(sock) : half_width;
1877
1878 ColorTheme4f socket_color;
1879 ColorTheme4f outline_color;
1880 node_socket_color_get(C, ntree, node_ptr, sock, socket_color);
1881 node_socket_outline_color_get(selected, sock.type, outline_color);
1882
1883 const float2 socket_location = sock.runtime->location;
1884
1885 const rctf rect = {
1886 socket_location.x - half_width,
1887 socket_location.x + half_width,
1888 socket_location.y - half_height,
1889 socket_location.y + half_height,
1890 };
1891
1893 &rect, socket_color, outline_color, outline_thickness, sock.display_shape, aspect);
1894
1896 block, sock.index_in_tree(), socket_location, float2(2.0f * half_width, 2.0f * half_height));
1897}
1898
1899static void node_draw_sockets(const bContext &C,
1900 uiBlock &block,
1901 const SpaceNode &snode,
1902 const bNodeTree &ntree,
1903 const bNode &node)
1904{
1905 if (!draw_node_details(snode)) {
1906 return;
1907 }
1908
1909 if (node.input_sockets().is_empty() && node.output_sockets().is_empty()) {
1910 return;
1911 }
1912
1914 const_cast<ID *>(&ntree.id), &RNA_Node, const_cast<bNode *>(&node));
1915
1916 const float outline_thickness = NODE_SOCKET_OUTLINE;
1917
1919 /* Input sockets. */
1920 for (const bNodeSocket *sock : node.input_sockets()) {
1921 if (!sock->is_icon_visible()) {
1922 continue;
1923 }
1924 const bool selected = (sock->flag & SELECT);
1926 C, ntree, node, nodeptr, block, *sock, outline_thickness, selected, snode.runtime->aspect);
1927 }
1928
1929 /* Output sockets. */
1930 for (const bNodeSocket *sock : node.output_sockets()) {
1931 if (!sock->is_icon_visible()) {
1932 continue;
1933 }
1934 const bool selected = (sock->flag & SELECT);
1936 C, ntree, node, nodeptr, block, *sock, outline_thickness, selected, snode.runtime->aspect);
1937 }
1939}
1940
1941static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
1942{
1943 Main *bmain = CTX_data_main(C);
1944 bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
1945 bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
1946
1947 panel_state->flag ^= NODE_PANEL_COLLAPSED;
1948
1949 BKE_main_ensure_invariants(*bmain, ntree->id);
1950}
1951
1952/* Draw panel backgrounds first, so other node elements can be rendered on top. */
1953static void node_draw_panels_background(const bNode &node)
1954{
1956
1957 float panel_color[4];
1959 /* Increase contrast in nodes a bit. */
1960 panel_color[3] *= 1.5f;
1961 const rctf &draw_bounds = node.runtime->draw_bounds;
1962
1963 const nodes::PanelDeclaration *final_panel_decl = nullptr;
1964
1965 const nodes::NodeDeclaration &node_decl = *node.declaration();
1966 for (const int panel_i : node_decl.panels.index_range()) {
1967 const nodes::PanelDeclaration &panel_decl = *node_decl.panels[panel_i];
1968 const bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[panel_i];
1969 if (!panel_runtime.content_extent.has_value()) {
1970 continue;
1971 }
1972 const rctf content_rect = {draw_bounds.xmin,
1973 draw_bounds.xmax,
1974 panel_runtime.content_extent->min_y,
1975 panel_runtime.content_extent->max_y};
1977 UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, panel_color);
1978 if (panel_runtime.content_extent->fill_node_end) {
1979 final_panel_decl = &panel_decl;
1980 }
1981 }
1982 if (final_panel_decl) {
1983 const bke::bNodePanelRuntime &final_panel_runtime =
1984 node.runtime->panels[final_panel_decl->index];
1985 const rctf content_rect = {draw_bounds.xmin,
1986 draw_bounds.xmax,
1987 draw_bounds.ymin,
1988 final_panel_runtime.content_extent->min_y};
1990 const int repeats = final_panel_decl->depth() + 1;
1991 for ([[maybe_unused]] const int i : IndexRange(repeats)) {
1992 UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, panel_color);
1993 }
1994 }
1995}
1996
2003 const nodes::PanelDeclaration &panel_decl)
2004{
2005 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
2006 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
2007 if (socket_decl->in_out == SOCK_OUT) {
2008 return false;
2009 }
2010 const bNodeSocket &socket = node.socket_by_decl(*socket_decl);
2011 if (!socket.is_inactive()) {
2012 return false;
2013 }
2014 }
2015 else if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl))
2016 {
2017 if (!panel_has_only_inactive_inputs(node, *sub_panel_decl)) {
2018 return false;
2019 }
2020 }
2021 }
2022 return true;
2023}
2024
2025static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block)
2026{
2028 const rctf &draw_bounds = node.runtime->draw_bounds;
2029
2030 const nodes::NodeDeclaration &node_decl = *node.declaration();
2031 for (const int panel_i : node_decl.panels.index_range()) {
2032 const nodes::PanelDeclaration &panel_decl = *node_decl.panels[panel_i];
2033 const bke::bNodePanelRuntime &panel_runtime = node.runtime->panels[panel_i];
2034 bNodeSocket *input_socket = panel_runtime.input_socket;
2035 const bNodePanelState &panel_state = node.panel_states_array[panel_i];
2036 if (!panel_runtime.header_center_y.has_value()) {
2037 continue;
2038 }
2039 const bool only_inactive_inputs = panel_has_only_inactive_inputs(node, panel_decl);
2040 const bool panel_is_inactive = node.is_muted() || only_inactive_inputs;
2041
2042 const rctf header_rect = {draw_bounds.xmin,
2043 draw_bounds.xmax,
2044 *panel_runtime.header_center_y - NODE_DYS,
2045 *panel_runtime.header_center_y + NODE_DYS};
2047
2048 /* Invisible button covering the entire header for collapsing/expanding. */
2049 const int header_but_margin = NODE_MARGIN_X / 3;
2050 uiBut *toggle_action_but = uiDefIconBut(
2051 &block,
2053 0,
2054 ICON_NONE,
2055 header_rect.xmin + header_but_margin,
2056 header_rect.ymin,
2057 std::max(int(header_rect.xmax - header_rect.xmin - 2 * header_but_margin), 0),
2058 header_rect.ymax - header_rect.ymin,
2059 nullptr,
2060 0.0f,
2061 0.0f,
2062 panel_decl.description.c_str());
2064 toggle_action_but, [&panel_state](const uiBut &) { return panel_state.is_collapsed(); });
2065 UI_but_func_set(toggle_action_but,
2067 const_cast<bNodePanelState *>(&panel_state),
2068 &ntree);
2069
2070 /* Collapse/expand icon. */
2071 const int but_size = U.widget_unit * 0.8f;
2072 const int but_padding = NODE_MARGIN_X / 4;
2073 int offsetx = draw_bounds.xmin + (NODE_MARGIN_X / 3);
2074 uiDefIconBut(&block,
2076 0,
2077 panel_state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
2078 offsetx,
2079 *panel_runtime.header_center_y - but_size / 2,
2080 but_size,
2081 but_size,
2082 nullptr,
2083 0.0f,
2084 0.0f,
2085 "");
2086 offsetx += but_size + but_padding;
2087
2089
2090 /* Panel toggle. */
2091 if (input_socket && !input_socket->is_logically_linked()) {
2093 &ntree.id, &RNA_NodeSocket, input_socket);
2094 uiBut *panel_toggle_but = uiDefButR(&block,
2096 -1,
2097 "",
2098 offsetx,
2099 int(*panel_runtime.header_center_y - NODE_DYS),
2100 UI_UNIT_X,
2101 NODE_DY,
2102 &socket_ptr,
2103 "default_value",
2104 0,
2105 0,
2106 0,
2107 "");
2109 panel_toggle_but,
2110 [](bContext &C, uiTooltipData &tip, uiBut *but, void *argN) {
2111 const SpaceNode &snode = *CTX_wm_space_node(&C);
2112 const bNodeTree &ntree = *snode.edittree;
2113 const int index_in_tree = POINTER_AS_INT(argN);
2114 ntree.ensure_topology_cache();
2115 const bNodeSocket &socket = *ntree.all_sockets()[index_in_tree];
2116 build_socket_tooltip(tip, C, but, ntree, socket);
2117 },
2118 POINTER_FROM_INT(input_socket->index_in_tree()),
2119 nullptr);
2120 if (panel_is_inactive) {
2121 UI_but_flag_enable(panel_toggle_but, UI_BUT_INACTIVE);
2122 }
2123 offsetx += UI_UNIT_X;
2124 }
2125
2126 /* Panel label. */
2127 const char *panel_translation_context = (panel_decl.translation_context.has_value() ?
2128 panel_decl.translation_context->c_str() :
2129 nullptr);
2130 uiBut *label_but = uiDefBut(
2131 &block,
2133 0,
2134 CTX_IFACE_(panel_translation_context, panel_decl.name),
2135 offsetx,
2136 int(*panel_runtime.header_center_y - NODE_DYS),
2137 short(draw_bounds.xmax - draw_bounds.xmin - (30.0f * UI_SCALE_FAC)),
2138 NODE_DY,
2139 nullptr,
2140 0,
2141 0,
2142 "");
2143
2144 if (panel_is_inactive) {
2146 }
2147 }
2148}
2149
2151{
2152 int highest_priority = 0;
2154 for (const geo_log::NodeWarning &warning : warnings) {
2155 const int priority = node_warning_type_severity(warning.type);
2156 if (priority > highest_priority) {
2157 highest_priority = priority;
2158 highest_priority_type = warning.type;
2159 }
2160 }
2161 return highest_priority_type;
2162}
2163
2164static std::string node_errors_tooltip_fn(const Span<geo_log::NodeWarning> warnings)
2165{
2166 std::string complete_string;
2167
2168 for (const geo_log::NodeWarning &warning : warnings.drop_back(1)) {
2169 complete_string += warning.message;
2170 /* Adding the period is not ideal for multi-line messages, but it is consistent
2171 * with other tooltip implementations in Blender, so it is added here. */
2172 complete_string += '.';
2173 complete_string += '\n';
2174 }
2175
2176 /* Let the tooltip system automatically add the last period. */
2177 complete_string += warnings.last().message;
2178
2179 return complete_string;
2180}
2181
2182#define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit)
2183
2185 const rctf &rect,
2186 const int icon,
2187 float &icon_offset,
2188 const char *tooltip = nullptr)
2189{
2190 icon_offset -= NODE_HEADER_ICON_SIZE;
2192 uiBut *but = uiDefIconBut(&block,
2194 0,
2195 icon,
2196 icon_offset,
2197 rect.ymax - NODE_DY,
2199 UI_UNIT_Y,
2200 nullptr,
2201 0,
2202 0,
2203 tooltip);
2205 return but;
2206}
2207
2208static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx,
2209 const bNodeTree &ntree,
2210 const bNode &node,
2211 uiBlock &block,
2212 const rctf &rect,
2213 float &icon_offset)
2214{
2215 if (ntree.type == NTREE_GEOMETRY) {
2216 geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
2217 const bNodeTreeZones *zones = node.owner_tree().zones();
2218 if (!zones) {
2219 return nullptr;
2220 }
2221 const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
2222 if (zone && ELEM(node.identifier, zone->input_node_id, zone->output_node_id)) {
2223 zone = zone->parent_zone;
2224 }
2225 return tree_draw_ctx.tree_logs.get_main_tree_log(zone);
2226 }();
2227
2229 if (geo_tree_log) {
2230 geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
2231 if (node_log != nullptr) {
2232 warnings = node_log->warnings;
2233 }
2234 }
2235 if (warnings.is_empty()) {
2236 return;
2237 }
2238
2239 const nodes::NodeWarningType display_type = node_error_highest_priority(warnings);
2240
2242 block, rect, nodes::node_warning_type_icon(display_type), icon_offset);
2244 but, [warnings = Array<geo_log::NodeWarning>(warnings)](const uiBut * /*but*/) {
2245 return node_errors_tooltip_fn(warnings);
2246 });
2247 return;
2248 }
2249 if (ntree.type == NTREE_SHADER) {
2250 const VectorSet<std::string> *errors = tree_draw_ctx.shader_node_errors.lookup_ptr(
2251 node.identifier);
2252 if (!errors) {
2253 return;
2254 }
2255 if (errors->is_empty()) {
2256 return;
2257 }
2258 uiBut *but = add_error_message_button(block, rect, ICON_ERROR, icon_offset);
2259 UI_but_func_quick_tooltip_set(but, [errors = *errors](const uiBut * /*but*/) {
2260 std::string tooltip;
2261 for (const int i : errors.index_range()) {
2262 const StringRefNull error = errors[i];
2263 tooltip += error.c_str();
2264 if (i + 1 < errors.size()) {
2265 tooltip += ".\n";
2266 }
2267 }
2268 return tooltip;
2269 });
2270 }
2271}
2272
2273static std::optional<std::chrono::nanoseconds> geo_node_get_execution_time(
2274 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2275{
2276 const bNodeTree &ntree = *snode.edittree;
2277
2278 geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
2279 const bNodeTreeZones *zones = ntree.zones();
2280 if (!zones) {
2281 return nullptr;
2282 }
2283 const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
2284 if (zone && ELEM(node.identifier, zone->input_node_id, zone->output_node_id)) {
2285 zone = zone->parent_zone;
2286 }
2287 return tree_draw_ctx.tree_logs.get_main_tree_log(zone);
2288 }();
2289
2290 if (tree_log == nullptr) {
2291 return std::nullopt;
2292 }
2293 if (node.is_group_output()) {
2294 return tree_log->execution_time;
2295 }
2296 if (node.is_frame()) {
2297 /* Could be cached in the future if this recursive code turns out to be slow. */
2298 std::chrono::nanoseconds run_time{0};
2299 bool found_node = false;
2300
2301 for (const bNode *tnode : node.direct_children_in_frame()) {
2302 if (tnode->is_frame()) {
2303 std::optional<std::chrono::nanoseconds> sub_frame_run_time = geo_node_get_execution_time(
2304 tree_draw_ctx, snode, *tnode);
2305 if (sub_frame_run_time.has_value()) {
2306 run_time += *sub_frame_run_time;
2307 found_node = true;
2308 }
2309 }
2310 else {
2311 if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr_as(tnode->identifier))
2312 {
2313 found_node = true;
2314 run_time += node_log->execution_time;
2315 }
2316 }
2317 }
2318 if (found_node) {
2319 return run_time;
2320 }
2321 return std::nullopt;
2322 }
2323 if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier)) {
2324 return node_log->execution_time;
2325 }
2326 return std::nullopt;
2327}
2328
2329/* Create node key instance, assuming the node comes from the currently edited node tree. */
2331{
2332 const bNodeTreePath *path = static_cast<const bNodeTreePath *>(snode.treepath.last);
2333
2334 /* Some code in this file checks for the non-null elements of the tree path. However, if we did
2335 * iterate into a node it is expected that there is a tree, and it should be in the path.
2336 * Otherwise something else went wrong. */
2337 BLI_assert(path);
2338
2339 /* Assume that the currently editing tree is the last in the path. */
2340 BLI_assert(snode.edittree == path->nodetree);
2341
2342 return bke::node_instance_key(path->parent_key, snode.edittree, &node);
2343}
2344
2345static std::optional<std::chrono::nanoseconds> compositor_accumulate_frame_node_execution_time(
2346 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2347{
2349
2350 timeit::Nanoseconds frame_execution_time(0);
2351 bool has_any_execution_time = false;
2352
2353 for (const bNode *current_node : node.direct_children_in_frame()) {
2354 const bNodeInstanceKey key = current_node_instance_key(snode, *current_node);
2355 if (const timeit::Nanoseconds *node_execution_time =
2357 {
2358 frame_execution_time += *node_execution_time;
2359 has_any_execution_time = true;
2360 }
2361 }
2362
2363 if (!has_any_execution_time) {
2364 return std::nullopt;
2365 }
2366
2367 return frame_execution_time;
2368}
2369
2370static std::optional<std::chrono::nanoseconds> compositor_node_get_execution_time(
2371 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2372{
2374
2375 /* For the frame nodes accumulate execution time of its children. */
2376 if (node.is_frame()) {
2377 return compositor_accumulate_frame_node_execution_time(tree_draw_ctx, snode, node);
2378 }
2379
2380 /* For other nodes simply lookup execution time.
2381 * The group node instances have their own entries in the execution times map. */
2382 const bNodeInstanceKey key = current_node_instance_key(snode, node);
2383 if (const timeit::Nanoseconds *execution_time =
2385 {
2386 if (execution_time->count() == 0) {
2387 return std::nullopt;
2388 }
2389 return *execution_time;
2390 }
2391
2392 return std::nullopt;
2393}
2394
2395static std::optional<std::chrono::nanoseconds> node_get_execution_time(
2396 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2397{
2398 switch (snode.edittree->type) {
2399 case NTREE_GEOMETRY:
2400 return geo_node_get_execution_time(tree_draw_ctx, snode, node);
2401 case NTREE_COMPOSIT:
2402 return compositor_node_get_execution_time(tree_draw_ctx, snode, node);
2403 default:
2404 return std::nullopt;
2405 }
2406}
2407
2408static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx,
2409 const SpaceNode &snode,
2410 const bNode &node)
2411{
2412 const std::optional<std::chrono::nanoseconds> exec_time = node_get_execution_time(
2413 tree_draw_ctx, snode, node);
2414
2415 if (!exec_time.has_value()) {
2416 return std::string("");
2417 }
2418
2419 const uint64_t exec_time_us =
2420 std::chrono::duration_cast<std::chrono::microseconds>(*exec_time).count();
2421
2422 /* Don't show time if execution time is 0 microseconds. */
2423 if (exec_time_us == 0) {
2424 return std::string("-");
2425 }
2426 if (exec_time_us < 100) {
2427 return std::string("< 0.1 ms");
2428 }
2429
2430 int precision = 0;
2431 /* Show decimal if value is below 1ms */
2432 if (exec_time_us < 1000) {
2433 precision = 2;
2434 }
2435 else if (exec_time_us < 10000) {
2436 precision = 1;
2437 }
2438
2439 std::stringstream stream;
2440 stream << std::fixed << std::setprecision(precision) << (exec_time_us / 1000.0f);
2441 return stream.str() + " ms";
2442}
2443
2447
2448static std::string named_attribute_tooltip(bContext * /*C*/, void *argN, const StringRef /*tip*/)
2449{
2450 NamedAttributeTooltipArg &arg = *static_cast<NamedAttributeTooltipArg *>(argN);
2451
2452 fmt::memory_buffer buf;
2453 fmt::format_to(fmt::appender(buf), "{}", TIP_("Accessed named attributes:"));
2454 fmt::format_to(fmt::appender(buf), "\n");
2455
2456 struct NameWithUsage {
2459 };
2460
2461 Vector<NameWithUsage> sorted_used_attribute;
2462 for (auto &&item : arg.usage_by_attribute.items()) {
2463 sorted_used_attribute.append({item.key, item.value});
2464 }
2465 std::sort(sorted_used_attribute.begin(),
2466 sorted_used_attribute.end(),
2467 [](const NameWithUsage &a, const NameWithUsage &b) {
2468 return BLI_strcasecmp_natural(a.name.c_str(), b.name.c_str()) < 0;
2469 });
2470
2471 for (const NameWithUsage &attribute : sorted_used_attribute) {
2472 const StringRefNull name = attribute.name;
2473 const geo_log::NamedAttributeUsage usage = attribute.usage;
2474 fmt::format_to(fmt::appender(buf), fmt::runtime(TIP_(" \u2022 \"{}\": ")), name);
2475 Vector<std::string> usages;
2477 usages.append(TIP_("read"));
2478 }
2480 usages.append(TIP_("write"));
2481 }
2483 usages.append(TIP_("remove"));
2484 }
2485 for (const int i : usages.index_range()) {
2486 fmt::format_to(fmt::appender(buf), "{}", usages[i]);
2487 if (i < usages.size() - 1) {
2488 fmt::format_to(fmt::appender(buf), ", ");
2489 }
2490 }
2491 fmt::format_to(fmt::appender(buf), "\n");
2492 }
2493 fmt::format_to(fmt::appender(buf), "\n");
2494 fmt::format_to(
2495 fmt::appender(buf),
2496 fmt::runtime(TIP_("Attributes with these names used within the group may conflict with "
2497 "existing attributes")));
2498 return fmt::to_string(buf);
2499}
2500
2502 const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute_name)
2503{
2504 const int attributes_num = usage_by_attribute_name.size();
2505
2506 NodeExtraInfoRow row;
2507 row.text = std::to_string(attributes_num) +
2508 (attributes_num == 1 ? RPT_(" Named Attribute") : RPT_(" Named Attributes"));
2509 row.icon = ICON_SPREADSHEET;
2511 row.tooltip_fn_arg = new NamedAttributeTooltipArg{usage_by_attribute_name};
2512 row.tooltip_fn_free_arg = [](void *arg) { delete static_cast<NamedAttributeTooltipArg *>(arg); };
2513 row.tooltip_fn_copy_arg = [](void *arg) -> void * {
2514 return new NamedAttributeTooltipArg(*static_cast<NamedAttributeTooltipArg *>(arg));
2515 };
2516 return row;
2517}
2518
2519static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
2520 TreeDrawContext &tree_draw_ctx, const bNode &node)
2521{
2522 geo_log::GeoTreeLog *geo_tree_log = tree_draw_ctx.tree_logs.get_main_tree_log(node);
2523 if (geo_tree_log == nullptr) {
2524 return std::nullopt;
2525 }
2526 if (ELEM(node.type_legacy,
2530 {
2531 /* Only show the overlay when the name is passed in from somewhere else. */
2532 for (const bNodeSocket *socket : node.input_sockets()) {
2533 if (STREQ(socket->name, "Name")) {
2534 if (!socket->is_directly_linked()) {
2535 return std::nullopt;
2536 }
2537 }
2538 }
2539 }
2540 geo_tree_log->ensure_used_named_attributes();
2541 geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
2542 if (node_log == nullptr) {
2543 return std::nullopt;
2544 }
2545 if (node_log->used_named_attributes.is_empty()) {
2546 return std::nullopt;
2547 }
2549}
2550
2551static std::optional<NodeExtraInfoRow> node_get_execution_time_label_row(
2552 TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2553{
2554 NodeExtraInfoRow row;
2555 row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
2556 if (row.text.empty()) {
2557 return std::nullopt;
2558 }
2559 row.tooltip = TIP_(
2560 "The execution time from the node tree's latest evaluation. For frame and group "
2561 "nodes, the time for all sub-nodes");
2562 row.icon = ICON_PREVIEW_RANGE;
2563 return row;
2564}
2565
2567 const SpaceNode &snode,
2568 const bNode &node,
2570{
2571 if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS) {
2572 std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
2573 tree_draw_ctx, snode, node);
2574 if (row.has_value()) {
2575 rows.append(std::move(*row));
2576 }
2577 }
2578}
2579
2581 TreeDrawContext &tree_draw_ctx,
2582 const SpaceNode &snode,
2583 const bNode &node)
2584{
2586
2587 if (node.typeinfo->get_extra_info) {
2588 nodes::NodeExtraInfoParams params{rows, *snode.edittree, node, C};
2589 node.typeinfo->get_extra_info(params);
2590 }
2591
2592 if (node.typeinfo->deprecation_notice) {
2593 NodeExtraInfoRow row;
2594 row.text = IFACE_("Deprecated");
2595 row.icon = ICON_INFO;
2596 row.tooltip = TIP_(node.typeinfo->deprecation_notice);
2597 rows.append(std::move(row));
2598 }
2599
2600 if (snode.edittree->type == NTREE_COMPOSIT) {
2601 node_get_compositor_extra_info(tree_draw_ctx, snode, node, rows);
2602 return rows;
2603 }
2604
2605 if (!(snode.edittree->type == NTREE_GEOMETRY)) {
2606 /* Currently geometry and compositor nodes are the only nodes to have extra info per nodes. */
2607 return rows;
2608 }
2609
2611 if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(tree_draw_ctx,
2612 node))
2613 {
2614 rows.append(std::move(*row));
2615 }
2616 }
2617
2618 if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS &&
2620 ELEM(node.type_legacy,
2621 NODE_FRAME,
2627 StringRef(node.idname).startswith("GeometryNodeImport")))
2628 {
2629 std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
2630 tree_draw_ctx, snode, node);
2631 if (row.has_value()) {
2632 rows.append(std::move(*row));
2633 }
2634 }
2635
2636 geo_log::GeoTreeLog *tree_log = tree_draw_ctx.tree_logs.get_main_tree_log(node);
2637
2638 if (tree_log) {
2639 tree_log->ensure_debug_messages();
2640 const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier);
2641 if (node_log != nullptr) {
2642 for (const StringRef message : node_log->debug_messages) {
2643 NodeExtraInfoRow row;
2644 row.text = message;
2645 row.icon = ICON_INFO;
2646 rows.append(std::move(row));
2647 }
2648 }
2649 }
2650
2651 return rows;
2652}
2653
2654static void node_draw_extra_info_row(const bNode &node,
2655 uiBlock &block,
2656 const rctf &rect,
2657 const int row,
2658 const NodeExtraInfoRow &extra_info_row)
2659{
2660 const float but_icon_left = rect.xmin + 6.0f * UI_SCALE_FAC;
2661 const float but_icon_width = NODE_HEADER_ICON_SIZE * 0.8f;
2662 const float but_icon_right = but_icon_left + but_icon_width;
2663
2664 void *tooltip_arg = extra_info_row.tooltip_fn_arg;
2665 if (tooltip_arg && extra_info_row.tooltip_fn_free_arg) {
2666 BLI_assert(extra_info_row.tooltip_fn_copy_arg);
2667 tooltip_arg = extra_info_row.tooltip_fn_copy_arg(tooltip_arg);
2668 }
2669
2671 uiBut *but_icon = uiDefIconBut(&block,
2673 0,
2674 extra_info_row.icon,
2675 int(but_icon_left),
2676 int(rect.ymin + row * EXTRA_INFO_ROW_HEIGHT),
2677 but_icon_width,
2678 UI_UNIT_Y,
2679 nullptr,
2680 0,
2681 0,
2682 extra_info_row.tooltip);
2683 if (extra_info_row.set_execute_fn) {
2684 extra_info_row.set_execute_fn(*but_icon);
2685 }
2686 if (extra_info_row.tooltip_fn != nullptr) {
2688 but_icon, extra_info_row.tooltip_fn, tooltip_arg, extra_info_row.tooltip_fn_free_arg);
2689 }
2690
2691 const float but_text_left = but_icon_right + 6.0f * UI_SCALE_FAC;
2692 const float but_text_right = rect.xmax;
2693 const float but_text_width = but_text_right - but_text_left;
2694
2695 uiBut *but_text = uiDefBut(&block,
2696 extra_info_row.set_execute_fn ? ButType::But : ButType::Label,
2697 0,
2698 extra_info_row.text.c_str(),
2699 int(but_text_left),
2700 int(rect.ymin + row * EXTRA_INFO_ROW_HEIGHT),
2701 short(but_text_width),
2702 NODE_DY,
2703 nullptr,
2704 0,
2705 0,
2706 extra_info_row.tooltip);
2708 if (extra_info_row.set_execute_fn) {
2709 extra_info_row.set_execute_fn(*but_text);
2710 }
2711 if (extra_info_row.tooltip_fn != nullptr) {
2712 /* Don't pass tooltip free function because it's already used on the uiBut above. */
2713 UI_but_func_tooltip_set(but_text, extra_info_row.tooltip_fn, tooltip_arg, nullptr);
2714 }
2715
2716 if (node.is_muted()) {
2719 }
2720
2722}
2723
2724static void node_draw_extra_info_panel_back(const bNode &node, const rctf &extra_info_rect)
2725{
2726 rctf panel_back_rect = extra_info_rect;
2727
2729 if (node.is_muted()) {
2731 }
2732 else {
2734 }
2735 color.a -= 0.35f;
2736
2737 ColorTheme4f color_outline;
2738 UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
2739
2740 const float outline_width = U.pixelsize;
2741 BLI_rctf_pad(&panel_back_rect, outline_width, outline_width);
2742
2745 &panel_back_rect, color, nullptr, 0.0f, color_outline, outline_width, BASIS_RAD);
2746}
2747
2749 TreeDrawContext &tree_draw_ctx,
2750 const SpaceNode &snode,
2751 const bNode &node,
2752 ImBuf *preview,
2753 uiBlock &block)
2754{
2755 const Scene *scene = CTX_data_scene(&C);
2756 if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) {
2757 return;
2758 }
2759 if (preview && !(preview->x > 0 && preview->y > 0)) {
2760 /* If the preview has an non-drawable size, just don't draw it. */
2761 preview = nullptr;
2762 }
2763 const Span<NodeExtraInfoRow> extra_info_rows =
2764 tree_draw_ctx.extra_info_rows_per_node[node.index()];
2765 if (extra_info_rows.is_empty() && !preview) {
2766 return;
2767 }
2768
2769 const rctf &rct = node.runtime->draw_bounds;
2770 rctf extra_info_rect;
2771
2772 if (node.is_frame()) {
2773 extra_info_rect.xmin = rct.xmin;
2774 extra_info_rect.xmax = rct.xmin + 95.0f * UI_SCALE_FAC;
2775 extra_info_rect.ymin = rct.ymin + 2.0f * UI_SCALE_FAC;
2776 extra_info_rect.ymax = rct.ymin + 2.0f * UI_SCALE_FAC;
2777 }
2778 else {
2779 const float padding = 3.0f * UI_SCALE_FAC;
2780
2781 extra_info_rect.xmin = rct.xmin + padding;
2782 extra_info_rect.xmax = rct.xmax - padding;
2783 extra_info_rect.ymin = rct.ymax;
2784 extra_info_rect.ymax = rct.ymax + extra_info_rows.size() * EXTRA_INFO_ROW_HEIGHT;
2785
2786 float preview_height = 0.0f;
2787 rctf preview_rect;
2788 if (preview) {
2789 const float width = BLI_rctf_size_x(&extra_info_rect);
2790 if (preview->x > preview->y) {
2791 preview_height = (width - 2.0f * padding) * float(preview->y) / float(preview->x) +
2792 2.0f * padding;
2793 preview_rect.ymin = extra_info_rect.ymin + padding;
2794 preview_rect.ymax = extra_info_rect.ymin + preview_height - padding;
2795 preview_rect.xmin = extra_info_rect.xmin + padding;
2796 preview_rect.xmax = extra_info_rect.xmax - padding;
2797 extra_info_rect.ymax += preview_height;
2798 }
2799 else {
2800 preview_height = width;
2801 const float preview_width = (width - 2.0f * padding) * float(preview->x) /
2802 float(preview->y) +
2803 2.0f * padding;
2804 preview_rect.ymin = extra_info_rect.ymin + padding;
2805 preview_rect.ymax = extra_info_rect.ymin + preview_height - padding;
2806 preview_rect.xmin = extra_info_rect.xmin + padding + (width - preview_width) / 2;
2807 preview_rect.xmax = extra_info_rect.xmax - padding - (width - preview_width) / 2;
2808 extra_info_rect.ymax += preview_height;
2809 }
2810 }
2811
2812 node_draw_extra_info_panel_back(node, extra_info_rect);
2813
2814 if (preview) {
2815 node_draw_preview(scene, preview, &preview_rect);
2816 }
2817
2818 /* Resize the rect to draw the textual infos on top of the preview. */
2819 extra_info_rect.ymin += preview_height;
2820 }
2821
2822 for (int row : extra_info_rows.index_range()) {
2823 node_draw_extra_info_row(node, block, extra_info_rect, row, extra_info_rows[row]);
2824 }
2825}
2826
2827static short get_viewer_shortcut_icon(const bNode &node)
2828{
2829 BLI_assert(node.is_type("CompositorNodeViewer") || node.is_type("GeometryNodeViewer"));
2830 switch (node.custom1) {
2832 /* No change by default. */
2833 return node.typeinfo->ui_icon;
2835 return ICON_EVENT_ONEKEY;
2837 return ICON_EVENT_TWOKEY;
2839 return ICON_EVENT_THREEKEY;
2841 return ICON_EVENT_FOURKEY;
2843 return ICON_EVENT_FIVEKEY;
2845 return ICON_EVENT_SIXKEY;
2847 return ICON_EVENT_SEVENKEY;
2849 return ICON_EVENT_EIGHTKEY;
2851 return ICON_EVENT_NINEKEY;
2852 }
2853
2854 return node.typeinfo->ui_icon;
2855}
2856
2857/* Returns true if the given node has an undefined type, a missing group node tree, or is
2858 * unsupported in the given node tree. */
2859static bool node_undefined_or_unsupported(const bNodeTree &node_tree, const bNode &node)
2860{
2861 if (node.typeinfo == &bke::NodeTypeUndefined) {
2862 return true;
2863 }
2864
2865 const char *disabled_hint = nullptr;
2866 if (!node.typeinfo->poll(node.typeinfo, &node_tree, &disabled_hint)) {
2867 return true;
2868 }
2869
2870 if (node.is_group()) {
2871 const ID *group_tree = node.id;
2872 if (group_tree == nullptr) {
2873 return false;
2874 }
2875 if (!ID_IS_LINKED(group_tree)) {
2876 return false;
2877 }
2878 if ((group_tree->tag & ID_TAG_MISSING) == 0) {
2879 return false;
2880 }
2881 return true;
2882 }
2883 return false;
2884}
2885
2887 const bNode &node,
2888 const int color_id)
2889{
2890 ColorTheme4f color_header;
2891
2892 /* The base color of the node header. */
2893 if (node_undefined_or_unsupported(ntree, node)) {
2894 /* Use warning color to indicate undefined types. */
2895 UI_GetThemeColorBlendShade4fv(TH_REDALERT, color_id, 0.1f, -40, color_header);
2896 }
2897 else if ((node.flag & NODE_COLLAPSED) && (node.flag & NODE_CUSTOM_COLOR)) {
2898 rgba_float_args_set(color_header, node.color[0], node.color[1], node.color[2], 1.0f);
2899 }
2900 else {
2901 UI_GetThemeColor4fv(color_id, color_header);
2902 }
2903
2904 /* Draw selected nodes fully opaque. */
2905 if (node.flag & SELECT) {
2906 color_header.a = 1.0f;
2907 }
2908
2909 /* Muted nodes get a mix of the background with the node color and are drawn slightly
2910 * transparent so the wires inside are visible. */
2911 if (node.is_muted()) {
2912 ColorTheme4f color_background;
2913 UI_GetThemeColor4fv(TH_BACK, color_background);
2914
2915 UI_GetColorPtrBlendAlpha4fv(color_header, color_background, 0.6f, -0.2f, color_header);
2916 }
2917
2918 return color_header;
2919}
2920
2921static void node_header_custom_tooltip(const bNode &node, uiBut &but)
2922{
2924 &but,
2925 [](bContext & /*C*/, uiTooltipData &data, uiBut * /*but*/, void *argN) {
2926 const bNode &node = *static_cast<const bNode *>(argN);
2927 const std::string description = node.typeinfo->ui_description_fn ?
2928 TIP_(node.typeinfo->ui_description_fn(node)) :
2929 TIP_(node.typeinfo->ui_description);
2930 if (!description.empty()) {
2932 data, std::move(description), "", UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
2933 }
2934 if (U.flag & USER_TOOLTIPS_PYTHON) {
2936 fmt::format("Python: {}", node.idname),
2937 "",
2940 !description.empty());
2941 }
2942 },
2943 &const_cast<bNode &>(node),
2944 nullptr);
2945}
2946
2947static void node_draw_basis(const bContext &C,
2948 TreeDrawContext &tree_draw_ctx,
2949 const View2D &v2d,
2950 const SpaceNode &snode,
2951 bNodeTree &ntree,
2952 const bNode &node,
2953 uiBlock &block,
2954 bNodeInstanceKey key)
2955{
2956 const float iconbutw = NODE_HEADER_ICON_SIZE;
2957 const bool show_preview = (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) &&
2959 (node.flag & NODE_PREVIEW) &&
2960 (USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) ||
2961 ntree.type != NTREE_SHADER);
2962
2963 /* Skip if out of view. */
2964 rctf rect_with_preview = node.runtime->draw_bounds;
2965 if (show_preview) {
2966 rect_with_preview.ymax += NODE_WIDTH(node);
2967 }
2968 if (BLI_rctf_isect(&rect_with_preview, &v2d.cur, nullptr) == false) {
2970 tree_draw_ctx.bmain,
2971 tree_draw_ctx.window,
2972 tree_draw_ctx.scene,
2973 tree_draw_ctx.region,
2974 tree_draw_ctx.depsgraph,
2975 &block);
2976 return;
2977 }
2978
2979 /* Shadow. */
2980 if (!bke::all_zone_node_types().contains(node.type_legacy)) {
2981 node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
2982 }
2983
2984 const rctf &rct = node.runtime->draw_bounds;
2985 float color[4];
2986 int color_id = node_get_colorid(tree_draw_ctx, node);
2987
2988 GPU_line_width(1.0f);
2989
2990 /* Overlay atop the node. */
2991 {
2992 bool drawn_with_previews = false;
2993
2994 if (show_preview) {
2997 CTX_data_pointer_get(&C, "node_previews").data);
2998 NestedTreePreviews *previews_shader = tree_draw_ctx.nested_group_infos;
2999
3000 if (previews_shader) {
3001 ImBuf *preview = node_preview_acquire_ibuf(ntree, *previews_shader, node);
3002 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, preview, block);
3003 node_release_preview_ibuf(*previews_shader);
3004 drawn_with_previews = true;
3005 }
3006 else if (previews_compo) {
3007 if (bke::bNodePreview *preview_compositor = previews_compo->lookup_ptr(key)) {
3009 C, tree_draw_ctx, snode, node, preview_compositor->ibuf, block);
3010 drawn_with_previews = true;
3011 }
3012 }
3013 }
3014
3015 if (drawn_with_previews == false) {
3016 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
3017 }
3018 }
3019
3020 const float padding = 0.5f;
3021 const float corner_radius = BASIS_RAD + padding;
3022 const float outline_width = U.pixelsize;
3023 /* Header. */
3024 {
3025 /* Add some padding to prevent transparent gaps with the outline. */
3026 const rctf rect = {
3027 rct.xmin - padding,
3028 rct.xmax + padding,
3029 rct.ymax - NODE_DY - padding,
3030 rct.ymax + padding,
3031 };
3032
3033 const ColorTheme4f color_header = node_header_color_get(ntree, node, color_id);
3034
3036 UI_draw_roundbox_4fv(&rect, true, corner_radius, color_header);
3037 }
3038
3039 /* Show/hide icons. */
3040 float iconofs = rct.xmax - 0.35f * U.widget_unit;
3041
3042 if (nodes::node_can_sync_sockets(C, ntree, node)) {
3043 iconofs -= iconbutw;
3045 uiBut *but = uiDefIconBut(&block,
3047 0,
3048 ICON_FILE_REFRESH,
3049 iconofs,
3050 rct.ymax - NODE_DY,
3051 iconbutw,
3052 UI_UNIT_Y,
3053 nullptr,
3054 0,
3055 0,
3056 "");
3057
3058 wmOperatorType *ot = WM_operatortype_find("NODE_OT_sockets_sync", false);
3061 opptr->data = bke::idprop::create_group("wmOperatorProperties").release();
3062 RNA_string_set(opptr, "node_name", node.name);
3064 }
3065
3066 /* Preview. */
3067 if (node_is_previewable(snode, ntree, node)) {
3068 const bool is_active = node.flag & NODE_PREVIEW;
3069 iconofs -= iconbutw;
3071 uiBut *but = uiDefIconBut(&block,
3073 0,
3074 is_active ? ICON_HIDE_OFF : ICON_HIDE_ON,
3075 iconofs,
3076 rct.ymax - NODE_DY,
3077 iconbutw,
3078 UI_UNIT_Y,
3079 nullptr,
3080 0,
3081 0,
3082 "");
3083 UI_but_func_set(but,
3086 (void *)"NODE_OT_preview_toggle");
3088 }
3090 node.typeinfo->ui_icon != ICON_NONE)
3091 {
3092 iconofs -= iconbutw;
3094 uiDefIconBut(&block,
3096 0,
3097 node.typeinfo->ui_icon,
3098 iconofs,
3099 rct.ymax - NODE_DY,
3100 iconbutw,
3101 UI_UNIT_Y,
3102 nullptr,
3103 0,
3104 0,
3105 "");
3107 }
3108 if (node.type_legacy == GEO_NODE_VIEWER) {
3109 const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer;
3110 iconofs -= iconbutw;
3112 uiBut *but = uiDefIconBut(&block,
3114 0,
3115 is_active ? ICON_RESTRICT_VIEW_OFF : ICON_RESTRICT_VIEW_ON,
3116 iconofs,
3117 rct.ymax - NODE_DY,
3118 iconbutw,
3119 UI_UNIT_Y,
3120 nullptr,
3121 0,
3122 0,
3123 "");
3124 /* Selection implicitly activates the node. */
3125 const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" :
3126 "NODE_OT_activate_viewer";
3128 but, node_toggle_button_cb, POINTER_FROM_INT(node.identifier), (void *)operator_idname);
3129
3130 short shortcut_icon = get_viewer_shortcut_icon(node);
3131 uiDefIconBut(&block,
3133 0,
3134 shortcut_icon,
3135 iconofs - 1.2 * iconbutw,
3136 rct.ymax - NODE_DY,
3137 iconbutw,
3138 UI_UNIT_Y,
3139 nullptr,
3140 0,
3141 0,
3142 "");
3144 }
3145 /* Viewer node shortcuts. */
3146 if (node.is_type("CompositorNodeViewer")) {
3147 short shortcut_icon = get_viewer_shortcut_icon(node);
3148 iconofs -= iconbutw;
3149 const bool is_active = node.flag & NODE_DO_OUTPUT;
3151 uiBut *but = uiDefIconBut(&block,
3153 0,
3154 is_active ? ICON_RESTRICT_VIEW_OFF : ICON_RESTRICT_VIEW_ON,
3155 iconofs,
3156 rct.ymax - NODE_DY,
3157 iconbutw,
3158 UI_UNIT_Y,
3159 nullptr,
3160 0,
3161 0,
3162 "");
3163
3164 UI_but_func_set(but,
3167 (void *)"NODE_OT_activate_viewer");
3168
3169 uiDefIconBut(&block,
3171 0,
3172 shortcut_icon,
3173 iconofs - 1.2 * iconbutw,
3174 rct.ymax - NODE_DY,
3175 iconbutw,
3176 UI_UNIT_Y,
3177 nullptr,
3178 0,
3179 0,
3180 "");
3182 }
3183
3184 node_add_error_message_button(tree_draw_ctx, ntree, node, block, rct, iconofs);
3185
3186 /* Title. */
3187 if (node.flag & SELECT) {
3189 }
3190 else {
3191 UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
3192 }
3193
3194 /* Collapse/expand icon. */
3195 {
3196 const int but_size = U.widget_unit * 0.8f;
3198
3199 uiBut *but = uiDefIconBut(&block,
3201 0,
3202 ICON_DOWNARROW_HLT,
3203 rct.xmin + (NODE_MARGIN_X / 3),
3204 rct.ymax - NODE_DY / 2.2f - but_size / 2,
3205 but_size,
3206 but_size,
3207 nullptr,
3208 0.0f,
3209 0.0f,
3210 "");
3211
3212 UI_but_func_set(but,
3215 (void *)"NODE_OT_hide_toggle");
3217 }
3218
3219 const std::string showname = bke::node_label(ntree, node);
3220
3221 uiBut *but = uiDefBut(&block,
3223 0,
3224 showname,
3226 int(rct.ymax - NODE_DY),
3227 short(iconofs - rct.xmin - NODE_MARGIN_X),
3228 NODE_DY,
3229 nullptr,
3230 0,
3231 0,
3232 std::nullopt);
3233 node_header_custom_tooltip(node, *but);
3234
3235 if (node.is_muted()) {
3237 }
3238
3239 /* Wire across the node when muted/disabled. */
3240 if (node.is_muted()) {
3241 node_draw_mute_line(C, v2d, snode, node);
3242 }
3243
3244 /* Body. */
3245 {
3246 /* Use warning color to indicate undefined types. */
3247 if (node_undefined_or_unsupported(ntree, node)) {
3249 }
3250 else if (node.flag & NODE_CUSTOM_COLOR) {
3251 rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], 1.0f);
3252 }
3253 else {
3255 }
3256
3257 /* Draw selected nodes fully opaque. */
3258 if (node.flag & SELECT) {
3259 color[3] = 1.0f;
3260 }
3261
3262 /* Muted nodes get a mix of the background with the node color and are drawn slightly
3263 * transparent so the wires inside are visible. */
3264 if (node.is_muted()) {
3265 float color_background[4];
3266 UI_GetThemeColor4fv(TH_BACK, color_background);
3267
3268 UI_GetColorPtrBlendAlpha4fv(color, color_background, 0.8f, -0.2f, color);
3269 }
3270
3271 /* Add some padding to prevent transparent gaps with the outline. */
3272 const rctf rect = {
3273 rct.xmin - padding,
3274 rct.xmax + padding,
3275 rct.ymin - padding,
3276 rct.ymax - NODE_DY + padding,
3277 };
3278
3279 /* Node Group indicator. */
3280 if (draw_node_details(snode)) {
3281 node_draw_node_group_indicator(snode, node, rect, corner_radius, color);
3282 }
3283
3285 UI_draw_roundbox_4fv(&rect, true, corner_radius, color);
3286
3287 if (is_node_panels_supported(node)) {
3289 }
3290 }
3291
3292 /* Outline around the entire node to highlight selection, alert, or for simulation zones. */
3293 {
3294 const rctf rect_node = {
3295 rct.xmin - outline_width,
3296 rct.xmax + outline_width,
3297 rct.ymin - outline_width,
3298 rct.ymax + outline_width,
3299 };
3300 float color_outline[4] = {0.0f, 0.0f, 0.0f, 1.0f};
3301 if (node.flag & SELECT) {
3302 UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
3303 }
3304 else if (node_undefined_or_unsupported(ntree, node)) {
3305 UI_GetThemeColor4fv(TH_REDALERT, color_outline);
3306 }
3307 else if (const bke::bNodeZoneType *zone_type = bke::zone_type_by_node_type(node.type_legacy)) {
3308 UI_GetThemeColor4fv(zone_type->theme_id, color_outline);
3309 color_outline[3] = 1.0f;
3310 }
3311 else {
3312 UI_GetThemeColor4fv(TH_NODE_OUTLINE, color_outline);
3313 }
3315 UI_draw_roundbox_4fv(&rect_node, false, BASIS_RAD + outline_width, color_outline);
3316 }
3317
3318 /* Skip slow socket drawing if zoom is small. */
3319 if (draw_node_details(snode)) {
3320 node_draw_sockets(C, block, snode, ntree, node);
3321 }
3322
3323 if (is_node_panels_supported(node)) {
3324 node_draw_panels(ntree, node, block);
3325 }
3326
3328 tree_draw_ctx.bmain,
3329 tree_draw_ctx.window,
3330 tree_draw_ctx.scene,
3331 tree_draw_ctx.region,
3332 tree_draw_ctx.depsgraph,
3333 &block);
3334 UI_block_draw(&C, &block);
3335}
3336
3337static void node_draw_collapsed(const bContext &C,
3338 TreeDrawContext &tree_draw_ctx,
3339 const View2D &v2d,
3340 const SpaceNode &snode,
3341 bNodeTree &ntree,
3342 bNode &node,
3343 uiBlock &block)
3344{
3345 const rctf &rct = node.runtime->draw_bounds;
3346 float centy = BLI_rctf_cent_y(&rct);
3347
3348 float scale;
3349 UI_view2d_scale_get(&v2d, &scale, nullptr);
3350
3351 const int color_id = node_get_colorid(tree_draw_ctx, node);
3352
3353 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
3354
3355 /* Shadow. */
3356 node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
3357
3358 /* Wire across the node when muted/disabled. */
3359 if (node.is_muted()) {
3360 node_draw_mute_line(C, v2d, snode, node);
3361 }
3362
3363 /* Body. */
3364 ColorTheme4f color = node_header_color_get(ntree, node, color_id);
3365 {
3366 /* Add some padding to prevent transparent gaps with the outline. */
3367 const float padding = 0.5f;
3368 const rctf rect = {
3369 rct.xmin - padding,
3370 rct.xmax + padding,
3371 rct.ymin - padding,
3372 rct.ymax + padding,
3373 };
3374
3375 /* Node Group indicator. */
3376 if (draw_node_details(snode)) {
3378 }
3379
3382 }
3383
3384 /* Title. */
3385 if (node.flag & SELECT) {
3387 }
3388 else {
3389 UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
3390 }
3391
3392 /* Collapse/expand icon. */
3393 {
3394 const int but_size = U.widget_unit * 1.0f;
3396
3397 uiBut *but = uiDefIconBut(&block,
3399 0,
3400 ICON_RIGHTARROW,
3401 rct.xmin + (NODE_MARGIN_X / 3),
3402 centy - but_size / 2,
3403 but_size,
3404 but_size,
3405 nullptr,
3406 0.0f,
3407 0.0f,
3408 "");
3409
3410 UI_but_func_set(but,
3413 (void *)"NODE_OT_hide_toggle");
3415 }
3416
3417 const std::string showname = bke::node_label(ntree, node);
3418
3419 uiBut *but = uiDefBut(&block,
3421 0,
3422 showname,
3424 round_fl_to_int(centy - NODE_DY * 0.5f),
3425 short(BLI_rctf_size_x(&rct) - (2 * U.widget_unit)),
3426 NODE_DY,
3427 nullptr,
3428 0,
3429 0,
3430 std::nullopt);
3431 node_header_custom_tooltip(node, *but);
3432
3433 /* Outline. */
3434 {
3435 const float outline_width = U.pixelsize;
3436 const rctf rect = {
3437 rct.xmin - outline_width,
3438 rct.xmax + outline_width,
3439 rct.ymin - outline_width,
3440 rct.ymax + outline_width,
3441 };
3442
3443 /* Color the outline according to active, selected, or undefined status. */
3444 float color_outline[4];
3445
3446 if (node.flag & SELECT) {
3447 UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
3448 }
3449 else if (node_undefined_or_unsupported(ntree, node)) {
3450 UI_GetThemeColor4fv(TH_REDALERT, color_outline);
3451 }
3452 else if (node.is_muted()) {
3453 /* Muted nodes get a mix of the background with the node color. */
3454 UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, .4f, 10, color_outline);
3455 }
3456 else {
3457 UI_GetThemeColor4fv(TH_NODE_OUTLINE, color_outline);
3458 }
3459
3461 UI_draw_roundbox_4fv(&rect, false, BASIS_RAD + outline_width, color_outline);
3462 }
3463
3464 if (node.is_muted()) {
3466 }
3467
3468 /* Scale widget thing. */
3470 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
3473
3475 float dx = 0.5f * U.widget_unit;
3476 const float dx2 = 0.15f * U.widget_unit * snode.runtime->aspect;
3477 const float dy = 0.2f * U.widget_unit;
3478
3480 immVertex2f(pos, rct.xmax - dx, centy - dy);
3481 immVertex2f(pos, rct.xmax - dx, centy + dy);
3482
3483 immVertex2f(pos, rct.xmax - dx - dx2, centy - dy);
3484 immVertex2f(pos, rct.xmax - dx - dx2, centy + dy);
3485 immEnd();
3486
3488 dx -= snode.runtime->aspect;
3489
3491 immVertex2f(pos, rct.xmax - dx, centy - dy);
3492 immVertex2f(pos, rct.xmax - dx, centy + dy);
3493
3494 immVertex2f(pos, rct.xmax - dx - dx2, centy - dy);
3495 immVertex2f(pos, rct.xmax - dx - dx2, centy + dy);
3496 immEnd();
3497
3500
3501 node_draw_sockets(C, block, snode, ntree, node);
3502
3504 tree_draw_ctx.bmain,
3505 tree_draw_ctx.window,
3506 tree_draw_ctx.scene,
3507 tree_draw_ctx.region,
3508 tree_draw_ctx.depsgraph,
3509 &block);
3510 UI_block_draw(&C, &block);
3511}
3512
3514{
3515 if (directions == 0) {
3516 return WM_CURSOR_DEFAULT;
3517 }
3518 if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0) {
3519 return WM_CURSOR_Y_MOVE;
3520 }
3521 if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0) {
3522 return WM_CURSOR_X_MOVE;
3523 }
3524 return WM_CURSOR_EDIT;
3525}
3526
3527static const bNode *find_node_under_cursor(SpaceNode &snode, const float2 &cursor)
3528{
3529 for (const bNode *node : tree_draw_order_calc_nodes_reversed(*snode.edittree)) {
3530 if (BLI_rctf_isect_pt(&node->runtime->draw_bounds, cursor[0], cursor[1])) {
3531 return node;
3532 }
3533 }
3534 return nullptr;
3535}
3536
3537void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
3538{
3539 const bNodeTree *ntree = snode.edittree;
3540 if (ntree == nullptr) {
3542 return;
3543 }
3544 if (node_find_indicated_socket(snode, region, cursor, SOCK_IN | SOCK_OUT)) {
3546 return;
3547 }
3548 const bNode *node = find_node_under_cursor(snode, cursor);
3549 if (!node) {
3551 return;
3552 }
3553 const NodeResizeDirection dir = node_get_resize_direction(snode, node, cursor[0], cursor[1]);
3554 if (node->is_frame() && dir == NODE_RESIZE_NONE) {
3555 /* Indicate that frame nodes can be moved/selected on their borders. */
3556 const rctf frame_inside = node_frame_rect_inside(snode, *node);
3557 if (!BLI_rctf_isect_pt(&frame_inside, cursor[0], cursor[1])) {
3559 return;
3560 }
3562 return;
3563 }
3564
3566}
3567
3569{
3570 for (bNode *node : ntree.all_nodes()) {
3571 for (bNodeSocket *socket : node->input_sockets()) {
3572 if (socket->is_multi_input()) {
3573 socket->runtime->total_inputs = socket->directly_linked_links().size();
3574 }
3575 }
3576 }
3577 /* Count temporary links going into this socket. */
3578 if (snode.runtime->linkdrag) {
3579 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
3580 if (link.tosock && (link.tosock->flag & SOCK_MULTI_INPUT)) {
3581 link.tosock->runtime->total_inputs++;
3582 }
3583 }
3584 }
3585}
3586
3588 float margin = 0;
3589 float margin_top = 0;
3590 float label_height = 0;
3592 bool has_label = false;
3593};
3594
3595static FrameNodeLayout frame_node_layout(const bNode &frame_node)
3596{
3597 BLI_assert(frame_node.is_frame());
3598
3599 const NodeFrame *frame_data = (NodeFrame *)frame_node.storage;
3600
3601 FrameNodeLayout frame_layout;
3602
3603 frame_layout.has_label = frame_node.label[0] != '\0';
3604
3605 /* This is not the actual height of the letters in the label, but an approximation that includes
3606 * some of the white-space above and below the actual letters. */
3607 frame_layout.label_height = frame_data->label_size * UI_SCALE_FAC;
3608
3609 /* The side and bottom margins are 50% bigger than the widget unit */
3610 frame_layout.margin = 1.5f * U.widget_unit;
3611
3612 if (frame_layout.has_label) {
3613 /* The label takes up 1.5 times the label height plus 0.2 times the margin.
3614 * These coefficients are selected to provide good layout and spacing for the descenders. */
3615 float room_for_label = 1.5f * frame_layout.label_height + 0.2f * frame_layout.margin;
3616
3617 /* Make top margin bigger, if needed for the label, but never smaller than the side margins. */
3618 frame_layout.margin_top = std::max(frame_layout.margin, room_for_label);
3619
3620 /* This adjustment approximately centers the cap height in the margin.
3621 * This is achieved by finding the y value that is the center of the top margin, then lowering
3622 * that by 35% of the label height. Since font cap heights are typically about 70% of the total
3623 * line height, moving the text by half that achieves rough centering. */
3624 frame_layout.label_baseline = 0.5f * frame_layout.margin_top +
3625 0.35f * frame_layout.label_height;
3626 }
3627 else {
3628 /* If there is no label, the top margin is the same as the sides. */
3629 frame_layout.margin_top = frame_layout.margin;
3630 frame_layout.label_baseline = 0;
3631 }
3632
3633 return frame_layout;
3634}
3635
3643 TreeDrawContext &tree_draw_ctx,
3644 const SpaceNode &snode,
3645 bNode &node)
3646{
3647 if (!node.is_frame()) {
3648 rctf node_bounds = node.runtime->draw_bounds;
3649
3650 float zone_padding = 0;
3651 float extra_row_padding = 0;
3652
3653 /* Pad if the node type is a zone input or output. */
3654 if (bke::zone_type_by_node_type(node.type_legacy) != nullptr) {
3655 zone_padding = NODE_ZONE_PADDING;
3656 }
3657
3658 /* Compute the height of the info row for each node, which may vary per child node.
3659 * This has to get the full extra_rows information (including all the text strings), even
3660 * though all that's actually needed is the count of how many info_rows there are. */
3662 extra_row_padding = tree_draw_ctx.extra_info_rows_per_node[node.index()].size() *
3664 }
3665
3666 node_bounds.ymax += std::max(zone_padding, extra_row_padding);
3667 node_bounds.ymin -= zone_padding;
3668
3669 return node_bounds;
3670 }
3671
3672 NodeFrame *data = (NodeFrame *)node.storage;
3673
3674 const FrameNodeLayout frame_layout = frame_node_layout(node);
3675
3676 /* Initialize rect from current frame size. */
3677 rctf rect;
3678 node_to_updated_rect(node, rect);
3679
3680 /* Frame can be resized manually only if shrinking is disabled or no children are attached. */
3681 data->flag |= NODE_FRAME_RESIZEABLE;
3682 /* For shrinking bounding box, initialize the rect from first child node. */
3683 bool bbinit = (data->flag & NODE_FRAME_SHRINK);
3684 /* Fit bounding box to all children. */
3685 for (bNode *child : node.direct_children_in_frame()) {
3686 /* Add margin to node rect. */
3687 rctf noderect = calc_node_frame_dimensions(C, tree_draw_ctx, snode, *child);
3688
3689 noderect.xmin -= frame_layout.margin;
3690 noderect.xmax += frame_layout.margin;
3691 noderect.ymin -= frame_layout.margin;
3692 noderect.ymax += frame_layout.margin_top;
3693
3694 /* First child initializes frame. */
3695 if (bbinit) {
3696 bbinit = false;
3697 rect = noderect;
3699 }
3700 else {
3701 BLI_rctf_union(&rect, &noderect);
3702 }
3703 }
3704
3705 /* Now adjust the frame size from view-space bounding box. */
3706 const float2 min = node_from_view({rect.xmin, rect.ymin});
3707 const float2 max = node_from_view({rect.xmax, rect.ymax});
3708 node.location[0] = min.x;
3709 node.location[1] = max.y;
3710 node.width = max.x - min.x;
3711 node.height = max.y - min.y;
3712
3713 node.runtime->draw_bounds = rect;
3714 return rect;
3715}
3716
3718{
3719 const float2 loc = node_to_view(node.location);
3720
3721 /* When the node is collapsed, the input and output socket are both in the same place. */
3722 node.input_socket(0).runtime->location = loc;
3723 node.output_socket(0).runtime->location = loc;
3724
3725 const float radius = NODE_SOCKSIZE;
3726 node.width = radius * 2;
3727 node.runtime->draw_bounds.xmin = loc.x - radius;
3728 node.runtime->draw_bounds.xmax = loc.x + radius;
3729 node.runtime->draw_bounds.ymax = loc.y + radius;
3730 node.runtime->draw_bounds.ymin = loc.y - radius;
3731}
3732
3734 TreeDrawContext &tree_draw_ctx,
3735 bNodeTree &ntree,
3737 Span<uiBlock *> blocks)
3738{
3739 /* Make sure socket "used" tags are correct, for displaying value buttons. */
3740 SpaceNode *snode = CTX_wm_space_node(&C);
3741
3742 count_multi_input_socket_links(ntree, *snode);
3743
3744 for (const int i : nodes.index_range()) {
3745 bNode &node = *nodes[i];
3746 uiBlock &block = *blocks[node.index()];
3747 if (node.is_frame()) {
3748 /* Frame sizes are calculated after all other nodes have calculating their #draw_bounds. */
3749 continue;
3750 }
3751
3752 if (node.is_reroute()) {
3754 }
3755 else {
3756 if (node.flag & NODE_COLLAPSED) {
3757 node_update_collapsed(node, block);
3758 }
3759 else {
3760 node_update_basis(C, tree_draw_ctx, ntree, node, block);
3761 }
3762 }
3763 }
3764
3765 /* Now calculate the size of frame nodes, which can depend on the size of other nodes. */
3766 for (bNode *frame : ntree.root_frames()) {
3767 calc_node_frame_dimensions(C, tree_draw_ctx, *snode, *frame);
3768 }
3769}
3770
3771static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx,
3772 const bNode &node,
3773 const SpaceNode &snode)
3774{
3775 /* XXX font id is crap design */
3776 const int fontid = UI_style_get()->widget.uifont_id;
3777 const NodeFrame *data = (const NodeFrame *)node.storage;
3778
3779 /* Setting BLF_aspect() and then counter-scaling by aspect in BLF_size() has no effect on the
3780 * rendered text size, because the two adjustments cancel each other out. But, using aspect
3781 * renders the text at higher resolution, which sharpens the rasterization of the text. */
3782 const float aspect = snode.runtime->aspect;
3783 BLF_enable(fontid, BLF_ASPECT);
3784 BLF_aspect(fontid, aspect, aspect, 1.0f);
3785 BLF_size(fontid, data->label_size * UI_SCALE_FAC / aspect);
3786
3787 const FrameNodeLayout frame_layout = frame_node_layout(node);
3788
3789 /* Title color. */
3790 int color_id = node_get_colorid(tree_draw_ctx, node);
3791 uchar color[3];
3792 UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color);
3793 BLF_color3ubv(fontid, color);
3794
3795 const float label_width = BLF_width(fontid, node.label, strlen(node.label));
3796
3797 const rctf &rct = node.runtime->draw_bounds;
3798 const float label_x = BLI_rctf_cent_x(&rct) - (0.5f * label_width);
3799 const float label_y = rct.ymax - frame_layout.label_baseline;
3800
3801 /* Label. */
3802 if (frame_layout.has_label) {
3803 BLF_position(fontid, label_x, label_y, 0);
3804 BLF_draw(fontid, node.label, strlen(node.label));
3805 }
3806
3807 /* Draw text body. */
3808 if (node.id) {
3809 const Text *text = (const Text *)node.id;
3810 const float line_spacing = BLF_height_max(fontid) * aspect;
3811 const float line_width = (BLI_rctf_size_x(&rct) - 2 * frame_layout.margin) / aspect;
3812
3813 const float x = rct.xmin + frame_layout.margin;
3814 float y = rct.ymax - frame_layout.label_height -
3815 (frame_layout.has_label ? line_spacing + frame_layout.margin : 0);
3816
3817 const int y_min = rct.ymin + frame_layout.margin;
3818
3820 BLF_clipping(fontid, rct.xmin, rct.ymin + frame_layout.margin, rct.xmax, rct.ymax);
3821
3822 BLF_wordwrap(fontid, line_width);
3823
3824 LISTBASE_FOREACH (const TextLine *, line, &text->lines) {
3825 if (line->line[0]) {
3826 BLF_position(fontid, x, y, 0);
3827 ResultBLF info;
3828 BLF_draw(fontid, line->line, line->len, &info);
3829 y -= line_spacing * info.lines;
3830 }
3831 else {
3832 y -= line_spacing;
3833 }
3834 if (y < y_min) {
3835 break;
3836 }
3837 }
3838
3840 }
3841
3842 BLF_disable(fontid, BLF_ASPECT);
3843}
3844
3845static void frame_node_draw_background(const ARegion &region,
3846 const SpaceNode &snode,
3847 const bNode &node)
3848{
3849 /* Skip if out of view. */
3850 if (BLI_rctf_isect(&node.runtime->draw_bounds, &region.v2d.cur, nullptr) == false) {
3851 return;
3852 }
3853
3854 float color[4];
3856 const float alpha = color[3];
3857
3858 node_draw_shadow(snode, node, BASIS_RAD, alpha);
3859
3860 if (node.flag & NODE_CUSTOM_COLOR) {
3861 rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], alpha);
3862 }
3863 else {
3864 int depth = 0;
3865 for (const bNode *parent = node.parent; parent; parent = parent->parent) {
3866 depth++;
3867 }
3868
3869 if (depth % 2 == 0) {
3871 }
3872 else {
3874 }
3875 }
3876
3877 const rctf &rct = node.runtime->draw_bounds;
3879 UI_draw_roundbox_4fv(&rct, true, BASIS_RAD, color);
3880}
3881
3882static void frame_node_draw_outline(const ARegion &region,
3883 const SpaceNode &snode,
3884 const bNode &node)
3885{
3886 /* Skip if out of view. */
3887 const rctf &rct = node.runtime->draw_bounds;
3888 if (BLI_rctf_isect(&rct, &region.v2d.cur, nullptr) == false) {
3889 return;
3890 }
3891
3892 ColorTheme4f outline_color;
3893 bool draw_outline = false;
3894
3896 draw_outline = true;
3897 UI_GetThemeColorShadeAlpha4fv(TH_ACTIVE, 0, -100, outline_color);
3898 }
3899 else if (node.flag & SELECT) {
3900 draw_outline = true;
3901 if (node.flag & NODE_ACTIVE) {
3902 UI_GetThemeColorShadeAlpha4fv(TH_ACTIVE, 0, -40, outline_color);
3903 }
3904 else {
3905 UI_GetThemeColorShadeAlpha4fv(TH_SELECT, 0, -40, outline_color);
3906 }
3907 }
3908
3909 if (draw_outline) {
3910 UI_draw_roundbox_aa(&rct, false, BASIS_RAD, outline_color);
3911 }
3912}
3913
3915 TreeDrawContext &tree_draw_ctx,
3916 const ARegion &region,
3917 const SpaceNode &snode,
3918 const bNode &node,
3919 uiBlock &block)
3920{
3921 /* Skip if out of view. */
3922 if (BLI_rctf_isect(&node.runtime->draw_bounds, &region.v2d.cur, nullptr) == false) {
3924 tree_draw_ctx.bmain,
3925 tree_draw_ctx.window,
3926 tree_draw_ctx.scene,
3927 tree_draw_ctx.region,
3928 tree_draw_ctx.depsgraph,
3929 &block);
3930 return;
3931 }
3932
3933 /* Label and text. */
3934 frame_node_draw_label(tree_draw_ctx, node, snode);
3935
3936 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
3937
3939 tree_draw_ctx.bmain,
3940 tree_draw_ctx.window,
3941 tree_draw_ctx.scene,
3942 tree_draw_ctx.region,
3943 tree_draw_ctx.depsgraph,
3944 &block);
3945 UI_block_draw(&C, &block);
3946}
3947
3949 const bContext &C, const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
3950{
3951 const std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
3953 if (!object_and_modifier) {
3954 return {};
3955 }
3956 snode.edittree->ensure_topology_cache();
3957
3958 const ComputeContext *current_compute_context = ed::space_node::compute_context_for_edittree(
3959 snode, compute_context_cache);
3960 if (!current_compute_context) {
3961 return {};
3962 }
3963
3964 Set<const bNodeSocket *> sockets_on_gizmo_paths;
3965
3967 C,
3968 compute_context_cache,
3969 [&](const Object &gizmo_object,
3970 const NodesModifierData &gizmo_nmd,
3971 const ComputeContext &gizmo_context,
3972 const bNode &gizmo_node,
3973 const bNodeSocket &gizmo_socket) {
3974 if (&gizmo_object != object_and_modifier->object) {
3975 return;
3976 }
3977 if (&gizmo_nmd != object_and_modifier->nmd) {
3978 return;
3979 }
3981 gizmo_context,
3982 gizmo_node,
3983 gizmo_socket,
3984 [&](const ComputeContext &compute_context,
3985 const bNodeSocket &socket,
3986 const nodes::inverse_eval::ElemVariant & /*elem*/) {
3987 if (compute_context.hash() == current_compute_context->hash()) {
3988 sockets_on_gizmo_paths.add(&socket);
3989 }
3990 });
3991 });
3992
3993 return sockets_on_gizmo_paths;
3994}
3995
3999static const bNode *reroute_node_get_linked_reroute(const bNode &reroute)
4000{
4001 BLI_assert(reroute.is_reroute());
4002
4003 const bNodeSocket *input_socket = reroute.input_sockets().first();
4004 if (input_socket->directly_linked_links().is_empty()) {
4005 return nullptr;
4006 }
4007 const bNodeLink *input_link = input_socket->directly_linked_links().first();
4008 const bNode *from_node = input_link->fromnode;
4009 return from_node->is_reroute() ? from_node : nullptr;
4010}
4011
4017 const bNode &src_reroute)
4018{
4019 BLI_assert(src_reroute.is_reroute());
4020
4021 if (src_reroute.label[0] != '\0') {
4022 return src_reroute.label;
4023 }
4024
4025 Map<const bNode *, StringRef> &reroute_auto_labels = tree_draw_ctx.reroute_auto_labels;
4026
4027 StringRef label;
4028 Vector<const bNode *> reroute_path;
4029
4030 /* Traverse reroute path backwards until label, non-reroute node or link-cycle is found. */
4031 for (const bNode *reroute = &src_reroute; reroute;
4032 reroute = reroute_node_get_linked_reroute(*reroute))
4033 {
4034 reroute_path.append(reroute);
4035 if (const StringRef *label_ptr = reroute_auto_labels.lookup_ptr(reroute)) {
4036 label = *label_ptr;
4037 break;
4038 }
4039 if (reroute->label[0] != '\0') {
4040 label = reroute->label;
4041 break;
4042 }
4043 /* This makes sure that the loop eventually ends even if there are link-cycles. */
4044 reroute_auto_labels.add(reroute, "");
4045 }
4046
4047 /* Remember the label for each node on the path to avoid recomputing it. */
4048 for (const bNode *reroute : reroute_path) {
4049 reroute_auto_labels.add_overwrite(reroute, label);
4050 }
4051
4052 return label;
4053}
4054
4056 const SpaceNode &snode,
4057 const bNodeTree &ntree,
4058 const bNode &node,
4059 uiBlock &block,
4060 const bool selected)
4061{
4062 BLI_assert(node.is_reroute());
4063
4064 bNodeSocket &sock = *static_cast<bNodeSocket *>(node.inputs.first);
4065
4067 const_cast<ID *>(&ntree.id), &RNA_Node, const_cast<bNode *>(&node));
4068
4069 ColorTheme4f socket_color;
4070 ColorTheme4f outline_color;
4071
4072 node_socket_color_get(C, ntree, nodeptr, sock, socket_color);
4073 node_socket_outline_color_get(selected, sock.type, outline_color);
4074
4075 node_draw_nodesocket(&node.runtime->draw_bounds,
4076 socket_color,
4077 outline_color,
4079 sock.display_shape,
4080 snode.runtime->aspect);
4081
4082 const float2 location = float2(BLI_rctf_cent_x(&node.runtime->draw_bounds),
4083 BLI_rctf_cent_y(&node.runtime->draw_bounds));
4084 const float2 size = float2(BLI_rctf_size_x(&node.runtime->draw_bounds),
4085 BLI_rctf_size_y(&node.runtime->draw_bounds));
4086 node_socket_tooltip_set(block, sock.index_in_tree(), location, size);
4087}
4088
4089static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx,
4090 const SpaceNode &snode,
4091 const bNode &node,
4092 uiBlock &block)
4093{
4094 const bool has_label = node.label[0] != '\0';
4095 const bool use_auto_label = !has_label && (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) &&
4097
4098 if (!has_label && !use_auto_label) {
4099 return;
4100 }
4101
4102 /* Don't show the automatic label, when being zoomed out. */
4103 if (!has_label && !draw_node_details(snode)) {
4104 return;
4105 }
4106
4107 const StringRef text = has_label ? node.label : reroute_node_get_auto_label(tree_draw_ctx, node);
4108 if (text.is_empty()) {
4109 return;
4110 }
4111
4112 const short width = 512;
4113 const int x = BLI_rctf_cent_x(&node.runtime->draw_bounds) - (width / 2);
4114 const int y = node.runtime->draw_bounds.ymax;
4115
4116 uiBut *label_but = uiDefBut(
4117 &block, ButType::Label, 0, text, x, y, width, NODE_DY, nullptr, 0, 0, std::nullopt);
4118
4120
4121 if (use_auto_label && !(node.flag & NODE_SELECT)) {
4123 }
4124}
4125
4126static void reroute_node_draw(const bContext &C,
4127 TreeDrawContext &tree_draw_ctx,
4128 ARegion &region,
4129 const SpaceNode &snode,
4130 bNodeTree &ntree,
4131 const bNode &node,
4132 uiBlock &block)
4133{
4134 const rctf &rct = node.runtime->draw_bounds;
4135 const View2D &v2d = region.v2d;
4136
4137 /* Skip if out of view. */
4138 if (rct.xmax < v2d.cur.xmin || rct.xmin > v2d.cur.xmax || rct.ymax < v2d.cur.ymin ||
4139 node.runtime->draw_bounds.ymin > v2d.cur.ymax)
4140 {
4142 tree_draw_ctx.bmain,
4143 tree_draw_ctx.window,
4144 tree_draw_ctx.scene,
4145 tree_draw_ctx.region,
4146 tree_draw_ctx.depsgraph,
4147 &block);
4148 return;
4149 }
4150
4151 if (draw_node_details(snode)) {
4152 reroute_node_draw_label(tree_draw_ctx, snode, node, block);
4153 }
4154
4155 /* Only draw the input socket, since all sockets are at the same location. */
4156 const bool selected = node.flag & NODE_SELECT;
4157 reroute_node_draw_body(C, snode, ntree, node, block, selected);
4158
4160 tree_draw_ctx.bmain,
4161 tree_draw_ctx.window,
4162 tree_draw_ctx.scene,
4163 tree_draw_ctx.region,
4164 tree_draw_ctx.depsgraph,
4165 &block);
4166 UI_block_draw(&C, &block);
4167}
4168
4169static void node_draw(const bContext &C,
4170 TreeDrawContext &tree_draw_ctx,
4171 ARegion &region,
4172 const SpaceNode &snode,
4173 bNodeTree &ntree,
4174 bNode &node,
4175 uiBlock &block,
4176 bNodeInstanceKey key)
4177{
4178 if (node.is_frame()) {
4179 /* Should have been drawn before already. */
4181 }
4182 else if (node.is_reroute()) {
4183 reroute_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block);
4184 }
4185 else {
4186 const View2D &v2d = region.v2d;
4187 if (node.flag & NODE_COLLAPSED) {
4188 node_draw_collapsed(C, tree_draw_ctx, v2d, snode, ntree, node, block);
4189 }
4190 else {
4191 node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, node, block, key);
4192 }
4193 }
4194}
4195
4196static void add_rect_corner_positions(Vector<float2> &positions, const rctf &rect)
4197{
4198 positions.append({rect.xmin, rect.ymin});
4199 positions.append({rect.xmin, rect.ymax});
4200 positions.append({rect.xmax, rect.ymin});
4201 positions.append({rect.xmax, rect.ymax});
4202}
4203
4205 const bNodeTreeZone &zone,
4206 const Span<const bNodeTreeZone *> all_zones,
4207 MutableSpan<Vector<float2>> r_bounds_by_zone)
4208{
4209 const float node_padding = NODE_ZONE_PADDING;
4210 const float zone_padding = ZONE_ZONE_PADDING;
4211
4212 Vector<float2> &bounds = r_bounds_by_zone[zone.index];
4213 if (!bounds.is_empty()) {
4214 return;
4215 }
4216
4217 Vector<float2> possible_bounds;
4218 for (const bNodeTreeZone *child_zone : zone.child_zones) {
4219 find_bounds_by_zone_recursive(snode, *child_zone, all_zones, r_bounds_by_zone);
4220 const Span<float2> child_bounds = r_bounds_by_zone[child_zone->index];
4221 for (const float2 &pos : child_bounds) {
4222 rctf rect;
4223 BLI_rctf_init_pt_radius(&rect, pos, zone_padding);
4224 add_rect_corner_positions(possible_bounds, rect);
4225 }
4226 }
4227 for (const int child_node_id : zone.child_node_ids) {
4228 const bNode *child_node = snode.edittree->node_by_id(child_node_id);
4229 if (!child_node) {
4230 /* Can happen when drawing zone errors. */
4231 continue;
4232 }
4233 rctf rect = child_node->runtime->draw_bounds;
4234 BLI_rctf_pad(&rect, node_padding, node_padding);
4235 add_rect_corner_positions(possible_bounds, rect);
4236 }
4237 if (const bNode *input_node = zone.input_node()) {
4238 const rctf &draw_bounds = input_node->runtime->draw_bounds;
4239 rctf rect = draw_bounds;
4240 BLI_rctf_pad(&rect, node_padding, node_padding);
4241 rect.xmin = math::interpolate(draw_bounds.xmin, draw_bounds.xmax, 0.25f);
4242 add_rect_corner_positions(possible_bounds, rect);
4243 }
4244 if (const bNode *output_node = zone.output_node()) {
4245 const rctf &draw_bounds = output_node->runtime->draw_bounds;
4246 rctf rect = draw_bounds;
4247 BLI_rctf_pad(&rect, node_padding, node_padding);
4248 rect.xmax = math::interpolate(draw_bounds.xmin, draw_bounds.xmax, 0.75f);
4249 add_rect_corner_positions(possible_bounds, rect);
4250 }
4251
4252 if (snode.runtime->linkdrag) {
4253 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
4254 if (link.fromnode == nullptr) {
4255 continue;
4256 }
4257 if (zone.contains_node_recursively(*link.fromnode) &&
4258 zone.output_node_id != link.fromnode->identifier)
4259 {
4260 const float2 pos = node_link_bezier_points_dragged(snode, link)[3];
4261 rctf rect;
4262 BLI_rctf_init_pt_radius(&rect, pos, node_padding);
4263 add_rect_corner_positions(possible_bounds, rect);
4264 }
4265 }
4266 }
4267
4268 Vector<int> convex_indices(possible_bounds.size());
4269 const int convex_positions_num = BLI_convexhull_2d(possible_bounds, convex_indices.data());
4270 convex_indices.resize(convex_positions_num);
4271
4272 for (const int i : convex_indices) {
4273 bounds.append(possible_bounds[i]);
4274 }
4275}
4276
4277static void node_draw_zones_and_frames(const ARegion &region,
4278 const SpaceNode &snode,
4279 const bNodeTree &ntree)
4280{
4281 const bNodeTreeZones *zones = ntree.zones();
4282 if (!zones) {
4283 /* Try use backup zones. */
4284 zones = ntree.runtime->last_valid_zones.get();
4285 }
4286 const int zones_num = zones ? zones->zones.size() : 0;
4287
4288 Array<Vector<float2>> bounds_by_zone(zones_num);
4289 Array<std::optional<bke::CurvesGeometry>> fillet_curve_by_zone(zones_num);
4290 /* Bounding box area of zones is used to determine draw order. */
4291 Array<float> bounding_box_width_by_zone(zones_num);
4292
4293 for (const int zone_i : IndexRange(zones_num)) {
4294 const bNodeTreeZone &zone = *zones->zones[zone_i];
4295
4296 find_bounds_by_zone_recursive(snode, zone, zones->zones, bounds_by_zone);
4297 const Span<float2> boundary_positions = bounds_by_zone[zone_i];
4298 const int boundary_positions_num = boundary_positions.size();
4299 if (boundary_positions_num < 3) {
4300 /* Can happen when drawing zone errors. */
4301 continue;
4302 }
4303
4304 const Bounds<float2> bounding_box = *bounds::min_max(boundary_positions);
4305 const float bounding_box_width = bounding_box.max.x - bounding_box.min.x;
4306 bounding_box_width_by_zone[zone_i] = bounding_box_width;
4307
4308 bke::CurvesGeometry boundary_curve(boundary_positions_num, 1);
4309 boundary_curve.cyclic_for_write().first() = true;
4310 boundary_curve.fill_curve_types(CURVE_TYPE_POLY);
4311 MutableSpan<float3> boundary_curve_positions = boundary_curve.positions_for_write();
4312 boundary_curve.offsets_for_write().copy_from({0, boundary_positions_num});
4313 for (const int i : boundary_positions.index_range()) {
4314 boundary_curve_positions[i] = float3(boundary_positions[i], 0.0f);
4315 }
4316
4317 fillet_curve_by_zone[zone_i] = geometry::fillet_curves_poly(
4318 boundary_curve,
4319 IndexRange(1),
4320 VArray<float>::from_single(BASIS_RAD, boundary_positions_num),
4321 VArray<int>::from_single(5, boundary_positions_num),
4322 true,
4323 {});
4324 }
4325
4326 const View2D &v2d = region.v2d;
4327 float scale;
4328 UI_view2d_scale_get(&v2d, &scale, nullptr);
4329 float line_width = 1.0f * scale;
4330 float viewport[4] = {};
4331 GPU_viewport_size_get_f(viewport);
4332
4333 const auto get_theme_id = [&](const int zone_i) {
4334 const bNode *node = zones->zones[zone_i]->output_node();
4335 if (!node) {
4336 return TH_REDALERT;
4337 }
4338 return ThemeColorID(bke::zone_type_by_node_type(node->type_legacy)->theme_id);
4339 };
4340
4342 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
4343
4344 using ZoneOrNode = std::variant<const bNodeTreeZone *, const bNode *>;
4345 Vector<ZoneOrNode> draw_order;
4346 for (const int zone_i : IndexRange(zones_num)) {
4347 draw_order.append(zones->zones[zone_i]);
4348 }
4349 for (const bNode *node : ntree.all_nodes()) {
4350 if (node->flag & NODE_BACKGROUND) {
4351 draw_order.append(node);
4352 }
4353 }
4354 auto get_zone_or_node_width = [&](const ZoneOrNode &zone_or_node) {
4355 if (const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node)) {
4356 const bNodeTreeZone &zone = **zone_p;
4357 return bounding_box_width_by_zone[zone.index];
4358 }
4359 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4360 const bNode &node = **node_p;
4361 return BLI_rctf_size_x(&node.runtime->draw_bounds);
4362 }
4364 return 0.0f;
4365 };
4366 std::sort(draw_order.begin(), draw_order.end(), [&](const ZoneOrNode &a, const ZoneOrNode &b) {
4367 /* Draw zones with smaller bounding box on top to make them visible. */
4368 return get_zone_or_node_width(a) > get_zone_or_node_width(b);
4369 });
4370
4371 for (const ZoneOrNode &zone_or_node : draw_order) {
4372 if (const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node)) {
4373 const bNodeTreeZone &zone = **zone_p;
4374 const int zone_i = zone.index;
4375 float zone_color[4];
4376 UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color);
4377 if (zone_color[3] == 0.0f) {
4378 continue;
4379 }
4380 if (!fillet_curve_by_zone[zone_i].has_value()) {
4381 /* Can happen when drawing zone errors. */
4382 continue;
4383 }
4384 const Span<float3> fillet_boundary_positions = fillet_curve_by_zone[zone_i]->positions();
4385 /* Draw the background. */
4387 immUniformThemeColorBlend(TH_BACK, get_theme_id(zone_i), zone_color[3]);
4388
4389 immBegin(GPU_PRIM_TRI_FAN, fillet_boundary_positions.size() + 1);
4390 for (const float3 &p : fillet_boundary_positions) {
4391 immVertex3fv(pos, p);
4392 }
4393 immVertex3fv(pos, fillet_boundary_positions[0]);
4394 immEnd();
4395
4397 }
4398 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4399 const bNode &node = **node_p;
4400 frame_node_draw_background(region, snode, node);
4401 }
4402 }
4403
4405
4406 /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */
4407 for (const ZoneOrNode &zone_or_node : draw_order) {
4408 if (const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node)) {
4409 const bNodeTreeZone &zone = **zone_p;
4410 const int zone_i = zone.index;
4411 if (!fillet_curve_by_zone[zone_i].has_value()) {
4412 /* Can happen when drawing zone errors. */
4413 continue;
4414 }
4415 const Span<float3> fillet_boundary_positions = fillet_curve_by_zone[zone_i]->positions();
4416 /* Draw the contour lines. */
4418
4419 immUniform2fv("viewportSize", &viewport[2]);
4420 immUniform1f("lineWidth", line_width * U.pixelsize);
4421
4422 const ThemeColorID theme_id = ntree.runtime->invalid_zone_output_node_ids.contains(
4423 *zone.output_node_id) ?
4424 TH_REDALERT :
4425 get_theme_id(zone_i);
4426
4427 immUniformThemeColorAlpha(theme_id, 1.0f);
4428 immBegin(GPU_PRIM_LINE_STRIP, fillet_boundary_positions.size() + 1);
4429 for (const float3 &p : fillet_boundary_positions) {
4430 immVertex3fv(pos, p);
4431 }
4432 immVertex3fv(pos, fillet_boundary_positions[0]);
4433 immEnd();
4434
4436 }
4437 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4438 const bNode &node = **node_p;
4439 frame_node_draw_outline(region, snode, node);
4440 }
4441 }
4442
4444}
4445
4446static void draw_frame_overlays(const bContext &C,
4447 TreeDrawContext &tree_draw_ctx,
4448 const ARegion &region,
4449 const SpaceNode &snode,
4450 const bNodeTree &ntree,
4451 Span<uiBlock *> blocks)
4452{
4453 for (const bNode *node : ntree.nodes_by_type("NodeFrame")) {
4454 frame_node_draw_overlay(C, tree_draw_ctx, region, snode, *node, *blocks[node->index()]);
4455 }
4456}
4457
4463static std::optional<float2> find_visible_center_of_link(const View2D &v2d,
4464 const bNodeLink &link,
4465 const float radius,
4466 const float region_padding)
4467{
4468 /* Compute center of the link because that's used as "ideal" position. */
4469 const float2 start = socket_link_connection_location(*link.fromnode, *link.fromsock, link);
4470 const float2 end = socket_link_connection_location(*link.tonode, *link.tosock, link);
4471 const float2 center = math::midpoint(start, end);
4472
4473 /* The rectangle that we would like to stay within if possible. */
4474 rctf inner_rect = v2d.cur;
4475 BLI_rctf_pad(&inner_rect, -(region_padding + radius), -(region_padding + radius));
4476
4477 if (BLI_rctf_isect_pt_v(&inner_rect, center)) {
4478 /* The center is visible. */
4479 return center;
4480 }
4481
4482 /* The rectangle containing all points which are valid result positions. */
4483 rctf outer_rect = v2d.cur;
4484 BLI_rctf_pad(&outer_rect, radius, radius);
4485
4486 /* Get the straight individual link segments. */
4487 std::array<float2, NODE_LINK_RESOL + 1> link_points;
4488 node_link_bezier_points_evaluated(link, link_points);
4489
4490 const float required_socket_distance = UI_UNIT_X;
4491 /* Define a cost function that returns a value that is larger the worse the given position is.
4492 * The point on the link with the lowest cost will be picked. */
4493 const auto cost_function = [&](const float2 &p) -> float {
4494 const float distance_to_inner_rect = std::max(BLI_rctf_length_x(&inner_rect, p.x),
4495 BLI_rctf_length_y(&inner_rect, p.y));
4496 const float distance_to_center = math::distance(p, center);
4497
4498 /* Set a high cost when the point is close to a socket. The distance to the center still has to
4499 * be taken account though. Otherwise there is bad behavior when both sockets are close to the
4500 * point. */
4501 const float distance_to_socket = std::min(math::distance(p, start), math::distance(p, end));
4502 if (distance_to_socket < required_socket_distance) {
4503 return 1e5f + distance_to_center;
4504 }
4505 return
4506 /* The larger the distance to the link center, the higher the cost.
4507 * The importance of this distance decreases the further the center is away. */
4508 std::sqrt(distance_to_center)
4509 /* The larger the distance to the inner rectangle, the higher the cost. Apply an additional
4510 * factor because it's more important that the position stays visible than that it is at
4511 * the center. */
4512 + 10.0f * distance_to_inner_rect;
4513 };
4514
4515 /* Iterate over visible points on the link, compute the cost of each and pick the best one. A
4516 * more direct algorithm to find a good position would be nice. However, that seems to be
4517 * surprisingly tricky to achieve without resulting in very "jumpy" positions, especially when
4518 * the link is colinear to the region border. */
4519 float best_cost;
4520 std::optional<float2> best_position;
4521 for (const int i : IndexRange(link_points.size() - 1)) {
4522 float2 p0 = link_points[i];
4523 float2 p1 = link_points[i + 1];
4524 if (!BLI_rctf_clamp_segment(&outer_rect, p0, p1)) {
4525 continue;
4526 }
4527 const float length = math::distance(p0, p1);
4528 const float point_distance = 1.0f;
4529 /* Might be possible to do a smarter scan of the cost function using some sort of binary sort,
4530 * but it's not entirely straight forward because the cost function is not monotonic. */
4531 const int points_to_check = std::max(2, 1 + int(length / point_distance));
4532 for (const int j : IndexRange(points_to_check)) {
4533 const float t = float(j) / (points_to_check - 1);
4534 const float2 p = math::interpolate(p0, p1, t);
4535 const float cost = cost_function(p);
4536 if (!best_position.has_value() || cost < best_cost) {
4537 best_cost = cost;
4538 best_position = p;
4539 }
4540 }
4541 }
4542 return best_position;
4543}
4544
4545static void draw_link_errors(const bContext &C,
4546 SpaceNode &snode,
4547 const bNodeLink &link,
4548 const Span<bke::NodeLinkError> errors,
4549 uiBlock &invalid_links_block)
4550{
4551 const ARegion &region = *CTX_wm_region(&C);
4552 if (errors.is_empty()) {
4553 return;
4554 }
4555 if (!link.fromsock || !link.tosock || !link.fromnode || !link.tonode) {
4556 /* Likely because the link is being dragged. */
4557 return;
4558 }
4559
4560 /* Generate full tooltip from potentially multiple errors. */
4561 std::string error_tooltip;
4562 if (errors.size() == 1) {
4563 error_tooltip = errors[0].tooltip;
4564 }
4565 else {
4566 for (const bke::NodeLinkError &error : errors) {
4567 error_tooltip += fmt::format("\u2022 {}\n", error.tooltip);
4568 }
4569 }
4570
4571 const float bg_radius = UI_UNIT_X * 0.5f;
4572 const float bg_corner_radius = UI_UNIT_X * 0.2f;
4573 const float icon_size = UI_UNIT_X;
4574 const float region_padding = UI_UNIT_X * 0.5f;
4575
4576 /* Compute error icon location. */
4577 std::optional<float2> draw_position_opt = find_visible_center_of_link(
4578 region.v2d, link, bg_radius, region_padding);
4579 if (!draw_position_opt.has_value()) {
4580 return;
4581 }
4582 const int2 draw_position = int2(draw_position_opt.value());
4583
4584 /* Draw a background for the error icon. */
4585 rctf bg_rect;
4586 BLI_rctf_init_pt_radius(&bg_rect, float2(draw_position), bg_radius);
4587 ColorTheme4f bg_color;
4590 ui_draw_dropshadow(&bg_rect, bg_corner_radius, UI_UNIT_X * 0.2f, snode.runtime->aspect, 0.5f);
4591 UI_draw_roundbox_4fv(&bg_rect, true, bg_corner_radius, bg_color);
4592
4593 /* Draw the icon itself with a tooltip. */
4594 UI_block_emboss_set(&invalid_links_block, ui::EmbossType::None);
4595 uiBut *but = uiDefIconBut(&invalid_links_block,
4597 0,
4598 ICON_ERROR,
4599 draw_position.x - icon_size / 2,
4600 draw_position.y - icon_size / 2,
4601 icon_size,
4602 icon_size,
4603 nullptr,
4604 0,
4605 0,
4606 std::nullopt);
4608 but, [tooltip = std::move(error_tooltip)](const uiBut * /*but*/) { return tooltip; });
4609}
4610
4612{
4613 Scene *scene = CTX_data_scene(&C);
4614 wmWindow *window = CTX_wm_window(&C);
4615 ARegion *region = CTX_wm_region(&C);
4616 return *UI_block_begin(&C, scene, window, region, "invalid_links", ui::EmbossType::None);
4617}
4618
4619#define USE_DRAW_TOT_UPDATE
4620
4621static void node_draw_nodetree(const bContext &C,
4622 TreeDrawContext &tree_draw_ctx,
4623 ARegion &region,
4624 SpaceNode &snode,
4625 bNodeTree &ntree,
4627 Span<uiBlock *> blocks,
4628 bNodeInstanceKey parent_key)
4629{
4630#ifdef USE_DRAW_TOT_UPDATE
4631 BLI_rctf_init_minmax(&region.v2d.tot);
4632#endif
4633
4634 for (const int i : nodes.index_range()) {
4635#ifdef USE_DRAW_TOT_UPDATE
4636 /* Unrelated to background nodes, update the v2d->tot,
4637 * can be anywhere before we draw the scroll bars. */
4638 BLI_rctf_union(&region.v2d.tot, &nodes[i]->runtime->draw_bounds);
4639#endif
4640 }
4641
4642 /* Node lines. */
4644 nodelink_batch_start(snode);
4645
4646 for (const bNodeLink *link : ntree.all_links()) {
4648 node_draw_link(C, region.v2d, snode, *link, false);
4649 }
4650 }
4651
4652 /* Draw selected node links after the unselected ones, so they are shown on top. */
4653 for (const bNodeLink *link : ntree.all_links()) {
4655 node_draw_link(C, region.v2d, snode, *link, true);
4656 }
4657 }
4658
4659 nodelink_batch_end(snode);
4660
4662
4663 draw_frame_overlays(C, tree_draw_ctx, region, snode, ntree, blocks);
4664
4665 /* Draw foreground nodes, last nodes in front. */
4666 for (const int i : nodes.index_range()) {
4667 bNode &node = *nodes[i];
4668 if (node.flag & NODE_BACKGROUND) {
4669 /* Background nodes are drawn before mixed with zones already. */
4670 continue;
4671 }
4672
4673 const bNodeInstanceKey key = bke::node_instance_key(parent_key, &ntree, &node);
4674 node_draw(C, tree_draw_ctx, region, snode, ntree, node, *blocks[node.index()], key);
4675 }
4676
4677 uiBlock &invalid_links_block = invalid_links_uiblock_init(C);
4678 for (auto &&item : ntree.runtime->link_errors.items()) {
4679 if (const bNodeLink *link = item.key.try_find(ntree)) {
4680 if (!bke::node_link_is_hidden(*link)) {
4681 draw_link_errors(C, snode, *link, item.value, invalid_links_block);
4682 }
4683 }
4684 }
4685 UI_block_end(&C, &invalid_links_block);
4686 UI_block_draw(&C, &invalid_links_block);
4687}
4688
4689/* Draw the breadcrumb on the top of the editor. */
4690static void draw_tree_path(const bContext &C, ARegion &region)
4691{
4694
4695 const rcti *rect = ED_region_visible_rect(&region);
4696
4697 const uiStyle *style = UI_style_get_dpi();
4698 const float padding_x = 16 * UI_SCALE_FAC;
4699 const int x = rect->xmin + padding_x;
4700 const int y = region.winy - UI_UNIT_Y * 0.6f;
4701 const int width = BLI_rcti_size_x(rect) - 2 * padding_x;
4702
4703 uiBlock *block = UI_block_begin(&C, &region, __func__, ui::EmbossType::None);
4704 uiLayout &layout = ui::block_layout(
4705 block, ui::LayoutDirection::Vertical, ui::LayoutType::Panel, x, y, width, 1, 0, style);
4706
4708 ui::template_breadcrumbs(layout, context_path);
4709
4711 UI_block_end(&C, block);
4712 UI_block_draw(&C, block);
4713
4715}
4716
4717static void snode_setup_v2d(SpaceNode &snode, ARegion &region, const float2 &center)
4718{
4719 View2D &v2d = region.v2d;
4720
4721 /* Shift view to node tree center. */
4722 UI_view2d_center_set(&v2d, center[0], center[1]);
4724
4725 snode.runtime->aspect = BLI_rctf_size_x(&v2d.cur) / float(region.winx);
4726}
4727
4729 const SpaceNode &snode,
4730 const bNodeTree &ntree,
4731 bke::ComputeContextCache &compute_context_cache)
4732{
4734 for (const bNode *index_switch_node : ntree.nodes_by_type("GeometryNodeIndexSwitch")) {
4735 const bNodeSocket &index_socket = index_switch_node->input_socket(0);
4737 snode, compute_context_cache, index_socket);
4738 if (!compute_context) {
4739 continue;
4740 }
4741 const std::optional<nodes::NodeInContext> menu_switch = nodes::find_origin_index_menu_switch(
4742 {compute_context, &index_socket}, compute_context_cache);
4743 if (!menu_switch) {
4744 continue;
4745 }
4746 result.add(index_switch_node, menu_switch->node);
4747 }
4748 return result;
4749}
4750
4751static void draw_nodetree(const bContext &C,
4752 ARegion &region,
4753 bNodeTree &ntree,
4754 bNodeInstanceKey parent_key)
4755{
4756 SpaceNode *snode = CTX_wm_space_node(&C);
4757 ntree.ensure_topology_cache();
4758
4760
4762
4763 bke::ComputeContextCache compute_context_cache;
4764
4765 TreeDrawContext tree_draw_ctx;
4766 tree_draw_ctx.bmain = CTX_data_main(&C);
4767 tree_draw_ctx.window = CTX_wm_window(&C);
4768 tree_draw_ctx.scene = CTX_data_scene(&C);
4769 tree_draw_ctx.region = CTX_wm_region(&C);
4770 tree_draw_ctx.depsgraph = CTX_data_depsgraph_pointer(&C);
4771 tree_draw_ctx.extra_info_rows_per_node.reinitialize(nodes.size());
4773 find_menu_switch_sources_for_index_switch_nodes(*snode, ntree, compute_context_cache);
4774
4775 BLI_SCOPED_DEFER([&]() { ntree.runtime->sockets_on_active_gizmo_paths.clear(); });
4776 if (ntree.type == NTREE_GEOMETRY) {
4779 log.ensure_node_warnings(*tree_draw_ctx.bmain);
4780 log.ensure_execution_times();
4781 });
4782 const WorkSpace *workspace = CTX_wm_workspace(&C);
4784 workspace->viewer_path, *snode);
4785
4786 /* This set of socket is used when drawing links to determine which links should use the
4787 * special gizmo drawing. */
4788 ntree.runtime->sockets_on_active_gizmo_paths = find_sockets_on_active_gizmo_paths(
4789 C, *snode, compute_context_cache);
4790 }
4791 else if (ntree.type == NTREE_COMPOSIT) {
4792 const Scene *scene = CTX_data_scene(&C);
4794 &scene->runtime->compositor.per_node_execution_time;
4795 }
4796 else if (ntree.type == NTREE_SHADER) {
4797 if (USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) &&
4801 {
4802 tree_draw_ctx.nested_group_infos = get_nested_previews(C, *snode);
4803 }
4804 {
4805 std::lock_guard lock(ntree.runtime->shader_node_errors_mutex);
4806 /* Make a local copy to avoid mutex access for each node. Typically, there are only very few
4807 * error message. */
4808 tree_draw_ctx.shader_node_errors = ntree.runtime->shader_node_errors;
4809 }
4810 }
4811
4812 for (const int i : nodes.index_range()) {
4813 const bNode &node = *nodes[i];
4814 tree_draw_ctx.extra_info_rows_per_node[node.index()] = node_get_extra_info(
4815 C, tree_draw_ctx, *snode, node);
4816 }
4817
4818 node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
4819 node_draw_zones_and_frames(region, *snode, ntree);
4820 node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, nodes, blocks, parent_key);
4821}
4822
4826static void draw_background_color(const SpaceNode &snode)
4827{
4828 const int max_tree_length = 3;
4829 const float bright_factor = 0.25f;
4830
4831 /* We ignore the first element of the path since it is the top-most tree and it doesn't need to
4832 * be brighter. We also set a cap to how many levels we want to set apart, to avoid the
4833 * background from getting too bright. */
4834 const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode.treepath,
4835 max_tree_length);
4836 const int depth = max_ii(0, clamped_tree_path_length - 1);
4837
4838 float color[3];
4840 mul_v3_fl(color, 1.0f + bright_factor * depth);
4841 GPU_clear_color(color[0], color[1], color[2], 1.0);
4842}
4843
4844void node_draw_space(const bContext &C, ARegion &region)
4845{
4846 wmWindow *win = CTX_wm_window(&C);
4847 SpaceNode &snode = *CTX_wm_space_node(&C);
4848 View2D &v2d = region.v2d;
4849
4850 /* Setup off-screen buffers. */
4851 GPUViewport *viewport = WM_draw_region_get_viewport(&region);
4852
4853 blender::gpu::FrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
4854 GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
4855
4857 draw_background_color(snode);
4859 GPU_scissor_test(true);
4860
4861 /* XXX `snode->runtime->cursor` set in coordinate-space for placing new nodes,
4862 * used for drawing noodles too. */
4864 win->eventstate->xy[0] - region.winrct.xmin,
4865 win->eventstate->xy[1] - region.winrct.ymin,
4866 &snode.runtime->cursor[0],
4867 &snode.runtime->cursor[1]);
4868 snode.runtime->cursor[0] /= UI_SCALE_FAC;
4869 snode.runtime->cursor[1] /= UI_SCALE_FAC;
4870
4872
4873 /* Only set once. */
4875
4876 /* Nodes. */
4878
4879 const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE);
4881
4882 /* Draw parent node trees. */
4883 if (snode.treepath.last) {
4884 bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last;
4885
4886 /* Update tree path name (drawn in the bottom left). */
4887 ID *name_id = (path->nodetree && path->nodetree != snode.nodetree) ? &path->nodetree->id :
4888 snode.id;
4889
4890 if (name_id && UNLIKELY(!STREQ(path->display_name, name_id->name + 2))) {
4891 STRNCPY_UTF8(path->display_name, name_id->name + 2);
4892 }
4893
4894 /* Current View2D center, will be set temporarily for parent node trees. */
4895 float2 center;
4896 UI_view2d_center_get(&v2d, &center.x, &center.y);
4897
4898 /* Store new view center in path and current edit tree. */
4899 copy_v2_v2(path->view_center, center);
4900 if (snode.edittree) {
4901 copy_v2_v2(snode.edittree->view_center, center);
4902 }
4903
4904 /* Top-level edit tree. */
4905 bNodeTree *ntree = path->nodetree;
4906 if (ntree) {
4907 snode_setup_v2d(snode, region, center);
4908
4909 /* Backdrop. */
4910 draw_nodespace_back_pix(C, region, snode, path->parent_key);
4911
4912 {
4913 float original_proj[4][4];
4914 GPU_matrix_projection_get(original_proj);
4915
4918
4919 wmOrtho2_pixelspace(region.winx, region.winy);
4920
4921 WM_gizmomap_draw(region.runtime->gizmo_map, &C, WM_GIZMOMAP_DRAWSTEP_2D);
4922
4924 GPU_matrix_projection_set(original_proj);
4925 }
4926
4927 draw_nodetree(C, region, *ntree, path->parent_key);
4928 }
4929
4930 /* Temporary links. */
4932 GPU_line_smooth(true);
4933 if (snode.runtime->linkdrag) {
4934 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
4935 node_draw_link_dragged(C, v2d, snode, link);
4936 }
4937 }
4938 GPU_line_smooth(false);
4940
4942 /* Draw grease-pencil annotations. */
4944 }
4945 }
4946 else {
4947
4948 /* Backdrop. */
4950 }
4951
4953
4954 /* Reset view matrix. */
4956
4958 if (snode.flag & SNODE_SHOW_GPENCIL && snode.treepath.last) {
4959 /* Draw grease-pencil (screen strokes, and also paint-buffer). */
4961 }
4962
4963 /* Draw context path. */
4964 if (snode.overlay.flag & SN_OVERLAY_SHOW_PATH) {
4965 draw_tree_path(C, region);
4966 }
4967 }
4968
4969 /* Scrollers. */
4970
4971 /* Hide the right scrollbar while a right-aligned region
4972 * is open. Otherwise we can have two scroll bars. #141225 */
4973 ScrArea *area = CTX_wm_area(&C);
4974 bool sidebar = false;
4975 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
4976 if (region->alignment == RGN_ALIGN_RIGHT && region->overlap &&
4977 !(region->flag & RGN_FLAG_HIDDEN))
4978 {
4979 sidebar = true;
4980 break;
4981 }
4982 }
4983
4984 if (sidebar) {
4986 }
4987 else {
4988 v2d.scroll |= V2D_SCROLL_RIGHT;
4989 }
4990
4991 UI_view2d_scrollers_draw(&v2d, nullptr);
4992}
4993
4994} // namespace blender::ed::space_node
void ED_draw_imbuf(ImBuf *ibuf, float x, float y, bool use_filter, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float zoom_x, float zoom_y)
Definition glutil.cc:559
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
WorkSpace * CTX_wm_workspace(const bContext *C)
SpaceNode * CTX_wm_space_node(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
void id_us_ensure_real(ID *id)
Definition lib_id.cc:313
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_CLASS_OUTPUT
Definition BKE_node.hh:448
#define NODE_CLASS_INTERFACE
Definition BKE_node.hh:459
#define NODE_CUSTOM_GROUP
Definition BKE_node.hh:816
#define NODE_CLASS_MATTE
Definition BKE_node.hh:454
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:453
#define NODE_CUSTOM
Definition BKE_node.hh:810
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:815
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:455
#define NODE_CLASS_OP_VECTOR
Definition BKE_node.hh:450
#define NODE_GROUP
Definition BKE_node.hh:811
#define NODE_CLASS_LAYOUT
Definition BKE_node.hh:463
#define NODE_CLASS_OP_COLOR
Definition BKE_node.hh:449
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:451
#define NODE_FRAME
Definition BKE_node.hh:812
#define NODE_CLASS_GROUP
Definition BKE_node.hh:452
#define NODE_CLASS_ATTRIBUTE
Definition BKE_node.hh:462
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:457
#define NODE_CLASS_SHADER
Definition BKE_node.hh:460
#define NODE_CLASS_SCRIPT
Definition BKE_node.hh:458
#define NODE_EVALUATE_CLOSURE
#define GEO_NODE_STORE_NAMED_ATTRIBUTE
#define GEO_NODE_VIEWER
#define GEO_NODE_REMOVE_ATTRIBUTE
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
#define GEO_NODE_INPUT_NAMED_ATTRIBUTE
#define GEO_NODE_SIMULATION_OUTPUT
#define CMP_NODE_OUTPUT_FILE
#define GEO_NODE_REPEAT_OUTPUT
General operations, lookup, etc. for blender objects.
bool BKE_scene_uses_shader_previews(const Scene *scene)
Definition scene.cc:2839
void BLF_size(int fontid, float size)
Definition blf.cc:443
void BLF_enable(int fontid, FontFlags flag)
Definition blf.cc:320
void BLF_aspect(int fontid, float x, float y, float z)
Definition blf.cc:377
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:476
void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax)
Definition blf.cc:912
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:585
void BLF_wordwrap(int fontid, int wrap_width, BLFWrapMode mode=BLFWrapMode::Minimal)
Definition blf.cc:924
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:802
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:850
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
@ BLF_WORD_WRAP
Definition BLF_enums.hh:39
@ BLF_ASPECT
Definition BLF_enums.hh:38
@ BLF_CLIPPING
Definition BLF_enums.hh:34
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_convexhull_2d(blender::Span< blender::float2 > points, int r_points[])
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE void rgba_float_args_set(float col[4], float r, float g, float b, float a)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
#define BLI_SCOPED_DEFER(function_to_defer)
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
BLI_INLINE float BLI_rcti_cent_x_fl(const struct rcti *rct)
Definition BLI_rect.h:169
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest)
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
bool BLI_rctf_clamp_segment(const struct rctf *rect, float s1[2], float s2[2])
float BLI_rctf_length_x(const rctf *rect, float x)
Definition rct.cc:171
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
bool BLI_rctf_isect_pt(const struct rctf *rect, float x, float y)
BLI_INLINE float BLI_rcti_cent_y_fl(const struct rcti *rct)
Definition BLI_rect.h:173
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
void BLI_rctf_mul(struct rctf *rect, float factor)
Definition rct.cc:588
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size)
Definition rct.cc:458
float BLI_rctf_length_y(const rctf *rect, float y)
Definition rct.cc:182
void BLI_rctf_init_minmax(struct rctf *rect)
Definition rct.cc:480
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
#define STREQLEN(a, b, n)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_TAG_MISSING
Definition DNA_ID.h:867
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
@ ID_NT
@ ID_LA
@ ID_WO
@ ID_MA
@ NODE_OPTIONS
@ NODE_DO_OUTPUT
@ NODE_ACTIVE
@ NODE_CUSTOM_COLOR
@ NODE_BACKGROUND
@ NODE_COLLAPSED
@ NODE_SELECT
@ NODE_PREVIEW
@ NTREE_TEXTURE
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ SOCK_OUT
@ SOCK_IN
@ NODE_VIEWER_SHORCTUT_SLOT_9
@ NODE_VIEWER_SHORCTUT_SLOT_5
@ NODE_VIEWER_SHORCTUT_SLOT_4
@ NODE_VIEWER_SHORCTUT_SLOT_2
@ NODE_VIEWER_SHORCTUT_SLOT_7
@ NODE_VIEWER_SHORCTUT_SLOT_1
@ NODE_VIEWER_SHORCTUT_SLOT_3
@ NODE_VIEWER_SHORTCUT_NONE
@ NODE_VIEWER_SHORCTUT_SLOT_8
@ NODE_VIEWER_SHORCTUT_SLOT_6
@ SOCK_MULTI_INPUT
@ SOCK_PANEL_COLLAPSED
@ SOCK_CUSTOM
@ NODE_FRAME_RESIZEABLE
@ NODE_FRAME_SHRINK
@ NODE_PANEL_COLLAPSED
@ RGN_ALIGN_RIGHT
@ RGN_FLAG_HIDDEN
@ SN_OVERLAY_SHOW_PATH
@ SN_OVERLAY_SHOW_PREVIEWS
@ SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS
@ SN_OVERLAY_SHOW_TIMINGS
@ SN_OVERLAY_SHOW_OVERLAYS
@ SN_OVERLAY_SHOW_NAMED_ATTRIBUTES
@ SNODE_SHOW_GPENCIL
@ SPACE_NODE
#define UI_SCALE_FAC
#define UI_INV_SCALE_FAC
@ USER_TOOLTIPS_PYTHON
#define USER_EXPERIMENTAL_TEST(userdef, member)
@ V2D_SCROLL_RIGHT
#define NODE_GRID_STEP_SIZE
Definition ED_node_c.hh:27
const rcti * ED_region_visible_rect(ARegion *region)
Definition area.cc:4301
void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
#define REGION_DRAW_POST_VIEW
#define REGION_DRAW_PRE_VIEW
void GPU_clear_color(float red, float green, float blue, float alpha)
void GPU_framebuffer_bind_no_srgb(blender::gpu::FrameBuffer *fb)
void immUniform4f(const char *name, float x, float y, float z, float w)
void immUniformThemeColorAlpha(int color_id, float a)
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immVertex2f(uint attr_id, float x, float y)
void immUniform1i(const char *name, int x)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniformThemeColorBlend(int color_id1, int color_id2, float fac)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
void GPU_matrix_identity_set()
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
#define GPU_matrix_projection_get(x)
#define GPU_matrix_projection_set(x)
void GPU_matrix_pop()
@ GPU_PRIM_TRI_FAN
@ GPU_PRIM_LINES
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_2D_CHECKER
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_scissor_test(bool enable)
Definition gpu_state.cc:188
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
blender::gpu::FrameBuffer * GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void UI_but_func_pushed_state_set(uiBut *but, std::function< bool(const uiBut &)> func)
#define UI_ALPHA_CHECKER_LIGHT
#define UI_UNIT_Y
uiBut * uiDefButR(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, blender::StringRefNull propname, int index, float min, float max, std::optional< blender::StringRef > tip)
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha)
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
@ UI_BLOCK_CLIP_EVENTS
void UI_but_func_tooltip_custom_set(uiBut *but, uiButToolTipCustomFunc func, void *arg, uiFreeArgFunc free_arg)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
@ UI_BUT_INACTIVE
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
uiBut * uiDefIconBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_draw_roundbox_4fv_ex(const rctf *rect, const float inner1[4], const float inner2[4], float shade_dir, const float outline[4], float outline_width, float rad)
void UI_tooltip_text_field_add(uiTooltipData &data, std::string text, std::string suffix, const uiTooltipStyle style, const uiTooltipColorID color_id, const bool is_pad=false)
#define UI_ALPHA_CHECKER_DARK
const uiStyle * UI_style_get_dpi()
void UI_but_drawflag_enable(uiBut *but, int flag)
void UI_draw_roundbox_corner_set(int type)
const uiStyle * UI_style_get()
PointerRNA * UI_but_operator_ptr_ensure(uiBut *but)
@ UI_TIP_STYLE_MONO
@ UI_TIP_STYLE_NORMAL
void UI_but_drawflag_disable(uiBut *but, int flag)
void UI_block_draw(const bContext *C, uiBlock *block)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
@ UI_TIP_LC_PYTHON
@ UI_TIP_LC_NORMAL
#define UI_UNIT_X
void UI_but_operator_set(uiBut *but, wmOperatorType *optype, blender::wm::OpCallContext opcontext, const PointerRNA *opptr=nullptr)
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_TEXT_LEFT
void UI_draw_roundbox_aa(const rctf *rect, bool filled, float rad, const float color[4])
void UI_block_end_ex(const bContext *C, Main *bmain, wmWindow *window, Scene *scene, ARegion *region, Depsgraph *depsgraph, uiBlock *block, const int xy[2]=nullptr, int r_xy[2]=nullptr)
void UI_block_end(const bContext *C, uiBlock *block)
void UI_but_func_quick_tooltip_set(uiBut *but, std::function< std::string(const uiBut *but)> func)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_flag_enable(uiBut *but, int flag)
void UI_block_bounds_set_explicit(uiBlock *block, int minx, int miny, int maxx, int maxy)
Definition interface.cc:685
void UI_block_align_end(uiBlock *block)
void uiLayoutSetTooltipCustomFunc(uiLayout *layout, uiButToolTipCustomFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
void UI_GetThemeColor3fv(int colorid, float col[3])
int UI_GetThemeValueType(int colorid, int spacetype)
ThemeColorID
@ TH_NODE_FRAME
@ TH_NODE_INPUT
@ TH_NODE
@ TH_NODE_FILTER
@ TH_NODE_GROUP
@ TH_GRID
@ TH_NODE_SCRIPT
@ TH_BACK
@ TH_NODE_GEOMETRY
@ TH_NODE_OUTPUT
@ TH_NODE_COLOR
@ TH_WIRE
@ TH_NODE_ATTRIBUTE
@ TH_PANEL_SUB_BACK
@ TH_NODE_DISTORT
@ TH_REDALERT
@ TH_NODE_MATTE
@ TH_NODE_GRID_LEVELS
@ TH_NODE_TEXTURE
@ TH_NODE_VECTOR
@ TH_NODE_OUTLINE
@ TH_NODE_CONVERTER
@ TH_SELECT
@ TH_TEXT
@ TH_NODE_SHADER
@ TH_WIRE_INNER
@ TH_NODE_INTERFACE
@ TH_ACTIVE
void UI_GetThemeColorBlend4f(int colorid1, int colorid2, float fac, float r_col[4])
void UI_GetThemeColorBlendShade4fv(int colorid1, int colorid2, float fac, int offset, float col[4])
void UI_GetThemeColorType4fv(int colorid, int spacetype, float col[4])
void UI_GetThemeColorShadeAlpha4fv(int colorid, int coloffset, int alphaoffset, float col[4])
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
void UI_GetThemeColorBlendShade3ubv(int colorid1, int colorid2, float fac, int offset, unsigned char col[3])
void UI_GetColorPtrBlendAlpha4fv(const float cp1[4], const float cp2[4], float fac, float alphaoffset, float r_col[4])
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1504
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1162
void UI_view2d_center_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1939
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1095
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1912
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
void UI_view2d_center_set(View2D *v2d, float x, float y)
Definition view2d.cc:1949
void UI_view2d_dot_grid_draw(const View2D *v2d, int grid_color_id, float min_step, int grid_subdivisions)
Definition view2d.cc:1287
@ WM_GIZMOMAP_DRAWSTEP_2D
#define NC_WORLD
Definition WM_types.hh:387
#define ND_SHADING
Definition WM_types.hh:477
#define ND_WORLD
Definition WM_types.hh:452
#define NC_SCENE
Definition WM_types.hh:378
#define ND_NODES
Definition WM_types.hh:436
#define ND_MODIFIER
Definition WM_types.hh:462
#define NC_MATERIAL
Definition WM_types.hh:380
#define NC_LAMP
Definition WM_types.hh:382
#define NC_TEXTURE
Definition WM_types.hh:381
#define ND_LIGHTING
Definition WM_types.hh:483
#define NC_OBJECT
Definition WM_types.hh:379
void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d)
volatile int lock
#define U
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
constexpr T & first() const
Definition BLI_span.hh:679
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
const T * end() const
Definition BLI_array.hh:325
IndexRange index_range() const
Definition BLI_array.hh:360
const T * begin() const
Definition BLI_array.hh:321
ChannelStorageType a
const ComputeContextHash & hash() const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:325
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
int64_t size() const
Definition BLI_map.hh:976
constexpr int64_t size() const
Definition BLI_span.hh:493
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:182
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool startswith(StringRef prefix) const
constexpr const char * c_str() const
static VArray from_single(T value, const int64_t size)
int64_t size() const
IndexRange index_range() const
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
MutableSpan< float3 > positions_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
MutableSpan< bool > cyclic_for_write()
std::optional< float > header_center_y
std::optional< bNodePanelExtent > content_extent
std::optional< int > output_node_id
const bNode * output_node() const
bool contains_node_recursively(const bNode &node) const
std::optional< int > input_node_id
Vector< bNodeTreeZone * > child_zones
const bNode * input_node() const
Vector< bNodeTreeZone * > zones
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
std::function< DrawNodeLayoutFn > draw
Vector< PanelDeclaration * > panels
std::optional< std::string > translation_context
Vector< ItemDeclaration * > items
const SocketDeclaration * panel_input_decl() const
std::unique_ptr< CustomSocketDrawFn > custom_draw_fn
GeoTreeLog * get_main_tree_log(const bke::bNodeTreeZone *zone) const
void foreach_tree_log(FunctionRef< void(GeoTreeLog &)> callback) const
Map< StringRefNull, NamedAttributeUsage > used_named_attributes
static ContextualGeoTreeLogs get_contextual_tree_logs(const SpaceNode &snode)
nullptr float
#define SELECT
#define GS(x)
uint pos
#define log
#define round
float length(VecOp< float, D >) RET
uint padding(uint offset, uint alignment)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
format
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
static void error(const char *str)
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRef prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
const bNodeZoneType * zone_type_by_node_type(const int node_type)
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:4970
bool node_link_is_selected(const bNodeLink &link)
Definition node.cc:3887
std::optional< StringRefNull > node_socket_short_label(const bNodeSocket &sock)
Definition node.cc:4985
const char * node_socket_translation_context(const bNodeSocket &sock)
Definition node.cc:5001
bNodeType NodeTypeUndefined
Definition node.cc:126
bNodeInstanceKey node_instance_key(bNodeInstanceKey parent_key, const bNodeTree *ntree, const bNode *node)
Definition node.cc:4867
Span< int > all_zone_node_types()
const bNodeInstanceKey NODE_INSTANCE_KEY_NONE
Definition node.cc:4847
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:4568
StringRefNull node_socket_label(const bNodeSocket &sock)
Definition node.cc:4996
bool node_link_is_hidden(const bNodeLink &link)
Definition node.cc:3882
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
static bool compare_node_depth(const bNode *a, const bNode *b)
Definition node_draw.cc:254
void tag_update_id(ID *id)
Definition node_draw.cc:211
static rctf calc_node_frame_dimensions(const bContext &C, TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, bNode &node)
static void determine_visible_panels_impl_recursive(const bNode &node, const nodes::PanelDeclaration &panel_decl, const Span< bool > potentially_visible_states, MutableSpan< bool > r_result)
Definition node_draw.cc:698
const ComputeContext * compute_context_for_edittree_socket(const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache, const bNodeSocket &socket)
static void add_flat_items_for_panel(bNode &node, const nodes::PanelDeclaration &panel_decl, const Span< bool > panel_visibility, Vector< FlatNodeItem > &r_items)
Definition node_draw.cc:790
float node_socket_calculate_height(const bNodeSocket &socket)
Definition node_edit.cc:114
rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node)
static bNodeInstanceKey current_node_instance_key(const SpaceNode &snode, const bNode &node)
int node_get_resize_cursor(NodeResizeDirection directions)
NodeLinkData data[NODELINK_GROUP_SIZE]
Definition drawnode.cc:1863
static void reroute_node_draw(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, const SpaceNode &snode, bNodeTree &ntree, const bNode &node, uiBlock &block)
static float get_margin_to_bottom(const Span< FlatNodeItem > items)
Definition node_draw.cc:903
static void node_socket_outline_color_get(const bool selected, const int socket_type, float r_outline_color[4])
static void mark_sockets_collapsed_recursive(bNode &node, const int node_left_x, const nodes::PanelDeclaration &visible_panel_decl, const nodes::PanelDeclaration &panel_decl)
static const bNode * reroute_node_get_linked_reroute(const bNode &reroute)
void tree_draw_order_update(bNodeTree &ntree)
Definition node_draw.cc:316
static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node, uiBlock &block, const rctf &rect, float &icon_offset)
static void draw_link_errors(const bContext &C, SpaceNode &snode, const bNodeLink &link, const Span< bke::NodeLinkError > errors, uiBlock &invalid_links_block)
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
static Map< const bNode *, const bNode * > find_menu_switch_sources_for_index_switch_nodes(const SpaceNode &snode, const bNodeTree &ntree, bke::ComputeContextCache &compute_context_cache)
static void update_collapsed_sockets(bNode &node, const int node_left_x)
static void tag_final_panel(bNode &node, const Span< FlatNodeItem > items)
static void draw_nodetree(const bContext &C, ARegion &region, bNodeTree &ntree, bNodeInstanceKey parent_key)
static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx, const bNode &node, const SpaceNode &snode)
static float get_margin_between_elements(const Span< FlatNodeItem > items, const int next_index)
Definition node_draw.cc:926
static void reroute_node_draw_body(const bContext &C, const SpaceNode &snode, const bNodeTree &ntree, const bNode &node, uiBlock &block, const bool selected)
static void node_draw_collapsed(const bContext &C, TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, bNode &node, uiBlock &block)
static void node_update_nodetree(const bContext &C, TreeDrawContext &tree_draw_ctx, bNodeTree &ntree, Span< bNode * > nodes, Span< uiBlock * > blocks)
static float node_tree_view_scale(const SpaceNode &snode)
static void determine_potentially_visible_panels(const bNode &node, MutableSpan< bool > r_result)
Definition node_draw.cc:689
static void frame_node_draw_background(const ARegion &region, const SpaceNode &snode, const bNode &node)
static void node_draw_node_group_indicator(const SpaceNode &snode, const bNode &node, const rctf &rect, const float radius, const float color[4])
float2 node_from_view(const float2 &co)
Definition node_draw.cc:394
void node_draw_link_bezier(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const int th_col1, const int th_col2, const int th_col3, const bool selected)
Definition drawnode.cc:2265
static void draw_background_color(const SpaceNode &snode)
static std::optional< std::chrono::nanoseconds > node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static bool node_update_basis_socket(TreeDrawContext &tree_draw_ctx, const bContext &C, bNodeTree &ntree, bNode &node, const char *panel_label, bNodeSocket *input_socket, bNodeSocket *output_socket, uiBlock &block, const int &locx, int &locy)
Definition node_draw.cc:512
static std::optional< std::chrono::nanoseconds > compositor_accumulate_frame_node_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, uiBlock &block)
static float get_margin_from_top(const Span< FlatNodeItem > items)
Definition node_draw.cc:881
ImBuf * node_preview_acquire_ibuf(bNodeTree &ntree, NestedTreePreviews &tree_previews, const bNode &node)
std::array< float2, 4 > node_link_bezier_points_dragged(const SpaceNode &snode, const bNodeLink &link)
Definition drawnode.cc:2322
bNodeSocket * node_find_indicated_socket(SpaceNode &snode, ARegion &region, const float2 &cursor, const eNodeSocketInOut in_out)
static void node_draw_panels_background(const bNode &node)
static void find_bounds_by_zone_recursive(const SpaceNode &snode, const bNodeTreeZone &zone, const Span< const bNodeTreeZone * > all_zones, MutableSpan< Vector< float2 > > r_bounds_by_zone)
static std::optional< NodeExtraInfoRow > node_get_accessed_attributes_row(TreeDrawContext &tree_draw_ctx, const bNode &node)
static void node_update_basis(const bContext &C, TreeDrawContext &tree_draw_ctx, bNodeTree &ntree, bNode &node, uiBlock &block)
void node_socket_color_get(const bContext &C, const bNodeTree &ntree, PointerRNA &node_ptr, const bNodeSocket &sock, float r_color[4])
Array< bNode * > tree_draw_order_calc_nodes_reversed(bNodeTree &ntree)
Definition node_draw.cc:340
static void node_get_compositor_extra_info(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, Vector< NodeExtraInfoRow > &rows)
void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const bool selected)
Definition drawnode.cc:2284
static void node_draw_mute_line(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNode &node)
static void reroute_node_prepare_for_draw(bNode &node)
static const bNode * find_node_under_cursor(SpaceNode &snode, const float2 &cursor)
static bool is_node_panels_supported(const bNode &node)
Definition node_draw.cc:399
static NodeExtraInfoRow row_from_used_named_attribute(const Map< StringRefNull, geo_log::NamedAttributeUsage > &usage_by_attribute_name)
void node_draw_link_dragged(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link)
Definition drawnode.cc:2336
static std::optional< std::chrono::nanoseconds > compositor_node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void add_flat_items_for_separator(Vector< FlatNodeItem > &r_items)
Definition node_draw.cc:775
static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
static void node_update_collapsed(bNode &node, uiBlock &block)
static Set< const bNodeSocket * > find_sockets_on_active_gizmo_paths(const bContext &C, const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
static Array< uiBlock * > node_uiblocks_init(const bContext &C, const Span< bNode * > nodes)
Definition node_draw.cc:352
static void add_rect_corner_positions(Vector< float2 > &positions, const rctf &rect)
bool node_is_previewable(const SpaceNode &snode, const bNodeTree &ntree, const bNode &node)
static void node_socket_tooltip_set(uiBlock &block, const int socket_index_in_tree, const float2 location, const float2 size)
static void determine_potentially_visible_panels_recursive(const bNode &node, const nodes::PanelDeclaration &panel_decl, MutableSpan< bool > r_result)
Definition node_draw.cc:668
static bool node_update_basis_buttons(const bContext &C, bNodeTree &ntree, bNode &node, blender::FunctionRef< nodes::DrawNodeLayoutFn > draw_buttons, uiBlock &block, int &dy)
Definition node_draw.cc:405
const ComputeContext * compute_context_for_edittree(const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
void draw_nodespace_back_pix(const bContext &C, ARegion &region, SpaceNode &snode, bNodeInstanceKey parent_key)
Definition drawnode.cc:1531
static void draw_frame_overlays(const bContext &C, TreeDrawContext &tree_draw_ctx, const ARegion &region, const SpaceNode &snode, const bNodeTree &ntree, Span< uiBlock * > blocks)
static std::optional< std::chrono::nanoseconds > geo_node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static bool panel_has_only_inactive_inputs(const bNode &node, const nodes::PanelDeclaration &panel_decl)
static std::string named_attribute_tooltip(bContext *, void *argN, const StringRef)
static void node_draw_basis(const bContext &C, TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, const bNode &node, uiBlock &block, bNodeInstanceKey key)
static void node_draw_extra_info_panel_back(const bNode &node, const rctf &extra_info_rect)
static void node_draw_extra_info_panel(const bContext &C, TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, ImBuf *preview, uiBlock &block)
static void count_multi_input_socket_links(bNodeTree &ntree, SpaceNode &snode)
static void determine_visible_panels(const bNode &node, MutableSpan< bool > r_visibility_states)
Definition node_draw.cc:738
static void node_draw_sockets(const bContext &C, uiBlock &block, const SpaceNode &snode, const bNodeTree &ntree, const bNode &node)
static bNodeTree * node_tree_from_ID(ID *id)
Definition node_draw.cc:199
void tree_update(const bContext *C)
Definition node_draw.cc:186
static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label=nullptr)
Definition node_draw.cc:453
static void node_draw_extra_info_row(const bNode &node, uiBlock &block, const rctf &rect, const int row, const NodeExtraInfoRow &extra_info_row)
static void node_draw_preview(const Scene *scene, ImBuf *preview, const rctf *prv)
static void node_draw_nodetree(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, SpaceNode &snode, bNodeTree &ntree, Span< bNode * > nodes, Span< uiBlock * > blocks, bNodeInstanceKey parent_key)
static short get_viewer_shortcut_icon(const bNode &node)
Array< bNode * > tree_draw_order_calc_nodes(bNodeTree &ntree)
Definition node_draw.cc:328
static void draw_socket_layout(TreeDrawContext &tree_draw_ctx, const bContext &C, uiLayout &layout, bNodeSocket &socket, bNodeTree &ntree, bNode &node, PointerRNA &node_ptr, PointerRNA &socket_ptr, const char *panel_label)
Definition node_draw.cc:482
static void update_collapsed_sockets_recursive(bNode &node, const int node_left_x, const nodes::PanelDeclaration &panel_decl)
Vector< ui::ContextPathItem > context_path_for_space_node(const bContext &C)
float2 node_to_view(const float2 &co)
Definition node_draw.cc:373
static void add_flat_items_for_socket(bNode &node, const nodes::SocketDeclaration &socket_decl, const nodes::PanelDeclaration *panel_decl, const nodes::SocketDeclaration *prev_socket_decl, Vector< FlatNodeItem > &r_items)
Definition node_draw.cc:745
void node_select_single(bContext &C, bNode &node)
static std::string node_errors_tooltip_fn(const Span< geo_log::NodeWarning > warnings)
static rctf node_to_rect(const bNode &node)
Definition node_draw.cc:378
static Vector< NodeExtraInfoRow > node_get_extra_info(const bContext &C, TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
NestedTreePreviews * get_nested_previews(const bContext &C, SpaceNode &snode)
std::optional< ObjectAndModifier > get_modifier_for_node_editor(const SpaceNode &snode)
static std::optional< NodeExtraInfoRow > node_get_execution_time_label_row(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static nodes::NodeWarningType node_error_highest_priority(Span< geo_log::NodeWarning > warnings)
void node_link_bezier_points_evaluated(const bNodeLink &link, std::array< float2, NODE_LINK_RESOL+1 > &coords)
Definition drawnode.cc:1683
static Vector< FlatNodeItem > make_flat_node_items(bNode &node)
Definition node_draw.cc:840
void build_socket_tooltip(uiTooltipData &tip_data, bContext &C, uiBut *but, const bNodeTree &tree, const bNodeSocket &socket)
static const float virtual_node_socket_outline_color[4]
static std::optional< float2 > find_visible_center_of_link(const View2D &v2d, const bNodeLink &link, const float radius, const float region_padding)
float2 socket_link_connection_location(const bNode &node, const bNodeSocket &socket, const bNodeLink &link)
Definition drawnode.cc:1623
static void node_draw_socket(const bContext &C, const bNodeTree &ntree, const bNode &node, PointerRNA &node_ptr, uiBlock &block, const bNodeSocket &sock, const float outline_thickness, const bool selected, const float aspect)
static FrameNodeLayout frame_node_layout(const bNode &frame_node)
static void frame_node_draw_outline(const ARegion &region, const SpaceNode &snode, const bNode &node)
void snode_set_context(const bContext &C)
Definition node_edit.cc:682
static void node_header_custom_tooltip(const bNode &node, uiBut &but)
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout)
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
NodeResizeDirection node_get_resize_direction(const SpaceNode &snode, const bNode *node, const int x, const int y)
Definition drawnode.cc:199
void nodelink_batch_end(const SpaceNode &snode)
Definition drawnode.cc:2033
static void node_draw(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, const SpaceNode &snode, bNodeTree &ntree, bNode &node, uiBlock &block, bNodeInstanceKey key)
void nodelink_batch_start(const SpaceNode &)
Definition drawnode.cc:2028
void node_release_preview_ibuf(NestedTreePreviews &tree_previews)
static void node_draw_zones_and_frames(const ARegion &region, const SpaceNode &snode, const bNodeTree &ntree)
static void add_flat_items_for_layout(const bNode &node, const nodes::LayoutDeclaration &layout_decl, Vector< FlatNodeItem > &r_items)
Definition node_draw.cc:780
static uiBut * add_error_message_button(uiBlock &block, const rctf &rect, const int icon, float &icon_offset, const char *tooltip=nullptr)
void node_to_updated_rect(const bNode &node, rctf &r_rect)
Definition node_draw.cc:388
static bool draw_node_details(const SpaceNode &snode)
void node_draw_space(const bContext &C, ARegion &region)
static void node_draw_preview_background(rctf *rect)
static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block)
static void node_draw_shadow(const SpaceNode &snode, const bNode &node, const float radius, const float alpha)
static uiBlock & invalid_links_uiblock_init(const bContext &C)
static bool node_undefined_or_unsupported(const bNodeTree &node_tree, const bNode &node)
static void node_update_basis_from_declaration(TreeDrawContext &tree_draw_ctx, const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
static void frame_node_draw_overlay(const bContext &C, TreeDrawContext &tree_draw_ctx, const ARegion &region, const SpaceNode &snode, const bNode &node, uiBlock &block)
static void draw_tree_path(const bContext &C, ARegion &region)
static float get_margin_empty()
Definition node_draw.cc:875
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
static StringRef reroute_node_get_auto_label(TreeDrawContext &tree_draw_ctx, const bNode &src_reroute)
void node_draw_nodesocket(const rctf *rect, const float color_inner[4], const float color_outline[4], float outline_thickness, int shape, float aspect)
Definition drawnode.cc:1805
static void node_update_basis_from_socket_lists(TreeDrawContext &tree_draw_ctx, const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
static ColorTheme4f node_header_color_get(const bNodeTree &ntree, const bNode &node, const int color_id)
static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
static void determine_visible_panels_impl(const bNode &node, const Span< bool > potentially_visible_states, MutableSpan< bool > r_result)
Definition node_draw.cc:721
static void snode_setup_v2d(SpaceNode &snode, ARegion &region, const float2 &center)
bNode * find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode)
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, const IndexMask &curve_selection, const VArray< float > &radius, const VArray< int > &counts, bool limit_radius, const bke::AttributeFilter &attribute_filter)
T distance(const T &a, const T &b)
T midpoint(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T round(const T &a)
void foreach_socket_on_gizmo_path(const ComputeContext &gizmo_context, const bNode &gizmo_node, const bNodeSocket &gizmo_socket, FunctionRef< void(const ComputeContext &context, const bNodeSocket &socket, const ie::ElemVariant &elem)> fn)
void foreach_active_gizmo(const bContext &C, bke::ComputeContextCache &compute_context_cache, const ForeachGizmoFn fn)
bool node_can_sync_sockets(const bContext &C, const bNodeTree &, const bNode &node)
int node_warning_type_icon(const NodeWarningType type)
std::optional< NodeInContext > find_origin_index_menu_switch(const SocketInContext &src_socket, bke::ComputeContextCache &compute_context_cache)
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:21
void template_breadcrumbs(uiLayout &layout, Span< ContextPathItem > context_path)
int2 block_layout_resolve(uiBlock *block)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorTheme4< float > ColorTheme4f
VecBase< float, 3 > float3
#define NODE_HEADER_ICON_SIZE
#define NODE_SOCKET_OUTLINE
#define ZONE_ZONE_PADDING
Definition node_draw.cc:117
#define EXTRA_INFO_ROW_HEIGHT
Definition node_draw.cc:118
#define NODE_ZONE_PADDING
Definition node_draw.cc:116
#define NODE_TREE_SCALE_SMALL
#define NODE_MULTI_INPUT_LINK_GAP
#define BASIS_RAD
#define NODE_SOCKSIZE
#define NODE_WIDTH(node)
#define NODE_DYS
#define NODE_ITEM_SPACING_Y
#define NODE_MARGIN_X
#define NODE_DY
const char * name
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
#define min(a, b)
Definition sort.cc:36
ARegionRuntimeHandle * runtime
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
char name[258]
Definition DNA_ID.h:432
void * last
void * first
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
int lines
Definition BLF_api.hh:453
ColorManagedViewSettings view_settings
SceneRuntimeHandle * runtime
ColorManagedDisplaySettings display_settings
ListBase regionbase
SpaceNode_Runtime * runtime
ListBase treepath
struct bNodeTree * edittree
struct ID * id
SpaceNodeOverlay overlay
struct bNodeTree * nodetree
ViewerPath viewer_path
bNodeSocketRuntimeHandle * runtime
bNodeSocketTypeHandle * typeinfo
struct bNodeTree * nodetree
bNodeInstanceKey parent_key
char display_name[64]
float view_center[2]
bNodeTreeRuntimeHandle * runtime
float location[2]
bNodeTypeHandle * typeinfo
int16_t custom1
float width
ListBase inputs
int num_panel_states
float height
struct ID * id
float color[3]
struct bNode * parent
char name[64]
bNodePanelState * panel_states_array
int16_t type_legacy
bNodeRuntimeHandle * runtime
void * storage
char idname[64]
char label[64]
int32_t identifier
std::variant< flat_item::Socket, flat_item::Separator, flat_item::PanelHeader, flat_item::PanelContentBegin, flat_item::PanelContentEnd, flat_item::Layout > item
Definition node_draw.cc:660
Map< StringRefNull, geo_log::NamedAttributeUsage > usage_by_attribute
std::optional< int > frame_identifier_to_highlight
std::unique_ptr< bNodeLinkDrag > linkdrag
geo_log::ContextualGeoTreeLogs tree_logs
Definition node_draw.cc:142
Map< bNodeInstanceKey, timeit::Nanoseconds > * compositor_per_node_execution_time
Definition node_draw.cc:146
Array< Vector< NodeExtraInfoRow > > extra_info_rows_per_node
Definition node_draw.cc:164
Map< int32_t, VectorSet< std::string > > shader_node_errors
Definition node_draw.cc:166
Map< const bNode *, const bNode * > menu_switch_source_by_index_switch
Definition node_draw.cc:158
Map< const bNode *, StringRef > reroute_auto_labels
Definition node_draw.cc:151
const nodes::LayoutDeclaration * decl
Definition node_draw.cc:648
const nodes::PanelDeclaration * decl
Definition node_draw.cc:634
const nodes::PanelDeclaration * panel_decl
Definition node_draw.cc:627
std::function< void(uiBut &)> set_execute_fn
float xmax
float xmin
float ymax
float ymin
int ymin
int xmin
void alignment_set(blender::ui::LayoutAlign alignment)
void active_set(bool active)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void enabled_set(bool enabled)
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiLayout & row(bool align)
uiFontStyle widget
int xy[2]
Definition WM_types.hh:761
struct wmEvent * eventstate
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_cursor_set(wmWindow *win, int curs)
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:52
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_Y_MOVE
Definition wm_cursors.hh:40
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
@ WM_CURSOR_X_MOVE
Definition wm_cursors.hh:39
GPUViewport * WM_draw_region_get_viewport(ARegion *region)
Definition wm_draw.cc:940
void WM_main_add_notifier(uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_gizmomap_draw(wmGizmoMap *gzmap, const bContext *C, const eWM_GizmoFlagMapDrawStep drawstep)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void wmOrtho2_pixelspace(const float x, const float y)
void wmOrtho2_region_pixelspace(const ARegion *region)