00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <cmath>
00024 #include <cstdlib>
00025
00026
00027
00028 #include <QPixmap>
00029 #include <QPainter>
00030 #include <QPoint>
00031 #include <QPen>
00032 #include <QEvent>
00033 #include <QRect>
00034 #include <QFont>
00035 #include <QFontMetrics>
00036 #include <QMouseEvent>
00037 #include <QKeyEvent>
00038 #include <QPaintEvent>
00039 #include <QList>
00040
00041 #include <QSpinBox>
00042
00043
00044
00045 #include <kis_debug.h>
00046 #include <kcursor.h>
00047 #include <klocale.h>
00048
00049
00050
00051 #include "widgets/kis_curve_widget.h"
00052
00053
00054 #define bounds(x,a,b) (x<a ? a : (x>b ? b :x))
00055 #define MOUSE_AWAY_THRES 15
00056 #define POINT_AREA 1E-4
00057 #define CURVE_AREA 1E-4
00058
00059 #include "kis_curve_widget_p.h"
00060
00061
00062 static
00063 bool pointLessThan(const QPointF &a, const QPointF &b);
00064
00065
00066 KisCurveWidget::KisCurveWidget(QWidget *parent, Qt::WFlags f)
00067 : QWidget(parent, f), d(new KisCurveWidget::Private(this))
00068 {
00069 setObjectName("KisCurveWidget");
00070 d->m_grab_point_index = -1;
00071 d->m_readOnlyMode = false;
00072 d->m_guideVisible = false;
00073 d->m_pixmapDirty = true;
00074 d->m_pixmapCache = NULL;
00075 d->setState(ST_NORMAL);
00076
00077 d->m_intIn = NULL;
00078 d->m_intOut = NULL;
00079
00080 setMouseTracking(true);
00081 setAutoFillBackground(false);
00082 setAttribute(Qt::WA_OpaquePaintEvent);
00083 setMinimumSize(150, 50);
00084 setMaximumSize(250, 250);
00085 QPointF p;
00086 p.rx() = 0.0; p.ry() = 0.0;
00087 d->m_points.append(p);
00088 p.rx() = 1.0; p.ry() = 1.0;
00089 d->m_points.append(p);
00090
00091 d->setCurveModified();
00092
00093 setFocusPolicy(Qt::StrongFocus);
00094 }
00095
00096 KisCurveWidget::~KisCurveWidget()
00097 {
00098 if (d->m_pixmapCache)
00099 delete d->m_pixmapCache;
00100 delete d;
00101 }
00102
00103 void KisCurveWidget::setupInOutControls(QSpinBox *in, QSpinBox *out, int min, int max)
00104 {
00105 d->m_intIn = in;
00106 d->m_intOut = out;
00107
00108 if (!d->m_intIn || !d->m_intOut)
00109 return;
00110
00111 d->m_inOutMin = min;
00112 d->m_inOutMax = max;
00113
00114 d->m_intIn->setRange(d->m_inOutMin, d->m_inOutMax);
00115 d->m_intOut->setRange(d->m_inOutMin, d->m_inOutMax);
00116
00117
00118 connect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
00119 connect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
00120 d->syncIOControls();
00121
00122 }
00123 void KisCurveWidget::dropInOutControls()
00124 {
00125 if (!d->m_intIn || !d->m_intOut)
00126 return;
00127
00128 disconnect(d->m_intIn, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
00129 disconnect(d->m_intOut, SIGNAL(valueChanged(int)), this, SLOT(inOutChanged(int)));
00130
00131 d->m_intIn = d->m_intOut = NULL;
00132
00133 }
00134
00135 void KisCurveWidget::inOutChanged(int)
00136 {
00137 QPointF pt;
00138
00139 Q_ASSERT(d->m_grab_point_index >= 0);
00140
00141 pt.rx() = d->io2sp(d->m_intIn->value());
00142 pt.ry() = d->io2sp(d->m_intOut->value());
00143
00144 if (d->jumpOverExistingPoints(pt, d->m_grab_point_index)) {
00145 d->m_points[d->m_grab_point_index] = pt;
00146 qSort(d->m_points.begin(), d->m_points.end(), pointLessThan);
00147 d->m_grab_point_index = d->m_points.indexOf(pt);
00148 } else
00149 pt = d->m_points[d->m_grab_point_index];
00150
00151
00152 d->m_intIn->blockSignals(true);
00153 d->m_intOut->blockSignals(true);
00154
00155 d->m_intIn->setValue(d->sp2io(pt.rx()));
00156 d->m_intOut->setValue(d->sp2io(pt.ry()));
00157
00158 d->m_intIn->blockSignals(false);
00159 d->m_intOut->blockSignals(false);
00160
00161 d->setCurveModified();
00162 }
00163
00164
00165 void KisCurveWidget::reset(void)
00166 {
00167 d->m_grab_point_index = -1;
00168 d->m_guideVisible = false;
00169
00170 d->setCurveModified();
00171 }
00172
00173 void KisCurveWidget::setCurveGuide(const QColor & color)
00174 {
00175 d->m_guideVisible = true;
00176 d->m_colorGuide = color;
00177
00178 }
00179
00180 void KisCurveWidget::setPixmap(const QPixmap & pix)
00181 {
00182 d->m_pix = pix;
00183 d->m_pixmapDirty = true;
00184 d->setCurveRepaint();
00185 }
00186
00187 void KisCurveWidget::keyPressEvent(QKeyEvent *e)
00188 {
00189 if (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace) {
00190 if (d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_points.count() - 1) {
00191
00192 double grab_point_x = d->m_points[d->m_grab_point_index].rx();
00193
00194 int left_of_grab_point_index = d->m_grab_point_index - 1;
00195 int right_of_grab_point_index = d->m_grab_point_index + 1;
00196 int new_grab_point_index;
00197
00198 if (fabs(d->m_points[left_of_grab_point_index].rx() - grab_point_x) <
00199 fabs(d->m_points[right_of_grab_point_index].rx() - grab_point_x)) {
00200 new_grab_point_index = left_of_grab_point_index;
00201 } else {
00202 new_grab_point_index = d->m_grab_point_index;
00203 }
00204 d->m_points.removeAt(d->m_grab_point_index);
00205 d->m_grab_point_index = new_grab_point_index;
00206 setCursor(Qt::ArrowCursor);
00207 d->setState(ST_NORMAL);
00208 }
00209 d->setCurveModified();
00210 } else if (e->key() == Qt::Key_Escape && d->state() != ST_NORMAL) {
00211 d->m_points[d->m_grab_point_index].rx() = d->m_grabOriginalX;
00212 d->m_points[d->m_grab_point_index].ry() = d->m_grabOriginalY;
00213 setCursor(Qt::ArrowCursor);
00214 d->setState(ST_NORMAL);
00215
00216 d->setCurveModified();
00217 } else if ((e->key() == Qt::Key_A || e->key() == Qt::Key_Insert) && d->state() == ST_NORMAL) {
00218
00219 addPointInTheMiddle();
00220 } else
00221 QWidget::keyPressEvent(e);
00222 }
00223
00224 void KisCurveWidget::addPointInTheMiddle()
00225 {
00226 QPointF pt;
00227 pt.rx() = 0.5;
00228 pt.ry() = getCurveValue(pt.rx());
00229
00230 if (!d->jumpOverExistingPoints(pt, -1))
00231 return;
00232
00233 d->m_points.append(pt);
00234 qSort(d->m_points.begin(), d->m_points.end(), pointLessThan);
00235 d->m_grab_point_index = d->m_points.indexOf(pt);
00236
00237 if (d->m_intIn)
00238 d->m_intIn->setFocus(Qt::TabFocusReason);
00239 d->setCurveModified();
00240 }
00241
00242 void KisCurveWidget::resizeEvent(QResizeEvent *e)
00243 {
00244 d->m_pixmapDirty = true;
00245 QWidget::resizeEvent(e);
00246 }
00247
00248 void KisCurveWidget::paintEvent(QPaintEvent *)
00249 {
00250 int wWidth = width() - 1;
00251 int wHeight = height() - 1;
00252
00253 QPainter p(this);
00254
00255
00256
00257
00258
00259
00260
00261
00262 if (!d->m_pix.isNull()) {
00263 if (d->m_pixmapDirty || !d->m_pixmapCache) {
00264 if (d->m_pixmapCache)
00265 delete d->m_pixmapCache;
00266 d->m_pixmapCache = new QPixmap(width(), height());
00267 QPainter cachePainter(d->m_pixmapCache);
00268
00269 cachePainter.scale(1.0*width() / d->m_pix.width(), 1.0*height() / d->m_pix.height());
00270 cachePainter.drawPixmap(0, 0, d->m_pix);
00271 d->m_pixmapDirty = false;
00272 }
00273 p.drawPixmap(0, 0, *d->m_pixmapCache);
00274 } else
00275 p.fillRect(rect(), palette().background());
00276
00277
00278 d->drawGrid(p, wWidth, wHeight);
00279
00280
00281
00282 double prevY = wHeight - getCurveValue(0.) * wHeight;
00283 double prevX = 0.;
00284 double curY;
00285 double normalizedX;
00286 int x;
00287
00288 p.setPen(QPen(Qt::black, 1, Qt::SolidLine));
00289 for (x = 0 ; x < wWidth ; x++) {
00290 normalizedX = double(x) / wWidth;
00291 curY = wHeight - getCurveValue(normalizedX) * wHeight;
00292
00298 p.drawLine(QLineF(prevX, prevY,
00299 x, curY));
00300 prevX = x;
00301 prevY = curY;
00302 }
00303 p.drawLine(QLineF(prevX, prevY ,
00304 x, wHeight - getCurveValue(1.0) * wHeight));
00305
00306
00307 double curveX;
00308 double curveY;
00309 if (!d->m_readOnlyMode) {
00310 for (int i = 0; i < d->m_points.count(); ++i) {
00311 curveX = d->m_points.at(i).x();
00312 curveY = d->m_points.at(i).y();
00313
00314 if (i == d->m_grab_point_index) {
00315 p.setPen(QPen::QPen(Qt::red, 3, Qt::SolidLine));
00316 p.drawEllipse(QRectF(curveX * wWidth - 2,
00317 wHeight - 2 - curveY * wHeight, 4, 4));
00318 } else {
00319 p.setPen(QPen::QPen(Qt::red, 1, Qt::SolidLine));
00320 p.drawEllipse(QRectF(curveX * wWidth - 3,
00321 wHeight - 3 - curveY * wHeight, 6, 6));
00322 }
00323 }
00324 }
00325 }
00326
00327 static bool pointLessThan(const QPointF &a, const QPointF &b)
00328 {
00329 return a.x() < b.x();
00330 }
00331
00332 void KisCurveWidget::mousePressEvent(QMouseEvent * e)
00333 {
00334 if (d->m_readOnlyMode) return;
00335
00336 if (e->button() != Qt::LeftButton)
00337 return;
00338
00339 double x = e->pos().x() / (double)(width() - 1);
00340 double y = 1.0 - e->pos().y() / (double)(height() - 1);
00341
00342
00343
00344 int closest_point_index = d->nearestPointInRange(QPointF(x, y), width(), height());
00345 if (closest_point_index < 0) {
00346 QPointF newPoint(x, y);
00347 if (!d->jumpOverExistingPoints(newPoint, -1))
00348 return;
00349 d->m_points.append(newPoint);
00350 qSort(d->m_points.begin(), d->m_points.end(), pointLessThan);
00351 d->m_grab_point_index = d->m_points.indexOf(newPoint);
00352 } else {
00353 d->m_grab_point_index = closest_point_index;
00354 }
00355
00356 d->m_grabOriginalX = d->m_points[d->m_grab_point_index].x();
00357 d->m_grabOriginalY = d->m_points[d->m_grab_point_index].y();
00358 d->m_grabOffsetX = d->m_points[d->m_grab_point_index].x() - x;
00359 d->m_grabOffsetY = d->m_points[d->m_grab_point_index].y() - y;
00360 d->m_points[d->m_grab_point_index].rx() = x + d->m_grabOffsetX;
00361 d->m_points[d->m_grab_point_index].ry() = y + d->m_grabOffsetY;
00362
00363 d->m_draggedAwayPointIndex = -1;
00364 d->setState(ST_DRAG);
00365
00366
00367 d->setCurveModified();
00368 }
00369
00370
00371 void KisCurveWidget::mouseReleaseEvent(QMouseEvent *e)
00372 {
00373 if (d->m_readOnlyMode) return;
00374
00375 if (e->button() != Qt::LeftButton)
00376 return;
00377
00378 setCursor(Qt::ArrowCursor);
00379 d->setState(ST_NORMAL);
00380
00381 d->setCurveModified();
00382 }
00383
00384
00385 void KisCurveWidget::mouseMoveEvent(QMouseEvent * e)
00386 {
00387 if (d->m_readOnlyMode) return;
00388
00389 double x = e->pos().x() / (double)(width() - 1);
00390 double y = 1.0 - e->pos().y() / (double)(height() - 1);
00391
00392 if (d->state() == ST_NORMAL) {
00393 int nearestPointIndex = d->nearestPointInRange(QPointF(x, y), width(), height());
00394
00395 if (nearestPointIndex < 0)
00396 setCursor(Qt::ArrowCursor);
00397 else
00398 setCursor(Qt::CrossCursor);
00399 } else {
00400 bool crossedHoriz = e->pos().x() - width() > MOUSE_AWAY_THRES ||
00401 e->pos().x() < -MOUSE_AWAY_THRES;
00402 bool crossedVert = e->pos().y() - height() > MOUSE_AWAY_THRES ||
00403 e->pos().y() < -MOUSE_AWAY_THRES;
00404
00405 bool removePoint = (crossedHoriz || crossedVert);
00406
00407 if (!removePoint && d->m_draggedAwayPointIndex >= 0) {
00408
00409 QPointF newPoint(d->m_draggedAwayPoint);
00410 d->m_points.insert(d->m_draggedAwayPointIndex, newPoint);
00411 d->m_grab_point_index = d->m_draggedAwayPointIndex;
00412 d->m_draggedAwayPointIndex = -1;
00413 }
00414
00415 if (removePoint &&
00416 (d->m_draggedAwayPointIndex >= 0))
00417 return;
00418
00419
00420 setCursor(Qt::CrossCursor);
00421
00422 x += d->m_grabOffsetX;
00423 y += d->m_grabOffsetY;
00424
00425 double leftX;
00426 double rightX;
00427 if (d->m_grab_point_index == 0) {
00428 leftX = 0.0;
00429 if (d->m_points.count() > 1)
00430 rightX = d->m_points[d->m_grab_point_index + 1].rx() - POINT_AREA;
00431 else
00432 rightX = 1.0;
00433 } else if (d->m_grab_point_index == d->m_points.count() - 1) {
00434 leftX = d->m_points[d->m_grab_point_index - 1].rx() + POINT_AREA;
00435 rightX = 1.0;
00436 } else {
00437 Q_ASSERT(d->m_grab_point_index > 0 && d->m_grab_point_index < d->m_points.count() - 1);
00438
00439
00440 leftX = d->m_points[d->m_grab_point_index - 1].rx() + POINT_AREA;
00441 rightX = d->m_points[d->m_grab_point_index + 1].rx() - POINT_AREA;
00442 }
00443
00444 x = bounds(x, leftX, rightX);
00445 y = bounds(y, 0., 1.);
00446
00447 d->m_points[d->m_grab_point_index].rx() = x;
00448 d->m_points[d->m_grab_point_index].ry() = y;
00449
00450
00451 if (removePoint && d->m_points.count() > 2) {
00452 d->m_draggedAwayPoint.rx() = d->m_points[d->m_grab_point_index].rx();
00453 d->m_draggedAwayPoint.ry() = d->m_points[d->m_grab_point_index].ry();
00454 d->m_draggedAwayPointIndex = d->m_grab_point_index;
00455 d->m_points.removeAt(d->m_grab_point_index);
00456 d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_points.count() - 1);
00457 }
00458
00459 d->setCurveModified();
00460 }
00461 }
00462
00463 double KisCurveWidget::getCurveValue(double x)
00464 {
00465 if (d->m_splineDirty) {
00466 d->m_spline.createSpline(QVector<QPointF>::fromList(d->m_points));
00467 d->m_splineDirty = false;
00468 }
00469 return Private::checkBounds(d->m_spline, x);
00470 }
00471
00472 double KisCurveWidget::getCurveValue(const QList<QPointF>& curve, double x)
00473 {
00474 KisCubicSpline<QPointF, double> spline(QVector<QPointF>::fromList(curve));
00475 return Private::checkBounds(spline, x);
00476 }
00477
00478 QList<QPointF> KisCurveWidget::getCurve()
00479 {
00480 return d->m_points;
00481 }
00482
00483 void KisCurveWidget::setCurve(QList<QPointF >inlist)
00484 {
00485 d->m_points = inlist;
00486 d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_points.count() - 1);
00487 d->setCurveModified();
00488 }
00489
00490 void KisCurveWidget::leaveEvent(QEvent *)
00491 {
00492 }
00493
00494 #include "kis_curve_widget.moc"