New interpolation/animation system.

Inspired by jQuery.animate() properties can be interpolated using
different easing functions and specifying an animation duration.
Additionally animations can be chained to get table-based
animations like with the current SGInterpolator, or also create
looped animations or other more complicated curves.

Currently this system is not used yet, but it is intended to
replace SGInterpolator and allow more advanced animations of
eg. also colors, for example, for the canvas.
This commit is contained in:
Thomas Geymayer 2013-03-15 23:37:17 +01:00
parent e08eda18d5
commit c9bbbd18ec
13 changed files with 1050 additions and 6 deletions

View File

@ -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)

View File

@ -0,0 +1,197 @@
// 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

View File

@ -0,0 +1,118 @@
// 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_ */

View File

@ -0,0 +1,104 @@
// 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

View File

@ -0,0 +1,118 @@
// 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_ */

View File

@ -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 <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

View File

@ -0,0 +1,35 @@
// 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_ */

View File

@ -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 <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;
}

View File

@ -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

View File

@ -0,0 +1,74 @@
// 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

View File

@ -0,0 +1,49 @@
// 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_ */

View File

@ -1,26 +1,33 @@
#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)
{
@ -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<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;
}

View File

@ -21,3 +21,4 @@
#cmakedefine SYSTEM_EXPAT
#cmakedefine ENABLE_SOUND
#cmakedefine SIMGEAR_HEADLESS