set(HEADERS
AtomicChangeListener.hxx
+ condition.hxx
+ easing_functions.hxx
ExtendedPropertyAdapter.hxx
PropertyBasedElement.hxx
PropertyBasedMgr.hxx
- condition.hxx
+ PropertyInterpolationMgr.hxx
+ PropertyInterpolator.hxx
propertyObject.hxx
props.hxx
props_io.hxx
set(SOURCES
AtomicChangeListener.cxx
+ condition.cxx
+ easing_functions.cxx
PropertyBasedElement.cxx
PropertyBasedMgr.cxx
- condition.cxx
+ PropertyInterpolationMgr.cxx
+ PropertyInterpolator.cxx
propertyObject.cxx
props.cxx
props_io.cxx
add_executable(test_propertyObject propertyObject_test.cxx)
target_link_libraries(test_propertyObject ${TEST_LIBS})
add_test(test_propertyObject ${EXECUTABLE_OUTPUT_PATH}/test_propertyObject)
+
+add_executable(test_easing_functions easing_functions_test.cxx)
+target_link_libraries(test_easing_functions ${TEST_LIBS})
+add_test(test_easing_functions ${EXECUTABLE_OUTPUT_PATH}/test_easing_functions)
+
endif(ENABLE_TESTS)
--- /dev/null
+// Subsystem that manages interpolation of properties.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "PropertyInterpolationMgr.hxx"
+#include "PropertyInterpolator.hxx"
+
+#ifndef SIMGEAR_HEADLESS
+# include <simgear/scene/util/ColorInterpolator.hxx>
+#endif
+
+#include <simgear/props/props.hxx>
+#include <simgear_config.h>
+
+#include <algorithm>
+
+namespace simgear
+{
+
+ //----------------------------------------------------------------------------
+ PropertyInterpolationMgr::PropertyInterpolationMgr()
+ {
+ addInterpolatorFactory<NumericInterpolator>("numeric");
+#ifndef SIMGEAR_HEADLESS
+ addInterpolatorFactory<ColorInterpolator>("color");
+#endif
+
+ for( size_t i = 0; easing_functions[i].name; ++i )
+ addEasingFunction
+ (
+ easing_functions[i].name,
+ easing_functions[i].func
+ );
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolationMgr::update(double dt)
+ {
+ for( InterpolatorList::iterator it = _interpolators.begin();
+ it != _interpolators.end();
+ ++it )
+ {
+ for(double unused_time = dt;;)
+ {
+ PropertyInterpolatorRef interp = it->second;
+ unused_time = interp->update(it->first, unused_time);
+
+ if( unused_time <= 0.0 )
+ // No time left for next animation
+ break;
+
+ if( interp->_next )
+ {
+ // Step to next animation. Note that we do not invalidate or delete
+ // the current interpolator to allow for looped animations.
+ it->second = interp->_next;
+ }
+ else
+ {
+ // No more animations so just remove it
+ it = _interpolators.erase(it);
+ break;
+ }
+ }
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ struct PropertyInterpolationMgr::PredicateIsSameProp
+ {
+ public:
+ PredicateIsSameProp(SGPropertyNode* node):
+ _node(node)
+ {}
+ bool operator()(const PropertyInterpolatorPair& interp) const
+ {
+ return interp.first == _node;
+ }
+ protected:
+ SGPropertyNode *_node;
+ };
+
+ //----------------------------------------------------------------------------
+ PropertyInterpolatorRef
+ PropertyInterpolationMgr::createInterpolator( const std::string& type,
+ const SGPropertyNode* target,
+ double duration,
+ const std::string& easing )
+ {
+ InterpolatorFactoryMap::iterator interpolator_factory =
+ _interpolator_factories.find(type);
+ if( interpolator_factory == _interpolator_factories.end() )
+ {
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "PropertyInterpolationMgr: no factory found for type '" << type << "'"
+ );
+ return 0;
+ }
+
+ EasingFunctionMap::iterator easing_func = _easing_functions.find(easing);
+ if( easing_func == _easing_functions.end() )
+ {
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "PropertyInterpolationMgr: no such easing '" << type << "'"
+ );
+ return 0;
+ }
+
+ PropertyInterpolatorRef interp;
+ interp = (*interpolator_factory->second)(target);
+ interp->_type = type;
+ interp->_duration = duration;
+ interp->_easing = easing_func->second;
+
+ return interp;
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolationMgr::interpolate( SGPropertyNode* prop,
+ PropertyInterpolatorRef interp )
+ {
+ // Search for active interpolator on given property
+ InterpolatorList::iterator it = std::find_if
+ (
+ _interpolators.begin(),
+ _interpolators.end(),
+ PredicateIsSameProp(prop)
+ );
+
+ if( it != _interpolators.end() )
+ {
+ // Ensure no circular reference is left
+ it->second->_next = 0;
+
+ // and now safely replace old interpolator
+ // TODO maybe cache somewhere for reuse or use allocator?
+ it->second = interp;
+ }
+ else
+ _interpolators.push_front( std::make_pair(prop, interp) );
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolationMgr::addInterpolatorFactory
+ (
+ const std::string& type,
+ InterpolatorFactory factory
+ )
+ {
+ if( _interpolator_factories.find(type) != _interpolator_factories.end() )
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "PropertyInterpolationMgr: replace existing factor for type " << type
+ );
+
+ _interpolator_factories[type] = factory;
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolationMgr::addEasingFunction( const std::string& type,
+ easing_func_t func )
+ {
+ // TODO it's probably time for a generic factory map
+ if( _easing_functions.find(type) != _easing_functions.end() )
+ SG_LOG
+ (
+ SG_GENERAL,
+ SG_WARN,
+ "PropertyInterpolationMgr: replace existing easing function " << type
+ );
+
+ _easing_functions[type] = func;
+ }
+
+} // namespace simgear
--- /dev/null
+// Subsystem that manages interpolation of properties.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_PROPERTY_INTERPOLATION_MGR_HXX_
+#define SG_PROPERTY_INTERPOLATION_MGR_HXX_
+
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/props/PropertyInterpolator.hxx>
+
+#include <list>
+
+namespace simgear
+{
+
+ /**
+ * Subsystem that manages interpolation of properties.
+ *
+ * By default the numeric values of the properties are interpolated. For
+ * example, for strings this is probably not the wanted behavior. For this
+ * adapter classes can be registered to allow providing specific
+ * interpolations for certain types of properties. Using the type "color",
+ * provided by ColorInterpolator, strings containing %CSS colors can also be
+ * interpolated.
+ *
+ * Additionally different functions can be used for easing of the animation.
+ * By default "linear" (constant animation speed) and "swing" (smooth
+ * acceleration and deceleration) are available.
+ */
+ class PropertyInterpolationMgr:
+ public SGSubsystem
+ {
+ public:
+ typedef PropertyInterpolator*
+ (*InterpolatorFactory)(const SGPropertyNode* target);
+
+ PropertyInterpolationMgr();
+
+ /**
+ * Update all active interpolators.
+ */
+ void update(double dt);
+
+ /**
+ * Create a new property interpolator.
+ *
+ * @note To actually use it the interpolator needs to be attached to a
+ * property using PropertyInterpolationMgr::interpolate.
+ *
+ * @param type Type of animation ("numeric", "color", etc.)
+ * @param target Property containing target value
+ * @param duration Duration if the animation (in seconds)
+ * @param easing Type of easing ("linear", "swing", etc.)
+ */
+ PropertyInterpolatorRef
+ createInterpolator( const std::string& type,
+ const SGPropertyNode* target,
+ double duration = 1.0,
+ const std::string& easing = "swing" );
+
+ /**
+ * Add animation of the given property from current its current value to
+ * the target value of the interpolator.
+ *
+ * @param prop Property to be interpolated
+ * @param interp Interpolator used for interpolation
+ */
+ void interpolate( SGPropertyNode* prop,
+ PropertyInterpolatorRef interp );
+
+ /**
+ * Register factory for interpolation type.
+ */
+ void addInterpolatorFactory( const std::string& type,
+ InterpolatorFactory factory );
+ template<class T>
+ void addInterpolatorFactory(const std::string& type)
+ {
+ addInterpolatorFactory(type, &PropertyInterpolator::create<T>);
+ }
+
+ /**
+ * Register easing function.
+ */
+ void addEasingFunction(const std::string& type, easing_func_t func);
+
+ protected:
+
+ typedef std::map<std::string, InterpolatorFactory> InterpolatorFactoryMap;
+ typedef std::map<std::string, easing_func_t> EasingFunctionMap;
+ typedef std::pair< SGPropertyNode*,
+ PropertyInterpolatorRef > PropertyInterpolatorPair;
+ typedef std::list<PropertyInterpolatorPair> InterpolatorList;
+
+ struct PredicateIsSameProp;
+
+ InterpolatorFactoryMap _interpolator_factories;
+ EasingFunctionMap _easing_functions;
+ InterpolatorList _interpolators;
+ };
+
+} // namespace simgear
+
+#endif /* SG_PROPERTY_INTERPOLATION_MGR_HXX_ */
--- /dev/null
+// Adapter for interpolating different types of properties.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "PropertyInterpolator.hxx"
+#include "props.hxx"
+
+#include <cassert>
+#include <cmath>
+
+namespace simgear
+{
+
+ //----------------------------------------------------------------------------
+ PropertyInterpolator::~PropertyInterpolator()
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolator::reset(const SGPropertyNode* target)
+ {
+ _cur_t = 0;
+ setTarget(target);
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyInterpolator::setEasingFunction(easing_func_t easing)
+ {
+ _easing = easing ? easing : easing_functions[0].func;
+ }
+
+ //----------------------------------------------------------------------------
+ double PropertyInterpolator::update(SGPropertyNode* prop, double dt)
+ {
+ if( _cur_t == 0 )
+ init(prop);
+
+ _cur_t += dt / _duration;
+
+ double unused = _cur_t - 1;
+ if( unused > 0 )
+ _cur_t = 1;
+
+ write(prop, _easing(_cur_t) );
+
+ if( _cur_t == 1 )
+ // Reset timer to allow animation to be run again.
+ _cur_t = 0;
+
+ return unused;
+ }
+
+ //----------------------------------------------------------------------------
+ PropertyInterpolator::PropertyInterpolator():
+ _duration(1),
+ _cur_t(-1)
+ {
+ setEasingFunction(0);
+ }
+
+ //----------------------------------------------------------------------------
+ void NumericInterpolator::setTarget(const SGPropertyNode* target)
+ {
+ _end = target->getDoubleValue();
+ }
+
+ //----------------------------------------------------------------------------
+ void NumericInterpolator::init(const SGPropertyNode* prop)
+ {
+ // If unable to get start value, immediately change to target value
+ double value_start = prop->getType() == props::NONE
+ ? _end
+ : prop->getDoubleValue();
+
+ _diff = _end - value_start;
+ }
+
+ //----------------------------------------------------------------------------
+ void NumericInterpolator::write(SGPropertyNode* prop, double t)
+ {
+ double cur = _end - (1 - t) * _diff;
+
+ if( prop->getType() == props::INT || prop->getType() == props::LONG )
+ prop->setLongValue( static_cast<long>(std::floor(cur + 0.5)) );
+ else
+ prop->setDoubleValue(cur);
+ }
+
+} // namespace simgear
--- /dev/null
+// Adapter for interpolating different types of properties.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_PROPERTY_INTERPOLATOR_HXX_
+#define SG_PROPERTY_INTERPOLATOR_HXX_
+
+#include "easing_functions.hxx"
+#include "propsfwd.hxx"
+
+#include <memory>
+#include <cassert>
+#include <string>
+
+#include <iostream>
+
+namespace simgear
+{
+
+ class PropertyInterpolator;
+ typedef SGSharedPtr<PropertyInterpolator> PropertyInterpolatorRef;
+
+ /**
+ * Base class for interpolating different types of properties over time.
+ */
+ class PropertyInterpolator:
+ public SGReferenced
+ {
+ public:
+ virtual ~PropertyInterpolator();
+
+ /**
+ * Resets animation timer to zero and prepares for interpolation to new
+ * target value.
+ */
+ void reset(const SGPropertyNode* target);
+
+ /**
+ * Set easing function to be used for interpolation.
+ */
+ void setEasingFunction(easing_func_t easing);
+
+ /**
+ * Calculate an animation step.
+ *
+ * @param prop Property being animated
+ * @param dt Current frame duration
+ * @return Time not used by the animation (>= 0 if animation has finished,
+ * else time is negative indicating the remaining time until
+ * finished)
+ */
+ double update(SGPropertyNode* prop, double dt);
+
+ const std::string& getType() const { return _type; }
+
+ /**
+ * Create new animation for given property.
+ *
+ * @param prop Property to be animated
+ * @param target Property containing target value
+ */
+ template<class Derived>
+ static PropertyInterpolator* create(const SGPropertyNode* target)
+ {
+ assert(target);
+
+ PropertyInterpolator* interp = new Derived;
+ interp->reset(target);
+
+ return interp;
+ }
+
+ protected:
+ friend class PropertyInterpolationMgr;
+
+ std::string _type;
+ easing_func_t _easing;
+ PropertyInterpolatorRef _next;
+ double _duration,
+ _cur_t;
+
+ PropertyInterpolator();
+
+ virtual void setTarget(const SGPropertyNode* target) = 0;
+ virtual void init(const SGPropertyNode* prop) = 0;
+ virtual void write(SGPropertyNode* prop, double t) = 0;
+ };
+
+ class NumericInterpolator:
+ public PropertyInterpolator
+ {
+ protected:
+ double _end,
+ _diff;
+
+ virtual void setTarget(const SGPropertyNode* target);
+ virtual void init(const SGPropertyNode* prop);
+ virtual void write(SGPropertyNode* prop, double t);
+ };
+
+} // namespace simgear
+
+
+#endif /* SG_PROPERTY_INTERPOLATOR_HXX_ */
--- /dev/null
+///@file
+/// Easing functions for property interpolation.
+///
+/// Based on easing functions by Robert Penner
+/// (http://www.robertpenner.com/easing)
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "easing_functions.hxx"
+#include <cmath>
+#include <cstddef>
+
+namespace simgear
+{
+ // TODO move somewhere to math
+ template<size_t N>
+ double pow(double base)
+ {
+ return base * pow<N - 1>(base);
+ }
+
+ template<>
+ double pow<0>(double)
+ {
+ return 1.0;
+ }
+
+ /// Simple linear easing.
+ double easingLinear(double t)
+ {
+ return t;
+ }
+
+ /// http://easings.net/#easeInSine
+ double easeInSine(double t)
+ {
+ return 1 - std::cos(t * M_PI_2);
+ }
+
+ /// http://easings.net/#easeOutSine
+ double easeOutSine(double t)
+ {
+ return std::sin(t * M_PI_2);
+ }
+
+ /// http://easings.net/#easeInOutSine
+ double easeInOutSine(double t)
+ {
+ return 0.5 - 0.5 * std::cos(t * M_PI);
+ }
+
+ template<easing_func_t easeIn, easing_func_t easeOut>
+ double easeInOut(double t)
+ {
+ if( (t *= 2) < 1 )
+ return 0.5 * (*easeIn)(t);
+ else
+ return 0.5 + 0.5 * (*easeOut)(t - 1);
+ }
+
+ template<size_t N, bool is_odd>
+ struct easeOutImpl;
+
+ /// http://easings.net/#easeOutCubic (N = 3)
+ /// http://easings.net/#easeOutQuint (N = 5)
+ template<size_t N>
+ struct easeOutImpl<N, true>
+ {
+ static double calc(double t)
+ {
+ return pow<N>(t - 1) + 1;
+ }
+ };
+
+ /// http://easings.net/#easeOutQuad (N = 2)
+ /// http://easings.net/#easeOutQuart (N = 4)
+ template<size_t N>
+ struct easeOutImpl<N, false>
+ {
+ static double calc(double t)
+ {
+ return -pow<N>(t - 1) + 1;
+ }
+ };
+
+ /// http://easings.net/#easeOutQuad (N = 2)
+ /// http://easings.net/#easeOutCubic (N = 3)
+ /// http://easings.net/#easeOutQuart (N = 4)
+ /// http://easings.net/#easeOutQuint (N = 5)
+ template<size_t N>
+ double easeOutPow(double t)
+ {
+ return easeOutImpl<N, N & 1>::calc(t);
+ }
+
+ /// http://easings.net/#easeInOutQuad (N = 2)
+ /// http://easings.net/#easeInOutCubic (N = 3)
+ /// http://easings.net/#easeInOutQuart (N = 4)
+ /// http://easings.net/#easeInOutQuint (N = 5)
+ template<size_t N>
+ double easeInOutPow(double t)
+ {
+ return easeInOut<&pow<N>, &easeOutPow<N> >(t);
+ }
+
+ /// http://easings.net/#easeInExpo
+ double easeInExpo(double t)
+ {
+ return (t == 0) ? 0 : std::pow(2, 10 * (t - 1));
+ }
+
+ /// http://easings.net/#easeOutExpo
+ double easeOutExpo(double t)
+ {
+ return (t == 1) ? 1 : 1 - std::pow(2, -10 * t);
+ }
+
+ /// http://easings.net/#easeInCirc
+ double easeInCirc(double t)
+ {
+ return 1 - std::sqrt(1 - pow<2>(t));
+ }
+
+ /// http://easings.net/#easeOutCirc
+ double easeOutCirc(double t)
+ {
+ return std::sqrt(1 - pow<2>(t - 1));
+ }
+
+ static const double ease_s = 1.70158;
+
+ /// http://easings.net/#easeInBack
+ double easeInBack(double t)
+ {
+
+ return pow<2>(t) * ((ease_s + 1) * t - ease_s);
+ }
+
+ /// http://easings.net/#easeOutBack
+ double easeOutBack(double t)
+ {
+ t -= 1;
+ return pow<2>(t) * ((ease_s + 1) * t + ease_s) + 1;
+ }
+
+ /// http://easings.net/#easeOutBack
+ double easeInElastic(double t)
+ {
+ if( t == 0 )
+ return 0;
+ if( t == 1 )
+ return 1;
+
+ t -= 1;
+ const double p = .3;
+ const double s = p * 0.25;
+
+ return -std::pow(2, 10 * t) * std::sin((t - s) * 2 * M_PI / p);
+ }
+
+ /// http://easings.net/#easeOutBack
+ double easeOutElastic(double t)
+ {
+ if( t == 0 )
+ return 0;
+ if( t == 1 )
+ return 1;
+
+ const double p = .3;
+ const double s = p * 0.25;
+
+ return std::pow(2, -10 * t) * std::sin((t - s) * 2 * M_PI / p) + 1;
+ }
+
+ /// http://easings.net/#easeOutBounce
+ double easeOutBounce(double t)
+ {
+ if( t < 1/2.75 )
+ return 7.5625 * pow<2>(t);
+ else if( t < 2/2.75 )
+ return 7.5625 * pow<2>(t - 1.5/2.75) + .75;
+ else if( t < 2.5/2.75 )
+ return 7.5625 * pow<2>(t - 2.25/2.75) + .9375;
+ else
+ return 7.5625 * pow<2>(t - 2.625/2.75) + .984375;
+ }
+
+ /// http://easings.net/#easeInBounce
+ double easeInBounce(double time)
+ {
+ return 1 - easeOutBounce(1 - time);
+ }
+
+#define SG_ADD_EASING(name) {#name, &name},
+#define SG_STR(str) #str
+#define SG_ADD_EASING_IN_OUT(name)\
+ SG_ADD_EASING(easeIn##name)\
+ SG_ADD_EASING(easeOut##name)\
+ {SG_STR(easeInOut##name), &easeInOut<&easeIn##name, &easeOut##name>},
+
+ const EasingMapEntry easing_functions[] = {
+ {"linear", &easingLinear},
+ {"swing", &easeInOutSine},
+ SG_ADD_EASING_IN_OUT(Sine)
+ {"easeInQuad", &pow<2>},
+ {"easeInCubic", &pow<3>},
+ {"easeInQuart", &pow<4>},
+ {"easeInQuint", &pow<5>},
+ {"easeOutQuad", &easeOutPow<2>},
+ {"easeOutCubic", &easeOutPow<3>},
+ {"easeOutQuart", &easeOutPow<4>},
+ {"easeOutQuint", &easeOutPow<5>},
+ {"easeInOutQuad", &easeInOutPow<2>},
+ {"easeInOutCubic",&easeInOutPow<3>},
+ {"easeInOutQuart",&easeInOutPow<4>},
+ {"easeInOutQuint",&easeInOutPow<5>},
+ SG_ADD_EASING_IN_OUT(Expo)
+ SG_ADD_EASING_IN_OUT(Circ)
+ SG_ADD_EASING_IN_OUT(Back)
+ SG_ADD_EASING_IN_OUT(Elastic)
+ SG_ADD_EASING_IN_OUT(Bounce)
+ {0, 0}
+ };
+
+#undef SG_ADD_EASING
+#undef SG_STR
+#undef SG_ADD_EASING_IN_OUT
+
+} // namespace simgear
--- /dev/null
+// Easing functions for property interpolation.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_EASING_HXX_
+#define SG_EASING_HXX_
+
+namespace simgear
+{
+
+ typedef double (*easing_func_t)(double);
+ struct EasingMapEntry { const char* name; easing_func_t func; };
+
+ /**
+ * List of all available easing functions and their names.
+ */
+ extern const EasingMapEntry easing_functions[];
+
+} // namespace simgear
+
+#endif /* SG_EASING_HXX_ */
--- /dev/null
+/*
+ * easing_functions_test.cxx
+ *
+ * Output values of all easing functions for plotting and some simple tests.
+ *
+ * Created on: 15.03.2013
+ * Author: tom
+ */
+
+#include "easing_functions.hxx"
+#include <cmath>
+#include <iostream>
+
+#define VERIFY_CLOSE(a, b) \
+ if( std::fabs(a - b) > 1e-5 ) \
+ { \
+ std::cerr << "failed: line " << __LINE__ << ": "\
+ << a << " != " << b\
+ << std::endl; \
+ return 1; \
+ }
+
+int main(int argc, char* argv[])
+{
+ using simgear::easing_functions;
+
+ for( double t = 0; t <= 1; t += 1/32. )
+ {
+ if( t == 0 )
+ {
+ for( size_t i = 0; easing_functions[i].name; ++i )
+ std::cout << easing_functions[i].name << " ";
+ std::cout << '\n';
+ }
+
+ for( size_t i = 0; easing_functions[i].name; ++i )
+ {
+ double val = (*easing_functions[i].func)(t);
+ std::cout << val << " ";
+
+ if( t == 0 )
+ {
+ VERIFY_CLOSE(val, 0)
+ }
+ else if( t == 1 )
+ {
+ VERIFY_CLOSE(val, 1)
+ }
+ }
+ std::cout << '\n';
+ }
+
+ return 0;
+}
include (SimGearComponent)
-set(HEADERS
+set(HEADERS
+ ColorInterpolator.hxx
CopyOp.hxx
DeletionManager.hxx
NodeAndDrawableVisitor.hxx
project.hxx
)
-set(SOURCES
+set(SOURCES
+ ColorInterpolator.cxx
CopyOp.cxx
DeletionManager.cxx
NodeAndDrawableVisitor.cxx
--- /dev/null
+// Adapter for interpolating string properties representing CSS colors.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#include "ColorInterpolator.hxx"
+#include "parse_color.hxx"
+
+#include <simgear/props/props.hxx>
+
+namespace simgear
+{
+
+ //----------------------------------------------------------------------------
+ void ColorInterpolator::setTarget(const SGPropertyNode* target)
+ {
+ if( !parseColor(target->getStringValue(), _color_end) )
+ SG_LOG
+ (
+ SG_GENERAL, SG_WARN, "ColorInterpolator: failed to parse end color."
+ );
+ }
+
+ //----------------------------------------------------------------------------
+ void ColorInterpolator::init(const SGPropertyNode* prop)
+ {
+ osg::Vec4 color_start;
+ if( !parseColor(prop->getStringValue(), color_start) )
+ // If unable to get current color, immediately change to target color
+ color_start = _color_end;
+
+ _color_diff = _color_end - color_start;
+ }
+
+ //----------------------------------------------------------------------------
+ void ColorInterpolator::write(SGPropertyNode* prop, double t)
+ {
+ osg::Vec4 color_cur = _color_end - _color_diff * (1 - t);
+ bool has_alpha = color_cur.a() < 0.999;
+
+ std::ostringstream strm;
+ strm << (has_alpha ? "rgba(" : "rgb(");
+
+ // r, g, b (every component in [0, 255])
+ for(size_t i = 0; i < 3; ++i)
+ {
+ if( i > 0 )
+ strm << ',';
+ strm << static_cast<int>(color_cur._v[i] * 255);
+ }
+
+ // Write alpha if a < 1 (alpha is in [0, 1])
+ if( has_alpha )
+ strm << ',' << color_cur.a();
+
+ strm << ')';
+
+ prop->setStringValue(strm.str());
+ }
+
+} // namespace simgear
--- /dev/null
+// Adapter for interpolating string properties representing CSS colors.
+//
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+#ifndef SG_COLOR_INTERPOLATOR_HXX_
+#define SG_COLOR_INTERPOLATOR_HXX_
+
+#include <simgear/props/PropertyInterpolator.hxx>
+
+#include <osg/Vec4>
+#include <string>
+
+namespace simgear
+{
+
+ /**
+ * Interpolate a string property containing containing a %CSS color.
+ */
+ class ColorInterpolator:
+ public PropertyInterpolator
+ {
+ protected:
+ osg::Vec4 _color_end,
+ _color_diff;
+
+ virtual void setTarget(const SGPropertyNode* target);
+ virtual void init(const SGPropertyNode* prop);
+ virtual void write(SGPropertyNode* prop, double t);
+
+
+ };
+
+} // namespace simgear
+
+#endif /* SG_COLOR_INTERPOLATOR_HXX_ */
#include <simgear/compiler.h>
#include "parse_color.hxx"
+#include "ColorInterpolator.hxx"
+#include <simgear/props/props.hxx>
#include <iostream>
#define COMPARE(a, b) \
if( (a) != (b) ) \
{ \
- std::cerr << "failed:" << #a << " != " << #b << std::endl; \
+ std::cerr << "line " << __LINE__ << ": failed: "\
+ << #a << " != " << #b << std::endl; \
return 1; \
}
#define VERIFY(a) \
if( !(a) ) \
{ \
- std::cerr << "failed:" << #a << std::endl; \
+ std::cerr << "line " << __LINE__ << ": failed: "\
+ << #a << std::endl; \
return 1; \
}
#define VERIFY_COLOR(str, r, g, b, a) \
VERIFY(simgear::parseColor(str, color)) \
COMPARE(color, osg::Vec4(r, g, b, a))
+
+#define VERIFY_NODE_STR(node, str) \
+ COMPARE(node.getStringValue(), std::string(str))
int main (int ac, char ** av)
{
VERIFY_COLOR("#0000ff", 0,0,1,1);
VERIFY_COLOR("rgb( 255,\t127.5,0)", 1, 0.5, 0, 1);
VERIFY_COLOR("rgba(255, 127.5,0, 0.5)", 1, 0.5, 0, 0.5);
+
+ SGPropertyNode color_node, color_arg;
+ color_arg.setStringValue("#000000");
+
+ simgear::PropertyInterpolator* interp =
+ simgear::PropertyInterpolator
+ ::create<simgear::ColorInterpolator>(&color_arg);
+
+ interp->update(&color_node, 0.5); // with no color it should immediately set to the target
+ VERIFY_NODE_STR(color_node, "rgb(0,0,0)");
+
+ color_arg.setStringValue("rgba(255,0,0,0.5)");
+ interp->reset(&color_arg);
+
+ interp->update(&color_node, 0.5);
+ VERIFY_NODE_STR(color_node, "rgba(127,0,0,0.75)");
+
+ interp->update(&color_node, 0.5);
+ VERIFY_NODE_STR(color_node, "rgba(255,0,0,0.5)");
+
+ // Animation has already completed and therefore should be reset and start a
+ // new animation starting with the current value of the animation. As this
+ // is already the same as the target value, nothing should change.
+ interp->update(&color_node, 0.5);
+ VERIFY_NODE_STR(color_node, "rgba(255,0,0,0.5)");
+
+ color_arg.setStringValue("#00ff00");
+ interp->reset(&color_arg);
+ interp->update(&color_node, 1.0);
+ VERIFY_NODE_STR(color_node, "rgb(0,255,0)");
+
std::cout << "all tests passed successfully!" << std::endl;
return 0;
}
#cmakedefine SYSTEM_EXPAT
#cmakedefine ENABLE_SOUND
+#cmakedefine SIMGEAR_HEADLESS
\ No newline at end of file