From 231c72b5eb2c81a3e5e21db019db029e9640cc48 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Tue, 23 Aug 2016 10:15:47 -0300 Subject: [PATCH] prettier connections for graph edit --- scene/gui/graph_edit.cpp | 180 +++++++++++++----- scene/gui/graph_edit.h | 7 +- .../resources/default_theme/default_theme.cpp | 2 + scene/resources/default_theme/graph_node.png | Bin 752 -> 762 bytes .../default_theme/graph_node_selected.png | Bin 920 -> 933 bytes scene/resources/default_theme/theme_data.h | 4 +- 6 files changed, 139 insertions(+), 54 deletions(-) diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 458e51b4fd..6a4bf0f4a2 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -61,6 +61,7 @@ Error GraphEdit::connect_node(const StringName& p_from, int p_from_port,const St c.to_port=p_to_port; connections.push_back(c); top_layer->update(); + update(); return OK; } @@ -85,6 +86,7 @@ void GraphEdit::disconnect_node(const StringName& p_from, int p_from_port,const connections.erase(E); top_layer->update(); + update(); return; } } @@ -118,10 +120,7 @@ void GraphEdit::_scroll_moved(double) { _update_scroll_offset(); top_layer->update(); - if (is_using_snap()) { - //must redraw grid - update(); - } + update(); if (!setting_scroll_ofs) {//in godot, signals on change value are avoided as a convention emit_signal("scroll_offset_changed",get_scroll_ofs()); @@ -205,6 +204,7 @@ void GraphEdit::_graph_node_moved(Node *p_gn) { GraphNode *gn=p_gn->cast_to(); ERR_FAIL_COND(!gn); top_layer->update(); + update(); } void GraphEdit::add_child_notify(Node *p_child) { @@ -306,11 +306,60 @@ void GraphEdit::_notification(int p_what) { } + { + //draw connections + List::Element* > to_erase; + for(List::Element *E=connections.front();E;E=E->next()) { + + NodePath fromnp(E->get().from); + + Node * from = get_node(fromnp); + if (!from) { + to_erase.push_back(E); + continue; + } + + GraphNode *gfrom = from->cast_to(); + + if (!gfrom) { + to_erase.push_back(E); + continue; + } + + NodePath tonp(E->get().to); + Node * to = get_node(tonp); + if (!to) { + to_erase.push_back(E); + continue; + } + + GraphNode *gto = to->cast_to(); + + if (!gto) { + to_erase.push_back(E); + continue; + } + + Vector2 frompos=gfrom->get_connection_output_pos(E->get().from_port)+gfrom->get_pos(); + Color color = gfrom->get_connection_output_color(E->get().from_port); + Vector2 topos=gto->get_connection_input_pos(E->get().to_port)+gto->get_pos(); + Color tocolor = gto->get_connection_input_color(E->get().to_port); + _draw_cos_line(this,frompos,topos,color,tocolor); + + } + + while(to_erase.size()) { + connections.erase(to_erase.front()->get()); + to_erase.pop_front(); + } + } + } if (p_what==NOTIFICATION_RESIZED) { _update_scroll(); top_layer->update(); + } } @@ -466,7 +515,7 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { connecting_to=Vector2(p_ev.mouse_motion.x,p_ev.mouse_motion.y); connecting_target=false; - top_layer->update(); + top_layer->update(); Ref port =get_icon("port","GraphNode"); Vector2 mpos(p_ev.mouse_button.x,p_ev.mouse_button.y); @@ -529,15 +578,82 @@ void GraphEdit::_top_layer_input(const InputEvent& p_ev) { } connecting=false; top_layer->update(); + update(); } } -void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color,const Color& p_to_color) { + +template +static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { + /* Formula from Wikipedia article on Bezier curves. */ + real_t omt = (1.0 - t); + real_t omt2 = omt*omt; + real_t omt3 = omt2*omt; + real_t t2 = t*t; + real_t t3 = t2*t; + + return start * omt3 + + control_1 * omt2 * t * 3.0 + + control_2 * omt * t2 * 3.0 + + end * t3; +} + + +void GraphEdit::_bake_segment2d(CanvasItem* p_where,float p_begin, float p_end,const Vector2& p_a,const Vector2& p_out,const Vector2& p_b, const Vector2& p_in,int p_depth,int p_min_depth,int p_max_depth,float p_tol,const Color& p_color,const Color& p_to_color,int &lines) const { + + float mp = p_begin+(p_end-p_begin)*0.5; + Vector2 beg = _bezier_interp(p_begin,p_a,p_a+p_out,p_b+p_in,p_b); + Vector2 mid = _bezier_interp(mp,p_a,p_a+p_out,p_b+p_in,p_b); + Vector2 end = _bezier_interp(p_end,p_a,p_a+p_out,p_b+p_in,p_b); + + Vector2 na = (mid-beg).normalized(); + Vector2 nb = (end-mid).normalized(); + float dp = Math::rad2deg(Math::acos(na.dot(nb))); + + if (p_depth>=p_min_depth && ( dp=p_max_depth)) { + + + + p_where->draw_line(beg,end,p_color.linear_interpolate(p_to_color,mp),2); + lines++; + } else { + _bake_segment2d(p_where,p_begin,mp,p_a,p_out,p_b,p_in,p_depth+1,p_min_depth,p_max_depth,p_tol,p_color,p_to_color,lines); + _bake_segment2d(p_where,mp,p_end,p_a,p_out,p_b,p_in,p_depth+1,p_min_depth,p_max_depth,p_tol,p_color,p_to_color,lines); + } +} + + +void GraphEdit::_draw_cos_line(CanvasItem* p_where,const Vector2& p_from, const Vector2& p_to,const Color& p_color,const Color& p_to_color) { + +#if 1 + + //cubic bezier code + float diff = p_to.x-p_from.x; + float cp_offset; + int cp_len = get_constant("bezier_len_pos"); + int cp_neg_len = get_constant("bezier_len_neg"); + + if (diff>0) { + cp_offset=MAX(cp_len,diff*0.5); + } else { + cp_offset=MAX(MIN(cp_len-diff,cp_neg_len),-diff*0.5); + } + + Vector2 c1 = Vector2(cp_offset,0); + Vector2 c2 = Vector2(-cp_offset,0); + + int lines=0; + _bake_segment2d(p_where,0,1,p_from,c1,p_to,c2,0,5,12,8,p_color,p_to_color,lines); + //print_line("used lines: "+itos(lines)); + + +#else static const int steps = 20; + //old cosine code Rect2 r; r.pos=p_from; r.expand_to(p_to); @@ -547,6 +663,7 @@ void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Vector2 prev; for(int i=0;i<=steps;i++) { + float d = i/float(steps); float c=-Math::cos(d*Math_PI) * 0.5+0.5; if (flip) @@ -555,11 +672,12 @@ void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const if (i>0) { - top_layer->draw_line(prev,p,p_color.linear_interpolate(p_to_color,d),2); + p_where->draw_line(prev,p,p_color.linear_interpolate(p_to_color,d),2); } prev=p; } +#endif } void GraphEdit::_top_layer_draw() { @@ -589,53 +707,10 @@ void GraphEdit::_top_layer_draw() { col.g+=0.4; col.b+=0.4; } - _draw_cos_line(pos,topos,col,col); + _draw_cos_line(top_layer,pos,topos,col,col); } - List::Element* > to_erase; - for(List::Element *E=connections.front();E;E=E->next()) { - NodePath fromnp(E->get().from); - - Node * from = get_node(fromnp); - if (!from) { - to_erase.push_back(E); - continue; - } - - GraphNode *gfrom = from->cast_to(); - - if (!gfrom) { - to_erase.push_back(E); - continue; - } - - NodePath tonp(E->get().to); - Node * to = get_node(tonp); - if (!to) { - to_erase.push_back(E); - continue; - } - - GraphNode *gto = to->cast_to(); - - if (!gto) { - to_erase.push_back(E); - continue; - } - - Vector2 frompos=gfrom->get_connection_output_pos(E->get().from_port)+gfrom->get_pos(); - Color color = gfrom->get_connection_output_color(E->get().from_port); - Vector2 topos=gto->get_connection_input_pos(E->get().to_port)+gto->get_pos(); - Color tocolor = gto->get_connection_input_color(E->get().to_port); - _draw_cos_line(frompos,topos,color,tocolor); - - } - - while(to_erase.size()) { - connections.erase(to_erase.front()->get()); - to_erase.pop_front(); - } if (box_selecting) top_layer->draw_rect(box_selecting_rect,Color(0.7,0.7,1.0,0.3)); } @@ -765,6 +840,7 @@ void GraphEdit::_input_event(const InputEvent& p_ev) { dragging = false; top_layer->update(); + update(); } if (b.button_index==BUTTON_LEFT && b.pressed) { @@ -1173,4 +1249,6 @@ GraphEdit::GraphEdit() { setting_scroll_ofs=false; + + } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 6d35e1518f..9f5dd56ed5 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -112,7 +112,9 @@ private: bool updating; List connections; - void _draw_cos_line(const Vector2& p_from, const Vector2& p_to, const Color& p_color, const Color &p_to_color); + void _bake_segment2d(CanvasItem* p_where,float p_begin, float p_end, const Vector2& p_a, const Vector2& p_out, const Vector2& p_b, const Vector2& p_in, int p_depth, int p_min_depth, int p_max_depth, float p_tol, const Color& p_color, const Color& p_to_color, int &lines) const; + + void _draw_cos_line(CanvasItem* p_where,const Vector2& p_from, const Vector2& p_to, const Color& p_color, const Color &p_to_color); void _graph_node_raised(Node* p_gn); void _graph_node_moved(Node *p_gn); @@ -128,6 +130,9 @@ private: Array _get_connection_list() const; + bool lines_on_bg; + + struct ConnType { union { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 2033599307..90f63a5053 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -930,6 +930,8 @@ void fill_default_theme(Ref& t,const Ref & default_font,const Refset_stylebox("bg","GraphEdit", make_stylebox( tree_bg_png,4,4,4,5) ); t->set_color("grid_minor","GraphEdit", Color(1,1,1,0.05) ); t->set_color("grid_major","GraphEdit", Color(1,1,1,0.2) ); + t->set_constant("bezier_len_pos","GraphEdit", 80*scale ); + t->set_constant("bezier_len_neg","GraphEdit", 160*scale ); diff --git a/scene/resources/default_theme/graph_node.png b/scene/resources/default_theme/graph_node.png index ed0b6a6cd24bcae2d5994cc5ca49ebc2fa8fc707..d4b4dd3c1fc288fc55d2158620981e7769e53d62 100644 GIT binary patch delta 674 zcmeys`ipgf11E<#FRRc(i{{xAJvHh}JY5_^A`ZWuZkvBOLEw13_-r**rL|lfAA?F6 z#8f6|O{-yKJ96%bID_E^0|w=$R#r}y_=Zkd!w;+x0jdF!4GSNw)iCj5RxNw=+>*_{ zEcdu){EZ3u8^1o;dH?R;_ewuobcGkNRlL)D;>*Y_@jb+7de(uu1U?I%Ee*A-2h=Xq zvs*cc|6snMk|k_mTAc7LYw^7u7N(*0j-9;i52q{e#I5`P%9hoRN&1nJ?wPZ4H5Ek< zzbviZE?+Y*&-BzE$uG?B?(DbxpZWA~cV7Nmwhc-S%n>$KPkyXU_n%k3_=FJe;-Cim zr;mS|u8Y}i^ZoDf>)Zz>y)Vi5HSfO6AHLlk$K30=v?5!lChj~%Sw3~~*D z(!Y;09gni^NIReWS*Ss6|B9_ExH^2Co0D@=ehI?{ z%cCb3YIMen)L)D;H|qSGmJ&YzjHWo17 zuJ!7jmzEP>l-8m^bB9)sXlnvP?~d zTP8a5Z2woO`+2{=x5e8y;{Ers*$k%tE^0Zju3!*dz!cKJ^&!5VX~`4eitCa$Y=7Kh zNoYJEA$%)?Jx)*}*-3#R=j_@|*^U+w?@GdNPgXE%@+;pe+tQqxz-w^wnHBS#Y}0gk z^H-PCZO&_pUYAI2F8Hu-|Ha*WdswfjZa%cNA^Cvf0=u-Ie2w!Dm~VHN*5r7l{`zopr0Dmk delta 664 zcmeyx`hj(V11GyIBZq)ozL54rPmTI4PZ!6Kh{JEE@6Hnr6lmN3QEy{&Yci|ABc-Jb zQYsUa<~1^14F1TZz$L}Cz~#aPg~kW0D_#h)o^McUbYd2nxPzrNqEE5n`px3DM|1AJ zS$F+hE8km_-Bxy=&z`YQ&UC+F#wg|RV7}8ukqr$W{Z-3XGRm+#Yh3KWZV|wg!^oUa z&-;S0o?*6IE_3=bzS8yEXRSSLY(t53~;sPO3C;oCKFKYy&c9>1S;4aWs0v3_~$pU;kNp8i&4qsrnXLIUSn zS`wSH&DZ7C{r~t?XoEyfF6!Rm#HB~k}goy}X3 zqW>;dv&+bmIeweIk85$%4MPvcgf+S!Sp!R5GotqUolp4Pq#af4x9t&s5YvsOMQesjqar%kX1cQ-|N{rnJsg)+d5I9K*`aBvhA_u95O|FDhbt^gPV+pL;<9qZ3an*4BI&1kxS?Mj1G0CU!Xdd>-5{ts-^6Ta6!%noi~RZ8~BnH*c8 zn$XkP$gzQU|EYEE9}6niZa-zx;hV5_yO#+6^v2QyOJ={8DOh;=hOW@Jyt(|t<8R_V Z(`8knIl8AZFfcH9y85}WtaD0e0s!z+FkS!v diff --git a/scene/resources/default_theme/graph_node_selected.png b/scene/resources/default_theme/graph_node_selected.png index 33c4d061285b33e5e738aac60e095d2fa49cdf1f..f76c9703dd04d01533e31636517368fcdd2dbecf 100644 GIT binary patch delta 881 zcmbQizLb4}W4$;pi}kOGTH6>Hm|Z+w977@wznyNMai>h=`2P3H=hs=?wfuH>>SpGH zrP2jlFK4JuZV$3zVLJEVl8Nb*KG{82y^rguCNsryE!`;1^!0+nf(ZgvDW@hGHmY2> zG|NF>{J#9}l20$y|HiR*e=UC}C9QCW`EG4lZWm?YhM9tV$^ohQg_XrGueC_~sd&jvWFF4h+%^^=BB4 zTM8;&ikiG7cebEvXWK!A28jia-??8nW4Lx=o{y*E3>i}C$(YOBH5f2=V%9W%} ze)4SZJl`d9gkiEs+OCTq-z<|zc=T)Hy_@UmO@0~%?UK`o^_lW(<{o}mE}s~SYbPJP z=zOwPHvZI{deQFWJ4b5zdnWFjTzKI6{hF_@Zgwwkl|4VdTK@k1Uu7R%4%}Rt8gWRE zMMrnv=l5JT>-+E3Z5Mb@`FCP=<9~_Z10n{jK8#@M&wqxf?55I3&(EKn_$)$_RZNGC zA^KE(=hsZXC}o+aE`33a3{&pT>})VO9;j6?Mc7mI?t&-Nq7OTtm7YC6ZcpJ{IsQM) zFAQXEmfQ?l^k&|nW6!R3A9g;gy8TYjCEkqe*LA7y*H1rv_wTv(`L%^FcRjw<>c>-i z*8cB@*J-8OzI|A`eR{l*arE~6yKQg3Dw%F|_gyvf{n^*EqHZ7SsawmXm~eZW-tMW_ zw`P5f9gL3u|3cuv#cwQU we5|dl7hTK{>AR4%wIVn7tiaLRyXqM>$**rps>FVdQ&MBb@02$`9bN~PV delta 868 zcmZ3=K7)ONV?7&>rchztrXLIp%;ughjv*0;-%j`MxKko>eE<9D^|Q~sdy|q<#(MGU zp*+X_z3)A%1h`%&F5D`1WO4i6o#Op*OOA`};aGHAjK6wAfJR8wtGkPv#6(yR-hAn2 zWPLJ4ZU2++HYzvOW||g%Q2U&aCiHCY`R{w*zZXBzKVv3ChkJir{gz1!)A`S{O-oW; z(!;sbfM+r*=hfWrX;s&I*TwEOVqVZOb8Gv3n;7ZV$=BQV)n}S~GHW&{cw}+yG{a?+ z$ycwvN~yj3w%mH|{cF+Z;;ZG~_P%`a!c$xB+x<^&hwpyRe{;`JH`dwt$Yzzl|8}Rd zzpMH_^XA<@u^#8kf9d;`eOvdw{$6TYZq+R}p&cHF^PWy-(Ob4W*I@Iu^4jlt|Bs&R z`%>fHKlubt^T)QWi@IlSXA^31(F*XL$v8nl;nks{<4Qcoq&G^>blNC1IU|YLK}+&X z<;GQE!V+hCOcEImsLZ<2HB}(U%WI+#gF))9GdvwJVhkJ!tPPS6bktqFg1j#A9CNKr9mVl89J}2$yaQIwe-o|yieb%xRcZhCbo0tRDSqy&eBbVlP##*9SgK|7T-Tx?s~GOV!rTqAR1V_fOsV zPs8l^p3c(VNfWnrR~|^8U*9#q{_o1bk8kIef4I6mK89z)n^`KcyMDiH=}D`>a2~aQ7iLamF-EgZoV1v>EJ{5lILcLLcNNUmlVA)Dv|Bm z+tz$odWNs+`WxpCTzNaAuW#NKzyCG+-p;RlU-#o<^>*Fc86`<#p8NiPeSFyOcI~fI z(znld&fLl#Z