1 /***************************************************************************
2 * Copyright (C) 2011 by Till Theato (root@ttill.de) *
3 * This file is part of Kdenlive (www.kdenlive.org). *
5 * Kdenlive is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
10 * Kdenlive is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with Kdenlive. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
19 #include "splineitem.h"
20 #include "bpointitem.h"
21 #include "nearestpoint.h"
22 #include "kdenlivesettings.h"
24 #include <QGraphicsScene>
26 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsView>
30 inline QPointF closestPointInRect(QPointF point, QRectF rect)
33 rect = rect.normalized();
34 closest.setX(qBound<qreal>(rect.left(), point.x(), rect.right()));
35 closest.setY(qBound<qreal>(rect.top(), point.y(), rect.bottom()));
39 void deCasteljau(BPoint *p1, BPoint *p2, BPoint *res, double t)
43 ab = QLineF(p1->p, p1->h2).pointAt(t);
44 bc = QLineF(p1->h2, p2->h1).pointAt(t);
45 cd = QLineF(p2->h1, p2->p).pointAt(t);
47 res->h1 = QLineF(ab, bc).pointAt(t);
48 res->h2 = QLineF(bc, cd).pointAt(t);
49 res->p = QLineF(res->h1, res->h2).pointAt(t);
56 SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGraphicsScene *scene) :
57 QGraphicsPathItem(parent, scene),
61 QPen framepen(Qt::SolidLine);
62 framepen.setColor(Qt::yellow);
64 setBrush(Qt::NoBrush);
65 setAcceptHoverEvents(true);
67 m_view = scene->views()[0];
72 int SplineItem::type() const
77 bool SplineItem::editing()
82 void SplineItem::updateSpline(bool editing)
84 QPainterPath path(qgraphicsitem_cast<BPointItem *>(childItems().at(0))->getPoint().p);
88 for (int i = 0; i < childItems().count() - !m_closed; ++i) {
89 j = (i + 1) % childItems().count();
90 p1 = qgraphicsitem_cast<BPointItem *>(childItems().at(i))->getPoint();
91 p2 = qgraphicsitem_cast<BPointItem *>(childItems().at(j))->getPoint();
92 path.cubicTo(p1.h2, p2.h1, p2.p);
98 if (m_closed && (!editing || KdenliveSettings::monitorscene_directupdate()))
99 emit changed(editing);
102 QList <BPoint> SplineItem::getPoints()
104 QList <BPoint> points;
105 foreach (QGraphicsItem *child, childItems())
106 points << qgraphicsitem_cast<BPointItem *>(child)->getPoint();
110 void SplineItem::setPoints(const QList< BPoint >& points)
112 if (points.count() < 2) {
116 } else if (!m_closed) {
121 qDeleteAll(childItems());
122 childItems().clear();
124 QPainterPath path(points.at(0).p);
126 for (int i = 0; i < points.count(); ++i) {
127 new BPointItem(points.at(i), this);
128 j = (i + 1) % points.count();
129 path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p);
134 void SplineItem::removeChild(QGraphicsItem* child)
136 if (childItems().count() > 2) {
137 scene()->removeItem(child);
143 void SplineItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
145 event->setAccepted(m_closed);
146 QGraphicsItem::mousePressEvent(event);
148 if (event->isAccepted())
152 qreal size = 12 / m_view->matrix().m11();
153 QRectF r(event->scenePos() - QPointF(size / 2, size / 2), QSizeF(size, size));
154 if (event->button() == Qt::LeftButton && path().intersects(r) && !path().contains(r)) {
158 int ix = getClosestPointOnCurve(event->scenePos(), &t);
159 i1 = qgraphicsitem_cast<BPointItem *>(childItems().at(ix));
160 i2 = qgraphicsitem_cast<BPointItem *>(childItems().at((ix + 1) % childItems().count()));
164 deCasteljau(&p1, &p2, &p, t);
169 #if QT_VERSION >= 0x040600
170 BPointItem *i = new BPointItem(p, this);
173 QList <BPoint> points;
175 while (childItems().count()) {
176 item = qgraphicsitem_cast<BPointItem *>(childItems().takeFirst());
177 points.append(item->getPoint());
181 for ( ; j < points.count(); ++j) {
183 new BPointItem(p, this);
184 new BPointItem(points.at(j), this);
187 new BPointItem(p, this);
193 QList <QGraphicsItem *> items = childItems();
194 if (items.count() > 1) {
195 BPointItem *bp = qgraphicsitem_cast<BPointItem *>(items.at(0));
196 int selectionType = bp->getSelection(mapToItem(bp, event->pos()));
197 // since h1 == p we need to check for both
198 if (selectionType == 0 || selectionType == 1)
202 if (close || event->button() == Qt::RightButton) {
203 if (items.count() > 1) {
205 BPointItem *i1 = qgraphicsitem_cast<BPointItem *>(items.first());
206 BPointItem *i2 = qgraphicsitem_cast<BPointItem *>(items.last());
207 BPoint p1 = i1->getPoint();
208 BPoint p2 = i2->getPoint();
209 p1.h1 = QLineF(p1.p, p2.p).pointAt(.2);
210 p2.h2 = QLineF(p1.p, p2.p).pointAt(.8);
217 } else if (event->modifiers() == Qt::NoModifier) {
219 p.p = p.h1 = p.h2 = event->scenePos();
221 BPointItem *i = qgraphicsitem_cast<BPointItem *>(items.last());
222 BPoint prev = i->getPoint();
223 prev.h2 = QLineF(prev.p, p.p).pointAt(.2);
224 p.h1 = QLineF(prev.p, p.p).pointAt(.8);
227 new BPointItem(p, this);
233 void SplineItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
235 QGraphicsItem::mouseMoveEvent(event);
238 void SplineItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
240 QGraphicsItem::mouseReleaseEvent(event);
243 void SplineItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
245 QGraphicsItem::hoverMoveEvent(event);
247 qreal size = 12 / m_view->matrix().m11();
248 QRectF r(event->scenePos() - QPointF(size / 2, size / 2), QSizeF(size, size));
249 if (path().intersects(r) && !path().contains(r))
250 setCursor(QCursor(Qt::PointingHandCursor));
255 int SplineItem::getClosestPointOnCurve(QPointF point, double *tFinal)
257 // TODO: proper minDiff
258 qreal diff = 10000, param = 0;
260 int curveSegment = 0, j;
261 QList <QGraphicsItem *> items = childItems();
262 for (int i = 0; i < items.count(); ++i) {
263 j = (i + 1) % items.count();
264 p1 = qgraphicsitem_cast<BPointItem *>(items.at(i))->getPoint();
265 p2 = qgraphicsitem_cast<BPointItem *>(items.at(j))->getPoint();
266 QPolygonF bounding = QPolygonF() << p1.p << p1.h2 << p2.h1 << p2.p;
267 QPointF cl = closestPointInRect(point, bounding.boundingRect());
268 #if QT_VERSION >= 0x040600
269 qreal d = (point - cl).manhattanLength();
271 qreal d = qAbs((point - cl).x()) + qAbs((point - cl).y());
292 Point2 n = NearestPointOnCurve(p, b, &t);
296 #if QT_VERSION >= 0x040600
297 d = (point - cl).manhattanLength();
299 d = qAbs((point - cl).x()) + qAbs((point - cl).y());
312 #include "splineitem.moc"