Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / synfigapp / timegather.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file timegather.cpp
3 **      \brief Time Gather File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2004 Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "timegather.h"
33 #include "value_desc.h"
34
35 #include <synfig/layer_pastecanvas.h>
36
37 #endif
38
39 /* === U S I N G =========================================================== */
40
41 using namespace std;
42 using namespace etl;
43 using namespace synfig;
44 using namespace synfigapp;
45
46 /* === M A C R O S ========================================================= */
47
48 /* === G L O B A L S ======================================================= */
49
50 /* === P R O C E D U R E S ================================================= */
51
52 /* === M E T H O D S ======================================================= */
53
54 /* === E N T R Y P O I N T ================================================= */
55
56 //! Definitions for build a list of accurate valuenode references
57
58 void synfigapp::timepoints_ref::insert(synfig::ValueNode_Animated::Handle v, synfig::Waypoint w)
59 {
60         ValueBaseTimeInfo       vt;
61         vt.val = v;
62
63         waytracker::iterator i = waypointbiglist.find(vt);
64
65         if(i != waypointbiglist.end())
66         {
67                 i->waypoints.insert(w);
68         }else
69         {
70                 vt.waypoints.insert(w);
71                 waypointbiglist.insert(vt);
72         }
73 }
74
75 void synfigapp::timepoints_ref::insert(synfigapp::ValueDesc v, synfig::Activepoint a)
76 {
77         ActiveTimeInfo  vt;
78         vt.val = v;
79
80         acttracker::iterator i = actpointbiglist.find(vt);
81
82         if(i != actpointbiglist.end())
83         {
84                 i->activepoints.insert(a);
85                 /*{ //if it fails...
86                         synfig::info("!!!!For some reason it wasn't able to insert the activepoint in the list (%s,%.4lg)",
87                                                         a.state?"true":"false", (double)a.time);
88                 }*/
89         }else
90         {
91                 vt.activepoints.insert(a);
92                 actpointbiglist.insert(vt);
93                 //synfig::info("Insert new activept list for valdesc");
94         }
95 }
96
97 //recursion functions
98 void synfigapp::recurse_canvas(synfig::Canvas::Handle h, const std::set<Time> &tlist,
99                                                                 timepoints_ref &vals)
100 {
101
102         //synfig::info("Canvas...\n Recurse through layers");
103         // iterate through the layers
104
105         synfig::Canvas::iterator i = h->begin(), end = h->end();
106
107         for(; i != end; ++i)
108         {
109                 const Node::time_set &tset = (*i)->get_times();
110                 if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end()))
111                 {
112                         recurse_layer(*i,tlist,vals);
113                 }
114         }
115 }
116
117 void synfigapp::recurse_layer(synfig::Layer::Handle h, const std::set<Time> &tlist,
118                                                                 timepoints_ref &vals)
119 {
120         // iterate through the layers
121         //check for special case of paste canvas
122         etl::handle<synfig::Layer_PasteCanvas> p = etl::handle<synfig::Layer_PasteCanvas>::cast_dynamic(h);
123
124         //synfig::info("Layer...");
125
126         if(p)
127         {
128                 //synfig::info("We are a paste canvas so go into that");
129                 //recurse into the canvas
130                 const synfig::Node::time_set &tset = p->get_sub_canvas()->get_times();
131
132                 if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end()))
133                 {
134                         //we have to offset the times so it won't wreck havoc if the canvas is imported more than once...
135                         // and so we get correct results when offsets are present
136                         std::set<Time>  tlistoff;
137                         std::set<Time>::iterator i = tlist.begin(), end = tlist.end();
138                         for(; i != end; ++i)
139                         {
140                                 tlistoff.insert(*i - p->get_time_offset());
141                         }
142
143                         recurse_canvas(p->get_sub_canvas(),tlist,vals);
144                 }
145         }
146
147         //check all the valuenodes regardless...
148         //synfig::info("Recurse all valuenodes");
149         synfig::Layer::DynamicParamList::const_iterator         i = h->dynamic_param_list().begin(),
150                                                                                                         end = h->dynamic_param_list().end();
151         for(; i != end; ++i)
152         {
153                 const synfig::Node::time_set &tset = i->second->get_times();
154
155                 if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end()))
156                 {
157                         recurse_valuedesc(ValueDesc(h,i->first),tlist,vals);
158                 }
159         }
160 }
161
162 template < typename IT, typename CMP >
163 static bool sorted(IT i,IT end, const CMP &cmp = CMP())
164 {
165         if(i == end) return true;
166
167         for(IT last = i++; i != end; last = i++)
168         {
169                 if(!cmp(*last,*i))
170                         return false;
171         }
172
173         return true;
174 }
175
176 void synfigapp::recurse_valuedesc(synfigapp::ValueDesc h, const std::set<Time> &tlist,
177                                                                 timepoints_ref &vals)
178 {
179         //special cases for Animated, DynamicList, and Linkable
180
181         //synfig::info("ValueBasenode... %p, %s", h.get_value_node().get(),typeid(*h.get_value_node()).name());
182
183
184         //animated case
185         {
186                 synfig::ValueNode_Animated::Handle p = synfig::ValueNode_Animated::Handle::cast_dynamic(h.get_value_node());
187
188                 if(p)
189                 {
190                         //loop through and determine which waypoint we will need to reference
191                         const synfig::WaypointList &w = p->waypoint_list();
192
193                         synfig::WaypointList::const_iterator i = w.begin(),
194                                                                                                 end = w.end();
195
196                         std::set<Time>::const_iterator          j = tlist.begin(),
197                                                                                                 jend = tlist.end();
198                         for(; i != end && j != jend;)
199                         {
200                                 //synfig::info("tpair t(%.3f) = %.3f", (float)*j, (float)(i->get_time()));
201
202                                 if(j->is_equal(i->get_time()))
203                                 {
204                                         vals.insert(p,*i);
205                                         ++i,++j;
206                                 }else if(*i < *j)
207                                 {
208                                         ++i;
209                                 }else ++j;
210                         }
211                         return;
212                 }
213         }
214
215         //parent dynamiclist case - just for active points for that object...
216         if(h.parent_is_value_node())
217         {
218                 synfig::ValueNode_DynamicList::Handle p = synfig::ValueNode_DynamicList::Handle::cast_dynamic(h.get_parent_value_node());
219
220                 if(p)
221                 {
222                         int index = h.get_index();
223
224                         //check all the active points in each list...
225                         const synfig::ActivepointList &a = p->list[index].timing_info;
226
227                         //synfig::info("Our parent = dynamic list, searching in %d activepts",a.size());
228
229                         std::set<Time>::const_iterator                  i = tlist.begin(),
230                                                                                                         end = tlist.end();
231
232                         synfig::ActivepointList::const_iterator         j = a.begin(),
233                                                                                                         jend = a.end();
234
235                         for(; j != jend && i != end;)
236                         {
237                                 double it = *i;
238                                 double jt = j->get_time();
239                                 double diff = (double)(it - jt);
240
241                                 //synfig::info("\ttpair match(%.4lg) - %.4lg (diff = %lg",it,jt,diff);
242
243                                 //
244                                 if(abs(diff) < (double)Time::epsilon())
245                                 {
246                                         //synfig::info("\tActivepoint to add being referenced (%x,%s,%.4lg)",
247                                         //                              (int)j->get_uid(),j->state?"true":"false", (double)j->time);
248                                         vals.insert(ValueDesc(p,index),*j);
249                                         ++i,++j;
250                                 }else if(it < jt)
251                                 {
252                                         ++i;
253                                         //synfig::info("\tIncrementing time");
254                                 }
255                                 else
256                                 {
257                                         ++j;
258                                         //synfig::info("\tIncrementing actpt");
259                                 }
260                         }
261                 }
262         }
263
264         //dynamiclist case - we must still make sure that we read from the list entries the time values
265         //                                              because just the linked valuenodes will not do that
266         {
267                 synfig::ValueNode_DynamicList::Handle p = synfig::ValueNode_DynamicList::Handle::cast_dynamic(h.get_value_node());
268
269                 if(p)
270                 {
271                         //synfig::info("Process dynamic list valuenode");
272                         int index = 0;
273
274                         std::vector<synfig::ValueNode_DynamicList::ListEntry>::const_iterator
275                                                         i = p->list.begin(),
276                                                         end = p->list.end();
277
278                         for(; i != end; ++i, ++index)
279                         {
280                                 const Node::time_set &tset = i->get_times();
281
282                                 if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end()))
283                                 {
284                                         recurse_valuedesc(ValueDesc(p,index),tlist,vals);
285                                 }
286                         }
287                         return;
288                 }
289         }
290
291         //the linkable case...
292         {
293                 etl::handle<synfig::LinkableValueNode> p = etl::handle<synfig::LinkableValueNode>::cast_dynamic(h.get_value_node());
294
295                 if(p)
296                 {
297                         //synfig::info("Process Linkable ValueBasenode");
298                         int i = 0, size = p->link_count();
299
300                         for(; i < size; ++i)
301                         {
302                                 ValueNode::Handle v = p->get_link(i);
303                                 const Node::time_set &tset = v->get_times();
304
305                                 if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end()))
306                                 {
307                                         recurse_valuedesc(ValueDesc(p,i),tlist,vals);
308                                 }
309                         }
310                 }
311         }
312 }