***************************************************************************/
#include "beziersplineeditor.h"
+#include "kdenlivesettings.h"
#include <QPainter>
#include <QMouseEvent>
BezierSplineEditor::BezierSplineEditor(QWidget* parent) :
- QWidget(parent),
- m_mode(ModeNormal),
- m_zoomLevel(0),
- m_gridLines(3),
- m_pixmapCache(NULL),
- m_pixmapIsDirty(true),
- m_currentPointIndex(-1)
+ QWidget(parent)
+ , m_mode(ModeNormal)
+ , m_zoomLevel(0)
+ , m_gridLines(3)
+ , m_showAllHandles(true)
+ , m_pixmapCache(NULL)
+ , m_pixmapIsDirty(true)
+ , m_currentPointIndex(-1)
+ , m_currentPointType(PTypeP)
+ , m_grabOffsetX(0)
+ , m_grabOffsetY(0)
{
setMouseTracking(true);
setAutoFillBackground(false);
BezierSplineEditor::~BezierSplineEditor()
{
- if (m_pixmapCache)
- delete m_pixmapCache;
+ delete m_pixmapCache;
}
-CubicBezierSpline BezierSplineEditor::spline()
+CubicBezierSpline BezierSplineEditor::spline() const
{
return m_spline;
}
void BezierSplineEditor::setSpline(const CubicBezierSpline& spline)
{
- int precision = m_spline.getPrecision();
m_spline = spline;
- m_spline.setPrecision(precision);
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();
}
}
update();
}
-int BezierSplineEditor::gridLines()
+void BezierSplineEditor::setShowAllHandles(bool show)
+{
+ if (m_showAllHandles != show) {
+ m_showAllHandles = show;
+ update();
+ }
+}
+
+int BezierSplineEditor::gridLines() const
{
return m_gridLines;
}
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)
*/
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
- QPolygon tmp;
+ 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.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() * wWidth, wHeight - point.h1.y() * wHeight));
+ p.drawConvexPolygon(handle.translated(point.h1.x(), point.h1.y()));
#else
tmp = handle;
- tmp.translate(point.h1.x() * wWidth, wHeight - point.h1.y() * wHeight);
+ tmp.translate(point.h1.x(), point.h1.y());
p.drawConvexPolygon(tmp);
#endif
}
- if (i != max) {
+ if (i != max && (i == m_currentPointIndex || m_showAllHandles)) {
#if QT_VERSION >= 0x040600
- p.drawConvexPolygon(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight));
+ p.drawConvexPolygon(handle.translated(point.h2.x(), point.h2.y()));
#else
tmp = handle;
- tmp.translate(handle.translated(point.h2.x() * wWidth, wHeight - point.h2.y() * wHeight);
+ tmp.translate(point.h2.x(), point.h2.y());
p.drawConvexPolygon(tmp);
#endif
}
void BezierSplineEditor::resizeEvent(QResizeEvent* event)
{
- m_spline.setPrecision(width() > height() ? width() : height());
m_pixmapIsDirty = true;
QWidget::resizeEvent(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);
--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();
}
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;
{
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
} 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:
// 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;
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);
}
-int BezierSplineEditor::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineEditor::point_types* sel)
+int BezierSplineEditor::nearestPointInRange(const 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;
}