From 4b21dc51ee09d6755f60696f3ae1b30159e6433f Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sat, 16 Mar 2013 16:43:55 +0100 Subject: [PATCH] Replace SGInterpolator with new advanced interpolation system. Allow for advanced animations using easing functions and adapters for interpolating specific property types (eg. CSS colors). Old behavior should not have changed. --- src/Main/CMakeLists.txt | 2 + src/Main/FGInterpolator.cxx | 15 +++++ src/Main/FGInterpolator.hxx | 21 +++++++ src/Main/fg_commands.cxx | 115 ++++++++++++++++++++-------------- src/Main/fg_init.cxx | 6 +- src/Main/subsystemFactory.cxx | 4 +- src/Scripting/NasalSys.cxx | 68 +++++++++++++------- 7 files changed, 157 insertions(+), 74 deletions(-) create mode 100644 src/Main/FGInterpolator.cxx create mode 100644 src/Main/FGInterpolator.hxx diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt index 254a44c1e..dceb3db3c 100644 --- a/src/Main/CMakeLists.txt +++ b/src/Main/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCES fg_io.cxx fg_os_common.cxx fg_props.cxx + FGInterpolator.cxx globals.cxx locale.cxx logger.cxx @@ -30,6 +31,7 @@ set(HEADERS fg_init.hxx fg_io.hxx fg_props.hxx + FGInterpolator.hxx globals.hxx locale.hxx logger.hxx diff --git a/src/Main/FGInterpolator.cxx b/src/Main/FGInterpolator.cxx new file mode 100644 index 000000000..2783e0c7f --- /dev/null +++ b/src/Main/FGInterpolator.cxx @@ -0,0 +1,15 @@ +/* + * FGInterpolator.cxx + * + * Created on: 16.03.2013 + * Author: tom + */ + +#include "FGInterpolator.hxx" +#include + +//------------------------------------------------------------------------------ +FGInterpolator::FGInterpolator() +{ + addInterpolatorFactory("color"); +} diff --git a/src/Main/FGInterpolator.hxx b/src/Main/FGInterpolator.hxx new file mode 100644 index 000000000..ef7f62171 --- /dev/null +++ b/src/Main/FGInterpolator.hxx @@ -0,0 +1,21 @@ +/* + * FGInterpolator.hxx + * + * Created on: 16.03.2013 + * Author: tom + */ + +#ifndef FG_INTERPOLATOR_HXX_ +#define FG_INTERPOLATOR_HXX_ + +#include + +class FGInterpolator: + public simgear::PropertyInterpolationMgr +{ + public: + FGInterpolator(); +}; + + +#endif /* FG_INTERPOLATOR_HXX_ */ diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx index 27980afba..9d39d237d 100644 --- a/src/Main/fg_commands.cxx +++ b/src/Main/fg_commands.cxx @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -45,6 +44,7 @@ #include "fg_os.hxx" #include "fg_commands.hxx" #include "fg_props.hxx" +#include "FGInterpolator.hxx" #include "globals.hxx" #include "logger.hxx" #include "util.hxx" @@ -883,13 +883,15 @@ do_property_randomize (const SGPropertyNode * arg) * Built-in command: interpolate a property value over time * * property: the name of the property value to interpolate. + * type: the interpolation type ("numeric", "color", etc.) + * easing: name of easing function (see http://easings.net/) * value[0..n] any number of constant values to interpolate * time/rate[0..n] time between each value, number of time elements must * match those of value elements. Instead of time also rate can * be used which automatically calculates the time to change * the property value at the given speed. * -or- - * property[1..n] any number of target values taken from named properties + * property[1..n+1] any number of target values taken from named properties * time/rate[0..n] time between each value, number of time elements must * match those of value elements. Instead of time also rate can * be used which automatically calculates the time to change @@ -898,63 +900,82 @@ do_property_randomize (const SGPropertyNode * arg) static bool do_property_interpolate (const SGPropertyNode * arg) { - SGPropertyNode * prop = get_prop(arg); + FGInterpolator* mgr = + static_cast + ( + globals->get_subsystem_mgr() + ->get_group(SGSubsystemMgr::INIT) + ->get_subsystem("prop-interpolator") + ); - simgear::PropertyList valueNodes = arg->getChildren( "value" ); - simgear::PropertyList timeNodes = arg->getChildren( "time" ); - simgear::PropertyList rateNodes = arg->getChildren( "rate" ); + if( !mgr ) + { + SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available"); + return false; + } - if( !timeNodes.empty() && !rateNodes.empty() ) - // mustn't specify time and rate - return false; + SGPropertyNode * prop = get_prop(arg); + simgear::PropertyList time_nodes = arg->getChildren("time"); + simgear::PropertyList rate_nodes = arg->getChildren("rate"); - simgear::PropertyList::size_type num_times = timeNodes.empty() - ? rateNodes.size() - : timeNodes.size(); + if( !time_nodes.empty() && !rate_nodes.empty() ) + // mustn't specify time and rate + return false; - boost::scoped_array value; - boost::scoped_array time; + simgear::PropertyList::size_type num_times = time_nodes.empty() + ? rate_nodes.size() + : time_nodes.size(); - if( valueNodes.size() > 0 ) { - // must match - if( num_times != valueNodes.size() ) - return false; + simgear::PropertyList value_nodes = arg->getChildren("value"); + if( value_nodes.empty() ) + { + simgear::PropertyList prop_nodes = arg->getChildren("property"); - value.reset( new double[valueNodes.size()] ); - for( simgear::PropertyList::size_type n = 0; n < valueNodes.size(); n++ ) { - value[n] = valueNodes[n]->getDoubleValue(); - } - } else { - valueNodes = arg->getChildren("property"); - // must have one more property node - if( valueNodes.size() - 1 != num_times ) - return false; + // must have one more property node + if( prop_nodes.size() != num_times + 1 ) + return false; - value.reset( new double[valueNodes.size()-1] ); - for( simgear::PropertyList::size_type n = 0; n < valueNodes.size()-1; n++ ) { - value[n] = fgGetNode(valueNodes[n+1]->getStringValue(), "/null")->getDoubleValue(); - } + value_nodes.reserve(num_times); + for( size_t i = 1; i < prop_nodes.size(); ++i ) + value_nodes.push_back( fgGetNode(prop_nodes[i]->getStringValue()) ); + } - } + // must match + if( value_nodes.size() != num_times ) + return false; - time.reset( new double[num_times] ); - if( !timeNodes.empty() ) { - for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) { - time[n] = timeNodes[n]->getDoubleValue(); - } - } else { - for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) { - double delta = value[n] - - (n > 0 ? value[n - 1] : prop->getDoubleValue()); - time[n] = fabs(delta / rateNodes[n]->getDoubleValue()); - } + double_list deltas; + deltas.reserve(num_times); + + if( !time_nodes.empty() ) + { + for( size_t i = 0; i < num_times; ++i ) + deltas.push_back( time_nodes[i]->getDoubleValue() ); + } + else + { + for( size_t i = 0; i < num_times; ++i ) + { + // TODO calculate delta based on property type + double delta = value_nodes[i]->getDoubleValue() + - ( i > 0 + ? value_nodes[i - 1]->getDoubleValue() + : prop->getDoubleValue() + ); + deltas.push_back( fabs(delta / rate_nodes[i]->getDoubleValue()) ); } + } - ((SGInterpolator*)globals->get_subsystem_mgr() - ->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator")) - ->interpolate(prop, num_times, value.get(), time.get() ); + mgr->interpolate + ( + prop, + arg->getStringValue("type", "numeric"), + value_nodes, + deltas, + arg->getStringValue("easing", "linear") + ); - return true; + return true; } /** diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 729301c24..b0f8b511a 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -51,7 +51,6 @@ #include #include -#include #include #include @@ -107,6 +106,7 @@ #include "fg_io.hxx" #include "fg_commands.hxx" #include "fg_props.hxx" +#include "FGInterpolator.hxx" #include "options.hxx" #include "globals.hxx" #include "logger.hxx" @@ -570,7 +570,9 @@ void fgCreateSubsystems() { // Initialize the property interpolator subsystem. Put into the INIT // group because the "nasal" subsystem may need it at GENERAL take-down. //////////////////////////////////////////////////////////////////// - globals->add_subsystem("interpolator", new SGInterpolator, SGSubsystemMgr::INIT); + globals->add_subsystem( "prop-interpolator", + new FGInterpolator, + SGSubsystemMgr::INIT ); //////////////////////////////////////////////////////////////////// diff --git a/src/Main/subsystemFactory.cxx b/src/Main/subsystemFactory.cxx index c174ad5fb..3931a6c8e 100644 --- a/src/Main/subsystemFactory.cxx +++ b/src/Main/subsystemFactory.cxx @@ -33,8 +33,8 @@ // subsystem includes #include -#include #include
+#include
#include
#include #include @@ -76,7 +76,7 @@ SGSubsystem* createSubsystemByName(const std::string& name) MAKE_SUB(FGControls, "controls"); MAKE_SUB(FGSoundManager, "sound"); - MAKE_SUB(SGInterpolator, "interpolator"); + MAKE_SUB(FGInterpolator, "prop-interpolator") MAKE_SUB(FGProperties, "properties"); MAKE_SUB(FDMShell, "fdm"); MAKE_SUB(FGEnvironmentMgr, "environment"); diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index f7a1cbd38..58a9417dc 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +45,7 @@ #include
#include
#include
- +#include
using std::map; @@ -514,29 +513,52 @@ static naRef f_cmdarg(naContext c, naRef me, int argc, naRef* args) // value/delta numbers. static naRef f_interpolate(naContext c, naRef me, int argc, naRef* args) { - SGPropertyNode* node; - naRef prop = argc > 0 ? args[0] : naNil(); - if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true); - else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop); - else return naNil(); - - naRef curve = argc > 1 ? args[1] : naNil(); - if(!naIsVector(curve)) return naNil(); - int nPoints = naVec_size(curve) / 2; - double* values = new double[nPoints]; - double* deltas = new double[nPoints]; - for(int i=0; i + ( + globals->get_subsystem_mgr() + ->get_group(SGSubsystemMgr::INIT) + ->get_subsystem("prop-interpolator") + ); + if( !mgr ) + { + SG_LOG(SG_GENERAL, SG_WARN, "No property interpolator available"); + return naNil(); + }; - ((SGInterpolator*)globals->get_subsystem_mgr() - ->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator")) - ->interpolate(node, nPoints, values, deltas); + SGPropertyNode* node; + naRef prop = argc > 0 ? args[0] : naNil(); + if(naIsString(prop)) node = fgGetNode(naStr_data(prop), true); + else if(naIsGhost(prop)) node = *(SGPropertyNode_ptr*)naGhost_ptr(prop); + else return naNil(); - delete[] values; - delete[] deltas; - return naNil(); + naRef curve = argc > 1 ? args[1] : naNil(); + if(!naIsVector(curve)) return naNil(); + int nPoints = naVec_size(curve) / 2; + + simgear::PropertyList value_nodes; + value_nodes.reserve(nPoints); + double_list deltas; + deltas.reserve(nPoints); + + for( int i = 0; i < nPoints; ++i ) + { + SGPropertyNode* val = new SGPropertyNode; + val->setDoubleValue(naNumValue(naVec_get(curve, 2*i)).num); + value_nodes.push_back(val); + deltas.push_back(naNumValue(naVec_get(curve, 2*i+1)).num); + } + + mgr->interpolate + ( + node, + "numeric", + value_nodes, + deltas, + "linear" + ); + + return naNil(); } // This is a better RNG than the one in the default Nasal distribution -- 2.39.5