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