From 8f59e06aedf509255edb93295514c40eab2adf18 Mon Sep 17 00:00:00 2001 From: Till Theato Date: Sat, 1 Jan 2011 21:06:59 +0000 Subject: [PATCH] =?utf8?q?B=C3=A9zier=20spline:=20-=20rename=20BezierSplin?= =?utf8?q?eWidget=20to=20BezierSplineEditor=20-=20add=20BezierSplineWidget?= =?utf8?q?=20containing=20the=20editor=20and=20more=20controls=20(for=20no?= =?utf8?q?w=20spinboxes=20for=20the=20values)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit svn path=/trunk/kdenlive/; revision=5233 --- src/CMakeLists.txt | 2 + src/beziercurve/beziersplineeditor.cpp | 381 +++++++++++++++++++++++++ src/beziercurve/beziersplineeditor.h | 71 +++++ src/beziercurve/beziersplinewidget.cpp | 374 ++++-------------------- src/beziercurve/beziersplinewidget.h | 37 +-- src/beziercurve/cubicbezierspline.h | 1 + src/effectstackedit.cpp | 8 +- src/widgets/bezierspline_ui.ui | 206 +++++++++++++ 8 files changed, 731 insertions(+), 349 deletions(-) create mode 100644 src/beziercurve/beziersplineeditor.cpp create mode 100644 src/beziercurve/beziersplineeditor.h create mode 100644 src/widgets/bezierspline_ui.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9952bd0c..a50e08ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,7 @@ kde4_add_ui_files(kdenlive_UI widgets/audiospectrum_ui.ui widgets/spectrogram_ui.ui widgets/smconfig_ui.ui + widgets/bezierspline_ui.ui ) set(kdenlive_SRCS @@ -263,6 +264,7 @@ set(kdenlive_SRCS kiss_fft/kiss_fft.c kiss_fft/tools/kiss_fftr.c beziercurve/cubicbezierspline.cpp + beziercurve/beziersplineeditor.cpp beziercurve/beziersplinewidget.cpp ) diff --git a/src/beziercurve/beziersplineeditor.cpp b/src/beziercurve/beziersplineeditor.cpp new file mode 100644 index 00000000..351a6c61 --- /dev/null +++ b/src/beziercurve/beziersplineeditor.cpp @@ -0,0 +1,381 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "beziersplineeditor.h" + +#include +#include + +#include + +BezierSplineEditor::BezierSplineEditor(QWidget* parent) : + QWidget(parent), + m_mode(ModeNormal), + m_currentPointIndex(-1) +{ + setMouseTracking(true); + setAutoFillBackground(false); + setAttribute(Qt::WA_OpaquePaintEvent); + setMinimumSize(150, 150); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +CubicBezierSpline BezierSplineEditor::spline() +{ + return m_spline; +} + +void BezierSplineEditor::setSpline(const CubicBezierSpline& spline) +{ + // TODO: cleanup + m_spline.fromString(spline.toString()); +} + +BPoint BezierSplineEditor::getCurrentPoint() +{ + if (m_currentPointIndex >= 0) + return m_spline.points()[m_currentPointIndex]; + else + return BPoint(); +} + +void BezierSplineEditor::updateCurrentPoint(const BPoint& p) +{ + if (m_currentPointIndex >= 0) { + m_spline.setPoint(m_currentPointIndex, p); + // during validation the point might have changed + emit currentPoint(m_spline.points()[m_currentPointIndex]); + update(); + } +} + +void BezierSplineEditor::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + + QPainter p(this); + + p.fillRect(rect(), palette().background()); + + int wWidth = width() - 1; + int wHeight = height() - 1; + + /* + * Spline + */ + double prevY = wHeight - m_spline.value(0.) * wHeight; + double prevX = 0.; + double curY; + double normalizedX = -1; + int x; + + p.setPen(QPen(Qt::black, 1, Qt::SolidLine)); + for (x = 0 ; x < wWidth ; ++x) { + normalizedX = x / (double)wWidth; + curY = wHeight - m_spline.value(normalizedX, true) * wHeight; + + /* + * Keep in mind that QLineF rounds doubles + * to ints mathematically, not just rounds down + * like in C + */ + p.drawLine(QLineF(prevX, prevY, + x, curY)); + prevX = x; + prevY = curY; + } + p.drawLine(QLineF(prevX, prevY , + x, wHeight - m_spline.value(1.0, true) * wHeight)); + + /* + * Points + Handles + */ + p.setPen(QPen(Qt::red, 1, Qt::SolidLine)); + BPoint point; + QPolygon handle(4); + handle.setPoints(4, + 1, -2, + 4, 1, + 1, 4, + -2, 1); + for (int i = 0; i < m_spline.points().count(); ++i) { + point = m_spline.points().at(i); + if (i == m_currentPointIndex) + p.setBrush(QBrush(QColor(Qt::red), Qt::SolidPattern)); + + p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight)); + p.drawEllipse(QRectF(point.p.x() * wWidth - 3, + wHeight - 3 - point.p.y() * wHeight, 6, 6)); + p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight)); + + if ( i == m_currentPointIndex) + p.setBrush(QBrush(Qt::NoBrush)); + } +} + +void BezierSplineEditor::resizeEvent(QResizeEvent* event) +{ + m_spline.setPrecision(width()); + QWidget::resizeEvent(event); +} + +void BezierSplineEditor::mousePressEvent(QMouseEvent* event) +{ + double x = event->pos().x() / (double)(width() - 1); + double y = 1.0 - event->pos().y() / (double)(height() - 1); + + point_types selectedPoint; + int closestPointIndex = nearestPointInRange(QPointF(x, y), width(), height(), &selectedPoint); + + if (event->button() == Qt::RightButton && closestPointIndex > 0 && closestPointIndex < m_spline.points().count() - 1 && selectedPoint == PTypeP) { + m_spline.removePoint(closestPointIndex); + setCursor(Qt::ArrowCursor); + m_mode = ModeNormal; + if (closestPointIndex < m_currentPointIndex) + --m_currentPointIndex; + update(); + if (m_currentPointIndex >= 0) + emit currentPoint(m_spline.points()[m_currentPointIndex]); + else + emit currentPoint(BPoint()); + emit modified(); + return; + } else if (event->button() != Qt::LeftButton) { + return; + } + + if (closestPointIndex < 0) { + BPoint po; + po.p = QPointF(x, y); + po.h1 = QPointF(x-0.05, y-0.05); + po.h2 = QPointF(x+0.05, y+0.05); + m_currentPointIndex = m_spline.addPoint(po); + m_currentPointType = PTypeP; + /*if (!d->jumpOverExistingPoints(newPoint, -1)) return;*/ + } else { + m_currentPointIndex = closestPointIndex; + m_currentPointType = selectedPoint; + } + + BPoint point = m_spline.points()[m_currentPointIndex]; + QPointF p; + switch (m_currentPointType) { + case PTypeH1: + p = point.h1; + break; + case PTypeP: + p = point.p; + break; + case PTypeH2: + p = point.h2; + } + + m_grabOriginalX = p.x(); + m_grabOriginalY = p.y(); + m_grabOffsetX = p.x() - x; + m_grabOffsetY = p.y() - y; + + switch (m_currentPointType) { + case PTypeH1: + point.h1 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + break; + case PTypeP: + point.p = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + break; + case PTypeH2: + point.h2 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + } + m_spline.setPoint(m_currentPointIndex, point); + + //d->m_draggedAwayPointIndex = -1; + + m_mode = ModeDrag; + + emit currentPoint(point); + update(); +} + +void BezierSplineEditor::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() != Qt::LeftButton) + return; + + setCursor(Qt::ArrowCursor); + m_mode = ModeNormal; + + emit modified(); +} + +void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event) +{ + double x = event->pos().x() / (double)(width() - 1); + double y = 1.0 - event->pos().y() / (double)(height() - 1); + + if (m_mode == ModeNormal) { // If no point is selected set the the cursor shape if on top + point_types type; + int nearestPointIndex = nearestPointInRange(QPointF(x, y), width(), height(), &type); + + if (nearestPointIndex < 0) + setCursor(Qt::ArrowCursor); + else + setCursor(Qt::CrossCursor); + } else { // Else, drag the selected point + /*bool crossedHoriz = event->pos().x() - width() > MOUSE_AWAY_THRES || + event->pos().x() < -MOUSE_AWAY_THRES; + bool crossedVert = event->pos().y() - height() > MOUSE_AWAY_THRES || + event->pos().y() < -MOUSE_AWAY_THRES; + + bool removePoint = (crossedHoriz || crossedVert); + + if (!removePoint && d->m_draggedAwayPointIndex >= 0) { + // point is no longer dragged away so reinsert it + QPointF newPoint(d->m_draggedAwayPoint); + d->m_grab_point_index = d->m_curve.addPoint(newPoint); + d->m_draggedAwayPointIndex = -1; + } + + if (removePoint && + (d->m_draggedAwayPointIndex >= 0)) + return; + */ + + setCursor(Qt::CrossCursor); + + x += m_grabOffsetX; + y += m_grabOffsetY; + + double leftX, rightX; + BPoint point = m_spline.points()[m_currentPointIndex]; + switch (m_currentPointType) { + case PTypeH1: + rightX = point.p.x(); + if (m_currentPointIndex == 0) + leftX = -1000; + else + leftX = m_spline.points()[m_currentPointIndex - 1].p.x(); + x = qBound(leftX, x, rightX); + point.h1 = QPointF(x, y); + break; + case PTypeP: + if (m_currentPointIndex == 0) { + leftX = 0.0; + rightX = 0.0; + /*if (d->m_curve.points().count() > 1) + * rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; + * else + * rightX = 1.0;*/ + } else if (m_currentPointIndex == m_spline.points().count() - 1) { + leftX = 1.0;//m_spline.points()[m_currentPointIndex - 1].p.x(); + rightX = 1.0; + } else { + //// the 1E-4 addition so we can grab the dot later. + leftX = m_spline.points()[m_currentPointIndex - 1].p.x();// + POINT_AREA; + rightX = m_spline.points()[m_currentPointIndex + 1].p.x();// - POINT_AREA; + } + x = qBound(leftX, x, rightX); + y = qBound(0., y, 1.); + + // move handles by same offset + point.h1 += QPointF(x, y) - point.p; + point.h2 += QPointF(x, y) - point.p; + + point.p = QPointF(x, y); + break; + case PTypeH2: + leftX = point.p.x(); + if (m_currentPointIndex == m_spline.points().count() - 1) + rightX = 1001; + else + rightX = m_spline.points()[m_currentPointIndex + 1].p.x(); + x = qBound(leftX, x, rightX); + point.h2 = QPointF(x, y); + }; + + m_spline.setPoint(m_currentPointIndex, point); + + /*if (removePoint && d->m_curve.points().count() > 2) { + d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index]; + d->m_draggedAwayPointIndex = d->m_grab_point_index; + d->m_curve.removePoint(d->m_grab_point_index); + d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1); + } + + d->setCurveModified();*/ + emit currentPoint(point); + update(); + } +} + +void BezierSplineEditor::leaveEvent(QEvent* event) +{ + QWidget::leaveEvent(event); +} + +int BezierSplineEditor::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineEditor::point_types* sel) +{ + double nearestDistanceSquared = 1000; + point_types selectedPoint; + int nearestIndex = -1; + int i = 0; + + double distanceSquared; + foreach(const BPoint & point, m_spline.points()) { + distanceSquared = pow(point.h1.x() - p.x(), 2) + pow(point.h1.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeH1; + } + distanceSquared = pow(point.p.x() - p.x(), 2) + pow(point.p.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeP; + } + distanceSquared = pow(point.h2.x() - p.x(), 2) + pow(point.h2.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeH2; + } + ++i; + } + + if (nearestIndex >= 0) { + BPoint point = m_spline.points()[nearestIndex]; + QPointF p2; + switch (selectedPoint) { + case PTypeH1: + p2 = point.h1; + break; + case PTypeP: + p2 = point.p; + break; + case PTypeH2: + p2 = point.h2; + } + if (qAbs(p.x() - p2.x()) * (wWidth - 1) < 5 && qAbs(p.y() - p2.y()) * (wHeight - 1) < 5) { + *sel = selectedPoint; + return nearestIndex; + } + } + + return -1; +} + +#include "beziersplineeditor.moc" diff --git a/src/beziercurve/beziersplineeditor.h b/src/beziercurve/beziersplineeditor.h new file mode 100644 index 00000000..1c8229d0 --- /dev/null +++ b/src/beziercurve/beziersplineeditor.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef BEZIERSPLINEEDITOR_H +#define BEZIERSPLINEEDITOR_H + +#include "cubicbezierspline.h" + +#include +#include + +class BezierSplineEditor : public QWidget +{ + Q_OBJECT + +public: + BezierSplineEditor(QWidget* parent = 0); + + CubicBezierSpline spline(); + void setSpline(const CubicBezierSpline &spline); + + BPoint getCurrentPoint(); + void updateCurrentPoint(const BPoint &p); + +protected: + //void keyPressEvent(QKeyEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void leaveEvent(QEvent *event); + void resizeEvent(QResizeEvent *event); + +private: + CubicBezierSpline m_spline; + enum modes { ModeDrag, ModeNormal }; + enum point_types { PTypeH1, PTypeP, PTypeH2 }; + modes m_mode; + int m_currentPointIndex; + point_types m_currentPointType; + double m_grabOffsetX; + double m_grabOffsetY; + double m_grabOriginalX; + double m_grabOriginalY; + //QPointF m_draggedAwayPoint; + //int m_draggedAwayPointIndex; + + //inline void drawGrid(QPainter &p, int width, int height); + int nearestPointInRange(QPointF p, int wWidth, int wHeight, point_types *sel); + +signals: + void modified(); + void currentPoint(const BPoint &p); +}; + +#endif diff --git a/src/beziercurve/beziersplinewidget.cpp b/src/beziercurve/beziersplinewidget.cpp index efc9b000..e784e044 100644 --- a/src/beziercurve/beziersplinewidget.cpp +++ b/src/beziercurve/beziersplinewidget.cpp @@ -18,340 +18,82 @@ #include "beziersplinewidget.h" -#include -#include +#include -#include +#include -BezierSplineWidget::BezierSplineWidget(QWidget* parent) : - QWidget(parent), - m_mode(ModeNormal), - m_currentPointIndex(-1) +BezierSplineWidget::BezierSplineWidget(const QString& spline, QWidget* parent) : + QWidget(parent) { - setMouseTracking(true); - setAutoFillBackground(false); - setAttribute(Qt::WA_OpaquePaintEvent); - setMinimumSize(150, 150); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(&m_edit); + QWidget *widget = new QWidget(this); + m_ui.setupUi(widget); + layout->addWidget(widget); + + m_ui.buttonLinkHandles->setIcon(KIcon("insert-link")); + m_ui.buttonLinkHandles->setEnabled(false); + m_ui.widgetPoint->setEnabled(false); + + CubicBezierSpline s; + s.fromString(spline); + m_edit.setSpline(s); + + connect(&m_edit, SIGNAL(modified()), this, SIGNAL(modified())); + connect(&m_edit, SIGNAL(currentPoint(const BPoint&)), this, SLOT(slotUpdatePoint(const BPoint&))); + + connect(m_ui.spinPX, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); + connect(m_ui.spinPY, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); + connect(m_ui.spinH1X, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); + connect(m_ui.spinH1Y, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); + connect(m_ui.spinH2X, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); + connect(m_ui.spinH2Y, SIGNAL(editingFinished()), this, SLOT(slotUpdateSpline())); } -CubicBezierSpline BezierSplineWidget::spline() +QString BezierSplineWidget::spline() { - return m_spline; + return m_edit.spline().toString(); } -void BezierSplineWidget::setSpline(const CubicBezierSpline& spline) +void BezierSplineWidget::slotUpdatePoint(const BPoint &p) { - // TODO: cleanup - m_spline.fromString(spline.toString()); -} - -void BezierSplineWidget::paintEvent(QPaintEvent* event) -{ - Q_UNUSED(event); - - QPainter p(this); - - p.fillRect(rect(), palette().background()); - - int wWidth = width() - 1; - int wHeight = height() - 1; - - /* - * Spline - */ - double prevY = wHeight - m_spline.value(0.) * wHeight; - double prevX = 0.; - double curY; - double normalizedX = -1; - int x; - - p.setPen(QPen(Qt::black, 1, Qt::SolidLine)); - for (x = 0 ; x < wWidth ; ++x) { - normalizedX = x / (double)wWidth; - curY = wHeight - m_spline.value(normalizedX, true) * wHeight; - - /* - * Keep in mind that QLineF rounds doubles - * to ints mathematically, not just rounds down - * like in C - */ - p.drawLine(QLineF(prevX, prevY, - x, curY)); - prevX = x; - prevY = curY; - } - p.drawLine(QLineF(prevX, prevY , - x, wHeight - m_spline.value(1.0, true) * wHeight)); - - /* - * Points + Handles - */ - p.setPen(QPen(Qt::red, 1, Qt::SolidLine)); - BPoint point; - QPolygon handle(4); - handle.setPoints(4, - 1, -2, - 4, 1, - 1, 4, - -2, 1); - for (int i = 0; i < m_spline.points().count(); ++i) { - point = m_spline.points().at(i); - if (i == m_currentPointIndex) - p.setBrush(QBrush(QColor(Qt::red), Qt::SolidPattern)); - - p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight)); - p.drawEllipse(QRectF(point.p.x() * wWidth - 3, - wHeight - 3 - point.p.y() * wHeight, 6, 6)); - p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight)); - - if ( i == m_currentPointIndex) - p.setBrush(QBrush(Qt::NoBrush)); - } -} - -void BezierSplineWidget::resizeEvent(QResizeEvent* event) -{ - m_spline.setPrecision(width()); - QWidget::resizeEvent(event); -} - -void BezierSplineWidget::mousePressEvent(QMouseEvent* event) -{ - double x = event->pos().x() / (double)(width() - 1); - double y = 1.0 - event->pos().y() / (double)(height() - 1); - - point_types selectedPoint; - int closestPointIndex = nearestPointInRange(QPointF(x, y), width(), height(), &selectedPoint); - - if (event->button() == Qt::RightButton && closestPointIndex > 0 && closestPointIndex < m_spline.points().count() - 1 && selectedPoint == PTypeP) { - m_spline.removePoint(closestPointIndex); - setCursor(Qt::ArrowCursor); - m_mode = ModeNormal; - if (closestPointIndex < m_currentPointIndex) - --m_currentPointIndex; - update(); - emit modified(); - return; - } else if (event->button() != Qt::LeftButton) { - return; - } - - if (closestPointIndex < 0) { - BPoint po; - po.p = QPointF(x, y); - po.h1 = QPointF(x-0.05, y-0.05); - po.h2 = QPointF(x+0.05, y+0.05); - m_currentPointIndex = m_spline.addPoint(po); - m_currentPointType = PTypeP; - /*if (!d->jumpOverExistingPoints(newPoint, -1)) return;*/ + blockSignals(true); + if (p == BPoint()) { + m_ui.widgetPoint->setEnabled(false); } else { - m_currentPointIndex = closestPointIndex; - m_currentPointType = selectedPoint; - } - - BPoint point = m_spline.points()[m_currentPointIndex]; - QPointF p; - switch (m_currentPointType) { - case PTypeH1: - p = point.h1; - break; - case PTypeP: - p = point.p; - break; - case PTypeH2: - p = point.h2; + m_ui.widgetPoint->setEnabled(true); + m_ui.spinPX->setValue(qRound(p.p.x() * 255)); + m_ui.spinPY->setValue(qRound(p.p.y() * 255)); + m_ui.spinH1X->setValue(qRound(p.h1.x() * 255)); + m_ui.spinH1Y->setValue(qRound(p.h1.y() * 255)); + m_ui.spinH2X->setValue(qRound(p.h2.x() * 255)); + m_ui.spinH2Y->setValue(qRound(p.h2.y() * 255)); } - - m_grabOriginalX = p.x(); - m_grabOriginalY = p.y(); - m_grabOffsetX = p.x() - x; - m_grabOffsetY = p.y() - y; - - switch (m_currentPointType) { - case PTypeH1: - point.h1 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); - break; - case PTypeP: - point.p = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); - break; - case PTypeH2: - point.h2 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); - } - m_spline.setPoint(m_currentPointIndex, point); - - //d->m_draggedAwayPointIndex = -1; - - m_mode = ModeDrag; - - update(); + blockSignals(false); } -void BezierSplineWidget::mouseReleaseEvent(QMouseEvent* event) +void BezierSplineWidget::slotUpdateSpline() { - if (event->button() != Qt::LeftButton) - return; + BPoint p = m_edit.getCurrentPoint(); - setCursor(Qt::ArrowCursor); - m_mode = ModeNormal; - - emit modified(); -} - -void BezierSplineWidget::mouseMoveEvent(QMouseEvent* event) -{ - double x = event->pos().x() / (double)(width() - 1); - double y = 1.0 - event->pos().y() / (double)(height() - 1); - - if (m_mode == ModeNormal) { // If no point is selected set the the cursor shape if on top - point_types type; - int nearestPointIndex = nearestPointInRange(QPointF(x, y), width(), height(), &type); - - if (nearestPointIndex < 0) - setCursor(Qt::ArrowCursor); - else - setCursor(Qt::CrossCursor); - } else { // Else, drag the selected point - /*bool crossedHoriz = event->pos().x() - width() > MOUSE_AWAY_THRES || - event->pos().x() < -MOUSE_AWAY_THRES; - bool crossedVert = event->pos().y() - height() > MOUSE_AWAY_THRES || - event->pos().y() < -MOUSE_AWAY_THRES; - - bool removePoint = (crossedHoriz || crossedVert); - - if (!removePoint && d->m_draggedAwayPointIndex >= 0) { - // point is no longer dragged away so reinsert it - QPointF newPoint(d->m_draggedAwayPoint); - d->m_grab_point_index = d->m_curve.addPoint(newPoint); - d->m_draggedAwayPointIndex = -1; - } - - if (removePoint && - (d->m_draggedAwayPointIndex >= 0)) - return; - */ - - setCursor(Qt::CrossCursor); - - x += m_grabOffsetX; - y += m_grabOffsetY; - - double leftX, rightX; - BPoint point = m_spline.points()[m_currentPointIndex]; - switch (m_currentPointType) { - case PTypeH1: - rightX = point.p.x(); - if (m_currentPointIndex == 0) - leftX = -1000; - else - leftX = m_spline.points()[m_currentPointIndex - 1].p.x(); - x = qBound(leftX, x, rightX); - point.h1 = QPointF(x, y); - break; - case PTypeP: - if (m_currentPointIndex == 0) { - leftX = 0.0; - rightX = 0.0; - /*if (d->m_curve.points().count() > 1) - * rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; - * else - * rightX = 1.0;*/ - } else if (m_currentPointIndex == m_spline.points().count() - 1) { - leftX = 1.0;//m_spline.points()[m_currentPointIndex - 1].p.x(); - rightX = 1.0; - } else { - //// the 1E-4 addition so we can grab the dot later. - leftX = m_spline.points()[m_currentPointIndex - 1].p.x();// + POINT_AREA; - rightX = m_spline.points()[m_currentPointIndex + 1].p.x();// - POINT_AREA; - } - x = qBound(leftX, x, rightX); - y = qBound(0., y, 1.); + // check for every value, so we do not lose too much info through rounding + if (m_ui.spinPX->value() != qRound(p.p.x() * 255)) + p.p.setX(m_ui.spinPX->value() / 255.); + if (m_ui.spinPY->value() != qRound(p.p.y() * 255)) + p.p.setY(m_ui.spinPY->value() / 255.); - // move handles by same offset - point.h1 += QPointF(x, y) - point.p; - point.h2 += QPointF(x, y) - point.p; + if (m_ui.spinH1X->value() != qRound(p.h1.x() * 255)) + p.h1.setX(m_ui.spinH1X->value() / 255.); + if (m_ui.spinH1Y->value() != qRound(p.h1.y() * 255)) + p.h1.setY(m_ui.spinH1Y->value() / 255.); - point.p = QPointF(x, y); - break; - case PTypeH2: - leftX = point.p.x(); - if (m_currentPointIndex == m_spline.points().count() - 1) - rightX = 1001; - else - rightX = m_spline.points()[m_currentPointIndex + 1].p.x(); - x = qBound(leftX, x, rightX); - point.h2 = QPointF(x, y); - }; + if (m_ui.spinH2X->value() != qRound(p.h2.x() * 255)) + p.h2.setX(m_ui.spinH2X->value() / 255.); + if (m_ui.spinH2Y->value() != qRound(p.h2.y() * 255)) + p.h2.setY(m_ui.spinH2Y->value() / 255.); - m_spline.setPoint(m_currentPointIndex, point); - - /*if (removePoint && d->m_curve.points().count() > 2) { - d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index]; - d->m_draggedAwayPointIndex = d->m_grab_point_index; - d->m_curve.removePoint(d->m_grab_point_index); - d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1); - } - - d->setCurveModified();*/ - update(); - } -} - -void BezierSplineWidget::leaveEvent(QEvent* event) -{ - QWidget::leaveEvent(event); -} - -int BezierSplineWidget::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineWidget::point_types* sel) -{ - double nearestDistanceSquared = 1000; - point_types selectedPoint; - int nearestIndex = -1; - int i = 0; - - double distanceSquared; - foreach(const BPoint & point, m_spline.points()) { - distanceSquared = pow(point.h1.x() - p.x(), 2) + pow(point.h1.y() - p.y(), 2); - if (distanceSquared < nearestDistanceSquared) { - nearestIndex = i; - nearestDistanceSquared = distanceSquared; - selectedPoint = PTypeH1; - } - distanceSquared = pow(point.p.x() - p.x(), 2) + pow(point.p.y() - p.y(), 2); - if (distanceSquared < nearestDistanceSquared) { - nearestIndex = i; - nearestDistanceSquared = distanceSquared; - selectedPoint = PTypeP; - } - distanceSquared = pow(point.h2.x() - p.x(), 2) + pow(point.h2.y() - p.y(), 2); - if (distanceSquared < nearestDistanceSquared) { - nearestIndex = i; - nearestDistanceSquared = distanceSquared; - selectedPoint = PTypeH2; - } - ++i; - } - - if (nearestIndex >= 0) { - BPoint point = m_spline.points()[nearestIndex]; - QPointF p2; - switch (selectedPoint) { - case PTypeH1: - p2 = point.h1; - break; - case PTypeP: - p2 = point.p; - break; - case PTypeH2: - p2 = point.h2; - } - if (qAbs(p.x() - p2.x()) * (wWidth - 1) < 5 && qAbs(p.y() - p2.y()) * (wHeight - 1) < 5) { - *sel = selectedPoint; - return nearestIndex; - } - } - - return -1; + m_edit.updateCurrentPoint(p); + emit modified(); } #include "beziersplinewidget.moc" diff --git a/src/beziercurve/beziersplinewidget.h b/src/beziercurve/beziersplinewidget.h index 245db968..5c4ba0de 100644 --- a/src/beziercurve/beziersplinewidget.h +++ b/src/beziercurve/beziersplinewidget.h @@ -20,6 +20,8 @@ #define BEZIERSPLINEWIDGET_H #include "cubicbezierspline.h" +#include "beziersplineeditor.h" +#include "ui_bezierspline_ui.h" #include #include @@ -27,38 +29,19 @@ class BezierSplineWidget : public QWidget { Q_OBJECT - + public: - BezierSplineWidget(QWidget* parent = 0); + BezierSplineWidget(const QString &spline, QWidget* parent = 0); - CubicBezierSpline spline(); - void setSpline(const CubicBezierSpline &spline); + QString spline(); -protected: - //void keyPressEvent(QKeyEvent *event); - void paintEvent(QPaintEvent *event); - void mousePressEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent * event); - void mouseMoveEvent(QMouseEvent * event); - void leaveEvent(QEvent *event); - void resizeEvent(QResizeEvent *event); +private slots: + void slotUpdatePoint(const BPoint &p); + void slotUpdateSpline(); private: - CubicBezierSpline m_spline; - enum modes { ModeDrag, ModeNormal }; - enum point_types { PTypeH1, PTypeP, PTypeH2 }; - modes m_mode; - int m_currentPointIndex; - point_types m_currentPointType; - double m_grabOffsetX; - double m_grabOffsetY; - double m_grabOriginalX; - double m_grabOriginalY; - //QPointF m_draggedAwayPoint; - //int m_draggedAwayPointIndex; - - //inline void drawGrid(QPainter &p, int width, int height); - int nearestPointInRange(QPointF p, int wWidth, int wHeight, point_types *sel); + Ui::BezierSpline_UI m_ui; + BezierSplineEditor m_edit; signals: void modified(); diff --git a/src/beziercurve/cubicbezierspline.h b/src/beziercurve/cubicbezierspline.h index 078066e2..4ddd0ce8 100644 --- a/src/beziercurve/cubicbezierspline.h +++ b/src/beziercurve/cubicbezierspline.h @@ -29,6 +29,7 @@ public: QPointF p; // point QPointF h2; // handle 2 + BPoint() { p = QPointF(-1,-1); } // makes it illegal -> cannot be equal any point bool operator==(const BPoint &point) const { return point.h1 == h1 && point.p == p && point.h2 == h2; } }; diff --git a/src/effectstackedit.cpp b/src/effectstackedit.cpp index 6e4cbc8a..458f53c2 100644 --- a/src/effectstackedit.cpp +++ b/src/effectstackedit.cpp @@ -35,7 +35,6 @@ #include "doubleparameterwidget.h" #include "cornerswidget.h" #include "beziercurve/beziersplinewidget.h" -#include "beziercurve/cubicbezierspline.h" #include #include @@ -384,10 +383,7 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in if (!depends.isEmpty()) meetDependency(paramName, type, EffectsList::parameter(e, depends)); } else if (type == "bezier_spline") { - BezierSplineWidget *widget = new BezierSplineWidget(this); - CubicBezierSpline spline; - spline.fromString(value); - widget->setSpline(spline); + BezierSplineWidget *widget = new BezierSplineWidget(value, this); stretch = false; m_vbox->addWidget(widget); m_valueItems[paramName] = widget; @@ -675,7 +671,7 @@ void EffectStackEdit::collectAllParameters() meetDependency(paramName, type, EffectsList::parameter(newparam, depends)); } else if (type == "bezier_spline") { BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName); - setValue = widget->spline().toString(); + setValue = widget->spline(); } else if (type == "corners") { CornersWidget *corners = ((CornersWidget*)m_valueItems.value(paramName)); QString xName = pa.attributes().namedItem("xpoints").nodeValue(); diff --git a/src/widgets/bezierspline_ui.ui b/src/widgets/bezierspline_ui.ui new file mode 100644 index 00000000..6e9f4cb8 --- /dev/null +++ b/src/widgets/bezierspline_ui.ui @@ -0,0 +1,206 @@ + + + BezierSpline_UI + + + + 0 + 0 + 471 + 84 + + + + Form + + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Point In + + + 255 + + + + + + + Point + + + + + + + Point Out + + + 255 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Handle 1 X + + + -100 + + + 400 + + + + + + + Handle 1 + + + H1 + + + + + + + Handle 1 Y + + + -100 + + + 400 + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Link the Handles' position so you draw a line through Handle -> Point -> Handle.<br />Results in a natural spline. + + + ... + + + true + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Handle 2 X + + + -100 + + + 400 + + + + + + + Handle 2 + + + H2 + + + + + + + Handle 2 Y + + + -100 + + + 400 + + + + + + + + + + + -- 2.39.2