diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/curveeditor/animationcurve.cpp | 143 | ||||
-rw-r--r-- | src/curveeditor/animationcurve.h | 99 | ||||
-rw-r--r-- | src/curveeditor/curveeditor.cpp | 2 | ||||
-rw-r--r-- | src/curveeditor/curveeditor.pri | 15 | ||||
-rw-r--r-- | src/curveeditor/curveeditormodel.h | 4 | ||||
-rw-r--r-- | src/curveeditor/curveeditorstyle.cpp | 169 | ||||
-rw-r--r-- | src/curveeditor/curveeditorstyle.h | 91 | ||||
-rw-r--r-- | src/curveeditor/curveeditorstyledialog.cpp | 244 | ||||
-rw-r--r-- | src/curveeditor/curveeditorstyledialog.h | 117 | ||||
-rw-r--r-- | src/curveeditor/curveeditorview.cpp | 70 | ||||
-rw-r--r-- | src/curveeditor/curveeditorview.h | 3 | ||||
-rw-r--r-- | src/curveeditor/curveitem.cpp | 100 | ||||
-rw-r--r-- | src/curveeditor/curveitem.h | 31 | ||||
-rw-r--r-- | src/curveeditor/handleitem.cpp | 134 | ||||
-rw-r--r-- | src/curveeditor/handleitem.h | 60 | ||||
-rw-r--r-- | src/curveeditor/keyframeitem.cpp | 181 | ||||
-rw-r--r-- | src/curveeditor/keyframeitem.h | 85 | ||||
-rw-r--r-- | src/curveeditor/utils.cpp | 78 | ||||
-rw-r--r-- | src/curveeditor/utils.h | 48 |
19 files changed, 1377 insertions, 297 deletions
diff --git a/src/curveeditor/animationcurve.cpp b/src/curveeditor/animationcurve.cpp new file mode 100644 index 0000000..4818a81 --- /dev/null +++ b/src/curveeditor/animationcurve.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "animationcurve.h" + +#include <assert.h> +#include <cmath> + +namespace DesignTools { + +Keyframe::Keyframe() + : m_position() + , m_leftHandle() + , m_rightHandle() +{} + +Keyframe::Keyframe(const QPointF &position) + : m_position(position) + , m_leftHandle() + , m_rightHandle() +{} + +Keyframe::Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle) + : m_position(position) + , m_leftHandle(leftHandle) + , m_rightHandle(rightHandle) +{} + +bool Keyframe::hasLeftHandle() const +{ + return !m_leftHandle.isNull(); +} + +bool Keyframe::hasRightHandle() const +{ + return !m_rightHandle.isNull(); +} + +QPointF Keyframe::position() const +{ + return m_position; +} + +QPointF Keyframe::leftHandle() const +{ + return m_leftHandle; +} + +QPointF Keyframe::rightHandle() const +{ + return m_rightHandle; +} + +void Keyframe::setPosition(const QPointF &pos) +{ + m_position = pos; +} + +void Keyframe::setLeftHandle(const QPointF &pos) +{ + m_leftHandle = pos; +} + +void Keyframe::setRightHandle(const QPointF &pos) +{ + m_rightHandle = pos; +} + + +CurveSegment::CurveSegment() + : m_left() + , m_right() +{} + +CurveSegment::CurveSegment(const Keyframe &left, const Keyframe &right) + : m_left(left) + , m_right(right) +{} + +QPointF CurveSegment::evaluate(double t) const +{ + assert(t >= 0. && t <= 1.); + + const double it = 1.0 - t; + + auto binomialPolynomial = [t, it](double p0, double p1, double p2, double p3) { + return p0 * std::pow(it, 3.0) + p1 * 3.0 * std::pow(it, 2.0) * t + + p2 * 3.0 * it * std::pow(t, 2.0) + p3 * std::pow(t, 3.0); + }; + + const double x = binomialPolynomial( + m_left.position().x(), m_left.rightHandle().x(), + m_right.leftHandle().x(), m_right.position().x()); + + const double y = binomialPolynomial( + m_left.position().y(), m_left.rightHandle().y(), + m_right.leftHandle().y(), m_right.position().y()); + + return QPointF(x, y); +} + + +AnimationCurve::AnimationCurve() + : m_frames() +{} + +AnimationCurve::AnimationCurve(const std::vector<Keyframe> &frames) + : m_frames(frames) +{} + +bool AnimationCurve::isValid() const +{ + return !m_frames.empty(); +} + +std::vector<Keyframe> AnimationCurve::keyframes() const +{ + return m_frames; +} + +} // End namespace DesignTools. diff --git a/src/curveeditor/animationcurve.h b/src/curveeditor/animationcurve.h new file mode 100644 index 0000000..7b42e07 --- /dev/null +++ b/src/curveeditor/animationcurve.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QPointF> + +#include <vector> + +namespace DesignTools { + +class Keyframe +{ +public: + Keyframe(); + + Keyframe(const QPointF &position); + + Keyframe(const QPointF &position, const QPointF &leftHandle, const QPointF &rightHandle); + + bool hasLeftHandle() const; + + bool hasRightHandle() const; + + QPointF position() const; + + QPointF leftHandle() const; + + QPointF rightHandle() const; + + void setPosition(const QPointF &pos); + + void setLeftHandle(const QPointF &pos); + + void setRightHandle(const QPointF &pos); + +private: + QPointF m_position = QPointF(); + + QPointF m_leftHandle = QPointF(); + + QPointF m_rightHandle = QPointF(); +}; + + +class CurveSegment +{ +public: + CurveSegment(); + + CurveSegment(const Keyframe &first, const Keyframe &last); + + QPointF evaluate(double t) const; + +private: + Keyframe m_left; + + Keyframe m_right; +}; + + +class AnimationCurve +{ +public: + AnimationCurve(); + + AnimationCurve(const std::vector<Keyframe> &frames); + + bool isValid() const; + + std::vector<Keyframe> keyframes() const; + +private: + std::vector<Keyframe> m_frames; +}; + +} // End namespace DesignTools. diff --git a/src/curveeditor/curveeditor.cpp b/src/curveeditor/curveeditor.cpp index 95adefd..b72f905 100644 --- a/src/curveeditor/curveeditor.cpp +++ b/src/curveeditor/curveeditor.cpp @@ -37,6 +37,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent) , m_tree(new QTreeView) , m_view(new CurveEditorView(model)) { + m_tree->setFixedWidth(100); + QSplitter *splitter = new QSplitter; splitter->addWidget(m_tree); splitter->addWidget(m_view); diff --git a/src/curveeditor/curveeditor.pri b/src/curveeditor/curveeditor.pri index ba5aaf9..861abd8 100644 --- a/src/curveeditor/curveeditor.pri +++ b/src/curveeditor/curveeditor.pri @@ -2,15 +2,22 @@ INCLUDEPATH += $$PWD ### include required files HEADERS += \ + $$PWD/animationcurve.h \ $$PWD/curveeditor.h \ - $$PWD/curveeditorstyle.h \ + $$PWD/curveeditorstyledialog.h \ $$PWD/curveeditorview.h \ $$PWD/curveitem.h \ - $$PWD/colorcontrol.h + $$PWD/colorcontrol.h \ + $$PWD/handleitem.h \ + $$PWD/keyframeitem.h SOURCES += \ + $$PWD/animationcurve.cpp \ $$PWD/curveeditor.cpp \ - $$PWD/curveeditorstyle.cpp \ + $$PWD/curveeditorstyledialog.cpp \ $$PWD/curveeditorview.cpp \ $$PWD/curveitem.cpp \ - $$PWD/colorcontrol.cpp + $$PWD/colorcontrol.cpp \ + $$PWD/handleitem.cpp \ + $$PWD/keyframeitem.cpp \ + $$PWD/utils.cpp diff --git a/src/curveeditor/curveeditormodel.h b/src/curveeditor/curveeditormodel.h index 883d928..351aa73 100644 --- a/src/curveeditor/curveeditormodel.h +++ b/src/curveeditor/curveeditormodel.h @@ -33,6 +33,8 @@ class QPointF; namespace DesignTools { +class AnimationCurve; + class CurveEditorModel { public: @@ -44,7 +46,7 @@ public: virtual double maximumValue() const = 0; - virtual std::vector<QPointF> curve(int i) const = 0; + virtual AnimationCurve curve(int i) const = 0; virtual CurveEditorStyle style() const { return CurveEditorStyle(); } }; diff --git a/src/curveeditor/curveeditorstyle.cpp b/src/curveeditor/curveeditorstyle.cpp deleted file mode 100644 index a055596..0000000 --- a/src/curveeditor/curveeditorstyle.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt Design Tooling -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ -#include "curveeditorstyle.h" -#include "colorcontrol.h" - -#include <QDebug> -#include <QDoubleSpinBox> -#include <QLabel> -#include <QPushButton> -#include <QSpinBox> -#include <QVBoxLayout> - -namespace DesignTools { - -QHBoxLayout *createRow(const QString &title, QWidget *widget) -{ - auto *label = new QLabel(title); - label->setFixedWidth(200); - label->setAlignment(Qt::AlignRight); - - auto *box = new QHBoxLayout; - box->addWidget(label); - box->addWidget(widget); - return box; -} - -CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent) - : QDialog(parent) - , m_printButton(new QPushButton("Print")) - , m_abscissa(new QDoubleSpinBox()) - , m_ordinate(new QDoubleSpinBox()) - , m_canvasMargin(new QDoubleSpinBox()) - , m_cellCountX(new QSpinBox()) - , m_cellCountY(new QSpinBox()) - , m_background(new ColorControl(style.backgroundBrush.color())) - , m_backgroundAlternate(new ColorControl(style.backgroundAlternateBrush.color())) - , m_fontColor(new ColorControl(style.fontColor)) - , m_curveColor(new ColorControl(style.curveColor)) - , m_gridColor(new ColorControl(style.gridColor)) - , m_rangeBarColor(new ColorControl(style.rangeBarCapsColor)) - , m_rangeBarCapsColor(new ColorControl(style.rangeBarCapsColor)) - , m_playhead(new ColorControl(style.playheadColor)) -{ - m_abscissa->setValue(style.abscissaHeight); - m_ordinate->setValue(style.ordinateWidth); - m_canvasMargin->setValue(style.canvasMargin); - m_cellCountX->setValue(style.cellCountX); - m_cellCountY->setValue(style.cellCountY); - - connect(m_printButton, &QPushButton::released, this, &CurveEditorStyleDialog::printStyle); - - auto intChanged = [this](int) { emitStyleChanged(); }; - auto doubleChanged = [this](double) { emitStyleChanged(); }; - auto colorChanged = [this]() { emitStyleChanged(); }; - - auto intSignal = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged); - auto doubleSignal = static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged); - - connect(m_abscissa, doubleSignal, doubleChanged); - connect(m_ordinate, doubleSignal, doubleChanged); - connect(m_canvasMargin, doubleSignal, doubleChanged); - - connect(m_cellCountX, intSignal, intChanged); - connect(m_cellCountY, intSignal, intChanged); - - connect(m_background, &ColorControl::valueChanged, colorChanged); - connect(m_backgroundAlternate, &ColorControl::valueChanged, colorChanged); - - connect(m_fontColor, &ColorControl::valueChanged, colorChanged); - connect(m_curveColor, &ColorControl::valueChanged, colorChanged); - connect(m_gridColor, &ColorControl::valueChanged, colorChanged); - connect(m_rangeBarColor, &ColorControl::valueChanged, colorChanged); - connect(m_rangeBarCapsColor, &ColorControl::valueChanged, colorChanged); - connect(m_playhead, &ColorControl::valueChanged, colorChanged); - - auto *box = new QVBoxLayout; - box->addLayout(createRow("Abscissa Width", m_abscissa)); - box->addLayout(createRow("Ordinate Width", m_ordinate)); - box->addLayout(createRow("Canvas Margin", m_canvasMargin)); - box->addLayout(createRow("Cellcount X", m_cellCountX)); - box->addLayout(createRow("Cellcount Y", m_cellCountY)); - box->addLayout(createRow("Background Color", m_background)); - box->addLayout(createRow("Alternate Background Color", m_backgroundAlternate)); - box->addLayout(createRow("Font Color", m_fontColor)); - box->addLayout(createRow("Curve Color", m_curveColor)); - box->addLayout(createRow("Grid Color", m_gridColor)); - box->addLayout(createRow("Range Bar Color", m_rangeBarColor)); - box->addLayout(createRow("Range Bar Caps Color", m_rangeBarCapsColor)); - box->addLayout(createRow("Playhead Color", m_playhead)); - - box->addWidget(m_printButton); - setLayout(box); -} - -CurveEditorStyle CurveEditorStyleDialog::style() const -{ - CurveEditorStyle style; - style.backgroundBrush = QBrush(m_background->value()); - style.backgroundAlternateBrush = QBrush(m_backgroundAlternate->value()); - style.fontColor = m_fontColor->value(); - style.curveColor = m_curveColor->value(); - style.gridColor = m_gridColor->value(); - style.rangeBarColor = m_rangeBarColor->value(); - style.rangeBarCapsColor = m_rangeBarCapsColor->value(); - style.playheadColor = m_playhead->value(); - style.abscissaHeight = m_abscissa->value(); - style.ordinateWidth = m_ordinate->value(); - style.canvasMargin = m_canvasMargin->value(); - style.cellCountX = m_cellCountX->value(); - style.cellCountY = m_cellCountY->value(); - return style; -} - -void CurveEditorStyleDialog::emitStyleChanged() -{ - emit styleChanged(style()); -} - -void CurveEditorStyleDialog::printStyle() -{ - auto toString = [](const QColor &color) { - QString tmp = QString("QColor(%1, %2, %3)").arg(color.red()).arg(color.green()).arg(color.blue()); - return qPrintable(tmp); - }; - - CurveEditorStyle s = style(); - qDebug() << ""; - qDebug().nospace() << "CurveEditorStyle out;"; - qDebug().nospace() << "out.backgroundBrush = QBrush(" << toString(s.backgroundBrush.color()) << ");"; - qDebug().nospace() << "out.backgroundAlternateBrush = QBrush(" << toString(s.backgroundAlternateBrush.color()) - << ");"; - qDebug().nospace() << "out.fontColor = " << toString(s.fontColor) << ";"; - qDebug().nospace() << "out.curveColor = " << toString(s.curveColor) << ";"; - qDebug().nospace() << "out.gridColor = " << toString(s.gridColor) << ";"; - qDebug().nospace() << "out.rangeBarColor = " << toString(s.rangeBarColor) << ";"; - qDebug().nospace() << "out.rangeBarCapsColor = " << toString(s.rangeBarCapsColor) << ";"; - qDebug().nospace() << "out.playheadColor = " << toString(s.playheadColor) << ";"; - qDebug().nospace() << "out.abscissaHeight = " << s.abscissaHeight << ";"; - qDebug().nospace() << "out.ordinateWidth = " << s.ordinateWidth << ";"; - qDebug().nospace() << "out.canvasMargin = " << s.canvasMargin << ";"; - qDebug().nospace() << "out.cellCountX = " << s.cellCountX << ";"; - qDebug().nospace() << "out.cellCountY = " << s.cellCountY << ";"; - qDebug().nospace() << "return out;"; - qDebug() << ""; -} - -} // End namespace DesignTools. diff --git a/src/curveeditor/curveeditorstyle.h b/src/curveeditor/curveeditorstyle.h index 81cb83c..23fdaf8 100644 --- a/src/curveeditor/curveeditorstyle.h +++ b/src/curveeditor/curveeditorstyle.h @@ -29,13 +29,29 @@ #include <QColor> #include <QDialog> -class QPushButton; -class QSpinBox; -class QDoubleSpinBox; - namespace DesignTools { -class ColorControl; +struct HandleItemStyleOption +{ + double size = 10.0; + double lineWidth = 1.0; + QColor color = QColor(200, 0, 0); + QColor selectionColor = QColor(200, 200, 200); +}; + +struct KeyframeItemStyleOption +{ + double size = 10.0; + QColor color = QColor(200, 200, 0); + QColor selectionColor = QColor(200, 200, 200); +}; + +struct CurveItemStyleOption +{ + double width = 1.0; + QColor color = QColor(0, 200, 0); + QColor selectionColor = QColor(200, 200, 200); +}; struct CurveEditorStyle { @@ -45,78 +61,37 @@ struct CurveEditorStyle QColor fontColor = QColor(200, 200, 200); - QColor curveColor = QColor(128, 128, 128); - QColor gridColor = QColor(128, 128, 128); - QColor rangeBarColor = QColor(128, 128, 128); - - QColor rangeBarCapsColor = QColor(50, 50, 255); - - QColor playheadColor = QColor(200, 200, 0); - - double abscissaHeight = 40.0; - - double ordinateWidth = 60.0; - double canvasMargin = 5.0; int zoomInWidth = 100; int zoomInHeight = 100; - int cellCountX = 10; - - int cellCountY = 10; -}; - -class CurveEditorView; - -class CurveEditorStyleDialog : public QDialog -{ - Q_OBJECT - -signals: - void styleChanged(const CurveEditorStyle &style); - -public: - CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent = nullptr); - - CurveEditorStyle style() const; + double timeAxisHeight = 40.0; -private: - void emitStyleChanged(); + double timeOffsetLeft = 10.0; - void printStyle(); + double timeOffsetRight = 10.0; -private: - QPushButton *m_printButton; - - QDoubleSpinBox *m_abscissa; - - QDoubleSpinBox *m_ordinate; - - QDoubleSpinBox *m_canvasMargin; - - QSpinBox *m_cellCountX; - - QSpinBox *m_cellCountY; + QColor rangeBarColor = QColor(128, 128, 128); - ColorControl *m_background; + QColor rangeBarCapsColor = QColor(50, 50, 255); - ColorControl *m_backgroundAlternate; + double valueAxisWidth = 60.0; - ColorControl *m_fontColor; + double valueOffsetTop = 10.0; - ColorControl *m_curveColor; + double valueOffsetBottom = 10.0; - ColorControl *m_gridColor; + QColor playheadColor = QColor(200, 200, 0); - ColorControl *m_rangeBarColor; + HandleItemStyleOption handleStyle; - ColorControl *m_rangeBarCapsColor; + KeyframeItemStyleOption keyframeStyle; - ColorControl *m_playhead; + CurveItemStyleOption curveStyle; }; } // End namespace DesignTools. diff --git a/src/curveeditor/curveeditorstyledialog.cpp b/src/curveeditor/curveeditorstyledialog.cpp new file mode 100644 index 0000000..2629f24 --- /dev/null +++ b/src/curveeditor/curveeditorstyledialog.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "curveeditorstyledialog.h" +#include "colorcontrol.h" +#include "curveeditorstyle.h" + +#include <QDebug> +#include <QDoubleSpinBox> +#include <QLabel> +#include <QPushButton> +#include <QSpinBox> +#include <QVBoxLayout> + +namespace DesignTools { + +QHBoxLayout *createRow(const QString &title, QWidget *widget) +{ + auto *label = new QLabel(title); + label->setFixedWidth(200); + label->setAlignment(Qt::AlignRight); + + auto *box = new QHBoxLayout; + box->addWidget(label); + box->addWidget(widget); + return box; +} + +CurveEditorStyleDialog::CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent) + : QDialog(parent) + , m_printButton(new QPushButton("Print")) + , m_background(new ColorControl(style.backgroundBrush.color())) + , m_backgroundAlternate(new ColorControl(style.backgroundAlternateBrush.color())) + , m_fontColor(new ColorControl(style.fontColor)) + , m_gridColor(new ColorControl(style.gridColor)) + , m_canvasMargin(new QDoubleSpinBox()) + , m_zoomInWidth(new QSpinBox()) + , m_zoomInHeight(new QSpinBox()) + , m_timeAxisHeight(new QDoubleSpinBox()) + , m_timeOffsetLeft(new QDoubleSpinBox()) + , m_timeOffsetRight(new QDoubleSpinBox()) + , m_rangeBarColor(new ColorControl(style.rangeBarCapsColor)) + , m_rangeBarCapsColor(new ColorControl(style.rangeBarCapsColor)) + , m_valueAxisWidth(new QDoubleSpinBox()) + , m_valueOffsetTop(new QDoubleSpinBox()) + , m_valueOffsetBottom(new QDoubleSpinBox()) + , m_playhead(new ColorControl(style.playheadColor)) + , m_handleSize(new QDoubleSpinBox()) + , m_handleLineWidth(new QDoubleSpinBox()) + , m_handleColor(new ColorControl(style.handleStyle.color)) + , m_handleSelectionColor(new ColorControl(style.handleStyle.selectionColor)) + , m_keyframeSize(new QDoubleSpinBox()) + , m_keyframeColor(new ColorControl(style.keyframeStyle.color)) + , m_keyframeSelectionColor(new ColorControl(style.keyframeStyle.selectionColor)) + , m_curveWidth(new QDoubleSpinBox()) + , m_curveColor(new ColorControl(style.curveStyle.color)) + , m_curveSelectionColor(new ColorControl(style.curveStyle.selectionColor)) +{ + m_canvasMargin->setValue(style.canvasMargin); + m_zoomInWidth->setValue(style.zoomInWidth); + m_zoomInHeight->setValue(style.zoomInHeight); + m_timeAxisHeight->setValue(style.timeAxisHeight); + m_timeOffsetLeft->setValue(style.timeOffsetLeft); + m_timeOffsetRight->setValue(style.timeOffsetRight); + m_valueAxisWidth->setValue(style.valueAxisWidth); + m_valueOffsetTop->setValue(style.valueOffsetTop); + m_valueOffsetBottom->setValue(style.valueOffsetBottom); + m_handleSize->setValue(style.handleStyle.size); + m_handleLineWidth->setValue(style.handleStyle.lineWidth); + m_keyframeSize->setValue(style.keyframeStyle.size); + m_curveWidth->setValue(style.curveStyle.width); + + connect(m_printButton, &QPushButton::released, this, &CurveEditorStyleDialog::printStyle); + + auto intChanged = [this](int) { emitStyleChanged(); }; + auto doubleChanged = [this](double) { emitStyleChanged(); }; + auto colorChanged = [this]() { emitStyleChanged(); }; + + auto intSignal = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged); + auto doubleSignal = static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged); + + connect(m_background, &ColorControl::valueChanged, colorChanged); + connect(m_backgroundAlternate, &ColorControl::valueChanged, colorChanged); + connect(m_fontColor, &ColorControl::valueChanged, colorChanged); + connect(m_gridColor, &ColorControl::valueChanged, colorChanged); + connect(m_canvasMargin, doubleSignal, doubleChanged); + connect(m_zoomInWidth, intSignal, intChanged); + connect(m_zoomInHeight, intSignal, intChanged); + connect(m_timeAxisHeight, doubleSignal, doubleChanged); + connect(m_timeOffsetLeft, doubleSignal, doubleChanged); + connect(m_timeOffsetRight, doubleSignal, doubleChanged); + connect(m_rangeBarColor, &ColorControl::valueChanged, colorChanged); + connect(m_rangeBarCapsColor, &ColorControl::valueChanged, colorChanged); + connect(m_valueAxisWidth, doubleSignal, doubleChanged); + connect(m_valueOffsetTop, doubleSignal, doubleChanged); + connect(m_valueOffsetBottom, doubleSignal, doubleChanged); + connect(m_playhead, &ColorControl::valueChanged, colorChanged); + connect(m_handleSize, doubleSignal, doubleChanged); + connect(m_handleLineWidth, doubleSignal, doubleChanged); + connect(m_handleColor, &ColorControl::valueChanged, colorChanged); + connect(m_handleSelectionColor, &ColorControl::valueChanged, colorChanged); + connect(m_keyframeSize, doubleSignal, doubleChanged); + connect(m_keyframeColor, &ColorControl::valueChanged, colorChanged); + connect(m_keyframeSelectionColor, &ColorControl::valueChanged, colorChanged); + connect(m_curveWidth, doubleSignal, doubleChanged); + connect(m_curveColor, &ColorControl::valueChanged, colorChanged); + connect(m_curveSelectionColor, &ColorControl::valueChanged, colorChanged); + + auto *box = new QVBoxLayout; + box->addLayout(createRow("Background Color", m_background)); + box->addLayout(createRow("Alternate Background Color", m_backgroundAlternate)); + box->addLayout(createRow("Font Color", m_fontColor)); + box->addLayout(createRow("Grid Color", m_gridColor)); + box->addLayout(createRow("Canvas Margin", m_canvasMargin)); + box->addLayout(createRow("Zoom In Width", m_zoomInWidth)); + box->addLayout(createRow("Zoom In Height", m_zoomInHeight)); + box->addLayout(createRow("Time Axis Height", m_timeAxisHeight)); + box->addLayout(createRow("Time Axis Left Offset", m_timeOffsetLeft)); + box->addLayout(createRow("Time Axis Right Offset", m_timeOffsetRight)); + box->addLayout(createRow("Range Bar Color", m_rangeBarColor)); + box->addLayout(createRow("Range Bar Caps Color", m_rangeBarCapsColor)); + box->addLayout(createRow("Value Axis Width", m_valueAxisWidth)); + box->addLayout(createRow("Value Axis Top Offset", m_valueOffsetTop)); + box->addLayout(createRow("Value Axis Bottom Offset", m_valueOffsetBottom)); + box->addLayout(createRow("Playhead Color", m_playhead)); + box->addLayout(createRow("Handle Size", m_handleSize)); + box->addLayout(createRow("Handle Line Width", m_handleLineWidth)); + box->addLayout(createRow("Handle Color", m_handleColor)); + box->addLayout(createRow("Handle Selection Color", m_handleSelectionColor)); + box->addLayout(createRow("Keyframe Size", m_keyframeSize)); + box->addLayout(createRow("Keyframe Color", m_keyframeColor)); + box->addLayout(createRow("Keyframe Selection Color", m_keyframeSelectionColor)); + box->addLayout(createRow("Curve Width", m_curveWidth)); + box->addLayout(createRow("Curve Color", m_curveColor)); + box->addLayout(createRow("Curve Selection Color", m_curveSelectionColor)); + + box->addWidget(m_printButton); + setLayout(box); +} + +CurveEditorStyle CurveEditorStyleDialog::style() const +{ + CurveEditorStyle style; + style.backgroundBrush = QBrush(m_background->value()); + style.backgroundAlternateBrush = QBrush(m_backgroundAlternate->value()); + style.fontColor = m_fontColor->value(); + style.gridColor = m_gridColor->value(); + style.canvasMargin = m_canvasMargin->value(); + style.zoomInWidth = m_zoomInWidth->value(); + style.zoomInHeight = m_zoomInHeight->value(); + style.timeAxisHeight = m_timeAxisHeight->value(); + style.timeOffsetLeft = m_timeOffsetLeft->value(); + style.timeOffsetRight = m_timeOffsetRight->value(); + style.rangeBarColor = m_rangeBarColor->value(); + style.rangeBarCapsColor = m_rangeBarCapsColor->value(); + style.valueAxisWidth = m_valueAxisWidth->value(); + style.valueOffsetTop = m_valueOffsetTop->value(); + style.valueOffsetBottom = m_valueOffsetBottom->value(); + style.playheadColor = m_playhead->value(); + style.handleStyle.size = m_handleSize->value(); + style.handleStyle.lineWidth = m_handleLineWidth->value(); + style.handleStyle.color = m_handleColor->value(); + style.handleStyle.selectionColor = m_handleSelectionColor->value(); + style.keyframeStyle.size = m_keyframeSize->value(); + style.keyframeStyle.color = m_keyframeColor->value(); + style.keyframeStyle.selectionColor = m_keyframeSelectionColor->value(); + style.curveStyle.width = m_curveWidth->value(); + style.curveStyle.color = m_curveColor->value(); + style.curveStyle.selectionColor = m_curveSelectionColor->value(); + return style; +} + +void CurveEditorStyleDialog::emitStyleChanged() +{ + emit styleChanged(style()); +} + +void CurveEditorStyleDialog::printStyle() +{ + auto toString = [](const QColor &color) { + QString tmp = QString("QColor(%1, %2, %3)").arg(color.red()).arg(color.green()).arg(color.blue()); + return qPrintable(tmp); + }; + + CurveEditorStyle s = style(); + qDebug() << ""; + qDebug().nospace() << "CurveEditorStyle out;"; + qDebug().nospace() << "out.backgroundBrush = QBrush(" << toString(s.backgroundBrush.color()) + << ");"; + qDebug().nospace() << "out.backgroundAlternateBrush = QBrush(" + << toString(s.backgroundAlternateBrush.color()) << ");"; + qDebug().nospace() << "out.fontColor = " << toString(s.fontColor) << ";"; + qDebug().nospace() << "out.gridColor = " << toString(s.gridColor) << ";"; + qDebug().nospace() << "out.canvasMargin = " << s.canvasMargin << ";"; + qDebug().nospace() << "out.zoomInWidth = " << s.zoomInWidth << ";"; + qDebug().nospace() << "out.zoomInHeight = " << s.zoomInHeight << ";"; + qDebug().nospace() << "out.timeAxisHeight = " << s.timeAxisHeight << ";"; + qDebug().nospace() << "out.timeOffsetLeft = " << s.timeOffsetLeft << ";"; + qDebug().nospace() << "out.timeOffsetRight = " << s.timeOffsetRight << ";"; + qDebug().nospace() << "out.rangeBarColor = " << toString(s.rangeBarColor) << ";"; + qDebug().nospace() << "out.rangeBarCapsColor = " << toString(s.rangeBarCapsColor) << ";"; + qDebug().nospace() << "out.valueAxisWidth = " << s.valueAxisWidth << ";"; + qDebug().nospace() << "out.valueOffsetTop = " << s.valueOffsetTop << ";"; + qDebug().nospace() << "out.valueOffsetBottom = " << s.valueOffsetBottom << ";"; + qDebug().nospace() << "out.playheadColor = " << toString(s.playheadColor) << ";"; + qDebug().nospace() << "out.handleStyle.size = " << s.handleStyle.size << ";"; + qDebug().nospace() << "out.handleStyle.lineWidth = " << s.handleStyle.lineWidth << ";"; + qDebug().nospace() << "out.handleStyle.color = " << toString(s.handleStyle.color) << ";"; + qDebug().nospace() << "out.handleStyle.selectionColor = " + << toString(s.handleStyle.selectionColor) << ";"; + qDebug().nospace() << "out.keyframeStyle.size = " << s.keyframeStyle.size << ";"; + qDebug().nospace() << "out.keyframeStyle.color = " << toString(s.keyframeStyle.color) << ";"; + qDebug().nospace() << "out.keyframeStyle.selectionColor = " + << toString(s.keyframeStyle.selectionColor) << ";"; + qDebug().nospace() << "out.curveStyle.width = " << s.curveStyle.width << ";"; + qDebug().nospace() << "out.curveStyle.color = " << toString(s.curveStyle.color) << ";"; + qDebug().nospace() << "out.curveStyle.selectionColor = " + << toString(s.curveStyle.selectionColor) << ";"; + qDebug().nospace() << "return out;"; + qDebug() << ""; +} + +} // End namespace DesignTools. diff --git a/src/curveeditor/curveeditorstyledialog.h b/src/curveeditor/curveeditorstyledialog.h new file mode 100644 index 0000000..645f545 --- /dev/null +++ b/src/curveeditor/curveeditorstyledialog.h @@ -0,0 +1,117 @@ + +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QDialog> + +class QPushButton; +class QSpinBox; +class QDoubleSpinBox; + +namespace DesignTools { + +class ColorControl; + +struct CurveEditorStyle; + +class CurveEditorStyleDialog : public QDialog +{ + Q_OBJECT + +signals: + void styleChanged(const CurveEditorStyle &style); + +public: + CurveEditorStyleDialog(CurveEditorStyle &style, QWidget *parent = nullptr); + + CurveEditorStyle style() const; + +private: + void emitStyleChanged(); + + void printStyle(); + +private: + QPushButton *m_printButton; + + ColorControl *m_background; + + ColorControl *m_backgroundAlternate; + + ColorControl *m_fontColor; + + ColorControl *m_gridColor; + + QDoubleSpinBox *m_canvasMargin; + + QSpinBox *m_zoomInWidth; + + QSpinBox *m_zoomInHeight; + + QDoubleSpinBox *m_timeAxisHeight; + + QDoubleSpinBox *m_timeOffsetLeft; + + QDoubleSpinBox *m_timeOffsetRight; + + ColorControl *m_rangeBarColor; + + ColorControl *m_rangeBarCapsColor; + + QDoubleSpinBox *m_valueAxisWidth; + + QDoubleSpinBox *m_valueOffsetTop; + + QDoubleSpinBox *m_valueOffsetBottom; + + ColorControl *m_playhead; + + // HandleItem + QDoubleSpinBox *m_handleSize; + + QDoubleSpinBox *m_handleLineWidth; + + ColorControl *m_handleColor; + + ColorControl *m_handleSelectionColor; + + // KeyframeItem + QDoubleSpinBox *m_keyframeSize; + + ColorControl *m_keyframeColor; + + ColorControl *m_keyframeSelectionColor; + + // CurveItem + QDoubleSpinBox *m_curveWidth; + + ColorControl *m_curveColor; + + ColorControl *m_curveSelectionColor; +}; + +} // End namespace DesignTools. diff --git a/src/curveeditor/curveeditorview.cpp b/src/curveeditor/curveeditorview.cpp index 5aba268..0a54c82 100644 --- a/src/curveeditor/curveeditorview.cpp +++ b/src/curveeditor/curveeditorview.cpp @@ -25,37 +25,16 @@ #include "curveeditorview.h" #include "curveeditormodel.h" #include "curveitem.h" +#include "utils.h" #include <QAction> -#include <QDebug> #include <QMenu> #include <QResizeEvent> -#include <QScrollBar> #include <cmath> 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(); -} - -double scaleY(const QTransform &transform) -{ - return transform.m22(); -} - CurveEditorView::CurveEditorView(CurveEditorModel *model, QWidget *parent) : QGraphicsView(parent) , m_scene() @@ -74,7 +53,6 @@ CurveEditorView::CurveEditorView(CurveEditorModel *model, QWidget *parent) connect(&m_dialog, &CurveEditorStyleDialog::styleChanged, this, &CurveEditorView::setStyle); m_scene.addItem(new CurveItem(model->curve(0))); - m_scene.addItem(new CurveItem(model->curve(1))); applyZoom(m_zoomX, m_zoomY); update(); @@ -83,7 +61,15 @@ CurveEditorView::CurveEditorView(CurveEditorModel *model, QWidget *parent) void CurveEditorView::setStyle(const CurveEditorStyle &style) { m_style = style; + + const auto itemList = items(); + for (auto *item : itemList) { + if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) + curveItem->setStyle(style); + } + updateSceneRect(); + applyZoom(m_zoomX, m_zoomY); viewport()->update(); } @@ -101,11 +87,9 @@ void CurveEditorView::zoomY(double zoom) void CurveEditorView::resizeEvent(QResizeEvent *event) { - Q_UNUSED(event); - updateSceneRect(); applyZoom(m_zoomX, m_zoomY); - viewport()->update(); + QGraphicsView::resizeEvent(event); } void CurveEditorView::contextMenuEvent(QContextMenuEvent *event) @@ -123,7 +107,7 @@ void CurveEditorView::contextMenuEvent(QContextMenuEvent *event) void CurveEditorView::drawForeground(QPainter *painter, const QRectF &rect) { - auto gap = QRectF(rect.topLeft(), QSizeF(m_style.ordinateWidth, m_style.abscissaHeight)); + auto gap = QRectF(rect.topLeft(), QSizeF(m_style.valueAxisWidth, m_style.timeAxisHeight)); auto abscissa = QRectF(gap.topRight(), rect.topRight() + QPointF(0.0, gap.height())); drawTimeScale(painter, abscissa); @@ -144,7 +128,7 @@ void CurveEditorView::drawBackground(QPainter *painter, const QRectF &rect) drawExtremaY(painter, rect); } -int CurveEditorView::mapTimetoX(double time) +int CurveEditorView::mapTimeToX(double time) { return std::round(time * scaleX(m_transform)); } @@ -169,12 +153,12 @@ void CurveEditorView::applyZoom(double x, double y) m_zoomX = x; m_zoomY = y; - double canvasWidth = rect().width() - m_style.ordinateWidth - 2 * m_style.canvasMargin - rect().width() / 20.0; + double canvasWidth = rect().width() - m_style.valueAxisWidth - 2 * m_style.canvasMargin - rect().width() / 20.0; double xZoomedOut = canvasWidth / (m_model->maximumTime() - m_model->minimumTime()); double xZoomedIn = m_style.zoomInWidth; double scaleX = lerp(clamp(m_zoomX, 0.0, 1.0), xZoomedOut, xZoomedIn); - double canvasHeight = rect().height() - m_style.abscissaHeight - 2 * m_style.canvasMargin - rect().height() / 10; + double canvasHeight = rect().height() - m_style.timeAxisHeight - 2 * m_style.canvasMargin - rect().height() / 10; double yZoomedOut = canvasHeight / (m_model->maximumValue() - m_model->minimumValue()); double yZoomedIn = m_style.zoomInHeight; double scaleY = lerp(clamp(m_zoomY, 0.0, 1.0), -yZoomedOut, -yZoomedIn); @@ -184,7 +168,7 @@ void CurveEditorView::applyZoom(double x, double y) QRectF sceneBounds; for (auto *item : items()) if (auto *curveItem = qgraphicsitem_cast<CurveItem *>(item)) - sceneBounds = sceneBounds.united(curveItem->zoom(m_transform)); + sceneBounds = sceneBounds.united(curveItem->setComponentTransform(m_transform)); updateSceneRect(sceneBounds); } @@ -195,8 +179,8 @@ void CurveEditorView::updateSceneRect(const QRectF &rect) scene()->setSceneRect(rect); QRectF sr = scene()->sceneRect().adjusted( - -m_style.ordinateWidth - m_style.canvasMargin, - -m_style.abscissaHeight - m_style.canvasMargin, + -m_style.valueAxisWidth - m_style.canvasMargin, + -m_style.timeAxisHeight - m_style.canvasMargin, m_style.canvasMargin, m_style.canvasMargin); @@ -209,8 +193,8 @@ void CurveEditorView::drawGrid(QPainter *painter, const QRectF &rect) painter->setPen(m_style.gridColor); QRectF gridRect = rect.adjusted( - m_style.ordinateWidth + m_style.canvasMargin, - m_style.abscissaHeight + m_style.canvasMargin, + m_style.valueAxisWidth + m_style.canvasMargin, + m_style.timeAxisHeight + m_style.canvasMargin, -m_style.canvasMargin, -m_style.canvasMargin); @@ -220,7 +204,7 @@ void CurveEditorView::drawGrid(QPainter *painter, const QRectF &rect) double timeIncrement = timeLabelInterval(painter, m_model->maximumTime()); for (double i = m_model->minimumTime(); i <= m_model->maximumTime(); i += timeIncrement) - drawVerticalLine(mapTimetoX(i)); + drawVerticalLine(mapTimeToX(i)); painter->restore(); } @@ -233,8 +217,8 @@ void CurveEditorView::drawExtremaX(QPainter *painter, const QRectF &rect) painter->save(); painter->setPen(Qt::red); - drawVerticalLine(mapTimetoX(m_model->minimumTime())); - drawVerticalLine(mapTimetoX(m_model->maximumTime())); + drawVerticalLine(mapTimeToX(m_model->minimumTime())); + drawVerticalLine(mapTimeToX(m_model->maximumTime())); painter->restore(); } @@ -248,6 +232,9 @@ void CurveEditorView::drawExtremaY(QPainter *painter, const QRectF &rect) painter->setPen(Qt::blue); drawHorizontalLine(mapValueToY(m_model->minimumValue())); drawHorizontalLine(mapValueToY(m_model->maximumValue())); + + drawHorizontalLine(mapValueToY(0.0)); + painter->restore(); } @@ -262,7 +249,7 @@ void CurveEditorView::drawTimeScale(QPainter *painter, const QRectF &rect) auto paintLabeledTick = [this, painter, rect, fm](double time) { QString timeText = QString("%1").arg(time); - int position = mapTimetoX(time); + int position = mapTimeToX(time); QRect textRect = fm.boundingRect(timeText); textRect.moveCenter(QPoint(position, rect.center().y())); @@ -307,20 +294,19 @@ double CurveEditorView::timeLabelInterval(QPainter *painter, double maxTime) int deltaTime = 1; int nextFactor = 5; - double tickDistance = mapTimetoX(deltaTime); + double tickDistance = mapTimeToX(deltaTime); while (true) { if (tickDistance > minTextSpacing) break; deltaTime *= nextFactor; - tickDistance = mapTimetoX(deltaTime); + tickDistance = mapTimeToX(deltaTime); if (nextFactor == 5) nextFactor = 2; else nextFactor = 5; } - return deltaTime; } diff --git a/src/curveeditor/curveeditorview.h b/src/curveeditor/curveeditorview.h index 91a7023..fe35933 100644 --- a/src/curveeditor/curveeditorview.h +++ b/src/curveeditor/curveeditorview.h @@ -26,6 +26,7 @@ #pragma once #include "curveeditorstyle.h" +#include "curveeditorstyledialog.h" #include <QGraphicsScene> #include <QGraphicsView> @@ -57,7 +58,7 @@ protected: void drawBackground(QPainter *painter, const QRectF &rect) override; private: - int mapTimetoX(double time); + int mapTimeToX(double time); int mapValueToY(double value); diff --git a/src/curveeditor/curveitem.cpp b/src/curveeditor/curveitem.cpp index 1514081..128b60e 100644 --- a/src/curveeditor/curveitem.cpp +++ b/src/curveeditor/curveitem.cpp @@ -23,6 +23,8 @@ ** ****************************************************************************/ #include "curveitem.h" +#include "keyframeitem.h" +#include "utils.h" #include <QPainter> #include <QPainterPath> @@ -31,41 +33,109 @@ namespace DesignTools { CurveItem::CurveItem(QGraphicsItem *parent) : QGraphicsObject(parent) - , m_points({QPointF(0.0, 0.0), QPointF(1.1, 1.1)}) + , m_transform() + , m_keyframes() {} -CurveItem::CurveItem(const std::vector<QPointF> &points, QGraphicsItem *parent) +CurveItem::CurveItem(const AnimationCurve &curve, QGraphicsItem *parent) : QGraphicsObject(parent) - , m_points(points) -{} + , m_transform() + , m_keyframes() +{ + auto emitCurveChanged = [this]() { + update(); + emit curveChanged(this->curve()); + }; + + for (auto frame : curve.keyframes()) { + auto *item = new KeyframeItem(frame, this); + connect(item, &KeyframeItem::keyframeChanged, emitCurveChanged); + m_keyframes.push_back(item); + } +} + +CurveItem::~CurveItem() {} + +int CurveItem::type() const +{ + return Type; +} QRectF CurveItem::boundingRect() const { - return path().boundingRect(); + QRectF bounds; + + auto bbox = [&bounds](const Keyframe &frame) { + grow(bounds, frame.position()); + grow(bounds, frame.leftHandle()); + grow(bounds, frame.rightHandle()); + }; + + for (auto *item : m_keyframes) + bbox(item->keyframe()); + + return m_transform.mapRect(bounds); } void CurveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { - painter->drawPath(path()); + if (m_keyframes.size() > 1) { + painter->save(); + + QPen pen = painter->pen(); + pen.setWidthF(m_style.width); + pen.setColor(isSelected() ? m_style.selectionColor : m_style.color); + painter->setPen(pen); + + Keyframe previous = m_keyframes.front()->keyframe(); + Keyframe current; + for (size_t i = 1; i < m_keyframes.size(); ++i) { + current = m_keyframes[i]->keyframe(); + + QPainterPath path(m_transform.map(previous.position())); + + if (previous.rightHandle().isNull() || current.leftHandle().isNull()) + path.lineTo(m_transform.map(current.position())); + else + path.cubicTo( + m_transform.map(previous.rightHandle()), + m_transform.map(current.leftHandle()), + m_transform.map(current.position())); + + painter->drawPath(path); + + previous = current; + } + + painter->restore(); + } +} + +AnimationCurve CurveItem::curve() const +{ + std::vector<Keyframe> out; + for (auto item : m_keyframes) { + out.push_back(item->keyframe()); + } + return out; } -QRectF CurveItem::zoom(const QTransform &transform) +QRectF CurveItem::setComponentTransform(const QTransform &transform) { prepareGeometryChange(); m_transform = transform; + for (auto frame : m_keyframes) + frame->setComponentTransform(transform); + return boundingRect(); } -QPainterPath CurveItem::path() const +void CurveItem::setStyle(const CurveEditorStyle &style) { - if (m_points.empty()) - return QPainterPath(); - - auto out = QPainterPath(m_points.front()); - for (size_t i = 1; i < m_points.size(); ++i) - out.lineTo(m_transform.map(m_points[i])); + m_style = style.curveStyle; - return out; + for (auto *frame : m_keyframes) + frame->setStyle(style); } } // End namespace DesignTools. diff --git a/src/curveeditor/curveitem.h b/src/curveeditor/curveitem.h index 6bfc177..bae12e8 100644 --- a/src/curveeditor/curveitem.h +++ b/src/curveeditor/curveitem.h @@ -25,34 +25,51 @@ #pragma once -#include <QGraphicsObject> +#include "animationcurve.h" +#include "curveeditorstyle.h" -class QPainterPath; +#include <QGraphicsObject> namespace DesignTools { +class KeyframeItem; + class CurveItem : public QGraphicsObject { Q_OBJECT +signals: + void curveChanged(const AnimationCurve &curve); + public: CurveItem(QGraphicsItem *parent = nullptr); - CurveItem(const std::vector<QPointF> &points, QGraphicsItem *parent = nullptr); + CurveItem(const AnimationCurve &curve, QGraphicsItem *parent = nullptr); + + ~CurveItem() override; + + enum { Type = UserType + 1 }; + + int type() const override; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - QRectF zoom(const QTransform &transform); + AnimationCurve curve() const; -private: - QPainterPath path() const; + QRectF setComponentTransform(const QTransform &transform); + + void setStyle(const CurveEditorStyle &style); private: QTransform m_transform; - std::vector<QPointF> m_points; + CurveItemStyleOption m_style; + + AnimationCurve m_curve; + + std::vector<KeyframeItem *> m_keyframes; }; } // End namespace DesignTools. diff --git a/src/curveeditor/handleitem.cpp b/src/curveeditor/handleitem.cpp new file mode 100644 index 0000000..4653af6 --- /dev/null +++ b/src/curveeditor/handleitem.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "handleitem.h" +#include "utils.h" + +#include <QEvent> +#include <QGraphicsSceneMouseEvent> +#include <QPainter> + +namespace DesignTools { + +struct HandleGeometry +{ + HandleGeometry(const QPointF &pos, const HandleItemStyleOption &style) + { + QPointF topLeft(-style.size / 2.0, -style.size / 2.0); + + handle = QRectF(topLeft, -topLeft); + toKeyframe = QLineF(QPointF(0.0, 0.0), -pos); + angle = -toKeyframe.angle() + 45.0; + } + + QRectF handle; + + QLineF toKeyframe; + + double angle; +}; + +HandleItem::HandleItem(QGraphicsItem *parent) + : QGraphicsObject(parent) + , m_style() +{ + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIgnoresTransformations, true); + setFlag(QGraphicsItem::ItemStacksBehindParent, true); +} + +HandleItem::~HandleItem() +{ } + +int HandleItem::type() const +{ + return Type; +} + +QRectF HandleItem::boundingRect() const +{ + HandleGeometry geom(pos(), m_style); + + QTransform transform; + transform.rotate(geom.angle); + + QRectF bounds = bbox(geom.handle, transform); + grow(bounds, -pos()); + return bounds; +} + +void HandleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + QColor handleColor(isSelected() ? m_style.selectionColor : m_style.color); + + HandleGeometry geom(pos(), m_style); + + QPen pen = painter->pen(); + pen.setWidthF(m_style.lineWidth); + pen.setColor(handleColor); + + painter->save(); + painter->setPen(pen); + + painter->drawLine(geom.toKeyframe); + painter->rotate(geom.angle); + painter->fillRect(geom.handle, handleColor); + + painter->restore(); +} + +void HandleItem::setStyle(const CurveEditorStyle &style) +{ + m_style = style.handleStyle; +} + +bool HandleItem::sceneEvent(QEvent *event) +{ + auto intersectsHandle = [this, event]() { + HandleGeometry geom(pos(), m_style); + + QTransform transform; + transform.rotate(geom.angle); + + auto *mousePressEvent = static_cast<QGraphicsSceneMouseEvent *>(event); + return bbox(geom.handle, transform).contains(mousePressEvent->pos()); + }; + + switch (event->type()) { + + case QEvent::GraphicsSceneMousePress: + if (!intersectsHandle()) + event->ignore(); + return true; + + default: + return QGraphicsObject::sceneEvent(event); + } +} + +} // End namespace DesignTools. diff --git a/src/curveeditor/handleitem.h b/src/curveeditor/handleitem.h new file mode 100644 index 0000000..8e2ffd7 --- /dev/null +++ b/src/curveeditor/handleitem.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "curveeditorstyle.h" + +#include <QGraphicsObject> + +namespace DesignTools { + +class HandleItem : public QGraphicsObject +{ + Q_OBJECT + +public: + HandleItem(QGraphicsItem *parent); + + ~HandleItem() override; + + enum { Type = UserType + 3 }; + + int type() const override; + + QRectF boundingRect() const override; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + + void setStyle(const CurveEditorStyle &style); + +protected: + bool sceneEvent(QEvent *event) override; + +private: + HandleItemStyleOption m_style; +}; + +} // End namespace DesignTools. diff --git a/src/curveeditor/keyframeitem.cpp b/src/curveeditor/keyframeitem.cpp new file mode 100644 index 0000000..867585c --- /dev/null +++ b/src/curveeditor/keyframeitem.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "keyframeitem.h" +#include "handleitem.h" + +#include <QPainter> + +#include <cmath> + +namespace DesignTools { + +KeyframeItem::KeyframeItem(QGraphicsItem *parent) + : QGraphicsObject(parent) + , m_frame() +{} + +KeyframeItem::KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent) + : QGraphicsObject(parent) + , m_transform() + , m_frame(keyframe) + , m_left(keyframe.hasLeftHandle() ? new HandleItem(this) : nullptr) + , m_right(keyframe.hasRightHandle() ? new HandleItem(this) : nullptr) +{ + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIgnoresTransformations, true); + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); + + connect(this, &QGraphicsObject::xChanged, this, &KeyframeItem::updatePosition); + connect(this, &QGraphicsObject::yChanged, this, &KeyframeItem::updatePosition); + + if (m_left) { + m_left->setPos(m_frame.leftHandle() - m_frame.position()); + auto updateLeftHandle = [this]() { updateHandle(m_left); }; + connect(m_left, &QGraphicsObject::xChanged, updateLeftHandle); + connect(m_left, &QGraphicsObject::yChanged, updateLeftHandle); + } + + if (m_right) { + m_right->setPos(m_frame.rightHandle() - m_frame.position()); + auto updateRightHandle = [this]() { updateHandle(m_right); }; + connect(m_right, &QGraphicsObject::xChanged, updateRightHandle); + connect(m_right, &QGraphicsObject::yChanged, updateRightHandle); + } + + setPos(m_frame.position()); +} + +int KeyframeItem::type() const +{ + return Type; +} + +QRectF KeyframeItem::boundingRect() const +{ + QPointF topLeft(-m_style.size / 2.0, -m_style.size / 2.0); + return QRectF(topLeft, -topLeft); +} + +void KeyframeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + QPen pen = painter->pen(); + pen.setColor(Qt::black); + + painter->save(); + painter->setPen(pen); + painter->setBrush(isSelected() ? m_style.selectionColor : m_style.color); + painter->drawEllipse(boundingRect()); + + painter->restore(); +} + +KeyframeItem::~KeyframeItem() {} + +Keyframe KeyframeItem::keyframe() const +{ + return m_frame; +} + +void KeyframeItem::setComponentTransform(const QTransform &transform) +{ + m_transform = transform; + + if (m_left) + m_left->setPos(m_transform.map(m_frame.leftHandle() - m_frame.position())); + + if (m_right) + m_right->setPos(m_transform.map(m_frame.rightHandle() - m_frame.position())); + + setPos(m_transform.map(m_frame.position())); +} + +void KeyframeItem::setStyle(const CurveEditorStyle &style) +{ + m_style = style.keyframeStyle; + + if (m_left) + m_left->setStyle(style); + + if (m_right) + m_right->setStyle(style); +} + +void KeyframeItem::updatePosition() +{ + bool ok = false; + QPointF position = m_transform.inverted(&ok).map(pos()); + + if (!ok) + return; + + m_frame.setPosition(position); + + if (m_left) + updateHandle(m_left, false); + + if (m_right) + updateHandle(m_right, false); + + emit keyframeChanged(); +} + +void KeyframeItem::updateHandle(HandleItem *handle, bool emitChanged) +{ + bool ok = false; + + QPointF handlePosition = m_transform.inverted(&ok).map(handle->pos()); + + if (!ok) + return; + + if (handle == m_left) + m_frame.setLeftHandle(m_frame.position() + handlePosition); + else + m_frame.setRightHandle(m_frame.position() + handlePosition); + + if (emitChanged) + emit keyframeChanged(); +} + +QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemPositionChange) { + // Snap to keyframe. + bool ok; + QPointF position = m_transform.inverted(&ok).map(value.toPointF()); + if (ok) { + position.setX(std::round(position.x())); + return QVariant(m_transform.map(position)); + } + } + + return QGraphicsItem::itemChange(change, value); +} + +} // End namespace DesignTools. diff --git a/src/curveeditor/keyframeitem.h b/src/curveeditor/keyframeitem.h new file mode 100644 index 0000000..39221d2 --- /dev/null +++ b/src/curveeditor/keyframeitem.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "animationcurve.h" +#include "curveeditorstyle.h" + +#include <QGraphicsObject> + +namespace DesignTools { + +class HandleItem; + +class KeyframeItem : public QGraphicsObject +{ + Q_OBJECT + +signals: + void keyframeChanged(); + +public: + KeyframeItem(QGraphicsItem *parent = nullptr); + + KeyframeItem(const Keyframe &keyframe, QGraphicsItem *parent = nullptr); + + ~KeyframeItem() override; + + enum { Type = UserType + 2 }; + + int type() const override; + + QRectF boundingRect() const override; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + + Keyframe keyframe() const; + + void setComponentTransform(const QTransform &transform); + + void setStyle(const CurveEditorStyle &style); + +protected: + QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override; + +private: + void updatePosition(); + + void updateHandle(HandleItem *handle, bool emit = true); + +private: + QTransform m_transform; + + KeyframeItemStyleOption m_style; + + Keyframe m_frame; + + HandleItem *m_left; + + HandleItem *m_right; +}; + +} // End namespace DesignTools. diff --git a/src/curveeditor/utils.cpp b/src/curveeditor/utils.cpp new file mode 100644 index 0000000..da74946 --- /dev/null +++ b/src/curveeditor/utils.cpp @@ -0,0 +1,78 @@ + +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include <QPointF> +#include <QRectF> +#include <QTransform> + +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(); +} + +double scaleY(const QTransform &transform) +{ + return transform.m22(); +} + +void grow(QRectF &rect, const QPointF &point) +{ + if (rect.left() > point.x()) + rect.setLeft(point.x()); + + if (rect.right() < point.x()) + rect.setRight(point.x()); + + if (rect.top() > point.y()) + rect.setTop(point.y()); + + if (rect.bottom() < point.y()) + rect.setBottom(point.y()); +} + +QRectF bbox(const QRectF &rect, const QTransform &transform) +{ + QRectF out = rect; + grow(out, transform.map(rect.topLeft())); + grow(out, transform.map(rect.topRight())); + grow(out, transform.map(rect.bottomLeft())); + grow(out, transform.map(rect.bottomRight())); + return out; +} + +} // End namespace DesignTools. diff --git a/src/curveeditor/utils.h b/src/curveeditor/utils.h new file mode 100644 index 0000000..c8a44fb --- /dev/null +++ b/src/curveeditor/utils.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Design Tooling +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +class QPointF; +class QRectF; +class QTransform; + +namespace DesignTools { + +class Keyframe; + +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); + +void grow(QRectF &rect, const QPointF &point); + +QRectF bbox(const QRectF &rect, const QTransform &transform); + +} // End namespace DesignTools. |