summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKnud Dollereder <knud.dollereder@qt.io>2019-06-03 15:12:47 +0200
committerKnud Dollereder <knud.dollereder@qt.io>2019-06-04 09:00:04 +0000
commitbb54345474c928250f65f778fcfc2fb061f72406 (patch)
treef866babde39b2cb1fd4a65bea6c02845989457f2 /src
parent69e0ce6b1aee59585f141a320cef4f980e6ae7dc (diff)
Implement insertion and deletion of keyframes
Change-Id: I3d0c10a765588a53ae2053b74b8a3173c440e982 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/curveeditor/animationcurve.cpp58
-rw-r--r--src/curveeditor/animationcurve.h8
-rw-r--r--src/curveeditor/curveeditorstyle.h3
-rw-r--r--src/curveeditor/detail/curveitem.cpp70
-rw-r--r--src/curveeditor/detail/curveitem.h10
-rw-r--r--src/curveeditor/detail/curvesegment.cpp47
-rw-r--r--src/curveeditor/detail/curvesegment.h10
-rw-r--r--src/curveeditor/detail/graphicsview.cpp27
-rw-r--r--src/curveeditor/detail/graphicsview.h4
-rw-r--r--src/curveeditor/detail/selector.cpp7
-rw-r--r--src/curveeditor/detail/utils.cpp10
-rw-r--r--src/curveeditor/detail/utils.h16
12 files changed, 236 insertions, 34 deletions
diff --git a/src/curveeditor/animationcurve.cpp b/src/curveeditor/animationcurve.cpp
index 8e2d522..17e17dd 100644
--- a/src/curveeditor/animationcurve.cpp
+++ b/src/curveeditor/animationcurve.cpp
@@ -25,6 +25,7 @@
#include "animationcurve.h"
#include "detail/curvesegment.h"
+#include "detail/utils.h"
#include <QLineF>
@@ -91,6 +92,19 @@ double AnimationCurve::maximumValue() const
return m_maxY;
}
+CurveSegment AnimationCurve::segment(double time) const
+{
+ CurveSegment seg;
+ for (auto &frame : m_frames) {
+ if (frame.position().x() > time) {
+ seg.setRight(frame);
+ return seg;
+ }
+ seg.setLeft(frame);
+ }
+ return CurveSegment();
+}
+
std::vector<Keyframe> AnimationCurve::keyframes() const
{
return m_frames;
@@ -141,7 +155,7 @@ std::vector<double> AnimationCurve::xForY(double y, uint segment) const
return std::vector<double>();
}
-bool AnimationCurve::intersects(const QPointF &coord, double radius)
+bool AnimationCurve::intersects(const QPointF &coord, double radius) const
{
if (m_frames.size() < 2)
return false;
@@ -152,13 +166,13 @@ bool AnimationCurve::intersects(const QPointF &coord, double radius)
current.setLeft(m_frames.at(0));
for (size_t i = 1; i < m_frames.size(); ++i) {
- Keyframe &frame = m_frames.at(i);
+ const Keyframe &frame = m_frames.at(i);
current.setRight(frame);
- if (current.containsX(coord.x() - radius) ||
- current.containsX(coord.x()) ||
- current.containsX(coord.x() + radius)) {
+ if (current.containsX(coord.x() - radius)
+ || current.containsX(coord.x())
+ || current.containsX(coord.x() + radius)) {
influencer.push_back(current);
}
@@ -184,4 +198,38 @@ bool AnimationCurve::intersects(const QPointF &coord, double radius)
return false;
}
+void AnimationCurve::insert(double time)
+{
+ CurveSegment seg = segment(time);
+
+ if (!seg.isValid())
+ return;
+
+ for (double t : seg.tForX(time)) {
+ auto p0 = lerp(t, seg.left().position(), seg.left().rightHandle());
+ auto p1 = lerp(t, seg.left().rightHandle(), seg.right().leftHandle());
+ auto p2 = lerp(t, seg.right().leftHandle(), seg.right().position());
+
+ auto p01 = lerp(t, p0, p1);
+ auto p12 = lerp(t, p1, p2);
+ auto p01p12 = lerp(t, p01, p12);
+
+ std::vector<Keyframe> frames = {
+ Keyframe(seg.left().position(), seg.left().leftHandle(), p0),
+ Keyframe(p01p12, p01, p12),
+ Keyframe(seg.right().position(), p2, seg.right().rightHandle())
+ };
+
+ auto samePosition = [frames](const Keyframe &frame) {
+ return frame.position() == frames[0].position();
+ };
+
+ auto iter = std::find_if(m_frames.begin(), m_frames.end(), samePosition);
+ if (iter != m_frames.end()) {
+ auto erased = m_frames.erase(iter, iter + 2);
+ m_frames.insert(erased, frames.begin(), frames.end());
+ }
+ }
+}
+
} // End namespace DesignTools.
diff --git a/src/curveeditor/animationcurve.h b/src/curveeditor/animationcurve.h
index 0533e47..7e483cf 100644
--- a/src/curveeditor/animationcurve.h
+++ b/src/curveeditor/animationcurve.h
@@ -31,6 +31,8 @@
namespace DesignTools {
+class CurveSegment;
+
class AnimationCurve
{
public:
@@ -48,6 +50,8 @@ public:
double maximumValue() const;
+ CurveSegment segment(double time) const;
+
std::vector<Keyframe> keyframes() const;
std::vector<QPointF> extrema() const;
@@ -56,7 +60,9 @@ public:
std::vector<double> xForY(double y, uint segment) const;
- bool intersects(const QPointF &coord, double radius);
+ bool intersects(const QPointF &coord, double radius) const;
+
+ void insert(double time);
private:
std::vector<Keyframe> m_frames;
diff --git a/src/curveeditor/curveeditorstyle.h b/src/curveeditor/curveeditorstyle.h
index 03ea11c..835d138 100644
--- a/src/curveeditor/curveeditorstyle.h
+++ b/src/curveeditor/curveeditorstyle.h
@@ -84,6 +84,9 @@ struct Shortcuts
Shortcut zoom = Shortcut(Qt::RightButton, Qt::AltModifier);
Shortcut pan = Shortcut(Qt::MiddleButton, Qt::AltModifier);
Shortcut frameAll = Shortcut(Qt::NoModifier, Qt::Key_A);
+
+ Shortcut insertKeyframe = Shortcut(Qt::MiddleButton, Qt::NoModifier);
+ Shortcut deleteKeyframe = Shortcut(Qt::NoModifier, Qt::Key_Delete);
};
struct CurveEditorStyle
diff --git a/src/curveeditor/detail/curveitem.cpp b/src/curveeditor/detail/curveitem.cpp
index 5973e70..43d3edc 100644
--- a/src/curveeditor/detail/curveitem.cpp
+++ b/src/curveeditor/detail/curveitem.cpp
@@ -31,8 +31,18 @@
#include <QPainter>
#include <QPainterPath>
+#include <algorithm>
+
namespace DesignTools {
+template<typename T>
+void freeClear(std::vector<T *> &vec)
+{
+ for (auto *&el : vec)
+ delete el;
+ vec.clear();
+}
+
CurveItem::CurveItem(QGraphicsItem *parent)
: QGraphicsObject(parent)
, m_id(0)
@@ -58,17 +68,7 @@ CurveItem::CurveItem(unsigned int id, const AnimationCurve &curve, QGraphicsItem
setFlag(QGraphicsItem::ItemIsMovable, false);
- auto emitCurveChanged = [this]() {
- m_itemDirty = true;
- m_pathDirty = true;
- update();
- };
-
- for (auto frame : curve.keyframes()) {
- auto *item = new KeyframeItem(frame, this);
- QObject::connect(item, &KeyframeItem::redrawCurve, emitCurveChanged);
- m_keyframes.push_back(item);
- }
+ setCurve(curve);
}
CurveItem::~CurveItem() {}
@@ -128,6 +128,11 @@ bool CurveItem::isDirty() const
return m_itemDirty;
}
+bool CurveItem::isUnderMouse() const
+{
+ return m_underMouse;
+}
+
bool CurveItem::hasSelection() const
{
for (auto *frame : m_keyframes) {
@@ -185,6 +190,20 @@ void CurveItem::setDirty(bool dirty)
m_itemDirty = dirty;
}
+void CurveItem::setCurve(const AnimationCurve &curve)
+{
+ freeClear(m_keyframes);
+
+ for (auto frame : curve.keyframes()) {
+ auto *item = new KeyframeItem(frame, this);
+ item->setComponentTransform(m_transform);
+ m_keyframes.push_back(item);
+ QObject::connect(item, &KeyframeItem::redrawCurve, this, &CurveItem::emitCurveChanged);
+ }
+
+ emitCurveChanged();
+}
+
QRectF CurveItem::setComponentTransform(const QTransform &transform)
{
m_pathDirty = true;
@@ -221,4 +240,33 @@ void CurveItem::setIsUnderMouse(bool under)
}
}
+void CurveItem::insertKeyframeByTime(double time)
+{
+ AnimationCurve acurve = curve();
+ acurve.insert(time);
+ setCurve(acurve);
+}
+
+void CurveItem::deleteSelectedKeyframes()
+{
+ for (auto *&item : m_keyframes) {
+ if (item->selected()) {
+ delete item;
+ item = nullptr;
+ }
+ }
+
+ auto isNullptr = [](KeyframeItem *frame) { return frame == nullptr; };
+ auto iter = std::remove_if(m_keyframes.begin(), m_keyframes.end(), isNullptr);
+ m_keyframes.erase(iter, m_keyframes.end());
+ emitCurveChanged();
+}
+
+void CurveItem::emitCurveChanged()
+{
+ m_itemDirty = true;
+ m_pathDirty = true;
+ update();
+}
+
} // End namespace DesignTools.
diff --git a/src/curveeditor/detail/curveitem.h b/src/curveeditor/detail/curveitem.h
index 90e68e2..5272996 100644
--- a/src/curveeditor/detail/curveitem.h
+++ b/src/curveeditor/detail/curveitem.h
@@ -59,6 +59,8 @@ public:
bool isDirty() const;
+ bool isUnderMouse() const;
+
bool hasSelection() const;
unsigned int id() const;
@@ -67,6 +69,8 @@ public:
void setDirty(bool dirty);
+ void setCurve(const AnimationCurve &curve);
+
QRectF setComponentTransform(const QTransform &transform);
void setStyle(const CurveEditorStyle &style);
@@ -75,9 +79,15 @@ public:
void setIsUnderMouse(bool under);
+ void insertKeyframeByTime(double time);
+
+ void deleteSelectedKeyframes();
+
private:
QPainterPath path() const;
+ void emitCurveChanged();
+
unsigned int m_id;
CurveItemStyleOption m_style;
diff --git a/src/curveeditor/detail/curvesegment.cpp b/src/curveeditor/detail/curvesegment.cpp
index 40f675f..67bc8e0 100644
--- a/src/curveeditor/detail/curvesegment.cpp
+++ b/src/curveeditor/detail/curvesegment.cpp
@@ -145,6 +145,21 @@ CurveSegment::CurveSegment(const Keyframe &left, const Keyframe &right)
, m_right(right)
{}
+bool CurveSegment::isValid()
+{
+ return m_left.position() != m_right.position();
+}
+
+Keyframe CurveSegment::left() const
+{
+ return m_left;
+}
+
+Keyframe CurveSegment::right() const
+{
+ return m_right;
+}
+
bool CurveSegment::containsX(double x) const
{
return m_left.position().x() <= x && m_right.position().x() >= x;
@@ -210,6 +225,38 @@ std::vector<QPointF> CurveSegment::extrema() const
return out;
}
+std::vector<double> CurveSegment::tForX(double x)
+{
+ auto polynomial = CubicPolynomial(
+ m_left.position().x() - x,
+ m_left.rightHandle().x() - x,
+ m_right.leftHandle().x() - x,
+ m_right.position().x() - x);
+
+ std::vector<double> out;
+ for (double t : polynomial.roots()) {
+ if (t >= 0.0 && t <= 1.0)
+ out.push_back(t);
+ }
+ return out;
+}
+
+std::vector<double> CurveSegment::tForY(double y)
+{
+ auto polynomial = CubicPolynomial(
+ m_left.position().y() - y,
+ m_left.rightHandle().y() - y,
+ m_right.leftHandle().y() - y,
+ m_right.position().y() - y);
+
+ std::vector<double> out;
+ for (double t : polynomial.roots()) {
+ if (t >= 0.0 && t <= 1.0)
+ out.push_back(t);
+ }
+ return out;
+}
+
std::vector<double> CurveSegment::yForX(double x) const
{
std::vector<double> out;
diff --git a/src/curveeditor/detail/curvesegment.h b/src/curveeditor/detail/curvesegment.h
index ce2d700..9c7ceb6 100644
--- a/src/curveeditor/detail/curvesegment.h
+++ b/src/curveeditor/detail/curvesegment.h
@@ -40,12 +40,22 @@ public:
CurveSegment(const Keyframe &first, const Keyframe &last);
+ bool isValid();
+
+ Keyframe left() const;
+
+ Keyframe right() const;
+
bool containsX(double x) const;
QPointF evaluate(double t) const;
std::vector<QPointF> extrema() const;
+ std::vector<double> tForX(double x);
+
+ std::vector<double> tForY(double y);
+
std::vector<double> yForX(double x) const;
std::vector<double> xForY(double y) const;
diff --git a/src/curveeditor/detail/graphicsview.cpp b/src/curveeditor/detail/graphicsview.cpp
index ae60888..4162f53 100644
--- a/src/curveeditor/detail/graphicsview.cpp
+++ b/src/curveeditor/detail/graphicsview.cpp
@@ -220,6 +220,8 @@ void GraphicsView::keyPressEvent(QKeyEvent *event)
Shortcut shortcut(event->modifiers(), static_cast<Qt::Key>(event->key()));
if (shortcut == m_style.shortcuts.frameAll)
applyZoom(0.0, 0.0);
+ else if (shortcut == m_style.shortcuts.deleteKeyframe)
+ deleteSelectedKeyframes();
}
void GraphicsView::mousePressEvent(QMouseEvent *event)
@@ -228,6 +230,11 @@ void GraphicsView::mousePressEvent(QMouseEvent *event)
return;
Shortcut shortcut(event);
+ if (shortcut == m_style.shortcuts.insertKeyframe) {
+ insertKeyframe(globalToRaster(event->globalPos()).x());
+ return;
+ }
+
if (shortcut == Shortcut(Qt::LeftButton)) {
QPointF pos = mapToScene(event->pos());
if (timeScaleRect().contains(pos)) {
@@ -385,6 +392,26 @@ void GraphicsView::applyZoom(double x, double y, const QPoint &pivot)
}
}
+void GraphicsView::insertKeyframe(double time)
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) {
+ if (curveItem->isUnderMouse())
+ curveItem->insertKeyframeByTime(std::round(time));
+ }
+ }
+}
+
+void GraphicsView::deleteSelectedKeyframes()
+{
+ const auto itemList = items();
+ for (auto *item : itemList) {
+ if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item))
+ curveItem->deleteSelectedKeyframes();
+ }
+}
+
void GraphicsView::drawGrid(QPainter *painter, const QRectF &rect)
{
QRectF gridRect = rect.adjusted(
diff --git a/src/curveeditor/detail/graphicsview.h b/src/curveeditor/detail/graphicsview.h
index 1975e36..7888b7b 100644
--- a/src/curveeditor/detail/graphicsview.h
+++ b/src/curveeditor/detail/graphicsview.h
@@ -122,6 +122,10 @@ protected:
private:
void applyZoom(double x, double y, const QPoint &pivot = QPoint());
+ void insertKeyframe(double time);
+
+ void deleteSelectedKeyframes();
+
void drawGrid(QPainter *painter, const QRectF &rect);
void drawExtremaX(QPainter *painter, const QRectF &rect);
diff --git a/src/curveeditor/detail/selector.cpp b/src/curveeditor/detail/selector.cpp
index b6e7a8f..5e9ffb5 100644
--- a/src/curveeditor/detail/selector.cpp
+++ b/src/curveeditor/detail/selector.cpp
@@ -75,11 +75,12 @@ void Selector::mouseMove(QMouseEvent *event, GraphicsView *view, Playhead &playh
if (m_mouseInit.isNull())
return;
- QPointF delta = event->globalPos() - m_mouseInit;
- if (delta.manhattanLength() < QApplication::startDragDistance())
+ if ((event->globalPos() - m_mouseInit).manhattanLength() < QApplication::startDragDistance())
return;
- if (m_shortcut == m_shortcuts.newSelection || m_shortcut == m_shortcuts.addToSelection
+ QPointF delta = event->globalPos() - m_mouseCurr;
+ if (m_shortcut == m_shortcuts.newSelection
+ || m_shortcut == m_shortcuts.addToSelection
|| m_shortcut == m_shortcuts.removeFromSelection
|| m_shortcut == m_shortcuts.toggleSelection) {
if (view->hasActiveItem())
diff --git a/src/curveeditor/detail/utils.cpp b/src/curveeditor/detail/utils.cpp
index 4933bcb..4bc1cd2 100644
--- a/src/curveeditor/detail/utils.cpp
+++ b/src/curveeditor/detail/utils.cpp
@@ -31,16 +31,6 @@
namespace DesignTools {
-double clamp(double val, double lo, double hi)
-{
- return val < lo ? lo : (val > hi ? hi : val);
-}
-
-double lerp(double blend, double a, double b)
-{
- return (1.0 - blend) * a + blend * b;
-}
-
double scaleX(const QTransform &transform)
{
return transform.m11();
diff --git a/src/curveeditor/detail/utils.h b/src/curveeditor/detail/utils.h
index 812c5d0..d53086a 100644
--- a/src/curveeditor/detail/utils.h
+++ b/src/curveeditor/detail/utils.h
@@ -33,10 +33,6 @@ class QTransform;
namespace DesignTools {
-double clamp(double val, double lo, double hi);
-
-double lerp(double blend, double a, double b);
-
double scaleX(const QTransform &transform);
double scaleY(const QTransform &transform);
@@ -47,4 +43,16 @@ QRectF bbox(const QRectF &rect, const QTransform &transform);
QPalette singleColorPalette(const QColor &color);
+template<typename TV, typename TC>
+inline double clamp(const TV &val, const TC &lo, const TC &hi)
+{
+ return val < lo ? lo : (val > hi ? hi : val);
+}
+
+template<typename T>
+inline T lerp(double blend, const T &a, const T &b)
+{
+ return (1.0 - blend) * a + blend * b;
+}
+
} // End namespace DesignTools.