2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
21 * The Original Code is: all of this file.
22 * Contributor(s): Nathan Letwory
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/space_node/node_draw.c
29 * \brief higher level node drawing for the node editor.
32 #include "DNA_node_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_space_types.h"
35 #include "DNA_screen_types.h"
38 #include "BLI_blenlib.h"
40 #include "BLF_translation.h"
42 #include "BKE_context.h"
43 #include "BKE_depsgraph.h"
48 #include "BIF_glutil.h"
54 #include "ED_gpencil.h"
55 #include "ED_space_api.h"
57 #include "UI_resources.h"
58 #include "UI_view2d.h"
60 #include "RNA_access.h"
62 #include "node_intern.h" /* own include */
63 #include "COM_compositor.h"
65 /* width of socket columns in group display */
66 #define NODE_GROUP_FRAME 120
68 extern void ui_dropshadow(rctf *rct, float radius, float aspect, float alpha, int select);
70 /* XXX update functions for node editor are a mess, needs a clear concept */
71 void ED_node_tree_update(SpaceNode *snode, Scene *scene)
73 snode_set_context(snode, scene);
75 if (snode->nodetree && snode->nodetree->id.us == 0)
76 snode->nodetree->id.us = 1;
79 void ED_node_changed_update(ID *id, bNode *node)
81 bNodeTree *nodetree, *edittree;
84 node_tree_from_ID(id, &nodetree, &edittree, &treetype);
86 if (treetype == NTREE_SHADER) {
87 DAG_id_tag_update(id, 0);
89 if (GS(id->name) == ID_MA)
90 WM_main_add_notifier(NC_MATERIAL | ND_SHADING_DRAW, id);
91 else if (GS(id->name) == ID_LA)
92 WM_main_add_notifier(NC_LAMP | ND_LIGHTING_DRAW, id);
93 else if (GS(id->name) == ID_WO)
94 WM_main_add_notifier(NC_WORLD | ND_WORLD_DRAW, id);
96 else if (treetype == NTREE_COMPOSIT) {
98 nodeUpdate(edittree, node);
99 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
101 node = node_tree_get_editgroup(nodetree);
103 nodeUpdateID(nodetree, node->id);
105 WM_main_add_notifier(NC_SCENE | ND_NODES, id);
107 else if (treetype == NTREE_TEXTURE) {
108 DAG_id_tag_update(id, 0);
109 WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
113 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
120 for (node = ntree->nodes.first; node; node = node->next)
121 if (node->type == NODE_GROUP && node->id)
122 if (has_nodetree((bNodeTree *)node->id, lookup))
128 typedef struct NodeUpdateCalldata {
131 } NodeUpdateCalldata;
132 static void node_generic_update_cb(void *calldata, ID *owner_id, bNodeTree *ntree)
134 NodeUpdateCalldata *cd = (NodeUpdateCalldata *)calldata;
135 /* check if nodetree uses the group stored in calldata */
136 if (has_nodetree(ntree, cd->ntree))
137 ED_node_changed_update(owner_id, cd->node);
139 void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
141 bNodeTreeType *tti = ntreeGetType(ntree->type);
142 NodeUpdateCalldata cd;
145 /* look through all datablocks, to support groups */
146 tti->foreach_nodetree(bmain, &cd, node_generic_update_cb);
148 if (ntree->type == NTREE_TEXTURE)
149 ntreeTexCheckCyclics(ntree);
152 static int compare_nodes(bNode *a, bNode *b)
155 /* These tell if either the node or any of the parent nodes is selected.
156 * A selected parent means an unselected node is also in foreground!
158 int a_select = (a->flag & NODE_SELECT), b_select = (b->flag & NODE_SELECT);
159 int a_active = (a->flag & NODE_ACTIVE), b_active = (b->flag & NODE_ACTIVE);
161 /* if one is an ancestor of the other */
162 /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
163 for (parent = a->parent; parent; parent = parent->parent) {
164 /* if b is an ancestor, it is always behind a */
167 /* any selected ancestor moves the node forward */
168 if (parent->flag & NODE_ACTIVE)
170 if (parent->flag & NODE_SELECT)
173 for (parent = b->parent; parent; parent = parent->parent) {
174 /* if a is an ancestor, it is always behind b */
177 /* any selected ancestor moves the node forward */
178 if (parent->flag & NODE_ACTIVE)
180 if (parent->flag & NODE_SELECT)
184 /* if one of the nodes is in the background and the other not */
185 if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
187 else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
190 /* if one has a higher selection state (active > selected > nothing) */
191 if (!b_active && a_active)
193 else if (!b_select && (a_active || a_select))
199 /* Sorts nodes by selection: unselected nodes first, then selected,
200 * then the active node at the very end. Relative order is kept intact!
202 void ED_node_sort(bNodeTree *ntree)
204 /* merge sort is the algorithm of choice here */
205 bNode *first_a, *first_b, *node_a, *node_b, *tmp;
206 int totnodes = BLI_countlist(&ntree->nodes);
210 while (k < totnodes) {
211 first_a = first_b = ntree->nodes.first;
214 /* setup first_b pointer */
215 for (b = 0; b < k && first_b; ++b) {
216 first_b = first_b->next;
218 /* all batches merged? */
226 while (a < k && b < k && node_b) {
227 if (compare_nodes(node_a, node_b) == 0) {
228 node_a = node_a->next;
233 node_b = node_b->next;
235 BLI_remlink(&ntree->nodes, tmp);
236 BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
240 /* setup first pointers for next batch */
243 /* all nodes sorted? */
246 first_b = first_b->next;
256 static void do_node_internal_buttons(bContext *C, void *node_v, int event)
258 if (event == B_NODE_EXEC) {
259 SpaceNode *snode = CTX_wm_space_node(C);
260 if (snode && snode->id)
261 ED_node_changed_update(snode->id, node_v);
265 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
270 /* add node uiBlocks in drawing order - prevents events going to overlapping nodes */
272 for (node = ntree->nodes.first; node; node = node->next) {
274 BLI_snprintf(uiblockstr, sizeof(uiblockstr), "node buttons %p", (void *)node);
275 node->block = uiBeginBlock(C, CTX_wm_region(C), uiblockstr, UI_EMBOSS);
276 uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
278 /* this cancels events for background nodes */
279 uiBlockSetFlag(node->block, UI_BLOCK_CLIP_EVENTS);
283 /* based on settings in node, sets drawing rect info. each redraw! */
284 static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
293 /* get "global" coords */
294 nodeToView(node, 0.0f, 0.0f, &locx, &locy);
300 /* little bit space in top */
301 if (node->outputs.first)
305 for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
306 if (!nodeSocketIsHidden(nsock)) {
307 nsock->locx = locx + node->width;
308 nsock->locy = dy - NODE_DYS;
313 node->prvr.xmin = locx + NODE_DYS;
314 node->prvr.xmax = locx + node->width - NODE_DYS;
317 if (node->flag & NODE_PREVIEW) {
318 if (node->preview && node->preview->rect) {
321 if (node->preview && node->preview->xsize && node->preview->ysize)
322 aspect = (float)node->preview->ysize / (float)node->preview->xsize;
325 node->prvr.ymax = dy;
328 node->prvr.ymin = dy - aspect * (node->width - NODE_DY);
330 /* width correction of image */
331 float dx = (node->width - NODE_DYS) - (node->width - NODE_DYS) / aspect;
333 node->prvr.ymin = dy - (node->width - NODE_DY);
335 node->prvr.xmin += 0.5f * dx;
336 node->prvr.xmax -= 0.5f * dx;
339 dy = node->prvr.ymin - NODE_DYS / 2;
341 /* make sure that maximums are bigger or equal to minimums */
342 if (node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
343 if (node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
346 float oldh = node->prvr.ymax - node->prvr.ymin;
348 oldh = 0.6f * node->width - NODE_DY;
350 node->prvr.ymax = dy;
351 node->prvr.ymin = dy - oldh;
352 dy = node->prvr.ymin - NODE_DYS / 2;
357 if ((node->flag & NODE_OPTIONS) && node->typeinfo->uifunc) {
360 /* set this for uifunc() that don't use layout engine yet */
362 node->butr.xmax = node->width - 2 * NODE_DYS;
366 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
368 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
369 locx + NODE_DYS, dy, node->butr.xmax, NODE_DY, UI_GetStyle());
370 uiLayoutSetContextPointer(layout, "node", &ptr);
372 node->typeinfo->uifunc(layout, (bContext *)C, &ptr);
374 uiBlockEndAlign(node->block);
375 uiBlockLayoutResolve(node->block, NULL, &buty);
377 dy = buty - NODE_DYS / 2;
381 for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
382 if (!nodeSocketIsHidden(nsock)) {
384 nsock->locy = dy - NODE_DYS;
389 /* little bit space in end */
390 if (node->inputs.first || (node->flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0)
393 node->totr.xmin = locx;
394 node->totr.xmax = locx + node->width;
395 node->totr.ymax = locy;
396 node->totr.ymin = minf(dy, locy - 2 * NODE_DY);
398 /* Set the block bounds to clip mouse events from underlying nodes.
399 * Add a margin for sockets on each side.
401 uiExplicitBoundsBlock(node->block,
402 node->totr.xmin - NODE_SOCKSIZE,
404 node->totr.xmax + NODE_SOCKSIZE,
408 /* based on settings in node, sets drawing rect info. each redraw! */
409 static void node_update_hidden(bNode *node)
413 float rad, drad, hiddenrad = HIDDEN_RAD;
414 int totin = 0, totout = 0, tot;
416 /* get "global" coords */
417 nodeToView(node, 0.0f, 0.0f, &locx, &locy);
419 /* calculate minimal radius */
420 for (nsock = node->inputs.first; nsock; nsock = nsock->next)
421 if (!nodeSocketIsHidden(nsock))
423 for (nsock = node->outputs.first; nsock; nsock = nsock->next)
424 if (!nodeSocketIsHidden(nsock))
427 tot = MAX2(totin, totout);
429 hiddenrad += 5.0f * (float)(tot - 4);
432 node->totr.xmin = locx;
433 node->totr.xmax = locx + 3 * hiddenrad + node->miniwidth;
434 node->totr.ymax = locy + (hiddenrad - 0.5f * NODE_DY);
435 node->totr.ymin = node->totr.ymax - 2 * hiddenrad;
438 rad = drad = (float)M_PI / (1.0f + (float)totout);
440 for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
441 if (!nodeSocketIsHidden(nsock)) {
442 nsock->locx = node->totr.xmax - hiddenrad + (float)sin(rad) * hiddenrad;
443 nsock->locy = node->totr.ymin + hiddenrad + (float)cos(rad) * hiddenrad;
449 rad = drad = -(float)M_PI / (1.0f + (float)totin);
451 for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
452 if (!nodeSocketIsHidden(nsock)) {
453 nsock->locx = node->totr.xmin + hiddenrad + (float)sin(rad) * hiddenrad;
454 nsock->locy = node->totr.ymin + hiddenrad + (float)cos(rad) * hiddenrad;
459 /* Set the block bounds to clip mouse events from underlying nodes.
460 * Add a margin for sockets on each side.
462 uiExplicitBoundsBlock(node->block,
463 node->totr.xmin - NODE_SOCKSIZE,
465 node->totr.xmax + NODE_SOCKSIZE,
469 void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
471 if (node->flag & NODE_HIDDEN)
472 node_update_hidden(node);
474 node_update_basis(C, ntree, node);
477 int node_select_area_default(bNode *node, int x, int y)
479 return BLI_in_rctf(&node->totr, x, y);
482 int node_tweak_area_default(bNode *node, int x, int y)
484 return BLI_in_rctf(&node->totr, x, y);
487 int node_get_colorid(bNode *node)
489 if (node->typeinfo->nclass == NODE_CLASS_INPUT)
490 return TH_NODE_IN_OUT;
491 if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) {
492 if (node->flag & NODE_DO_OUTPUT)
493 return TH_NODE_IN_OUT;
497 if (node->typeinfo->nclass == NODE_CLASS_CONVERTOR)
498 return TH_NODE_CONVERTOR;
499 if (ELEM3(node->typeinfo->nclass, NODE_CLASS_OP_COLOR, NODE_CLASS_OP_VECTOR, NODE_CLASS_OP_FILTER))
500 return TH_NODE_OPERATOR;
501 if (node->typeinfo->nclass == NODE_CLASS_GROUP)
502 return TH_NODE_GROUP;
506 /* note: in cmp_util.c is similar code, for node_compo_pass_on()
507 * the same goes for shader and texture nodes. */
508 /* note: in node_edit.c is similar code, for untangle node */
509 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
514 if (node->typeinfo->internal_connect == NULL)
517 /* Get default muting links. */
518 links = node->typeinfo->internal_connect(snode->edittree, node);
521 glEnable(GL_LINE_SMOOTH);
523 for (link = links.first; link; link = link->next)
524 node_draw_link_bezier(v2d, snode, link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
527 glDisable(GL_LINE_SMOOTH);
529 BLI_freelistN(&links);
532 /* this might have some more generic use */
533 static void node_circle_draw(float x, float y, float size, char *col, int highlight)
535 /* 16 values of sin function */
536 static float si[16] = {
537 0.00000000f, 0.39435585f, 0.72479278f, 0.93775213f,
538 0.99871650f, 0.89780453f, 0.65137248f, 0.29936312f,
539 -0.10116832f, -0.48530196f, -0.79077573f, -0.96807711f,
540 -0.98846832f, -0.84864425f, -0.57126821f, -0.20129852f
542 /* 16 values of cos function */
543 static float co[16] = {
544 1.00000000f, 0.91895781f, 0.68896691f, 0.34730525f,
545 -0.05064916f, -0.44039415f, -0.75875812f, -0.95413925f,
546 -0.99486932f, -0.87434661f, -0.61210598f, -0.25065253f,
547 0.15142777f, 0.52896401f, 0.82076344f, 0.97952994f,
551 glColor3ub(col[0], col[1], col[2]);
554 for (a = 0; a < 16; a++)
555 glVertex2f(x + size * si[a], y + size * co[a]);
559 UI_ThemeColor(TH_TEXT_HI);
563 glColor4ub(0, 0, 0, 150);
566 glEnable(GL_LINE_SMOOTH);
567 glBegin(GL_LINE_LOOP);
568 for (a = 0; a < 16; a++)
569 glVertex2f(x + size * si[a], y + size * co[a]);
571 glDisable(GL_LINE_SMOOTH);
576 void node_socket_circle_draw(bNodeTree *UNUSED(ntree), bNodeSocket *sock, float size, int highlight)
578 bNodeSocketType *stype = ntreeGetSocketType(sock->type);
579 node_circle_draw(sock->locx, sock->locy, size, stype->ui_color, highlight);
582 /* ************** Socket callbacks *********** */
585 static void node_draw_preview(bNodePreview *preview, rctf *prv)
587 float xscale = (prv->xmax - prv->xmin) / ((float)preview->xsize);
588 float yscale = (prv->ymax - prv->ymin) / ((float)preview->ysize);
589 float tile = (prv->xmax - prv->xmin) / 10.0f;
592 /* draw checkerboard backdrop to show alpha */
593 glColor3ub(120, 120, 120);
594 glRectf(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
595 glColor3ub(160, 160, 160);
597 for (y = prv->ymin; y < prv->ymax; y += tile * 2) {
598 for (x = prv->xmin; x < prv->xmax; x += tile * 2) {
599 float tilex = tile, tiley = tile;
601 if (x + tile > prv->xmax)
602 tilex = prv->xmax - x;
603 if (y + tile > prv->ymax)
604 tiley = prv->ymax - y;
606 glRectf(x, y, x + tilex, y + tiley);
609 for (y = prv->ymin + tile; y < prv->ymax; y += tile * 2) {
610 for (x = prv->xmin + tile; x < prv->xmax; x += tile * 2) {
611 float tilex = tile, tiley = tile;
613 if (x + tile > prv->xmax)
614 tilex = prv->xmax - x;
615 if (y + tile > prv->ymax)
616 tiley = prv->ymax - y;
618 glRectf(x, y, x + tilex, y + tiley);
622 glPixelZoom(xscale, yscale);
625 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* premul graphics */
627 glColor4f(1.0, 1.0, 1.0, 1.0);
628 glaDrawPixelsTex(prv->xmin, prv->ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, preview->rect);
631 glPixelZoom(1.0f, 1.0f);
633 UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
634 fdrawbox(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
638 /* common handle function for operator buttons that need to select the node first */
639 static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
641 bNode *node = (bNode *)node_argv;
642 const char *opname = (const char *)op_argv;
644 /* select & activate only the button's node */
645 node_select_single(C, node);
647 WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
650 void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha)
652 rctf *rct = &node->totr;
654 uiSetRoundBox(UI_CNR_ALL);
655 if (node->parent == NULL)
656 ui_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
658 const float margin = 3.0f;
660 glColor4f(0.0f, 0.0f, 0.0f, 0.33f);
662 uiRoundBox(rct->xmin - margin, rct->ymin - margin,
663 rct->xmax + margin, rct->ymax + margin, radius + margin);
668 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
671 rctf *rct = &node->totr;
673 /* float socket_size= NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
674 float iconbutw = 0.8f * UI_UNIT_X;
675 int color_id = node_get_colorid(node);
676 char showname[128]; /* 128 used below */
677 View2D *v2d = &ar->v2d;
679 /* hurmf... another candidate for callback, have to see how this works first */
680 if (node->id && node->block && snode->treetype == NTREE_SHADER)
681 nodeShaderSynchronizeID(node, 0);
683 /* skip if out of view */
684 if (BLI_rctf_isect(&node->totr, &ar->v2d.cur, NULL) == FALSE) {
685 uiEndBlock(C, node->block);
691 node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
694 if (color_id == TH_NODE)
695 UI_ThemeColorShade(color_id, -20);
697 UI_ThemeColor(color_id);
699 if (node->flag & NODE_MUTED)
700 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
702 #ifdef WITH_COMPOSITOR
703 if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
704 if (COM_isHighlightedbNode(node)) {
705 UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
710 uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
711 uiRoundBox(rct->xmin, rct->ymax - NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
713 /* show/hide icons */
714 iconofs = rct->xmax - 7.0f;
717 if (node->typeinfo->flag & NODE_PREVIEW) {
720 uiBlockSetEmboss(node->block, UI_EMBOSSN);
721 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_MATERIAL,
722 iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
723 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_preview_toggle");
724 /* XXX this does not work when node is activated and the operator called right afterwards,
725 * since active ID is not updated yet (needs to process the notifier).
726 * This can only work as visual indicator!
728 // if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT)))
729 // uiButSetFlag(but, UI_BUT_DISABLED);
730 uiBlockSetEmboss(node->block, UI_EMBOSS);
733 if (node->type == NODE_GROUP) {
736 uiBlockSetEmboss(node->block, UI_EMBOSSN);
737 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_NODETREE,
738 iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
739 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit");
740 uiBlockSetEmboss(node->block, UI_EMBOSS);
744 if (node->flag & SELECT)
745 UI_ThemeColor(TH_SELECT);
747 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
749 /* open/close entirely? */
752 int but_size = UI_UNIT_X * 0.6f;
753 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
754 uiBlockSetEmboss(node->block, UI_EMBOSSN);
755 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
756 rct->xmin + 10.0f - but_size / 2, rct->ymax - NODE_DY / 2.0f - but_size / 2,
757 but_size, but_size, NULL, 0, 0, 0, 0, "");
758 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
759 uiBlockSetEmboss(node->block, UI_EMBOSS);
761 /* custom draw function for this button */
762 UI_DrawTriIcon(rct->xmin + 10.0f, rct->ymax - NODE_DY / 2.0f, 'v');
765 /* this isn't doing anything for the label, so commenting out */
767 if (node->flag & SELECT)
768 UI_ThemeColor(TH_TEXT_HI);
770 UI_ThemeColor(TH_TEXT);
773 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
775 //if (node->flag & NODE_MUTED)
776 // BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
778 uiDefBut(node->block, LABEL, 0, showname,
779 (int)(rct->xmin + (NODE_MARGIN_X / snode->aspect_sqrt)), (int)(rct->ymax - NODE_DY),
780 (short)(iconofs - rct->xmin - 18.0f), (short)NODE_DY,
781 NULL, 0, 0, 0, 0, "");
784 if (node->flag & NODE_CUSTOM_COLOR)
785 glColor3fv(node->color);
787 UI_ThemeColor4(TH_NODE);
789 uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
790 uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax - NODE_DY, BASIS_RAD);
793 /* outline active and selected emphasis */
794 if (node->flag & SELECT) {
796 glEnable(GL_LINE_SMOOTH);
798 if (node->flag & NODE_ACTIVE)
799 UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
801 UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
802 uiSetRoundBox(UI_CNR_ALL);
803 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
805 glDisable(GL_LINE_SMOOTH);
810 if (node->flag & NODE_MUTED)
811 node_draw_mute_line(v2d, snode, node);
814 /* socket inputs, buttons */
815 for (sock = node->inputs.first; sock; sock = sock->next) {
816 if (nodeSocketIsHidden(sock))
819 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE, sock->flag & SELECT);
821 node->typeinfo->drawinputfunc(C, node->block, ntree, node, sock, IFACE_(sock->name),
822 sock->locx + (NODE_DYS / snode->aspect_sqrt), sock->locy - NODE_DYS,
823 node->width - NODE_DY);
827 for (sock = node->outputs.first; sock; sock = sock->next) {
828 if (nodeSocketIsHidden(sock))
831 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE, sock->flag & SELECT);
833 node->typeinfo->drawoutputfunc(C, node->block, ntree, node, sock, IFACE_(sock->name),
834 sock->locx - node->width + (NODE_DYS / snode->aspect_sqrt), sock->locy - NODE_DYS,
835 node->width - NODE_DY);
839 if (node->flag & NODE_PREVIEW) {
840 if (node->preview && node->preview->rect && !BLI_rctf_is_empty(&node->prvr))
841 node_draw_preview(node->preview, &node->prvr);
844 UI_ThemeClearColor(color_id);
846 uiEndBlock(C, node->block);
847 uiDrawBlock(C, node->block);
851 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
854 rctf *rct = &node->totr;
855 float dx, centy = 0.5f * (rct->ymax + rct->ymin);
856 float hiddenrad = 0.5f * (rct->ymax - rct->ymin);
857 float socket_size = NODE_SOCKSIZE * U.dpi / 72;
858 int color_id = node_get_colorid(node);
859 char showname[128]; /* 128 is used below */
862 node_draw_shadow(snode, node, hiddenrad, 1.0f);
865 UI_ThemeColor(color_id);
866 if (node->flag & NODE_MUTED)
867 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
869 #ifdef WITH_COMPOSITOR
870 if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
871 if (COM_isHighlightedbNode(node)) {
872 UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
879 uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
881 /* outline active and selected emphasis */
882 if (node->flag & SELECT) {
884 glEnable(GL_LINE_SMOOTH);
886 if (node->flag & NODE_ACTIVE)
887 UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
889 UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
890 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
892 glDisable(GL_LINE_SMOOTH);
897 if (node->flag & SELECT)
898 UI_ThemeColor(TH_SELECT);
900 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
902 /* open entirely icon */
905 int but_size = UI_UNIT_X * 0.6f;
906 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
907 uiBlockSetEmboss(node->block, UI_EMBOSSN);
908 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
909 rct->xmin + 10.0f - but_size / 2, centy - but_size / 2,
910 but_size, but_size, NULL, 0, 0, 0, 0, "");
911 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
912 uiBlockSetEmboss(node->block, UI_EMBOSS);
914 /* custom draw function for this button */
915 UI_DrawTriIcon(rct->xmin + 10.0f, centy, 'h');
919 if (node->flag & NODE_MUTED)
920 node_draw_mute_line(&ar->v2d, snode, node);
922 if (node->flag & SELECT)
923 UI_ThemeColor(TH_SELECT);
925 UI_ThemeColor(TH_TEXT);
927 if (node->miniwidth > 0.0f) {
928 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
930 //if (node->flag & NODE_MUTED)
931 // BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
933 uiDefBut(node->block, LABEL, 0, showname,
934 (int)(rct->xmin + (NODE_MARGIN_X / snode->aspect_sqrt)), (int)(centy - 10),
935 (short)(rct->xmax - rct->xmin - 18.0f - 12.0f), (short)NODE_DY,
936 NULL, 0, 0, 0, 0, "");
939 /* scale widget thing */
940 UI_ThemeColorShade(color_id, -10);
942 fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
943 fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
945 UI_ThemeColorShade(color_id, +30);
947 fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
948 fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
951 for (sock = node->inputs.first; sock; sock = sock->next) {
952 if (!nodeSocketIsHidden(sock))
953 node_socket_circle_draw(snode->nodetree, sock, socket_size, sock->flag & SELECT);
956 for (sock = node->outputs.first; sock; sock = sock->next) {
957 if (!nodeSocketIsHidden(sock))
958 node_socket_circle_draw(snode->nodetree, sock, socket_size, sock->flag & SELECT);
961 uiEndBlock(C, node->block);
962 uiDrawBlock(C, node->block);
966 int node_get_resize_cursor(int directions)
970 else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0)
971 return CURSOR_Y_MOVE;
972 else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0)
973 return CURSOR_X_MOVE;
978 void node_set_cursor(wmWindow *win, SpaceNode *snode)
980 bNodeTree *ntree = snode->edittree;
983 int cursor = CURSOR_STD;
986 if (node_find_indicated_socket(snode, &node, &sock, SOCK_IN | SOCK_OUT)) {
990 /* check nodes front to back */
991 for (node = ntree->nodes.last; node; node = node->prev) {
992 if (BLI_in_rctf(&node->totr, snode->mx, snode->my))
993 break; /* first hit on node stops */
996 int dir = node->typeinfo->resize_area_func(node, snode->mx, snode->my);
997 cursor = node_get_resize_cursor(dir);
1002 WM_cursor_set(win, cursor);
1005 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
1007 if (node->flag & NODE_HIDDEN)
1008 node_draw_hidden(C, ar, snode, ntree, node);
1010 node_draw_basis(C, ar, snode, ntree, node);
1013 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
1015 if (node->typeinfo->drawupdatefunc)
1016 node->typeinfo->drawupdatefunc(C, ntree, node);
1019 void node_update_nodetree(const bContext *C, bNodeTree *ntree, float offsetx, float offsety)
1023 /* update nodes front to back, so children sizes get updated before parents */
1024 for (node = ntree->nodes.last; node; node = node->prev) {
1025 /* XXX little hack */
1026 node->locx += offsetx;
1027 node->locy += offsety;
1029 node_update(C, ntree, node);
1031 node->locx -= offsetx;
1032 node->locy -= offsety;
1036 static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
1038 if (node->typeinfo->drawfunc)
1039 node->typeinfo->drawfunc(C, ar, snode, ntree, node);
1042 void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
1048 if (ntree == NULL) return; /* groups... */
1050 /* draw background nodes, last nodes in front */
1051 for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1052 if (!(node->flag & NODE_BACKGROUND))
1054 node->nr = a; /* index of node in list, used for exec event code */
1055 node_draw(C, ar, snode, ntree, node);
1060 glEnable(GL_LINE_SMOOTH);
1061 for (link = ntree->links.first; link; link = link->next)
1062 node_draw_link(&ar->v2d, snode, link);
1063 glDisable(GL_LINE_SMOOTH);
1064 glDisable(GL_BLEND);
1066 /* draw foreground nodes, last nodes in front */
1067 for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1068 if (node->flag & NODE_BACKGROUND)
1070 node->nr = a; /* index of node in list, used for exec event code */
1071 node_draw(C, ar, snode, ntree, node);
1075 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
1077 View2DScrollers *scrollers;
1078 SpaceNode *snode = CTX_wm_space_node(C);
1079 bNodeLinkDrag *nldrag;
1082 UI_ThemeClearColor(TH_BACK);
1083 glClear(GL_COLOR_BUFFER_BIT);
1085 UI_view2d_view_ortho(v2d);
1087 //uiFreeBlocksWin(&sa->uiblocks, sa->win);
1089 ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
1092 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1093 glEnable(GL_MAP1_VERTEX_3);
1095 /* aspect+font, set each time */
1096 snode->aspect = (v2d->cur.xmax - v2d->cur.xmin) / ((float)ar->winx);
1097 snode->aspect_sqrt = sqrtf(snode->aspect);
1098 // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
1101 UI_view2d_multi_grid_draw(v2d, 25.0f, 5, 2);
1104 draw_nodespace_back_pix(C, ar, snode);
1107 snode_set_context(snode, CTX_data_scene(C));
1109 if (snode->nodetree) {
1111 /* void** highlights = 0; */ /* UNUSED */
1113 node_uiblocks_init(C, snode->nodetree);
1115 /* uiBlocks must be initialized in drawing order for correct event clipping.
1116 * Node group internal blocks added after the main group block.
1118 for (node = snode->nodetree->nodes.first; node; node = node->next) {
1119 if (node->flag & NODE_GROUP_EDIT)
1120 node_uiblocks_init(C, (bNodeTree *)node->id);
1123 node_update_nodetree(C, snode->nodetree, 0.0f, 0.0f);
1125 #ifdef WITH_COMPOSITOR
1126 if (snode->nodetree->type == NTREE_COMPOSIT) {
1127 COM_startReadHighlights();
1131 node_draw_nodetree(C, ar, snode, snode->nodetree);
1135 for (node = snode->nodetree->nodes.first; node; node = node->next) {
1136 if (node->flag & NODE_GROUP_EDIT)
1137 node_draw_group(C, ar, snode, snode->nodetree, node);
1142 /* temporary links */
1144 glEnable(GL_LINE_SMOOTH);
1145 for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) {
1146 for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next) {
1147 node_draw_link(&ar->v2d, snode, (bNodeLink *)linkdata->data);
1150 glDisable(GL_LINE_SMOOTH);
1151 glDisable(GL_BLEND);
1153 ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
1155 /* draw grease-pencil ('canvas' strokes) */
1156 if (snode->nodetree)
1157 draw_gpencil_view2d(C, 1);
1159 /* reset view matrix */
1160 UI_view2d_view_restore(C);
1162 /* draw grease-pencil (screen strokes, and also paintbuffer) */
1163 if (snode->nodetree)
1164 draw_gpencil_view2d(C, 0);
1167 scrollers = UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1168 UI_view2d_scrollers_draw(C, v2d, scrollers);
1169 UI_view2d_scrollers_free(scrollers);