Radium Engine  1.5.20
Loading...
Searching...
No Matches
TimelineFrameSelector.cpp
1#include <Gui/Timeline/TimelineFrameSelector.hpp>
2
3#include <QMouseEvent>
4#include <QPainter>
5#include <QTimer>
6#include <QtGlobal>
7
8#include <Core/Math/Math.hpp>
9#include <Gui/Timeline/TimelineScrollArea.hpp>
10
11#include "ui_Timeline.h"
12
13namespace Ra::Gui {
14
15TimelineFrameSelector::TimelineFrameSelector( QWidget* parent ) : QFrame( parent ) {
16 m_timer = new QTimer( this );
17 connect( m_timer, SIGNAL( timeout() ), this, SLOT( update() ) );
18}
19
20TimelineFrameSelector::~TimelineFrameSelector() {
21 delete m_timer;
22}
23
24void TimelineFrameSelector::setTimelineUi( Ui::Timeline* ui ) {
25 // register UI
26 m_timelineUI = ui;
27
28 // init values from UI
29 m_start = Scalar( m_timelineUI->m_startSpin->value() );
30 m_end = Scalar( m_timelineUI->m_endSpin->value() );
31 m_cursor = Scalar( m_timelineUI->m_cursorSpin->value() );
32 m_timelineUI->m_scrollArea->setMaxDuration( Scalar( m_timelineUI->m_durationSpin->value() ) );
33
34 // create connections
35#define CAST_DOUBLE static_cast<void ( QDoubleSpinBox::* )( double )>
36 connect( m_timelineUI->m_startSpin,
37 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
38 [this]( double start ) { onChangeStart( Scalar( start ) ); } );
39 connect( m_timelineUI->m_cursorSpin,
40 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
41 [this]( double cursor ) { onChangeCursor( Scalar( cursor ) ); } );
42 connect( m_timelineUI->m_endSpin,
43 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
44 [this]( double end ) { onChangeEnd( Scalar( end ) ); } );
45 connect( m_timelineUI->m_durationSpin,
46 CAST_DOUBLE( &QDoubleSpinBox::valueChanged ),
47 [this]( double duration ) { onChangeDuration( Scalar( duration ) ); } );
48#undef CAST_DOUBLE
49
50 connect( m_timelineUI->m_leftSlider,
51 &TimelineSlider::slide,
52 this,
53 &TimelineFrameSelector::onSlideLeftSlider );
54 connect( m_timelineUI->m_rightSlider,
55 &TimelineSlider::slide,
56 this,
57 &TimelineFrameSelector::onSlideRightSlider );
58
59 connect( m_timelineUI->toolButton_start,
60 &QToolButton::clicked,
61 this,
62 &TimelineFrameSelector::onSetCursorToStart );
63 connect( m_timelineUI->toolButton_rearward,
64 &QToolButton::clicked,
65 this,
66 &TimelineFrameSelector::onSetCursorToPreviousKeyFrame );
67 connect( m_timelineUI->toolButton_forward,
68 &QToolButton::clicked,
69 this,
70 &TimelineFrameSelector::onSetCursorToNextKeyFrame );
71 connect( m_timelineUI->toolButton_end,
72 &QToolButton::clicked,
73 this,
74 &TimelineFrameSelector::onSetCursorToEnd );
75
76 connect(
77 m_timelineUI->toolButton_keyFrame, SIGNAL( clicked() ), this, SLOT( onAddingKeyFrame() ) );
78 connect( m_timelineUI->m_removeKeyFrameButton,
79 SIGNAL( clicked() ),
80 this,
81 SLOT( onDeletingKeyFrame() ) );
82
83 connect(
84 m_timelineUI->m_scrollArea, SIGNAL( addKeyFrame() ), this, SLOT( onAddingKeyFrame() ) );
85 connect( m_timelineUI->m_scrollArea,
86 SIGNAL( removeKeyFrame() ),
87 this,
88 SLOT( onDeletingKeyFrame() ) );
89 connect( m_timelineUI->m_scrollArea,
90 &TimelineScrollArea::nextKeyFrame,
91 this,
92 &TimelineFrameSelector::onSetCursorToNextKeyFrame );
93 connect( m_timelineUI->m_scrollArea,
94 &TimelineScrollArea::previousKeyFrame,
95 this,
96 &TimelineFrameSelector::onSetCursorToPreviousKeyFrame );
97 connect( m_timelineUI->m_scrollArea,
98 &TimelineScrollArea::togglePlayPause,
99 m_timelineUI->toolButton_playPause,
100 &QToolButton::toggle );
101 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::durationIncrement, [this]() {
102 onChangeDuration( Scalar( m_timelineUI->m_durationSpin->value() +
103 m_timelineUI->m_durationSpin->singleStep() ) );
104 } );
105 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::durationDecrement, [this]() {
106 onChangeDuration( Scalar( m_timelineUI->m_durationSpin->value() -
107 m_timelineUI->m_durationSpin->singleStep() ) );
108 } );
109 connect( m_timelineUI->m_scrollArea, &TimelineScrollArea::stepChanged, [this]( double step ) {
110 m_timelineUI->m_startSpin->setSingleStep( 0.5 * step );
111 m_timelineUI->m_endSpin->setSingleStep( 0.5 * step );
112 m_timelineUI->m_cursorSpin->setSingleStep( 0.5 * step );
113 m_timelineUI->m_durationSpin->setSingleStep( 0.5 * step );
114 } );
115}
116
117void TimelineFrameSelector::onChangeStart( Scalar time, bool internal ) {
118 Scalar newStart = std::max( std::min( time, m_end ), 0_ra );
119
120 bool out = std::abs( newStart - time ) > 1e-5_ra;
121 bool change = std::abs( newStart - m_start ) > 1e-5_ra;
122
123 if ( change ) {
124 m_start = newStart;
125 updateStartSpin();
126 redrawPlayZone();
127
128 // emit signal if time of emitter is internal changed due of limits
129 if ( internal || out ) { emit startChanged( m_start ); }
130 }
131 else {
132 if ( out ) { updateStartSpin(); }
133 }
134}
135
136void TimelineFrameSelector::onChangeEnd( Scalar time, bool internal ) {
137 Scalar newEnd =
138 std::min( std::max( time, m_start ), m_timelineUI->m_scrollArea->getMaxDuration() );
139
140 bool out = std::abs( newEnd - time ) > 1e-5_ra;
141 bool change = std::abs( newEnd - m_end ) > 1e-5_ra;
142
143 // emit signal only if new value of end
144 if ( change ) {
145 m_end = newEnd;
146 updateEndSpin();
147 if ( internal || out ) { emit endChanged( m_end ); }
148 update();
149 }
150 else {
151 if ( out ) { updateEndSpin(); }
152 }
153}
154
155void TimelineFrameSelector::onChangeDuration( Scalar time, bool internal ) {
156 Scalar newDuration = std::max( time, 0_ra );
157 Scalar oldDuration = m_timelineUI->m_scrollArea->getMaxDuration();
158
159 bool out = std::abs( newDuration - time ) > 1e-5_ra;
160 bool change = std::abs( newDuration - oldDuration ) > 1e-5_ra;
161
162 if ( change ) {
163 oldDuration = newDuration;
164 m_timelineUI->m_scrollArea->setMaxDuration( newDuration );
165 m_timelineUI->m_scrollArea->onDrawRuler(
166 m_timelineUI->m_scrollArea->widget()->minimumWidth() );
167 updateDurationSpin();
168
169 // emit signal if time of emitter is internal changed due of limits
170 if ( internal || out ) { emit durationChanged( newDuration ); }
171 }
172 else {
173 if ( out ) { updateDurationSpin(); }
174 }
175
176 if ( oldDuration < m_start ) onChangeStart( oldDuration );
177
178 if ( oldDuration < m_end ) onChangeEnd( oldDuration );
179
180 // auto update
181}
182
183void TimelineFrameSelector::onChangeCursor( Scalar time, bool internal ) {
184 Scalar newCursor = std::max( 0_ra, time );
185
186 if ( internal ) { newCursor = nearestStep( newCursor ); }
187
188 bool out = std::abs( newCursor - time ) > 1e-5_ra;
189 bool change = std::abs( newCursor - m_cursor ) > 1e-5_ra;
190
191 if ( change ) {
192 m_cursor = newCursor;
193 updateCursorSpin();
194 if ( internal || out ) { emit cursorChanged( m_cursor ); }
195 update();
196 }
197 else {
198 if ( out ) { updateCursorSpin(); }
199 }
200}
201
202void TimelineFrameSelector::onAddingKeyFrame( Scalar time, bool internal ) {
203 // by default (time = -1.0), add keyFrame on cursor
204 if ( static_cast<int>( time ) == -1 ) time = m_cursor;
205
206 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [time]( const auto& t ) {
207 return Ra::Core::Math::areApproxEqual( t, time );
208 } );
209
210 // if keyFrame not already here
211 if ( it == m_keyFrames.end() ) {
212 updateCursorSpin();
213
214 m_keyFrames.push_back( time );
215 std::sort( m_keyFrames.begin(), m_keyFrames.end() );
216
217 if ( internal ) { emit keyFrameAdded( time ); }
218
219 updateNbKeyFrameSpin();
220
221 update();
222 }
223 // KeyFrame already here, change actual KeyFrame
224 else {
225 if ( internal ) { emit keyFrameChanged( std::distance( m_keyFrames.begin(), it ) ); }
226
227 m_updateKeyFrameFlash = 6;
228 m_keyFrameFlash = time;
229
230 m_timer->start( 50 );
231
232 update();
233 }
234}
235
236void TimelineFrameSelector::onDeletingKeyFrame( bool internal ) {
237 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [this]( const auto& t ) {
238 return Ra::Core::Math::areApproxEqual( t, m_cursor );
239 } );
240 if ( it == m_keyFrames.end() ) { return; }
241
242 if ( internal ) { emit keyFrameDeleted( std::distance( m_keyFrames.begin(), it ) ); }
243
244 m_keyFrames.erase( it );
245
246 updateCursorSpin();
247
248 updateNbKeyFrameSpin();
249
250 update();
251}
252
253void TimelineFrameSelector::onMoveKeyFrame( Scalar time0, Scalar time1, bool internal ) {
254 if ( Ra::Core::Math::areApproxEqual( time0, time1 ) ) { return; }
255
256 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [this]( const auto& t ) {
257 return Ra::Core::Math::areApproxEqual( t, m_cursor );
258 } );
259 if ( it == m_keyFrames.end() ) { return; }
260
261 if ( internal ) { emit keyFrameMoved( std::distance( m_keyFrames.begin(), it ), time1 ); }
262
263 m_cursor = time1;
264
265 m_keyFrames.erase( it );
266 m_keyFrames.push_back( m_cursor );
267 std::sort( m_keyFrames.begin(), m_keyFrames.end() );
268
269 updateCursorSpin();
270
271 update();
272}
273
274void TimelineFrameSelector::onMoveKeyFrames( Scalar time, Scalar offset, bool internal ) {
275 if ( Ra::Core::Math::areApproxEqual( offset, 0_ra ) ) { return; }
276
277 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [time]( const auto& t ) {
278 return Ra::Core::Math::areApproxEqual( t, time );
279 } );
280 if ( it == m_keyFrames.end() ) { return; }
281
282 // emit keyFramesMoved before emitting cursorChanged to render the truly Frame on cursor
283 if ( internal ) { emit keyFramesMoved( std::distance( m_keyFrames.begin(), it ), offset ); }
284
286 std::transform( m_keyFrames.begin(),
287 m_keyFrames.end(),
288 std::back_inserter( clone ),
289 [time, offset]( Scalar p ) { return ( p < time ? p : p + offset ); } );
290 std::exchange( m_keyFrames, clone );
291
292 Scalar left = ( offset > 0 ) ? ( time ) : ( time + offset );
293
294 if ( m_start >= left ) {
295 m_start = std::max(
296 std::min( m_start + offset, m_timelineUI->m_scrollArea->getMaxDuration() ), 0_ra );
297 updateStartSpin();
298 if ( internal ) { emit startChanged( m_start ); }
299 }
300
301 Scalar right = *m_keyFrames.rbegin();
302 if ( right > m_timelineUI->m_scrollArea->getMaxDuration() ) {
303 m_timelineUI->m_scrollArea->setMaxDuration( right );
304 updateDurationSpin();
305 if ( internal ) { emit durationChanged( right ); }
306 }
307
308 if ( m_end >= left ) {
309 m_end = m_end + offset;
310 updateEndSpin();
311 if ( internal ) { emit endChanged( m_end ); }
312 }
313
314 if ( m_cursor >= left ) {
315 m_cursor = std::max( m_cursor + offset, 0_ra );
316 updateCursorSpin();
317
318 if ( internal ) { emit cursorChanged( m_cursor ); }
319 }
320
321 update();
322}
323
324void TimelineFrameSelector::onClearKeyFrames() {
325 m_keyFrames.clear();
326 updateNbKeyFrameSpin();
327
328 updateCursorSpin();
329 update();
330}
331
332// -------------------------- INTERNAL SLOTS ----------------------------------
333void TimelineFrameSelector::onSlideLeftSlider( int deltaX ) {
334 { m_timelineUI->m_leftSlider->setStyleSheet( "background-color: green" ); }
335
336 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
337 Scalar newStart = m_start + deltaX / pixPerSec;
338
339 onChangeStart( newStart );
340}
341
342void TimelineFrameSelector::onSlideRightSlider( int deltaX ) {
343 { m_timelineUI->m_rightSlider->setStyleSheet( "background-color: red" ); }
344 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
345 Scalar newEnd = m_end + deltaX / pixPerSec;
346
347 onChangeEnd( newEnd );
348}
349
350void TimelineFrameSelector::onSetCursorToStart() {
351 onChangeCursor( m_start );
352}
353
354void TimelineFrameSelector::onSetCursorToEnd() {
355 onChangeCursor( m_end );
356}
357
358void TimelineFrameSelector::onSetCursorToPreviousKeyFrame() {
359 auto it = m_keyFrames.rbegin();
360 while ( it != m_keyFrames.rend() && *it >= m_cursor )
361 it++;
362
363 if ( it != m_keyFrames.rend() ) { onChangeCursor( *it ); }
364}
365
366void TimelineFrameSelector::onSetCursorToNextKeyFrame() {
367 auto it = m_keyFrames.begin();
368 while ( it != m_keyFrames.end() && *it <= m_cursor )
369 it++;
370
371 if ( it != m_keyFrames.end() ) { onChangeCursor( *it ); }
372}
373
374void TimelineFrameSelector::paintEvent( QPaintEvent* ) {
375
376 QPainter painter( this );
377 int h = height();
378 int w = width();
379
380 redrawPlayZone();
381
382 // DRAW FRAME SCALE
383 painter.setPen( QPen( Qt::lightGray ) );
384 Scalar frameDuration = 1_ra / TIMELINE_FPS;
385 int hUp = h / 3;
386 Scalar nbFrame = m_timelineUI->m_scrollArea->getMaxDuration() / frameDuration;
387 Scalar pixPerSec = m_timelineUI->m_scrollArea->getPixPerSec();
388 Scalar step = m_timelineUI->m_scrollArea->getStep();
389 int zero = m_timelineUI->m_scrollArea->getZero();
390 for ( int i = 0; i < nbFrame; i++ ) {
391 int x = int( i * frameDuration * pixPerSec + zero );
392 painter.drawLine( x, 0, x, hUp );
393 }
394
395 // DRAW CURSOR
396 painter.setPen( QPen( QColor( 0, 0, 255, 255 ), 3 ) );
397 int xCursor = int( zero + m_cursor * pixPerSec );
398 painter.drawLine( xCursor, 0, xCursor, h );
399
400 // DRAW KEYFRAMES
401 painter.setPen( QPen( QColor( 255, 255, 0, 255 ), 3 ) );
402 int hTemp = h / 3 + 2;
403 for ( Scalar keyFrame : m_keyFrames ) {
404 int xKeyFrame = int( zero + keyFrame * pixPerSec );
405 painter.drawLine( xKeyFrame, hTemp, xKeyFrame, h );
406 }
407
408 // DRAW TIME SCALE
409 int hDown = 2 * h / 3;
410 painter.setPen( Qt::black );
411 for ( int i = 1; i < m_timelineUI->m_scrollArea->getNbInterval(); i++ ) {
412 int x = int( i * step * pixPerSec );
413 painter.drawLine( x, hDown, x, h );
414 }
415 int hDown2 = 3 * h / 4;
416 painter.setPen( Qt::darkGray );
417 for ( int i = 1; i < m_timelineUI->m_scrollArea->getNbInterval() - 1; i++ ) {
418 int middle = int( ( i + 0.5_ra ) * step * pixPerSec );
419 painter.drawLine( middle, hDown2, middle, h );
420 }
421
422 if ( m_updateKeyFrameFlash > 0 ) {
423
424 if ( m_updateKeyFrameFlash % 2 == 0 ) {
425 painter.setPen( QPen( QColor( 0, 0, 255, 255 ), 3 ) );
426 int xKeyFrame = static_cast<int>( zero + m_keyFrameFlash * pixPerSec );
427 painter.drawLine( xKeyFrame, hTemp, xKeyFrame, h );
428 }
429
430 if ( --m_updateKeyFrameFlash == 0 ) m_timer->stop();
431 }
432
433 // DRAW MIDDLE RULER SEPARATOR
434 int gap = 5;
435 painter.setPen( Qt::white );
436 painter.drawLine( 0, h / 2 + gap + 2, w, h / 2 + gap + 2 );
437 painter.drawLine( 0, h / 2 + gap + 1, w, h / 2 + gap + 1 );
438
439 painter.setPen( Qt::darkGray );
440 painter.drawLine( 0, h / 3, w, h / 3 );
441 painter.drawLine( 0, h / 2 + gap, w, h / 2 + gap );
442}
443
444void TimelineFrameSelector::mousePressEvent( QMouseEvent* event ) {
445 bool shiftDown = event->modifiers() & Qt::Modifier::SHIFT;
446 bool ctrlDown = event->modifiers() & Qt::Modifier::CTRL;
447 // ---------------------- LEFT CLICK --------------------------------------
448 if ( event->button() == Qt::LeftButton ) {
449 Scalar newCursor = std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
450 m_timelineUI->m_scrollArea->getPixPerSec(),
451 0_ra );
452 // move cursor without render
453 if ( ctrlDown ) { onChangeCursor( newCursor, false ); }
454 // delete keyFrames between cursor and newCursor
455 else if ( shiftDown ) { deleteZone( m_cursor, newCursor ); }
456 // move cursor and update renderer
457 else {
458 onChangeCursor( newCursor );
459 m_mouseLeftClicked = true;
460 }
461 }
462 // ------------------ RIGHT CLICK -------------------------------------
463 else if ( event->button() == Qt::RightButton ) {
464 Scalar newFrame = std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
465 m_timelineUI->m_scrollArea->getPixPerSec(),
466 0_ra );
467 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [this]( const auto& t ) {
468 return Ra::Core::Math::areApproxEqual( t, m_cursor );
469 } );
470 // if already on keyFrame, move current KeyFrame
471 // ------------------- CURSOR ON KEYFRAME -----------------------
472 if ( it != m_keyFrames.end() ) {
473 Scalar nearest = nearestStep( newFrame );
474 // -------------- SINGLE MOVE -------------------------------------
475 if ( shiftDown ) {
476 // if no keyFrame under mouse, move KeyFrame to newFrame
477 auto it2 = std::find_if(
478 m_keyFrames.begin(), m_keyFrames.end(), [nearest]( const auto& t ) {
479 return Ra::Core::Math::areApproxEqual( t, nearest );
480 } );
481 if ( it2 == m_keyFrames.end() && ( std::abs( m_cursor - nearest ) > 1e-5_ra ) ) {
482 onMoveKeyFrame( m_cursor, nearest );
483 }
484 }
485 // ---------- MULTIPLE MOVE -----------------------------------
486 else {
487 // if not before preceding KeyFrame, remove or insert time
488 auto itLeft = it;
489 --itLeft;
490 Scalar left = ( it == m_keyFrames.begin() ) ? ( 0.0 ) : ( *itLeft );
491 if ( nearest > left ) { onMoveKeyFrames( m_cursor, nearest - m_cursor ); }
492 }
493 }
494 // ---------------- CURSOR NOT ON KEYFRAME --------------------------
495 else {
496 // if shiftdown, slide first right KeyFrame to the left
497 // --------------- MOVE RIGHT KEYFRAME TO THE LEFT -----------------
498 if ( shiftDown ) {
499 auto itRight = std::lower_bound( m_keyFrames.begin(), m_keyFrames.end(), newFrame );
500 // if KeyFrames on the right, remove or insert time
501 if ( itRight != m_keyFrames.end() ) {
502 onMoveKeyFrames( *itRight, newFrame - *itRight );
503 }
504 }
505 // if not shiftdown, slide first left KeyFrame to the right
506 // ---------------- MOVE LEFT KEYFRAME TO THE RIGHT -----------
507 else {
508 auto itLeft =
509 --std::lower_bound( m_keyFrames.begin(), m_keyFrames.end(), newFrame );
510 // if KeyFrames on the left, remove or insert time
511 if ( itLeft != m_keyFrames.end() ) {
512 onMoveKeyFrames( *itLeft, newFrame - *itLeft );
513 }
514 }
515 }
516 }
517 // no catch mouse event
518 else {
519 event->ignore();
520 return;
521 }
522 event->accept();
523}
524
525void TimelineFrameSelector::mouseMoveEvent( QMouseEvent* event ) {
526 if ( m_mouseLeftClicked ) {
527 Scalar newCursor = std::max( Scalar( event->x() - m_timelineUI->m_scrollArea->getZero() ) /
528 m_timelineUI->m_scrollArea->getPixPerSec(),
529 0_ra );
530
531 onChangeCursor( newCursor );
532 }
533 else { event->ignore(); }
534}
535
536void TimelineFrameSelector::mouseReleaseEvent( QMouseEvent* event ) {
537 if ( event->button() == Qt::LeftButton ) {
538 m_mouseLeftClicked = false;
539 event->accept();
540 }
541 else { event->ignore(); }
542}
543
544void TimelineFrameSelector::updateCursorSpin() {
545 auto it = std::find_if( m_keyFrames.begin(), m_keyFrames.end(), [this]( const auto& t ) {
546 return Ra::Core::Math::areApproxEqual( t, m_cursor );
547 } );
548 if ( it != m_keyFrames.end() ) {
549 m_timelineUI->m_cursorSpin->setStyleSheet( "background-color: yellow" );
550 m_timelineUI->m_removeKeyFrameButton->setEnabled( true );
551 }
552 else {
553 m_timelineUI->m_cursorSpin->setStyleSheet( "background-color: #5555ff" );
554 m_timelineUI->m_removeKeyFrameButton->setEnabled( false );
555 }
556 m_timelineUI->m_cursorSpin->setValue( double( m_cursor ) );
557}
558
559void TimelineFrameSelector::updateStartSpin() {
560 m_timelineUI->m_startSpin->setValue( double( m_start ) );
561 updateDurationSpin();
562}
563
564void TimelineFrameSelector::updateEndSpin() {
565 m_timelineUI->m_endSpin->setValue( double( m_end ) );
566 updateDurationSpin();
567}
568
569void TimelineFrameSelector::updateDurationSpin() {
570 m_timelineUI->m_durationSpin->setValue(
571 double( m_timelineUI->m_scrollArea->getMaxDuration() ) );
572}
573
574void TimelineFrameSelector::updateNbKeyFrameSpin() {
575 m_timelineUI->m_nbKeyFramesSpin->setText( " " + QString::number( m_keyFrames.size() ) + " " );
576}
577
578Scalar TimelineFrameSelector::nearestStep( Scalar time ) const {
579 Scalar deltaT =
580 TIMELINE_AUTO_SUGGEST_CURSOR_RADIUS / m_timelineUI->m_scrollArea->getPixPerSec();
581
582 Scalar minDist = Scalar( m_timelineUI->m_durationSpin->maximum() );
583 Scalar dist;
584
585 Scalar newCursor = time;
586
587 for ( Scalar keyFrame : m_keyFrames ) {
588 dist = qAbs( keyFrame - time );
589 if ( dist < deltaT && dist < minDist ) {
590 minDist = dist;
591 newCursor = keyFrame;
592 }
593 if ( time < keyFrame ) break;
594 }
595
596 Scalar step = m_timelineUI->m_scrollArea->getStep();
597 for ( int i = 0; i < m_timelineUI->m_scrollArea->getNbInterval() - 1; ++i ) {
598 Scalar pos = i * step;
599 dist = std::abs( pos - time );
600 if ( dist < deltaT && dist < minDist ) {
601 minDist = dist;
602 newCursor = pos;
603 }
604
605 pos = i * step + 0.5_ra * step;
606 dist = std::abs( pos - time );
607 if ( dist < deltaT && dist < minDist ) {
608 minDist = dist;
609 newCursor = pos;
610 }
611
612 if ( time < pos ) break;
613 }
614
615 return newCursor;
616}
617
618void TimelineFrameSelector::deleteZone( Scalar time, Scalar time2 ) {
619 Scalar left = std::min( time, time2 );
620 Scalar right = std::max( time, time2 );
621
622 Scalar dist = right - left;
623
624 // remove KeyFrames
625 auto it = m_keyFrames.begin();
626 bool first = true;
627 while ( it != m_keyFrames.end() ) {
628 Scalar keyFrame = *it;
629
630 if ( keyFrame >= left ) {
631 it = m_keyFrames.erase( it );
632
633 if ( keyFrame > right ) {
634 if ( first ) {
635 emit keyFramesMoved( std::distance( m_keyFrames.begin(), it ), -dist );
636 first = false;
637 }
638 it = m_keyFrames.insert( it, keyFrame - dist );
639 ++it;
640 }
641 else { emit keyFrameDeleted( std::distance( m_keyFrames.begin(), it ) ); }
642 }
643 else { ++it; }
644 }
645 updateNbKeyFrameSpin();
646
647 // update playzone
648 Scalar newStart = std::max( std::max( std::min( m_start, left ), m_start - dist ), 0_ra );
649 if ( std::abs( newStart - m_start ) > 1e-5_ra ) {
650 m_start = newStart;
651 updateStartSpin();
652
653 emit startChanged( m_start );
654 }
655
656 Scalar newEnd = std::max( std::max( std::min( m_end, left ), m_end - dist ), 0_ra );
657 if ( std::abs( newEnd - m_end ) > 1e-5_ra ) {
658 m_end = newEnd;
659 updateEndSpin();
660
661 emit endChanged( m_end );
662 }
663
664 emit cursorChanged( m_cursor );
665
666 update();
667}
668
669void TimelineFrameSelector::redrawPlayZone() {
670 m_timelineUI->m_leftSpacer->setMinimumWidth(
671 int( m_timelineUI->m_scrollArea->getZero() +
672 m_start * m_timelineUI->m_scrollArea->getPixPerSec() -
673 m_timelineUI->m_leftSlider->width() ) );
674 m_timelineUI->m_playZone->setMinimumWidth(
675 int( ( m_end - m_start ) * m_timelineUI->m_scrollArea->getPixPerSec() ) );
676}
677
678} // namespace Ra::Gui
T back_inserter(T... args)
T distance(T... args)
T find_if(T... args)
T left(T... args)
T lower_bound(T... args)
T max(T... args)
T min(T... args)
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.
Definition Math.hpp:42
T sort(T... args)
T time(T... args)
T transform(T... args)