1#include "ui_Timeline.h"
2#include <Gui/Timeline/Timeline.hpp>
10#include <Core/Animation/KeyFramedValueInterpolators.hpp>
11#include <Core/Utils/Log.hpp>
13#include <Engine/RadiumEngine.hpp>
14#include <Engine/Rendering/RenderObject.hpp>
15#include <Engine/Rendering/RenderObjectManager.hpp>
16#include <Engine/Scene/Component.hpp>
17#include <Engine/Scene/Entity.hpp>
18#include <Engine/Scene/SignalManager.hpp>
20#include <Gui/Timeline/HelpDialog.hpp>
22using namespace Ra::Core::Utils;
26Timeline::Timeline( QWidget* parent ) : QDialog( parent ), ui( new Ui::
Timeline ) {
30 ui->m_scrollArea->onDrawRuler( width() - 2 );
33 ui->frame_timescale->setScrollArea( ui->m_scrollArea );
34 ui->frame_selector->setTimelineUi( ui );
37 connect( ui->frame_selector,
38 &TimelineFrameSelector::cursorChanged,
40 &Timeline::updateKeyFrames );
41 connect( ui->frame_selector,
42 &TimelineFrameSelector::keyFrameAdded,
44 &Timeline::onAddingKeyFrame );
45 connect( ui->frame_selector,
46 &TimelineFrameSelector::keyFrameDeleted,
48 &Timeline::onRemovingKeyFrame );
49 connect( ui->frame_selector,
50 &TimelineFrameSelector::keyFrameChanged,
52 &Timeline::onChangingKeyFrame );
53 connect( ui->frame_selector,
54 &TimelineFrameSelector::keyFrameMoved,
56 &Timeline::onMovingKeyFrame );
57 connect( ui->frame_selector,
58 &TimelineFrameSelector::keyFramesMoved,
60 &Timeline::onMovingKeyFrames );
63 connect( ui->toolButton_playPause, &QToolButton::toggled,
this, &Timeline::playClicked );
65 ui->frame_selector, &TimelineFrameSelector::cursorChanged,
this, &Timeline::cursorChanged );
67 ui->frame_selector, &TimelineFrameSelector::startChanged,
this, &Timeline::startChanged );
68 connect( ui->frame_selector, &TimelineFrameSelector::endChanged,
this, &Timeline::endChanged );
69 connect( ui->frame_selector,
70 &TimelineFrameSelector::durationChanged,
72 &Timeline::durationChanged );
75 auto signalManager = Ra::Engine::RadiumEngine::getInstance()->getSignalManager();
77 auto& entityAddedObs = signalManager->getEntityCreatedNotifier();
78 m_entityAddObserverId =
80 auto it = std::find_if(
81 m_entityKeyFrames.begin(), m_entityKeyFrames.end(), [entry]( const auto& frames ) {
82 return entry.m_entity == frames.first;
84 if ( it != m_entityKeyFrames.end() ) { m_entityKeyFrames.erase( it ); }
87 auto& entityRemovedObs = signalManager->getEntityDestroyedNotifier();
88 m_entityRemoveObserverId =
91 m_componentKeyFrames.begin(),
92 m_componentKeyFrames.end(),
93 [entry](
const auto& frames ) { return entry.m_component == frames.first; } );
94 if ( it != m_componentKeyFrames.end() ) { m_componentKeyFrames.erase( it ); }
97 auto& roAddedObs = signalManager->getRenderObjectCreatedNotifier();
100 m_renderObjectKeyFrames.begin(),
101 m_renderObjectKeyFrames.end(),
102 [entry](
const auto& frames ) { return entry.m_roIndex == frames.first; } );
103 if ( it != m_renderObjectKeyFrames.end() ) { m_renderObjectKeyFrames.erase( it ); }
107void Timeline::detachFromEngine() {
109 if (
auto engine = Ra::Engine::RadiumEngine::getInstance() ) {
110 if (
auto signalManager = engine->getSignalManager() ) {
111 if ( m_entityAddObserverId != -1 ) {
112 auto& obs = signalManager->getEntityCreatedNotifier();
113 obs.detach( m_entityAddObserverId );
114 m_entityAddObserverId = -1;
116 if ( m_entityRemoveObserverId != -1 ) {
117 auto& obs = signalManager->getEntityDestroyedNotifier();
118 obs.detach( m_entityRemoveObserverId );
119 m_entityRemoveObserverId = -1;
121 if ( m_roAddObserverId ) {
122 auto& obs = signalManager->getRenderObjectCreatedNotifier();
123 obs.detach( m_roAddObserverId );
124 m_roAddObserverId = -1;
130Timeline::~Timeline() {
137 auto enableUI = [
this](
bool enable ) {
138 ui->comboBox_attribute->setEnabled( enable );
139 ui->toolButton_keyFrame->setEnabled( enable );
140 ui->m_nbKeyFramesSpin->setEnabled( enable );
141 ui->m_removeKeyFrameButton->setEnabled( enable );
142 ui->pushButton_editAttribute->setEnabled( enable );
145 ui->comboBox_attribute->blockSignals(
true );
146 ui->comboBox_attribute->clear();
147 ui->comboBox_attribute->blockSignals(
false );
154 ui->comboBox_attribute->addItem( QString( ( prefix + frame.
m_name ).c_str() ) );
155 if ( ui->comboBox_attribute->count() == 1 ) {
157 for (
const auto& t : times ) {
158 ui->frame_selector->onAddingKeyFrame( t,
false );
165#define REGISTER_KEYFRAMED_VALUES( map, key, prefix ) \
167 auto it = map.find( key ); \
168 if ( it != map.end() ) { \
169 for ( const auto& keyFramedValue : it->second ) { \
170 registerFrames( keyFramedValue, prefix ); \
175 if ( ent.
m_entity ==
nullptr ) {
return; }
177 REGISTER_KEYFRAMED_VALUES( m_entityKeyFrames, ent.
m_entity, entityName +
"::" );
180 enableUI( ui->comboBox_attribute->count() > 0 );
184 REGISTER_KEYFRAMED_VALUES(
185 m_componentKeyFrames, ent.
m_component, entityName +
"::" + compName +
"::" );
187 if ( ent.
m_roIndex == Ra::Core::Utils::Index::Invalid() ) {
188 enableUI( ui->comboBox_attribute->count() > 0 );
191 const auto& roMngr = Ra::Engine::RadiumEngine::getInstance()->getRenderObjectManager();
194 REGISTER_KEYFRAMED_VALUES( m_renderObjectKeyFrames,
196 entityName +
"::" + compName +
"::" + roName +
"::" );
197#undef REGISTER_KEYFRAMED_VALUES
198 if ( ui->comboBox_attribute->count() > 0 ) {
200 ui->comboBox_attribute->setCurrentIndex( 0 );
204void Timeline::registerKeyFramedValue(
207 auto& values = m_entityKeyFrames[ent];
208 auto& name = keyFramedValueController.
m_name;
209 auto it =
std::find_if( values.begin(), values.end(), [&name](
const auto& frame ) {
210 return frame.m_name == name;
212 if ( it != values.end() ) {
213 LOG( logWARNING ) <<
"[Timeline] Already existing KeyFramedValue: " << name
214 <<
"\" on Entity \"" << ent->getName() <<
"\". Will not register."
218 values.push_back( keyFramedValueController );
223 auto& values = m_entityKeyFrames[ent];
224 auto it =
std::find_if( values.begin(), values.end(), [&name](
const auto& frame ) {
225 return frame.m_name == name;
227 if ( it != values.end() ) { values.erase( it ); }
230void Timeline::registerKeyFramedValue(
233 auto& values = m_componentKeyFrames[comp];
234 auto name = keyFramedValueController.
m_name;
235 auto it =
std::find_if( values.begin(), values.end(), [name](
const auto& frame ) {
236 return frame.m_name == name;
238 if ( it != values.end() ) {
239 LOG( logWARNING ) <<
"[Timeline] Already existing KeyFramedValue: \"" << name
240 <<
"\" on Component \"" << comp->
getName() <<
"\". Will not register."
244 values.push_back( keyFramedValueController );
250 auto& values = m_componentKeyFrames[comp];
251 auto it =
std::find_if( values.begin(), values.end(), [&name](
const auto& frame ) {
252 return frame.m_name == name;
254 if ( it != values.end() ) { values.erase( it ); }
257void Timeline::registerKeyFramedValue(
258 Ra::Core::Utils::Index roIdx,
260 auto& values = m_renderObjectKeyFrames[roIdx];
261 auto name = keyFramedValueController.
m_name;
262 auto it =
std::find_if( values.begin(), values.end(), [&name](
const auto& frame ) {
263 return frame.m_name == name;
265 if ( it != values.end() ) {
266 LOG( logWARNING ) <<
"[Timeline] Already existing KeyFramedValue: " << name
267 <<
"\" on RenderObject \"" << roIdx <<
"\". Will not register."
271 values.push_back( keyFramedValueController );
272 auto roMgr = Engine::RadiumEngine::getInstance()->getRenderObjectManager();
273 auto RO = roMgr->getRenderObject( roIdx );
274 auto comp = RO->getComponent();
278void Timeline::unregisterKeyFramedValue( Ra::Core::Utils::Index roIdx,
const std::string& name ) {
279 auto& values = m_renderObjectKeyFrames[roIdx];
280 auto it =
std::find_if( values.begin(), values.end(), [&name](
const auto& frame ) {
281 return frame.m_name == name;
283 if ( it != values.end() ) { values.erase( it ); }
286Scalar Timeline::getTime()
const {
287 return Scalar( ui->m_cursorSpin->value() );
290void Timeline::onChangeStart( Scalar time ) {
291 ui->frame_selector->onChangeStart( time,
false );
294void Timeline::onChangeEnd( Scalar time ) {
295 ui->frame_selector->onChangeEnd( time,
false );
298void Timeline::onChangeDuration( Scalar time ) {
299 ui->frame_selector->onChangeDuration( time,
false );
302void Timeline::onChangeCursor( Scalar time ) {
304 ui->frame_selector->onChangeCursor( time,
false );
305 updateKeyFrames( time );
308void Timeline::onSetPlay(
bool play ) {
309 ui->toolButton_playPause->setChecked( play );
313void Timeline::resizeEvent( QResizeEvent* event ) {
314 ui->m_scrollArea->onDrawRuler( event->size().width() - 2 );
317void Timeline::on_pingPong_toggled(
bool checked ) {
318 emit setPingPong( checked );
321void Timeline::updateKeyFrames( Scalar time ) {
322 auto update = [time](
auto& keyFrames ) {
323 for (
auto& KF : keyFrames ) {
324 for (
auto& kf : KF.second ) {
325 kf.updateKeyFrame( time );
329 update( m_entityKeyFrames );
330 update( m_componentKeyFrames );
331 update( m_renderObjectKeyFrames );
334void Timeline::onClearKeyFrames() {
335 ui->frame_selector->onClearKeyFrames();
338void Timeline::onAddingKeyFrame( Scalar time ) {
339 if ( m_current.m_value ) {
340 m_current.insertKeyFrame( time );
341 emit keyFrameAdded( time );
345void Timeline::onRemovingKeyFrame(
size_t i ) {
346 if ( m_current.m_value ) {
347 const Scalar
time = m_current.m_value->getTimes()[i];
348 if ( m_current.m_value->removeKeyFrame( i ) ) {
349 m_current.updateKeyFrame( time );
350 emit keyFrameDeleted( i );
353 LOG( logWARNING ) <<
"[Timeline] Error: Cannot remove keyFrame at "
354 << Scalar( ui->m_cursorSpin->value() );
359void Timeline::onChangingKeyFrame(
size_t i ) {
360 if ( m_current.m_value ) {
361 Scalar
time = m_current.m_value->getTimes()[i];
362 m_current.insertKeyFrame( time );
363 emit keyFrameChanged( i );
367void Timeline::onMovingKeyFrame(
size_t i, Scalar time1 ) {
368 if ( m_current.m_value ) {
370 m_current.m_value->moveKeyFrame( i, time1 );
371 m_current.updateKeyFrame( time1 );
372 emit keyFrameMoved( i, time1 );
377void Timeline::onMovingKeyFrames(
size_t first, Scalar offset ) {
378 if ( m_current.m_value ) {
379 auto times = m_current.m_value->getTimes();
380 const Scalar
time = times[first];
382 for (
int i = 0; i < int( times.size() ); ++i ) {
383 const Scalar t = times[i];
384 if ( t >= time ) { m_current.m_value->moveKeyFrame( i, t + offset ); }
389 for (
int i = times.size() - 1; i >= 0; --i ) {
390 const Scalar t = times[i];
391 if ( t >= time ) { m_current.m_value->moveKeyFrame( i, t + offset ); }
394 m_current.updateKeyFrame( time );
395 emit keyFramesMoved( first, offset );
399void Timeline::on_comboBox_attribute_currentTextChanged(
const QString& arg1 ) {
402 const QStringList names = arg1.split(
"::" );
405#define LOG_AND_RETURN \
406 LOG( logWARNING ) << "[Timeline] Error: attribute \"" << arg1.toStdString() \
407 << "\"'s name is not conform."; \
414#define GET_KEYFRAMEDVALUE( list, name ) \
416 auto it = std::find_if( list.begin(), list.end(), [&name]( const auto& frame ) { \
417 return frame.m_name == name; \
419 if ( it == list.end() ) { LOG_AND_RETURN; } \
426#define GET_KEYFRAMEDVALUE_LIST( map, lambda ) \
428 auto keyFramedValues = std::find_if( map.begin(), map.end(), lambda ); \
429 if ( keyFramedValues == map.end() ) { LOG_AND_RETURN; } \
430 list = keyFramedValues->second; \
433 switch ( names.size() ) {
437 auto lambda = [entityName](
const auto& frame ) {
438 return frame.first->getName() == entityName;
440 GET_KEYFRAMEDVALUE_LIST( m_entityKeyFrames, lambda );
441 GET_KEYFRAMEDVALUE( list, frameName );
446 auto lambda = [compName](
const auto& frame ) {
447 return frame.first->getName() == compName;
449 GET_KEYFRAMEDVALUE_LIST( m_componentKeyFrames, lambda );
450 GET_KEYFRAMEDVALUE( list, frameName );
453 const QStringList fullRoName = names.at( 2 ).split(
'_' );
455 const auto roIdx = fullRoName.last().toInt( &ok );
456 if ( !ok ) { LOG_AND_RETURN; }
457 auto lambda = [&roIdx](
const auto& frame ) {
return frame.first == roIdx; };
459 GET_KEYFRAMEDVALUE_LIST( m_renderObjectKeyFrames, lambda );
460 GET_KEYFRAMEDVALUE( list, frameName );
465#undef GET_KEYFRAMEDVALUE_LIST
466#undef GET_KEYFRAMEDVALUE
470 const auto times = m_current.m_value->getTimes();
471 for (
const auto& t : times ) {
472 ui->frame_selector->onAddingKeyFrame( t,
false );
476void Timeline::on_pushButton_editAttribute_clicked() {
480void Timeline::on_toolButton_help_clicked() {
481 static bool is_showing =
false;
486 connect( dialog, &HelpDialog::closed, [=]() { is_showing =
false; } );
virtual std::vector< Scalar > getTimes() const =0
A component is an element that can be updated by a system. It is also linked to some other components...
virtual const std::string & getName() const
Return the component's name.
virtual Entity * getEntity() const
Return the entity the component belongs to.
An entity is an scene element. It ties together components with a transform.
Dialog to display navigation/interaction controls.
The Timeline class provides display and management of time, as well as keyframes.
std::enable_if<!std::numeric_limits< T >::is_integer, bool >::type areApproxEqual(T x, T y, T espilonBoostFactor=T(10))
Compare two numbers such that |x-y| < espilon*epsilonBoostFactor.
Ra::Core::Utils::Index m_roIndex
RO index of the represented object.
Entity * m_entity
The entity represented by the item, or owning the object represented.