ValueBase ValueNode_WPList::operator()(Time t)const { if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS")) printf("%s:%d operator()\n", __FILE__, __LINE__); std::vector<WidthPoint> ret_list; std::vector<ListEntry>::const_iterator iter; bool rising; WidthPoint curr; // go through all the list's entries for(iter=list.begin();iter!=list.end();++iter) { // how 'on' is this widthpoint? float amount(iter->amount_at_time(t,&rising)); assert(amount>=0.0f); assert(amount<=1.0f); // we store the current width point curr=(*iter->value_node)(t).get(curr); // it's fully on if (amount > 1.0f - 0.0000001f) { // push back to the returning list ret_list.push_back(curr); } // it's partly on else if(amount>0.0f) { // This is where the interesting stuff happens Time off_time, on_time; if(!rising) // if not rising, then we were fully 'on' in the past, and will be fully 'off' in the future { try{ on_time=iter->find_prev(t)->get_time(); } catch(...) { on_time=Time::begin(); } try{ off_time=iter->find_next(t)->get_time(); } catch(...) { off_time=Time::end(); } } else // otherwise we were fully 'off' in the past, and will be fully 'on' in the future { try{ off_time=iter->find_prev(t)->get_time(); } catch(...) { off_time=Time::begin(); } try{ on_time=iter->find_next(t)->get_time(); } catch(...) { on_time=Time::end(); } } // i_width is the interpolated width at current time given by fully 'on' surrounding width points Real i_width(interpolated_width(curr.get_norm_position(get_loop()), t)); Real curr_width(curr.get_width()); // linear interpolation by amount curr.set_width(i_width*(1.0-amount)+(curr_width)*amount); // now insert the calculated width point into the widht list ret_list.push_back(curr); } } if(list.empty()) synfig::warning(string("ValueNode_WPList::operator()():")+_("No entries in list")); else if(ret_list.empty()) synfig::warning(string("ValueNode_WPList::operator()():")+_("No entries in ret_list")); return ValueBase(ret_list,get_loop()); }
/*! * @param prev previous withpoint * @param next next withpoint * @param p position to calculate the new width between previous position and next position * @param smoothness [0,1] how much linear (0) or smooth (1) the interpolation is * @return the interpolated width */ Real synfig::widthpoint_interpolate(const WidthPoint& prev, const WidthPoint& next, const Real p, const Real smoothness) { // Smoothness gives linear interpolation between // the result of the linear interpolation between the withpoints // and the interpolation based on a 5th degree polynomial that matches // following rules: // q [0,1] // p(0)= 0 p(1)= 1 // p'(0)= 0 p'(1)= 0 // p''(0)= 0 p''(1)= 0 // It is: p(q) = 6*q^5-15*q^4+10*q^3 = q*q*q*(10+q*(6*q-15) WidthPoint::SideType side_int(WidthPoint::TYPE_INTERPOLATE); int nsb, nsa, psb, psa; Real pp, np; Real nw, pw, rw(0.0); const Real epsilon(0.0000001f); np=next.get_position(); pp=prev.get_position(); nw=next.get_width(); pw=prev.get_width(); nsb=next.get_side_type_before(); nsa=next.get_side_type_after(); psb=prev.get_side_type_before(); psa=prev.get_side_type_after(); if(p==np) return nw; if(p==pp) return pw; // Normal case: previous position is lower than next position if(np > pp) { if(np > p && p > pp ) { Real q; if(nsb != side_int) nw=0.0; if(psa != side_int) pw=0.0; if(np-pp < epsilon) q=0.5; else q=(p-pp)/(np-pp); rw=pw+(nw-pw)*(q*(1.0-smoothness)+q*q*q*(10+q*(6*q-15))*smoothness); } else if(p < pp) { if(psb != side_int) pw=0.0; rw=pw; } else if(p > np) { if(nsa != side_int) nw=0.0; rw=nw; } } // particular case: previous position is higher than next position else if(p > pp || np > p) { Real q(0); if(nsb != side_int) nw=0.0; if(psa != side_int) pw=0.0; if(np+1.0-pp < epsilon) q=0.5; else { if(p > pp) q=(p-pp)/(np+1.0-pp); if(np > p) q=(p+1.0-pp)/(np+1.0-pp); } rw=pw+(nw-pw)*(q*(1.0-smoothness)+q*q*q*(10+q*(6*q-15))*smoothness); } else if(p > np && p < pp) { Real q; if(nsa != side_int) nw=0.0; if(psb != side_int) pw=0.0; if(pp-np < epsilon) q=0.5; else q=(p-np)/(pp-np); rw=nw+(pw-nw)*(q*(1.0-smoothness)+q*q*q*(10+q*(6*q-15))*smoothness); } return rw; }