/* === S Y N F I G ========================================================= */
/*! \file valuenode_dynamic.cpp
** \brief Implementation of the "Dynamic" valuenode conversion.
**
** $Id$
**
** \legal
** Copyright (c) 2014 Carlos López
**
** 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 <config.h>
#endif
#include "valuenode_dynamic.h"
#include "valuenode_const.h"
#include <synfig/general.h>
#include <synfig/localization.h>
#include <synfig/valuenode_registry.h>
#include <ETL/misc>
#include <boost/numeric/odeint/integrate/integrate.hpp>
#endif
/* === U S I N G =========================================================== */
using namespace std;
using namespace etl;
using namespace synfig;
using namespace boost::numeric::odeint;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
REGISTER_VALUENODE(ValueNode_Dynamic, RELEASE_VERSION_0_61_06, "dynamic", "Dynamic")
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
ValueNode_Dynamic::ValueNode_Dynamic(const ValueBase &value):
LinkableValueNode(value.get_type())
{
Vocab ret(get_children_vocab());
set_children_vocab(ret);
set_link("origin", ValueNode_Const::create(Vector(0,0)));
set_link("force", ValueNode_Const::create(Vector(0,0)));
set_link("torque", ValueNode_Const::create(Real(0.0)));
set_link("damping", ValueNode_Const::create(Real(0.4)));
set_link("friction", ValueNode_Const::create(Real(0.4)));
set_link("spring", ValueNode_Const::create(Real(30.0)));
set_link("torsion", ValueNode_Const::create(Real(30.0)));
set_link("mass", ValueNode_Const::create(Real(0.3)));
set_link("inertia", ValueNode_Const::create(Real(0.3)));
set_link("spring_rigid",ValueNode_Const::create(false));
set_link("torsion_rigid",ValueNode_Const::create(false));
set_link("origin_drags_tip",ValueNode_Const::create(true));
if (get_type() == type_vector)
set_link("tip_static",ValueNode_Const::create(value.get(Vector())));
else
throw Exception::BadType(get_type().description.local_name);
/* Initial values*/
state.resize(4);
reset_state(Time(0.0));
/*Derivative of the base position*/
origin_d_=ValueNode_Derivative::create(ValueBase(Vector()));
origin_d_->set_link("order", ValueNode_Const::create((int)(ValueNode_Derivative::SECOND)));
/* Initialize the last time called to be 0*/
last_time=Time(0);
}
void
ValueNode_Dynamic::reset_state(Time t)const
{
state[0]=((*tip_static_)(t).get(Vector())).mag();
state[1]=0.0; // d/dt(radius) = 0 initially
state[2]=(double)(Angle::rad(((*tip_static_)(t).get(Vector())).angle()).get());
state[3]=0.0; // d/dt(angle) = 0 initially
}
LinkableValueNode*
ValueNode_Dynamic::create_new()const
{
return new ValueNode_Dynamic(get_type());
}
ValueNode_Dynamic*
ValueNode_Dynamic::create(const ValueBase &x)
{
return new ValueNode_Dynamic(x);
}
ValueNode_Dynamic::~ValueNode_Dynamic()
{
unlink_all();
}
ValueBase
ValueNode_Dynamic::operator()(Time t)const
{
if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS"))
printf("%s:%d operator()\n", __FILE__, __LINE__);
double t0=last_time;
double t1=t;
double step;
// If we are at the initial conditions
if(t1==t0 && t0==0.0)
{
reset_state(Time(0.0));
return (*origin_)(0).get(Vector()) + Vector(state[0], Angle::rad(state[2]));
}
// If we are playing backwards then calculate the value from the initial conditions
if(t1<t0 && t0>0.0)
{
reset_state(Time(0.0));
last_time=Time(0);
t0=0.0;
}
// Prepare the step size based on distance between start and end time
step=(t1-t0)/4.0;
// Before call the integrator we need to be sure that the derivative of the
// origin is properly set. Maybe the user changed the origin
ValueNode::RHandle value_node(ValueNode::RHandle::cast_dynamic(origin_d_->get_link("link")));
value_node->replace(origin_);
Oscillator oscillator(this);
std::vector<double> x(state.begin(), state.end());
integrate(oscillator, x, t0, t1, step);
// Remember time and state for the next call
last_time=Time(t);
state.assign(x.begin(), x.end());
// We need to check if the spring or the torsion are riggid
bool spring_is_rigid=(*(spring_rigid_))(t).get(bool());
bool torsion_is_rigid=(*(torsion_rigid_))(t).get(bool());
Vector tip=(*(tip_static_))(t).get(Vector());
// Also check if origin drags tip
bool origin_drags_tip=(*(origin_drags_tip_))(t).get(bool());
return Vector(origin_drags_tip?(*origin_)(t).get(Vector()):Vector(0,0))
+
Vector(spring_is_rigid?tip.mag():state[0], torsion_is_rigid?tip.angle():Angle::rad(state[2]));
}
bool
ValueNode_Dynamic::check_type(Type &type)
{
return type==type_vector;
}
bool
ValueNode_Dynamic::set_link_vfunc(int i,ValueNode::Handle value)
{
assert(i>=0 && i<link_count());
switch(i)
{
case 0: CHECK_TYPE_AND_SET_VALUE(tip_static_, get_type());
case 1: CHECK_TYPE_AND_SET_VALUE(origin_, type_vector);
case 2: CHECK_TYPE_AND_SET_VALUE(force_, type_vector);
case 3: CHECK_TYPE_AND_SET_VALUE(torque_, type_real);
case 4: CHECK_TYPE_AND_SET_VALUE(damping_coef_, type_real);
case 5: CHECK_TYPE_AND_SET_VALUE(friction_coef_, type_real);
case 6: CHECK_TYPE_AND_SET_VALUE(spring_coef_, type_real);
case 7: CHECK_TYPE_AND_SET_VALUE(torsion_coef_, type_real);
case 8: CHECK_TYPE_AND_SET_VALUE(mass_, type_real);
case 9: CHECK_TYPE_AND_SET_VALUE(inertia_, type_real);
case 10: CHECK_TYPE_AND_SET_VALUE(spring_rigid_, type_bool);
case 11: CHECK_TYPE_AND_SET_VALUE(torsion_rigid_,type_bool);
case 12: CHECK_TYPE_AND_SET_VALUE(origin_drags_tip_,type_bool);
}
return false;
}
ValueNode::LooseHandle
ValueNode_Dynamic::get_link_vfunc(int i)const
{
assert(i>=0 && i<link_count());
switch(i)
{
case 0: return tip_static_;
case 1: return origin_;
case 2: return force_;
case 3: return torque_;
case 4: return damping_coef_;
case 5: return friction_coef_;
case 6: return spring_coef_;
case 7: return torsion_coef_;
case 8: return mass_;
case 9: return inertia_;
case 10: return spring_rigid_;
case 11: return torsion_rigid_;
case 12: return origin_drags_tip_;
default:
return 0;
}
}
LinkableValueNode::Vocab
ValueNode_Dynamic::get_children_vocab_vfunc()const
{
if(children_vocab.size())
return children_vocab;
LinkableValueNode::Vocab ret;
ret.push_back(ParamDesc(ValueBase(),"tip_static")
.set_local_name(_("Tip static"))
.set_description(_("Equilibrium tip position without external forces"))
);
ret.push_back(ParamDesc(ValueBase(),"origin")
.set_local_name(_("Origin"))
.set_description(_("Basement of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"force")
.set_local_name(_("Force"))
.set_description(_("External force applied on the mass center of gravity"))
);
ret.push_back(ParamDesc(ValueBase(),"torque")
.set_local_name(_("Torque"))
.set_description(_("External momentum applied at the center of inertia"))
);
ret.push_back(ParamDesc(ValueBase(),"damping")
.set_local_name(_("Damping coefficient"))
.set_description(_("Radial damping coefficient of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"friction")
.set_local_name(_("Friction coefficient"))
.set_description(_("Rotational friction coefficient of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"spring")
.set_local_name(_("Spring coefficient"))
.set_description(_("Radial spring coefficient of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"torsion")
.set_local_name(_("Torsion coefficient"))
.set_description(_("Torsion coefficient of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"mass")
.set_local_name(_("Mass"))
.set_description(_("Mass of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"inertia")
.set_local_name(_("Moment of Inertia"))
.set_description(_("Moment of inertia of the dynamic system"))
);
ret.push_back(ParamDesc(ValueBase(),"spring_rigid")
.set_local_name(_("Spring rigid"))
.set_description(_("When checked, linear spring is rigid"))
);
ret.push_back(ParamDesc(ValueBase(),"torsion_rigid")
.set_local_name(_("Torsion rigid"))
.set_description(_("When checked torsion spring is rigid"))
);
ret.push_back(ParamDesc(ValueBase(),"origin_drags_tip")
.set_local_name(_("Origin drags tip"))
.set_description(_("When checked result is origin + tip otherwise result is just tip"))
);
return ret;
}