]> git.sesse.net Git - kdenlive/blob - src/onmonitoritems/onmonitorcornersitem.cpp
on-monitor editing:
[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(QGraphicsItem* parent) :
31         QGraphicsPolygonItem(parent),
32         m_selectedCorner(-1),
33         m_modified(false)
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     setAcceptHoverEvents(true);
42 }
43
44 OnMonitorCornersItem::cornersActions OnMonitorCornersItem::getMode(QPointF pos, int *corner)
45 {
46     *corner = -1;
47     if (polygon().count() != 4)
48         return NoAction;
49
50     QPainterPath mouseArea;
51     mouseArea.addRect(pos.x() - 6, pos.y() - 6, 12, 12);
52     for (int i = 0; i < 4; ++i) {
53         if (mouseArea.contains(polygon().at(i))) {
54             *corner = i;
55             return Corner;
56         }
57     }
58     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols()) {
59         if (mouseArea.contains(getCentroid()))
60             return Move;
61
62         int j;
63         for (int i = 0; i < 4; ++i) {
64             j = (i + 1) % 4;
65             if (mouseArea.contains(QLineF(polygon().at(i), polygon().at(j)).pointAt(.5))) {
66                 *corner = i;
67                 return MoveSide;
68             }
69         }
70     }
71
72     return NoAction;
73 }
74
75 void OnMonitorCornersItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
76 {
77     m_mode = getMode(event->pos(), &m_selectedCorner);
78     m_lastPoint = event->scenePos();
79
80     if (m_mode == NoAction)
81         event->ignore();
82 }
83
84 void OnMonitorCornersItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
85 {
86     /*if (event->buttons() != Qt::NoButton && (event->screenPos() - m_screenClickPoint).manhattanLength() < QApplication::startDragDistance()) {
87      *   event->accept();
88      *   return;
89     }*/
90
91     if (event->buttons() & Qt::LeftButton) {
92         QPointF mousePos = mapFromScene(event->scenePos());
93         QPolygonF p = polygon();
94         switch (m_mode) {
95         case Corner:
96             p.replace(m_selectedCorner, mousePos);
97             m_modified = true;
98             break;
99         case Move:
100             p.translate(mousePos - m_lastPoint);
101             m_modified = true;
102             break;
103         case MoveSide:
104             p[m_selectedCorner] += mousePos - m_lastPoint;
105             p[(m_selectedCorner + 1) % 4] += mousePos - m_lastPoint;
106             m_modified = true;
107             break;
108         default:
109             break;
110         }
111         m_lastPoint = mousePos;
112         setPolygon(p);
113     }
114
115     if (m_modified) {
116         event->accept();
117         if (KdenliveSettings::monitorscene_directupdate()) {
118             emit changed();
119             m_modified = false;
120         }
121     } else {
122         event->ignore();
123     }
124 }
125
126 void OnMonitorCornersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
127 {
128     if (m_modified) {
129         m_modified = false;
130         emit changed();
131     }
132     event->accept();
133 }
134
135 void OnMonitorCornersItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
136 {
137     int corner;
138     switch (getMode(event->pos(), &corner)) {
139     case NoAction:
140         unsetCursor();
141         break;
142     case Move:
143         setCursor(QCursor(Qt::SizeAllCursor));
144         break;
145     default:
146         setCursor(QCursor(Qt::OpenHandCursor));
147         break;
148     }
149 }
150
151 void OnMonitorCornersItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
152 {
153     painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
154
155     if (KdenliveSettings::onmonitoreffects_cornersshowlines())
156         QGraphicsPolygonItem::paint(painter, option, widget);
157
158     if (polygon().count() != 4)
159         return;
160
161     double baseSize = 1 / painter->matrix().m11();
162     painter->setRenderHint(QPainter::Antialiasing);
163     painter->setBrush(QBrush(isEnabled() ? Qt::yellow : Qt::red));
164     double handleSize = 4  * baseSize;
165     for (int i = 0; i < 4; ++i)
166         painter->drawEllipse(polygon().at(i), handleSize, handleSize);
167
168     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols() && isEnabled()) {
169         painter->setPen(QPen(Qt::red, 2, Qt::SolidLine));
170         double toolSize = 6 * baseSize;
171         // move tool
172         QPointF c = getCentroid();
173         painter->drawLine(QLineF(c - QPointF(toolSize, toolSize), c + QPointF(toolSize, toolSize)));
174         painter->drawLine(QLineF(c - QPointF(-toolSize, toolSize), c + QPointF(-toolSize, toolSize)));
175
176         // move side tools (2 corners at once)
177         int j;
178         for (int i = 0; i < 4; ++i) {
179             j = (i + 1) % 4;
180             QPointF m = QLineF(polygon().at(i), polygon().at(j)).pointAt(.5);
181             painter->drawRect(QRectF(-toolSize / 2., -toolSize / 2., toolSize, toolSize).translated(m));
182         }
183     }
184 }
185
186 QPointF OnMonitorCornersItem::getCentroid()
187 {
188     QList <QPointF> p = sortedClockwise();
189
190     /*
191      * See: http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
192      */
193
194     double A = 0;
195     int i, j;
196     for (i = 0; i < 4; ++i) {
197         j = (i + 1) % 4;
198         A += p[j].x() * p[i].y() - p[i].x() * p[j].y();
199     }
200     A /= 2.;
201     A = qAbs(A);
202
203     double x = 0, y = 0, f;
204     for (i = 0; i < 4; ++i) {
205         j = (i + 1) % 4;
206         f = (p[i].x() * p[j].y() - p[j].x() * p[i].y());
207         x += f * (p[i].x() + p[j].x());
208         y += f * (p[i].y() + p[j].y());
209     }
210     x /= 6*A;
211     y /= 6*A;
212     return QPointF(x, y);
213 }
214
215 QList <QPointF> OnMonitorCornersItem::sortedClockwise()
216 {
217     QList <QPointF> points = polygon().toList();
218     QPointF& a = points[0];
219     QPointF& b = points[1];
220     QPointF& c = points[2];
221     QPointF& d = points[3];
222
223     /*
224      * http://stackoverflow.com/questions/242404/sort-four-points-in-clockwise-order
225      */
226
227     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();
228     if (abc > 0) {
229         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();
230         if (acd > 0) {
231             ;
232         } else {
233             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();
234             if (abd > 0) {
235                 std::swap(d, c);
236             } else{
237                 std::swap(a, d);
238             }
239         }
240     } else {
241         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();
242         if (acd > 0) {
243             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();
244             if (abd > 0) {
245                 std::swap(b, c);
246             } else {
247                 std::swap(a, b);
248             }
249         } else {
250             std::swap(a, c);
251         }
252     }
253     return points;
254 }
255
256 #include "onmonitorcornersitem.moc"