diff --git a/simgear/props/CMakeLists.txt b/simgear/props/CMakeLists.txt index c770f78f..993a303e 100644 --- a/simgear/props/CMakeLists.txt +++ b/simgear/props/CMakeLists.txt @@ -3,10 +3,13 @@ include (SimGearComponent) 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 @@ -17,9 +20,12 @@ set(HEADERS 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 @@ -36,4 +42,9 @@ add_test(test_props ${EXECUTABLE_OUTPUT_PATH}/test_props) 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) diff --git a/simgear/props/PropertyInterpolationMgr.cxx b/simgear/props/PropertyInterpolationMgr.cxx new file mode 100644 index 00000000..4f217a78 --- /dev/null +++ b/simgear/props/PropertyInterpolationMgr.cxx @@ -0,0 +1,197 @@ +// Subsystem that manages interpolation of properties. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#endif + +#include +#include + +#include + +namespace simgear +{ + + //---------------------------------------------------------------------------- + PropertyInterpolationMgr::PropertyInterpolationMgr() + { + addInterpolatorFactory("numeric"); +#ifndef SIMGEAR_HEADLESS + addInterpolatorFactory("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 diff --git a/simgear/props/PropertyInterpolationMgr.hxx b/simgear/props/PropertyInterpolationMgr.hxx new file mode 100644 index 00000000..4271a9f0 --- /dev/null +++ b/simgear/props/PropertyInterpolationMgr.hxx @@ -0,0 +1,118 @@ +// Subsystem that manages interpolation of properties. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include + +#include + +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 + void addInterpolatorFactory(const std::string& type) + { + addInterpolatorFactory(type, &PropertyInterpolator::create); + } + + /** + * Register easing function. + */ + void addEasingFunction(const std::string& type, easing_func_t func); + + protected: + + typedef std::map InterpolatorFactoryMap; + typedef std::map EasingFunctionMap; + typedef std::pair< SGPropertyNode*, + PropertyInterpolatorRef > PropertyInterpolatorPair; + typedef std::list InterpolatorList; + + struct PredicateIsSameProp; + + InterpolatorFactoryMap _interpolator_factories; + EasingFunctionMap _easing_functions; + InterpolatorList _interpolators; + }; + +} // namespace simgear + +#endif /* SG_PROPERTY_INTERPOLATION_MGR_HXX_ */ diff --git a/simgear/props/PropertyInterpolator.cxx b/simgear/props/PropertyInterpolator.cxx new file mode 100644 index 00000000..19c30179 --- /dev/null +++ b/simgear/props/PropertyInterpolator.cxx @@ -0,0 +1,104 @@ +// Adapter for interpolating different types of properties. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include + +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(std::floor(cur + 0.5)) ); + else + prop->setDoubleValue(cur); + } + +} // namespace simgear diff --git a/simgear/props/PropertyInterpolator.hxx b/simgear/props/PropertyInterpolator.hxx new file mode 100644 index 00000000..7936acbb --- /dev/null +++ b/simgear/props/PropertyInterpolator.hxx @@ -0,0 +1,118 @@ +// Adapter for interpolating different types of properties. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include +#include + +#include + +namespace simgear +{ + + class PropertyInterpolator; + typedef SGSharedPtr 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 + 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_ */ diff --git a/simgear/props/easing_functions.cxx b/simgear/props/easing_functions.cxx new file mode 100644 index 00000000..1a8320ef --- /dev/null +++ b/simgear/props/easing_functions.cxx @@ -0,0 +1,243 @@ +///@file +/// Easing functions for property interpolation. +/// +/// Based on easing functions by Robert Penner +/// (http://www.robertpenner.com/easing) +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 +#include + +namespace simgear +{ + // TODO move somewhere to math + template + double pow(double base) + { + return base * pow(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 + double easeInOut(double t) + { + if( (t *= 2) < 1 ) + return 0.5 * (*easeIn)(t); + else + return 0.5 + 0.5 * (*easeOut)(t - 1); + } + + template + struct easeOutImpl; + + /// http://easings.net/#easeOutCubic (N = 3) + /// http://easings.net/#easeOutQuint (N = 5) + template + struct easeOutImpl + { + static double calc(double t) + { + return pow(t - 1) + 1; + } + }; + + /// http://easings.net/#easeOutQuad (N = 2) + /// http://easings.net/#easeOutQuart (N = 4) + template + struct easeOutImpl + { + static double calc(double t) + { + return -pow(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 + double easeOutPow(double t) + { + return easeOutImpl::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 + double easeInOutPow(double t) + { + return easeInOut<&pow, &easeOutPow >(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 diff --git a/simgear/props/easing_functions.hxx b/simgear/props/easing_functions.hxx new file mode 100644 index 00000000..f66e4ab1 --- /dev/null +++ b/simgear/props/easing_functions.hxx @@ -0,0 +1,35 @@ +// Easing functions for property interpolation. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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_ */ diff --git a/simgear/props/easing_functions_test.cxx b/simgear/props/easing_functions_test.cxx new file mode 100644 index 00000000..f4862931 --- /dev/null +++ b/simgear/props/easing_functions_test.cxx @@ -0,0 +1,54 @@ +/* + * 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 +#include + +#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; +} diff --git a/simgear/scene/util/CMakeLists.txt b/simgear/scene/util/CMakeLists.txt index 02d7766d..5ee97f85 100644 --- a/simgear/scene/util/CMakeLists.txt +++ b/simgear/scene/util/CMakeLists.txt @@ -1,6 +1,7 @@ include (SimGearComponent) -set(HEADERS +set(HEADERS + ColorInterpolator.hxx CopyOp.hxx DeletionManager.hxx NodeAndDrawableVisitor.hxx @@ -29,7 +30,8 @@ set(HEADERS project.hxx ) -set(SOURCES +set(SOURCES + ColorInterpolator.cxx CopyOp.cxx DeletionManager.cxx NodeAndDrawableVisitor.cxx diff --git a/simgear/scene/util/ColorInterpolator.cxx b/simgear/scene/util/ColorInterpolator.cxx new file mode 100644 index 00000000..6abe2e11 --- /dev/null +++ b/simgear/scene/util/ColorInterpolator.cxx @@ -0,0 +1,74 @@ +// Adapter for interpolating string properties representing CSS colors. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 + +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(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 diff --git a/simgear/scene/util/ColorInterpolator.hxx b/simgear/scene/util/ColorInterpolator.hxx new file mode 100644 index 00000000..0fad5659 --- /dev/null +++ b/simgear/scene/util/ColorInterpolator.hxx @@ -0,0 +1,49 @@ +// Adapter for interpolating string properties representing CSS colors. +// +// Copyright (C) 2013 Thomas Geymayer +// +// 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 + +#include +#include + +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_ */ diff --git a/simgear/scene/util/parse_color_test.cxx b/simgear/scene/util/parse_color_test.cxx index c41b2645..d5605bf2 100644 --- a/simgear/scene/util/parse_color_test.cxx +++ b/simgear/scene/util/parse_color_test.cxx @@ -1,26 +1,33 @@ #include #include "parse_color.hxx" +#include "ColorInterpolator.hxx" +#include #include #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) { @@ -30,6 +37,37 @@ 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(&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; } diff --git a/simgear/simgear_config_cmake.h.in b/simgear/simgear_config_cmake.h.in index bb6d092b..725556a6 100644 --- a/simgear/simgear_config_cmake.h.in +++ b/simgear/simgear_config_cmake.h.in @@ -21,3 +21,4 @@ #cmakedefine SYSTEM_EXPAT #cmakedefine ENABLE_SOUND +#cmakedefine SIMGEAR_HEADLESS \ No newline at end of file