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
9 ** Copyright (c) 2008 Chris Moore
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.
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.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
33 #include "valuenode_dynamiclist.h"
34 #include "valuenode_const.h"
35 #include "valuenode_composite.h"
37 #include "exception.h"
45 /* === U S I N G =========================================================== */
49 using namespace synfig;
51 /* === M A C R O S ========================================================= */
53 /* === G L O B A L S ======================================================= */
55 /* === P R O C E D U R E S ================================================= */
57 /* === M E T H O D S ======================================================= */
59 ValueNode_DynamicList::ListEntry::ListEntry():
64 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node):
65 value_node(value_node),
70 ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node,Time begin, Time end):
71 value_node(value_node)
75 add((begin+end)*0.5,true);
78 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
79 ValueNode_DynamicList::ListEntry::add(Time time, bool status, int priority)
81 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
84 Activepoint ap(time,status,priority);
85 ap.set_parent_index(get_index());
86 ap.set_parent_value_node(get_parent_value_node());
87 timing_info.push_back(ap);
88 iterator iter(--iterator(timing_info.end()));
94 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
95 ValueNode_DynamicList::ListEntry::add(const Activepoint &x)
97 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator;
101 ap.set_parent_index(get_index());
102 ap.set_parent_value_node(get_parent_value_node());
103 timing_info.push_back(ap);
104 iterator iter(--iterator(timing_info.end()));
111 ValueNode_DynamicList::reindex()
115 std::vector<ListEntry>::iterator iter;
117 for(iter=list.begin();iter!=list.end();++iter)
119 assert(iter->value_node);
120 if(iter->index!=i || iter->get_parent_value_node().get()!=this)
122 ActivepointList::iterator iter2;
124 if(iter->timing_info.size()) // is this line really necessary?
125 for(iter2=iter->timing_info.begin();iter2!=iter->timing_info.end();++iter2)
127 iter2->set_parent_index(i);
128 iter2->set_parent_value_node(this);
131 iter->set_parent_value_node(this);
136 ValueNode_DynamicList::ListEntry
137 ValueNode_DynamicList::create_list_entry(int index, Time time, Real origin)
139 ValueNode_DynamicList::ListEntry ret;
142 synfig::ValueBase prev,next;
144 index=index%link_count();
149 ret.set_parent_value_node(this);
151 next=(*list[index].value_node)(time);
154 prev=(*list[index-1].value_node)(time);
158 prev=(*list[link_count()-1].value_node)(time);
166 switch(get_contained_type())
168 case ValueBase::TYPE_VECTOR:
170 Vector a(prev.get(Vector())), b(next.get(Vector()));
171 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
174 case ValueBase::TYPE_REAL:
176 Real a(prev.get(Real())), b(next.get(Real()));
177 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
180 case ValueBase::TYPE_COLOR:
182 Color a(prev.get(Color())), b(next.get(Color()));
183 ret.value_node=ValueNode_Composite::create((b-a)*origin+a);
186 case ValueBase::TYPE_ANGLE:
188 Angle a(prev.get(Angle())), b(next.get(Angle()));
189 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
192 case ValueBase::TYPE_TIME:
194 Time a(prev.get(Time())), b(next.get(Time()));
195 ret.value_node=ValueNode_Const::create((b-a)*origin+a);
199 ret.value_node=ValueNode_Const::create(get_contained_type());
207 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
208 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)
210 return std::find(timing_info.begin(),timing_info.end(),x);
213 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
214 ValueNode_DynamicList::ListEntry::find(const UniqueID& x)const
216 return std::find(timing_info.begin(),timing_info.end(),x);
220 ValueNode_DynamicList::ListEntry::erase(const UniqueID& x)
222 timing_info.erase(find(x));
225 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
226 ValueNode_DynamicList::ListEntry::find(const Time& x)
228 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
230 ActivepointList::iterator iter;
232 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
236 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find():"+x.get_string());
239 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
240 ValueNode_DynamicList::ListEntry::find(const Time& x)const
242 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
244 ActivepointList::const_iterator iter;
246 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
250 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find()const:"+x.get_string());
253 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
254 ValueNode_DynamicList::ListEntry::find_next(const Time& x)
256 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
258 ActivepointList::iterator iter;
260 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
264 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next():"+x.get_string());
267 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
268 ValueNode_DynamicList::ListEntry::find_next(const Time& x)const
270 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
272 ActivepointList::const_iterator iter;
274 for(iter=timing_info.begin();iter!=timing_info.end();++iter)
278 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next()const:"+x.get_string());
281 ValueNode_DynamicList::ListEntry::ActivepointList::iterator
282 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)
284 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
286 ActivepointList::iterator iter;
287 iter=timing_info.end();
294 while(iter!=timing_info.begin());
296 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev():"+x.get_string());
299 ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator
300 ValueNode_DynamicList::ListEntry::find_prev(const Time& x)const
302 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
304 ActivepointList::const_iterator iter;
305 iter=timing_info.end();
312 while(iter!=timing_info.begin());
314 throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_prev()const:"+x.get_string());
318 ValueNode_DynamicList::ListEntry::find(const Time& begin,const Time& end,std::vector<Activepoint*>& selected)
320 Time curr_time(begin);
323 // try to grab first waypoint
326 ActivepointList::iterator iter;
327 iter=find(curr_time);
328 selected.push_back(&*iter);
335 ActivepointList::iterator iter;
338 iter=find_next(curr_time);
339 curr_time=iter->get_time();
342 selected.push_back(&*iter);
352 ValueNode_DynamicList::ListEntry::amount_at_time(const Time &t,bool *rising)const
354 typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
355 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
357 if(timing_info.empty())
362 ActivepointList::const_iterator iter;
364 return iter->state?1.0f:0.0f;
368 ActivepointList::const_iterator prev_iter;
369 ActivepointList::const_iterator next_iter;
371 try { prev_iter=find_prev(t); }
372 catch(...) { return find_next(t)->state?1.0f:0.0f; }
374 try { next_iter=find_next(t); }
375 catch(...) { return prev_iter->state?1.0f:0.0f; }
377 if(next_iter->state==prev_iter->state)
378 return next_iter->state?1.0f:0.0f;
380 if(rising)*rising=next_iter->state;
382 if(next_iter->state==true)
383 return float((t-prev_iter->time)/(next_iter->time-prev_iter->time));
385 return float((next_iter->time-t)/(next_iter->time-prev_iter->time));
389 ValueNode_DynamicList::ListEntry::new_activepoint_at_time(const Time& time)const
391 Activepoint activepoint;
393 activepoint.set_state(status_at_time(time));
394 activepoint.set_priority(0);
400 ValueNode_DynamicList::ListEntry::status_at_time(const Time &t)const
402 typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint;
403 typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList;
405 ActivepointList::const_iterator entry_iter;
406 ActivepointList::const_iterator prev_iter;
409 // New "symmetric" state mechanism
410 if(!timing_info.empty())
412 if(timing_info.size()==1)
413 state=timing_info.front().state;
416 //! \optimize Perhaps we should use a binary search...?
417 // This will give us the first activepoint that is after t.
418 for(entry_iter=timing_info.begin();entry_iter!=timing_info.end();++entry_iter)
420 if(entry_iter->time==t)
422 // If we hit the entry right on the nose, then we don't
423 // have to do anything more
424 return entry_iter->state;
426 if(entry_iter->time>t)
430 prev_iter=entry_iter;
435 // |-------|---t---|-------|
436 // prev_iter^ ^entry_iter
438 if(entry_iter==timing_info.end())
440 state=prev_iter->state;
443 if(entry_iter==timing_info.begin())
445 state=entry_iter->state;
448 if(entry_iter->priority==prev_iter->priority)
450 state=entry_iter->state || prev_iter->state;
453 if(entry_iter->priority>prev_iter->priority)
455 state=entry_iter->state;
459 state=prev_iter->state;
470 ValueNode_DynamicList::add(const ValueNode::Handle &value_node, int index)
472 ListEntry list_entry(value_node);
473 list_entry.timing_info.size();
475 if(index<0 || index>=(int)list.size())
477 list.push_back(list_entry);
481 list.insert(list.begin()+index,list_entry);
484 add_child(value_node.get());
488 if(get_parent_canvas())
489 get_parent_canvas()->signal_value_node_child_added()(this,value_node);
490 else if(get_root_canvas() && get_parent_canvas())
491 get_root_canvas()->signal_value_node_child_added()(this,value_node);
495 ValueNode_DynamicList::add(const ListEntry &list_entry, int index)
497 if(index<0 || index>=(int)list.size())
498 list.push_back(list_entry);
500 list.insert(list.begin()+index,list_entry);
501 add_child(list_entry.value_node.get());
506 if(get_parent_canvas())
507 get_parent_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
508 else if(get_root_canvas() && get_parent_canvas())
509 get_root_canvas()->signal_value_node_child_added()(this,list_entry.value_node);
513 ValueNode_DynamicList::erase(const ValueNode::Handle &value_node_)
515 ValueNode::Handle value_node(value_node_);
519 throw String("ValueNode_DynamicList::erase(): Passed bad value node");
521 std::vector<ListEntry>::iterator iter;
522 for(iter=list.begin();iter!=list.end();++iter)
523 if(iter->value_node==value_node)
528 remove_child(value_node.get());
529 // changed to fix bug 1420091 - it seems that when a .sif file containing a bline layer encapsulated inside
530 // another layer, get_parent_canvas() is false and get_root_canvas() is true, but when we come to erase a
531 // vertex, both are true. So the signal is sent to the parent, but the signal wasn't sent to the parent
532 // when it was added. This probably isn't the right fix, but it seems to work for now. Note that the same
533 // strange "if (X) else if (Y && X)" code is also present in the two previous functions, above.
535 // if(get_parent_canvas())
536 // get_parent_canvas()->signal_value_node_child_removed()(this,value_node);
537 // else if(get_root_canvas() && get_parent_canvas())
538 // get_root_canvas()->signal_value_node_child_removed()(this,value_node);
539 if(get_root_canvas())
540 get_root_canvas()->signal_value_node_child_removed()(this,value_node);
548 ValueNode_DynamicList::ValueNode_DynamicList(ValueBase::Type container_type):
549 LinkableValueNode(ValueBase::TYPE_LIST),
550 container_type (container_type),
556 ValueNode_DynamicList::Handle
557 ValueNode_DynamicList::create(ValueBase::Type id)
559 return new ValueNode_DynamicList(id);
562 ValueNode_DynamicList::~ValueNode_DynamicList()
567 ValueNode_DynamicList*
568 ValueNode_DynamicList::create_from(const ValueBase &value)
570 //vector<ValueBase> value_list(value.operator vector<ValueBase>());
571 vector<ValueBase> value_list(value.get_list());
573 vector<ValueBase>::iterator iter;
575 if(value_list.empty())
578 ValueNode_DynamicList* value_node(new ValueNode_DynamicList(value_list.front().get_type()));
580 // when creating a list of vectors, start it off being looped.
581 // I think the only time this is used if for creating polygons,
582 // and we want them to be looped by default
583 if (value_node->get_contained_type() == ValueBase::TYPE_VECTOR)
584 value_node->set_loop(true);
586 for(iter=value_list.begin();iter!=value_list.end();++iter)
588 ValueNode::Handle item(ValueNode_Const::create(*iter));
589 value_node->add(ListEntry(item));
590 assert(value_node->list.back().value_node);
596 ValueNode_DynamicList::operator()(Time t)const
598 std::vector<ValueBase> ret_list;
599 std::vector<ListEntry>::const_iterator iter;
601 assert(container_type);
603 for(iter=list.begin();iter!=list.end();++iter)
605 bool state(iter->status_at_time(t));
609 if(iter->value_node->get_type()==container_type)
610 ret_list.push_back((*iter->value_node)(t));
613 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("List type/item type mismatch, throwing away mismatch"));
619 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in list"));
622 synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in ret_list"));
628 ValueNode_DynamicList::set_link_vfunc(int i,ValueNode::Handle x)
632 if((unsigned)i>=list.size())
634 if(x->get_type()!=container_type)
636 list[i].value_node=x;
640 ValueNode::LooseHandle
641 ValueNode_DynamicList::get_link_vfunc(int i)const
645 if((unsigned)i>=list.size())
647 return list[i].value_node;
651 ValueNode_DynamicList::link_count()const
657 ValueNode_DynamicList::link_local_name(int i)const
659 assert(i>=0 && i<link_count());
661 return etl::strprintf(_("Item %03d"),i+1);
665 ValueNode_DynamicList::clone(const GUID& deriv_guid)const
667 { ValueNode* x(find_value_node(get_guid()^deriv_guid).get()); if(x)return x; }
669 ValueNode_DynamicList* ret=new ValueNode_DynamicList(container_type);
670 ret->set_guid(get_guid()^deriv_guid);
672 std::vector<ListEntry>::const_iterator iter;
674 for(iter=list.begin();iter!=list.end();++iter)
676 if(iter->value_node->is_exported())
680 ListEntry list_entry(*iter);
681 //list_entry.value_node=find_value_node(iter->value_node->get_guid()^deriv_guid).get();
682 //if(!list_entry.value_node)
683 list_entry.value_node=iter->value_node->clone(deriv_guid);
684 ret->add(list_entry);
685 //ret->list.back().value_node=iter->value_node.clone();
688 ret->set_loop(get_loop());
693 ValueNode_DynamicList::link_name(int i)const
695 return strprintf("item%04d",i);
699 ValueNode_DynamicList::get_link_index_from_name(const String &name)const
701 throw Exception::BadLinkName(name);
705 ValueNode_DynamicList::get_name()const
707 return "dynamic_list";
711 ValueNode_DynamicList::get_local_name()const
713 return _("Dynamic List");
717 ValueNode_DynamicList::check_type(ValueBase::Type type)
719 return type==ValueBase::TYPE_LIST;
723 ValueNode_DynamicList::set_member_canvas(etl::loose_handle<Canvas> canvas)
725 for (vector<ListEntry>::iterator iter = list.begin(); iter != list.end(); iter++)
726 iter->value_node->set_parent_canvas(canvas);
730 ValueNode_DynamicList::get_contained_type()const
732 return container_type;
736 ValueNode_DynamicList::create_new()const
744 ValueNode_DynamicList::find_next_valid_entry(int orig_item, Time t)const
748 for(curr_item=orig_item+1;curr_item!=orig_item;curr_item++)
750 if(curr_item==(int)list.size())
755 if(list[curr_item].status_at_time(t))
762 ValueNode_DynamicList::find_prev_valid_entry(int orig_item, Time t)const
766 for(curr_item=orig_item-1;curr_item!=orig_item;curr_item--)
770 curr_item=list.size();
773 if(list[curr_item].status_at_time(t))
779 const synfig::Node::time_set & ValueNode_DynamicList::ListEntry::get_times() const
781 synfig::ActivepointList::const_iterator j = timing_info.begin(),
782 end = timing_info.end();
784 //must remerge with all the other values because we don't know if we've changed...
785 times = value_node->get_times();
790 t.set_time(j->get_time());
791 t.set_guid(j->get_guid());
799 void ValueNode_DynamicList::get_times_vfunc(Node::time_set &set) const
801 //add in the active points
802 int size = list.size();
804 //rebuild all the info...
805 for(int i = 0; i < size; ++i)
807 const Node::time_set & tset= list[i].get_times();
808 set.insert(tset.begin(),tset.end());
813 //new find functions that don't throw
818 timecmp(const Time &c) :t(c) {}
820 bool operator()(const Activepoint &rhs) const
822 return t.is_equal(rhs.get_time());
826 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x)
831 f.first = std::find(timing_info.begin(),timing_info.end(),x);
833 if(f.first != timing_info.end())
841 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) const
846 f.first = std::find(timing_info.begin(),timing_info.end(),x);
848 if(f.first != timing_info.end())
856 ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)
861 f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
863 if(f.first != timing_info.end())
871 ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)const
876 f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x));
878 if(f.first != timing_info.end())
887 ValueNode_DynamicList::insert_time(const Time& location, const Time& delta)
892 std::vector<ListEntry>::iterator iter(list.begin());
893 for(;iter!=list.end();++iter)
897 ListEntry& item(*iter);
899 ActivepointList::iterator iter(item.find_next(location));
900 for(;iter!=item.timing_info.end();++iter)
902 iter->set_time(iter->get_time()+delta);
905 catch(Exception::NotFound) { }