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 458e51b4fd3..6a4bf0f4a29 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 6d35e1518fb..9f5dd56ed5d 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 20335993078..90f63a50537 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 zcmV;T0$u&^1^NY$KnVyJ4FwXRID?y!Nhp7GNklgPEI zKXz@$NnhBIZ?W~{x!>LW?;`nxEfb&ycL6F4<hY30L8+n@v{e)ZRYc-4Y~mzw7@Y*LYSSAk=}Y{sur(oNF~_A2;gLXFSg<71tPJgK+S^ztOu6yam=J zv%tssI2007*qo IM6N<$f`2tEg8%>k delta 664 zcmV;J0%!gD1@HxsKnVvO0SFKtZxSn!Nhp76NklMTC3mR-90cZpqd*5ZPy%cL17Lp* z=mCEKn?`K|XXXxdueX}5%QG|0Q&iY!6n}~0==H|0{&V0Huqi&$!hHAe zh}ll7%AesN;@-pCd|vtZs@Gq?1*{0r0v3-SJNf3tvzP58u_U9U5)jUWgkghgH?D4e z|MB$_uoQY7+F=m3hreSGbw>&+d4+$KQM|<{hA;@*zHCVW0f15K{GGr~YY)-iPqf}kj(l5^9s-iC!nTSZY&MZ}e1Qzvm@bQ-{_&2Few;#9syoP1NI_P1%wT(Tb*vgmM{;ro;^$UH{awjKC_pPkH0lSa6v>ixWCszA zkC>Z>^7KvBK-I~Y52IRioHmK~pWI=umI0D2!{YgiyK-83|F$}VjX{6U88rAus{u8j z2GoEWPy=efe+iI^PvAXgVE2FFYg2#)B4bQ!lU;ZvV2p`@2(S&m%C1N8aCxn_$~1^g zV6C@G6c3kyb-<*X>l|>Q)9yZPHD}KSK~O2KF~&ym@WWuUe-n5IY)Ec}>i{Q!BS3S) y@*M*|fp30~Hc}EaO!vVFRj4mH*{AU1_7_Z-9VA-_i^-uHd=6X-|GOn(5xy?albu-$Rn`z^ZF%+}o>@1E*;gEQ@J_iV4%S?GKHau5VE3s6dh zGnJXa++3|$uU3DbtCU}@e!hA@TU+-lLA&{Gr`KM&a&_f~a-@ZV2!>(w%biCXwYmBu zRbTi+>Q1-Ze1G%$+{f!5++=2PxHr2i9ScMw6M&f!4kJ)yZ*ldLm770r|F(9ToTdmu zUwQq5d8Z@={Q52lLZ}VGAR7H?0}=%I^=%tb2nt}F4!aq#K#Jk+{Ap3jFGvMalqH# zeD&(ti>HMh&!2lA-{1On@kGGcrDa&cE(9zszVq)2Jg<-5ezy?ddHs=Vf&Um&z!WeA zOaW8C6fgz+{{UKRf_38O&&iSISQ!NtECv8u%72OVX---r9OXogQUL&z-IAK_Eg-#58%|HF{>t=Pf_VBH@mtPVyTerWvJ=^MZmpa|7U9bK>JA62=xz zFmrmrTVMa^5Wvy)1k6l3J3FG$XcUgnYPEQ6ZOstH+q!=MvLCO4VtQ%200000NkvXX Hu0mjf8RoNe delta 868 zcmV-q1DpJ%2bc$tL4O7eDH3sRviJZ112;)TK~z}7?Uzr8+;kMjzweiSo6PR)WMp;) z(bd9kL65!fNqP_p>tUg_7Q~~6y}1{UUZlqsya=M(77u%{P$*P->fNJ)78C`++38L) zJIQ1wzvTBkB-tjJHFNML^I&EY=DpANz3=ZA$d8zr0EkDAet)!*p=S@z29{zaq>Krr zFbtCg3Ds@)W_s6+u3ft_1E7eRwTIt4TpNXx*M`1-X*BXSgD`O7IM&Mm(=?OS*6L(^ z-R*Zfo!{15&R=^U?TzW^=t(Oc_TTbG!`=69?A|dgT|+~}vn2igyJrXPdiR;x-TYlh z&v*JSPImUL?|{~7B0Ia~OX8*j44Ibok=S?HuKE*KERZ|BUK;?xcHF`O_Y6hsRzM@@LrNzrg z#eSe-Gk;OxXZ}1ib)d4sIVFYj6{%Z0zm>WEC^pBuiFJ*Vk+q9?z-OO-il2Y|sZsIm zop>puOTZGa1S|nd!2b^* zr6gzNTB&a8cD!Oc zgV|K_!QlsV=Qd#yjUkhya_BO29ge+*gTot`O(n0`&cM{|n2wH~v`+tgzU`lR?|%64 zd$%pyXmny0NxuK};qnp*We6)?eRVUub?fRB!1?h6 u%uL6}$D-HkHI6VC40wNkUlC=;x_