/* === S Y N F I G ========================================================= */
/*! \file duckmatic.cpp
** \brief Template File
**
** $Id$
**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
** Copyright (c) 2007, 2008 Chris Moore
** Copyright (c) 2009, 2011 Nikita Kitaev
** Copyright (c) 2011 Carlos López
** Copyright (c) 2015 Blanchi Jérôme
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <fstream>
#include <iostream>
#include <algorithm>
#include <ETL/hermite>
#include <synfig/general.h>
#include "duckmatic.h"
#include "ducktransform_scale.h"
#include "ducktransform_translate.h"
#include "ducktransform_rotate.h"
#include <synfigapp/value_desc.h>
#include <synfigapp/canvasinterface.h>
#include <synfig/paramdesc.h>
#include <synfig/valuenodes/valuenode_timedswap.h>
#include <synfig/valuenodes/valuenode_animated.h>
#include <synfig/valuenodes/valuenode_composite.h>
#include <synfig/valuenodes/valuenode_range.h>
#include <synfig/valuenodes/valuenode_scale.h>
#include <synfig/valuenodes/valuenode_bline.h>
#include <synfig/valuenodes/valuenode_wplist.h>
#include <synfig/valuenodes/valuenode_blinecalctangent.h>
#include <synfig/valuenodes/valuenode_blinecalcvertex.h>
#include <synfig/valuenodes/valuenode_blinecalcwidth.h>
#include <synfig/valuenodes/valuenode_staticlist.h>
#include <synfig/valuenodes/valuenode_bone.h>
#include <synfig/valuenodes/valuenode_boneinfluence.h>
#include <synfig/valuenodes/valuenode_boneweightpair.h>
#include <synfig/segment.h>
#include <synfig/pair.h>
#include <synfig/curve_helper.h>
#include <synfig/context.h>
#include <synfig/layers/layer_pastecanvas.h>
#include <synfig/layers/layer_filtergroup.h>
#include "ducktransform_matrix.h"
#include "ducktransform_rotate.h"
#include "ducktransform_translate.h"
#include "ducktransform_scale.h"
#include "ducktransform_origin.h"
#include "canvasview.h"
#include "onemoment.h"
#include <gui/localization.h>
#endif
/* === U S I N G =========================================================== */
using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;
/* === M A C R O S ========================================================= */
/* 0.33333333333333333 makes for nice short tangent handles,
1.0 makes them draw as their real length */
#define TANGENT_HANDLE_SCALE 0.33333333333333333
/* leave this alone or the bezier won't lie on top of the bline */
#define TANGENT_BEZIER_SCALE 0.33333333333333333
/* === I N L I N E ======================================================= */
synfig::GUID calc_duck_guid(const synfigapp::ValueDesc& value_desc, const synfig::TransformStack& transform_stack);
void set_duck_value_desc(Duck& duck, const synfigapp::ValueDesc& value_desc, const synfig::TransformStack& transform_stack);
void set_duck_value_desc(Duck& duck, const synfigapp::ValueDesc& value_desc, const synfig::String& sub_name, const synfig::TransformStack& transform_stack);
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
/* === E N T R Y P O I N T ================================================= */
Duckmatic::Duckmatic(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface):
canvas_interface(canvas_interface),
type_mask(Type(
Duck::TYPE_ALL
& ~( Duck::TYPE_WIDTH
| Duck::TYPE_BONE_RECURSIVE
| Duck::TYPE_WIDTHPOINT_POSITION ))),
type_mask_state(Duck::TYPE_NONE),
alternative_mode_(false),
lock_animation_mode_(false),
grid_snap(false),
guide_snap(false),
grid_size(1.0/4.0,1.0/4.0),
grid_color(synfig::Color(159.0/255.0,159.0/255.0,159.0/255.0)),
guides_color(synfig::Color(111.0/255.0,111.0/255.0,1.0)),
zoom(1.0),
prev_zoom(1.0),
show_persistent_strokes(true),
axis_lock(false),
drag_offset_(0, 0)
{
clear_duck_dragger();
clear_bezier_dragger();
}
Duckmatic::~Duckmatic()
{
clear_ducks();
if (getenv("SYNFIG_DEBUG_DESTRUCTORS"))
synfig::info("Duckmatic::~Duckmatic(): Deleted");
}
void
Duckmatic::clear_ducks()
{
for(;!duck_changed_connections.empty();duck_changed_connections.pop_back())duck_changed_connections.back().disconnect();
duck_data_share_map.clear();
duck_map.clear();
//duck_list_.clear();
bezier_list_.clear();
stroke_list_.clear();
if(show_persistent_strokes)
stroke_list_=persistent_stroke_list_;
}
/*
-- ** -- D U C K M A N I P U L A T I O N M E T H O D S-----------------------
*/
bool
Duckmatic::duck_is_selected(const etl::handle<Duck> &duck)const
{
return duck && selected_ducks.count(duck->get_guid());
}
void
Duckmatic::clear_selected_ducks()
{
selected_ducks.clear();
signal_duck_selection_changed_();
}
etl::handle<Duckmatic::Duck>
Duckmatic::get_selected_duck()const
{
if(selected_ducks.empty() || duck_map.empty())
return 0;
return duck_map.find(*selected_ducks.begin())->second;
}
etl::handle<Duckmatic::Bezier>
Duckmatic::get_selected_bezier()const
{
return selected_bezier;
}
void
Duckmatic::refresh_selected_ducks()
{
/*
std::set<etl::handle<Duck> >::iterator iter;
std::set<etl::handle<Duck> > new_set;
if(duck_list().empty())
{
selected_duck_list.clear();
signal_duck_selection_changed_();
return;
}
for(iter=selected_duck_list.begin();iter!=selected_duck_list.end();++iter)
{
etl::handle<Duck> similar(find_similar_duck(*iter));
if(similar)
{
new_set.insert(similar);
}
}
selected_duck_list=new_set;
*/
GUIDSet old_set(selected_ducks);
GUIDSet::const_iterator iter;
for(iter=old_set.begin();iter!=old_set.end();++iter)
{
if(duck_map.count(*iter)==0)
selected_ducks.erase(*iter);
}
signal_duck_selection_changed_();
}
bool
Duckmatic::is_duck_group_selectable(const etl::handle<Duck>& x)const
{
const Type type(get_type_mask());
if (((x->get_type() && (!(type & x->get_type()))) ||
!x->get_editable()))
return false;
synfigapp::ValueDesc value_desc(x->get_value_desc());
if(value_desc.parent_is_layer() && (type & Duck::TYPE_POSITION))
{
Layer::Handle layer(value_desc.get_layer());
String layer_name(layer->get_name());
if (layer_name == "outline" || layer_name == "region" || layer_name == "plant" ||
layer_name == "polygon" || layer_name == "curve_gradient" || layer_name == "advanced_outline")
return false;
if(etl::handle<Layer_PasteCanvas>::cast_dynamic(layer) &&
!layer->get_param("children_lock").get(bool()))
return false;
}
else if (value_desc.parent_is_value_node())
{
if (ValueNode_BLineCalcVertex::Handle::cast_dynamic(value_desc.get_value_node()))
return false;
if (value_desc.parent_is_linkable_value_node())
{
ValueNode::Handle parent_value_node(value_desc.get_parent_value_node());
if (ValueNode_Composite::Handle composite = ValueNode_Composite::Handle::cast_dynamic(parent_value_node))
{
if (parent_value_node->get_type() == type_bline_point &&
ValueNode_BLineCalcVertex::Handle::cast_dynamic(
composite->get_link("point")))
return false;
// widths ducks of the widthpoints
// Do not avoid selection of the width ducks from widthpoints
//if (parent_value_node->get_type() == type_width_point)
// return false;
}
else if (ValueNode_BLine::Handle::cast_dynamic(parent_value_node))
{
ValueNode_Composite::Handle composite(ValueNode_Composite::Handle::cast_dynamic(
value_desc.get_value_node()));
if (composite &&
ValueNode_BLineCalcVertex::Handle::cast_dynamic(composite->get_link("point")))
return false;
}
// position ducks of the widthpoints
else if (ValueNode_WPList::Handle::cast_dynamic(parent_value_node))
return false;
}
}
return true;
}
void
Duckmatic::select_all_ducks()
{
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
if(is_duck_group_selectable(iter->second))
select_duck(iter->second);
else
unselect_duck(iter->second);
}
void
Duckmatic::unselect_all_ducks()
{
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
unselect_duck(iter->second);
}
void
Duckmatic::toggle_select_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br)
{
Vector vmin, vmax;
vmin[0]=std::min(tl[0],br[0]);
vmin[1]=std::min(tl[1],br[1]);
vmax[0]=std::max(tl[0],br[0]);
vmax[1]=std::max(tl[1],br[1]);
{
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
{
Point p(iter->second->get_trans_point());
if(p[0]<=vmax[0] && p[0]>=vmin[0] && p[1]<=vmax[1] && p[1]>=vmin[1] &&
is_duck_group_selectable(iter->second))
toggle_select_duck(iter->second);
}
}
}
void
Duckmatic::select_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br)
{
Vector vmin, vmax;
vmin[0]=std::min(tl[0],br[0]);
vmin[1]=std::min(tl[1],br[1]);
vmax[0]=std::max(tl[0],br[0]);
vmax[1]=std::max(tl[1],br[1]);
// Type type(get_type_mask());
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
{
Point p(iter->second->get_trans_point());
if(p[0]<=vmax[0] && p[0]>=vmin[0] && p[1]<=vmax[1] && p[1]>=vmin[1])
{
if(is_duck_group_selectable(iter->second))
select_duck(iter->second);
}
}
}
int
Duckmatic::count_selected_ducks()const
{
return selected_ducks.size();
}
void
Duckmatic::select_duck(const etl::handle<Duck> &duck)
{
if(duck)
{
selected_ducks.insert(duck->get_guid());
signal_duck_selection_changed_();
if(selected_ducks.size() == 1)
{
signal_duck_selection_single_(duck);
}
}
}
DuckList
Duckmatic::get_selected_ducks()const
{
DuckList ret;
GUIDSet::const_iterator iter;
const Type type(get_type_mask());
for(iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
{
const DuckMap::const_iterator d_iter(duck_map.find(*iter));
if(d_iter==duck_map.end())
continue;
if(( d_iter->second->get_type() && (!(type & d_iter->second->get_type())) ) )
continue;
ret.push_back(d_iter->second);
}
return ret;
}
DuckList
Duckmatic::get_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br)const
{
Vector vmin, vmax;
vmin[0]=std::min(tl[0],br[0]);
vmin[1]=std::min(tl[1],br[1]);
vmax[0]=std::max(tl[0],br[0]);
vmax[1]=std::max(tl[1],br[1]);
DuckList ret;
// Type type(get_type_mask());
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
{
Point p(iter->second->get_trans_point());
if(p[0]<=vmax[0] && p[0]>=vmin[0] && p[1]<=vmax[1] && p[1]>=vmin[1])
{
// if(is_duck_group_selectable(iter->second))
ret.push_back(iter->second);
}
}
return ret;
}
DuckList
Duckmatic::get_duck_list()const
{
DuckList ret;
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter) if (iter->second->get_type()&Duck::TYPE_POSITION) ret.push_back(iter->second);
for(iter=duck_map.begin();iter!=duck_map.end();++iter) if (iter->second->get_type()&Duck::TYPE_VERTEX ) ret.push_back(iter->second);
for(iter=duck_map.begin();iter!=duck_map.end();++iter) if (iter->second->get_type()&Duck::TYPE_TANGENT ) ret.push_back(iter->second);
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
if (!(iter->second->get_type()&Duck::TYPE_POSITION) &&
!(iter->second->get_type()&Duck::TYPE_VERTEX) &&
!(iter->second->get_type()&Duck::TYPE_TANGENT))
ret.push_back(iter->second);
return ret;
}
void
Duckmatic::unselect_duck(const etl::handle<Duck> &duck)
{
if(duck && selected_ducks.count(duck->get_guid()))
{
selected_ducks.erase(duck->get_guid());
signal_duck_selection_changed_();
}
}
void
Duckmatic::toggle_select_duck(const etl::handle<Duck> &duck)
{
if(duck_is_selected(duck))
unselect_duck(duck);
else
select_duck(duck);
}
void
Duckmatic::translate_selected_ducks(const synfig::Vector& vector)
{
if(duck_dragger_)
duck_dragger_->duck_drag(this,vector);
}
void
Duckmatic::start_duck_drag(const synfig::Vector& offset)
{
if(duck_dragger_)
duck_dragger_->begin_duck_drag(this,offset);
//drag_offset_=offset;
drag_offset_=find_duck(offset)->get_trans_point();
}
bool
Duckmatic::end_duck_drag()
{
if(duck_dragger_)
return duck_dragger_->end_duck_drag(this);
return false;
}
void
Duckmatic::update_ducks()
{
Time time(get_time());
DuckList duck_list(get_duck_list());
const DuckList selected_ducks(get_selected_ducks());
DuckList::const_iterator selected_iter;
if(get_selected_bezier())
{
etl::handle<Duck> c1(get_selected_bezier()->c1);
etl::handle<Duck> c2(get_selected_bezier()->c2);
if(c1->get_value_desc().parent_is_linkable_value_node())
{
ValueNode_Composite::Handle composite(ValueNode_Composite::Handle::cast_dynamic(c1->get_value_desc().get_parent_value_node()));
LinkableValueNode::Handle duck_value_node(LinkableValueNode::Handle::cast_dynamic(c1->get_value_desc().get_value_node()));
// it belongs to a composite and it is a BLinePoint
if(composite && composite->get_type() == type_bline_point && duck_value_node)
{
int index(c1->get_value_desc().get_index());
etl::handle<Duck> origin_duck=c1->get_origin_duck();
// Search all the rest of ducks
DuckList::iterator iter;
for (iter=duck_list.begin(); iter!=duck_list.end(); iter++)
// if the other duck has the same origin and it is tangent type
if ( (*iter)->get_origin_duck()==origin_duck && (*iter)->get_type() == Duck::TYPE_TANGENT)
{
ValueNode_Composite::Handle iter_composite;
iter_composite=ValueNode_Composite::Handle::cast_dynamic((*iter)->get_value_desc().get_parent_value_node());
// and their parent valuenode are the same
if(iter_composite.get() == composite.get())
{
BLinePoint bp=(*composite)(time).get(BLinePoint());
int t1_index=composite->get_link_index_from_name("t1");
int t2_index=composite->get_link_index_from_name("t2");
if(index==t1_index && (*iter)->get_value_desc().get_index()!=t1_index)
{
bp.set_tangent1(c1->get_point());
Vector t2(bp.get_tangent2());
(*iter)->set_point(Point(t2));
}
else if(index==t2_index && (*iter)->get_value_desc().get_index()!=t2_index)
{
// Create a new BLinePoint
BLinePoint nbp;
// Terporary set the flags for the new BLinePoint to all split
nbp.set_split_tangent_both(true);
// Now we can set the tangents. Tangent2 won't be modified by tangent1
nbp.set_tangent1(c1->get_point());
nbp.set_tangent2(bp.get_tangent1());
// Now update the flags
nbp.set_split_tangent_radius(bp.get_split_tangent_radius());
nbp.set_split_tangent_angle(bp.get_split_tangent_angle());
// Now retrieve the updated tangent2 (which will be stored as t1, see below)
Vector t1(nbp.get_tangent2());
(*iter)->set_point(Point(t1));
}
}
}
}
}
if(c2->get_value_desc().parent_is_linkable_value_node())
{
ValueNode_Composite::Handle composite(ValueNode_Composite::Handle::cast_dynamic(c2->get_value_desc().get_parent_value_node()));
LinkableValueNode::Handle duck_value_node(LinkableValueNode::Handle::cast_dynamic(c2->get_value_desc().get_value_node()));
// it belongs to a composite and it is a BLinePoint
if(composite && composite->get_type() == type_bline_point && duck_value_node)
{
int index(c2->get_value_desc().get_index());
etl::handle<Duck> origin_duck=c2->get_origin_duck();
// Search all the rest of ducks
DuckList::iterator iter;
for (iter=duck_list.begin(); iter!=duck_list.end(); iter++)
// if the other duck has the same origin and it is tangent type
if ( (*iter)->get_origin_duck()==origin_duck && (*iter)->get_type() == Duck::TYPE_TANGENT)
{
ValueNode_Composite::Handle iter_composite;
iter_composite=ValueNode_Composite::Handle::cast_dynamic((*iter)->get_value_desc().get_parent_value_node());
// and their parent valuenode are the same
if(iter_composite.get() == composite.get())
{
BLinePoint bp=(*composite)(time).get(BLinePoint());
int t1_index=composite->get_link_index_from_name("t1");
int t2_index=composite->get_link_index_from_name("t2");
if(index==t1_index && (*iter)->get_value_desc().get_index()!=t1_index)
{
bp.set_tangent1(c2->get_point());
Vector t2(bp.get_tangent2());
(*iter)->set_point(Point(t2));
}
else if(index==t2_index && (*iter)->get_value_desc().get_index()!=t2_index)
{
// Create a new BLinePoint
BLinePoint nbp;
// Terporary set the flags for the new BLinePoint to all split
nbp.set_split_tangent_both(true);
// Now we can set the tangents. Tangent2 won't be modified by tangent1
nbp.set_tangent1(c2->get_point());
nbp.set_tangent2(bp.get_tangent1());
// Now update the flags
nbp.set_split_tangent_radius(bp.get_split_tangent_radius());
nbp.set_split_tangent_angle(bp.get_split_tangent_angle());
// Now retrieve the updated tangent2 (which will be stored as t1, see below)
Vector t1(nbp.get_tangent2());
(*iter)->set_point(Point(t1));
}
}
}
}
}
}
for (selected_iter=selected_ducks.begin(); selected_iter!=selected_ducks.end(); ++selected_iter)
{
etl::handle<Duck> duck(*selected_iter);
if(!duck)
return;
if (duck->get_type() == Duck::TYPE_VERTEX || duck->get_type() == Duck::TYPE_POSITION)
{
ValueNode_BLineCalcVertex::Handle bline_vertex =
ValueNode_BLineCalcVertex::Handle::cast_dynamic(duck->get_value_desc().get_value_node());
if (!bline_vertex && duck->get_value_desc().parent_is_value_desc()) {
ValueNode_Composite::Handle composite =
ValueNode_Composite::Handle::cast_dynamic(duck->get_value_desc().get_value_node());
if (composite)
bline_vertex =
ValueNode_BLineCalcVertex::Handle::cast_dynamic(
composite->get_link(
duck->get_value_desc().get_sub_name() ));
}
if (bline_vertex)
{
synfig::Real radius = 0.0;
synfig::Point point(0.0, 0.0);
ValueNode_BLine::Handle bline(ValueNode_BLine::Handle::cast_dynamic(bline_vertex->get_link("bline")));
Real amount = synfig::find_closest_point((*bline)(time), duck->get_point(), radius, bline->get_loop(), &point);
bool homogeneous((*(bline_vertex->get_link("homogeneous")))(time).get(bool()));
if(homogeneous)
amount=std_to_hom((*bline)(time), amount, ((*(bline_vertex->get_link("loop")))(time).get(bool())), bline->get_loop() );
ValueNode::Handle vertex_amount_value_node(bline_vertex->get_link("amount"));
duck->set_point(point);
DuckList::iterator iter;
for (iter=duck_list.begin(); iter!=duck_list.end(); iter++)
{
if ( (*iter)->get_origin_duck()==duck /*&& !duck_is_selected(*iter)*/ )
{
ValueNode::Handle duck_value_node = (*iter)->get_value_desc().get_value_node();
if (duck_value_node)
{
ValueNode_Composite::Handle duck_value_node_composite = ValueNode_Composite::Handle::cast_dynamic(duck_value_node);
ValueNode::Handle sub_duck_value_node =
duck_value_node_composite && (*iter)->get_value_desc().parent_is_value_desc()
? ValueNode::Handle(duck_value_node_composite->get_link( (*iter)->get_value_desc().get_sub_name() ))
: duck_value_node;
if (sub_duck_value_node)
{
if ( ValueNode_BLineCalcTangent::Handle bline_tangent =
ValueNode_BLineCalcTangent::Handle::cast_dynamic(sub_duck_value_node) )
{
if (bline_tangent->get_link("amount") == vertex_amount_value_node)
{
synfig::Type &type(bline_tangent->get_type());
if (type == type_angle)
{
Angle angle((*bline_tangent)(time, amount).get(Angle()));
(*iter)->set_point(Point(Angle::cos(angle).get(), Angle::sin(angle).get()));
(*iter)->set_rotations(Angle::deg(0)); //hack: rotations are a relative value
}
else
if (type == type_real)
(*iter)->set_point(Point((*bline_tangent)(time, amount).get(Real()), 0));
else
if (type == type_vector)
(*iter)->set_point((*bline_tangent)(time, amount).get(Vector()));
}
} else
if ( ValueNode_BLineCalcWidth::Handle bline_width =
ValueNode_BLineCalcWidth::Handle::cast_dynamic(sub_duck_value_node) )
{
if (bline_width->get_link("amount") == vertex_amount_value_node)
(*iter)->set_point(Point((*bline_width)(time, amount).get(Real()), 0));
}
}
}
}
}
}
}
// We are moving a tangent handle
else
if(duck->get_type() == Duck::TYPE_TANGENT)
{
if (duck->get_value_desc().parent_is_value_desc()
&& duck->get_value_desc().is_value_node() )
{
ValueNode_Composite::Handle composite(ValueNode_Composite::Handle::cast_dynamic(duck->get_value_desc().get_value_node()));
if (composite && composite->get_type() == type_bline_point)
{
ValueNode::Handle duck_value_node(composite->get_link(duck->get_value_desc().get_sub_name()));
// it belongs to a composite and it is a BLinePoint
if (duck_value_node)
{
int index(duck->get_value_desc().get_index());
etl::handle<Duck> origin_duck=duck->get_origin_duck();
// Search all the rest of ducks
DuckList::iterator iter;
for (iter=duck_list.begin(); iter!=duck_list.end(); iter++)
{
// if the other duck has the same origin and it is tangent type
if ( (*iter)->get_origin_duck()==origin_duck && (*iter)->get_type() == Duck::TYPE_TANGENT)
{
ValueNode_Composite::Handle iter_composite;
iter_composite=ValueNode_Composite::Handle::cast_dynamic((*iter)->get_value_desc().get_parent_value_node());
// and their parent valuenode are the same
if(iter_composite.get() == composite.get())
{
// Check if the other tangent is also selected, in that case
// it is going to be moved itself so don't update it.
bool selected=false;
DuckList::const_iterator iter2;
for(iter2=selected_ducks.begin(); iter2!=selected_ducks.end(); ++iter2)
if(*iter == *iter2)
selected=true;
if(!selected)
{
BLinePoint bp=(*composite)(time).get(BLinePoint());
int t1_index=composite->get_link_index_from_name("t1");
int t2_index=composite->get_link_index_from_name("t2");
if(index==t1_index && (*iter)->get_value_desc().get_index()!=t1_index)
{
bp.set_tangent1(duck->get_point());
Vector t2(bp.get_tangent2());
(*iter)->set_point(Point(t2));
}
else if(index==t2_index && (*iter)->get_value_desc().get_index()!=t2_index)
{
// Create a new BLinePoint
BLinePoint nbp;
// Terporary set the flags for the new BLinePoint to all split
nbp.set_split_tangent_both(true);
// Now we can set the tangents. Tangent2 won't be modified by tangent1
nbp.set_tangent1(duck->get_point());
nbp.set_tangent2(bp.get_tangent1());
// Now update the flags
nbp.set_split_tangent_radius(bp.get_split_tangent_radius());
nbp.set_split_tangent_angle(bp.get_split_tangent_angle());
// Now retrieve the updated tangent2 (which will be stored as t1, see below)
Vector t1(nbp.get_tangent2());
(*iter)->set_point(Point(t1));
}
}
}
}
}
}
}
}
}
}
}
void
Duckmatic::start_bezier_drag(const synfig::Vector& offset, float bezier_click_pos)
{
if(bezier_dragger_)
bezier_dragger_->begin_bezier_drag(this,offset,bezier_click_pos);
}
void
Duckmatic::translate_selected_bezier(const synfig::Vector& vector)
{
if(bezier_dragger_)
bezier_dragger_->bezier_drag(this,vector);
}
bool
Duckmatic::end_bezier_drag()
{
if(bezier_dragger_)
return bezier_dragger_->end_bezier_drag(this);
return false;
}
/*
-- ** -- grid and guide M E T H O D S----------------------------
*/
void
Duckmatic::set_grid_size(const synfig::Vector &s)
{
if(grid_size!=s)
{
grid_size=s;
signal_grid_changed();
}
}
void
Duckmatic::set_grid_color(const synfig::Color &c)
{
if(grid_color!=c)
{
grid_color=c;
signal_grid_changed();
}
}
void
Duckmatic::set_grid_snap(bool x)
{
if(grid_snap!=x)
{
grid_snap=x;
signal_grid_changed();
}
}
void
Duckmatic::set_guide_snap(bool x)
{
if(guide_snap!=x)
{
guide_snap=x;
signal_grid_changed();
}
}
void
Duckmatic::set_guides_color(const synfig::Color &c)
{
if(guides_color!=c)
{
guides_color=c;
signal_grid_changed();
}
}
Duckmatic::GuideList::iterator
Duckmatic::find_guide_x(synfig::Point pos, float radius)
{
GuideList::iterator iter,best(guide_list_x_.end());
float dist(radius);
for(iter=guide_list_x_.begin();iter!=guide_list_x_.end();++iter)
{
float amount(abs(*iter-pos[0]));
if(amount<dist)
{
dist=amount;
best=iter;
}
}
return best;
}
Duckmatic::GuideList::iterator
Duckmatic::find_guide_y(synfig::Point pos, float radius)
{
GuideList::iterator iter,best(guide_list_y_.end());
float dist(radius);
for(iter=guide_list_y_.begin();iter!=guide_list_y_.end();++iter)
{
float amount(abs(*iter-pos[1]));
if(amount<=dist)
{
dist=amount;
best=iter;
}
}
return best;
}
Point
Duckmatic::snap_point_to_grid(const synfig::Point& x)const
{
Point ret(x);
float radius(0.1/zoom);
GuideList::const_iterator guide_x,guide_y;
bool has_guide_x(false), has_guide_y(false);
guide_x=find_guide_x(ret,radius);
if(guide_x!=guide_list_x_.end())
has_guide_x=true;
guide_y=find_guide_y(ret,radius);
if(guide_y!=guide_list_y_.end())
has_guide_y=true;
if(get_grid_snap())
{
Point snap(
floor(ret[0]/get_grid_size()[0]+0.5)*get_grid_size()[0],
floor(ret[1]/get_grid_size()[1]+0.5)*get_grid_size()[1]);
if(abs(snap[0]-ret[0])<=radius && (!has_guide_x || abs(snap[0]-ret[0])<=abs(*guide_x-ret[0])))
ret[0]=snap[0],has_guide_x=false;
if(abs(snap[1]-ret[1])<=radius && (!has_guide_y || abs(snap[1]-ret[1])<=abs(*guide_y-ret[1])))
ret[1]=snap[1],has_guide_y=false;
}
if(guide_snap)
{
if(has_guide_x)
ret[0]=*guide_x;
if(has_guide_y)
ret[1]=*guide_y;
}
if(axis_lock)
{
ret-=drag_offset_;
if(abs(ret[0])<abs(ret[1]))
ret[0]=0;
else
ret[1]=0;
ret+=drag_offset_;
}
return ret;
}
/*
-- ** -- S I G N A L S M E T H O D S----------------------------
*/
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_duck(const etl::handle<Duck> &duck, bool moving)
{
if (moving && !duck->get_edit_immediatelly()) return;
if (duck->get_type() == Duck::TYPE_ANGLE)
{
if(!duck->signal_edited()(*duck))
{
throw String("Bad edit");
}
}
else if (App::restrict_radius_ducks &&
duck->is_radius())
{
Point point(duck->get_point());
bool changed = false;
if (point[0] < 0)
{
point[0] = 0;
changed = true;
}
if (point[1] < 0)
{
point[1] = 0;
changed = true;
}
if (changed) duck->set_point(point);
if(!duck->signal_edited()(*duck))
{
throw String("Bad edit");
}
}
else
{
if(!duck->signal_edited()(*duck))
{
throw String("Bad edit");
}
}
}
void
Duckmatic::signal_edited_selected_ducks(bool moving)
{
const DuckList ducks(get_selected_ducks());
DuckList::const_iterator iter;
synfig::GUIDSet old_set(selected_ducks);
// If we have more than 20 things to move, then display
// something to explain that it may take a moment
//smart_ptr<OneMoment> wait; if(ducks.size()>20)wait.spawn();
for(iter=ducks.begin();iter!=ducks.end();++iter)
{
try
{
if (!moving || (*iter)->get_edit_immediatelly())
signal_edited_duck(*iter);
}
catch (const String&)
{
selected_ducks=old_set;
synfig::warning("signals must not throw exceptions");
// signals must not throw exceptions!!!
//throw;
}
}
selected_ducks=old_set;
}
bool
Duckmatic::on_duck_changed(const studio::Duck &duck,const synfigapp::ValueDesc& value_desc)
{
bool lock_animation = get_lock_animation_mode();
synfig::Point value=duck.get_point();
synfig::Type &type(value_desc.get_value_type());
if (type == type_real)
{
if (value_desc.parent_is_value_node())
{
etl::handle<ValueNode_Bone> bone_node =
etl::handle<ValueNode_Bone>::cast_dynamic(
value_desc.get_parent_value_node());
if (bone_node)
{
int index1 = bone_node->get_link_index_from_name("scalex");
int index2 = bone_node->get_link_index_from_name("scalelx");
int angleIndex = bone_node->get_link_index_from_name("angle");
if (value_desc.get_index() == index1
|| value_desc.get_index() == index2)
{
//Bone bone((*bone_node)(get_time()).get(Bone()));
//Real prev_duck_length = bone.get_length() * bone.get_scalex() * bone.get_scalex();
//Real duck_length = duck.get_point().mag();
//Real prev_length = value_desc.get_value(get_time()).get(Real());
//Real new_length = prev_length == 0.f || prev_duck_length == 0.f
// ? duck_length
// : prev_length * duck_length / prev_duck_length;
Real new_length = duck.get_point().mag();
Angle angle = (*bone_node->get_link(angleIndex))(get_time()).get(Angle());
angle += duck.get_rotations();
return canvas_interface->change_value(synfigapp::ValueDesc(bone_node, angleIndex, value_desc.get_parent_desc()), angle, lock_animation)
&& canvas_interface->change_value(value_desc, new_length, lock_animation);
}
}
}
// Zoom duck value (PasteCanvas and Zoom layers) should be
// converted back from exponent to normal
if( duck.get_exponential() ) {
return canvas_interface->change_value(value_desc,log(value.mag()),lock_animation);
} else {
return canvas_interface->change_value(value_desc,value.mag(),lock_animation);
}
}
else
if (type == type_angle)
//return canvas_interface->change_value(value_desc,Angle::tan(value[1],value[0]),lock_animation);
return canvas_interface->change_value(value_desc, value_desc.get_value(get_time()).get(Angle()) + duck.get_rotations(),lock_animation);
else
if (type == type_transformation)
{
if ( duck.get_move_origin()
&& duck.get_origin_duck()
&& duck.get_value_desc().is_valid()
&& duck.get_value_desc().parent_is_layer()
&& etl::handle<Layer_PasteCanvas>::cast_dynamic(duck.get_value_desc().get_layer())
&& duck.get_value_desc().get_param_name() == "origin" )
{
Point origin = duck.get_value_desc().get_value(get_time()).get(Point());
Transformation transformation = duck.get_origin_duck()->get_value_desc().get_value(get_time()).get(Transformation());
Point delta_offset = duck.get_origin_duck()->get_point() - transformation.offset;
Point delta_origin = transformation.back_transform(delta_offset, false);
transformation.offset += delta_offset;
origin += delta_origin;
return canvas_interface->change_value(duck.get_value_desc(), origin, lock_animation)
&& canvas_interface->change_value(duck.get_origin_duck()->get_value_desc(), transformation, lock_animation);
}
else
{
Transformation transformation = value_desc.get_value(get_time()).get(Transformation());
Point axis_x_one(1, transformation.angle);
Point axis_y_one(1, transformation.angle + Angle::deg(90.f) + transformation.skew_angle);
switch(duck.get_type()) {
case Duck::TYPE_POSITION:
transformation.offset = value;
break;
case Duck::TYPE_ANGLE:
transformation.angle += duck.get_rotations();
break;
case Duck::TYPE_SKEW:
transformation.skew_angle += duck.get_rotations();
break;
case Duck::TYPE_SCALE:
transformation.scale = transformation.scale.multiply_coords(duck.get_point());
break;
case Duck::TYPE_SCALE_X:
transformation.scale[0] *= duck.get_point()[0];
break;
case Duck::TYPE_SCALE_Y:
transformation.scale[1] *= duck.get_point()[0];
break;
default:
break;
}
return canvas_interface->change_value(value_desc, transformation, lock_animation);
}
return false;
}
else
if (type == type_bline_point)
{
BLinePoint point = value_desc.get_value(get_time()).get(BLinePoint());
switch(duck.get_type()) {
case Duck::TYPE_VERTEX:
point.set_vertex(duck.get_point());
break;
case Duck::TYPE_WIDTH:
point.set_width(duck.get_point().mag());
break;
case Duck::TYPE_TANGENT:
if (duck.get_scalar() < 0.f)
point.set_tangent1(duck.get_point());
else
if (point.get_merge_tangent_both())
point.set_tangent1(duck.get_point());
else
if (point.get_split_tangent_both())
point.set_tangent2(duck.get_point());
else
if (point.get_split_tangent_angle())
{
point.set_tangent1( Point(duck.get_point().mag(), point.get_tangent1().angle()) );
point.set_tangent2(duck.get_point());
}
else
{
point.set_tangent1( Point(point.get_tangent1().mag(), duck.get_point().angle()) );
point.set_tangent2(duck.get_point());
}
break;
default:
break;
}
return canvas_interface->change_value(value_desc, point, lock_animation);
}
return canvas_interface->change_value(value_desc,value,lock_animation);
}
void
Duckmatic::connect_signals(const Duck::Handle &duck, const synfigapp::ValueDesc& value_desc, CanvasView &canvas_view)
{
duck->signal_edited().connect(
sigc::bind(
sigc::mem_fun(
*this,
&studio::Duckmatic::on_duck_changed),
value_desc));
duck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
canvas_view,
&studio::CanvasView::popup_param_menu),
false),
1.0f),
value_desc));
}
/*
-- ** -- DUCK BEZIER STOKE ADD/ERASE/FIND... M E T H O D S----------------------------
*/
void
Duckmatic::add_duck(const etl::handle<Duck> &duck)
{
//if(!duck_map.count(duck->get_guid()))
{
if(duck_data_share_map.count(duck->get_data_guid()))
{
duck->set_shared_point(duck_data_share_map[duck->get_data_guid()]);
}
else
{
etl::smart_ptr<synfig::Point> point(new Point(duck->get_point()));
duck->set_shared_point(point);
duck_data_share_map[duck->get_data_guid()]=point;
}
duck_map.insert(duck);
}
last_duck_guid=duck->get_guid();
}
void
Duckmatic::add_bezier(const etl::handle<Bezier> &bezier)
{
bezier_list_.push_back(bezier);
}
void
Duckmatic::add_stroke(etl::smart_ptr<std::list<synfig::Point> > stroke_point_list, const synfig::Color& color)
{
assert(stroke_point_list);
std::list<etl::handle<Stroke> >::iterator iter;
for(iter=stroke_list_.begin();iter!=stroke_list_.end();++iter)
{
if((*iter)->stroke_data==stroke_point_list)
return;
}
etl::handle<Stroke> stroke(new Stroke());
stroke->stroke_data=stroke_point_list;
stroke->color=color;
stroke_list_.push_back(stroke);
}
void
Duckmatic::add_persistent_stroke(etl::smart_ptr<std::list<synfig::Point> > stroke_point_list, const synfig::Color& color)
{
add_stroke(stroke_point_list,color);
persistent_stroke_list_.push_back(stroke_list_.back());
}
void
Duckmatic::clear_persistent_strokes()
{
persistent_stroke_list_.clear();
}
void
Duckmatic::set_show_persistent_strokes(bool x)
{
if(x!=show_persistent_strokes)
{
show_persistent_strokes=x;
if(x)
stroke_list_=persistent_stroke_list_;
else
stroke_list_.clear();
}
}
void
Duckmatic::erase_duck(const etl::handle<Duck> &duck)
{
duck_map.erase(duck->get_guid());
}
etl::handle<Duckmatic::Duck>
Duckmatic::find_similar_duck(etl::handle<Duck> duck)
{
DuckMap::const_iterator iter(duck_map.find(duck->get_guid()));
if(iter!=duck_map.end())
return iter->second;
return 0;
/* std::list<handle<Duck> >::reverse_iterator iter;
for(iter=duck_list_.rbegin();iter!=duck_list_.rend();++iter)
{
if(*iter!=duck && **iter==*duck)
{
//synfig::info("Found similar duck! (iter:%08x vs. duck:%08x)",iter->get(), duck.get());
return *iter;
}
}
return 0;
*/
}
etl::handle<Duckmatic::Duck>
Duckmatic::add_similar_duck(etl::handle<Duck> duck)
{
etl::handle<Duck> similar(find_similar_duck(duck));
if(!similar)
{
add_duck(duck);
return duck;
}
return similar;
}
void
Duckmatic::erase_bezier(const etl::handle<Bezier> &bezier)
{
std::list<handle<Bezier> >::iterator iter;
for(iter=bezier_list_.begin();iter!=bezier_list_.end();++iter)
{
if(*iter==bezier)
{
bezier_list_.erase(iter);
return;
}
}
synfig::warning("Unable to find bezier to erase!");
}
etl::handle<Duckmatic::Duck>
Duckmatic::last_duck()const
{
DuckMap::const_iterator iter(duck_map.find(last_duck_guid));
if(iter!=duck_map.end())
return iter->second;
return 0;
}
etl::handle<Duckmatic::Bezier>
Duckmatic::last_bezier()const
{
return bezier_list_.back();
}
etl::handle<Duckmatic::Duck>
Duckmatic::find_duck(synfig::Point point, synfig::Real radius, Duck::Type type)
{
if(radius==0)radius=10000000;
if(type==Duck::TYPE_DEFAULT)
type=get_type_mask();
Real closest(10000000);
etl::handle<Duck> ret;
std::vector< etl::handle<Duck> > ret_vector;
DuckMap::const_iterator iter;
for(iter=duck_map.begin();iter!=duck_map.end();++iter)
{
const Duck::Handle& duck(iter->second);
if(duck->get_ignore() ||
(duck->get_type() && !(type & duck->get_type())))
continue;
Real dist((duck->get_trans_point()-point).mag_squared());
bool equal;
equal=fabs(dist-closest)<0.0000001?true:false;
if(dist<closest || equal)
{
// if there are two ducks at the "same" position, keep track of them
if(equal)
{
// if we haven't any duck stored keep track of last found
if(!ret_vector.size())
ret_vector.push_back(ret);
// and also keep track of the one on the same place
ret_vector.push_back(duck);
}
// we have another closer duck then discard the stored
else if (ret_vector.size())
ret_vector.clear();
closest=dist;
ret=duck;
}
}
// Priorization of duck selection when are in the same place.
bool found(false);
if(ret_vector.size())
{
unsigned int i;
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_WIDTHPOINT_POSITION)
{
ret=ret_vector[i];
found=true;
break;
}
if(!found)
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_WIDTH)
{
ret=ret_vector[i];
found=true;
break;
}
if(!found)
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_RADIUS)
{
ret=ret_vector[i];
found=true;
break;
}
if(!found)
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_VERTEX)
{
ret=ret_vector[i];
found=true;
break;
}
if(!found)
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_TANGENT)
{
ret=ret_vector[i];
found=true;
break;
}
if(!found)
for(i=0; i<ret_vector.size();i++)
if(ret_vector[i]->get_type() & Duck::TYPE_POSITION)
{
ret=ret_vector[i];
found=true;
break;
}
}
if(radius==0 || closest<radius*radius)
return ret;
return 0;
}
etl::handle<Duckmatic::Bezier>
Duckmatic::find_bezier(synfig::Point point, synfig::Real radius,float* location)
{
return find_bezier(point,radius,radius,location);
}
etl::handle<Duckmatic::Bezier>
Duckmatic::find_bezier(synfig::Point pos, synfig::Real scale, synfig::Real radius, float* location)
{
if(radius==0)radius=10000000;
Real closest(10000000);
etl::handle<Bezier> ret;
bezier<Point> curve;
Real d,step;
float time = 0;
float best_time = 0;
for(std::list<handle<Bezier> >::const_iterator iter=bezier_list().begin();iter!=bezier_list().end();++iter)
{
curve[0] = (*iter)->p1->get_trans_point();
curve[1] = (*iter)->c1->get_trans_point();
curve[2] = (*iter)->c2->get_trans_point();
curve[3] = (*iter)->p2->get_trans_point();
curve.sync();
#if 0
// I don't know why this doesn't work
time=curve.find_closest(pos,6);
d=((curve(time)-pos).mag_squared());
#else
//set the step size based on the size of the picture
d = (curve[1] - curve[0]).mag() + (curve[2]-curve[1]).mag() + (curve[3]-curve[2]).mag();
step = d/(2*scale); //want to make the distance between lines happy
step = max(step,0.01); //100 samples should be plenty
step = min(step,0.1); //10 is minimum
d = find_closest(curve,pos,step,&closest,&time);
#endif
if(d < closest)
{
closest = d;
ret = *iter;
best_time=time;
}
}
if(closest < radius*radius)
{
if(location)
*location = best_time; // We need to square-root this because we were dealing with squared distances
return ret;
}
return 0;
}
/*
-- ** -- Duckmatic sketch M E T H O D S----------------------------
*/
bool
Duckmatic::save_sketch(const synfig::String& filename)const
{
ChangeLocale change_locale(LC_NUMERIC, "C");
std::ofstream file(filename.c_str());
if(!file)return false;
file<<"SKETCH"<<endl;
std::list<etl::handle<Stroke> >::const_iterator iter;
for(iter=persistent_stroke_list_.begin();iter!=persistent_stroke_list_.end();++iter)
{
file<<"C "
<<(*iter)->color.get_r()<<' '
<<(*iter)->color.get_g()<<' '
<<(*iter)->color.get_b()
<<endl;
std::list<synfig::Point>::const_iterator viter;
for(viter=(*iter)->stroke_data->begin();viter!=(*iter)->stroke_data->end();++viter)
{
file<<"V "
<<(*viter)[0]<<' '
<<(*viter)[1]
<<endl;
}
}
if(!file)return false;
sketch_filename_=filename;
signal_sketch_saved_();
return true;
}
bool
Duckmatic::load_sketch(const synfig::String& filename)
{
ChangeLocale change_locale(LC_NUMERIC, "C");
std::ifstream file(filename.c_str());
if(!file)
return false;
std::string line;
getline(file,line);
if(line!="SKETCH")
{
synfig::error("Not a sketch");
return false;
}
etl::smart_ptr<std::list<synfig::Point> > stroke_data;
while(file)
{
getline(file,line);
if(line.empty())
continue;
switch(line[0])
{
case 'C':
case 'c':
{
stroke_data.spawn();
float r,g,b;
if(!strscanf(line,"C %f %f %f",&r, &g, &b))
{
synfig::warning("Bad color line \"%s\"",line.c_str());
r=0;g=0;b=0;
}
add_persistent_stroke(stroke_data, synfig::Color(r,g,b));
}
break;
case 'V':
case 'v':
if(!stroke_data)
{
stroke_data.spawn();
add_persistent_stroke(stroke_data, synfig::Color(0,0,0));
}
float x,y;
if(!strscanf(line,"V %f %f",&x, &y))
synfig::warning("Bad vertex \"%s\"",line.c_str());
else
stroke_data->push_back(synfig::Vector(x,y));
break;
default:
synfig::warning("Unexpected sketch token '%c'",line[0]);
break;
}
}
sketch_filename_=filename;
return true;
}
void
Duckmatic::add_ducks_layers(synfig::Canvas::Handle canvas, std::set<synfig::Layer::Handle>& selected_layer_set, etl::handle<CanvasView> canvas_view, synfig::TransformStack& transform_stack, int *out_transform_count)
{
int transforms(0);
String layer_name;
#define QUEUE_REBUILD_DUCKS sigc::mem_fun(*canvas_view,&CanvasView::queue_rebuild_ducks)
if(!canvas)
{
synfig::warning("Duckmatic::add_ducks_layers(): Layer doesn't have canvas set");
return;
}
for(Canvas::iterator iter(canvas->begin());iter!=canvas->end();++iter)
{
Layer::Handle layer(*iter);
if(selected_layer_set.count(layer))
{
if(!curr_transform_stack_set)
{
curr_transform_stack_set=true;
curr_transform_stack=transform_stack;
}
// This layer is currently selected.
duck_changed_connections.push_back(layer->signal_changed().connect(QUEUE_REBUILD_DUCKS));
// do the bounding box thing
synfig::Rect& bbox = canvas_view->get_bbox();
// special calculations for Layer_PasteCanvas
etl::handle<Layer_PasteCanvas> layer_pastecanvas( etl::handle<Layer_PasteCanvas>::cast_dynamic(layer) );
synfig::Rect layer_bounds = layer_pastecanvas
? layer_pastecanvas->get_bounding_rect_context_dependent(canvas_view->get_context_params())
: layer->get_bounding_rect();
bbox|=transform_stack.perform(layer_bounds);
// Grab the layer's list of parameters
Layer::ParamList paramlist(layer->get_param_list());
// Grab the layer vocabulary
Layer::Vocab vocab=layer->get_param_vocab();
Layer::Vocab::iterator iter;
for(iter=vocab.begin();iter!=vocab.end();iter++)
{
if(!iter->get_hidden() && !iter->get_invisible_duck())
{
synfigapp::ValueDesc value_desc(layer,iter->get_name());
add_to_ducks(value_desc,canvas_view,transform_stack,&*iter);
if(value_desc.is_value_node())
duck_changed_connections.push_back(value_desc.get_value_node()->signal_changed().connect(QUEUE_REBUILD_DUCKS));
}
}
}
layer_name=layer->get_name();
if(layer->active())
{
Transform::Handle trans(layer->get_transform());
if(trans)
{
transform_stack.push(trans);
transforms++;
}
}
// If this is a paste canvas layer, then we need to
// descend into it
if(etl::handle<Layer_PasteCanvas> layer_pastecanvas = etl::handle<Layer_PasteCanvas>::cast_dynamic(layer))
{
transform_stack.push_back(
new Transform_Matrix(
layer->get_guid(),
layer_pastecanvas->get_summary_transformation().get_matrix()
)
);
Canvas::Handle child_canvas(layer->get_param("canvas").get(Canvas::Handle()));
// keep stack
if ( etl::handle<Layer_FilterGroup>::cast_dynamic(layer_pastecanvas)
&& layer_pastecanvas->get_amount() > 0.5 )
{
transforms++;
add_ducks_layers(child_canvas,selected_layer_set,canvas_view,transform_stack, &transforms);
}
else
{
add_ducks_layers(child_canvas,selected_layer_set,canvas_view,transform_stack);
transform_stack.pop();
}
}
}
if (out_transform_count)
{
// keep stack and return count transforms ...
*out_transform_count += transforms;
}
else
{
// ... or remove all of the transforms we have added
while(transforms--) { transform_stack.pop(); }
}
#undef QUEUE_REBUILD_DUCKS
}
/*
-- ** -- add_to_ducks GIANT M E T H O D S-------------------------------------
-- ** -- -----------------------------------------------------------------------
-- ** -- S H O U L D B E S I M P L I F I E D---------------------------------
-- ** -- -----------------------------------------------------------------------
---**-----------------------TODO-Must be simplified!(1500 lines length)-------
-- ** -- -----------------------------------------------------------------------
*/
bool
Duckmatic::add_to_ducks(const synfigapp::ValueDesc& value_desc,etl::handle<CanvasView> canvas_view, const synfig::TransformStack& transform_stack, synfig::ParamDesc *param_desc)
{
synfig::Type &type=value_desc.get_value_type();
#define REAL_COOKIE reinterpret_cast<synfig::ParamDesc*>(28)
if (type == type_real)
{
if(!param_desc || param_desc==REAL_COOKIE || !param_desc->get_origin().empty())
{
etl::handle<Duck> duck=new Duck();
set_duck_value_desc(*duck, value_desc, transform_stack);
duck->set_radius(true);
duck->set_type(Duck::TYPE_RADIUS);
// put the duck on the right hand side of the center
// Zoom parameter value (PasteCanvas and Zoom layers)
// should be represented as exponent
if ( param_desc && param_desc!=REAL_COOKIE && param_desc->get_exponential() )
{
duck->set_point(Point(exp(value_desc.get_value(get_time()).get(Real())), 0));
duck->set_exponential(param_desc->get_exponential());
} else {
duck->set_point(Point(value_desc.get_value(get_time()).get(Real()), 0));
duck->set_exponential(false);
}
if(value_desc.is_value_node())
{
// If the ValueNode can be directly manipulated,
// then set it as so.
duck->set_editable(synfigapp::is_editable(value_desc.get_value_node()));
}
else
{
duck->set_editable(true);
}
if(param_desc && param_desc!=REAL_COOKIE)
{
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
/*
duck->set_origin(value_desc_origin.get_value(get_time()).get(synfig::Point()));
*/
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
Layer::Handle layer=value_desc.get_layer();
if(etl::handle<Layer_PasteCanvas>::cast_dynamic(layer))
{
Vector focus(layer->get_param("focus").get(Vector()));
duck->set_origin(last_duck()->get_point() + focus);
}
else
duck->set_origin(last_duck());
}
duck->set_scalar(param_desc->get_scalar());
}
duck->signal_edited().clear(); // value_desc.get_value_type() == type_real:
duck->signal_edited().connect(
sigc::bind(
sigc::mem_fun(
*this,
&studio::Duckmatic::on_duck_changed),
value_desc));
duck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu),
false),
0.0f),
value_desc));
add_duck(duck);
return true;
}
}
else
if (type == type_angle)
{
if(!param_desc || param_desc==REAL_COOKIE || !param_desc->get_origin().empty())
{
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_ANGLE);
set_duck_value_desc(*duck, value_desc, transform_stack);
synfig::Angle angle;
angle=value_desc.get_value(get_time()).get(Angle());
duck->set_point(Point(Angle::cos(angle).get(),Angle::sin(angle).get()));
if(value_desc.is_value_node())
{
ValueNode::Handle value_node=value_desc.get_value_node();
//duck->set_name(strprintf("%x",value_node.get()));
// If the ValueNode can be directly manipulated,
// then set it as so.
duck->set_editable(synfigapp::is_editable(value_desc.get_value_node()));
}
else
{
//angle=(value_desc.get_value().get(Angle()));
//duck->set_point(Point(Angle::cos(angle).get(),Angle::sin(angle).get()));
//duck->set_name(strprintf("%x",value_desc.get_layer().get())+value_desc.get_param_name());
duck->set_editable(true);
}
if(param_desc && param_desc!=REAL_COOKIE)
{
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
/*
duck->set_origin(value_desc_origin.get_value(get_time()).get(synfig::Point()));
*/
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_origin(last_duck());
}
duck->set_scalar(param_desc->get_scalar());
}
duck->signal_edited().clear(); // value_desc.get_value_type() == type_angle:
duck->signal_edited().connect(
sigc::bind(
sigc::mem_fun(
*this,
&studio::Duckmatic::on_duck_changed),
value_desc));
duck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu),
false),
0.0f),
value_desc));
add_duck(duck);
return true;
}
}
else
if (type == type_vector)
{
etl::handle<Layer_PasteCanvas> layer;
if (value_desc.parent_is_layer())
layer = etl::handle<Layer_PasteCanvas>::cast_dynamic(value_desc.get_layer());
if (!layer) {
etl::handle<Duck> duck=new Duck();
set_duck_value_desc(*duck, value_desc, transform_stack);
ValueNode_Composite::Handle blinepoint_value_node;
int index;
bool done(false);
if(value_desc.parent_is_linkable_value_node()
&&
value_desc.get_parent_value_node()->get_type() == type_bline_point)
{
blinepoint_value_node=ValueNode_Composite::Handle::cast_dynamic(value_desc.get_parent_value_node());
if(blinepoint_value_node)
{
index=blinepoint_value_node->get_link_index_from_name("t2");
if(index==value_desc.get_index())
{
BLinePoint bp=(*blinepoint_value_node)(get_time()).get(BLinePoint());
Vector t2=bp.get_tangent2();
duck->set_point(t2);
done=true;
}
}
}
if(!done)
duck->set_point(value_desc.get_value(get_time()).get(Point()));
if(value_desc.is_value_node())
{
// if the vertex is converted to 'bone influence', add the bones' ducks
if (ValueNode_BoneInfluence::Handle bone_influence_vertex_value_node =
ValueNode_BoneInfluence::Handle::cast_dynamic(value_desc.get_value_node()))
add_to_ducks(synfigapp::ValueDesc(bone_influence_vertex_value_node,
bone_influence_vertex_value_node->get_link_index_from_name("bone_weight_list")),
canvas_view, transform_stack);
// If the ValueNode can be directly manipulated,
// then set it as so.
duck->set_editable(synfigapp::is_editable(value_desc.get_value_node()));
}
else
{
//duck->set_point(value_desc.get_value().get(Point()));
//duck->set_name(strprintf("%x",value_desc.get_layer().get())+value_desc.get_param_name());
duck->set_editable(true);
}
// If we were passed a parameter description
if(param_desc)
{
if(!param_desc->get_connect().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_connect());
Duck::Handle connect_duck;
if(duck_map.find(calc_duck_guid(value_desc_origin, transform_stack))!=duck_map.end())
{
connect_duck=duck_map[calc_duck_guid(value_desc_origin, transform_stack)];
}
else
{
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
connect_duck=last_duck();
}
duck->set_connect_duck(connect_duck);
}
if(!param_desc->get_box().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_box());
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_box_duck(last_duck());
}
// If we have an origin
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
/*
duck->set_origin(value_desc_origin.get_value(get_time()).get(synfig::Point()));
*/
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_origin(last_duck());
duck->set_type(Duck::TYPE_VERTEX);
}
else
duck->set_type(Duck::TYPE_POSITION);
duck->set_scalar(param_desc->get_scalar());
}
else
duck->set_type(Duck::TYPE_POSITION);
duck->signal_edited().clear(); // value_desc.get_value_type() == type_vector:
duck->signal_edited().connect(
sigc::bind(
sigc::mem_fun(
*this,
&studio::Duckmatic::on_duck_changed),
value_desc));
duck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu),
false),
1.0f),
value_desc));
add_duck(duck);
return true;
}
}
else
if (type == type_transformation)
{
if (value_desc.parent_is_layer() && param_desc != NULL)
{
etl::handle<Layer_PasteCanvas> layer = etl::handle<Layer_PasteCanvas>::cast_dynamic(value_desc.get_layer());
if (layer)
{
synfigapp::ValueDesc origin_value_desc(value_desc.get_layer(), "origin");
Transformation transformation = value_desc.get_value(get_time()).get(Transformation());
bool editable = !value_desc.is_value_node()
|| synfigapp::is_editable(value_desc.get_value_node());
bool origin_editable = !origin_value_desc.is_value_node()
|| synfigapp::is_editable(origin_value_desc.get_value_node());
origin_editable = origin_editable && editable;
Point axis_x(1, transformation.angle);
Point axis_y(1, transformation.angle + Angle::deg(90.f) + transformation.skew_angle);
Point screen_offset = transform_stack.perform(transformation.offset);
Point screen_axis_x = transform_stack.perform(transformation.offset + axis_x) - screen_offset;
Point screen_axis_y = transform_stack.perform(transformation.offset + axis_y) - screen_offset;
Real scalar_x = screen_axis_x.mag();
if (scalar_x > 0.0) scalar_x = 1.0/scalar_x;
Real scalar_y = screen_axis_y.mag();
if (scalar_y > 0.0) scalar_y = 1.0/scalar_y;
scalar_x /= zoom;
scalar_y /= zoom;
Real pw = canvas_interface->get_canvas()->rend_desc().get_pw();
Real ph = canvas_interface->get_canvas()->rend_desc().get_ph();
scalar_x *= 75.0 * fabs(pw);
scalar_y *= 75.0 * fabs(ph);
Duck::Handle duck;
// add offset duck
duck=new Duck();
set_duck_value_desc(*duck, value_desc, "offset", transform_stack);
duck->set_point(transformation.offset);
duck->set_editable(editable);
duck->set_type(Duck::TYPE_POSITION);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> origin_duck = duck;
// add angle duck
duck=new Duck();
duck->set_type(Duck::TYPE_ANGLE);
set_duck_value_desc(*duck, value_desc, "angle", transform_stack);
duck->set_point(Point(0.8,transformation.angle));
duck->set_scalar(scalar_x);
duck->set_editable(editable);
duck->set_origin(origin_duck);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> angle_duck = duck;
// add skew duck
duck=new Duck();
duck->set_type(Duck::TYPE_SKEW);
set_duck_value_desc(*duck, value_desc, "skew_angle", transform_stack);
duck->set_point(Point(0.8,transformation.skew_angle));
duck->set_scalar(scalar_y);
duck->set_editable(editable);
duck->set_origin(origin_duck);
duck->set_axis_x_angle(angle_duck, Angle::deg(90));
duck->set_axis_y_angle(angle_duck, Angle::deg(180));
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> skew_duck = duck;
// add scale-x duck
duck=new Duck();
duck->set_type(Duck::TYPE_SCALE_X);
set_duck_value_desc(*duck, value_desc.get_sub_value("scale").get_sub_value("x"), transform_stack);
duck->set_point(Point(1,0));
duck->set_scalar(scalar_x);
duck->set_editable(editable);
duck->set_origin(origin_duck);
duck->set_linear(true, angle_duck);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> scale_x_duck = duck;
// add scale-y duck
duck=new Duck();
duck->set_type(Duck::TYPE_SCALE_Y);
set_duck_value_desc(*duck, value_desc.get_sub_value("scale").get_sub_value("y"), transform_stack);
duck->set_point(Point(1,0));
duck->set_scalar(scalar_y);
duck->set_editable(editable);
duck->set_origin(origin_duck);
duck->set_linear(true, skew_duck);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> scale_y_duck = duck;
// add scale duck
duck=new Duck();
duck->set_type(Duck::TYPE_SCALE);
set_duck_value_desc(*duck, value_desc, "scale", transform_stack);
duck->set_point(Point(1,1));
duck->set_lock_aspect(true);
duck->set_editable(editable);
duck->set_origin(origin_duck);
duck->set_axis_x_angle(scale_x_duck);
duck->set_axis_x_mag(scale_x_duck);
duck->set_axis_y_angle(scale_y_duck);
duck->set_axis_y_mag(scale_y_duck);
duck->set_track_axes(true);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
// add move-origin duck
if (origin_editable) {
duck=new Duck();
set_duck_value_desc(*duck, origin_value_desc, transform_stack);
duck->set_point(Point(0, -0.2));
duck->set_editable(origin_editable);
duck->set_type(Duck::TYPE_POSITION);
duck->set_origin(origin_duck);
duck->set_move_origin(true);
duck->set_scalar(scalar_y);
duck->set_axis_y_angle(scale_y_duck);
duck->set_track_axes(true);
connect_signals(duck, duck->get_origin_duck()->get_value_desc(), *canvas_view);
add_duck(duck);
}
return true;
}
}
}
else
if (type == type_segment)
{
int index;
etl::handle<Bezier> bezier(new Bezier());
ValueNode_Composite::Handle value_node;
if(value_desc.is_value_node() &&
(value_node=ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())))
{
index=value_node->get_link_index_from_name("p1");
if(!add_to_ducks(synfigapp::ValueDesc(value_node,index),canvas_view,transform_stack))
return false;
bezier->p1=last_duck();
bezier->p1->set_type(Duck::TYPE_VERTEX);
index=value_node->get_link_index_from_name("t1");
if(!add_to_ducks(synfigapp::ValueDesc(value_node,index),canvas_view,transform_stack))
return false;
bezier->c1=last_duck();
bezier->c1->set_type(Duck::TYPE_TANGENT);
bezier->c1->set_origin(bezier->p1);
bezier->c1->set_scalar(TANGENT_BEZIER_SCALE);
bezier->c1->set_tangent(true);
index=value_node->get_link_index_from_name("p2");
if(!add_to_ducks(synfigapp::ValueDesc(value_node,index),canvas_view,transform_stack))
return false;
bezier->p2=last_duck();
bezier->p2->set_type(Duck::TYPE_VERTEX);
index=value_node->get_link_index_from_name("t2");
if(!add_to_ducks(synfigapp::ValueDesc(value_node,index),canvas_view,transform_stack))
return false;
bezier->c2=last_duck();
bezier->c2->set_type(Duck::TYPE_TANGENT);
bezier->c2->set_origin(bezier->p2);
bezier->c2->set_scalar(-TANGENT_BEZIER_SCALE);
bezier->c2->set_tangent(true);
bezier->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
value_desc));
add_bezier(bezier);
}
else if(value_desc.get_value().is_valid())
{
Segment segment=value_desc.get_value().get(Segment());
etl::handle<Duck> duck_p,duck_c;
synfig::String name;
if(param_desc)
name=param_desc->get_local_name();
else
name=value_desc.get_guid_string();
duck_p=new Duck(segment.p1);
duck_p->set_name(name+".P1");
duck_p->set_type(Duck::TYPE_VERTEX);
add_duck(duck_p);
duck_c=new Duck(segment.t1);
duck_c->set_name(name+".T1");
duck_c->set_type(Duck::TYPE_TANGENT);
add_duck(duck_c);
duck_c->set_origin(duck_p);
duck_c->set_scalar(TANGENT_HANDLE_SCALE);
duck_c->set_tangent(true);
bezier->p1=duck_p;
bezier->c1=duck_c;
duck_p=new Duck(segment.p2);
duck_p->set_name(name+".P2");
duck_p->set_type(Duck::TYPE_VERTEX);
add_duck(duck_p);
duck_c=new Duck(segment.t2);
duck_c->set_type(Duck::TYPE_TANGENT);
duck_c->set_name(name+".T2");
add_duck(duck_c);
duck_c->set_origin(duck_p);
duck_c->set_scalar(-TANGENT_HANDLE_SCALE);
duck_c->set_tangent(true);
bezier->p2=duck_p;
bezier->c2=duck_c;
add_bezier(bezier);
}
return true;
}
else
if (type == type_bline_point)
{
bool editable = !value_desc.is_value_node() || synfigapp::is_editable(value_desc.get_value_node());
BLinePoint point = value_desc.get_value(get_time()).get(BLinePoint());
Duck::Handle duck;
// add vertex duck
duck=new Duck();
set_duck_value_desc(*duck, value_desc, "point", transform_stack);
duck->set_point(point.get_vertex());
duck->set_editable(editable);
duck->set_type(Duck::TYPE_VERTEX);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
etl::handle<Duck> vertex_duck = duck;
// add tangent1 duck
duck=new Duck();
duck->set_type(Duck::TYPE_TANGENT);
set_duck_value_desc(*duck, value_desc, "t1", transform_stack);
duck->set_point(point.get_tangent1());
duck->set_editable(editable);
duck->set_origin(vertex_duck);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
// add tangent2 duck
duck=new Duck();
duck->set_type(Duck::TYPE_TANGENT);
set_duck_value_desc(*duck, value_desc, "t2", transform_stack);
duck->set_point(point.get_tangent2());
duck->set_editable(editable);
duck->set_origin(vertex_duck);
duck->set_scalar(-1);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
add_duck(duck);
return true;
}
else
if (type == type_list)
{
// Check for BLine
if (value_desc.is_value_node() &&
ValueNode_BLine::Handle::cast_dynamic(value_desc.get_value_node()))
{
ValueNode_BLine::Handle value_node;
value_node=ValueNode_BLine::Handle::cast_dynamic(value_desc.get_value_node());
int i,first=-1;
etl::handle<Bezier> bezier;
etl::handle<Duck> first_duck, first_tangent2_duck;
for (i = 0; i < value_node->link_count(); i++)
{
float amount(value_node->list[i].amount_at_time(get_time()));
// skip vertices that aren't fully on
if (amount < 0.9999f)
continue;
// remember the index of the first vertex we didn't skip
if (first == -1)
first = i;
ValueNode::Handle sub_node = value_node->get_link(i);
bool editable = synfigapp::is_editable(sub_node);
BLinePoint bline_point((*value_node->get_link(i))(get_time()).get(BLinePoint()));
synfigapp::ValueDesc sub_value_desc(value_node, i, value_desc);
// Now add the ducks:
Duck::Handle duck;
// ----Vertex Duck
duck=new Duck(bline_point.get_vertex());
set_duck_value_desc(*duck, sub_value_desc, "point", transform_stack);
duck->set_editable(editable);
duck->set_type(Duck::TYPE_VERTEX);
if(param_desc)
{
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_origin(last_duck());
}
}
duck=add_similar_duck(duck);
if(i==first) first_duck=duck;
connect_signals(duck, duck->get_value_desc(), *canvas_view);
Duck::Handle vertex_duck = duck;
// ----Width duck
Duck::Handle width;
// Add the width duck if it is a parameter with a hint (ie. "width") or if it isn't a parameter
//if (! ((param_desc && !param_desc->get_hint().empty()) || !param_desc) )
if (param_desc && param_desc->get_hint().empty())
{
// if it's a parameter without a hint, then don't add the width duck
// (This prevents width ducks from being added to region layers, and possibly other cases)
}
else
{
// add width duck
duck=new Duck();
set_duck_value_desc(*duck, sub_value_desc, "width", transform_stack);
duck->set_radius(true);
duck->set_point(Point(bline_point.get_width(), 0));
duck->set_editable(editable);
duck->set_type(Duck::TYPE_WIDTH);
duck->set_origin(vertex_duck);
connect_signals(duck, duck->get_value_desc(), *canvas_view);
// if the bline is a layer's parameter, scale the width duck by the layer's "width" parameter
if (param_desc)
{
ValueBase value(synfigapp::ValueDesc(value_desc.get_layer(),param_desc->get_hint()).get_value(get_time()));
Real gv(value_desc.get_layer()->get_outline_grow_mark());
if(value.same_type_as(synfig::Real()))
duck->set_scalar(exp(gv)*value.get(synfig::Real())*0.5f);
// if it doesn't have a "width" parameter, scale by 0.5f instead
else
duck->set_scalar(0.5f);
}
// otherwise just present the raw unscaled width
else
duck->set_scalar(0.5f);
add_duck(duck);
width = duck;
}
// each bezier uses t2 of one point and t1 of the next
// the first time through this loop we won't have the t2 duck from the previous vertex
// and so we don't make a bezier. instead we skip on to t2 for this point
Duck::Handle tangent1_duck;
if(bezier)
{
// Add the tangent1 duck
duck=new Duck(bline_point.get_tangent1());
set_duck_value_desc(*duck, sub_value_desc, "t1", transform_stack);
duck->set_editable(editable);
duck=add_similar_duck(duck);
duck->set_origin(vertex_duck);
duck->set_scalar(-TANGENT_BEZIER_SCALE);
duck->set_tangent(true);
duck->set_shared_point(etl::smart_ptr<Point>());
duck->set_shared_angle(etl::smart_ptr<Angle>());
duck->set_shared_mag(etl::smart_ptr<Real>());
connect_signals(duck, duck->get_value_desc(), *canvas_view);
// each bezier uses t2 of one point and t1 of the next
// we should already have a bezier, so add the t1 of this point to it
bezier->p2=vertex_duck;
bezier->c2=duck;
bezier->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,i)));
add_bezier(bezier);
bezier=0;
tangent1_duck = duck;
}
// don't start a new bezier for the last point in the line if we're not looped
if ((i+1>=value_node->link_count() && !value_node->get_loop()))
continue;
bezier=new Bezier();
// Add the tangent2 duck
Duck::Handle tangent2_duck;
duck=new Duck(bline_point.get_tangent2());
set_duck_value_desc(*duck, sub_value_desc, "t2", transform_stack);
duck->set_editable(editable);
duck=add_similar_duck(duck);
duck->set_origin(vertex_duck);
duck->set_scalar(TANGENT_BEZIER_SCALE);
duck->set_tangent(true);
duck->set_shared_point(etl::smart_ptr<Point>());
duck->set_shared_angle(etl::smart_ptr<Angle>());
duck->set_shared_mag(etl::smart_ptr<Real>());
connect_signals(duck, duck->get_value_desc(), *canvas_view);
bezier->p1=vertex_duck;
bezier->c1=duck;
tangent2_duck = duck;
if (i == first) first_tangent2_duck = tangent2_duck;
// link tangents
if (tangent1_duck && tangent2_duck && !bline_point.get_split_tangent_both()) {
if (bline_point.get_merge_tangent_both())
{
etl::smart_ptr<synfig::Point> point(new Point(tangent1_duck->get_point()));
tangent1_duck->set_shared_point(point);
tangent2_duck->set_shared_point(point);
}
else
if (!bline_point.get_split_tangent_angle())
{
etl::smart_ptr<synfig::Angle> angle(new Angle(
approximate_zero( tangent1_duck->get_point().mag() )
? tangent2_duck->get_point().angle()
: tangent1_duck->get_point().angle() ));
tangent1_duck->set_shared_angle(angle);
tangent2_duck->set_shared_angle(angle);
}
else
if (!bline_point.get_split_tangent_radius())
{
etl::smart_ptr<synfig::Real> mag(new Real(tangent1_duck->get_point().mag()));
tangent1_duck->set_shared_mag(mag);
tangent2_duck->set_shared_mag(mag);
}
}
}
// Loop if necessary
if(bezier && value_node->get_loop())
{
ValueNode::Handle sub_node = value_node->get_link(first);
bool editable = synfigapp::is_editable(sub_node);
bool is_bline_point = sub_node->get_type() == type_bline_point;
BLinePoint bline_point;
if (is_bline_point) bline_point = (*sub_node)(get_time()).get(BLinePoint());
ValueNode_BoneInfluence::Handle bone_influence_vertex_value_node(
ValueNode_BoneInfluence::Handle::cast_dynamic(value_node->get_link(first)));
ValueNode_Composite::Handle composite_bone_link_value_node;
synfig::TransformStack bone_transform_stack(transform_stack);
if (bone_influence_vertex_value_node)
{
// apply bones transformation to the ducks
composite_bone_link_value_node = ValueNode_Composite::Handle::cast_dynamic(
bone_influence_vertex_value_node->get_link("link") );
if(param_desc)
{
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
add_to_ducks(value_desc_origin, canvas_view, transform_stack);
synfig::GUID guid(calc_duck_guid(value_desc_origin, transform_stack));
bone_transform_stack.push(new Transform_Origin(guid^synfig::GUID::hasher("origin"), last_duck()));
}
}
Matrix transform(bone_influence_vertex_value_node->calculate_transform(get_time()));
synfig::GUID guid(bone_influence_vertex_value_node->get_link("bone_weight_list")->get_guid());
bone_transform_stack.push(new Transform_Matrix(guid, transform));
}
// Add the vertex duck
Duck::Handle duck;
Duck::Handle vertex_duck(first_duck);
Duck::Handle tangent2_duck(first_tangent2_duck);
synfigapp::ValueDesc sub_value_desc(value_node,first,value_desc);
// Add the tangent1 duck
duck=new Duck(bline_point.get_tangent1());
set_duck_value_desc(*duck, sub_value_desc, "t1", transform_stack);
duck->set_editable(editable);
duck=add_similar_duck(duck);
duck->set_origin(vertex_duck);
duck->set_scalar(-TANGENT_BEZIER_SCALE);
duck->set_tangent(true);
duck->set_shared_point(etl::smart_ptr<Point>());
duck->set_shared_angle(etl::smart_ptr<Angle>());
duck->set_shared_mag(etl::smart_ptr<Real>());
connect_signals(duck, duck->get_value_desc(), *canvas_view);
bezier->p2=vertex_duck;
bezier->c2=duck;
bezier->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,first)));
add_bezier(bezier);
bezier=0;
Duck::Handle tangent1_duck = duck;
// link tangents
if (tangent1_duck && tangent2_duck && !bline_point.get_split_tangent_both()) {
if (bline_point.get_merge_tangent_both())
{
etl::smart_ptr<synfig::Point> point(new Point(tangent1_duck->get_point()));
tangent1_duck->set_shared_point(point);
tangent2_duck->set_shared_point(point);
}
else
if (!bline_point.get_split_tangent_angle())
{
etl::smart_ptr<synfig::Angle> angle(new Angle(
approximate_zero( tangent1_duck->get_point().mag() )
? tangent2_duck->get_point().angle()
: tangent1_duck->get_point().angle() ));
tangent1_duck->set_shared_angle(angle);
tangent2_duck->set_shared_angle(angle);
}
else
if (!bline_point.get_split_tangent_radius())
{
etl::smart_ptr<synfig::Real> mag(new Real(tangent1_duck->get_point().mag()));
tangent1_duck->set_shared_mag(mag);
tangent2_duck->set_shared_mag(mag);
}
}
}
return true;
}
else // Check for StaticList
if(value_desc.is_value_node() &&
ValueNode_StaticList::Handle::cast_dynamic(value_desc.get_value_node()))
{
ValueNode_StaticList::Handle value_node;
value_node=ValueNode_StaticList::Handle::cast_dynamic(value_desc.get_value_node());
int i;
synfig::Type &contained_type(value_node->get_contained_type());
if (contained_type == type_vector)
{
Bezier bezier;
etl::handle<Duck> first_duck, duck;
int first = -1;
for(i=0;i<value_node->link_count();i++)
{
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i),canvas_view,transform_stack))
return false;
duck = last_duck();
// remember the index of the first vertex we didn't skip
if (first == -1)
{
first = i;
first_duck = duck;
}
if(param_desc && !param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_origin(last_duck());
/*
ValueBase value(synfigapp::ValueDesc(value_desc.get_layer(),param_desc->get_origin()).get_value(get_time()));
if(value.same_type_as(synfig::Point()))
duck->set_origin(value.get(synfig::Point()));
*/
// if(!param_desc->get_origin().empty())
// last_duck()->set_origin(synfigapp::ValueDesc(value_desc.get_layer(),param_desc->get_origin()).get_value(get_time()).get(synfig::Point()));
}
duck->set_type(Duck::TYPE_VERTEX);
bezier.p1=bezier.p2;bezier.c1=bezier.c2;
bezier.p2=bezier.c2=duck;
if (first != i)
{
handle<Bezier> bezier_(new Bezier());
bezier_->p1=bezier.p1;
bezier_->c1=bezier.c1;
bezier_->p2=bezier.p2;
bezier_->c2=bezier.c2;
add_bezier(bezier_);
last_bezier()->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,i)));
}
}
if (value_node->get_loop() && first != -1 && first_duck != duck)
{
duck = first_duck;
bezier.p1=bezier.p2;bezier.c1=bezier.c2;
bezier.p2=bezier.c2=duck;
handle<Bezier> bezier_(new Bezier());
bezier_->p1=bezier.p1;
bezier_->c1=bezier.c1;
bezier_->p2=bezier.p2;
bezier_->c2=bezier.c2;
add_bezier(bezier_);
last_bezier()->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,first)));
}
}
else
if (contained_type == type_segment)
{
for(i=0;i<value_node->link_count();i++)
{
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i),canvas_view,transform_stack))
return false;
}
}
else
if (contained_type == type_bone_object)
{
printf("%s:%d adding ducks\n", __FILE__, __LINE__);
for(i=0;i<value_node->link_count();i++)
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i,value_desc),canvas_view,transform_stack))
return false;
printf("%s:%d adding ducks done\n\n", __FILE__, __LINE__);
}
else
if (contained_type == type_bone_weight_pair)
{
for(i=0;i<value_node->link_count();i++)
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i),canvas_view,transform_stack))
return false;
}
else
if (value_node->get_contained_type() == types_namespace::TypePair<Bone, Bone>::instance)
{
bool edit_second = value_desc.parent_is_layer() && value_desc.get_layer()->active();
for(i=0;i<value_node->link_count();i++)
{
ValueNode_Composite::Handle value_node_composite =
ValueNode_Composite::Handle::cast_dynamic(
value_node->get_link(i) );
if (value_node_composite)
{
if (!add_to_ducks(
synfigapp::ValueDesc(
value_node_composite,
value_node_composite->get_link_index_from_name(edit_second ? "second" : "first"),
synfigapp::ValueDesc(value_node,i,value_desc) ),
canvas_view,
transform_stack ))
return false;
}
}
}
else
return false;
}
else // Check for WPList
if(value_desc.is_value_node() &&
ValueNode_WPList::Handle::cast_dynamic(value_desc.get_value_node()))
{
ValueNode_WPList::Handle value_node;
bool homogeneous=true; // if we have an exported WPList without a layer consider it homogeneous
value_node=ValueNode_WPList::Handle::cast_dynamic(value_desc.get_value_node());
if(!value_node)
{
error("expected a ValueNode_WPList");
assert(0);
}
ValueNode::Handle bline(value_node->get_bline());
// it is not possible to place any widthpoint's duck if there is
// not associated bline.
if(!bline)
return false;
// Retrieve the homogeneous layer parameter
Layer::Handle layer_parent;
if(value_desc.parent_is_layer())
layer_parent=value_desc.get_layer();
if(layer_parent)
{
String layer_name(layer_parent->get_name());
if(layer_name=="advanced_outline")
homogeneous=layer_parent->get_param("homogeneous").get(bool());
}
int i;
for (i = 0; i < value_node->link_count(); i++)
{
float amount(value_node->list[i].amount_at_time(get_time()));
// skip width points that aren't fully on
if (amount < 0.9999f)
continue;
WidthPoint width_point((*value_node->get_link(i))(get_time()).get(WidthPoint()));
// try casting the width point to Composite - this tells us whether it is composite or not
ValueNode_Composite::Handle composite_width_point_value_node(
ValueNode_Composite::Handle::cast_dynamic(value_node->get_link(i)));
if(composite_width_point_value_node) // Add the position
{
etl::handle<Duck> pduck=new Duck();
synfigapp::ValueDesc wpoint_value_desc(value_node, i); // The i-widthpoint on WPList
pduck->set_type(Duck::TYPE_WIDTHPOINT_POSITION);
set_duck_value_desc(*pduck, wpoint_value_desc, transform_stack);
// This is a quick hack to obtain the ducks position.
// The position by amount and the amount by position
// has to be written considering the bline length too
// optionally
ValueNode_BLineCalcVertex::Handle bline_calc_vertex(ValueNode_BLineCalcVertex::create(Vector(0,0)));
bline_calc_vertex->set_link("bline", bline);
bline_calc_vertex->set_link("loop", ValueNode_Const::create(false));
bline_calc_vertex->set_link("amount", ValueNode_Const::create(width_point.get_norm_position(value_node->get_loop())));
bline_calc_vertex->set_link("homogeneous", ValueNode_Const::create(homogeneous));
pduck->set_point((*bline_calc_vertex)(Time()).get(Vector()));
// hack end
pduck->set_editable(synfigapp::is_editable(wpoint_value_desc.get_value_node()));
pduck->signal_edited().clear();
pduck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), wpoint_value_desc));
pduck->signal_user_click(2).clear();
pduck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu),
false),
1.0f),
wpoint_value_desc));
add_duck(pduck);
if(param_desc)
{
if(!param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
pduck->set_origin(last_duck());
}
}
// add the width duck
int index=composite_width_point_value_node->get_link_index_from_name("width");
if (add_to_ducks(synfigapp::ValueDesc(composite_width_point_value_node,index),canvas_view,transform_stack))
{
etl::handle<Duck> wduck;
wduck=last_duck();
wduck->set_origin(pduck);
wduck->set_type(Duck::TYPE_WIDTH);
// if the composite comes from a layer get the layer's "width" parameter and scale the
// duck by that value.
if (param_desc)
{
ValueBase value(synfigapp::ValueDesc(value_desc.get_layer(),"width").get_value(get_time()));
Real gv(value_desc.get_layer()->get_outline_grow_mark());
if(value.same_type_as(synfig::Real()))
wduck->set_scalar(exp(gv)*(value.get(synfig::Real())*0.5f));
// if it doesn't have a "width" parameter, scale by 0.5f instead
else
wduck->set_scalar(0.5f);
}
// otherwise just present the raw unscaled width
else
wduck->set_scalar(0.5f);
}
else
return false;
}
}
return true;
}
else // Check for DynamicList
if(value_desc.is_value_node() &&
ValueNode_DynamicList::Handle::cast_dynamic(value_desc.get_value_node()))
{
ValueNode_DynamicList::Handle value_node;
value_node=ValueNode_DynamicList::Handle::cast_dynamic(value_desc.get_value_node());
int i;
if(value_node->get_contained_type()==type_vector)
{
Bezier bezier;
etl::handle<Duck> first_duck, duck;
int first = -1;
for(i=0;i<value_node->link_count();i++)
{
if(!value_node->list[i].status_at_time(get_time()))
continue;
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i),canvas_view,transform_stack))
return false;
duck = last_duck();
// remember the index of the first vertex we didn't skip
if (first == -1)
{
first = i;
first_duck = duck;
}
if(param_desc && !param_desc->get_origin().empty())
{
synfigapp::ValueDesc value_desc_origin(value_desc.get_layer(),param_desc->get_origin());
add_to_ducks(value_desc_origin,canvas_view, transform_stack);
duck->set_origin(last_duck());
/*
ValueBase value(synfigapp::ValueDesc(value_desc.get_layer(),param_desc->get_origin()).get_value(get_time()));
if(value.same_type_as(synfig::Point()))
duck->set_origin(value.get(synfig::Point()));
*/
// if(!param_desc->get_origin().empty())
// last_duck()->set_origin(synfigapp::ValueDesc(value_desc.get_layer(),param_desc->get_origin()).get_value(get_time()).get(synfig::Point()));
}
duck->set_type(Duck::TYPE_VERTEX);
bezier.p1 = bezier.p2;
bezier.c1 = bezier.c2;
bezier.p2 = duck;
bezier.c2 = duck;
if (first != i)
{
handle<Bezier> bezier_(new Bezier());
bezier_->p1=bezier.p1;
bezier_->c1=bezier.c1;
bezier_->p2=bezier.p2;
bezier_->c2=bezier.c2;
add_bezier(bezier_);
last_bezier()->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,i)));
}
}
if (value_node->get_loop() && first != -1 && first_duck != duck)
{
duck = first_duck;
bezier.p1 = bezier.p2;
bezier.c1 = bezier.c2;
bezier.p2 = duck;
bezier.c2 = duck;
handle<Bezier> bezier_(new Bezier());
bezier_->p1 = bezier.p1;
bezier_->c1 = bezier.c1;
bezier_->p2 = bezier.p2;
bezier_->c2 = bezier.c2;
add_bezier(bezier_);
last_bezier()->signal_user_click(2).connect(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu_bezier),
synfigapp::ValueDesc(value_node,first)));
}
}
else if(value_node->get_contained_type()==type_segment)
{
for(i=0;i<value_node->link_count();i++)
{
if(!value_node->list[i].status_at_time(get_time()))
continue;
if(!add_to_ducks(synfigapp::ValueDesc(value_node,i),canvas_view,transform_stack))
return false;
}
}
else
return false;
}
else
{
// WRITEME
}
return true;
}
else
if (type == type_bone_object
|| type == type_bone_valuenode)
{
const synfigapp::ValueDesc &orig_value_desc = value_desc;
ValueNode::Handle value_node(value_desc.get_value_node());
if (type == type_bone_valuenode)
{
assert(value_desc.parent_is_value_node());
value_node = (*value_node)(get_time()).get(ValueNode_Bone::Handle());
}
else
assert(value_desc.parent_is_linkable_value_node() || value_desc.parent_is_canvas());
Duck::Handle fake_duck;
Duck::Handle tip_duck;
synfig::TransformStack origin_transform_stack(transform_stack), bone_transform_stack;
bool recursive(get_type_mask() & Duck::TYPE_BONE_RECURSIVE);
ValueNode_Bone::Handle bone_value_node;
if (!(bone_value_node = ValueNode_Bone::Handle::cast_dynamic(value_node)))
{
error("expected a ValueNode_Bone");
assert(0);
}
synfig::GUID guid(bone_value_node->get_guid());
Time time(get_time());
Bone bone((*bone_value_node)(time).get(Bone()));
bool invertible(true);
Angle angle;
Angle::deg parent_angle(0);
{
Matrix transform;
bool has_parent(!bone.is_root());
if (has_parent)
{
Bone parent_bone((*bone.get_parent())(time).get(Bone()));
// add the parent's ducks too
add_to_ducks(synfigapp::ValueDesc(bone_value_node, bone_value_node->get_link_index_from_name("parent"), value_desc),canvas_view,transform_stack);
transform = parent_bone.get_animated_matrix();
origin_transform_stack.push(new Transform_Matrix(guid, transform));
bone_transform_stack = origin_transform_stack;
invertible = transform.is_invertible();
Vector scale(parent_bone.get_local_scale());
bone_transform_stack.push(new Transform_Translate(guid, Point((scale[0])*bone.get_origin()[0],
(scale[1])*bone.get_origin()[1])));
origin_transform_stack.push(new Transform_Scale(guid, scale));
#ifdef TRY_TO_ALIGN_WIDTH_DUCKS
// this stuff doesn't work very well - we can find out
// the cumulative angle and so place the duck on the
// right of the circle, but recursive scales in the
// parent can cause the radius to look wrong, and the
// duck to appear off-center
printf("%s:%d bone %s:\n", __FILE__, __LINE__, bone.get_name().c_str());
while (true) {
printf("%s:%d parent_angle = %5.2f + %5.2f = %5.2f\n", __FILE__, __LINE__,
Angle::deg(parent_angle).get(), Angle::deg(parent_bone.get_angle()).get(),
Angle::deg(parent_angle + (parent_bone.get_angle())).get());
parent_angle += parent_bone.get_angle();
if (parent_bone.is_root()) break;
parent_bone = (*parent_bone.get_parent())(time).get(Bone());
}
printf("%s:%d finally %5.2f\n\n", __FILE__, __LINE__, Angle::deg(parent_angle).get());
#endif
}
else
{
bone_transform_stack = origin_transform_stack;
bone_transform_stack.push(new Transform_Translate(guid, bone.get_origin()));
}
}
bone_transform_stack.push(new Transform_Rotate(guid, bone.get_angle()));
// origin
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name("origin"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_POSITION);
set_duck_value_desc(*duck, value_desc, origin_transform_stack);
duck->set_point(value_desc.get_value(time).get(Point()));
// if the ValueNode can be directly manipulated, then set it as so
duck->set_editable(!invertible ? false :
!value_desc.is_value_node() ? true :
synfigapp::is_editable(value_desc.get_value_node()));
duck->signal_edited().clear();
duck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), value_desc));
duck->signal_user_click(2).connect(sigc::bind(sigc::bind(sigc::bind(sigc::mem_fun(*canvas_view,
&studio::CanvasView::popup_param_menu),
false), // bezier
0.0f), // location
value_desc)); // value_desc
add_duck(duck);
}
// fake
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name("name"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_NONE);
set_duck_value_desc(*duck, value_desc, bone_transform_stack);
duck->set_point(Point(0, 0));
duck->set_ignore(true);
add_duck(duck);
fake_duck = last_duck();
}
// angle
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name("angle"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_ANGLE);
set_duck_value_desc(*duck, value_desc, bone_transform_stack);
angle = value_desc.get_value(time).get(Angle());
Real length(bone.get_length() * (bone.get_scalex() * bone.get_scalelx()));
duck->set_point(Point(length*0.9, 0));
// if the ValueNode can be directly manipulated, then set it as so
duck->set_editable(!value_desc.is_value_node() ? true :
synfigapp::is_editable(value_desc.get_value_node()));
duck->signal_edited().clear();
duck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), value_desc));
duck->signal_user_click(2).connect(sigc::bind(sigc::bind(sigc::bind(sigc::mem_fun(*canvas_view,
&studio::CanvasView::popup_param_menu),
false), // bezier
0.0f), // location
value_desc)); // value_desc
duck->set_origin(fake_duck);
add_duck(duck);
}
// tip
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name(recursive ? "scalex" : "scalelx"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_VERTEX);
set_duck_value_desc(*duck, value_desc, bone_transform_stack);
//Real length = bone.get_length()*bone.get_scalex()*bone.get_scalelx();
Real length = value_desc.get_value(time).get(Real());
duck->set_point(Vector(length, 0.0));
// if the ValueNode can be directly manipulated, then set it as so
duck->set_editable(!invertible ? false :
!value_desc.is_value_node() ? true :
synfigapp::is_editable(value_desc.get_value_node()));
duck->signal_edited().clear();
duck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), value_desc));
duck->signal_user_click(2).connect(
sigc::bind(
sigc::bind(
sigc::bind(
sigc::mem_fun(
*canvas_view,
&studio::CanvasView::popup_param_menu
),
false // bezier
),
0.0f // location
),
value_desc
)
);
duck->set_origin(fake_duck);
add_duck(duck);
tip_duck = last_duck();
}
// origin width
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name("width"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_WIDTH);
set_duck_value_desc(*duck, value_desc, bone_transform_stack);
duck->set_radius(true);
duck->set_scalar(1);
duck->set_point(Point(0, value_desc.get_value(time).get(Real())));
// if the ValueNode can be directly manipulated, then set it as so
duck->set_editable(!invertible ? false :
!value_desc.is_value_node() ? true :
synfigapp::is_editable(value_desc.get_value_node()));
duck->signal_edited().clear();
duck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), value_desc));
duck->signal_user_click(2).connect(sigc::bind(sigc::bind(sigc::bind(sigc::mem_fun(*canvas_view,
&studio::CanvasView::popup_param_menu),
false), // bezier
0.0f), // location
value_desc)); // value_desc
duck->set_origin(fake_duck);
add_duck(duck);
}
// tip width
{
synfigapp::ValueDesc value_desc(bone_value_node, bone_value_node->get_link_index_from_name("tipwidth"), orig_value_desc);
etl::handle<Duck> duck=new Duck();
duck->set_type(Duck::TYPE_WIDTH);
set_duck_value_desc(*duck, value_desc, bone_transform_stack);
duck->set_radius(true);
duck->set_scalar(1);
duck->set_point(Point(0, value_desc.get_value(time).get(Real())));
// if the ValueNode can be directly manipulated, then set it as so
duck->set_editable(!invertible ? false :
!value_desc.is_value_node() ? true :
synfigapp::is_editable(value_desc.get_value_node()));
duck->signal_edited().clear();
duck->signal_edited().connect(sigc::bind(sigc::mem_fun(*this, &studio::Duckmatic::on_duck_changed), value_desc));
duck->signal_user_click(2).connect(sigc::bind(sigc::bind(sigc::bind(sigc::mem_fun(*canvas_view,
&studio::CanvasView::popup_param_menu),
false), // bezier
0.0f), // location
value_desc)); // value_desc
duck->set_origin(tip_duck);
add_duck(duck);
}
return true;
}
else
if (type == type_bone_weight_pair)
{
ValueNode_BoneWeightPair::Handle value_node;
if(value_desc.is_value_node() &&
(value_node=ValueNode_BoneWeightPair::Handle::cast_dynamic(value_desc.get_value_node())))
add_to_ducks(synfigapp::ValueDesc(value_node, value_node->get_link_index_from_name("bone"), value_desc), canvas_view, transform_stack);
}
return false;
}
/*
-- ** -- DuckDrag_Translate M E T H O D S----------------------------
*/
void
DuckDrag_Translate::begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& offset)
{
is_moving = false;
Duck::Handle duck = duckmatic->find_duck(offset);
assert(duck);
Duck::Handle origin_duck = duck;
while(origin_duck->get_move_origin() && origin_duck->get_origin_duck())
origin_duck = origin_duck->get_origin_duck();
last_translate_ = Vector(0, 0);
drag_offset_ = duck->get_trans_point();
snap_offset_ = origin_duck->get_trans_point() - drag_offset_;
const DuckList selected_ducks(duckmatic->get_selected_ducks());
DuckList::const_iterator iter;
positions.clear();
for(iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
{
Point p((*iter)->get_trans_point());
positions.push_back(p);
}
}
bool
DuckDrag_Translate::end_duck_drag(Duckmatic* duckmatic)
{
if(is_moving)
{
duckmatic->signal_edited_selected_ducks();
return true;
}
else
{
duckmatic->signal_user_click_selected_ducks(0);
return false;
}
}
void
DuckDrag_Translate::duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
{
const DuckList selected_ducks(duckmatic->get_selected_ducks());
DuckList::const_iterator iter;
synfig::Vector offset =
duckmatic->snap_point_to_grid(vector + snap_offset_)
- snap_offset_
- drag_offset_;
int i;
Time time(duckmatic->get_time());
// drag the vertex and position ducks first
for (i=0,iter=selected_ducks.begin(); iter!=selected_ducks.end(); ++iter,i++)
if((*iter)->get_type() == Duck::TYPE_VERTEX || (*iter)->get_type() == Duck::TYPE_POSITION)
(*iter)->set_trans_point(positions[i] + offset, time);
// then drag the others
for (i=0,iter=selected_ducks.begin(); iter!=selected_ducks.end(); ++iter,i++)
if ((*iter)->get_type() != Duck::TYPE_VERTEX && (*iter)->get_type() != Duck::TYPE_POSITION)
(*iter)->set_trans_point(positions[i] + offset, time);
last_translate_ = offset;
if(last_translate_.mag()>0.0001)
is_moving = true;
if (is_moving)
duckmatic->signal_edited_selected_ducks(true);
// then patch up the tangents for the vertices we've moved
duckmatic->update_ducks();
}
/*
-- ** -- BezierDrag_Default M E T H O D S----------------------------
*/
void
BezierDrag_Default::begin_bezier_drag(Duckmatic* duckmatic, const synfig::Vector& offset, float bezier_click_pos)
{
is_moving = false;
drag_offset_=offset;
click_pos_=bezier_click_pos;
etl::handle<Duck> c1(duckmatic->get_selected_bezier()->c1);
etl::handle<Duck> c2(duckmatic->get_selected_bezier()->c2);
c1_initial = c1->get_trans_point();
c2_initial = c2->get_trans_point();
last_translate_ = synfig::Vector(0,0);
if (c1 == duckmatic->get_selected_bezier()->p1
&& c2 == duckmatic->get_selected_bezier()->p2)
{
// This is a polygon segment
// We can't bend the curve, so drag it instead
c1_ratio = 1.0;
c2_ratio = 1.0;
}
else
{
// This is a bline segment, so we can bend the curve
// Magic Bezier Drag Equations follow! (stolen from Inkscape)
// "weight" describes how the influence of the drag should be
// distributed among the handles;
// 0 = front handle only, 1 = back handle only.
float t = bezier_click_pos;
float weight;
if (t <= 1.0/6.0 ) weight=0;
else if (t <= 0.5 ) weight = (pow((6 * t - 1) / 2.0, 3)) / 2;
else if (t <= 5.0 / 6.0) weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
else weight = 1;
c1_ratio = (1-weight)/(3*t*(1-t)*(1-t));
c2_ratio = weight/(3*t*t*(1-t));
}
}
void
BezierDrag_Default::bezier_drag(Duckmatic* duckmatic, const synfig::Vector& vector)
{
synfig::Vector vect(duckmatic->snap_point_to_grid(vector)-drag_offset_);
Time time(duckmatic->get_time());
synfig::Vector c1_offset(vect[0]*c1_ratio, vect[1]*c1_ratio);
synfig::Vector c2_offset(vect[0]*c2_ratio, vect[1]*c2_ratio);
etl::handle<Duck> c1(duckmatic->get_selected_bezier()->c1);
etl::handle<Duck> c2(duckmatic->get_selected_bezier()->c2);
c1->set_trans_point(c1_initial+c1_offset, time);
c2->set_trans_point(c2_initial+c2_offset, time);
last_translate_=vect;
if(last_translate_.mag()>0.0001)
is_moving = true;
if (is_moving) {
duckmatic->signal_edited_duck(c1, true);
duckmatic->signal_edited_duck(c2, true);
}
duckmatic->update_ducks();
}
bool
BezierDrag_Default::end_bezier_drag(Duckmatic* duckmatic)
{
if(is_moving)
{
etl::handle<Duck> c1(duckmatic->get_selected_bezier()->c1);
etl::handle<Duck> c2(duckmatic->get_selected_bezier()->c2);
duckmatic->signal_edited_duck(c1);
duckmatic->signal_edited_duck(c2);
return true;
}
else
{
return false;
}
}
/*
-- ** -- Duckmatic::Push M E T H O D S----------------------------
*/
Duckmatic::Push::Push(Duckmatic *duckmatic_):
duckmatic_(duckmatic_)
{
duck_map=duckmatic_->duck_map;
bezier_list_=duckmatic_->bezier_list_;
duck_data_share_map=duckmatic_->duck_data_share_map;
stroke_list_=duckmatic_->stroke_list_;
duck_dragger_=duckmatic_->duck_dragger_;
needs_restore=true;
}
Duckmatic::Push::~Push()
{
if(needs_restore)
restore();
}
void
Duckmatic::Push::restore()
{
duckmatic_->duck_map=duck_map;
duckmatic_->bezier_list_=bezier_list_;
duckmatic_->duck_data_share_map=duck_data_share_map;
duckmatic_->stroke_list_=stroke_list_;
duckmatic_->duck_dragger_=duck_dragger_;
needs_restore=false;
}
/*
-- ** -- inline M E T H O D S----------------------------
*/
inline synfig::GUID calc_duck_guid(const synfigapp::ValueDesc& value_desc, const synfig::TransformStack& transform_stack)
{
return value_desc.get_guid() % transform_stack.get_guid();
}
//! sets duck name, value_desc, transform_stack and GUID
inline void set_duck_value_desc(Duck& duck, const synfigapp::ValueDesc& value_desc, const synfig::TransformStack& transform_stack)
{
duck.set_name(value_desc.get_guid_string());
duck.set_value_desc(value_desc);
duck.set_transform_stack(transform_stack);
duck.set_guid(calc_duck_guid(value_desc, transform_stack));
}
//! sets duck name, value_desc and GUID
inline void set_duck_value_desc(Duck& duck, const synfigapp::ValueDesc& value_desc, const synfig::String& sub_name, const synfig::TransformStack& transform_stack)
{
set_duck_value_desc(duck, value_desc.get_sub_value(sub_name), transform_stack);
}