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>
29 inline QPointF closestPointInRect(QPointF point, QRectF rect)
32 rect = rect.normalized();
33 closest.setX(qBound<qreal>(rect.left(), point.x(), rect.right()));
34 closest.setY(qBound<qreal>(rect.top(), point.y(), rect.bottom()));
38 void deCasteljau(BPoint *p1, BPoint *p2, BPoint *res, double t)
42 ab = QLineF(p1->p, p1->h2).pointAt(t);
43 bc = QLineF(p1->h2, p2->h1).pointAt(t);
44 cd = QLineF(p2->h1, p2->p).pointAt(t);
46 res->h1 = QLineF(ab, bc).pointAt(t);
47 res->h2 = QLineF(bc, cd).pointAt(t);
48 res->p = QLineF(res->h1, res->h2).pointAt(t);
55 SplineItem::SplineItem(const QList< BPoint >& points, QGraphicsItem* parent, QGraphicsScene *scene) :
56 QGraphicsPathItem(parent, scene),
60 QPen framepen(Qt::SolidLine);
61 framepen.setColor(Qt::yellow);
63 setBrush(Qt::NoBrush);
64 setAcceptHoverEvents(true);
69 int SplineItem::type() const
74 bool SplineItem::editing()
79 void SplineItem::updateSpline(bool editing)
81 QPainterPath path(qgraphicsitem_cast<BPointItem *>(childItems().at(0))->getPoint().p);
85 for (int i = 0; i < childItems().count() - !m_closed; ++i) {
86 j = (i + 1) % childItems().count();
87 p1 = qgraphicsitem_cast<BPointItem *>(childItems().at(i))->getPoint();
88 p2 = qgraphicsitem_cast<BPointItem *>(childItems().at(j))->getPoint();
89 path.cubicTo(p1.h2, p2.h1, p2.p);
95 if (m_closed && (!editing || KdenliveSettings::monitorscene_directupdate()))
96 emit changed(editing);
99 QList <BPoint> SplineItem::getPoints()
101 QList <BPoint> points;
102 foreach (QGraphicsItem *child, childItems())
103 points << qgraphicsitem_cast<BPointItem *>(child)->getPoint();
107 void SplineItem::setPoints(const QList< BPoint >& points)
109 if (points.count() < 2) {
118 qDeleteAll(childItems());
119 childItems().clear();
121 QPainterPath path(points.at(0).p);
123 for (int i = 0; i < points.count(); ++i) {
124 new BPointItem(points.at(i), this);
125 j = (i + 1) % points.count();
126 path.cubicTo(points.at(i).h2, points.at(j).h1, points.at(j).p);
131 void SplineItem::removeChild(QGraphicsItem* child)
133 if (childItems().count() > 2) {
134 scene()->removeItem(child);
140 void SplineItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
142 event->setAccepted(m_closed);
143 QGraphicsItem::mousePressEvent(event);
145 if (event->isAccepted())
149 QRectF r(event->scenePos() - QPointF(6, 6), QSizeF(12, 12));
150 if (event->button() == Qt::LeftButton && path().intersects(r) && !path().contains(r)) {
154 int ix = getClosestPointOnCurve(event->scenePos(), &t);
155 i1 = qgraphicsitem_cast<BPointItem *>(childItems().at(ix));
156 i2 = qgraphicsitem_cast<BPointItem *>(childItems().at((ix + 1) % childItems().count()));
160 deCasteljau(&p1, &p2, &p, t);
165 #if QT_VERSION >= 0x040600
166 BPointItem *i = new BPointItem(p, this);
169 QList <BPoint> points;
171 while (childItems().count()) {
172 item = qgraphicsitem_cast<BPointItem *>(childItems().takeFirst());
173 points.append(item->getPoint());
177 for ( ; j < points.count(); ++j) {
179 new BPointItem(p, this);
180 new BPointItem(points.at(j), this);
183 new BPointItem(p, this);
188 if (event->button() == Qt::RightButton) {
189 if (childItems().count() > 1) {
191 BPointItem *i1 = qgraphicsitem_cast<BPointItem *>(childItems().first());
192 BPointItem *i2 = qgraphicsitem_cast<BPointItem *>(childItems().last());
193 BPoint p1 = i1->getPoint();
194 BPoint p2 = i2->getPoint();
195 p1.h1 = QLineF(p1.p, p2.p).pointAt(.2);
196 p2.h2 = QLineF(p1.p, p2.p).pointAt(.8);
203 } else if (event->modifiers() == Qt::NoModifier) {
205 p.p = p.h1 = p.h2 = event->scenePos();
206 if (childItems().count()) {
207 BPointItem *i = qgraphicsitem_cast<BPointItem *>(childItems().last());
208 BPoint prev = i->getPoint();
209 prev.h2 = QLineF(prev.p, p.p).pointAt(.2);
210 p.h1 = QLineF(prev.p, p.p).pointAt(.8);
213 new BPointItem(p, this);
219 void SplineItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
221 QGraphicsItem::mouseMoveEvent(event);
224 void SplineItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
226 QGraphicsItem::mouseReleaseEvent(event);
229 void SplineItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
231 QGraphicsItem::hoverMoveEvent(event);
233 QRectF r(event->scenePos() - QPointF(6, 6), QSizeF(12, 12));
234 if (path().intersects(r) && !path().contains(r))
235 setCursor(QCursor(Qt::PointingHandCursor));
240 int SplineItem::getClosestPointOnCurve(QPointF point, double *tFinal)
242 // TODO: proper minDiff
243 qreal diff = 10000, param = 0;
245 int curveSegment = 0, j;
246 for (int i = 0; i < childItems().count(); ++i) {
247 j = (i + 1) % childItems().count();
248 p1 = qgraphicsitem_cast<BPointItem *>(childItems().at(i))->getPoint();
249 p2 = qgraphicsitem_cast<BPointItem *>(childItems().at(j))->getPoint();
250 QPolygonF bounding = QPolygonF() << p1.p << p1.h2 << p2.h1 << p2.p;
251 QPointF cl = closestPointInRect(point, bounding.boundingRect());
252 #if QT_VERSION >= 0x040600
253 qreal d = (point - cl).manhattanLength();
255 qreal d = qAbs((point - cl).x()) + qAbs((point - cl).y());
276 Point2 n = NearestPointOnCurve(p, b, &t);
280 #if QT_VERSION >= 0x040600
281 d = (point - cl).manhattanLength();
283 d = qAbs((point - cl).x()) + qAbs((point - cl).y());
296 #include "splineitem.moc"