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