1 /* === S Y N F I G ========================================================= */
2 /*! \file valuenode_dynamiclist.cpp
3 ** \brief Implementation of the "Dynamic List" valuenode conversion.
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
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.
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.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
32 #include "valuenode_dynamiclist.h"
33 #include "valuenode_const.h"
34 #include "valuenode_composite.h"
36 #include "exception.h"
44 /* === U S I N G =========================================================== */
48 using namespace synfig;
50 /* === M A C R O S ========================================================= */
52 /* === G L O B A L S ======================================================= */
54 /* === P R O C E D U R E S ================================================= */
56 /* === M E T H O D S ======================================================= */
58 ValueNode_DynamicList::ListEntry::ListEntry():
63 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node):
64 value_node(value_node),
69 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node,Time begin, Time end):
70 value_node(value_node)
74 add((begin+end)*0.5,true);
77 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
78 ValueNode_DynamicList::ListEntry::add(Time time, bool status, int priority)
80 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
83 Activepoint ap(time,status,priority);
84 ap.set_parent_index(get_index());
85 ap.set_parent_value_node(get_parent_value_node());
86 timing_info.push_back(ap);
87 iterator iter(--iterator(timing_info.end()));
93 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
94 ValueNode_DynamicList::ListEntry::add(const Activepoint &x)
96 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
100 ap.set_parent_index(get_index());
101 ap.set_parent_value_node(get_parent_value_node());
102 timing_info.push_back(ap);
103 iterator iter(--iterator(timing_info.end()));
110 ValueNode_DynamicList::reindex()
114 std::vector<ListEntry>::iterator iter;
116 for(iter=list.begin();iter!=list.end();++iter)
118 assert(iter->value_node);
119 if(iter->index!=i || iter->get_parent_value_node().get()!=this)
121 ActivepointList::iterator iter2;
123 if(iter->timing_info.size()) // is this line really necessary?
124 for(iter2=iter->timing_info.begin();iter2!=iter->timing_info.end();++iter2)
126 iter2->set_parent_index(i);
127 iter2->set_parent_value_node(this);
130 iter->set_parent_value_node(this);
135 ValueNode_DynamicList::ListEntry
136 ValueNode_DynamicList::create_list_entry(int index, Time time, Real origin)
138 ValueNode_DynamicList::ListEntry ret;
141 synfig::ValueBase prev,next;
143 index=index%link_count();
148 ret.set_parent_value_node(this);
150 next=(*list[index].value_node)(time);
153 prev=(*list[index-1].value_node)(time);
157 prev=(*list[link_count()-1].value_node)(time);
165 switch(get_contained_type())
167 case ValueBase::TYPE_VECTOR:
169 Vector a(prev.get(Vector())), b(next.get(Vector()));
170 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
173 case ValueBase::TYPE_REAL:
175 Real a(prev.get(Real())), b(next.get(Real()));
176 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
179 case ValueBase::TYPE_COLOR:
181 Color a(prev.get(Color())), b(next.get(Color()));
182 ret.value_node=ValueNode_Composite::create((b-a)*origin+a);
185 case ValueBase::TYPE_ANGLE:
187 Angle a(prev.get(Angle())), b(next.get(Angle()));
188 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
191 case ValueBase::TYPE_TIME:
193 Time a(prev.get(Time())), b(next.get(Time()));
194 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
198 ret.value_node=ValueNode_Const::create(get_contained_type());
206 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
207 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)
209 return std::find(timing_info.begin(),timing_info.end(),x);
212 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
213 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)const
215 return std::find(timing_info.begin(),timing_info.end(),x);
219 ValueNode_DynamicList::ListEntry::erase(const UniqueID& x)
221 timing_info.erase(find(x));
224 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
225 ValueNode_DynamicList::ListEntry::find(const Time& x)
227 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
229 ActivepointList::iterator iter;
231 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
235 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find():"+x.get_string());
238 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
239 ValueNode_DynamicList::ListEntry::find(const Time& x)const
241 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
243 ActivepointList::const_iterator iter;
245 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
249 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find()const:"+x.get_string());
252 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
253 ValueNode_DynamicList::ListEntry::find_next(const Time& x)
255 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
257 ActivepointList::iterator iter;
259 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
263 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next():"+x.get_string());
266 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
267 ValueNode_DynamicList::ListEntry::find_next(const Time& x)const
269 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
271 ActivepointList::const_iterator iter;
273 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
277 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next()const:"+x.get_string());
280 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
281 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)
283 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
285 ActivepointList::iterator iter;
286 iter=timing_info.end();
293 while(iter!=timing_info.begin());
295 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev():"+x.get_string());
298 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
299 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)const
301 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
303 ActivepointList::const_iterator iter;
304 iter=timing_info.end();
311 while(iter!=timing_info.begin());
313 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev()const:"+x.get_string());
317 ValueNode_DynamicList::ListEntry::find(const Time& begin,const Time& end,std::vector<Activepoint*>& selected)
319 Time curr_time(begin);
322 // try to grab first waypoint
325 ActivepointList::iterator iter;
326 iter=find(curr_time);
327 selected.push_back(&*iter);
334 ActivepointList::iterator iter;
337 iter=find_next(curr_time);
338 curr_time=iter->get_time();
341 selected.push_back(&*iter);
351 ValueNode_DynamicList::ListEntry::amount_at_time(const Time &t,bool *rising)const
353 typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
354 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
356 if(timing_info.empty())
361 ActivepointList::const_iterator iter;
363 return iter->state?1.0f:0.0f;
367 ActivepointList::const_iterator prev_iter;
368 ActivepointList::const_iterator next_iter;
370 try { prev_iter=find_prev(t); }
371 catch(...) { return find_next(t)->state?1.0f:0.0f; }
373 try { next_iter=find_next(t); }
374 catch(...) { return prev_iter->state?1.0f:0.0f; }
376 if(next_iter->state==prev_iter->state)
377 return next_iter->state?1.0f:0.0f;
379 if(rising)*rising=next_iter->state;
381 if(next_iter->state==true)
382 return float((t-prev_iter->time)/(next_iter->time-prev_iter->time));
384 return float((next_iter->time-t)/(next_iter->time-prev_iter->time));
388 ValueNode_DynamicList::ListEntry::new_activepoint_at_time(const Time& time)const
390 Activepoint activepoint;
392 activepoint.set_state(status_at_time(time));
393 activepoint.set_priority(0);
399 ValueNode_DynamicList::ListEntry::status_at_time(const Time &t)const
401 typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
402 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
404 ActivepointList::const_iterator entry_iter;
405 ActivepointList::const_iterator prev_iter;
408 // New "symmetric" state mechanism
409 if(!timing_info.empty())
411 if(timing_info.size()==1)
412 state=timing_info.front().state;
415 //! \optimize Perhaps we should use a binary search...?
416 // This will give us the first activepoint that is after t.
417 for(entry_iter=timing_info.begin();entry_iter!=timing_info.end();++entry_iter)
419 if(entry_iter->time==t)
421 // If we hit the entry right on the nose, then we don't
422 // have to do anything more
423 return entry_iter->state;
425 if(entry_iter->time>t)
429 prev_iter=entry_iter;
434 // |-------|---t---|-------|
435 // prev_iter^ ^entry_iter
437 if(entry_iter==timing_info.end())
439 state=prev_iter->state;
442 if(entry_iter==timing_info.begin())
444 state=entry_iter->state;
447 if(entry_iter->priority==prev_iter->priority)
449 state=entry_iter->state || prev_iter->state;
452 if(entry_iter->priority>prev_iter->priority)
454 state=entry_iter->state;
458 state=prev_iter->state;
469 ValueNode_DynamicList::add(const ValueNode::Handle &value_node, int index)
471 ListEntry list_entry(value_node);
472 list_entry.timing_info.size();
474 if(index<0 || index>=(int)list.size())
476 list.push_back(list_entry);
480 list.insert(list.begin()+index,list_entry);
483 add_child(value_node.get());
487 if(get_parent_canvas())
488 get_parent_canvas()->signal_value_node_child_added()(this,value_node);
489 else if(get_root_canvas() && get_parent_canvas())
490 get_root_canvas()->signal_value_node_child_added()(this,value_node);
494 ValueNode_DynamicList::add(const ListEntry &list_entry, int index)
496 if(index<0 || index>=(int)list.size())
497 list.push_back(list_entry);
499 list.insert(list.begin()+index,list_entry);
500 add_child(list_entry.value_node.get());
505 if(get_parent_canvas())
506 get_parent_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
507 else if(get_root_canvas() && get_parent_canvas())
508 get_root_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
512 ValueNode_DynamicList::erase(const ValueNode::Handle &value_node_)
514 ValueNode::Handle value_node(value_node_);
518 throw String("ValueNode_DynamicList::erase(): Passed bad value node");
520 std::vector<ListEntry>::iterator iter;
521 for(iter=list.begin();iter!=list.end();++iter)
522 if(iter->value_node==value_node)
527 remove_child(value_node.get());
528 // changed to fix bug 1420091 - it seems that when a .sif file containing a bline layer encapsulated inside
529 // another layer, get_parent_canvas() is false and get_root_canvas() is true, but when we come to erase a
530 // vertex, both are true. So the signal is sent to the parent, but the signal wasn't sent to the parent
531 // when it was added. This probably isn't the right fix, but it seems to work for now. Note that the same
532 // strange "if (X) else if (Y && X)" code is also present in the two previous functions, above.
534 // if(get_parent_canvas())
535 // get_parent_canvas()->signal_value_node_child_removed()(this,value_node);
536 // else if(get_root_canvas() && get_parent_canvas())
537 // get_root_canvas()->signal_value_node_child_removed()(this,value_node);
538 if(get_root_canvas())
539 get_root_canvas()->signal_value_node_child_removed()(this,value_node);
547 ValueNode_DynamicList::ValueNode_DynamicList(ValueBase::Type container_type):
548 LinkableValueNode(ValueBase::TYPE_LIST),
549 container_type (container_type),
555 ValueNode_DynamicList::Handle
556 ValueNode_DynamicList::create(ValueBase::Type id)
558 return new ValueNode_DynamicList(id);
561 ValueNode_DynamicList::~ValueNode_DynamicList()
566 ValueNode_DynamicList*
567 ValueNode_DynamicList::create_from(const ValueBase &value)
569 //vector<ValueBase> value_list(value.operator vector<ValueBase>());
570 vector<ValueBase> value_list(value.get_list());
572 vector<ValueBase>::iterator iter;
574 if(value_list.empty())
577 ValueNode_DynamicList* value_node(new ValueNode_DynamicList(value_list.front().get_type()));
579 for(iter=value_list.begin();iter!=value_list.end();++iter)
581 ValueNode::Handle item(ValueNode_Const::create(*iter));
582 value_node->add(ListEntry(item));
583 assert(value_node->list.back().value_node);
589 ValueNode_DynamicList::operator()(Time t)const
591 std::vector<ValueBase> ret_list;
592 std::vector<ListEntry>::const_iterator iter;
594 assert(container_type);
596 for(iter=list.begin();iter!=list.end();++iter)
598 bool state(iter->status_at_time(t));
602 if(iter->value_node->get_type()==container_type)
603 ret_list.push_back((*iter->value_node)(t));
606 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("List type/item type mismatch, throwing away mismatch"));
612 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in list"));
615 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in ret_list"));
621 ValueNode_DynamicList::set_link_vfunc(int i,ValueNode::Handle x)
625 if((unsigned)i>=list.size())
627 if(x->get_type()!=container_type)
629 list[i].value_node=x;
633 ValueNode::LooseHandle
634 ValueNode_DynamicList::get_link_vfunc(int i)const
638 if((unsigned)i>=list.size())
640 return list[i].value_node;
644 ValueNode_DynamicList::link_count()const
650 ValueNode_DynamicList::link_local_name(int i)const
652 assert(i>=0 && i<link_count());
654 return etl::strprintf(_("Item %03d"),i+1);
658 ValueNode_DynamicList::clone(const GUID& deriv_guid)const
660 { ValueNode* x(find_value_node(get_guid()^deriv_guid).get()); if(x)return x; }
662 ValueNode_DynamicList* ret=new ValueNode_DynamicList(container_type);
663 ret->set_guid(get_guid()^deriv_guid);
665 std::vector<ListEntry>::const_iterator iter;
667 for(iter=list.begin();iter!=list.end();++iter)
669 if(iter->value_node->is_exported())
673 ListEntry list_entry(*iter);
674 //list_entry.value_node=find_value_node(iter->value_node->get_guid()^deriv_guid).get();
675 //if(!list_entry.value_node)
676 list_entry.value_node=iter->value_node->clone(deriv_guid);
677 ret->add(list_entry);
678 //ret->list.back().value_node=iter->value_node.clone();
681 ret->set_loop(get_loop());
686 ValueNode_DynamicList::link_name(int i)const
688 return strprintf("item%04d",i);
692 ValueNode_DynamicList::get_link_index_from_name(const String &name)const
694 throw Exception::BadLinkName(name);
698 ValueNode_DynamicList::get_name()const
700 return "dynamic_list";
704 ValueNode_DynamicList::get_local_name()const
706 return _("Dynamic List");
710 ValueNode_DynamicList::check_type(ValueBase::Type type)
712 return type==ValueBase::TYPE_LIST;
716 ValueNode_DynamicList::get_contained_type()const
718 return container_type;
722 ValueNode_DynamicList::create_new()const
730 ValueNode_DynamicList::find_next_valid_entry(int orig_item, Time t)const
734 for(curr_item=orig_item+1;curr_item!=orig_item;curr_item++)
736 if(curr_item==(int)list.size())
741 if(list[curr_item].status_at_time(t))
748 ValueNode_DynamicList::find_prev_valid_entry(int orig_item, Time t)const
752 for(curr_item=orig_item-1;curr_item!=orig_item;curr_item--)
756 curr_item=list.size();
759 if(list[curr_item].status_at_time(t))
765 const synfig::Node::time_set & ValueNode_DynamicList::ListEntry::get_times() const
767 synfig::ActivepointList::const_iterator j = timing_info.begin(),
768 end = timing_info.end();
770 //must remerge with all the other values because we don't know if we've changed...
771 times = value_node->get_times();
776 t.set_time(j->get_time());
777 t.set_guid(j->get_guid());
785 void ValueNode_DynamicList::get_times_vfunc(Node::time_set &set) const
787 //add in the active points
788 int size = list.size();
790 //rebuild all the info...
791 for(int i = 0; i < size; ++i)
793 const Node::time_set & tset= list[i].get_times();
794 set.insert(tset.begin(),tset.end());
799 //new find functions that don't throw
804 timecmp(const Time &c) :t(c) {}
806 bool operator()(const Activepoint &rhs) const
808 return t.is_equal(rhs.get_time());
812 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x)
817 f.first = std::find(timing_info.begin(),timing_info.end(),x);
819 if(f.first != timing_info.end())
827 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) const
832 f.first = std::find(timing_info.begin(),timing_info.end(),x);
834 if(f.first != timing_info.end())
842 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)
847 f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
849 if(f.first != timing_info.end())
857 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)const
862 f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
864 if(f.first != timing_info.end())
873 ValueNode_DynamicList::insert_time(const Time& location, const Time& delta)
878 std::vector<ListEntry>::iterator iter(list.begin());
879 for(;iter!=list.end();++iter)
883 ListEntry& item(*iter);
885 ActivepointList::iterator iter(item.find_next(location));
886 for(;iter!=item.timing_info.end();++iter)
888 iter->set_time(iter->get_time()+delta);
891 catch(Exception::NotFound) { }