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:
parent
e08eda18d5
commit
c9bbbd18ec
@ -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)
|
||||
|
197
simgear/props/PropertyInterpolationMgr.cxx
Normal file
197
simgear/props/PropertyInterpolationMgr.cxx
Normal 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
|
118
simgear/props/PropertyInterpolationMgr.hxx
Normal file
118
simgear/props/PropertyInterpolationMgr.hxx
Normal 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_ */
|
104
simgear/props/PropertyInterpolator.cxx
Normal file
104
simgear/props/PropertyInterpolator.cxx
Normal 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
|
118
simgear/props/PropertyInterpolator.hxx
Normal file
118
simgear/props/PropertyInterpolator.hxx
Normal 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_ */
|
243
simgear/props/easing_functions.cxx
Normal file
243
simgear/props/easing_functions.cxx
Normal 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
|
35
simgear/props/easing_functions.hxx
Normal file
35
simgear/props/easing_functions.hxx
Normal 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_ */
|
54
simgear/props/easing_functions_test.cxx
Normal file
54
simgear/props/easing_functions_test.cxx
Normal 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;
|
||||
}
|
@ -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
|
||||
|
74
simgear/scene/util/ColorInterpolator.cxx
Normal file
74
simgear/scene/util/ColorInterpolator.cxx
Normal 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
|
49
simgear/scene/util/ColorInterpolator.hxx
Normal file
49
simgear/scene/util/ColorInterpolator.hxx
Normal 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_ */
|
@ -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;
|
||||
}
|
||||
|
@ -21,3 +21,4 @@
|
||||
|
||||
#cmakedefine SYSTEM_EXPAT
|
||||
#cmakedefine ENABLE_SOUND
|
||||
#cmakedefine SIMGEAR_HEADLESS
|
Loading…
Reference in New Issue
Block a user