diff options
author | Mahmoud Badri <mahmoud.badri@qt.io> | 2019-08-07 14:35:53 +0300 |
---|---|---|
committer | Mahmoud Badri <mahmoud.badri@qt.io> | 2019-08-08 09:48:28 +0300 |
commit | 40987cdef2196c8c59646e16af52b60e469f81ed (patch) | |
tree | 572a4390481b6e272eea65498e3cd290ac0985fa /src | |
parent | 062382bcac16fe0258b57ed94465fc6092fc5c1a (diff) |
Separate bezier curve channels
- enable toggling a channel's visibility.
- each channel has its own bezier controls.
Task-number: QT3DS-3813
Change-Id: I4e0a4ec4b84c0ae570c134fe6ffd430f9ad4daa4
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src')
15 files changed, 288 insertions, 264 deletions
diff --git a/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp b/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp index 854bf116..04588dfc 100644 --- a/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp +++ b/src/Authoring/Client/Code/Core/Doc/DocumentEditor.cpp @@ -2842,11 +2842,9 @@ public: m_AnimationCore.SetKeyframeData(inKeyframe, kfData); } - void setBezierKeyframeValues(const QVector<std::pair<qt3dsdm::Qt3DSDMKeyframeHandle, - TKeyframe>> &changedKfs) override + void setBezierKeyframeValue(TKeyframeHandle kfHandle, const TKeyframe &kfData) override { - for (auto kf : changedKfs) - m_AnimationCore.SetKeyframeData(kf.first, kf.second); + m_AnimationCore.SetKeyframeData(kfHandle, kfData); } void DeleteAllKeyframes(Qt3DSDMAnimationHandle inAnimation) override diff --git a/src/Authoring/Client/Code/Core/Doc/IDocumentEditor.h b/src/Authoring/Client/Code/Core/Doc/IDocumentEditor.h index d1b655de..2378d713 100644 --- a/src/Authoring/Client/Code/Core/Doc/IDocumentEditor.h +++ b/src/Authoring/Client/Code/Core/Doc/IDocumentEditor.h @@ -307,8 +307,8 @@ public: virtual bool RemoveAnimation(TSlideHandle inSlide, TInstanceHandle instance, const wchar_t *propName, long subIndex) = 0; virtual void SetKeyframeTime(TKeyframeHandle inKeyframe, long inTimeInMilliseconds) = 0; - virtual void setBezierKeyframeValues(const QVector<std::pair<TKeyframeHandle, - qt3dsdm::TKeyframe>> &changedKfs) = 0; + virtual void setBezierKeyframeValue(TKeyframeHandle kfHandle, + const qt3dsdm::TKeyframe &kfData) = 0; virtual void DeleteAllKeyframes(Qt3DSDMAnimationHandle inAnimation) = 0; virtual void KeyframeProperty(Qt3DSDMInstanceHandle inInstance, Qt3DSDMPropertyHandle inProperty, bool inDoDiffValue) = 0; diff --git a/src/Authoring/Client/Code/Core/Utility/HotKeys.cpp b/src/Authoring/Client/Code/Core/Utility/HotKeys.cpp index a47c2eb8..278bf23b 100644 --- a/src/Authoring/Client/Code/Core/Utility/HotKeys.cpp +++ b/src/Authoring/Client/Code/Core/Utility/HotKeys.cpp @@ -566,6 +566,16 @@ bool CHotKeys::IsKeyDown(Qt::KeyboardModifier inKeyCode) return qApp->keyboardModifiers() & inKeyCode; } +bool CHotKeys::isCtrlDown() +{ + return qApp->keyboardModifiers() & Qt::ControlModifier; +} + +bool CHotKeys::isAltDown() +{ + return qApp->keyboardModifiers() & Qt::AltModifier; +} + //============================================================================= /** * Retrieves the current status of the various modifier keys. diff --git a/src/Authoring/Client/Code/Core/Utility/HotKeys.h b/src/Authoring/Client/Code/Core/Utility/HotKeys.h index 75376322..27dc8fb9 100644 --- a/src/Authoring/Client/Code/Core/Utility/HotKeys.h +++ b/src/Authoring/Client/Code/Core/Utility/HotKeys.h @@ -238,6 +238,8 @@ public: void ReleaseHotKeys(); static bool IsKeyDown(Qt::KeyboardModifier inKeyCode); + static bool isCtrlDown(); + static bool isAltDown(); static Qt::KeyboardModifiers GetCurrentKeyModifiers(); static bool isFocusOnControlThatWantsKeys(); diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/ITimelineItemProperty.h b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/ITimelineItemProperty.h index 4754bb0d..cdfbd0f1 100644 --- a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/ITimelineItemProperty.h +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/ITimelineItemProperty.h @@ -50,8 +50,6 @@ public: virtual Q3DStudio::CString GetName() const = 0; virtual bool IsMaster() const = 0; virtual qt3dsdm::TDataTypePair GetType() const = 0; - virtual float GetMaximumValue() const = 0; - virtual float GetMinimumValue() const = 0; virtual void SetSelected() = 0; virtual void DeleteAllKeys() = 0; diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.cpp b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.cpp index f863435c..182f9d78 100644 --- a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.cpp @@ -188,35 +188,6 @@ qt3dsdm::TDataTypePair Qt3DSDMTimelineItemProperty::GetType() const return m_Type; } -void CompareAndSet(const Qt3DSDMTimelineKeyframe *inKeyframe, float &outRetValue, bool inGreaterThan) -{ - float theValue = (inGreaterThan) ? inKeyframe->GetMaxValue() : inKeyframe->GetMinValue(); - if ((inGreaterThan && theValue > outRetValue) || (!inGreaterThan && theValue < outRetValue)) - outRetValue = theValue; -} - -// returns the max keyframe value in a property. For bezier keyframes this includes the control -// points values as well -float Qt3DSDMTimelineItemProperty::GetMaximumValue() const -{ - float theRetVal = FLT_MIN; - do_all(m_Keyframes, std::bind(CompareAndSet, std::placeholders::_1, std::ref(theRetVal), true)); - if (m_Type.first == DataModelDataType::Float4 && m_Type.second == AdditionalMetaDataType::Color) - theRetVal = DataModelToColor(theRetVal); - return theRetVal; -} - -// returns the min keyframe value in a property. For bezier keyframes this includes the control -// points values as well -float Qt3DSDMTimelineItemProperty::GetMinimumValue() const -{ - float theRetVal = FLT_MAX; - do_all(m_Keyframes, std::bind(CompareAndSet, std::placeholders::_1, std::ref(theRetVal), false)); - if (m_Type.first == DataModelDataType::Float4 && m_Type.second == AdditionalMetaDataType::Color) - theRetVal = DataModelToColor(theRetVal); - return theRetVal; -} - RowTree *Qt3DSDMTimelineItemProperty::getRowTree() const { return m_rowTree; diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.h b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.h index ff508ae8..e386fe38 100644 --- a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.h +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemProperty.h @@ -61,8 +61,6 @@ public: Q3DStudio::CString GetName() const override; bool IsMaster() const override; qt3dsdm::TDataTypePair GetType() const override; - float GetMaximumValue() const override; - float GetMinimumValue() const override; void SetSelected() override; void DeleteAllKeys() override; IKeyframe *GetKeyframeByTime(long inTime) const override; diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.cpp b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.cpp index d8d7aad3..c7431b3b 100644 --- a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.cpp @@ -192,54 +192,3 @@ void Qt3DSDMTimelineKeyframe::GetKeyframeHandles(TKeyframeHandleList &outList) c { outList = m_KeyframeHandles; } - -void CompareAndSet(Qt3DSDMKeyframeHandle inKeyframe, IAnimationCore *inAnimationCore, - float &ioRetValue, bool inGreaterThan) -{ - TKeyframe keyframeData = inAnimationCore->GetKeyframeData(inKeyframe); - float value = KeyframeValueValue(keyframeData); - if ((inGreaterThan && value > ioRetValue) || (!inGreaterThan && value < ioRetValue)) - ioRetValue = value; - - // for bezier keyframes compare tangents in/out also - if (keyframeData.getType() == qt3dsdm::EAnimationTypeBezier) { - float inTime, inValue, outTime, outValue; - getBezierValues(keyframeData, inTime, inValue, outTime, outValue); - - if (!inAnimationCore->IsFirstKeyframe(inKeyframe) // check tangent-in value - && ((inGreaterThan && inValue > ioRetValue) - || (!inGreaterThan && inValue < ioRetValue))) { - ioRetValue = inValue; - } - - if (!inAnimationCore->IsLastKeyframe(inKeyframe) // check tangent-out value - && ((inGreaterThan && outValue > ioRetValue) - || (!inGreaterThan && outValue < ioRetValue))) { - ioRetValue = outValue; - } - } -} - -// returns the max keyframe channel value. For bezier keyframes this includes the control -// points values as well -float Qt3DSDMTimelineKeyframe::GetMaxValue() const -{ - IAnimationCore *animCore = m_Doc->GetStudioSystem()->GetAnimationCore(); - float theRetVal = FLT_MIN; - do_all(m_KeyframeHandles, - std::bind(CompareAndSet, std::placeholders::_1, animCore, std::ref(theRetVal), true)); - - return theRetVal; -} - -// returns the min keyframe channel value. For bezier keyframes this includes the control -// points values as well -float Qt3DSDMTimelineKeyframe::GetMinValue() const -{ - IAnimationCore *animCore = m_Doc->GetStudioSystem()->GetAnimationCore(); - float theRetVal = FLT_MAX; - do_all(m_KeyframeHandles, - std::bind(CompareAndSet, std::placeholders::_1, animCore, std::ref(theRetVal), false)); - - return theRetVal; -} diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.h b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.h index 7799cc0d..99596c84 100644 --- a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.h +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineKeyframe.h @@ -78,10 +78,6 @@ public: void UpdateKeyframesTime(COffsetKeyframesCommandHelper *inCommandHelper, long inTime); void GetKeyframeHandles(TKeyframeHandleList &outList) const; - // support drawing graphs - float GetMaxValue() const; - float GetMinValue() const; - static float GetTimeInSecs(long inTime); }; diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/RowManager.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/RowManager.cpp index 50eff996..6dd0f991 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/RowManager.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/RowManager.cpp @@ -219,6 +219,9 @@ void RowManager::selectRow(RowTree *row, bool multiSelect) if (row->locked()) return; + if (multiSelect && row->propertyExpanded()) + return; + if (row->isProperty()) row = row->parentRow(); diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp index f33cf263..63b347b6 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp @@ -539,7 +539,7 @@ void TimelineGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) // Prepare to change selection to single selection at release if a multiselected // row is clicked without ctrl. if (!ctrlKeyDown && m_rowManager->isRowSelected(rowTree) - && !m_rowManager->isSingleSelected() ) { + && !m_rowManager->isSingleSelected()) { m_releaseSelectRow = rowTree; } m_rowManager->selectRow(rowTree, ctrlKeyDown); diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp index 6c6c6f86..b2587864 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp @@ -76,107 +76,114 @@ void RowTimelinePropertyGraph::paintGraphs(QPainter *painter, const QRectF &rect painter->setPen(QPen(CStudioPreferences::studioColor3())); painter->drawLine(edgeOffset.x(), m_graphY, rect.right(), m_graphY); - // draw channel curves + // draw channel curves and control points (for bezier) auto animHandles = m_propBinding->animationHandles(); + bool isBezier = m_rowTimeline->rowTree()->propBinding()->animationType() + == EAnimationTypeBezier; for (size_t channelIndex = 0; channelIndex < animHandles.size(); ++channelIndex) { - // Mahmoud_TODO: this block will draw the channel curves without sampling which is more - // efficient, but it cannot be used till the bezier equation is fixed (check:QT3DS-3777) - //-------------------------------draw using cubicTo -------------------------------------- -// QPainterPath path2; -// Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; -// m_animCore->GetKeyframes(animHandles[channelIndex], keyframeHandles); -// if (!keyframeHandles.empty()) { -// TKeyframe kf0 = m_animCore->GetKeyframeData(keyframeHandles[0]); -// path2.moveTo(getKeyframePosition(GetKeyframeSeconds(kf0), KeyframeValueValue(kf0)) -// + edgeOffset); - -// for (int i = 1; i < keyframeHandles.size(); ++i) { -// TKeyframe kf1 = m_animCore->GetKeyframeData(keyframeHandles[i]); -// float kf0InTime = -1, kf0InValue, kf0OutTime, kf0OutValue; -// float kf1InTime = -1, kf1InValue, kf1OutTime, kf1OutValue; -// getBezierValues(kf0, kf0InTime, kf0InValue, kf0OutTime, kf0OutValue); -// getBezierValues(kf1, kf1InTime, kf1InValue, kf1OutTime, kf1OutValue); -// if (kf0InTime != -1) { -// path2.cubicTo(getKeyframePosition(kf0OutTime, kf0OutValue) + edgeOffset, -// getKeyframePosition(kf1InTime, kf1InValue) + edgeOffset, -// getKeyframePosition(GetKeyframeSeconds(kf1), -// KeyframeValueValue(kf1)) -// + edgeOffset); -// } else { -// path2.lineTo(getKeyframePosition(GetKeyframeSeconds(kf1), -// KeyframeValueValue(kf1)) -// + edgeOffset); + if (m_rowTimeline->rowTree()->channelActive(channelIndex)) { + // draw channel curve + // Mahmoud_TODO: this block will draw the channel curves without sampling which is more + // efficient, but it cannot be used till the bezier equation is fixed (check:QT3DS-3777) + //-------------------------------draw using cubicTo ------------------------------------ +// QPainterPath path2; +// Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; +// m_animCore->GetKeyframes(animHandles[channelIndex], keyframeHandles); +// if (!keyframeHandles.empty()) { +// TKeyframe kf0 = m_animCore->GetKeyframeData(keyframeHandles[0]); +// path2.moveTo(getKeyframePosition(GetKeyframeSeconds(kf0), KeyframeValueValue(kf0)) +// + edgeOffset); + +// for (int i = 1; i < keyframeHandles.size(); ++i) { +// TKeyframe kf1 = m_animCore->GetKeyframeData(keyframeHandles[i]); +// float kf0InTime = -1, kf0InValue, kf0OutTime, kf0OutValue; +// float kf1InTime = -1, kf1InValue, kf1OutTime, kf1OutValue; +// getBezierValues(kf0, kf0InTime, kf0InValue, kf0OutTime, kf0OutValue); +// getBezierValues(kf1, kf1InTime, kf1InValue, kf1OutTime, kf1OutValue); +// if (kf0InTime != -1) { +// path2.cubicTo(getKeyframePosition(kf0OutTime, kf0OutValue) + edgeOffset, +// getKeyframePosition(kf1InTime, kf1InValue) + edgeOffset, +// getKeyframePosition(GetKeyframeSeconds(kf1), +// KeyframeValueValue(kf1)) +// + edgeOffset); +// } else { +// path2.lineTo(getKeyframePosition(GetKeyframeSeconds(kf1), +// KeyframeValueValue(kf1)) +// + edgeOffset); +// } +// // Mahmoud_TODO: handle ease in/out curves as well +// kf0 = kf1; // } -// // Mahmoud_TODO: handle ease in/out curves as well -// kf0 = kf1; +// painter->setPen(QPen(QColor("#ff0000"), 1)); +// painter->drawPath(path2); // } -// painter->setPen(QPen(QColor("#ff0000"), 1)); -// painter->drawPath(path2); -// } - //-------------------------------draw using cubicTo -------------------------------------- - - QPainterPath path; - int start_i = qMax(rect.x(), edgeOffset.x()); - for (int i = start_i; i < rect.right(); i += 5) { // 5 = sampling step in pixels - // Value time in ms - long time = (i - edgeOffset.x()) / (TimelineConstants::RULER_MILLI_W * timelineScale); - qreal value = m_propBinding->GetChannelValueAtTime(channelIndex, time); - qreal yPos = m_graphY - value * m_valScale; - if (i == start_i) - path.moveTo(i, yPos); - else - path.lineTo(i, yPos); - } - QColor channelColor; - if (channelIndex == 0) - channelColor = CStudioPreferences::GetXAxisColor(); - else if (channelIndex == 1) - channelColor = CStudioPreferences::GetYAxisColor(); - else if (channelIndex == 2) - channelColor = CStudioPreferences::GetZAxisColor(); - painter->setPen(QPen(channelColor, 2)); - painter->drawPath(path); - } - - // draw bezier controls - if (m_rowTimeline->rowTree()->propBinding()->animationType() == EAnimationTypeBezier) { - static const QPixmap pixBezierHandle = QPixmap("://images/breadcrumb_component_button.png"); - static const QPixmap pixBezierHandlePressed = QPixmap("://images/breadcrumb_component_grey" - "_button.png"); - - Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; - m_animCore->GetKeyframes(animHandles[0], keyframeHandles); - - size_t kfHandlesSize = keyframeHandles.size(); - for (size_t i = 0; i < kfHandlesSize; ++i) { - SBezierKeyframe kf = get<SBezierKeyframe>(m_animCore->GetKeyframeData( - keyframeHandles[i])); - - QPointF centerPos = getBezierControlPosition(kf) + edgeOffset; - const QPointF PIX_HALF_W = QPointF(8.0, 8.0); - - // draw vertical keyframe separator line - painter->setPen(QPen(CStudioPreferences::studioColor2(), 1)); - painter->drawLine(centerPos.x(), rect.y(), centerPos.x(), rect.height()); - - // draw tangent-in part - painter->setPen(CStudioPreferences::getBezierControlColor()); - if (i > 0) { - QPointF cInPos = getBezierControlPosition(kf, BezierControlType::In) + edgeOffset; - painter->drawLine(cInPos, centerPos); - painter->drawPixmap(cInPos - PIX_HALF_W, pixBezierHandle); + //-------------------------------draw using cubicTo ------------------------------------ + + QPainterPath path; + int start_i = qMax(rect.x(), edgeOffset.x()); + for (int i = start_i; i < rect.right(); i += 5) { // 5 = sampling step in pixels + // Value time in ms + long time = (i - edgeOffset.x()) / (RULER_MILLI_W * timelineScale); + qreal value = m_propBinding->GetChannelValueAtTime(channelIndex, time); + qreal yPos = m_graphY - value * m_valScale; + if (i == start_i) + path.moveTo(i, yPos); + else + path.lineTo(i, yPos); } - - // draw tangent-out part - if (i < kfHandlesSize - 1) { - QPointF cOutPos = getBezierControlPosition(kf, BezierControlType::Out) + edgeOffset; - painter->drawLine(cOutPos, centerPos); - painter->drawPixmap(cOutPos - PIX_HALF_W, pixBezierHandle); + QColor channelColor; + if (channelIndex == 0) + channelColor = CStudioPreferences::GetXAxisColor(); + else if (channelIndex == 1) + channelColor = CStudioPreferences::GetYAxisColor(); + else if (channelIndex == 2) + channelColor = CStudioPreferences::GetZAxisColor(); + painter->setPen(QPen(channelColor, 2)); + painter->drawPath(path); + + // draw bezier control points + if (isBezier) { + static const QPixmap pixBezierHandle("://images/breadcrumb_component_button.png"); + static const QPixmap pixBezierHandlePressed("://images/breadcrumb_component_grey" + "_button.png"); + + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + m_animCore->GetKeyframes(animHandles[channelIndex], keyframeHandles); + + size_t kfHandlesSize = keyframeHandles.size(); + for (size_t i = 0; i < kfHandlesSize; ++i) { + SBezierKeyframe kf = get<SBezierKeyframe>(m_animCore->GetKeyframeData( + keyframeHandles[i])); + + QPointF centerPos = getBezierControlPosition(kf) + edgeOffset; + const QPointF PIX_HALF_W = QPointF(8.0, 8.0); + + // draw vertical keyframe separator line + painter->setPen(QPen(CStudioPreferences::studioColor2(), 1)); + painter->drawLine(centerPos.x(), rect.y(), centerPos.x(), rect.height()); + + // draw tangent-in part + painter->setPen(CStudioPreferences::getBezierControlColor()); + if (i > 0) { + QPointF cInPos = getBezierControlPosition(kf, BezierControlType::In) + + edgeOffset; + painter->drawLine(cInPos, centerPos); + painter->drawPixmap(cInPos - PIX_HALF_W, pixBezierHandle); + } + + // draw tangent-out part + if (i < kfHandlesSize - 1) { + QPointF cOutPos = getBezierControlPosition(kf, BezierControlType::Out) + + edgeOffset; + painter->drawLine(cOutPos, centerPos); + painter->drawPixmap(cOutPos - PIX_HALF_W, pixBezierHandle); + } + + // draw center point + painter->setPen(QPen(CStudioPreferences::getBezierControlColor(), 3)); + painter->drawPoint(centerPos); + } } - - // draw center point - painter->setPen(QPen(CStudioPreferences::getBezierControlColor(), 3)); - painter->drawPoint(centerPos); } } } @@ -194,28 +201,29 @@ TimelineControlType RowTimelinePropertyGraph::getClickedBezierControl(const QPoi if (m_rowTimeline->rowTree()->propBinding()->animationType() != EAnimationTypeBezier) return TimelineControlType::None; - m_currKeyframeHandles.clear(); - m_currKeyframeData.clear(); + m_currKeyframeData.first = 0; // reset the data auto animHandles = m_propBinding->animationHandles(); - Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; - m_animCore->GetKeyframes(animHandles[0], keyframeHandles); - const double CONTROL_RADIUS = 8; - for (auto kfHandle : keyframeHandles) { - SBezierKeyframe kf = get<SBezierKeyframe>(m_animCore->GetKeyframeData(kfHandle)); - - QPointF cInPos = getBezierControlPosition(kf, BezierControlType::In); - QPointF cOutPos = getBezierControlPosition(kf, BezierControlType::Out); - bool clickedInHandle = QLineF(cInPos, pos).length() < CONTROL_RADIUS; - bool clickedOutHandle = QLineF(cOutPos, pos).length() < CONTROL_RADIUS; - if (clickedInHandle || clickedOutHandle) { - m_rowTimeline->rowTree()->propBinding()->GetKeyframeByTime(kf.m_KeyframeSeconds * 1000) - ->getUI()->binding->GetKeyframeHandles(m_currKeyframeHandles); - for (auto h : m_currKeyframeHandles) - m_currKeyframeData.append({h, m_animCore->GetKeyframeData(h)}); - - return clickedInHandle ? TimelineControlType::BezierInHandle - : TimelineControlType::BezierOutHandle; + for (size_t chIndex = 0; chIndex < animHandles.size(); ++chIndex) { + if (m_rowTimeline->rowTree()->channelActive(chIndex)) { + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + m_animCore->GetKeyframes(animHandles[chIndex], keyframeHandles); + const double CONTROL_RADIUS = 8; + for (auto kfHandle : keyframeHandles) { + SBezierKeyframe kf = get<SBezierKeyframe>(m_animCore->GetKeyframeData(kfHandle)); + + QPointF cInPos = getBezierControlPosition(kf, BezierControlType::In); + QPointF cOutPos = getBezierControlPosition(kf, BezierControlType::Out); + bool clickedInHandle = QLineF(cInPos, pos).length() < CONTROL_RADIUS; + bool clickedOutHandle = QLineF(cOutPos, pos).length() < CONTROL_RADIUS; + if (clickedInHandle || clickedOutHandle) { + m_currKeyframeData.first = kfHandle; + m_currKeyframeData.second = m_animCore->GetKeyframeData(kfHandle); + + return clickedInHandle ? TimelineControlType::BezierInHandle + : TimelineControlType::BezierOutHandle; + } + } } } @@ -258,29 +266,27 @@ QPointF RowTimelinePropertyGraph::getKeyframePosition(float time, float value) c void RowTimelinePropertyGraph::updateBezierControlValue(TimelineControlType controlType, const QPointF &scenePos) { - QPointF p = m_rowTimeline->mapFromScene(scenePos.x() - TimelineConstants::RULER_EDGE_OFFSET, - scenePos.y()); + QPointF p = m_rowTimeline->mapFromScene(scenePos.x() - RULER_EDGE_OFFSET, scenePos.y()); + // time and value at current mouse position float time = m_rowTimeline->rowTree()->m_scene->ruler()->distanceToTime(p.x()) / 1000.f; // secs float value = (m_graphY - p.y()) / m_valScale; - // first channel keyframe - SBezierKeyframe kf0 = get<SBezierKeyframe>(m_animCore->GetKeyframeData( - m_currKeyframeHandles[0])); + SBezierKeyframe kf = get<SBezierKeyframe>(m_currKeyframeData.second); bool isBezierIn = controlType == TimelineControlType::BezierInHandle; // prevent handles from moving to the other side of the keyframe - if ((isBezierIn && time > kf0.m_KeyframeSeconds) - || (!isBezierIn && time < kf0.m_KeyframeSeconds)) { - time = kf0.m_KeyframeSeconds; + if ((isBezierIn && time > kf.m_KeyframeSeconds) + || (!isBezierIn && time < kf.m_KeyframeSeconds)) { + time = kf.m_KeyframeSeconds; } // prevent handles from going beyond prev. and next keyframes - auto animHandles = m_propBinding->animationHandles(); + Qt3DSDMAnimationHandle anim = m_animCore->GetAnimationForKeyframe(m_currKeyframeData.first); Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; - m_animCore->GetKeyframes(animHandles[0], keyframeHandles); + m_animCore->GetKeyframes(anim, keyframeHandles); for (size_t i = 0; i < keyframeHandles.size(); ++i) { - if (keyframeHandles[i] == m_currKeyframeHandles[0]) { + if (keyframeHandles[i] == m_currKeyframeData.first) { float currKfTime = KeyframeTime(m_animCore->GetKeyframeData(keyframeHandles[i])); float prevKfTime = i > 0 ? KeyframeTime(m_animCore->GetKeyframeData(keyframeHandles[i - 1])) : -FLT_MAX; @@ -289,41 +295,32 @@ void RowTimelinePropertyGraph::updateBezierControlValue(TimelineControlType cont if (isBezierIn) { if (time < prevKfTime) time = prevKfTime; - if (!CHotKeys::IsKeyDown(Qt::ControlModifier) - && time < currKfTime * 2 - nextKfTime) { + if (!CHotKeys::isCtrlDown() && time < currKfTime * 2 - nextKfTime) time = currKfTime * 2 - nextKfTime; - } } else { // bezier out if (time > nextKfTime) time = nextKfTime; - if (!CHotKeys::IsKeyDown(Qt::ControlModifier) - && time > currKfTime * 2 - prevKfTime) { + if (!CHotKeys::isCtrlDown() && time > currKfTime * 2 - prevKfTime) time = currKfTime * 2 - prevKfTime; - } } break; } } - for (size_t i = 0; i < m_currKeyframeHandles.size(); ++i) { - SBezierKeyframe kf = get<SBezierKeyframe>(m_animCore->GetKeyframeData( - m_currKeyframeHandles[i])); - float &currHandleTime = isBezierIn ? kf.m_InTangentTime : kf.m_OutTangentTime; - float &currHandleValue = isBezierIn ? kf.m_InTangentValue : kf.m_OutTangentValue; - float &otherHandleTime = isBezierIn ? kf.m_OutTangentTime : kf.m_InTangentTime; - float &otherHandleValue = isBezierIn ? kf.m_OutTangentValue : kf.m_InTangentValue; + float &currHandleTime = isBezierIn ? kf.m_InTangentTime : kf.m_OutTangentTime; + float &currHandleValue = isBezierIn ? kf.m_InTangentValue : kf.m_OutTangentValue; + float &otherHandleTime = isBezierIn ? kf.m_OutTangentTime : kf.m_InTangentTime; + float &otherHandleValue = isBezierIn ? kf.m_OutTangentValue : kf.m_InTangentValue; - currHandleTime = time; - currHandleValue = value + (kf.m_KeyframeValue - kf0.m_KeyframeValue); + currHandleTime = time; + currHandleValue = value; - if (!CHotKeys::IsKeyDown(Qt::ControlModifier)) { - otherHandleTime = kf.m_KeyframeSeconds + (kf.m_KeyframeSeconds - time); - otherHandleValue = kf.m_KeyframeValue + (kf.m_KeyframeValue - currHandleValue); - } - - m_animCore->SetKeyframeData(m_currKeyframeHandles[i], kf); + if (!CHotKeys::isCtrlDown()) { + otherHandleTime = kf.m_KeyframeSeconds + (kf.m_KeyframeSeconds - time); + otherHandleValue = kf.m_KeyframeValue + (kf.m_KeyframeValue - currHandleValue); } + m_animCore->SetKeyframeData(m_currKeyframeData.first, kf); } // adjust graph scale and y so that all keyframe and control points are visible @@ -333,8 +330,47 @@ void RowTimelinePropertyGraph::fitGraph() m_graphY = m_rowTimeline->size().height() - MARGIN_Y; m_graphH = m_rowTimeline->size().height() - MARGIN_Y * 2; - m_valScale = m_graphH / (m_propBinding->GetMaximumValue() - m_propBinding->GetMinimumValue()); - m_graphY += m_propBinding->GetMinimumValue() * m_valScale; + // get min/max keyframes values in the active channels + float minVal = FLT_MAX; + float maxVal = -FLT_MAX; + const auto animHandles = m_propBinding->animationHandles(); + for (int i = 0; i < animHandles.size(); ++i) { + if (m_rowTimeline->rowTree()->channelActive(i)) { + Qt3DSDMTimelineKeyframe::TKeyframeHandleList keyframeHandles; + m_animCore->GetKeyframes(animHandles[i], keyframeHandles); + for (auto kfHandle : keyframeHandles) { + TKeyframe keyframeData = m_animCore->GetKeyframeData(kfHandle); + float value = KeyframeValueValue(keyframeData); + if (value < minVal) + minVal = value; + if (value > maxVal) + maxVal = value; + + // for bezier keyframes compare tangents in/out also + if (keyframeData.getType() == qt3dsdm::EAnimationTypeBezier) { + float timeIn, valueIn, timeOut, valueOut; + getBezierValues(keyframeData, timeIn, valueIn, timeOut, valueOut); + + if (!m_animCore->IsFirstKeyframe(kfHandle)) { // check tangent-in value + if (valueIn < minVal) + minVal = valueIn; + if (valueIn > maxVal) + maxVal = valueIn; + } + + if (!m_animCore->IsLastKeyframe(kfHandle)) { // check tangent-out value + if (valueOut < minVal) + minVal = valueOut; + if (valueOut > maxVal) + maxVal = valueOut; + } + } + } + } + } + + m_valScale = m_graphH / (maxVal - minVal); + m_graphY += minVal * m_valScale; m_rowTimeline->update(); } @@ -365,15 +401,13 @@ void RowTimelinePropertyGraph::pan(qreal dy) void RowTimelinePropertyGraph::commitBezierEdit() { - // reset the changed keyframes and commit the changes, so the undo/redo works correctly - QVector<std::pair<qt3dsdm::Qt3DSDMKeyframeHandle, TKeyframe>> changedKfs; - for (auto data : qAsConst(m_currKeyframeData)) { - changedKfs.append({data.first, m_animCore->GetKeyframeData(data.first)}); - m_animCore->SetKeyframeData(data.first, data.second); - } + // reset the moved keyframe and commit the change, so the undo/redo works correctly + // Mahmoud_TODO: improve this (using Updatable editor?) + TKeyframe movedKeyframe = m_animCore->GetKeyframeData(m_currKeyframeData.first); + m_animCore->SetKeyframeData(m_currKeyframeData.first, m_currKeyframeData.second); Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), tr("Edit Bezier curve")) - ->setBezierKeyframeValues(changedKfs); + ->setBezierKeyframeValue(m_currKeyframeData.first, movedKeyframe); } void RowTimelinePropertyGraph::setExpandHeight(int h) diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.h b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.h index 5c3f1a71..93984fd3 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.h +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.h @@ -68,8 +68,7 @@ private: BezierControlType type = BezierControlType::None) const; QPointF getKeyframePosition(float time, float value) const; - Qt3DSDMTimelineKeyframe::TKeyframeHandleList m_currKeyframeHandles; - QVector<std::pair<qt3dsdm::Qt3DSDMKeyframeHandle, qt3dsdm::TKeyframe>> m_currKeyframeData; + std::pair<qt3dsdm::Qt3DSDMKeyframeHandle, qt3dsdm::TKeyframe> m_currKeyframeData; RowTimeline *m_rowTimeline = nullptr; ITimelineItemProperty *m_propBinding = nullptr; qt3dsdm::IAnimationCore *m_animCore = nullptr; diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp index f4159769..a86482fd 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp @@ -48,6 +48,7 @@ #include "Qt3DSDMSlides.h" #include "StudioUtils.h" #include "TimelineToolbar.h" +#include "HotKeys.h" #include <QtGui/qpainter.h> #include "QtGui/qtextcursor.h" @@ -331,15 +332,31 @@ void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q else if (m_actionStates & ActionState::ComponentAction) // component has action painter->drawPixmap(0, 0, hiResIcons ? pixCompAction2x : pixCompAction); } else { // property row - if (m_propGraphExpanded - && m_PropBinding->animationType() == qt3dsdm::EAnimationTypeBezier) { - const int PROP_GRAPH_CONTROLS_Y = int(TimelineConstants::ROW_H * 1.5); - m_rectMaximizePropGraph.setRect(rightDividerX() - 16 * 1.1, PROP_GRAPH_CONTROLS_Y, - ICON_SIZE, ICON_SIZE); - m_rectFitPropGraph.setRect(rightDividerX() - 16 * 2.2, PROP_GRAPH_CONTROLS_Y, - ICON_SIZE, ICON_SIZE); - painter->drawPixmap(m_rectMaximizePropGraph, pixShy); - painter->drawPixmap(m_rectFitPropGraph, pixShy); + if (m_propGraphExpanded) { + // draw maximize, fit graph buttons + if (m_PropBinding->animationType() == qt3dsdm::EAnimationTypeBezier) { + const int PROP_GRAPH_CONTROLS_Y = int(TimelineConstants::ROW_H * 1.5); + m_rectMaximizePropGraph.setRect(rightDividerX() - 16 * 1.1, PROP_GRAPH_CONTROLS_Y, + ICON_SIZE, ICON_SIZE); + m_rectFitPropGraph.setRect(rightDividerX() - 16 * 2.2, PROP_GRAPH_CONTROLS_Y, + ICON_SIZE, ICON_SIZE); + painter->drawPixmap(m_rectMaximizePropGraph, pixShy); + painter->drawPixmap(m_rectFitPropGraph, pixShy); + } + + // draw channel selection buttons + const QString channelNames = m_rowTimeline->isColorProperty() ? QStringLiteral("rgba") + : QStringLiteral("xyzw"); + for (int i = 0; i < m_rectChannels.size(); ++i) { + if (m_activeChannels[i]) + painter->fillRect(m_rectChannels[i], CStudioPreferences::selectionColor()); + + painter->setPen(CStudioPreferences::studioColor3()); + painter->drawRect(m_rectChannels[i]); + + painter->setPen(CStudioPreferences::textColor()); + painter->drawText(m_rectChannels[i].topLeft() + QPointF(5, 12), channelNames.at(i)); + } } } @@ -572,6 +589,18 @@ void RowTree::setPropBinding(ITimelineItemProperty *binding) { m_PropBinding = binding; + m_rectChannels.resize(m_PropBinding->GetChannelCount()); + m_activeChannels.resize(m_PropBinding->GetChannelCount()); + + // For bezier animation select first channel (ie x) only by default, else select all channels + if (m_PropBinding->animationType() == qt3dsdm::EAnimationTypeBezier) + m_activeChannels[0] = true; + else + std::fill(m_activeChannels.begin(), m_activeChannels.end(), true); + + for (int i = 0; i < m_rectChannels.size(); ++i) + m_rectChannels[i].setRect(22, TimelineConstants::ROW_H * (i+1), 16, 16); + if (parentRow()->expanded()) setRowVisible(true); @@ -997,13 +1026,36 @@ TreeControlType RowTree::getClickedControl(const QPointF &scenePos) } if (isProperty()) { - if (m_rectFitPropGraph.contains(p)) { + if (m_rectFitPropGraph.contains(p)) { // toggle fit graph m_rowTimeline->propertyGraph()->fitGraph(); - } else if (m_rectMaximizePropGraph.contains(p)) { + } else if (m_rectMaximizePropGraph.contains(p)) { // toggle maximize graph m_propGraphHeight = m_propGraphHeight == TimelineConstants::ROW_GRAPH_H ? TimelineConstants::ROW_GRAPH_H_MAX : TimelineConstants::ROW_GRAPH_H; m_rowTimeline->propertyGraph()->setExpandHeight(m_propGraphHeight); animateExpand(ExpandState::Expanded); + } else { + auto it = std::find_if(m_rectChannels.begin(), m_rectChannels.end(), + [&p](const QRect &r){ return r.contains(p); }); + + if (it != m_rectChannels.end()) { // clicked a channel button + bool ctrlDown = CHotKeys::isCtrlDown(); + int chIdx = int(it->y() / TimelineConstants::ROW_H) - 1; + int numSelectedChannel = std::count(m_activeChannels.begin(), + m_activeChannels.end(), true); + bool isSingleSelected = numSelectedChannel == 1 && m_activeChannels[chIdx]; + bool isMultiSelected = numSelectedChannel > 1 && m_activeChannels[chIdx]; + if (!isSingleSelected && !(isMultiSelected && !ctrlDown)) + m_activeChannels[chIdx] ^= 1; + + if (!ctrlDown) { + for (int i = 0; i < m_activeChannels.size(); ++i) { + if (i != chIdx) + m_activeChannels[i] = false; + } + } + m_rowTimeline->propertyGraph()->fitGraph(); + update(); + } } } @@ -1346,6 +1398,12 @@ bool RowTree::propertyExpanded() const void RowTree::togglePropertyExpanded(const QPointF &scenePos) { QPoint p = mapFromScene(scenePos).toPoint(); + + for (int i = 0; i < m_rectChannels.size(); ++i) { + if (m_rectChannels[i].contains(p)) + return; // mouse over a channel button + } + if (!m_rectFitPropGraph.contains(p) && !m_rectMaximizePropGraph.contains(p)) setPropertyExpanded(!m_propGraphExpanded); } @@ -1376,3 +1434,7 @@ void RowTree::showDataInputSelector(const QString &propertyname, const QPoint &p pos); } +bool RowTree::channelActive(int channelIndex) const +{ + return m_activeChannels[channelIndex]; +} diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.h b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.h index d82eada1..0e467e69 100644 --- a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.h +++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.h @@ -156,6 +156,7 @@ public: int rightDividerX() const; int clipX() const; qt3dsdm::Qt3DSDMInstanceHandle instance() const; + bool channelActive(int channelIndex) const; protected: void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; @@ -216,6 +217,9 @@ private: QRect m_rectType; QRect m_rectMaximizePropGraph; QRect m_rectFitPropGraph; + QVector<QRect> m_rectChannels; + QVector<bool> m_activeChannels; + QParallelAnimationGroup m_expandAnimation; QPropertyAnimation *m_expandHeightAnimation; |