SeExpr
ExprCurve.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 ExprCurve.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 <QDialog>
26#include <QDoubleValidator>
27#include <QGraphicsSceneMouseEvent>
28#include <QHBoxLayout>
29#include <QLabel>
30#include <QVBoxLayout>
31#include <QPushButton>
32#include <QResizeEvent>
33#include <QDialogButtonBox>
34#include <QMenu>
35
37#include <cfloat>
38
39#include "ExprCurve.h"
40
41void CurveScene::removeAll() { _cvs.clear(); }
42
43void CurveGraphicsView::resizeEvent(QResizeEvent *event) {
44 emit resizeSignal(event->size().width(), event->size().height());
45}
46
48 : _curve(new T_CURVE), _width(320), _height(170), _interp(T_CURVE::kMonotoneSpline), _selectedItem(-1),
49 _curvePoly(0), _baseRect(0), _lmb(false) {
52}
53
55
56void CurveScene::resize(const int width, const int height) {
57 // width and height already have the 8 px padding factored in
58 _width = width - 16;
59 _height = height - 16;
60 setSceneRect(-9, -7, width, height);
61 drawRect();
62 drawPoly();
63 drawPoints();
64}
65
67 delete _curve;
68 _curve = new T_CURVE;
69 for (unsigned int i = 0; i < _cvs.size(); i++) _curve->addPoint(_cvs[i]._pos, _cvs[i]._val, _cvs[i]._interp);
70 _curve->preparePoints();
71}
72
73void CurveScene::addPoint(double x, double y, const T_INTERP interp, const bool select) {
74 x = SeExpr2::clamp(x, 0, 1);
75 y = SeExpr2::clamp(y, 0, 1);
76
77 _cvs.push_back(T_CURVE::CV(x, y, T_INTERP(interp)));
78 int newIndex = _cvs.size() - 1;
79
81
82 if (select) _selectedItem = newIndex;
83 drawPoly();
84 drawPoints();
85}
86
88 _cvs.erase(_cvs.begin() + index);
89 _selectedItem = -1;
91
92 drawPoly();
93 drawPoints();
95}
96
97void CurveScene::keyPressEvent(QKeyEvent *event) {
98 if (((event->key() == Qt::Key_Backspace) || (event->key() == Qt::Key_Delete)) && (_selectedItem >= 0)) {
99 // user hit delete with cv selected
101 }
102}
103
104void CurveScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {
105 _lmb = true;
106 QPointF pos = mouseEvent->scenePos();
107 // get items under mouse click
108 QList<QGraphicsItem *> itemList = items(pos);
109 if (itemList.empty()) {
110 _selectedItem = -1;
111 emit cvSelected(-1, -1, _interp);
112 drawPoints();
113 } else if (itemList[0]->zValue() == 2) {
114 // getting here means we've selected a current point
115 const int numCircle = _circleObjects.size();
116 for (int i = 0; i < numCircle; i++) {
117 QGraphicsItem *obj = _circleObjects[i];
118 if (obj == itemList[0]) {
119 _selectedItem = i;
120 _interp = _cvs[i]._interp;
121 emit cvSelected(_cvs[i]._pos, _cvs[i]._val, _cvs[i]._interp);
122 }
123 }
124 drawPoints();
125 } else {
126 if (mouseEvent->buttons() == Qt::LeftButton) {
127 // getting here means we want to create a new point
128 double myx = pos.x() / _width;
129 T_INTERP interpFromNearby = _curve->getLowerBoundCV(SeExpr2::clamp(myx, 0, 1))._interp;
130 if (interpFromNearby == T_CURVE::kNone) interpFromNearby = T_CURVE::kMonotoneSpline;
131 addPoint(myx, pos.y() / _height, interpFromNearby);
133 } else {
134 _selectedItem = -1;
135 drawPoints();
136 }
137 }
138}
139
140void CurveScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
141 if (_selectedItem >= 0) {
142 QMenu *menu = new QMenu(event->widget());
143 QAction *deleteAction = menu->addAction("Delete Point");
144 // menu->addAction("Cancel");
145 QAction *action = menu->exec(event->screenPos());
146 if (action == deleteAction) removePoint(_selectedItem);
147 }
148}
149
150void CurveScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) {
151 if (_lmb) {
152 QPointF point = mouseEvent->scenePos();
153 if (_selectedItem >= 0) {
154 // clamp motion to inside curve area
155 double pos = SeExpr2::clamp(point.x() / _width, 0, 1);
156 double val = SeExpr2::clamp(point.y() / _height, 0, 1);
157 _cvs[_selectedItem]._pos = pos;
158 _cvs[_selectedItem]._val = val;
159 rebuildCurve();
160 emit cvSelected(pos, val, _cvs[_selectedItem]._interp);
161 drawPoly();
162 drawPoints();
164 }
165 }
166}
167
168void CurveScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {
169 Q_UNUSED(mouseEvent);
170 _lmb = false;
171}
172
173// user selected a different interpolation type, redraw
174void CurveScene::interpChanged(const int interp) {
175 _interp = T_INTERP(interp);
176 if (_selectedItem >= 0) {
177 _cvs[_selectedItem]._interp = _interp;
178 rebuildCurve();
179 drawPoly();
181 }
182}
183
184// user entered a different point position, redraw
185void CurveScene::selPosChanged(double posInput) {
186 if (_selectedItem >= 0) {
187 double pos = SeExpr2::clamp(posInput, 0, 1);
188 _cvs[_selectedItem]._pos = pos;
189 rebuildCurve();
190 drawPoly();
191 drawPoints();
193 }
194}
195
196// user entered a different point value, redraw
198 if (_selectedItem >= 0) {
199 val = SeExpr2::clamp(val, 0, 1);
200 _cvs[_selectedItem]._val = val;
201 rebuildCurve();
202 drawPoly();
203 drawPoints();
205 }
206}
207
208// return points in reverse order in order to use same parsing in editor
210
211// draws the base gray outline rectangle
213 if (_baseRect == 0) {
214 _baseRect = addRect(0, 0, _width, _height, QPen(Qt::black, 1.0), QBrush(Qt::gray));
215 }
216 _baseRect->setRect(0, 0, _width, _height);
217 _baseRect->setZValue(0);
218}
219
220// draws the poly curve representation
222 if (_curvePoly == 0) {
223 _curvePoly = addPolygon(QPolygonF(), QPen(Qt::black, 1.0), QBrush(Qt::darkGray));
224 }
225
226 QPolygonF poly;
227 poly.append(QPointF(_width, 0));
228 poly.append(QPointF(0, 0));
229 for (int i = 0; i < 1000; i++) {
230 double x = i / 1000.0;
231 poly.append(QPointF(_width * x, _height * _curve->getValue(x)));
232 }
233 poly.append(QPointF(_width, 0));
234 _curvePoly->setPolygon(poly);
235 _curvePoly->setZValue(1);
236}
237
238// draws the cv points
240 while (_circleObjects.size()) {
241 delete _circleObjects[0];
242 _circleObjects.erase(_circleObjects.begin());
243 }
244 const int numCV = _cvs.size();
245 for (int i = 0; i < numCV; i++) {
246 const T_CURVE::CV &pt = _cvs[i];
247 QPen pen;
248 if (i == _selectedItem) {
249 pen = QPen(Qt::white, 1.0);
250 } else {
251 pen = QPen(Qt::black, 1.0);
252 }
253 _circleObjects.push_back(addEllipse(pt._pos * _width - 4, pt._val * _height - 4, 8, 8, pen, QBrush()));
254 QGraphicsEllipseItem *circle = _circleObjects.back();
255 circle->setFlag(QGraphicsItem::ItemIsMovable, true);
256 circle->setZValue(2);
257 }
258}
259
260ExprCurve::ExprCurve(QWidget *parent, QString pLabel, QString vLabel, QString iLabel, bool expandable)
261 : QWidget(parent), _scene(0), _selPosEdit(0), _selValEdit(0), _interpComboBox(0) {
262 Q_UNUSED(iLabel);
263 QHBoxLayout *mainLayout = new QHBoxLayout();
264 mainLayout->setSpacing(2);
265 mainLayout->setMargin(4);
266
267 QWidget *edits = new QWidget;
268 QVBoxLayout *editsLayout = new QVBoxLayout;
269 editsLayout->setAlignment(Qt::AlignTop);
270 editsLayout->setSpacing(0);
271 editsLayout->setMargin(0);
272 edits->setLayout(editsLayout);
273
274 QWidget *selPos = new QWidget;
275 QHBoxLayout *selPosLayout = new QHBoxLayout;
276 selPosLayout->setSpacing(1);
277 selPosLayout->setMargin(1);
278 selPos->setLayout(selPosLayout);
279 _selPosEdit = new QLineEdit;
280 QDoubleValidator *posValidator = new QDoubleValidator(0.0, 1.0, 6, _selPosEdit);
281 _selPosEdit->setValidator(posValidator);
282 int editwidth = QFontMetrics(font()).width("9.999") + 8;
283 _selPosEdit->setFixedWidth(editwidth);
284 _selPosEdit->setFixedHeight(20);
285 selPosLayout->addStretch(50);
286 QLabel *posLabel;
287 if (pLabel.isEmpty()) {
288 posLabel = new QLabel("Selected Position: ");
289 } else {
290 posLabel = new QLabel(pLabel);
291 }
292 selPosLayout->addWidget(posLabel);
293 selPosLayout->addWidget(_selPosEdit);
294
295 QWidget *selVal = new QWidget;
296 QBoxLayout *selValLayout = new QHBoxLayout;
297 selValLayout->setSpacing(1);
298 selValLayout->setMargin(1);
299 selVal->setLayout(selValLayout);
300 _selValEdit = new QLineEdit;
301 QDoubleValidator *valValidator = new QDoubleValidator(0.0, 1.0, 6, _selValEdit);
302 _selValEdit->setValidator(valValidator);
303 _selValEdit->setFixedWidth(editwidth);
304 _selValEdit->setFixedHeight(20);
305 selValLayout->addStretch(50);
306 QLabel *valLabel;
307 if (vLabel.isEmpty()) {
308 valLabel = new QLabel("Selected Value: ");
309 } else {
310 valLabel = new QLabel(vLabel);
311 }
312 selValLayout->addWidget(valLabel);
313 selValLayout->addWidget(_selValEdit);
314
315 _interpComboBox = new QComboBox;
316 _interpComboBox->addItem("None");
317 _interpComboBox->addItem("Linear");
318 _interpComboBox->addItem("Smooth");
319 _interpComboBox->addItem("Spline");
320 _interpComboBox->addItem("MSpline");
321 _interpComboBox->setCurrentIndex(4);
322 _interpComboBox->setFixedWidth(70);
323 _interpComboBox->setFixedHeight(20);
324
325 editsLayout->addWidget(selPos);
326 editsLayout->addWidget(selVal);
327 editsLayout->addWidget(_interpComboBox);
328
329 QFrame *curveFrame = new QFrame;
330 curveFrame->setFrameShape(QFrame::Panel);
331 curveFrame->setFrameShadow(QFrame::Sunken);
332 curveFrame->setLineWidth(1);
333 QHBoxLayout *curveFrameLayout = new QHBoxLayout;
334 curveFrameLayout->setMargin(0);
335 CurveGraphicsView *curveView = new CurveGraphicsView;
336 curveView->setFrameShape(QFrame::Panel);
337 curveView->setFrameShadow(QFrame::Sunken);
338 curveView->setLineWidth(1);
339 curveView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
340 curveView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
341 _scene = new CurveScene;
342 curveView->setScene(_scene);
343 curveView->setTransform(QTransform().scale(1, -1));
344 curveView->setRenderHints(QPainter::Antialiasing);
345 curveFrameLayout->addWidget(curveView);
346 curveFrame->setLayout(curveFrameLayout);
347
348 mainLayout->addWidget(edits);
349 mainLayout->addWidget(curveFrame);
350 if (expandable) {
351 QPushButton *expandButton = new QPushButton(">");
352 expandButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
353 expandButton->setFixedWidth(15);
354 mainLayout->addWidget(expandButton);
355 // open a the detail widget when clicked
356 connect(expandButton, SIGNAL(clicked()), this, SLOT(openDetail()));
357 }
358 mainLayout->setStretchFactor(curveFrame, 100);
359 setLayout(mainLayout);
360
361 // SIGNALS
362
363 // when a user selects a cv, update the fields on left
364 connect(_scene, SIGNAL(cvSelected(double, double, T_INTERP)), this, SLOT(cvSelectedSlot(double, double, T_INTERP)));
365 // when a user selects a different interp, the curve has to redraw
366 connect(_interpComboBox, SIGNAL(activated(int)), _scene, SLOT(interpChanged(int)));
367 // when a user types a different position, the curve has to redraw
368 connect(_selPosEdit, SIGNAL(returnPressed()), this, SLOT(selPosChanged()));
369 connect(this, SIGNAL(selPosChangedSignal(double)), _scene, SLOT(selPosChanged(double)));
370 // when a user types a different value, the curve has to redraw
371 connect(_selValEdit, SIGNAL(returnPressed()), this, SLOT(selValChanged()));
372 connect(this, SIGNAL(selValChangedSignal(double)), _scene, SLOT(selValChanged(double)));
373 // when the widget is resized, resize the curve widget
374 connect(curveView, SIGNAL(resizeSignal(int, int)), _scene, SLOT(resize(int, int)));
375}
376
377// CV selected, update the user interface fields.
378void ExprCurve::cvSelectedSlot(double pos, double val, T_INTERP interp) {
379 QString posStr;
380 if (pos >= 0.0) posStr.setNum(pos, 'f', 3);
381 _selPosEdit->setText(posStr);
382 QString valStr;
383 if (val >= 0.0) valStr.setNum(val, 'f', 3);
384 _selValEdit->setText(valStr);
385 _interpComboBox->setCurrentIndex(interp);
386}
387
388// User entered new position, round and send signal to redraw curve.
390 double pos = QString(_selPosEdit->text()).toDouble();
391 _selPosEdit->setText(QString("%1").arg(pos, 0, 'f', 3));
392 emit selPosChangedSignal(pos);
393}
394
395// User entered new value, round and send signal to redraw curve.
397 double val = QString(_selValEdit->text()).toDouble();
398 val = SeExpr2::clamp(val, 0, 1);
399 _selValEdit->setText(QString("%1").arg(val, 0, 'f', 3));
400 emit selValChangedSignal(val);
401}
402
404 QDialog *dialog = new QDialog();
405 dialog->setMinimumWidth(1024);
406 dialog->setMinimumHeight(400);
407 ExprCurve *curve = new ExprCurve(0, "", "", "", false);
408
409 // copy points into new data
410 const std::vector<T_CURVE::CV> &data = _scene->_cvs;
411 typedef std::vector<T_CURVE::CV>::const_iterator ITERATOR;
412 for (ITERATOR i = data.begin(); i != data.end(); ++i) curve->addPoint(i->_pos, i->_val, i->_interp);
413
414 QVBoxLayout *layout = new QVBoxLayout();
415 dialog->setLayout(layout);
416 layout->addWidget(curve);
417 QDialogButtonBox *buttonbar = new QDialogButtonBox();
418 buttonbar->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
419 connect(buttonbar, SIGNAL(accepted()), dialog, SLOT(accept()));
420 connect(buttonbar, SIGNAL(rejected()), dialog, SLOT(reject()));
421 layout->addWidget(buttonbar);
422
423 if (dialog->exec() == QDialog::Accepted) {
424 // copy points back from child
425 _scene->removeAll();
426 const std::vector<T_CURVE::CV> &dataNew = curve->_scene->_cvs;
427 typedef std::vector<T_CURVE::CV>::const_iterator ITERATOR;
428 for (ITERATOR i = dataNew.begin(); i != dataNew.end(); ++i) addPoint(i->_pos, i->_val, i->_interp);
430 }
431
432 if (dialog->exec() == QDialog::Accepted) {
433 // copy points back from child
434 _scene->removeAll();
435 const std::vector<T_CURVE::CV> &dataNew = curve->_scene->_cvs;
436 typedef std::vector<T_CURVE::CV>::const_iterator ITERATOR;
437 for (ITERATOR i = dataNew.begin(); i != dataNew.end(); ++i) addPoint(i->_pos, i->_val, i->_interp);
439 }
440}
441
442void ExprCurve::addPoint(const double x, const double y, T_INTERP interp, const bool select) {
443 _scene->addPoint(x, y, interp, select);
444}
virtual void resizeEvent(QResizeEvent *event)
Definition ExprCurve.cpp:43
void resizeSignal(int width, int height)
void drawPoints()
void cvSelected(double x, double y, T_INTERP interp)
void removeAll()
Definition ExprCurve.cpp:41
virtual void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
void resize(const int width, const int height)
Definition ExprCurve.cpp:56
int _selectedItem
Definition ExprCurve.h:110
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
QGraphicsRectItem * _baseRect
Definition ExprCurve.h:112
void drawRect()
void selValChanged(double val)
void rebuildCurve()
Definition ExprCurve.cpp:66
void removePoint(const int index)
Definition ExprCurve.cpp:87
T_CURVE::InterpType T_INTERP
Definition ExprCurve.h:61
std::vector< QGraphicsEllipseItem * > _circleObjects
Definition ExprCurve.h:109
void drawPoly()
void emitCurveChanged()
virtual void keyPressEvent(QKeyEvent *event)
Definition ExprCurve.cpp:97
void addPoint(double x, double y, const T_INTERP interp, const bool select=true)
Definition ExprCurve.cpp:73
QGraphicsPolygonItem * _curvePoly
Definition ExprCurve.h:111
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
std::vector< T_CURVE::CV > _cvs
Definition ExprCurve.h:88
SeExpr2::Curve< double > T_CURVE
Definition ExprCurve.h:60
void curveChanged()
void selPosChanged(double pos)
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
T_INTERP _interp
Definition ExprCurve.h:108
T_CURVE * _curve
Definition ExprCurve.h:93
void interpChanged(const int interp)
QLineEdit * _selValEdit
Definition ExprCurve.h:148
void addPoint(const double x, const double y, const T_INTERP interp, bool select=false)
void cvSelectedSlot(double pos, double val, T_INTERP interp)
void selValChanged()
void selValChangedSignal(double val)
QComboBox * _interpComboBox
Definition ExprCurve.h:149
void selPosChangedSignal(double pos)
void openDetail()
CurveScene * _scene
Definition ExprCurve.h:133
QLineEdit * _selPosEdit
Definition ExprCurve.h:147
void selPosChanged()
ExprCurve(QWidget *parent=0, QString pLabel="", QString vLabel="", QString iLabel="", bool expandable=true)
InterpType
Supported interpolation types.
Definition Curve.h:43
double clamp(double x, double lo, double hi)
</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
At the mid point
Definition userdoc.txt:136