]> git.sesse.net Git - kdenlive/blob - src/kis_curve_widget_p.h
- Limit number of points in curve widget as they are limited in frei0r
[kdenlive] / src / kis_curve_widget_p.h
1 /*
2  *  Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
3  *  Copyright (c) 2009 Dmitry Kazakov <dimula73@gmail.com>
4  *
5  *  This program 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.
9  *
10  *  This program 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.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19 #ifndef _KIS_CURVE_WIDGET_P_H_
20 #define _KIS_CURVE_WIDGET_P_H_
21 #include "kis_cubic_curve.h"
22
23 enum enumState {
24     ST_NORMAL,
25     ST_DRAG
26 };
27
28 /**
29  * Private members for KisCurveWidget class
30  */
31 class KisCurveWidget::Private
32 {
33
34     KisCurveWidget *m_curveWidget;
35
36
37 public:
38     Private(KisCurveWidget *parent);
39     virtual ~Private();
40
41     /* Dragging variables */
42     int m_grab_point_index;
43     double m_grabOffsetX;
44     double m_grabOffsetY;
45     double m_grabOriginalX;
46     double m_grabOriginalY;
47     QPointF m_draggedAwayPoint;
48     int m_draggedAwayPointIndex;
49
50     bool m_readOnlyMode;
51     bool m_guideVisible;
52     QColor m_colorGuide;
53
54
55     /* The curve itself */
56     bool    m_splineDirty;
57     KisCubicCurve m_curve;
58
59     QPixmap m_pix;
60     bool m_pixmapDirty;
61     QPixmap *m_pixmapCache;
62
63     /* In/Out controls */
64     QSpinBox *m_intIn;
65     QSpinBox *m_intOut;
66
67     /* Working range of them */
68     int m_inOutMin;
69     int m_inOutMax;
70
71     /**
72      * State functions.
73      * At the moment used only for dragging.
74      */
75     enumState m_state;
76
77     inline void setState(enumState st);
78     inline enumState state() const;
79
80     int m_maxPoints;
81
82     /*** Internal routins ***/
83
84     /**
85      * Common update routins
86      */
87     void setCurveModified();
88     void setCurveRepaint();
89
90
91     /**
92      * Convert working range of
93      * In/Out controls to normalized
94      * range of spline (and reverse)
95      */
96     double io2sp(int x);
97     int sp2io(double x);
98
99
100     /**
101      * Check whether newly created/moved point @pt doesn't overlap
102      * with any of existing ones from @m_points and adjusts its coordinates.
103      * @skipIndex is the index of the point, that shouldn't be taken
104      * into account during the search
105      * (e.g. beacuse it's @pt itself)
106      *
107      * Returns false in case the point can't be placed anywhere
108      * without overlapping
109      */
110     bool jumpOverExistingPoints(QPointF &pt, int skipIndex);
111
112
113     /**
114      * Synchronize In/Out spinboxes with the curve
115      */
116     void syncIOControls();
117
118     /**
119      * Find the nearest point to @pt from m_points
120      */
121     int nearestPointInRange(QPointF pt, int wWidth, int wHeight) const;
122
123     /**
124      * Nothing to be said! =)
125      */
126     inline
127     void drawGrid(QPainter &p, int wWidth, int wHeight);
128
129 };
130
131 KisCurveWidget::Private::Private(KisCurveWidget *parent)
132 {
133     m_curveWidget = parent;
134 }
135
136 KisCurveWidget::Private::~Private()
137 {
138 }
139
140
141 double KisCurveWidget::Private::io2sp(int x)
142 {
143     int rangeLen = m_inOutMax - m_inOutMin;
144     return double(x - m_inOutMin) / rangeLen;
145 }
146
147 int KisCurveWidget::Private::sp2io(double x)
148 {
149     int rangeLen = m_inOutMax - m_inOutMin;
150     return int(x*rangeLen + 0.5) + m_inOutMin;
151 }
152
153
154 bool KisCurveWidget::Private::jumpOverExistingPoints(QPointF &pt, int skipIndex)
155 {
156     foreach(const QPointF &it, m_curve.points()) {
157         if (m_curve.points().indexOf(it) == skipIndex)
158             continue;
159         if (fabs(it.x() - pt.x()) < POINT_AREA)
160             pt.rx() = pt.x() >= it.x() ?
161                       it.x() + POINT_AREA : it.x() - POINT_AREA;
162     }
163     return (pt.x() >= 0 && pt.x() <= 1.);
164 }
165
166 int KisCurveWidget::Private::nearestPointInRange(QPointF pt, int wWidth, int wHeight) const
167 {
168     double nearestDistanceSquared = 1000;
169     int nearestIndex = -1;
170     int i = 0;
171
172     foreach(const QPointF & point, m_curve.points()) {
173         double distanceSquared = (pt.x() - point.x()) *
174                                  (pt.x() - point.x()) +
175                                  (pt.y() - point.y()) *
176                                  (pt.y() - point.y());
177
178         if (distanceSquared < nearestDistanceSquared) {
179             nearestIndex = i;
180             nearestDistanceSquared = distanceSquared;
181         }
182         ++i;
183     }
184
185     if (nearestIndex >= 0) {
186         if (fabs(pt.x() - m_curve.points()[nearestIndex].x()) *(wWidth - 1) < 5 &&
187                 fabs(pt.y() - m_curve.points()[nearestIndex].y()) *(wHeight - 1) < 5) {
188             return nearestIndex;
189         }
190     }
191
192     return -1;
193 }
194
195
196 #define div2_round(x) (((x)+1)>>1)
197 #define div4_round(x) (((x)+2)>>2)
198
199 void KisCurveWidget::Private::drawGrid(QPainter &p, int wWidth, int wHeight)
200 {
201     /**
202      * Hint: widget size should conform
203      * formula 4n+5 to draw grid correctly
204      * without curious shifts between
205      * spline and it caused by rounding
206      *
207      * That is not mandatory but desirable
208      */
209
210     p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
211     p.drawLine(div4_round(wWidth), 0, div4_round(wWidth), wHeight);
212     p.drawLine(div2_round(wWidth), 0, div2_round(wWidth), wHeight);
213     p.drawLine(div4_round(3*wWidth), 0, div4_round(3*wWidth), wHeight);
214
215     p.drawLine(0, div4_round(wHeight), wWidth, div4_round(wHeight));
216     p.drawLine(0, div2_round(wHeight), wWidth, div2_round(wHeight));
217     p.drawLine(0, div4_round(3*wHeight), wWidth, div4_round(3*wHeight));
218
219 }
220
221 void KisCurveWidget::Private::syncIOControls()
222 {
223     if (!m_intIn || !m_intOut)
224         return;
225
226     bool somethingSelected = (m_grab_point_index >= 0);
227
228     m_intIn->setEnabled(somethingSelected);
229     m_intOut->setEnabled(somethingSelected);
230
231     if (m_grab_point_index >= 0) {
232         m_intIn->blockSignals(true);
233         m_intOut->blockSignals(true);
234
235         m_intIn->setValue(sp2io(m_curve.points()[m_grab_point_index].x()));
236         m_intOut->setValue(sp2io(m_curve.points()[m_grab_point_index].y()));
237
238         m_intIn->blockSignals(false);
239         m_intOut->blockSignals(false);
240     } else {
241         /*FIXME: Ideally, these controls should hide away now */
242     }
243 }
244
245 void KisCurveWidget::Private::setCurveModified()
246 {
247     syncIOControls();
248     m_splineDirty = true;
249     m_curveWidget->update();
250     m_curveWidget->emit modified();
251 }
252
253 void KisCurveWidget::Private::setCurveRepaint()
254 {
255     m_curveWidget->update();
256 }
257
258 void KisCurveWidget::Private::setState(enumState st)
259 {
260     m_state = st;
261 }
262
263
264 enumState KisCurveWidget::Private::state() const
265 {
266     return m_state;
267 }
268
269
270 #endif /* _KIS_CURVE_WIDGET_P_H_ */