]> git.sesse.net Git - kdenlive/blobdiff - src/onmonitoritems/rotoscoping/splineitem.cpp
Still const'ref. Minor optimization
[kdenlive] / src / onmonitoritems / rotoscoping / splineitem.cpp
index 9260d4fc026c3a986d0bee945da05246bdb18fde..563cd3760914ca1b37a8b21e6bf0b1930695473b 100644 (file)
 
 #include "splineitem.h"
 #include "bpointitem.h"
+#include "nearestpoint.h"
+#include "kdenlivesettings.h"
 
 #include <QGraphicsScene>
+#include <QCursor>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsView>
+
+
+inline QPointF closestPointInRect(QPointF point, QRectF rect)
+{
+    QPointF closest;
+    rect = rect.normalized();
+    closest.setX(qBound<qreal>(rect.left(), point.x(), rect.right()));
+    closest.setY(qBound<qreal>(rect.top(), point.y(), rect.bottom()));
+    return closest;
+}
+
+void deCasteljau(BPoint *p1, BPoint *p2, BPoint *res, double t)
+{
+    QPointF ab, bc, cd;
+
+    ab = QLineF(p1->p, p1->h2).pointAt(t);
+    bc = QLineF(p1->h2, p2->h1).pointAt(t);
+    cd = QLineF(p2->h1, p2->p).pointAt(t);
+
+    res->h1 = QLineF(ab, bc).pointAt(t);
+    res->h2 = QLineF(bc, cd).pointAt(t);
+    res->p = QLineF(res->h1, res->h2).pointAt(t);
+
+    p1->h2 = ab;
+    p2->h1 = cd;
+}
+
 
 SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGraphicsScene *scene) :
-    QGraphicsPathItem(parent, scene)
+    QGraphicsPathItem(parent, scene),
+    m_closed(true),
+    m_editing(false)
 {
     QPen framepen(Qt::SolidLine);
     framepen.setColor(Qt::yellow);
     setPen(framepen);
     setBrush(Qt::NoBrush);
+    setAcceptHoverEvents(true);
 
-    if (points.isEmpty())
-        return;
+    m_view = scene->views()[0];
 
-    QPainterPath path(points.at(0).p);
-    int j;
-    for (int i = 0; i < points.count(); ++i) {
-        new BPointItem(points.at(i), this);
-        j = (i + 1) % points.count();
-        path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p);
-    }
-    setPath(path);
+    setPoints(points);
 }
 
 int SplineItem::type() const
@@ -47,13 +74,18 @@ int SplineItem::type() const
     return Type;
 }
 
-void SplineItem::updateSpline()
+bool SplineItem::editing()
+{
+    return m_editing;
+}
+
+void SplineItem::updateSpline(bool editing)
 {
     QPainterPath path(qgraphicsitem_cast<BPointItem *>(childItems().at(0))->getPoint().p);
 
     BPoint p1, p2;
     int j;
-    for (int i = 0; i < childItems().count(); ++i) {
+    for (int i = 0; i < childItems().count() - !m_closed; ++i) {
         j = (i + 1) % childItems().count();
         p1 = qgraphicsitem_cast<BPointItem *>(childItems().at(i))->getPoint();
         p2 = qgraphicsitem_cast<BPointItem *>(childItems().at(j))->getPoint();
@@ -61,7 +93,10 @@ void SplineItem::updateSpline()
     }
     setPath(path);
 
-    emit changed();
+    m_editing = editing;
+
+    if (m_closed && (!editing || KdenliveSettings::monitorscene_directupdate()))
+        emit changed(editing);
 }
 
 QList <BPoint> SplineItem::getPoints()
@@ -72,4 +107,206 @@ QList <BPoint> SplineItem::getPoints()
     return points;
 }
 
