Refactor duck reverse manipulation into duckmatic
[synfig.git] / synfig-studio / src / gui / duckmatic.cpp
index 51ed17f..fe2d6a3 100644 (file)
 
 #include "duckmatic.h"
 #include <synfigapp/value_desc.h>
+#include <synfigapp/canvasinterface.h>
 #include <synfig/general.h>
 #include <synfig/paramdesc.h>
 #include <synfig/valuenode_timedswap.h>
 #include <synfig/valuenode_animated.h>
 #include <synfig/valuenode_composite.h>
+#include <synfig/valuenode_range.h>
 #include <synfig/valuenode_scale.h>
 #include <synfig/valuenode_bline.h>
 #include <synfig/valuenode_blinecalctangent.h>
@@ -88,7 +90,8 @@ using namespace studio;
 
 /* === E N T R Y P O I N T ================================================= */
 
-Duckmatic::Duckmatic():
+Duckmatic::Duckmatic(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface):
+       canvas_interface(canvas_interface),
        type_mask(Duck::TYPE_ALL-Duck::TYPE_WIDTH),
        grid_snap(false),
        guide_snap(false),
@@ -635,6 +638,20 @@ DuckDrag_Translate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector
        last_translate_=vect;
 }
 
+
+void
+Duckmatic::signal_user_click_selected_ducks(int button)
+{
+       const DuckList ducks(get_selected_ducks());
+       DuckList::const_iterator iter;
+
+       for(iter=ducks.begin();iter!=ducks.end();++iter)
+       {
+               (*iter)->signal_user_click(button)();
+       }
+}
+
+
 void
 Duckmatic::signal_edited_selected_ducks()
 {
@@ -695,16 +712,152 @@ Duckmatic::signal_edited_selected_ducks()
        selected_ducks=old_set;
 }
 
