X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftags%2F0.61.09%2Fsrc%2Fsynfig%2Fvaluenode_dynamiclist.cpp;fp=synfig-core%2Ftags%2F0.61.09%2Fsrc%2Fsynfig%2Fvaluenode_dynamiclist.cpp;h=dfdf08409e52da01dd98ebab54f710e6dc99aec5;hb=476a1938d4cf024722f26d03b3e7e5c4b8c1d01c;hp=0000000000000000000000000000000000000000;hpb=d953ebc0f88bcc2533ed89a2800283f529a57620;p=synfig.git diff --git a/synfig-core/tags/0.61.09/src/synfig/valuenode_dynamiclist.cpp b/synfig-core/tags/0.61.09/src/synfig/valuenode_dynamiclist.cpp new file mode 100644 index 0000000..dfdf084 --- /dev/null +++ b/synfig-core/tags/0.61.09/src/synfig/valuenode_dynamiclist.cpp @@ -0,0 +1,908 @@ +/* === S Y N F I G ========================================================= */ +/*! \file valuenode_dynamiclist.cpp +** \brief Implementation of the "Dynamic List" valuenode conversion. +** +** $Id$ +** +** \legal +** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** Copyright (c) 2008 Chris Moore +** +** 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 +#endif + +#include "valuenode_dynamiclist.h" +#include "valuenode_const.h" +#include "valuenode_composite.h" +#include "general.h" +#include "exception.h" +#include +#include +#include +#include "canvas.h" + +#endif + +/* === U S I N G =========================================================== */ + +using namespace std; +using namespace etl; +using namespace synfig; + +/* === M A C R O S ========================================================= */ + +/* === G L O B A L S ======================================================= */ + +/* === P R O C E D U R E S ================================================= */ + +/* === M E T H O D S ======================================================= */ + +ValueNode_DynamicList::ListEntry::ListEntry(): + index(0) +{ +} + +ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node): + value_node(value_node), + index(0) +{ +} + +ValueNode_DynamicList::ListEntry::ListEntry(const ValueNode::Handle &value_node,Time begin, Time end): + value_node(value_node) +{ + add(begin,false); + add(end,false); + add((begin+end)*0.5,true); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::add(Time time, bool status, int priority) +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator; + + //! \optimize + Activepoint ap(time,status,priority); + ap.set_parent_index(get_index()); + ap.set_parent_value_node(get_parent_value_node()); + timing_info.push_back(ap); + iterator iter(--iterator(timing_info.end())); + timing_info.sort(); + + return iter; +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::add(const Activepoint &x) +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList::iterator iterator; + + //! \optimize + Activepoint ap(x); + ap.set_parent_index(get_index()); + ap.set_parent_value_node(get_parent_value_node()); + timing_info.push_back(ap); + iterator iter(--iterator(timing_info.end())); + timing_info.sort(); + + return iter; +} + +void +ValueNode_DynamicList::reindex() +{ + int i(0); + + std::vector::iterator iter; + + for(iter=list.begin();iter!=list.end();++iter) + { + assert(iter->value_node); + if(iter->index!=i || iter->get_parent_value_node().get()!=this) + { + ActivepointList::iterator iter2; + + if(iter->timing_info.size()) // is this line really necessary? + for(iter2=iter->timing_info.begin();iter2!=iter->timing_info.end();++iter2) + { + iter2->set_parent_index(i); + iter2->set_parent_value_node(this); + } + iter->index=i; + iter->set_parent_value_node(this); + } + } +} + +ValueNode_DynamicList::ListEntry +ValueNode_DynamicList::create_list_entry(int index, Time time, Real origin) +{ + ValueNode_DynamicList::ListEntry ret; + + + synfig::ValueBase prev,next; + + index=index%link_count(); + + assert(index>=0); + + ret.index=index; + ret.set_parent_value_node(this); + + next=(*list[index].value_node)(time); + + if(index!=0) + prev=(*list[index-1].value_node)(time); + else + { + if(get_loop()) + prev=(*list[link_count()-1].value_node)(time); + else + { + prev=next; + } + } + + + switch(get_contained_type()) + { + case ValueBase::TYPE_VECTOR: + { + Vector a(prev.get(Vector())), b(next.get(Vector())); + ret.value_node=ValueNode_Const::create((b-a)*origin+a); + break; + } + case ValueBase::TYPE_REAL: + { + Real a(prev.get(Real())), b(next.get(Real())); + ret.value_node=ValueNode_Const::create((b-a)*origin+a); + break; + } + case ValueBase::TYPE_COLOR: + { + Color a(prev.get(Color())), b(next.get(Color())); + ret.value_node=ValueNode_Composite::create((b-a)*origin+a); + break; + } + case ValueBase::TYPE_ANGLE: + { + Angle a(prev.get(Angle())), b(next.get(Angle())); + ret.value_node=ValueNode_Const::create((b-a)*origin+a); + break; + } + case ValueBase::TYPE_TIME: + { + Time a(prev.get(Time())), b(next.get(Time())); + ret.value_node=ValueNode_Const::create((b-a)*origin+a); + break; + } + default: + ret.value_node=ValueNode_Const::create(get_contained_type()); + break; + } + + + return ret; +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::find(const UniqueID& x) +{ + return std::find(timing_info.begin(),timing_info.end(),x); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator +ValueNode_DynamicList::ListEntry::find(const UniqueID& x)const +{ + return std::find(timing_info.begin(),timing_info.end(),x); +} + +void +ValueNode_DynamicList::ListEntry::erase(const UniqueID& x) +{ + timing_info.erase(find(x)); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::find(const Time& x) +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::iterator iter; + + for(iter=timing_info.begin();iter!=timing_info.end();++iter) + if(iter->time==x) + return iter; + + throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find():"+x.get_string()); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator +ValueNode_DynamicList::ListEntry::find(const Time& x)const +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::const_iterator iter; + + for(iter=timing_info.begin();iter!=timing_info.end();++iter) + if(iter->time==x) + return iter; + + throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find()const:"+x.get_string()); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::find_next(const Time& x) +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::iterator iter; + + for(iter=timing_info.begin();iter!=timing_info.end();++iter) + if(iter->time>x) + return iter; + + throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next():"+x.get_string()); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::const_iterator +ValueNode_DynamicList::ListEntry::find_next(const Time& x)const +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::const_iterator iter; + + for(iter=timing_info.begin();iter!=timing_info.end();++iter) + if(iter->time>x) + return iter; + + throw Exception::NotFound("ValueNode_DynamicList::ListEntry::find_next()const:"+x.get_string()); +} + +ValueNode_DynamicList::ListEntry::ActivepointList::iterator +ValueNode_DynamicList::ListEntry::find_prev(const Time& x) +{ + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::iterator iter; + iter=timing_info.end(); + do + { + --iter; + if(iter->timetime& selected) +{ + Time curr_time(begin); + int ret(0); + + // try to grab first waypoint + try + { + ActivepointList::iterator iter; + iter=find(curr_time); + selected.push_back(&*iter); + ret++; + } + catch(...) { } + + try + { + ActivepointList::iterator iter; + while(true) + { + iter=find_next(curr_time); + curr_time=iter->get_time(); + if(curr_time>=end) + break; + selected.push_back(&*iter); + ret++; + } + } + catch(...) { } + + return ret; +} + +float +ValueNode_DynamicList::ListEntry::amount_at_time(const Time &t,bool *rising)const +{ + typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint; + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + if(timing_info.empty()) + return 1.0f; + + try + { + ActivepointList::const_iterator iter; + iter=find(t); + return iter->state?1.0f:0.0f; + } + catch(...) { } + + ActivepointList::const_iterator prev_iter; + ActivepointList::const_iterator next_iter; + + try { prev_iter=find_prev(t); } + catch(...) { return find_next(t)->state?1.0f:0.0f; } + + try { next_iter=find_next(t); } + catch(...) { return prev_iter->state?1.0f:0.0f; } + + if(next_iter->state==prev_iter->state) + return next_iter->state?1.0f:0.0f; + + if(rising)*rising=next_iter->state; + + if(next_iter->state==true) + return float((t-prev_iter->time)/(next_iter->time-prev_iter->time)); + + return float((next_iter->time-t)/(next_iter->time-prev_iter->time)); +} + +Activepoint +ValueNode_DynamicList::ListEntry::new_activepoint_at_time(const Time& time)const +{ + Activepoint activepoint; + + activepoint.set_state(status_at_time(time)); + activepoint.set_priority(0); + + return activepoint; +} + +bool +ValueNode_DynamicList::ListEntry::status_at_time(const Time &t)const +{ + typedef synfig::ValueNode_DynamicList::ListEntry::Activepoint Activepoint; + typedef synfig::ValueNode_DynamicList::ListEntry::ActivepointList ActivepointList; + + ActivepointList::const_iterator entry_iter; + ActivepointList::const_iterator prev_iter; + bool state(true); + + // New "symmetric" state mechanism + if(!timing_info.empty()) + { + if(timing_info.size()==1) + state=timing_info.front().state; + else + { + //! \optimize Perhaps we should use a binary search...? + // This will give us the first activepoint that is after t. + for(entry_iter=timing_info.begin();entry_iter!=timing_info.end();++entry_iter) + { + if(entry_iter->time==t) + { + // If we hit the entry right on the nose, then we don't + // have to do anything more + return entry_iter->state; + } + if(entry_iter->time>t) + break; + + } + prev_iter=entry_iter; + prev_iter--; + + // ie: + // + // |-------|---t---|-------| + // prev_iter^ ^entry_iter + + if(entry_iter==timing_info.end()) + { + state=prev_iter->state; + } + else + if(entry_iter==timing_info.begin()) + { + state=entry_iter->state; + } + else + if(entry_iter->priority==prev_iter->priority) + { + state=entry_iter->state || prev_iter->state; + } + else + if(entry_iter->priority>prev_iter->priority) + { + state=entry_iter->state; + } + else + { + state=prev_iter->state; + } + } + } + return state; +} + + + + +void +ValueNode_DynamicList::add(const ValueNode::Handle &value_node, int index) +{ + ListEntry list_entry(value_node); + list_entry.timing_info.size(); + + if(index<0 || index>=(int)list.size()) + { + list.push_back(list_entry); + } + else + { + list.insert(list.begin()+index,list_entry); + } + + add_child(value_node.get()); + reindex(); + //changed(); + + if(get_parent_canvas()) + get_parent_canvas()->signal_value_node_child_added()(this,value_node); + else if(get_root_canvas() && get_parent_canvas()) + get_root_canvas()->signal_value_node_child_added()(this,value_node); +} + +void +ValueNode_DynamicList::add(const ListEntry &list_entry, int index) +{ + if(index<0 || index>=(int)list.size()) + list.push_back(list_entry); + else + list.insert(list.begin()+index,list_entry); + add_child(list_entry.value_node.get()); + + reindex(); + //changed(); + + if(get_parent_canvas()) + get_parent_canvas()->signal_value_node_child_added()(this,list_entry.value_node); + else if(get_root_canvas() && get_parent_canvas()) + get_root_canvas()->signal_value_node_child_added()(this,list_entry.value_node); +} + +void +ValueNode_DynamicList::erase(const ValueNode::Handle &value_node_) +{ + ValueNode::Handle value_node(value_node_); + + assert(value_node); + if(!value_node) + throw String("ValueNode_DynamicList::erase(): Passed bad value node"); + + std::vector::iterator iter; + for(iter=list.begin();iter!=list.end();++iter) + if(iter->value_node==value_node) + { + list.erase(iter); + if(value_node) + { + remove_child(value_node.get()); + // changed to fix bug 1420091 - it seems that when a .sif file containing a bline layer encapsulated inside + // another layer, get_parent_canvas() is false and get_root_canvas() is true, but when we come to erase a + // vertex, both are true. So the signal is sent to the parent, but the signal wasn't sent to the parent + // when it was added. This probably isn't the right fix, but it seems to work for now. Note that the same + // strange "if (X) else if (Y && X)" code is also present in the two previous functions, above. + + // if(get_parent_canvas()) + // get_parent_canvas()->signal_value_node_child_removed()(this,value_node); + // else if(get_root_canvas() && get_parent_canvas()) + // get_root_canvas()->signal_value_node_child_removed()(this,value_node); + if(get_root_canvas()) + get_root_canvas()->signal_value_node_child_removed()(this,value_node); + } + break; + } + reindex(); +} + + +ValueNode_DynamicList::ValueNode_DynamicList(ValueBase::Type container_type): + LinkableValueNode(ValueBase::TYPE_LIST), + container_type (container_type), + loop_(false) +{ + DCAST_HACK_ENABLE(); +} + +ValueNode_DynamicList::Handle +ValueNode_DynamicList::create(ValueBase::Type id) +{ + return new ValueNode_DynamicList(id); +} + +ValueNode_DynamicList::~ValueNode_DynamicList() +{ + unlink_all(); +} + +ValueNode_DynamicList* +ValueNode_DynamicList::create_from(const ValueBase &value) +{ + //vector value_list(value.operator vector()); + vector value_list(value.get_list()); + + vector::iterator iter; + + if(value_list.empty()) + return 0; + + ValueNode_DynamicList* value_node(new ValueNode_DynamicList(value_list.front().get_type())); + + // when creating a list of vectors, start it off being looped. + // I think the only time this is used if for creating polygons, + // and we want them to be looped by default + if (value_node->get_contained_type() == ValueBase::TYPE_VECTOR) + value_node->set_loop(true); + + for(iter=value_list.begin();iter!=value_list.end();++iter) + { + ValueNode::Handle item(ValueNode_Const::create(*iter)); + value_node->add(ListEntry(item)); + assert(value_node->list.back().value_node); + } + return value_node; +} + +ValueBase +ValueNode_DynamicList::operator()(Time t)const +{ + std::vector ret_list; + std::vector::const_iterator iter; + + assert(container_type); + + for(iter=list.begin();iter!=list.end();++iter) + { + bool state(iter->status_at_time(t)); + + if(state) + { + if(iter->value_node->get_type()==container_type) + ret_list.push_back((*iter->value_node)(t)); + else + { + synfig::warning(string("ValueNode_DynamicList::operator()():")+_("List type/item type mismatch, throwing away mismatch")); + } + } + } + + if(list.empty()) + synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in list")); + else + if(ret_list.empty()) + synfig::warning(string("ValueNode_DynamicList::operator()():")+_("No entries in ret_list")); + + return ret_list; +} + +bool +ValueNode_DynamicList::set_link_vfunc(int i,ValueNode::Handle x) +{ + assert(i>=0); + + if((unsigned)i>=list.size()) + return false; + if(x->get_type()!=container_type) + return false; + list[i].value_node=x; + return true; +} + +ValueNode::LooseHandle +ValueNode_DynamicList::get_link_vfunc(int i)const +{ + assert(i>=0); + + if((unsigned)i>=list.size()) + return 0; + return list[i].value_node; +} + +int +ValueNode_DynamicList::link_count()const +{ + return list.size(); +} + +String +ValueNode_DynamicList::link_local_name(int i)const +{ + assert(i>=0 && iset_guid(get_guid()^deriv_guid); + + std::vector::const_iterator iter; + + for(iter=list.begin();iter!=list.end();++iter) + { + if(iter->value_node->is_exported()) + ret->add(*iter); + else + { + ListEntry list_entry(*iter); + //list_entry.value_node=find_value_node(iter->value_node->get_guid()^deriv_guid).get(); + //if(!list_entry.value_node) + list_entry.value_node=iter->value_node->clone(deriv_guid); + ret->add(list_entry); + //ret->list.back().value_node=iter->value_node.clone(); + } + } + ret->set_loop(get_loop()); + return ret; +} + +String +ValueNode_DynamicList::link_name(int i)const +{ + return strprintf("item%04d",i); +} + +int +ValueNode_DynamicList::get_link_index_from_name(const String &name)const +{ + throw Exception::BadLinkName(name); +} + +String +ValueNode_DynamicList::get_name()const +{ + return "dynamic_list"; +} + +String +ValueNode_DynamicList::get_local_name()const +{ + return _("Dynamic List"); +} + +bool +ValueNode_DynamicList::check_type(ValueBase::Type type) +{ + return type==ValueBase::TYPE_LIST; +} + +void +ValueNode_DynamicList::set_member_canvas(etl::loose_handle canvas) +{ + for (vector::iterator iter = list.begin(); iter != list.end(); iter++) + iter->value_node->set_parent_canvas(canvas); +} + +ValueBase::Type +ValueNode_DynamicList::get_contained_type()const +{ + return container_type; +} + +LinkableValueNode* +ValueNode_DynamicList::create_new()const +{ + assert(0); + + return 0; +} + +int +ValueNode_DynamicList::find_next_valid_entry(int orig_item, Time t)const +{ + int curr_item; + + for(curr_item=orig_item+1;curr_item!=orig_item;curr_item++) + { + if(curr_item==(int)list.size()) + { + curr_item=0; + continue; + } + if(list[curr_item].status_at_time(t)) + return curr_item; + } + return curr_item; +} + +int +ValueNode_DynamicList::find_prev_valid_entry(int orig_item, Time t)const +{ + int curr_item; + + for(curr_item=orig_item-1;curr_item!=orig_item;curr_item--) + { + if(curr_item==-1) + { + curr_item=list.size(); + continue; + } + if(list[curr_item].status_at_time(t)) + return curr_item; + } + return curr_item; +} + +const synfig::Node::time_set & ValueNode_DynamicList::ListEntry::get_times() const +{ + synfig::ActivepointList::const_iterator j = timing_info.begin(), + end = timing_info.end(); + + //must remerge with all the other values because we don't know if we've changed... + times = value_node->get_times(); + + for(; j != end; ++j) + { + TimePoint t; + t.set_time(j->get_time()); + t.set_guid(j->get_guid()); + + times.insert(t); + } + + return times; +} + +void ValueNode_DynamicList::get_times_vfunc(Node::time_set &set) const +{ + //add in the active points + int size = list.size(); + + //rebuild all the info... + for(int i = 0; i < size; ++i) + { + const Node::time_set & tset= list[i].get_times(); + set.insert(tset.begin(),tset.end()); + } +} + + +//new find functions that don't throw +struct timecmp +{ + Time t; + + timecmp(const Time &c) :t(c) {} + + bool operator()(const Activepoint &rhs) const + { + return t.is_equal(rhs.get_time()); + } +}; + +ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) +{ + findresult f; + f.second = false; + + f.first = std::find(timing_info.begin(),timing_info.end(),x); + + if(f.first != timing_info.end()) + { + f.second = true; + } + + return f; +} + +ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_uid(const UniqueID& x) const +{ + const_findresult f; + f.second = false; + + f.first = std::find(timing_info.begin(),timing_info.end(),x); + + if(f.first != timing_info.end()) + { + f.second = true; + } + + return f; +} + +ValueNode_DynamicList::ListEntry::findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x) +{ + findresult f; + f.second = false; + + f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x)); + + if(f.first != timing_info.end()) + { + f.second = true; + } + + return f; +} + +ValueNode_DynamicList::ListEntry::const_findresult ValueNode_DynamicList::ListEntry::find_time(const Time& x)const +{ + const_findresult f; + f.second = false; + + f.first = std::find_if(timing_info.begin(),timing_info.end(),timecmp(x)); + + if(f.first != timing_info.end()) + { + f.second = true; + } + + return f; +} + +void +ValueNode_DynamicList::insert_time(const Time& location, const Time& delta) +{ + if(!delta) + return; + + std::vector::iterator iter(list.begin()); + for(;iter!=list.end();++iter) + { + try + { + ListEntry& item(*iter); + + ActivepointList::iterator iter(item.find_next(location)); + for(;iter!=item.timing_info.end();++iter) + { + iter->set_time(iter->get_time()+delta); + } + } + catch(Exception::NotFound) { } + } + changed(); +}