+void SplineItem::setPoints(const QList< BPoint >& points)
+{
+    if (points.count() < 2) {
+        m_closed = false;
+        grabMouse();
+        return;
+    } else if (!m_closed) {
+        ungrabMouse();
+        m_closed = true;
+    }
+
+    qDeleteAll(childItems());
+    childItems().clear();
+
+    QPainterPath path(points.at(0).p);
+    int j;
+    for (int i = 0; i < points.count(); ++i) {
+        new BPointItem(points.at(i), this);
+        j = (i + 1) % points.count();
+        path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p);
+    }
+    setPath(path);
+}
+
+void SplineItem::removeChild(QGraphicsItem* child)
+{
+    if (childItems().count() > 2) {
+        scene()->removeItem(child);
+        delete child;
+        updateSpline();
+    }
+}
+
+void SplineItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
+{
+    event->setAccepted(m_closed);
+    QGraphicsItem::mousePressEvent(event);
+
+    if (event->isAccepted())
+        return;
+
+    if (m_closed) {
+        qreal size = 12 / m_view->matrix().m11();
+        QRectF r(event->scenePos() - QPointF(size / 2, size / 2), QSizeF(size, size));
+        if (event->button() == Qt::LeftButton && path().intersects(r) && !path().contains(r)) {
+            double t = 0;
+            BPointItem *i1, *i2;
+            BPoint p, p1, p2;
+            int ix = getClosestPointOnCurve(event->scenePos(), &t);
+            i1 = qgraphicsitem_cast<BPointItem *>(childItems().at(ix));
+            i2 = qgraphicsitem_cast<BPointItem *>(childItems().at((ix + 1) % childItems().count()));
+            p1 = i1->getPoint();
+            p2 = i2->getPoint();
+
+            deCasteljau(&p1, &p2, &p, t);
+
+            i1->setPoint(p1);
+            i2->setPoint(p2);
+
+#if QT_VERSION >= 0x040600
+            BPointItem *i = new BPointItem(p, this);
+            i->stackBefore(i2);
+#else
+            QList <BPoint> points;
+            BPointItem *item;
+            while (childItems().count()) {
+                item = qgraphicsitem_cast<BPointItem *>(childItems().takeFirst());
+                points.append(item->getPoint());
+                delete item;
+            }
+            int j = 0;
+            for ( ; j < points.count(); ++j) {
+                if (j == ix + 1)
+                    new BPointItem(p, this);
+                new BPointItem(points.at(j), this);
+            }
+            if (j == ix + 1)
+                new BPointItem(p, this);
+#endif
+            updateSpline();
+        }
+    } else {
+        bool close = false;
+        QList <QGraphicsItem *> items = childItems();
+        if (items.count() > 1) {
+            BPointItem *bp = qgraphicsitem_cast<BPointItem *>(items.at(0));
+            int selectionType = bp->getSelection(mapToItem(bp, event->pos()));
+            // since h1 == p we need to check for both
+            if (selectionType == 0 || selectionType == 1)
+                close = true;
+        }
+
+        if (close || event->button() == Qt::RightButton) {
+            if (items.count() > 1) {
+                // close the spline
+                BPointItem *i1 = qgraphicsitem_cast<BPointItem *>(items.first());
+                BPointItem *i2 = qgraphicsitem_cast<BPointItem *>(items.last());
+                BPoint p1 = i1->getPoint();
+                BPoint p2 = i2->getPoint();
+                p1.h1 = QLineF(p1.p, p2.p).pointAt(.2);
+                p2.h2 = QLineF(p1.p, p2.p).pointAt(.8);
+                i1->setPoint(p1);
+                i2->setPoint(p2);
+                m_closed = true;
+                ungrabMouse();
+                updateSpline();
+            }
+        } else if (event->modifiers() == Qt::NoModifier) {
+            BPoint p;
+            p.p = p.h1 = p.h2 = event->scenePos();
+            if (items.count()) {
+                BPointItem *i = qgraphicsitem_cast<BPointItem *>(items.last());
+                BPoint prev = i->getPoint();
+                prev.h2 = QLineF(prev.p, p.p).pointAt(.2);
+                p.h1 = QLineF(prev.p, p.p).pointAt(.8);
+                i->setPoint(prev);
+            }
+            new BPointItem(p, this);
+            updateSpline();
+        }
+    }
+}
+
+void SplineItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
+    QGraphicsItem::mouseMoveEvent(event);
+}
+
+void SplineItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+    QGraphicsItem::mouseReleaseEvent(event);
+}
+
+void SplineItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+    QGraphicsItem::hoverMoveEvent(event);
+
+    qreal size = 12 / m_view->matrix().m11();
+    QRectF r(event->scenePos() - QPointF(size / 2, size / 2), QSizeF(size, size));
+    if (path().intersects(r) && !path().contains(r))
+        setCursor(QCursor(Qt::PointingHandCursor));
+    else
+        unsetCursor();
+}
+
+int SplineItem::getClosestPointOnCurve(const QPointF &point, double *tFinal)
+{
+    // TODO: proper minDiff
+    qreal diff = 10000, param = 0;
+    BPoint p1, p2;
+    int curveSegment = 0, j;
+    QList <QGraphicsItem *> items = childItems();
+    for (int i = 0; i < items.count(); ++i) {
+        j = (i + 1) % items.count();
+        p1 = qgraphicsitem_cast<BPointItem *>(items.at(i))->getPoint();
+        p2 = qgraphicsitem_cast<BPointItem *>(items.at(j))->getPoint();
+        QPolygonF bounding = QPolygonF() << p1.p << p1.h2 << p2.h1 << p2.p;
+        QPointF cl = closestPointInRect(point, bounding.boundingRect());
+#if QT_VERSION >= 0x040600
+        qreal d = (point - cl).manhattanLength();
+#else
+        qreal d = qAbs((point - cl).x()) + qAbs((point - cl).y());
+#endif
+
+        if (d > diff)
+            continue;
+
+        Point2 b[4], p;
+        double t;
+
+        b[0].x = p1.p.x();
+        b[0].y = p1.p.y();
+        b[1].x = p1.h2.x();
+        b[1].y = p1.h2.y();
+        b[2].x = p2.h1.x();
+        b[2].y = p2.h1.y();
+        b[3].x = p2.p.x();
+        b[3].y = p2.p.y();
+
+        p.x = point.x();
+        p.y = point.y();
+
+        Point2 n = NearestPointOnCurve(p, b, &t);
+        cl.setX(n.x);
+        cl.setY(n.y);
+
+#if QT_VERSION >= 0x040600
+        d = (point - cl).manhattanLength();
+#else
+        d = qAbs((point - cl).x()) + qAbs((point - cl).y());
+#endif
+        if (d < diff) {
+            diff = d;
+            param = t;
+            curveSegment = i;
+        }
+    }
+
+    *tFinal = param;
+    return curveSegment;
+}
+
 #include "splineitem.moc"