Merge branch 'nikitakit_smoothbline' into nikitakit_ducks
[synfig.git] / synfig-studio / src / gtkmm / duck.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file duck.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "duck.h"
34 #include <ETL/misc>
35
36 #include <synfig/valuenode_bline.h>
37 #include <synfig/valuenode_blinecalctangent.h>
38 #include <synfig/valuenode_blinecalcvertex.h>
39 #include <synfig/valuenode_blinecalcwidth.h>
40 #include <synfig/valuenode_composite.h>
41
42 #include "general.h"
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50 using namespace studio;
51
52 /* === M A C R O S ========================================================= */
53
54 /* === G L O B A L S ======================================================= */
55
56 int studio::Duck::duck_count(0);
57
58 struct _DuckCounter
59 {
60         static int counter;
61         ~_DuckCounter()
62         {
63                 if(counter)
64                         synfig::error("%d ducks not yet deleted!",counter);
65         }
66 } _duck_counter;
67
68 int _DuckCounter::counter(0);
69
70
71 /* === P R O C E D U R E S ================================================= */
72
73 /* === M E T H O D S ======================================================= */
74
75 Duck::Duck():
76         rotations(synfig::Angle::deg(0)),
77         origin(0,0),
78         scalar(1),
79         editable(false),
80         radius_(false),
81         tangent_(false),
82         hover_(false),
83         ignore_(false)
84 { duck_count++; _DuckCounter::counter++; }
85
86 Duck::Duck(const synfig::Point &point):
87         type_(TYPE_NONE),
88         point(point),
89         rotations(synfig::Angle::deg(0)),
90         origin(0,0),
91         scalar(1),
92         guid_(0),
93         editable(false),
94         radius_(false),
95         tangent_(false),
96         hover_(false),
97         ignore_(false)
98 { duck_count++; _DuckCounter::counter++;}
99
100 Duck::Duck(const synfig::Point &point,const synfig::Point &origin):
101         point(point),
102         rotations(synfig::Angle::deg(0)),
103         origin(origin),
104         scalar(1),
105         guid_(0),
106         editable(false),
107         radius_(true),
108         tangent_(false),
109         hover_(false),
110         ignore_(false)
111 { duck_count++; _DuckCounter::counter++;}
112
113 Duck::~Duck() { duck_count--; _DuckCounter::counter--;}
114
115 synfig::GUID
116 Duck::get_data_guid()const
117 {
118         if(value_desc_.is_value_node())
119                 return value_desc_.get_value_node()->get_guid();
120         return synfig::GUID::hasher(get_name());
121 }
122
123 void
124 Duck::set_name(const synfig::String &x)
125 {
126         name=x;
127         if(guid_==synfig::GUID::zero())
128         {
129                 guid_=synfig::GUID::hasher(name);
130         }
131 }
132
133
134 bool
135 Duck::operator==(const Duck &rhs)const
136 {
137         if(this==&rhs)
138                 return true;
139         return
140                 name==rhs.name &&
141                 scalar==rhs.scalar &&
142                 type_==rhs.type_ &&
143                 transform_stack_.size()==rhs.transform_stack_.size();
144                 //true;
145                 //(origin_duck?*origin_duck==*rhs.origin_duck:origin==rhs.origin) &&
146                 //(shared_point?*shared_point==*rhs.shared_point:point==rhs.point) ;
147 }
148
149 synfig::Point
150 Duck::get_trans_point()const
151 {
152         return transform_stack_.perform(get_sub_trans_point());
153 }
154
155 void
156 Duck::set_trans_point(const synfig::Point &x)
157 {
158         set_sub_trans_point(transform_stack_.unperform(x));
159 }
160
161 void
162 Duck::set_trans_point(const synfig::Point &x, const synfig::Time &time)
163 {
164         set_sub_trans_point(transform_stack_.unperform(x), time);
165 }
166
167 //! Sets the origin point.
168 void
169 Duck::set_origin(const synfig::Point &x)
170 {
171         origin=x; origin_duck=0;
172 }
173
174 //! Sets the origin point as another duck
175 void
176 Duck::set_origin(const etl::handle<Duck> &x)
177 {
178         origin_duck=x;
179 }
180
181 //! Retrieves the origin location
182 synfig::Point
183 Duck::get_origin()const
184 {
185         return origin_duck?origin_duck->get_point():origin;
186 }
187
188 //! Retrieves the origin duck
189 const etl::handle<Duck> &
190 Duck::get_origin_duck() const
191 {
192         return origin_duck;
193 }
194
195 //! Retrieves the origin location
196 synfig::Point
197 Duck::get_trans_origin()const
198 {
199         return transform_stack_.perform(get_sub_trans_origin());
200 }
201
202 synfig::Point
203 Duck::get_sub_trans_point()const
204 {
205         return get_point()*get_scalar()+get_sub_trans_origin();
206 }
207
208 void
209 Duck::set_sub_trans_point(const synfig::Point &x, const synfig::Time &time)
210 {
211         set_point((x-get_sub_trans_origin())/get_scalar());
212         if (get_type() == Duck::TYPE_TANGENT ||
213                 get_type() == Duck::TYPE_ANGLE)
214         {
215                 Angle old_angle = get_point().angle();
216                 Angle change = get_point().angle() - old_angle;
217                 while (change < Angle::deg(-180)) change += Angle::deg(360);
218                 while (change > Angle::deg(180)) change -= Angle::deg(360);
219                 int old_halves = round_to_int(Angle::deg(rotations).get()/180);
220                 rotations += change;
221                 int new_halves = round_to_int(Angle::deg(rotations).get()/180);
222                 if (old_halves != new_halves &&
223                         (new_halves > 1 || new_halves < -1 ||
224                          old_halves > 1 || old_halves < -1))
225                         synfig::info("rotation: %.2f turns", new_halves/2.0);
226         } else if(get_type() == Duck::TYPE_VERTEX || get_type() == Duck::TYPE_POSITION)
227         {
228
229                 ValueNode_BLineCalcVertex::Handle bline_vertex;
230                 ValueNode_Composite::Handle composite;
231
232                 if ((bline_vertex = ValueNode_BLineCalcVertex::Handle::cast_dynamic(get_value_desc().get_value_node())) ||
233                         ((composite = ValueNode_Composite::Handle::cast_dynamic(get_value_desc().get_value_node())) &&
234                          composite->get_type() == ValueBase::TYPE_BLINEPOINT &&
235                          (bline_vertex = ValueNode_BLineCalcVertex::Handle::cast_dynamic(composite->get_link("point")))))
236                 {
237                         synfig::Point closest_point = get_point();
238                         synfig::Real radius = 0.0;
239                         ValueNode_BLine::Handle bline = ValueNode_BLine::Handle::cast_dynamic(bline_vertex->get_link(bline_vertex->get_link_index_from_name("bline")));
240                         synfig::find_closest_point(
241                                 (*bline)(time),
242                                 get_point(),
243                                 radius,
244                                 bline->get_loop(),
245                                 &closest_point);
246                         set_point(closest_point);
247                 }
248         }
249 }
250
251 //! Updates width and tangent ducks that change when the origin moves
252 void
253 Duck::update(const synfig::Time &time)
254 {
255         if((get_type() == Duck::TYPE_TANGENT || get_type() == Duck::TYPE_WIDTH) && origin_duck)
256         {
257                 ValueNode_BLineCalcVertex::Handle bline_vertex;
258                 ValueNode_Composite::Handle composite;
259                 if ((bline_vertex = ValueNode_BLineCalcVertex::Handle::cast_dynamic(origin_duck->get_value_desc().get_value_node())) ||
260                         ((composite = ValueNode_Composite::Handle::cast_dynamic(origin_duck->get_value_desc().get_value_node())) &&
261                          composite->get_type() == ValueBase::TYPE_BLINEPOINT &&
262                          (bline_vertex = ValueNode_BLineCalcVertex::Handle::cast_dynamic(composite->get_link("point")))))
263                 {
264                         synfig::Real radius = 0.0;
265                         ValueNode_BLine::Handle bline(ValueNode_BLine::Handle::cast_dynamic(bline_vertex->get_link(bline_vertex->get_link_index_from_name("bline"))));
266                         Real amount = synfig::find_closest_point((*bline)(time), origin_duck->get_point(), radius, bline->get_loop());
267
268                         int vertex_amount_index(bline_vertex->get_link_index_from_name("amount"));
269                         ValueNode::Handle vertex_amount_value_node(bline_vertex->get_link(vertex_amount_index));
270
271
272                         if (ValueNode_BLineCalcTangent::Handle bline_tangent = ValueNode_BLineCalcTangent::Handle::cast_dynamic(get_value_desc().get_value_node()))
273                         {
274                                 switch (bline_tangent->get_type())
275                                 {
276                                 case ValueBase::TYPE_ANGLE:
277                                 {
278                                         Angle angle((*bline_tangent)(time, amount).get(Angle()));
279                                         set_point(Point(Angle::cos(angle).get(), Angle::sin(angle).get()));
280                                         break;
281                                 }
282                                 case ValueBase::TYPE_REAL:
283                                         set_point(Point((*bline_tangent)(time, amount).get(Real()), 0));
284                                         break;
285                                 case ValueBase::TYPE_VECTOR:
286                                         set_point((*bline_tangent)(time, amount).get(Vector()));
287                                         break;
288                                 default:
289                                         break;
290                                 }
291                         }
292                         else if (ValueNode_BLineCalcWidth::Handle bline_width = ValueNode_BLineCalcWidth::Handle::cast_dynamic(get_value_desc().get_value_node()))
293                                         set_point(Point((*bline_width)(time, amount).get(Real()), 0));
294                 }
295
296         }
297 }
298
299
300 void
301 Duck::set_sub_trans_point(const synfig::Point &x)
302 {
303         set_point((x-get_sub_trans_origin())/get_scalar());
304         if (get_type() == Duck::TYPE_TANGENT ||
305                 get_type() == Duck::TYPE_ANGLE)
306         {
307                 Angle old_angle = get_point().angle();
308                 Angle change = get_point().angle() - old_angle;
309                 while (change < Angle::deg(-180)) change += Angle::deg(360);
310                 while (change > Angle::deg(180)) change -= Angle::deg(360);
311                 int old_halves = round_to_int(Angle::deg(rotations).get()/180);
312                 rotations += change;
313                 int new_halves = round_to_int(Angle::deg(rotations).get()/180);
314                 if (old_halves != new_halves &&
315                         (new_halves > 1 || new_halves < -1 ||
316                          old_halves > 1 || old_halves < -1))
317                         synfig::info("rotation: %.2f turns", new_halves/2.0);
318         }
319 }
320
321 synfig::Point
322 Duck::get_sub_trans_origin()const
323 {
324         return origin_duck?origin_duck->get_sub_trans_point():origin;
325 }
326
327 #ifdef _DEBUG
328 synfig::String
329 Duck::type_name(Type id)
330 {
331         String ret;
332
333         if (id & TYPE_POSITION) { if (!ret.empty()) ret += ", "; ret += "position"; }
334         if (id & TYPE_TANGENT ) { if (!ret.empty()) ret += ", "; ret += "tangent" ; }
335         if (id & TYPE_RADIUS  ) { if (!ret.empty()) ret += ", "; ret += "radius"  ; }
336         if (id & TYPE_WIDTH       ) { if (!ret.empty()) ret += ", "; ret += "width"   ; }
337         if (id & TYPE_ANGLE       ) { if (!ret.empty()) ret += ", "; ret += "angle"   ; }
338         if (id & TYPE_VERTEX  ) { if (!ret.empty()) ret += ", "; ret += "vertex"  ; }
339
340         if (ret.empty())
341                 ret = "none";
342
343         return ret;
344 }
345 #endif  // _DEBUG