-void
-Duckmatic::signal_user_click_selected_ducks(int button)
+
+bool
+Duckmatic::on_duck_changed(const synfig::Point &value,const synfigapp::ValueDesc& value_desc)
 {
-       const DuckList ducks(get_selected_ducks());
-       DuckList::const_iterator iter;
+       if (ValueNode_BLineCalcWidth::Handle bline_width = ValueNode_BLineCalcWidth::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               Real old_width((*bline_width)(get_time()).get(Real()));
+               Real new_width(value.mag());
+               int scale_index(bline_width->get_link_index_from_name("scale"));
+               Real scale((*(bline_width->get_link(scale_index)))(get_time()).get(Real()));
+               return canvas_interface->change_value(synfigapp::ValueDesc(bline_width,scale_index), new_width * scale / old_width);
+       }
 
-       for(iter=ducks.begin();iter!=ducks.end();++iter)
+       if (ValueNode_BLineCalcVertex::Handle bline_vertex = ValueNode_BLineCalcVertex::Handle::cast_dynamic(value_desc.get_value_node()))
        {
-               (*iter)->signal_user_click(button)();
+               ValueNode_BLine::Handle bline = ValueNode_BLine::Handle::cast_dynamic(bline_vertex->get_link("bline"));
+               Real radius = 0.0;
+               if (((*(bline_vertex->get_link("loop")))(get_time()).get(bool()))){
+                       Real amount_old((*(bline_vertex->get_link("amount")))(get_time()).get(Real()));
+                       Real amount_new = synfig::find_closest_point((*bline)(get_time()), value, radius, bline->get_loop());
+                       Real difference = fmod( fmod(amount_new - amount_old, 1.0) + 1.0 , 1.0);
+                                                       //fmod is called twice to avoid negative values
+                       if (difference > 0.5) difference=difference-1.0;
+                       return canvas_interface->change_value(synfigapp::ValueDesc(bline_vertex,bline_vertex->get_link_index_from_name("amount")), amount_old+difference);
+
+               } else {
+                       Real amount = synfig::find_closest_point((*bline)(get_time()), value, radius, bline->get_loop());
+                       return canvas_interface->change_value(synfigapp::ValueDesc(bline_vertex,bline_vertex->get_link_index_from_name("amount")), amount);
+               }
+       }
+
+       if (ValueNode_BLineCalcTangent::Handle bline_tangent = ValueNode_BLineCalcTangent::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               switch(value_desc.get_value_type())
+               {
+               case ValueBase::TYPE_REAL:
+               {
+                       Real old_length = (*bline_tangent)(get_time()).get(Real());
+                       Real new_length = value.mag();
+                       int scale_index(bline_tangent->get_link_index_from_name("scale"));
+                       int fixed_length_index(bline_tangent->get_link_index_from_name("fixed_length"));
+                       Real scale((*(bline_tangent->get_link(scale_index)))(get_time()).get(Real()));
+                       bool fixed_length((*(bline_tangent->get_link(fixed_length_index)))(get_time()).get(bool()));
+                       if (fixed_length)
+                               return canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,scale_index), new_length);
+                       if (old_length == 0)
+                               return true;
+                       return canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,scale_index), new_length * scale / old_length);
+               }
+
+               case ValueBase::TYPE_ANGLE:
+                       assert(0);                      // doesn't happen?
+                       break;
+
+               case ValueBase::TYPE_VECTOR:
+               {
+                       Vector old_tangent = (*bline_tangent)(get_time()).get(Vector());
+                       Angle old_angle = old_tangent.angle();
+                       Real old_length = old_tangent.mag();
+                       Angle new_angle = value.angle();
+                       Real new_length = value.mag();
+                       int offset_index(bline_tangent->get_link_index_from_name("offset"));
+                       int scale_index(bline_tangent->get_link_index_from_name("scale"));
+                       int fixed_length_index(bline_tangent->get_link_index_from_name("fixed_length"));
+                       Angle old_offset((*(bline_tangent->get_link(offset_index)))(get_time()).get(Angle()));
+                       Real scale((*(bline_tangent->get_link(scale_index)))(get_time()).get(Real()));
+                       bool fixed_length((*(bline_tangent->get_link(fixed_length_index)))(get_time()).get(bool()));
+                       if (fixed_length)
+                       {
+                               if (!(canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,scale_index), new_length)))
+                                       return false;
+                       }
+                       else if (old_length != 0 && !(canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,scale_index), new_length * scale / old_length)))
+                               return false;
+                       return canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,offset_index), old_offset + new_angle - old_angle);
+               }
+               default:
+                       break;
+               }
+       }
+
+       if (ValueNode_Scale::Handle scale_value_node = ValueNode_Scale::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               int link_index(scale_value_node->get_link_index_from_name("link"));
+               if(scale_value_node->is_invertible(get_time()))
+                       return canvas_interface->change_value(
+                               synfigapp::ValueDesc(scale_value_node,link_index),
+                                       scale_value_node->get_inverse(get_time(), value)
+                                       );
+               else
+                       return false;
+       }
+
+       if (ValueNode_Range::Handle range_value_node = ValueNode_Range::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               int link_index(range_value_node->get_link_index_from_name("link"));
+               return canvas_interface->change_value(
+                       synfigapp::ValueDesc(range_value_node,link_index),
+                               range_value_node->get_inverse(get_time(), value)
+                               );
+       }
+
+       switch(value_desc.get_value_type())
+       {
+       case ValueBase::TYPE_REAL:
+               return canvas_interface->change_value(value_desc,value.mag());
+       case ValueBase::TYPE_ANGLE:
+               return canvas_interface->change_value(value_desc,Angle::tan(value[1],value[0]));
+       default:
+               return canvas_interface->change_value(value_desc,value);
+       }
+}
+
+bool
+Duckmatic::on_duck_angle_changed(const synfig::Angle &rotation,const synfigapp::ValueDesc& value_desc)
+{
+       if (ValueNode_BLineCalcTangent::Handle bline_tangent = ValueNode_BLineCalcTangent::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               int offset_index(bline_tangent->get_link_index_from_name("offset"));
+               Angle old_offset((*(bline_tangent->get_link(offset_index)))(get_time()).get(Angle()));
+               return canvas_interface->change_value(synfigapp::ValueDesc(bline_tangent,offset_index), old_offset + rotation);
+       }
+
+       if (ValueNode_Scale::Handle scale_value_node = ValueNode_Scale::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               int link_index(scale_value_node->get_link_index_from_name("link"));
+               if(scale_value_node->is_invertible(get_time()))
+                       return canvas_interface->change_value(
+                               synfigapp::ValueDesc(scale_value_node,link_index),
+                                       scale_value_node->get_inverse(get_time(), rotation)
+                                       );
+               else
+                       return false;
+
+       }
+       if (ValueNode_Range::Handle range_value_node = ValueNode_Range::Handle::cast_dynamic(value_desc.get_value_node()))
+       {
+               int link_index(range_value_node->get_link_index_from_name("link"));
+               return canvas_interface->change_value(
+                       synfigapp::ValueDesc(range_value_node,link_index),
+                               range_value_node->get_inverse(get_time(), rotation)
+                               );
        }
+       // \todo will this really always be the case?
+       assert(value_desc.get_value_type() == ValueBase::TYPE_ANGLE);
+       return canvas_interface->change_value(value_desc, value_desc.get_value(get_time()).get(Angle()) + rotation);
 }
 
 void
@@ -1170,8 +1323,8 @@ Duckmatic::add_to_ducks(const synfigapp::ValueDesc& value_desc,etl::handle<Canva
                        duck->signal_edited().connect(
                                sigc::bind(
                                        sigc::mem_fun(
-                                               *canvas_view,
-                                               &studio::CanvasView::on_duck_changed),
+                                               *this,
+                                               &studio::Duckmatic::on_duck_changed),
                                        value_desc));
                        duck->set_value_desc(value_desc);
 
@@ -1242,8 +1395,8 @@ Duckmatic::add_to_ducks(const synfigapp::ValueDesc& value_desc,etl::handle<Canva
                        duck->signal_edited_angle().connect(
                                sigc::bind(
                                        sigc::mem_fun(
-                                               *canvas_view,
-                                               &studio::CanvasView::on_duck_angle_changed),
+                                               *this,
+                                               &studio::Duckmatic::on_duck_angle_changed),
                                        value_desc));
                        duck->set_value_desc(value_desc);
 
@@ -1335,8 +1488,8 @@ Duckmatic::add_to_ducks(const synfigapp::ValueDesc& value_desc,etl::handle<Canva
                        duck->signal_edited().connect(
                                sigc::bind(
                                        sigc::mem_fun(
-                                               *canvas_view,
-                                               &studio::CanvasView::on_duck_changed),
+                                               *this,
+                                               &studio::Duckmatic::on_duck_changed),
                                        value_desc));
                        duck->set_value_desc(value_desc);