X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fbeziercurve%2Fbeziersplineeditor.cpp;h=67097872c9440a3c9288cb72398fd844f11c7f6b;hb=6c74a57534cb16a553be26517d12cd633de35ca6;hp=2d85c676445431bd3f6ad2a3c1cd1371dbd325b5;hpb=9d7ba15b33babcc1d6e96b18ed15caa78d4e51d7;p=kdenlive diff --git a/src/beziercurve/beziersplineeditor.cpp b/src/beziercurve/beziersplineeditor.cpp index 2d85c676..67097872 100644 --- a/src/beziercurve/beziersplineeditor.cpp +++ b/src/beziercurve/beziersplineeditor.cpp @@ -17,6 +17,7 @@ ***************************************************************************/ #include "beziersplineeditor.h" +#include "kdenlivesettings.h" #include #include @@ -27,6 +28,7 @@ BezierSplineEditor::BezierSplineEditor(QWidget* parent) : m_mode(ModeNormal), m_zoomLevel(0), m_gridLines(3), + m_showAllHandles(true), m_pixmapCache(NULL), m_pixmapIsDirty(true), m_currentPointIndex(-1) @@ -51,24 +53,30 @@ CubicBezierSpline BezierSplineEditor::spline() void BezierSplineEditor::setSpline(const CubicBezierSpline& spline) { - // TODO: cleanup - m_spline.fromString(spline.toString()); + m_spline = spline; + m_currentPointIndex = -1; + m_mode = ModeNormal; + emit currentPoint(BPoint()); + emit modified(); + update(); } BPoint BezierSplineEditor::getCurrentPoint() { if (m_currentPointIndex >= 0) - return m_spline.points()[m_currentPointIndex]; + return m_spline.getPoint(m_currentPointIndex); else return BPoint(); } -void BezierSplineEditor::updateCurrentPoint(const BPoint& p) +void BezierSplineEditor::updateCurrentPoint(const BPoint& p, bool final) { if (m_currentPointIndex >= 0) { m_spline.setPoint(m_currentPointIndex, p); // during validation the point might have changed - emit currentPoint(m_spline.points()[m_currentPointIndex]); + emit currentPoint(m_spline.getPoint(m_currentPointIndex)); + if (final) + emit modified(); update(); } } @@ -94,6 +102,12 @@ void BezierSplineEditor::slotZoomOut() update(); } +void BezierSplineEditor::setShowAllHandles(bool show) +{ + m_showAllHandles = show; + update(); +} + int BezierSplineEditor::gridLines() { return m_gridLines; @@ -107,22 +121,26 @@ void BezierSplineEditor::setGridLines(int lines) void BezierSplineEditor::paintEvent(QPaintEvent* event) { - Q_UNUSED(event); + Q_UNUSED(event) QPainter p(this); + /* + * Zoom + */ int wWidth = width() - 1; int wHeight = height() - 1; - int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight); - wWidth -= 2 * offset; - wHeight -= 2 * offset; + int offsetX = 1/8. * m_zoomLevel * wWidth; + int offsetY = 1/8. * m_zoomLevel * wHeight; + wWidth -= 2 * offsetX; + wHeight -= 2 * offsetY; - p.translate(offset, offset); + p.translate(offsetX, offsetY); /* * Background */ - p.fillRect(rect().translated(-offset, -offset), palette().background()); + p.fillRect(rect().translated(-offsetX, -offsetY), palette().background()); if (!m_pixmap.isNull()) { if (m_pixmapIsDirty || !m_pixmapCache) { if (m_pixmapCache) @@ -166,60 +184,73 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) */ p.drawLine(QLineF(0, wHeight, wWidth, 0)); + + /* + * Prepare Spline, Points + */ + int max = m_spline.points().count() - 1; + if (max < 1) + return; + BPoint point(m_spline.getPoint(0, wWidth, wHeight, true)); + /* * Spline */ - double prevY = wHeight - m_spline.value(0.) * wHeight; - double prevX = 0.; - double curY; - double normalizedX = -1; - int x; + BPoint next; - 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; + QPainterPath splinePath(QPointF(point.p.x(), point.p.y())); + for (int i = 0; i < max; ++i) { + point = m_spline.getPoint(i, wWidth, wHeight, true); + next = m_spline.getPoint(i + 1, wWidth, wHeight, true); + splinePath.cubicTo(point.h2, next.h1, next.p); } - p.drawLine(QLineF(prevX, prevY , - x, wHeight - m_spline.value(1.0, true) * wHeight)); + p.setPen(QPen(Qt::black, 1, Qt::SolidLine)); + p.drawPath(splinePath); + /* * Points + Handles */ - int max = m_spline.points().count() - 1; p.setPen(QPen(Qt::red, 1, Qt::SolidLine)); - BPoint point; - QPolygon handle(4); - handle.setPoints(4, - 1, -2, - 4, 1, - 1, 4, - -2, 1); + + QPolygonF handle = QPolygonF() << QPointF(0, -3) << QPointF(3, 0) << QPointF(0, 3) << QPointF(-3, 0); +#if QT_VERSION < 0x040600 + QPolygonF tmp; +#endif + for (int i = 0; i <= max; ++i) { - point = m_spline.points().at(i); + point = m_spline.getPoint(i, wWidth, wHeight, true); + if (i == m_currentPointIndex) { + // selected point: fill p and handles p.setBrush(QBrush(QColor(Qt::red), Qt::SolidPattern)); + // connect p and handles with lines if (i != 0) - p.drawLine(QLineF(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight, point.p.x() * wWidth, wHeight - point.p.y() * wHeight)); + p.drawLine(QLineF(point.h1.x(), point.h1.y(), point.p.x(),point.p.y())); if (i != max) - p.drawLine(QLineF(point.p.x() * wWidth, wHeight - point.p.y() * wHeight, point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight)); + p.drawLine(QLineF(point.p.x(), point.p.y(), point.h2.x(), point.h2.y())); } - p.drawEllipse(QRectF(point.p.x() * wWidth - 3, - wHeight - 3 - point.p.y() * wHeight, 6, 6)); - if (i != 0) - p.drawConvexPolygon(handle.translated(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight)); - if (i != max) - p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight)); + p.drawEllipse(QRectF(point.p.x() - 3, + point.p.y() - 3, 6, 6)); + if (i != 0 && (i == m_currentPointIndex || m_showAllHandles)) { +#if QT_VERSION >= 0x040600 + p.drawConvexPolygon(handle.translated(point.h1.x(), point.h1.y())); +#else + tmp = handle; + tmp.translate(point.h1.x(), point.h1.y()); + p.drawConvexPolygon(tmp); +#endif + } + if (i != max && (i == m_currentPointIndex || m_showAllHandles)) { +#if QT_VERSION >= 0x040600 + p.drawConvexPolygon(handle.translated(point.h2.x(), point.h2.y())); +#else + tmp = handle; + tmp.translate(point.h2.x(), point.h2.y()); + p.drawConvexPolygon(tmp); +#endif + } if ( i == m_currentPointIndex) p.setBrush(QBrush(Qt::NoBrush)); @@ -228,7 +259,6 @@ void BezierSplineEditor::paintEvent(QPaintEvent* event) void BezierSplineEditor::resizeEvent(QResizeEvent* event) { - m_spline.setPrecision(width()); m_pixmapIsDirty = true; QWidget::resizeEvent(event); } @@ -237,12 +267,13 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event) { int wWidth = width() - 1; int wHeight = height() - 1; - int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight); - wWidth -= 2 * offset; - wHeight -= 2 * offset; + int offsetX = 1/8. * m_zoomLevel * wWidth; + int offsetY = 1/8. * m_zoomLevel * wHeight; + wWidth -= 2 * offsetX; + wHeight -= 2 * offsetY; - double x = (event->pos().x() - offset) / (double)(wWidth); - double y = 1.0 - (event->pos().y() - offset) / (double)(wHeight); + double x = (event->pos().x() - offsetX) / (double)(wWidth); + double y = 1.0 - (event->pos().y() - offsetY) / (double)(wHeight); point_types selectedPoint; int closestPointIndex = nearestPointInRange(QPointF(x, y), wWidth, wHeight, &selectedPoint); @@ -255,7 +286,7 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event) --m_currentPointIndex; update(); if (m_currentPointIndex >= 0) - emit currentPoint(m_spline.points()[m_currentPointIndex]); + emit currentPoint(m_spline.getPoint(m_currentPointIndex)); else emit currentPoint(BPoint()); emit modified(); @@ -265,48 +296,27 @@ void BezierSplineEditor::mousePressEvent(QMouseEvent* event) } 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_currentPointIndex = m_spline.addPoint(BPoint(QPointF(x-0.05, y-0.05), + QPointF(x, y), + QPointF(x+0.05, y+0.05))); m_currentPointType = PTypeP; } 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; - } + BPoint point = m_spline.getPoint(m_currentPointIndex); m_grabPOriginal = point; if (m_currentPointIndex > 0) - m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1]; + m_grabPPrevious = m_spline.getPoint(m_currentPointIndex - 1); if (m_currentPointIndex < m_spline.points().count() - 1) - m_grabPNext = m_spline.points()[m_currentPointIndex + 1]; - m_grabOffsetX = p.x() - x; - m_grabOffsetY = p.y() - y; + m_grabPNext = m_spline.getPoint(m_currentPointIndex + 1); + m_grabOffsetX = point[(int)m_currentPointType].x() - x; + m_grabOffsetY = point[(int)m_currentPointType].y() - y; + + point[(int)m_currentPointType] = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); - 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); m_mode = ModeDrag; @@ -330,12 +340,13 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event) { int wWidth = width() - 1; int wHeight = height() - 1; - int offset = 1/8. * m_zoomLevel * (wWidth > wHeight ? wWidth : wHeight); - wWidth -= 2 * offset; - wHeight -= 2 * offset; + int offsetX = 1/8. * m_zoomLevel * wWidth; + int offsetY = 1/8. * m_zoomLevel * wHeight; + wWidth -= 2 * offsetX; + wHeight -= 2 * offsetY; - double x = (event->pos().x() - offset) / (double)(wWidth); - double y = 1.0 - (event->pos().y() - offset) / (double)(wHeight); + double x = (event->pos().x() - offsetX) / (double)(wWidth); + double y = 1.0 - (event->pos().y() - offsetY) / (double)(wHeight); if (m_mode == ModeNormal) { // If no point is selected set the the cursor shape if on top @@ -349,23 +360,23 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event) } else { // Else, drag the selected point setCursor(Qt::CrossCursor); - + x += m_grabOffsetX; y += m_grabOffsetY; double leftX = 0.; double rightX = 1.; - BPoint point = m_spline.points()[m_currentPointIndex]; + BPoint point = m_spline.getPoint(m_currentPointIndex); switch (m_currentPointType) { case PTypeH1: rightX = point.p.x(); if (m_currentPointIndex == 0) - leftX = -1000; + leftX = -4; else - leftX = m_spline.points()[m_currentPointIndex - 1].p.x(); + leftX = m_spline.getPoint(m_currentPointIndex - 1).p.x(); x = qBound(leftX, x, rightX); - point.h1 = QPointF(x, y); + point.setH1(QPointF(x, y)); break; case PTypeP: @@ -381,22 +392,23 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event) // try to restore point.h1 = m_grabPOriginal.h1; point.h2 = m_grabPOriginal.h2; - // and then move them by same offset + // and move by same offset + // (using update handle in point.setP won't work because the offset between new and old point is very small) point.h1 += QPointF(x, y) - m_grabPOriginal.p; point.h2 += QPointF(x, y) - m_grabPOriginal.p; - point.p = QPointF(x, y); + point.setP(QPointF(x, y), false); break; case PTypeH2: leftX = point.p.x(); if (m_currentPointIndex == m_spline.points().count() - 1) - rightX = 1001; + rightX = 5; else - rightX = m_spline.points()[m_currentPointIndex + 1].p.x(); + rightX = m_spline.getPoint(m_currentPointIndex + 1).p.x(); x = qBound(leftX, x, rightX); - point.h2 = QPointF(x, y); + point.setH2(QPointF(x, y)); }; int index = m_currentPointIndex; @@ -415,21 +427,33 @@ void BezierSplineEditor::mouseMoveEvent(QMouseEvent* event) m_spline.setPoint(index, m_grabPPrevious); m_grabPNext = m_grabPPrevious; if (m_currentPointIndex > 0) - m_grabPPrevious = m_spline.points()[m_currentPointIndex - 1]; + m_grabPPrevious = m_spline.getPoint(m_currentPointIndex - 1); } else { m_spline.setPoint(index, m_grabPNext); m_grabPPrevious = m_grabPNext; if (m_currentPointIndex < m_spline.points().count() - 1) - m_grabPNext = m_spline.points()[m_currentPointIndex + 1]; + m_grabPNext = m_spline.getPoint(m_currentPointIndex + 1); } } } emit currentPoint(point); + if (KdenliveSettings::dragvalue_directupdate()) + emit modified(); update(); } } +void BezierSplineEditor::mouseDoubleClickEvent(QMouseEvent* /*event*/) +{ + if (m_currentPointIndex >= 0) { + BPoint p = m_spline.getPoint(m_currentPointIndex); + p.handlesLinked = !p.handlesLinked; + m_spline.setPoint(m_currentPointIndex, p); + emit currentPoint(p); + } +} + void BezierSplineEditor::leaveEvent(QEvent* event) { QWidget::leaveEvent(event); @@ -438,48 +462,28 @@ void BezierSplineEditor::leaveEvent(QEvent* event) int BezierSplineEditor::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineEditor::point_types* sel) { double nearestDistanceSquared = 1000; - point_types selectedPoint; + point_types selectedPoint = PTypeP; int nearestIndex = -1; int i = 0; double distanceSquared; // find out distance using the Pythagorean theorem 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; + for (int j = 0; j < 3; ++j) { + distanceSquared = pow(point[j].x() - p.x(), 2) + pow(point[j].y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = (point_types)j; + } } ++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 < 5 && qAbs(p.y() - p2.y()) * wHeight < 5) { + if (nearestIndex >= 0 && (nearestIndex == m_currentPointIndex || selectedPoint == PTypeP || m_showAllHandles)) { + // a point was found and it is not a hidden handle + BPoint point = m_spline.getPoint(nearestIndex); + if (qAbs(p.x() - point[(int)selectedPoint].x()) * wWidth < 5 && qAbs(p.y() - point[(int)selectedPoint].y()) * wHeight < 5) { *sel = selectedPoint; return nearestIndex; }