]> git.sesse.net Git - kdenlive/blob - src/onmonitoritems/onmonitorcornersitem.cpp
corners on-monitor editing: add tools to move the sides (2 corners at once)
[kdenlive] / src / onmonitoritems / onmonitorcornersitem.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Till Theato (root@ttill.de)                     *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20 #include "onmonitorcornersitem.h"
21 #include "kdenlivesettings.h"
22
23 #include <algorithm>
24
25 #include <QGraphicsSceneMouseEvent>
26 #include <QPainter>
27 #include <QStyleOptionGraphicsItem>
28 #include <QCursor>
29
30 OnMonitorCornersItem::OnMonitorCornersItem(MonitorScene* scene, QGraphicsItem* parent) :
31         AbstractOnMonitorItem(scene),
32         QGraphicsPolygonItem(parent),
33         m_selectedCorner(-1)
34 {
35     setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
36
37     QPen framepen(Qt::SolidLine);
38     framepen.setColor(Qt::yellow);
39     setPen(framepen);
40     setBrush(Qt::NoBrush);
41 }
42
43 OnMonitorCornersItem::cornersActions OnMonitorCornersItem::getMode(QPointF pos, int *corner)
44 {
45     *corner = -1;
46
47     QPainterPath mouseArea;
48     pos = mapFromScene(pos);
49     mouseArea.addRect(pos.x() - 6, pos.y() - 6, 12, 12);
50     for (int i = 0; i < 4; ++i) {
51         if (mouseArea.contains(polygon().at(i))) {
52             *corner = 1;
53             return Corner;
54         }
55     }
56     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols()) {
57         if (mouseArea.contains(getCentroid()))
58             return Move;
59
60         int j;
61         for (int i = 0; i < 4; ++i) {
62             j = (i + 1) % 4;
63             if (mouseArea.contains(QLineF(polygon().at(i), polygon().at(j)).pointAt(.5))) {
64                 *corner = i;
65                 return MoveSide;
66             }
67         }
68     }
69
70     return NoAction;
71 }
72
73 void OnMonitorCornersItem::slotMousePressed(QGraphicsSceneMouseEvent* event)
74 {
75     event->accept();
76
77     if (!isEnabled())
78         return;
79
80     m_mode = getMode(event->scenePos(), &m_selectedCorner);
81     m_lastPoint = event->scenePos();
82 }
83
84 void OnMonitorCornersItem::slotMouseMoved(QGraphicsSceneMouseEvent* event)
85 {
86     event->accept();
87
88     if (!isEnabled()) {
89         emit requestCursor(QCursor(Qt::ArrowCursor));
90         return;
91     }
92
93     /*if (event->buttons() != Qt::NoButton && (event->screenPos() - m_screenClickPoint).manhattanLength() < QApplication::startDragDistance()) {
94      *   event->accept();
95      *   return;
96     }*/
97
98     if (event->buttons() & Qt::LeftButton) {
99         QPointF mousePos = mapFromScene(event->scenePos());
100         QPolygonF p = polygon();
101         switch (m_mode) {
102         case Corner:
103             p.replace(m_selectedCorner, mousePos);
104             m_modified = true;
105             break;
106         case Move:
107             p.translate(mousePos - m_lastPoint);
108             m_modified = true;
109             break;
110         case MoveSide:
111             p[m_selectedCorner] += mousePos - m_lastPoint;
112             p[(m_selectedCorner + 1) % 4] += mousePos - m_lastPoint;
113             m_modified = true;
114             break;
115         default:
116             break;
117         }
118         m_lastPoint = mousePos;
119         setPolygon(p);
120     } else {
121         int corner;
122         switch (getMode(event->scenePos(), &corner)) {
123         case NoAction:
124             emit requestCursor(QCursor(Qt::ArrowCursor));
125             break;
126         case Move:
127             emit requestCursor(QCursor(Qt::SizeAllCursor));
128             break;
129         default:
130             emit requestCursor(QCursor(Qt::OpenHandCursor));
131             break;
132         }
133     }
134     if (m_modified && KdenliveSettings::monitorscene_directupdate()) {
135         emit actionFinished();
136         m_modified = false;
137     }
138 }
139
140 void OnMonitorCornersItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
141 {
142     painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
143
144     if (KdenliveSettings::onmonitoreffects_cornersshowlines())
145         QGraphicsPolygonItem::paint(painter, option, widget);
146
147     double baseSize = 1 / painter->matrix().m11();
148     painter->setRenderHint(QPainter::Antialiasing);
149     painter->setBrush(QBrush(Qt::yellow));
150     double handleSize = 4  * baseSize;
151     for (int i = 0; i < 4; ++i)
152         painter->drawEllipse(polygon().at(i), handleSize, handleSize);
153
154     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols()) {
155         painter->setPen(QPen(Qt::red, 2, Qt::SolidLine));
156         double toolSize = 6 * baseSize;
157         // move tool
158         QPointF c = getCentroid();
159         painter->drawLine(QLineF(c - QPointF(toolSize, toolSize), c + QPointF(toolSize, toolSize)));
160         painter->drawLine(QLineF(c - QPointF(-toolSize, toolSize), c + QPointF(-toolSize, toolSize)));
161
162         // move side tools (2 corners at once)
163         int j;
164         for (int i = 0; i < 4; ++i) {
165             j = (i + 1) % 4;
166             QPointF m = QLineF(polygon().at(i), polygon().at(j)).pointAt(.5);
167             painter->drawRect(QRectF(-toolSize / 2., -toolSize / 2., toolSize, toolSize).translated(m));
168         }
169     }
170 }
171
172 QPointF OnMonitorCornersItem::getCentroid()
173 {
174     QList <QPointF> p = sortedClockwise();
175
176     /*
177      * See: http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
178      */
179
180     double A = 0;
181     int i, j;
182     for (i = 0; i < 4; ++i) {
183         j = (i + 1) % 4;
184         A += p[j].x() * p[i].y() - p[i].x() * p[j].y();
185     }
186     A /= 2.;
187     A = qAbs(A);
188
189     double x = 0, y = 0, f;
190     for (i = 0; i < 4; ++i) {
191         j = (i + 1) % 4;
192         f = (p[i].x() * p[j].y() - p[j].x() * p[i].y());
193         x += f * (p[i].x() + p[j].x());
194         y += f * (p[i].y() + p[j].y());
195     }
196     x /= 6*A;
197     y /= 6*A;
198     return QPointF(x, y);
199 }
200
201 QList <QPointF> OnMonitorCornersItem::sortedClockwise()
202 {
203     QList <QPointF> points = polygon().toList();
204     QPointF& a = points[0];
205     QPointF& b = points[1];
206     QPointF& c = points[2];
207     QPointF& d = points[3];
208
209     /*
210      * http://stackoverflow.com/questions/242404/sort-four-points-in-clockwise-order
211      */
212
213     double abc = a.x() * b.y() - a.y() * b.x() + b.x() * c.y() - b.y() * c.x() + c.x() * a.y() - c.y() * a.x();
214     if (abc > 0) {
215         double acd = a.x() * c.y() - a.y() * c.x() + c.x() * d.y() - c.y() * d.x() + d.x() * a.y() - d.y() * a.x();
216         if (acd > 0) {
217             ;
218         } else {
219             double abd = a.x() * b.y() - a.y() * b.x() + b.x() * d.y() - b.y() * d.x() + d.x() * a.y() - d.y() * a.x();
220             if (abd > 0) {
221                 std::swap(d, c);
222             } else{
223                 std::swap(a, d);
224             }
225         }
226     } else {
227         double acd = a.x() * c.y() - a.y() * c.x() + c.x() * d.y() - c.y() * d.x() + d.x() * a.y() - d.y() * a.x();
228         if (acd > 0) {
229             double abd = a.x() * b.y() - a.y() * b.x() + b.x() * d.y() - b.y() * d.x() + d.x() * a.y() - d.y() * a.x();
230             if (abd > 0) {
231                 std::swap(b, c);
232             } else {
233                 std::swap(a, b);
234             }
235         } else {
236             std::swap(a, c);
237         }
238     }
239     return points;
240 }
241
242 #include "onmonitorcornersitem.moc"