SeExpr
ExprColorCurve.cpp
Go to the documentation of this file.
1/*
2* Copyright Disney Enterprises, Inc. All rights reserved.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License
6* and the following modification to it: Section 6 Trademarks.
7* deleted and replaced with:
8*
9* 6. Trademarks. This License does not grant permission to use the
10* trade names, trademarks, service marks, or product names of the
11* Licensor and its affiliates, except as required for reproducing
12* the content of the NOTICE file.
13*
14* You may obtain a copy of the License at
15* http://www.apache.org/licenses/LICENSE-2.0
16*
17* @file ExprColorCurveUI.cpp
18* @brief Contains PyQt4 Ramp Widget to emulate Maya's ramp widget
19* @author Arthur Shek
20* @version ashek 05/04/09 Initial Version
21*/
22#include <iostream>
23#include <algorithm>
24
25#include <QColorDialog>
26#include <QDoubleValidator>
27#include <QGraphicsSceneMouseEvent>
28#include <QHBoxLayout>
29#include <QLabel>
30#include <QVBoxLayout>
31#include <QResizeEvent>
32#include <QPushButton>
33#include <QDialogButtonBox>
34#include <QMenu>
35
37#ifdef SEEXPR_USE_QDGUI
38#include <qdgui/QdColorPickerDialog.h>
39#endif
40
41#include "ExprColorCurve.h"
42
44 : _curve(new T_CURVE), _width(320), _height(170), _color(SeExpr2::Vec3d(.5)), _interp(T_CURVE::kMonotoneSpline),
45 _selectedItem(-1), _pixmapDirty(true), _baseRectW(0), _baseRect(0), _lmb(false) {
48}
49
51
52void CCurveScene::resize(const int width, const int height) {
53 // width and height already have the 8 px padding factored in
54 _width = width - 16;
55 _height = height - 16;
56 setSceneRect(-9, -2, width, height);
57 drawRect();
58 drawPoints();
59 _pixmap = QPixmap(_width, _height);
60 _pixmapDirty = true;
61}
62
64 delete _curve;
65 _curve = new T_CURVE;
66 for (unsigned int i = 0; i < _cvs.size(); i++) _curve->addPoint(_cvs[i]._pos, _cvs[i]._val, _cvs[i]._interp);
68}
69
70void CCurveScene::addPoint(double x, const SeExpr2::Vec3d y, const T_INTERP interp, const bool select) {
71 x = SeExpr2::clamp(x, 0, 1);
72
73 _cvs.push_back(T_CURVE::CV(x, y, T_INTERP(interp)));
74 int newIndex = _cvs.size() - 1;
75
77
78 if (select) {
79 _selectedItem = newIndex;
80 emit cvSelected(x, y, interp);
81 }
82 _pixmapDirty = true;
83 _baseRectW->update();
84 drawPoints();
85}
86
88 _cvs.erase(_cvs.begin() + index);
89 _selectedItem = -1;
91
92 _pixmapDirty = true;
93 _baseRectW->update();
94 drawPoints();
96}
97
98void CCurveScene::removeAll() { _cvs.clear(); }
99
100void CCurveScene::keyPressEvent(QKeyEvent *event) {
101 if (((event->key() == Qt::Key_Backspace) || (event->key() == Qt::Key_Delete)) && (_selectedItem >= 0)) {
102 // user hit delete with cv selected
104 }
105}
106
107void CCurveScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
108 _lmb = true;
109 QPointF pos = mouseEvent->scenePos();
110 // get items under mouse click
111 QList<QGraphicsItem *> itemList = items(pos);
112 if (itemList.empty()) {
113 _selectedItem = -1;
114 emit cvSelected(-1, SeExpr2::Vec3d(0.0), _interp);
115 drawPoints();
116 } else if (itemList[0]->zValue() == 2) {
117 // getting here means we've selected a current point
118 const int numCircle = _circleObjects.size();
119 for (int i = 0; i < numCircle; i++) {
120 QGraphicsItem *obj = _circleObjects[i];
121 if (obj == itemList[0]) {
122 _selectedItem = i;
123 _color = _cvs[i]._val;
124 _interp = _cvs[i]._interp;
125 emit cvSelected(_cvs[i]._pos, _cvs[i]._val, _cvs[i]._interp);
126 }
127 }
128 drawPoints();
129 } else {
130 if (mouseEvent->buttons() == Qt::LeftButton) {
131 // getting here means we want to create a new point
132 double myx = pos.x() / _width;
133 T_INTERP interpFromNearby = _curve->getLowerBoundCV(SeExpr2::clamp(myx, 0, 1))._interp;
134 if (interpFromNearby == T_CURVE::kNone) interpFromNearby = T_CURVE::kMonotoneSpline;
135 addPoint(myx, _curve->getValue(myx), interpFromNearby);
137 } else {
138 _selectedItem = -1;
139 drawPoints();
140 }
141 }
142}
143
144void CCurveScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) {
145 if (_lmb) {
146 QPointF point = mouseEvent->scenePos();
147 if (_selectedItem >= 0) {
148 // clamp motion to inside curve area
149 double pos = SeExpr2::clamp(point.x() / _width, 0, 1);
150 _cvs[_selectedItem]._pos = pos;
151 rebuildCurve();
152 _pixmapDirty = true;
153 _baseRectW->update();
155 drawPoints();
157 }
158 }
159}
160
161void CCurveScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
162 if (_selectedItem >= 0) {
163 QMenu *menu = new QMenu(event->widget());
164 QAction *deleteAction = menu->addAction("Delete Point");
165 QAction *action = menu->exec(event->screenPos());
166 if (action == deleteAction) removePoint(_selectedItem);
167 }
168}
169
170void CCurveScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {
171 Q_UNUSED(mouseEvent);
172 _lmb = false;
173}
174
175// user selected a different interpolation type, redraw
176void CCurveScene::interpChanged(const int interp) {
177 _interp = (T_INTERP)interp;
178 if (_selectedItem >= 0) {
179 _cvs[_selectedItem]._interp = _interp;
180 rebuildCurve();
181 _pixmapDirty = true;
182 _baseRectW->update();
184 }
185}
186
187// user entered a different point position, redraw
189 if (_selectedItem >= 0) {
190 pos = SeExpr2::clamp(pos, 0, 1);
191 _cvs[_selectedItem]._pos = pos;
192 rebuildCurve();
193 _pixmapDirty = true;
194 _baseRectW->update();
195 drawPoints();
197 }
198}
199
200// user entered a different point value, redraw
202 _color = val;
203 if (_selectedItem >= 0) {
204 _cvs[_selectedItem]._val = val;
205 rebuildCurve();
206 _pixmapDirty = true;
207 _baseRectW->update();
208 drawPoints();
210 }
211}
212
213// return points in reverse order in order to use same parsing in editor
215
217 if (_pixmapDirty) {
218 QByteArray buf;
219 buf.append(QString("P6\n%1 %2\n255\n").arg(_width).arg(_height));
220 buf.append(getCPixmap());
221 _pixmap.loadFromData(buf, "PPM");
222 _pixmapDirty = false;
223 }
224 return _pixmap;
225}
226
228 // create pixmap, set to gray
229 const int len = 3 * _width * _height;
230 QByteArray pixmap(len, 127);
231
232 double paramInc = 1.0 / (_width - 2);
233 double param = 0.5 * paramInc; // start at pixel center
234 // add black lines to left
235 char *ptr = pixmap.data();
236 *ptr++ = 0;
237 *ptr++ = 0;
238 *ptr++ = 0;
239 for (int i = 1; i < _width - 1; i++) {
240 SeExpr2::Vec3d color = _curve->getValue(param);
241 *ptr++ = char(std::min(std::max(0.0, 255 * color[0]), 255.0) + 0.5);
242 *ptr++ = char(std::min(std::max(0.0, 255 * color[1]), 255.0) + 0.5);
243 *ptr++ = char(std::min(std::max(0.0, 255 * color[2]), 255.0) + 0.5);
244 param += paramInc;
245 }
246 // add black lines to right
247 *ptr++ = 0;
248 *ptr++ = 0;
249 *ptr++ = 0;
250
251 for (int i = 1; i < _height - 1; i++) {
252 memcpy(pixmap.data() + (i * _width * 3), pixmap.data() + ((i - 1) * _width * 3), _width * 3);
253 }
254
255 // add black lines to top and bottom
256 memset(pixmap.data(), 0, _width * 3);
257 memset(pixmap.data() + ((_height - 1) * _width * 3), 0, _width * 3);
258
259 return pixmap;
260}
261
262// draws the base gray outline rectangle
264 if (_baseRectW == 0) {
265 _baseRectW = new ExprCBoxWidget(this);
266 }
267 if (_baseRect == 0) {
268 _baseRect = addWidget(_baseRectW);
269 }
270 _baseRect->widget()->setFixedSize(_width, _height);
271 _baseRect->widget()->update();
272 _baseRect->setZValue(0);
273}
274
275// draws the cv points
277 while (_circleObjects.size()) {
278 delete _circleObjects[0];
279 _circleObjects.erase(_circleObjects.begin());
280 }
281 const int numCV = _cvs.size();
282 for (int i = 0; i < numCV; i++) {
283 const T_CURVE::CV &pt = _cvs[i];
284 QPen pen;
285 if (i == _selectedItem) {
286 pen = QPen(QColor(255, 170, 0), 1.0);
287 } else {
288 pen = QPen(Qt::black, 1.0);
289 }
290 _circleObjects.push_back(addEllipse(
291 pt._pos * _width - 4,
292 _height + 3,
293 8,
294 8,
295 pen,
296 QBrush(QColor(int(255 * pt._val[0] + 0.5), int(255 * pt._val[1] + 0.5), int(255 * pt._val[2] + 0.5)))));
297 QGraphicsEllipseItem *circle = _circleObjects.back();
298 circle->setFlag(QGraphicsItem::ItemIsMovable, true);
299 circle->setZValue(2);
300 }
301}
302
303void ExprCBoxWidget::paintEvent(QPaintEvent *event) {
304 Q_UNUSED(event);
305 QPainter p(this);
306 p.drawPixmap(0, 0, _curveScene->getPixmap());
307}
308
309void ExprCSwatchFrame::paintEvent(QPaintEvent *event) {
310 Q_UNUSED(event);
311 QPainter p(this);
312 p.fillRect(contentsRect(), _color);
313}
314
315ExprCSwatchFrame::ExprCSwatchFrame(SeExpr2::Vec3d value, QWidget *parent) : QFrame(parent), _value(value) {
316 _color = QColor(int(255 * _value[0] + 0.5), int(255 * _value[1] + 0.5), int(255 * _value[2] + 0.5));
317}
318
320 _color = QColor(int(255 * value[0] + 0.5), int(255 * value[1] + 0.5), int(255 * value[2] + 0.5));
321 // setPalette(QPalette(_color));
322 _value = value;
323 repaint();
324}
325
327
328void ExprCSwatchFrame::mousePressEvent(QMouseEvent *event) {
329 Q_UNUSED(event);
330#ifdef SEEXPR_USE_QDGUI
331 QColor color = QdColorPickerDialog::chooseColorFromDialog(_color, this);
332#else
333 QColor color = QColorDialog::getColor(_color);
334#endif
335 if (color.isValid()) {
336 _value[0] = color.red() / 255.0;
337 _value[1] = color.green() / 255.0;
338 _value[2] = color.blue() / 255.0;
339 setPalette(QPalette(color));
340 _color = color;
342 emit swatchChanged(color);
343 }
344}
345
346ExprColorCurve::ExprColorCurve(QWidget *parent, QString pLabel, QString vLabel, QString iLabel, bool expandable)
347 : QWidget(parent), _scene(0), _selPosEdit(0), _selValEdit(0), _interpComboBox(0) {
348 Q_UNUSED(iLabel);
349 QHBoxLayout *mainLayout = new QHBoxLayout();
350 mainLayout->setSpacing(2);
351 mainLayout->setMargin(5);
352
353 QWidget *edits = new QWidget;
354 QVBoxLayout *editsLayout = new QVBoxLayout;
355 editsLayout->setAlignment(Qt::AlignTop);
356 editsLayout->setSpacing(0);
357 editsLayout->setMargin(0);
358 edits->setLayout(editsLayout);
359
360 QWidget *selPos = new QWidget;
361 QHBoxLayout *selPosLayout = new QHBoxLayout;
362 selPosLayout->setSpacing(1);
363 selPosLayout->setMargin(1);
364 selPos->setLayout(selPosLayout);
365 _selPosEdit = new QLineEdit;
366 QDoubleValidator *posValidator = new QDoubleValidator(0.0, 1.0, 6, _selPosEdit);
367 _selPosEdit->setValidator(posValidator);
368 _selPosEdit->setFixedWidth(38);
369 _selPosEdit->setFixedHeight(20);
370 selPosLayout->addStretch(50);
371 QLabel *posLabel;
372 if (pLabel.isEmpty()) {
373 posLabel = new QLabel("Selected Position: ");
374 } else {
375 posLabel = new QLabel(pLabel);
376 }
377 selPosLayout->addWidget(posLabel);
378 selPosLayout->addWidget(_selPosEdit);
379
380 QWidget *selVal = new QWidget;
381 QBoxLayout *selValLayout = new QHBoxLayout;
382 selValLayout->setSpacing(1);
383 selValLayout->setMargin(1);
384 selVal->setLayout(selValLayout);
386 _selValEdit->setFixedWidth(38);
387 _selValEdit->setFixedHeight(20);
388 selValLayout->addStretch(50);
389 QLabel *valLabel;
390 if (vLabel.isEmpty()) {
391 valLabel = new QLabel("Selected Color: ");
392 } else {
393 valLabel = new QLabel(vLabel);
394 }
395 selValLayout->addWidget(valLabel);
396 selValLayout->addWidget(_selValEdit);
397
398 _interpComboBox = new QComboBox;
399 _interpComboBox->addItem("None");
400 _interpComboBox->addItem("Linear");
401 _interpComboBox->addItem("Smooth");
402 _interpComboBox->addItem("Spline");
403 _interpComboBox->addItem("MSpline");
404 _interpComboBox->setCurrentIndex(4);
405 _interpComboBox->setFixedWidth(70);
406 _interpComboBox->setFixedHeight(20);
407
408 editsLayout->addWidget(selPos);
409 editsLayout->addWidget(selVal);
410 editsLayout->addWidget(_interpComboBox);
411
412 QFrame *curveFrame = new QFrame;
413 curveFrame->setFrameShape(QFrame::Panel);
414 curveFrame->setFrameShadow(QFrame::Sunken);
415 curveFrame->setLineWidth(1);
416 QHBoxLayout *curveFrameLayout = new QHBoxLayout;
417 curveFrameLayout->setMargin(0);
418 CurveGraphicsView *curveView = new CurveGraphicsView;
419 curveView->setFrameShape(QFrame::Panel);
420 curveView->setFrameShadow(QFrame::Sunken);
421 curveView->setLineWidth(1);
422 curveView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
423 curveView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
424 _scene = new CCurveScene;
425 curveView->setScene(_scene);
426 curveView->setTransform(QTransform().scale(1, -1));
427 curveView->setRenderHints(QPainter::Antialiasing);
428 curveFrameLayout->addWidget(curveView);
429 curveFrame->setLayout(curveFrameLayout);
430
431 mainLayout->addWidget(edits);
432 mainLayout->addWidget(curveFrame);
433 if (expandable) {
434 QPushButton *expandButton = new QPushButton(">");
435 expandButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
436 expandButton->setFixedWidth(15);
437 mainLayout->addWidget(expandButton);
438 // open a the detail widget when clicked
439 connect(expandButton, SIGNAL(clicked()), this, SLOT(openDetail()));
440 }
441 mainLayout->setStretchFactor(curveFrame, 100);
442 setLayout(mainLayout);
443
444 // SIGNALS
445
446 // when a user selects a cv, update the fields on left
447 connect(_scene,
448 SIGNAL(cvSelected(double, SeExpr2::Vec3d, T_INTERP)),
449 this,
450 SLOT(cvSelectedSlot(double, SeExpr2::Vec3d, T_INTERP)));
451 // when a user selects a different interp, the curve has to redraw
452 connect(_interpComboBox, SIGNAL(activated(int)), _scene, SLOT(interpChanged(int)));
453 // when a user types a different position, the curve has to redraw
454 connect(_selPosEdit, SIGNAL(returnPressed()), this, SLOT(selPosChanged()));
455 connect(this, SIGNAL(selPosChangedSignal(double)), _scene, SLOT(selPosChanged(double)));
456 // when a user selects a different color, the ramp has to redraw
457 connect(_selValEdit,
459 _scene,
460 SLOT(selValChanged(SeExpr2::Vec3d)));
461 connect(_selValEdit, SIGNAL(swatchChanged(QColor)), this, SLOT(internalSwatchChanged(QColor)));
462 // when the widget is resized, resize the curve widget
463 connect(curveView, SIGNAL(resizeSignal(int, int)), _scene, SLOT(resize(int, int)));
464}
465
466// CV selected, update the user interface fields.
467void ExprColorCurve::cvSelectedSlot(const double pos, const SeExpr2::Vec3d val, const T_INTERP interp) {
468 QString posStr;
469 if (pos >= 0.0) {
470 posStr.setNum(pos, 'f', 3);
471 _selPosEdit->setText(posStr);
472 _selValEdit->setValue(val);
473 emit swatchChanged(QColor::fromRgbF(val[0], val[1], val[2], 1));
474 _interpComboBox->setCurrentIndex(interp);
475 }
476}
477
478// User entered new position, round and send signal to redraw curve.
480 double pos = SeExpr2::clamp(QString(_selPosEdit->text()).toFloat(), 0, 1);
481 _selPosEdit->setText(QString("%1").arg(pos, 0, 'f', 3));
482 emit selPosChangedSignal(pos);
483}
484
485void ExprColorCurve::addPoint(const double x, const SeExpr2::Vec3d y, const T_INTERP interp, const bool select) {
486 _scene->addPoint(x, y, interp, select);
487}
488
490 SeExpr2::Vec3d newColor(color.redF(), color.greenF(), color.blueF());
491 _scene->selValChanged(newColor);
492 _selValEdit->setValue(newColor);
493}
494
497 return QColor::fromRgbF(val[0], val[1], val[2], 1);
498}
499
500void ExprColorCurve::internalSwatchChanged(QColor color) { emit swatchChanged(color); }
501
503 QDialog *dialog = new QDialog();
504 dialog->setMinimumWidth(1024);
505 dialog->setMinimumHeight(400);
506 ExprColorCurve *curve = new ExprColorCurve(0, "", "", "", false);
507
508 // copy points into new data
509 const std::vector<T_CURVE::CV> &data = _scene->_cvs;
510 typedef std::vector<T_CURVE::CV>::const_iterator ITERATOR;
511 for (ITERATOR i = data.begin(); i != data.end(); ++i) curve->addPoint(i->_pos, i->_val, i->_interp);
512
513 QVBoxLayout *layout = new QVBoxLayout();
514 dialog->setLayout(layout);
515 layout->addWidget(curve);
516
517 dialog->setLayout(layout);
518 layout->addWidget(curve);
519 QDialogButtonBox *buttonbar = new QDialogButtonBox();
520 buttonbar->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
521 connect(buttonbar, SIGNAL(accepted()), dialog, SLOT(accept()));
522 connect(buttonbar, SIGNAL(rejected()), dialog, SLOT(reject()));
523 layout->addWidget(buttonbar);
524
525 if (dialog->exec() == QDialog::Accepted) {
526 // copy points back from child
527 _scene->removeAll();
528 const std::vector<T_CURVE::CV> &dataNew = curve->_scene->_cvs;
529 typedef std::vector<T_CURVE::CV>::const_iterator ITERATOR;
530 for (ITERATOR i = dataNew.begin(); i != dataNew.end(); ++i) addPoint(i->_pos, i->_val, i->_interp);
532 }
533}
static const int p[514]
Definition NoiseTables.h:20
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
QByteArray getCPixmap()
QPixmap _pixmap
QGraphicsProxyWidget * _baseRect
void curveChanged()
SeExpr2::Curve< SeExpr2::Vec3d > T_CURVE
QWidget * _baseRectW
SeExpr2::Vec3d _color
void removePoint(const int index)
QPixmap & getPixmap()
std::vector< QGraphicsEllipseItem * > _circleObjects
void emitCurveChanged()
void selPosChanged(double pos)
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
std::vector< T_CURVE::CV > _cvs
void cvSelected(double x, const SeExpr2::Vec3d y, const T_INTERP interp)
void addPoint(double x, const SeExpr2::Vec3d y, const T_INTERP interp, const bool select=true)
void selValChanged(const SeExpr2::Vec3d &val)
virtual void keyPressEvent(QKeyEvent *event)
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
T_CURVE * _curve
void interpChanged(const int interp)
T_CURVE::InterpType T_INTERP
void resize(const int width, const int height)
T_INTERP _interp
virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
CCurveScene * _curveScene
virtual void paintEvent(QPaintEvent *event)
void selValChangedSignal(SeExpr2::Vec3d value)
void swatchChanged(QColor color)
virtual void paintEvent(QPaintEvent *event)
ExprCSwatchFrame(SeExpr2::Vec3d value, QWidget *parent=0)
SeExpr2::Vec3d _value
SeExpr2::Vec3d getValue() const
void setValue(const SeExpr2::Vec3d &value)
virtual void mousePressEvent(QMouseEvent *event)
void internalSwatchChanged(QColor color)
void addPoint(const double x, const SeExpr2::Vec3d y, const T_INTERP interp, bool select=false)
void swatchChanged(QColor color)
void setSwatchColor(QColor color)
ExprColorCurve(QWidget *parent=0, QString pLabel="", QString vLabel="", QString iLabel="", bool expandable=true)
void selValChangedSignal(SeExpr2::Vec3d val)
CCurveScene * _scene
void cvSelectedSlot(const double pos, const SeExpr2::Vec3d val, const T_INTERP interp)
QComboBox * _interpComboBox
ExprCSwatchFrame * _selValEdit
QLineEdit * _selPosEdit
void selPosChangedSignal(double pos)
Interpolation curve class for double->double and double->Vec3D.
Definition Curve.h:38
CV getLowerBoundCV(const double param) const
Definition Curve.cpp:199
InterpType
Supported interpolation types.
Definition Curve.h:43
@ kMonotoneSpline
Definition Curve.h:48
T getValue(const double param) const
Evaluates curve and returns full value.
Definition Curve.cpp:104
void addPoint(double position, const T &val, InterpType type)
Adds a point to the curve.
Definition Curve.cpp:50
void preparePoints()
Prepares points for evaluation (sorts and computes boundaries, clamps extrema)
Definition Curve.cpp:56
double clamp(double x, double lo, double hi)
InterpType _interp
Definition Curve.h:55
</pre >< h3 > A simple variable reference</h3 > This is not a very interesting subclass of expression until we add some additional variables Variables on some applications may be very dynamic In this we only need x
Definition tutorial.txt:108
This is the same as the prman cellnoise function< br ></div >< br > float< b > float y< br > float< b > float y
Definition userdoc.txt:218
The result is computed int int< br >< div style="margin-left: 40px;"> Picks values randomly between loRange and hiRange based on supplied index(which is automatically hashed). &nbsp
For any rgb or hsl value(except for negative s values)