From 0ed559b3ac357e8c671970e3e8896f7c2ab05ac9 Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Mar 18 2020 17:34:54 +0000 Subject: curvesfx enhancement --- diff --git a/toonz/sources/common/tparam/ttonecurveparam.cpp b/toonz/sources/common/tparam/ttonecurveparam.cpp index b2a91c2..cccb961 100644 --- a/toonz/sources/common/tparam/ttonecurveparam.cpp +++ b/toonz/sources/common/tparam/ttonecurveparam.cpp @@ -148,11 +148,81 @@ void TToneCurveParam::setCurrentChannel(ToneChannel channel) { //--------------------------------------------------------- QList TToneCurveParam::getValue(double frame) const { + // compute the handle angle and length + // in case the handle length is 0 on one side, take the oppositte handle to + // calculate the angle + auto handleAngleLength = [](TPointParamP handle, TPointParamP cp, + TPointParamP opposite, double f, + bool isLeft = true) { + TPointD vec_h_cp = handle->getValue(f) - cp->getValue(f); + double angle; + if (vec_h_cp.x == 0 && vec_h_cp.y == 0) { + TPointD vec_h_op = handle->getValue(f) - opposite->getValue(f); + angle = std::atan2(vec_h_op.y, vec_h_op.x); + } else + angle = std::atan2(vec_h_cp.y, vec_h_cp.x); + + // make the angle continuous + if (isLeft && angle < 0) angle += M_2PI; + double length = + std::sqrt(vec_h_cp.x * vec_h_cp.x + vec_h_cp.y * vec_h_cp.y); + return TPointD(angle, length); + }; + auto angleLengthToPos = [](TPointD anLen) { + return TPointD(anLen.y * std::cos(anLen.x), anLen.y * std::sin(anLen.x)); + }; + + std::set frames; + getCurrentParamSet()->getKeyframes(frames); + std::set::iterator prevIt = frames.lower_bound(frame); + std::set::iterator nextIt = frames.upper_bound(frame); + bool isNotInSegment = getCurrentParamSet()->isKeyframe(frame) || + prevIt == frames.begin() || nextIt == frames.end(); + if (prevIt != frames.begin()) prevIt--; + int i; QList points; - for (i = 0; i < getCurrentParamSet()->getParamCount(); i++) { - TPointParamP pointParam = getCurrentParamSet()->getParam(i); - points.push_back(pointParam->getValue(frame)); + int pointCount = getCurrentParamSet()->getParamCount(); + for (i = 0; i < pointCount; i++) { + // control point case or the current frame is not between the keys + if (i % 3 == 0 || isNotInSegment) { + TPointParamP pointParam = getCurrentParamSet()->getParam(i); + points.push_back(pointParam->getValue(frame)); + } else { + double prevF = (*prevIt); + double nextF = (*nextIt); + double ratio = (frame - prevF) / (nextF - prevF); + if (i % 3 == 2) { // left handle + TPointParamP left_Param = getCurrentParamSet()->getParam(i); + TPointParamP cp_Param = getCurrentParamSet()->getParam(i + 1); + TPointParamP right_Param = (i == pointCount - 2) + ? cp_Param + : TPointParamP(getCurrentParamSet()->getParam(i + 2)); + + TPointD prevAnLen = + handleAngleLength(left_Param, cp_Param, right_Param, prevF); + TPointD nextAnLen = + handleAngleLength(left_Param, cp_Param, right_Param, nextF); + // linear interpolation of angle & length + TPointD handle = + angleLengthToPos(prevAnLen * (1.0 - ratio) + nextAnLen * (ratio)); + points.push_back(cp_Param->getValue(frame) + handle); + } else { // right handle + TPointParamP right_Param = getCurrentParamSet()->getParam(i); + TPointParamP cp_Param = getCurrentParamSet()->getParam(i - 1); + TPointParamP left_Param = + (i == 1) ? cp_Param : TPointParamP(getCurrentParamSet()->getParam(i - 2)); + + TPointD prevAnLen = + handleAngleLength(right_Param, cp_Param, left_Param, prevF, false); + TPointD nextAnLen = + handleAngleLength(right_Param, cp_Param, left_Param, nextF, false); + // linear interpolation of angle & length + TPointD handle = + angleLengthToPos(prevAnLen * (1.0 - ratio) + nextAnLen * (ratio)); + points.push_back(cp_Param->getValue(frame) + handle); + } + } } return points; } diff --git a/toonz/sources/include/toonzqt/fxsettings.h b/toonz/sources/include/toonzqt/fxsettings.h index 5fa0619..d01bd6f 100644 --- a/toonz/sources/include/toonzqt/fxsettings.h +++ b/toonz/sources/include/toonzqt/fxsettings.h @@ -91,7 +91,7 @@ public: } /*- 現在のページの最適なサイズを返す -*/ - QSize getPreferedSize(); + QSize getPreferredSize(); void setTextColor(const QColor &color) { m_textColor = color; } QColor getTextColor() const { return m_textColor; } @@ -118,6 +118,11 @@ public: TOONZ_DECLARE_NEW_COMPONENT(newComboBox); #undef TOONZ_DECLARE_NEW_COMPONENT + + // make ParamsPageSet to re-compute preferred size. + // currently emitted only from ToneCurveParamField +signals: + void preferredPageSizeChanged(); }; //============================================================================= @@ -138,7 +143,7 @@ class DVAPI ParamsPageSet final : public QWidget { //! Allows to map page and index, useful to display a macro. QMap m_pageFxIndexTable; - QSize m_preferedSize; + QSize m_preferredSize; /*-- ヘルプのファイルパス(もしあれば)---*/ std::string m_helpFilePath; /*-- pdfファイルのページ指定など、引数が必要な場合の追加引数 --*/ @@ -170,7 +175,7 @@ public: ParamsPage *createParamsPage(); void addParamsPage(ParamsPage *page, const char *name); - QSize getPreferedSize() { return m_preferedSize; } + QSize getPreferredSize() { return m_preferredSize; } protected: void createPage(TIStream &is, const TFxP &fx, int index); @@ -179,6 +184,7 @@ protected slots: void setPage(int); void openHelpFile(); void openHelpUrl(); + void recomputePreferredSize(); }; //============================================================================= @@ -212,6 +218,10 @@ public: void setPointValue(int index, const TPointD &p); + void notifyPreferredSizeChanged(QSize size) { + emit preferredSizeChanged(size); + } + protected: ParamsPageSet *getCurrentPageSet() const; @@ -220,7 +230,7 @@ signals: void actualFxParamChanged(); void paramKeyChanged(); - void preferedSizeChanged(QSize); + void preferredSizeChanged(QSize); void showSwatchButtonToggled(bool); }; @@ -302,7 +312,7 @@ protected slots: void setBlackBg(); void setCheckboardBg(); - void onPreferedSizeChanged(QSize); + void onPreferredSizeChanged(QSize); void onShowSwatchButtonToggled(bool); }; diff --git a/toonz/sources/include/toonzqt/histogram.h b/toonz/sources/include/toonzqt/histogram.h index 7034a9b..44a14a8 100644 --- a/toonz/sources/include/toonzqt/histogram.h +++ b/toonz/sources/include/toonzqt/histogram.h @@ -62,7 +62,8 @@ public: void setLogScale(bool onOff) { m_logScale = onOff; } bool logScale() const { return m_logScale; } - void draw(QPainter *painter, QPoint translation = QPoint(0, 0)); + void draw(QPainter *painter, QPoint translation = QPoint(0, 0), + int size = -1); protected: void paintEvent(QPaintEvent *pe) override; @@ -75,16 +76,23 @@ protected: class DVAPI ChannelBar final : public QWidget { Q_OBJECT +public: + enum Range { Range_0_255, Range_0_1 }; + +private: QColor m_color; int m_colorBarLength; bool m_isHorizontal; bool m_drawNumbers; - + QColor m_textColor; - + Range m_range; + Q_PROPERTY(QColor TextColor READ getTextColor WRITE setTextColor) + int m_size; + public: ChannelBar(QWidget *parent = 0, QColor m_color = QColor(), bool isHorizontal = true); @@ -95,10 +103,14 @@ public: void setDrawNumbers(bool onOff); bool drawNumbers() const { return m_drawNumbers; } - void draw(QPainter *painter, QPoint translation = QPoint(0, 0)); + void draw(QPainter *painter, QPoint translation = QPoint(0, 0), + int size = -1); void setTextColor(const QColor &color) { m_textColor = color; } QColor getTextColor() const { return m_textColor; } + + void setLabelRange(Range range) { m_range = range; } + protected: void paintEvent(QPaintEvent *event) override; }; @@ -135,7 +147,8 @@ public: void setValues(const int values[]); const QVector &values() const { return m_histogramGraph->values(); } - void draw(QPainter *painter, QPoint translation = QPoint(0, 0)); + void draw(QPainter *painter, QPoint translation = QPoint(0, 0), + int width = -1); }; //============================================================================= diff --git a/toonz/sources/include/toonzqt/paramfield.h b/toonz/sources/include/toonzqt/paramfield.h index 7c9ea44..5de4fb3 100644 --- a/toonz/sources/include/toonzqt/paramfield.h +++ b/toonz/sources/include/toonzqt/paramfield.h @@ -590,7 +590,7 @@ public: void setParams(); - QSize getPreferedSize() override { return QSize(400, 380); } + QSize getPreferedSize() override; protected slots: void onChannelChanged(int); diff --git a/toonz/sources/include/toonzqt/tonecurvefield.h b/toonz/sources/include/toonzqt/tonecurvefield.h index cbba4b1..a8154e8 100644 --- a/toonz/sources/include/toonzqt/tonecurvefield.h +++ b/toonz/sources/include/toonzqt/tonecurvefield.h @@ -28,8 +28,9 @@ class FxHistogramRender; namespace DVGui { // forward declaration -class IntPairField; +class DoublePairField; class CheckBox; +class DoubleLineEdit; //============================================================================= // ChennelCurveEditor @@ -54,6 +55,10 @@ class DVAPI ChennelCurveEditor final : public QWidget { bool m_isLinear; + QPointF m_preMousePos; + + bool m_isEnlarged; + public: ChennelCurveEditor(QWidget *parent = 0, HistogramView *histogramView = 0); @@ -67,25 +72,29 @@ public: bool eventFilter(QObject *object, QEvent *event) override; - void setFirstLastXPosition(std::pair values, bool isDragging); + void setFirstLastXPosition(std::pair values, bool isDragging); void setLinear(bool isLinear); + void moveCurrentControlPoint(QPointF delta); + + void setEnlarged(bool isEnlarged); + void setLabelRange(ChannelBar::Range range); protected: - QPointF strokeToViewPoint(const TPointD p); - TPointD viewToStrokePoint(const QPointF &p); + QPointF viewToStrokePoint(const QPointF &p); int getClosestPointIndex(const QPointF &pos, double &minDistance2) const; - bool isCentralControlPoint(int index) { return index % 3 == 0; } - bool isLeftControlPoint(int index) { return index % 3 == 2; } - bool isRightControlPoint(int index) { return index % 3 == 1; } + bool isCentralControlPoint(const int index) const { return index % 3 == 0; } + bool isLeftControlPoint(const int index) const { return index % 3 == 2; } + bool isRightControlPoint(const int index) const { return index % 3 == 1; } - void movePoint(int index, const QPointF delta); void setPoint(int index, const QPointF point); + void movePoint(int index, const QPointF delta); QPointF checkPoint(const QPointF p); - void moveCurrentControlPoint(const QPointF delta); - void moveCentralControlPoint(int index, const QPointF delta); + QPointF getVisibleHandlePos(int index) const; + + void moveCentralControlPoint(int index, QPointF delta); // bool eraseControlPointWhileMove(int index, const QPointF delta); void addControlPoint(double percent); @@ -95,8 +104,6 @@ protected: void selectNextControlPoint(); void selectPreviousControlPoint(); - void moveCurrentControlPointUp(); - void moveCurrentControlPointDown(); QPainterPath getPainterPath(); @@ -105,8 +112,6 @@ protected: void mousePressEvent(QMouseEvent *) override; void mouseReleaseEvent(QMouseEvent *) override; void keyPressEvent(QKeyEvent *e) override; - void enterEvent(QEvent *) override; - void leaveEvent(QEvent *) override; void focusInEvent(QFocusEvent *fe) override; void focusOutEvent(QFocusEvent *fe) override; @@ -117,7 +122,8 @@ signals: void controlPointAdded(int index); void controlPointRemoved(int index); - void firstLastXPostionChanged(int, int); + void firstLastXPostionChanged(double, double); + void updateCurrentPosition(int, QPointF); }; //============================================================================= @@ -130,7 +136,11 @@ class DVAPI ToneCurveField final : public QWidget { QStackedWidget *m_toneCurveStackedWidget; QStackedWidget *m_sliderStackedWidget; QComboBox *m_channelListChooser; - CheckBox *m_isLinearCheckBox; + CheckBox *m_isLinearCheckBox, *m_isEnlargedCheckBox; + + DoubleLineEdit *m_currentInput, *m_currentOutput; + int m_currentPointIndex; + QComboBox *m_rangeMode; public: ToneCurveField(QWidget *parent = 0, FxHistogramRender *fxHistogramRender = 0); @@ -138,25 +148,31 @@ public: void setCurrentChannel(int currentChannel); ChennelCurveEditor *getChannelEditor(int channel) const; ChennelCurveEditor *getCurrentChannelEditor() const; - IntPairField *getCurrentSlider() const; + DoublePairField *getCurrentSlider() const; int getChannelCount() { return m_toneCurveStackedWidget->count(); } void setIsLinearCheckBox(bool isChecked); + bool isEnlarged(); protected slots: void sliderValueChanged(bool); - void onFirstLastXPostionChanged(int, int); + void onFirstLastXPostionChanged(double, double); + void onUpdateCurrentPosition(int, QPointF); + void onCurrentPointEditted(); + void onCurrentChannelSwitched(int); + void onRangeModeSwitched(int); public slots: void setLinear(bool); - + void setEnlarged(bool); void setLinearManually(bool); signals: void currentChannelIndexChanged(int); void isLinearChanged(bool); + void sizeChanged(); }; -} +} // namespace DVGui #endif // TONECURVEFIELD_H diff --git a/toonz/sources/stdfx/tonecurvefx.cpp b/toonz/sources/stdfx/tonecurvefx.cpp index b2b689c..f4b1ec6 100644 --- a/toonz/sources/stdfx/tonecurvefx.cpp +++ b/toonz/sources/stdfx/tonecurvefx.cpp @@ -33,6 +33,25 @@ int getLinearYfromX(TSegment t, int x, double &s0, double &s1) { return getLinearYfromX(t, x, s, s1); } +void truncateSpeeds(double aFrame, double bFrame, TPointD &aSpeedTrunc, + TPointD &bSpeedTrunc) { + double deltaX = bFrame - aFrame; + if (aSpeedTrunc.x < 0) aSpeedTrunc.x = 0; + if (bSpeedTrunc.x > 0) bSpeedTrunc.x = 0; + + if (aFrame + aSpeedTrunc.x > bFrame) { + if (aSpeedTrunc.x != 0) { + aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x); + } + } + + if (bFrame + bSpeedTrunc.x < aFrame) { + if (bSpeedTrunc.x != 0) { + bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x); + } + } +} + template void fill_lut(QList points, std::vector &lut, bool isLinear) { int i; @@ -45,19 +64,23 @@ void fill_lut(QList points, std::vector &lut, bool isLinear) { TPointD p2 = points.at(++i); TPointD p3 = points.at(++i); if (!isLinear) { + // truncate speed + TPointD aSpeed(p1 - p0); + TPointD bSpeed(p2 - p3); + truncateSpeeds(p0.x, p3.x, aSpeed, bSpeed); cubic.setP0(p0); - cubic.setP1(p1); - cubic.setP2(p2); + cubic.setP1(p0 + aSpeed); + cubic.setP2(p3 + bSpeed); cubic.setP3(p3); int x = (int)p0.x; while (x < 0) x++; while (x < p3.x && x < PIXEL::maxChannelValue + 1) { double s1 = 1.0; int y = getCubicYfromX(cubic, x, s0, s1); - if (y > PIXEL::maxChannelValue + 1) + if (y > PIXEL::maxChannelValue) y = PIXEL::maxChannelValue; else if (y < 0) - y = 0; + y = 0; lut[x] = y; x++; } @@ -69,10 +92,10 @@ void fill_lut(QList points, std::vector &lut, bool isLinear) { while (x < p3.x && x < PIXEL::maxChannelValue + 1) { double s1 = 1.0; int y = getLinearYfromX(segment, x, s0, s1); - if (y > PIXEL::maxChannelValue + 1) + if (y > PIXEL::maxChannelValue) y = PIXEL::maxChannelValue; else if (y < 0) - y = 0; + y = 0; lut[x] = y; x++; } @@ -138,7 +161,7 @@ QList getParamSetPoints(const TParamSet *paramSet, int frame) { } return points; } -} +} // namespace //------------------------------------------------------------------- diff --git a/toonz/sources/toonzqt/fxsettings.cpp b/toonz/sources/toonzqt/fxsettings.cpp index e252859..241b39b 100644 --- a/toonz/sources/toonzqt/fxsettings.cpp +++ b/toonz/sources/toonzqt/fxsettings.cpp @@ -627,7 +627,7 @@ void updateMaximumPageSize(QGridLayout *layout, int &maxLabelWidth, } }; // namespace -QSize ParamsPage::getPreferedSize() { +QSize ParamsPage::getPreferredSize() { int maxLabelWidth = 0; int maxWidgetWidth = 0; int fieldsHeight = 0; @@ -651,7 +651,7 @@ ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WindowFlags flags) ParamsPageSet::ParamsPageSet(QWidget *parent, Qt::WFlags flags) #endif : QWidget(parent, flags) - , m_preferedSize(0, 0) + , m_preferredSize(0, 0) , m_helpFilePath("") , m_helpCommand("") { // TabBar @@ -784,10 +784,10 @@ ParamsPage *ParamsPageSet::createParamsPage() { void ParamsPageSet::addParamsPage(ParamsPage *page, const char *name) { /*-- このFxで最大サイズのページに合わせてダイアログをリサイズ --*/ - QSize pagePreferedSize = page->getPreferedSize(); - m_preferedSize = m_preferedSize.expandedTo( - pagePreferedSize + QSize(m_tabBarContainer->height() + 2, - 2)); /*-- 2は上下左右のマージン --*/ + QSize pagePreferredSize = page->getPreferredSize(); + m_preferredSize = m_preferredSize.expandedTo( + pagePreferredSize + QSize(m_tabBarContainer->height() + 2, + 2)); /*-- 2は上下左右のマージン --*/ QScrollArea *pane = new QScrollArea(this); pane->setWidgetResizable(true); @@ -877,11 +877,14 @@ void ParamsPageSet::createPage(TIStream &is, const TFxP &fx, int index) { ParamsPage *paramsPage = new ParamsPage(this, m_parent); paramsPage->setPage(is, fx); + connect(paramsPage, SIGNAL(preferredPageSizeChanged()), this, + SLOT(recomputePreferredSize())); + /*-- このFxで最大サイズのページに合わせてダイアログをリサイズ --*/ - QSize pagePreferedSize = paramsPage->getPreferedSize(); - m_preferedSize = m_preferedSize.expandedTo( - pagePreferedSize + QSize(m_tabBarContainer->height() + 2, - 2)); /*-- 2は上下左右のマージン --*/ + QSize pagePreferredSize = paramsPage->getPreferredSize(); + m_preferredSize = m_preferredSize.expandedTo( + pagePreferredSize + QSize(m_tabBarContainer->height() + 2, + 2)); /*-- 2は上下左右のマージン --*/ QScrollArea *scrollAreaPage = new QScrollArea(this); scrollAreaPage->setWidgetResizable(true); @@ -897,6 +900,26 @@ void ParamsPageSet::createPage(TIStream &is, const TFxP &fx, int index) { //----------------------------------------------------------------------------- +void ParamsPageSet::recomputePreferredSize() { + QSize newSize(0, 0); + for (int i = 0; i < m_pagesList->count(); i++) { + QScrollArea *area = dynamic_cast(m_pagesList->widget(i)); + if (!area) continue; + ParamsPage *page = dynamic_cast(area->widget()); + if (!page) continue; + QSize pagePreferredSize = page->getPreferredSize(); + newSize = newSize.expandedTo(pagePreferredSize + + QSize(m_tabBarContainer->height() + 2, 2)); + } + if (!newSize.isEmpty()) { + m_preferredSize = newSize; + // resize the parent FxSettings + m_parent->notifyPreferredSizeChanged(m_preferredSize + QSize(2, 50)); + } +} + +//----------------------------------------------------------------------------- + /* TODO: Webサイト内のヘルプに対応すべきか検討 2016.02.01 shun_iwasawa */ void ParamsPageSet::openHelpFile() { if (m_helpFilePath == "") return; @@ -1016,9 +1039,9 @@ void ParamViewer::setFx(const TFxP ¤tFx, const TFxP &actualFx, int frame, getCurrentPageSet()->setFx(currentFx, actualFx, frame); if (m_actualFx != actualFx) { m_actualFx = actualFx; - QSize pageViewerPreferedSize = - getCurrentPageSet()->getPreferedSize() + QSize(2, 50); - emit preferedSizeChanged(pageViewerPreferedSize); + QSize pageViewerPreferredSize = + getCurrentPageSet()->getPreferredSize() + QSize(2, 50); + emit preferredSizeChanged(pageViewerPreferredSize); } } } @@ -1128,8 +1151,8 @@ FxSettings::FxSettings(QWidget *parent, const TPixel32 &checkCol1, ret = ret && connect(m_viewer, SIGNAL(pointPositionChanged(int, const TPointD &)), SLOT(onPointChanged(int, const TPointD &))); - ret = ret && connect(m_paramViewer, SIGNAL(preferedSizeChanged(QSize)), this, - SLOT(onPreferedSizeChanged(QSize))); + ret = ret && connect(m_paramViewer, SIGNAL(preferredSizeChanged(QSize)), this, + SLOT(onPreferredSizeChanged(QSize))); ret = ret && connect(m_paramViewer, SIGNAL(showSwatchButtonToggled(bool)), this, SLOT(onShowSwatchButtonToggled(bool))); assert(ret); @@ -1513,7 +1536,7 @@ void FxSettings::onViewModeChanged(QAction *triggeredAct) { //----------------------------------------------------------------------------- -void FxSettings::onPreferedSizeChanged(QSize pvBestSize) { +void FxSettings::onPreferredSizeChanged(QSize pvBestSize) { QSize popupBestSize = pvBestSize; // Set minimum size, just in case diff --git a/toonz/sources/toonzqt/histogram.cpp b/toonz/sources/toonzqt/histogram.cpp index a215b65..ee3954f 100644 --- a/toonz/sources/toonzqt/histogram.cpp +++ b/toonz/sources/toonzqt/histogram.cpp @@ -120,7 +120,7 @@ void computeRGB(int (*channelValues)[256], int *rgbValues) { void computeRGBM(int (*channelValues)[256], int *rgbmValues) { int i; - for (i = 0; i < 256; ++i) + for (i = 0; i < 256; ++i) rgbmValues[i] = channelValues[0][i] + channelValues[1][i] + channelValues[2][i] + channelValues[3][i]; } @@ -161,7 +161,7 @@ void HistogramGraph::setValues(const int values[]) { double maxValue = 0; for (i = 0; i < 256; i++) { - int count = m_values[i] = values[i]; + int count = m_values[i] = values[i]; if (maxValue < count) maxValue = count; } @@ -181,11 +181,13 @@ void HistogramGraph::setValues(const int values[]) { //----------------------------------------------------------------------------- -void HistogramGraph::draw(QPainter *p, QPoint translation) { +void HistogramGraph::draw(QPainter *p, QPoint translation, int w) { int x0 = translation.x() + drawMargin; int y0 = translation.y() + drawMargin - 2; - int w = width() - 2 * drawMargin; - int h = m_height + 1; + // in case that the size is not externally specified (i.e. the widget is in + // some layout) + if (w <= 0) w = width() - 2 * drawMargin; + int h = m_height + 1; p->setPen(Qt::NoPen); p->setBrush(Qt::white); @@ -241,7 +243,8 @@ ChannelBar::ChannelBar(QWidget *parent, QColor color, bool isHorizontal) , m_color(color) , m_isHorizontal(isHorizontal) , m_colorBarLength(13) - , m_drawNumbers(true) { + , m_drawNumbers(true) + , m_range(Range_0_255) { int d = 256 + HistogramGraph::drawMargin * 2 + 2; if (m_isHorizontal) @@ -269,15 +272,23 @@ void ChannelBar::setDrawNumbers(bool onOff) { //----------------------------------------------------------------------------- -void ChannelBar::draw(QPainter *p, QPoint translation) { +void ChannelBar::draw(QPainter *p, QPoint translation, int size) { // Calcolo i parametri necessari al draw a seconda del caso in cui mi trovo. int space = HistogramGraph::drawMargin; int w, h, x0, y0; QRect rect; QPoint initialPoint, finalPoint, delta; QColor initialColor, finalColor; + + // in case that the size is not externally specified (i.e. the widget is in + // some layout) + if (size <= 0) { + size = (m_isHorizontal) ? width() - 2 * HistogramGraph::drawMargin + : height() - 2 * HistogramGraph::drawMargin; + } + if (m_isHorizontal) { - w = width() - 2 * HistogramGraph::drawMargin; + w = size; h = m_colorBarLength; x0 = translation.x() + space; y0 = translation.y(); @@ -289,7 +300,7 @@ void ChannelBar::draw(QPainter *p, QPoint translation) { delta = QPoint(w / 4, 0); } else { w = m_colorBarLength; - h = height() - 2 * HistogramGraph::drawMargin; + h = size; x0 = translation.x() + width() - w; y0 = translation.y() + space; initialPoint = QPoint(0, y0); @@ -318,15 +329,23 @@ void ChannelBar::draw(QPainter *p, QPoint translation) { p->drawRect(x0, y0, w - 1, h - 1); if (m_drawNumbers) { - p->drawText(rect, Qt::AlignCenter, QString::number(0)); - rect.translate(delta); - p->drawText(rect, Qt::AlignCenter, QString::number(64)); - rect.translate(delta); - p->drawText(rect, Qt::AlignCenter, QString::number(128)); - rect.translate(delta); - p->drawText(rect, Qt::AlignCenter, QString::number(192)); - rect.translate(delta); - p->drawText(rect, Qt::AlignCenter, QString::number(255)); + QStringList labels; + if (m_range == Range_0_255) + labels << "0" + << "64" + << "128" + << "192" + << "255"; + else // m_range == Range_0_1 + labels << "0.0" + << "0.25" + << "0.5" + << "0.75" + << "1.0"; + for (QString &label : labels) { + p->drawText(rect, Qt::AlignCenter, label); + rect.translate(delta); + } } } @@ -373,11 +392,12 @@ void HistogramView::setValues(const int values[]) { //----------------------------------------------------------------------------- -void HistogramView::draw(QPainter *painter, QPoint translation) { - m_histogramGraph->draw(painter, translation); - m_colorBar->draw( - painter, QPoint(translation.x(), - translation.y() + m_histogramGraph->getHeight() + 17)); +void HistogramView::draw(QPainter *painter, QPoint translation, int width) { + m_histogramGraph->draw(painter, translation, width); + m_colorBar->draw(painter, + QPoint(translation.x(), + translation.y() + m_histogramGraph->getHeight() + 17), + width); } //----------------------------------------------------------------------------- @@ -398,7 +418,7 @@ Histograms::Histograms(QWidget *parent, bool rgba) HistogramView *histogramViews[6]; - int h = 0; + int h = 0; if (m_computeAlsoRGBA) histogramViews[h++] = new HistogramView(this); histogramViews[h++] = new HistogramView(this); @@ -419,7 +439,7 @@ Histograms::~Histograms() { memset(m_channelValue, 0, sizeof m_channelValue); } void Histograms::setRaster(const TRasterP &raster, const TPaletteP &palette) { if (palette.getPointer()) m_palette = palette; - m_raster = raster; + m_raster = raster; computeChannelsValue(); int i; for (i = 0; i < count(); i++) diff --git a/toonz/sources/toonzqt/paramfield.cpp b/toonz/sources/toonzqt/paramfield.cpp index ac6c43b..3af0729 100644 --- a/toonz/sources/toonzqt/paramfield.cpp +++ b/toonz/sources/toonzqt/paramfield.cpp @@ -1715,6 +1715,10 @@ ToneCurveParamField::ToneCurveParamField(QWidget *parent, QString name, connect(m_keyToggle, SIGNAL(keyToggled()), SLOT(onKeyToggled())); connect(m_toneCurveField, SIGNAL(currentChannelIndexChanged(int)), SLOT(onChannelChanged(int))); + // on enlarged, make the ParamPageSet to recompute the preferred size + if (paramsPage) + connect(m_toneCurveField, SIGNAL(sizeChanged()), paramsPage, + SIGNAL(preferredPageSizeChanged())); int i; for (i = 0; i < m_toneCurveField->getChannelCount(); i++) { @@ -1764,6 +1768,15 @@ void ToneCurveParamField::setParams() { //----------------------------------------------------------------------------- +QSize ToneCurveParamField::getPreferedSize() { + if (m_toneCurveField->isEnlarged()) + return QSize(676, 640); + else + return QSize(420, 384); +} + +//----------------------------------------------------------------------------- + void ToneCurveParamField::onChannelChanged(int channel) { if (m_actualParam->getCurrentChannel() == TToneCurveParam::ToneChannel(channel)) { diff --git a/toonz/sources/toonzqt/tonecurvefield.cpp b/toonz/sources/toonzqt/tonecurvefield.cpp index a450976..0dd016f 100644 --- a/toonz/sources/toonzqt/tonecurvefield.cpp +++ b/toonz/sources/toonzqt/tonecurvefield.cpp @@ -2,9 +2,9 @@ #include "toonzqt/tonecurvefield.h" #include "toonzqt/fxhistogramrender.h" -#include "toonzqt/intpairfield.h" +#include "toonzqt/doublepairfield.h" #include "toonzqt/checkbox.h" - +#include "toonzqt/doublefield.h" #include #include #include @@ -18,6 +18,10 @@ using namespace DVGui; //----------------------------------------------------------------------------- namespace { + +// minimum distance between control points +const double cpMargin = 4.0; + //----------------------------------------------------------------------------- double getPercentAtPoint(QPointF point, QPainterPath path) { @@ -66,57 +70,31 @@ QList getIntersectedPoint(QRectF rect, QPainterPath path) { double qtNorm2(const QPointF &p) { return p.x() * p.x() + p.y() * p.y(); } -double qtNorm(const QPointF &p) { return sqrt(qtNorm2(p)); } - -QPointF qtNormalize(const QPointF &p) { - double n = qtNorm(p); - assert(n != 0.0); - return (1.0 / n) * p; -} - double qtDistance2(const QPointF &p1, const QPointF &p2) { return qtNorm2(p2 - p1); } -double qtDistance(const QPointF &p1, const QPointF &p2) { - return qtNorm(p2 - p1); -} - -//----------------------------------------------------------------------------- - -QPointF getNewFirstHandlePoint(const QPointF &p, const QPointF &nextP, - const QPointF &oldHandlePoint) { - bool canMove = (nextP.x() - p.x() > 16); - int yDistance = nextP.y() - p.y(); - double sign = (yDistance != 0) ? yDistance / abs(yDistance) : 1; - double t = qtDistance(p, oldHandlePoint); - if (!canMove) { - QPointF normalizedP = - (yDistance != 0) ? qtNormalize(nextP - p) : QPoint(1, 1); - return QPointF(p + QPointF(t * normalizedP)); - } else if (abs(oldHandlePoint.x() - p.x()) < 16) - return QPointF(p.x() + 16, p.y() + sign * sqrt(t * t - 16 * 16)); - return oldHandlePoint; -} - -//----------------------------------------------------------------------------- +//--------------------------------------------------------- +// referred to the truncateSpeeds() in tdoubleparam.cpp +void truncateSpeeds(double aFrame, double bFrame, QVector2D &aSpeedTrunc, + QVector2D &bSpeedTrunc) { + double deltaX = bFrame - aFrame; + if (aSpeedTrunc.x() < 0) aSpeedTrunc.setX(0); + if (bSpeedTrunc.x() > 0) bSpeedTrunc.setX(0); + + if (aFrame + aSpeedTrunc.x() > bFrame) { + if (aSpeedTrunc.x() != 0) { + aSpeedTrunc = aSpeedTrunc * (deltaX / aSpeedTrunc.x()); + } + } -QPointF getNewSecondHandlePoint(const QPointF &p, const QPointF &nextP, - const QPointF &oldHandlePoint) { - bool canMove = (nextP.x() - p.x() > 16); - int yDistance = p.y() - nextP.y(); - double sign = (yDistance != 0) ? yDistance / abs(yDistance) : 1; - double s = qtDistance(oldHandlePoint, nextP); - if (!canMove) { - QPointF normalizedP = - (yDistance != 0) ? qtNormalize(nextP - p) : QPoint(1, 1); - return QPointF(nextP - QPointF(s * normalizedP)); - } else if (abs(nextP.x() - oldHandlePoint.x()) < 16) - return QPointF(nextP.x() - 16, nextP.y() + sign * sqrt(s * s - 16 * 16)); - return oldHandlePoint; + if (bFrame + bSpeedTrunc.x() < aFrame) { + if (bSpeedTrunc.x() != 0) { + bSpeedTrunc = -bSpeedTrunc * (deltaX / bSpeedTrunc.x()); + } + } } -//----------------------------------------------------------------------------- } // anonymous namespace //----------------------------------------------------------------------------- @@ -134,7 +112,8 @@ ChennelCurveEditor::ChennelCurveEditor(QWidget *parent, , m_LeftRightMargin(42) , m_TopMargin(9) , m_BottomMargin(48) - , m_isLinear(false) { + , m_isLinear(false) + , m_isEnlarged(false) { setFixedSize(m_curveHeight + 2 * m_LeftRightMargin + 2, m_curveHeight + m_TopMargin + m_BottomMargin); setAttribute(Qt::WA_KeyCompression); @@ -152,16 +131,13 @@ ChennelCurveEditor::ChennelCurveEditor(QWidget *parent, void ChennelCurveEditor::setPoints(QList points) { if (!m_points.isEmpty()) m_points.clear(); - int i; - for (i = 0; i < points.size(); i++) { - QPointF p = strokeToViewPoint(points.at(i)); - m_points.push_back(p); - } - /*--ポイント位置に合わせてスライダも更新する--*/ + for (const TPointD &point : points) + m_points.push_back(QPointF(point.x, point.y)); + // update slider position according to the first and the last control points int firstIndex = 3; int lastIndex = m_points.size() - 4; - emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, - viewToStrokePoint(m_points.at(lastIndex)).x); + emit firstLastXPostionChanged(m_points.at(firstIndex).x(), + m_points.at(lastIndex).x()); update(); } @@ -170,23 +146,17 @@ void ChennelCurveEditor::setPoints(QList points) { QList ChennelCurveEditor::getPoints() { QList points; if (m_points.isEmpty()) return points; - int i; - for (i = 0; i < m_points.size(); i++) - points.push_back(viewToStrokePoint(m_points.at(i))); + for (const QPointF &point : m_points) + points.push_back(TPointD(point.x(), point.y())); return points; } //----------------------------------------------------------------------------- -void ChennelCurveEditor::setFirstLastXPosition(std::pair values, +void ChennelCurveEditor::setFirstLastXPosition(std::pair values, bool isDragging) { - if (!isDragging) { - emit controlPointChanged(false); - return; - } - - QPointF newX0 = strokeToViewPoint(TPointD(values.first, 0)); - QPointF newX1 = strokeToViewPoint(TPointD(values.second, 0)); + QPointF newX0(values.first, 0); + QPointF newX1(values.second, 0); int indexX0 = 3; int indexX1 = m_points.size() - 4; @@ -203,6 +173,8 @@ void ChennelCurveEditor::setFirstLastXPosition(std::pair values, update(); } m_currentControlPointIndex = -1; + + if (!isDragging) emit controlPointChanged(false); } //----------------------------------------------------------------------------- @@ -215,33 +187,89 @@ void ChennelCurveEditor::setLinear(bool isLinear) { //----------------------------------------------------------------------------- +void ChennelCurveEditor::setEnlarged(bool isEnlarged) { + if (m_isEnlarged == isEnlarged) return; + m_isEnlarged = isEnlarged; + int widgetHeight = (m_isEnlarged) ? m_curveHeight * 2 : m_curveHeight; + setFixedSize(widgetHeight + 2 * m_LeftRightMargin + 2, + widgetHeight + m_TopMargin + m_BottomMargin); + m_histogramView->setGraphHeight(widgetHeight); + m_verticalChannelBar->setFixedHeight(widgetHeight + 22); + update(); +} + +//----------------------------------------------------------------------------- + +void ChennelCurveEditor::setLabelRange(ChannelBar::Range range) { + m_histogramView->channelBar()->setLabelRange(range); + m_verticalChannelBar->setLabelRange(range); +} + +//----------------------------------------------------------------------------- + QPointF ChennelCurveEditor::checkPoint(const QPointF p) { - QPointF checkedP = p; - int y0 = m_TopMargin + 1; - int y1 = m_curveHeight + m_TopMargin; - if (p.y() < y0) checkedP = QPointF(checkedP.x(), y0); - if (p.y() > y1) checkedP = QPointF(checkedP.x(), y1); - int x0 = m_LeftRightMargin + 1; - int x1 = m_curveHeight + m_LeftRightMargin; - if (p.x() < x0) checkedP = QPointF(x0, checkedP.y()); - if (p.x() > x1) checkedP = QPointF(x1, checkedP.y()); + QPointF checkedP = p; + if (p.x() < 0) + checkedP.setX(0); + else if (p.x() > m_curveHeight) + checkedP.setX(m_curveHeight); + if (p.y() < 0) + checkedP.setY(0); + else if (p.y() > m_curveHeight) + checkedP.setY(m_curveHeight); return checkedP; } //----------------------------------------------------------------------------- -QPointF ChennelCurveEditor::strokeToViewPoint(const TPointD p) { - double x = p.x + m_LeftRightMargin + 1; - double y = height() - m_BottomMargin - p.y; - return QPointF(x, y); +QPointF ChennelCurveEditor::getVisibleHandlePos(int index) const { + QRectF rect(0.0, 0.0, m_curveHeight, m_curveHeight); + QPointF handlePos(m_points.at(index)); + if (isCentralControlPoint(index) || rect.contains(handlePos)) + return handlePos; + + if (isLeftControlPoint(index)) { + QPointF cp = m_points.at(index + 1); + QVector2D lHandle(handlePos - cp); + if (handlePos.x() < 0) { + float ratio = -cp.x() / lHandle.x(); + handlePos = cp + lHandle.toPointF() * ratio; + } + if (handlePos.y() < 0) { + float ratio = -cp.y() / lHandle.y(); + handlePos = cp + lHandle.toPointF() * ratio; + } else if (handlePos.y() > 256) { + float ratio = (256 - cp.y()) / lHandle.y(); + handlePos = cp + lHandle.toPointF() * ratio; + } + } else { // isRightControlPoint + QPointF cp = m_points.at(index - 1); + QVector2D rHandle(handlePos - cp); + if (handlePos.x() > 256) { + float ratio = (256 - cp.x()) / rHandle.x(); + handlePos = cp + rHandle.toPointF() * ratio; + } + if (handlePos.y() < 0) { + float ratio = -cp.y() / rHandle.y(); + handlePos = cp + rHandle.toPointF() * ratio; + } else if (handlePos.y() > 256) { + float ratio = (256 - cp.y()) / rHandle.y(); + handlePos = cp + rHandle.toPointF() * ratio; + } + } + return handlePos; } //----------------------------------------------------------------------------- -TPointD ChennelCurveEditor::viewToStrokePoint(const QPointF &p) { +QPointF ChennelCurveEditor::viewToStrokePoint(const QPointF &p) { double x = p.x() - m_LeftRightMargin - 1; - double y = m_curveHeight - (p.y() - m_TopMargin); - return TThickPoint(x, y); + double y = p.y() - m_TopMargin; + if (m_isEnlarged) { + x *= 0.5; + y *= 0.5; + } + return QPointF(x, m_curveHeight - y); } //----------------------------------------------------------------------------- @@ -250,12 +278,24 @@ int ChennelCurveEditor::getClosestPointIndex(const QPointF &pos, double &minDistance2) const { int closestPointIndex = -1; minDistance2 = 0; + enum pointType { Handle = 0, ControlPoint, PseudoHandle } closestPointType; + QRectF rect(0, 0, m_curveHeight, m_curveHeight); int i; - for (i = 0; i < (int)m_points.size(); i++) { - double distance2 = qtDistance2(pos, m_points.at(i)); - if (closestPointIndex < 0 || distance2 < minDistance2) { + for (i = 3; i < (int)m_points.size() - 3; i++) { + if (m_isLinear && !isCentralControlPoint(i)) continue; + QPointF visiblePoint = getVisibleHandlePos(i); + + pointType type = + (isCentralControlPoint(i)) + ? ControlPoint + : (visiblePoint == m_points.at(i)) ? Handle : PseudoHandle; + + double distance2 = qtDistance2(pos, visiblePoint); + if (closestPointIndex < 0 || distance2 < minDistance2 || + (distance2 == minDistance2 && type < closestPointType)) { minDistance2 = distance2; closestPointIndex = i; + closestPointType = type; } } return closestPointIndex; @@ -271,11 +311,9 @@ void ChennelCurveEditor::movePoint(int index, const QPointF delta) { int firstIndex = 3; int lastIndex = m_points.size() - 4; if (index == firstIndex) - emit firstLastXPostionChanged(viewToStrokePoint(p).x, - viewToStrokePoint(m_points.at(lastIndex)).x); + emit firstLastXPostionChanged(p.x(), m_points.at(lastIndex).x()); if (index == lastIndex) - emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, - viewToStrokePoint(p).x); + emit firstLastXPostionChanged(m_points.at(firstIndex).x(), p.x()); } //----------------------------------------------------------------------------- @@ -287,53 +325,74 @@ void ChennelCurveEditor::setPoint(int index, const QPointF p) { int firstIndex = 3; int lastIndex = m_points.size() - 4; if (index == firstIndex) - emit firstLastXPostionChanged(viewToStrokePoint(p).x, - viewToStrokePoint(m_points.at(lastIndex)).x); + emit firstLastXPostionChanged(p.x(), m_points.at(lastIndex).x()); if (index == lastIndex) - emit firstLastXPostionChanged(viewToStrokePoint(m_points.at(firstIndex)).x, - viewToStrokePoint(p).x); + emit firstLastXPostionChanged(m_points.at(firstIndex).x(), p.x()); } //----------------------------------------------------------------------------- -void ChennelCurveEditor::moveCurrentControlPoint(const QPointF delta) { +void ChennelCurveEditor::moveCurrentControlPoint(QPointF delta) { assert(m_currentControlPointIndex != -1); int pointCount = m_points.size(); - /*- セグメントを動かした場合 -*/ + // in case moving the control point if (isCentralControlPoint(m_currentControlPointIndex)) moveCentralControlPoint(m_currentControlPointIndex, delta); - /*- 左のハンドルを動かした場合 -*/ + // in case moving the left handle else if (isLeftControlPoint(m_currentControlPointIndex)) { - QPointF p0 = - m_points.at(m_currentControlPointIndex) + QPointF(0, delta.y()); - setPoint(m_currentControlPointIndex, p0); + QPointF cp = m_points.at(m_currentControlPointIndex + 1); + QPointF left = m_points.at(m_currentControlPointIndex); + + // handle should not move across the control point + if (left.x() + delta.x() > cp.x()) delta.setX(cp.x() - left.x()); + + left += delta; + setPoint(m_currentControlPointIndex, left); + + // rotate the opposite handle keeping the handle length unchanged if (m_currentControlPointIndex < pointCount - 5) { - QPointF p2 = - m_points.at(m_currentControlPointIndex + 2) - QPointF(0, delta.y()); - setPoint(m_currentControlPointIndex + 2, p2); + QVector2D lHandle(cp - left); + if (!lHandle.isNull()) { + QPointF right = m_points.at(m_currentControlPointIndex + 2); + QVector2D rHandle(right - cp); + QVector2D newRHandle = lHandle.normalized() * rHandle.length(); + setPoint(m_currentControlPointIndex + 2, cp + newRHandle.toPointF()); + } } emit controlPointChanged(true); } - /*- 右のハンドルを動かした場合 -*/ + // in case moving the right handle else { assert(isRightControlPoint(m_currentControlPointIndex)); - QPointF p0 = - m_points.at(m_currentControlPointIndex) + QPointF(0, delta.y()); - setPoint(m_currentControlPointIndex, p0); + QPointF cp = m_points.at(m_currentControlPointIndex - 1); + QPointF right = m_points.at(m_currentControlPointIndex); + + // handle should not move across the control point + if (right.x() + delta.x() < cp.x()) delta.setX(cp.x() - right.x()); + + right += delta; + setPoint(m_currentControlPointIndex, right); + + // rotate the opposite handle keeping the handle length unchanged if (m_currentControlPointIndex > 4) { - QPointF p2 = - m_points.at(m_currentControlPointIndex - 2) - QPointF(0, delta.y()); - setPoint(m_currentControlPointIndex - 2, p2); + QVector2D rHandle(cp - right); + if (!rHandle.isNull()) { + QPointF left = m_points.at(m_currentControlPointIndex - 2); + QVector2D lHandle(left - cp); + QVector2D newLHandle = rHandle.normalized() * lHandle.length(); + setPoint(m_currentControlPointIndex - 2, cp + newLHandle.toPointF()); + } } emit controlPointChanged(true); } update(); + emit updateCurrentPosition(m_currentControlPointIndex, + m_points.at(m_currentControlPointIndex)); } //----------------------------------------------------------------------------- -void ChennelCurveEditor::moveCentralControlPoint(int index, - const QPointF delta) { +void ChennelCurveEditor::moveCentralControlPoint(int index, QPointF delta) { int pointCount = m_points.size(); assert(index < pointCount - 3 && index > 2); @@ -342,9 +401,7 @@ void ChennelCurveEditor::moveCentralControlPoint(int index, QPointF d = delta; // Trovo il valore di delta im modo tale che il punto di controllo non sia // trascinato fuori dal range consentito - int newX = p.x() + delta.x(); - int newY = p.y() + delta.y(); - QPointF newPoint = checkPoint(QPoint(newX, newY)); + QPointF newPoint = checkPoint(p + delta); d = newPoint - p; QPointF nextP = m_points.at(index + 3); @@ -352,49 +409,10 @@ void ChennelCurveEditor::moveCentralControlPoint(int index, double nextDistance = nextP.x() - (p.x() + d.x()); double precDistance = (p.x() + d.x()) - precP.x(); - // Caso particolare: Punto di controllo corrente == primo visibile, - // Punto di controllo successivo - //== - // l'ultimo - // visibile - if (index == 3 && index + 3 == pointCount - 4) { - setPoint(index + 1, - getNewFirstHandlePoint(p, nextP, m_points.at(index + 1))); - setPoint(index + 2, - getNewSecondHandlePoint(p, nextP, m_points.at(index + 2))); - if (nextDistance < 0) d = QPointF(nextP.x() - p.x(), d.y()); - if (nextDistance < 16) { - double nextYDistance = nextP.y() - (p.y() + d.y()); - if (nextYDistance > -16 && nextYDistance < 16) { - double offset = nextYDistance > 0 ? -16 : +16; - d = QPointF(d.x(), nextP.y() - p.y() + offset); - } - } - } - // Caso particolare: Punto di controllo corrente == ultimo visibile, - // Punto di controllo precedente - //== - // primo - // visibile - else if (index - 3 == 3 && index == pointCount - 4) { - setPoint(index - 2, - getNewFirstHandlePoint(precP, p, m_points.at(index - 2))); - setPoint(index - 1, - getNewSecondHandlePoint(precP, p, m_points.at(index - 1))); - if (precDistance < 0) d = QPointF(precP.x() - p.x(), d.y()); - if (precDistance < 16) { - double precYDistance = (p.y() + d.y()) - precP.y(); - if (precYDistance > -16 && precYDistance < 16) { - double offset = precYDistance > 0 ? 16 : -16; - d = QPointF(d.x(), precP.y() - p.y() + offset); - } - } - } - // Altrimenti calcolo il nuovo delta - else if (nextDistance < 16) - d = QPointF(nextP.x() - p.x() - 16, d.y()); - else if (precDistance < 16) - d = QPointF(precP.x() - p.x() + 16, d.y()); + if (nextDistance < cpMargin) + d = QPointF(nextP.x() - p.x() - cpMargin, d.y()); + else if (precDistance < cpMargin) + d = QPointF(precP.x() - p.x() + cpMargin, d.y()); if (d.isNull()) return; @@ -439,13 +457,13 @@ void ChennelCurveEditor::addControlPoint(double percent) { QPointF p0 = checkPoint(m_points.at(beforeControlPointIndex)); // Se sono troppo vicino al punto di controllo precedente ritorno - if (abs(p.x() - p0.x()) <= 16) return; + if (abs(p.x() - p0.x()) <= cpMargin) return; double beforeControlPointPercent = getPercentAtPoint(p0, path); QPointF p1 = checkPoint(m_points.at(beforeControlPointIndex + 1)); QPointF p2 = checkPoint(m_points.at(beforeControlPointIndex + 2)); QPointF p3 = checkPoint(m_points.at(beforeControlPointIndex + 3)); // Se sono troppo vicino al punto di controllo successivo ritorno - if (abs(p3.x() - p.x()) <= 16) return; + if (abs(p3.x() - p.x()) <= cpMargin) return; double nextControlPointPercent = getPercentAtPoint(p3, path); // Calcolo la velocita' e quindi il coiffciente angolare. @@ -464,6 +482,7 @@ void ChennelCurveEditor::addControlPoint(double percent) { QPointF(p.x() + 16, p.y() + 16 * m)); m_currentControlPointIndex = newControlPointIndex; + m_preMousePos = p; emit controlPointAdded(newControlPointIndex); update(); } @@ -476,73 +495,55 @@ void ChennelCurveEditor::removeCurrentControlPoint() { //----------------------------------------------------------------------------- -void ChennelCurveEditor::selectNextControlPoint() -{ +void ChennelCurveEditor::selectNextControlPoint() { int controlPointCount = (int)m_points.size(); - if (controlPointCount == 0) - return; + if (controlPointCount == 0) return; int firstVisibleControlPoint = 3; - int lastVisibleControlPoint = m_points.size() - 4; + int lastVisibleControlPoint = m_points.size() - 4; m_currentControlPointIndex++; - if (m_currentControlPointIndex < firstVisibleControlPoint || m_currentControlPointIndex > lastVisibleControlPoint) + if (m_currentControlPointIndex < firstVisibleControlPoint || + m_currentControlPointIndex > lastVisibleControlPoint) m_currentControlPointIndex = firstVisibleControlPoint; + emit updateCurrentPosition(m_currentControlPointIndex, + m_points.at(m_currentControlPointIndex)); update(); } //----------------------------------------------------------------------------- -void ChennelCurveEditor::selectPreviousControlPoint() -{ +void ChennelCurveEditor::selectPreviousControlPoint() { int controlPointCount = (int)m_points.size(); - if (controlPointCount == 0) - return; + if (controlPointCount == 0) return; int firstVisibleControlPoint = 3; - int lastVisibleControlPoint = m_points.size() - 4; + int lastVisibleControlPoint = m_points.size() - 4; m_currentControlPointIndex--; - if (m_currentControlPointIndex < firstVisibleControlPoint || m_currentControlPointIndex > lastVisibleControlPoint) + if (m_currentControlPointIndex < firstVisibleControlPoint || + m_currentControlPointIndex > lastVisibleControlPoint) m_currentControlPointIndex = lastVisibleControlPoint; + emit updateCurrentPosition(m_currentControlPointIndex, + m_points.at(m_currentControlPointIndex)); update(); } //----------------------------------------------------------------------------- -void ChennelCurveEditor::moveCurrentControlPointUp() -{ - if (m_currentControlPointIndex < 0) - return; - - moveCurrentControlPoint(QPointF(0, -10)); -} - -//----------------------------------------------------------------------------- - -void ChennelCurveEditor::moveCurrentControlPointDown() -{ - if (m_currentControlPointIndex < 0) - return; - - moveCurrentControlPoint(QPointF(0, 10)); -} - -//----------------------------------------------------------------------------- - void ChennelCurveEditor::removeControlPoint(int index) { // Non posso eliminare il primo punto di controllo visibile quindi lo rimetto // in condizione iniziale if (index <= 4) { - setPoint(0, strokeToViewPoint(TPointD(-40, 0))); - setPoint(1, strokeToViewPoint(TPointD(-20, 0))); - setPoint(2, strokeToViewPoint(TPointD(-20, 0))); - setPoint(3, strokeToViewPoint(TPointD(0, 0))); - setPoint(4, strokeToViewPoint(TPointD(16, 16))); + setPoint(0, QPointF(-40, 0)); + setPoint(1, QPointF(-20, 0)); + setPoint(2, QPointF(-20, 0)); + setPoint(3, QPointF(0, 0)); + setPoint(4, QPointF(16, 16)); update(); emit controlPointChanged(false); return; @@ -551,11 +552,11 @@ void ChennelCurveEditor::removeControlPoint(int index) { // rimetto in condizione iniziale if (index >= m_points.size() - 5) { int i = m_points.size() - 5; - setPoint(i, strokeToViewPoint(TPointD(239, 239))); - setPoint(i + 1, strokeToViewPoint(TPointD(255, 255))); - setPoint(i + 2, strokeToViewPoint(TPointD(275, 255))); - setPoint(i + 3, strokeToViewPoint(TPointD(275, 255))); - setPoint(i + 4, strokeToViewPoint(TPointD(295, 255))); + setPoint(i, QPointF(239, 239)); + setPoint(i + 1, QPointF(255, 255)); + setPoint(i + 2, QPointF(275, 255)); + setPoint(i + 3, QPointF(275, 255)); + setPoint(i + 4, QPointF(295, 255)); update(); emit controlPointChanged(false); return; @@ -575,6 +576,10 @@ void ChennelCurveEditor::removeControlPoint(int index) { emit controlPointRemoved(firstIndex + 1); m_currentControlPointIndex = firstIndex - 2; + + emit updateCurrentPosition(m_currentControlPointIndex, + m_points.at(m_currentControlPointIndex)); + update(); } @@ -592,15 +597,20 @@ QPainterPath ChennelCurveEditor::getPainterPath() { QPointF p2 = m_points.at(++i); QPointF p3 = m_points.at(++i); path.moveTo(p0); - if (!m_isLinear) - path.cubicTo(p1, p2, p3); - else + if (!m_isLinear) { + // truncate speed + QVector2D aSpeed(p1 - p0); + QVector2D bSpeed(p2 - p3); + truncateSpeeds(p0.x(), p3.x(), aSpeed, bSpeed); + path.cubicTo(p0 + aSpeed.toPointF(), p3 + bSpeed.toPointF(), p3); + } else path.lineTo(p3); p0 = p3; } // Cerco le eventuali intersezioni con il bordo. - QRectF rect(m_LeftRightMargin, m_TopMargin, m_curveHeight, m_curveHeight); + QRectF rect(0, 0, m_curveHeight, m_curveHeight); + // QRectF rect(m_LeftRightMargin, m_TopMargin, m_curveHeight, m_curveHeight); QRectF r = path.boundingRect(); if (!rect.contains(QRect(rect.left(), r.top(), rect.width(), r.height()))) { QList points = getIntersectedPoint(rect, path); @@ -625,30 +635,14 @@ QPainterPath ChennelCurveEditor::getPainterPath() { void ChennelCurveEditor::paintEvent(QPaintEvent *e) { QPainter painter(this); + double scale = (m_isEnlarged) ? 2.0 : 1.0; // Disegno il reticolato painter.setRenderHint(QPainter::Antialiasing, false); painter.setPen(QColor(250, 250, 250)); - int i; - int d = m_curveHeight * 0.25; - for (i = 1; i < 16; i++) { - // linee orizzontali - int delta = m_TopMargin + 16 * i; - int j; - for (j = 1; j < 4; j++) - painter.drawLine(QPoint((j - 1) * d + m_LeftRightMargin + 1, delta), - QPoint(j * d + m_LeftRightMargin - 1, delta)); - painter.drawLine(QPoint((4 - 1) * d + m_LeftRightMargin + 1, delta), - QPoint(4 * d + m_LeftRightMargin, delta)); - // linee verticali - delta = m_LeftRightMargin + 1 + 16 * i; - if (i % 4 == 0) continue; - for (j = 1; j < 5; j++) - painter.drawLine(QPoint(delta, (j - 1) * d + m_TopMargin), - QPoint(delta, j * d + m_TopMargin - 1)); - } // Disegno l'histogram. - m_histogramView->draw(&painter, QPoint(m_LeftRightMargin - 10, 0)); + m_histogramView->draw(&painter, QPoint(m_LeftRightMargin - 10, 0), + m_curveHeight * scale); // Disegno la barra verticale a sinistra. m_verticalChannelBar->draw( @@ -659,38 +653,53 @@ void ChennelCurveEditor::paintEvent(QPaintEvent *e) { -m_BottomMargin); // Disegno la curva entro i limiti del grafo painter.setClipRect(r, Qt::IntersectClip); + + painter.translate(m_LeftRightMargin + 1, height() - m_BottomMargin); + painter.scale(scale, -scale); + QPainterPath path = getPainterPath(); if (path.isEmpty()) return; painter.setRenderHint(QPainter::Antialiasing, true); - painter.setPen(Qt::black); + QPen blackPen(Qt::black); + QPen bluePen(Qt::blue); + blackPen.setWidthF(1.0 / scale); + bluePen.setWidthF(1.0 / scale); + + painter.setPen(blackPen); painter.setBrush(Qt::NoBrush); painter.drawPath(path); - // Disegno i punti di controllo (esclusi i primi tre e gli ultimi tre) - r = r.adjusted(-5, -5, 5, 5); int n = m_points.size(); QPointF p = m_points.at(3); - for (i = 3; i < n - 3; i++) { + for (int i = 3; i < n - 3; i++) { QBrush brush(Qt::white); - int rad = 3; + int rad = (m_isEnlarged) ? 1 : 2; QPointF nextP = m_points.at(i + 1); if (isCentralControlPoint(i)) - rad = 4; + rad = (m_isEnlarged) ? 2 : 3; else if (m_isLinear) { p = nextP; continue; } - if (m_currentControlPointIndex == i) brush = QBrush(Qt::black); - painter.setBrush(brush); - painter.setPen(Qt::black); + painter.setPen(blackPen); + QPointF handlePos = p; if (!m_isLinear) { - if (isLeftControlPoint(i)) + if (isLeftControlPoint(i)) { painter.drawLine(p, nextP); - else if (isCentralControlPoint(i) && i < n - 4) + } else if (isCentralControlPoint(i) && i < n - 4) painter.drawLine(p, nextP); + + handlePos = getVisibleHandlePos(i); } - QRectF pointRect(p.x() - rad, p.y() - rad, 2 * rad, 2 * rad); + + painter.setBrush((m_currentControlPointIndex != i) + ? Qt::white + : (p == handlePos) ? Qt::black : Qt::blue); + painter.setPen((p == handlePos) ? blackPen : bluePen); + + QRectF pointRect(handlePos.x() - rad, handlePos.y() - rad, 2 * rad, + 2 * rad); painter.drawEllipse(pointRect); p = nextP; } @@ -699,11 +708,12 @@ void ChennelCurveEditor::paintEvent(QPaintEvent *e) { //----------------------------------------------------------------------------- void ChennelCurveEditor::mouseMoveEvent(QMouseEvent *e) { + QPointF posF = viewToStrokePoint(QPointF(e->pos())); if (m_mouseButton == Qt::LeftButton && m_currentControlPointIndex != -1) { - QPoint pos = e->pos(); - QPointF posF = QPointF(pos.x(), pos.y()); - moveCurrentControlPoint(posF - m_points.at(m_currentControlPointIndex)); - } + moveCurrentControlPoint(posF - m_preMousePos); + m_preMousePos = posF; + } else if (m_currentControlPointIndex == -1) + emit updateCurrentPosition(-1, posF); } //----------------------------------------------------------------------------- @@ -712,15 +722,15 @@ void ChennelCurveEditor::mousePressEvent(QMouseEvent *e) { m_mouseButton = e->button(); setFocus(); if (m_mouseButton == Qt::LeftButton) { - QPoint pos = e->pos(); - QPointF posF = QPointF(pos.x(), pos.y()); + QPointF posF = viewToStrokePoint(QPointF(e->pos())); double minDistance; int controlPointIndex = getClosestPointIndex(posF, minDistance); // Se la distanza e' piccola seleziono il control point corrente - if (minDistance < 20) + if (minDistance < 20) { m_currentControlPointIndex = controlPointIndex; - else { + m_preMousePos = posF; + } else { m_currentControlPointIndex = -1; // Se sono sufficentemente lontano da un punto di controllo, ma abbastanza // vicino alla curva @@ -728,6 +738,11 @@ void ChennelCurveEditor::mousePressEvent(QMouseEvent *e) { double percent = getPercentAtPoint(posF, getPainterPath()); if (percent != 0 && minDistance > 20) addControlPoint(percent); } + QPointF currentPointPos = (m_currentControlPointIndex == -1) + ? posF + : m_points.at(m_currentControlPointIndex); + + emit updateCurrentPosition(m_currentControlPointIndex, currentPointPos); update(); } } @@ -735,7 +750,7 @@ void ChennelCurveEditor::mousePressEvent(QMouseEvent *e) { //----------------------------------------------------------------------------- void ChennelCurveEditor::mouseReleaseEvent(QMouseEvent *e) { - /*-- マウスドラッグ中はプレビューを更新しない。ここで初めて更新 --*/ + // fx preview updates here ( it does not update while mouse dragging ) if (m_mouseButton == Qt::LeftButton && m_currentControlPointIndex != -1 && e->button() == Qt::LeftButton) emit controlPointChanged(false); @@ -745,20 +760,32 @@ void ChennelCurveEditor::mouseReleaseEvent(QMouseEvent *e) { //----------------------------------------------------------------------------- void ChennelCurveEditor::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Delete) removeCurrentControlPoint(); - if (e->key() == Qt::Key_Right) selectNextControlPoint(); - if (e->key() == Qt::Key_Left) selectPreviousControlPoint(); - if (e->key() == Qt::Key_Up) moveCurrentControlPointUp(); - if (e->key() == Qt::Key_Down) moveCurrentControlPointDown(); -} + if (m_currentControlPointIndex == -1) return; -//----------------------------------------------------------------------------- - -void ChennelCurveEditor::enterEvent(QEvent *) { m_mouseButton = Qt::NoButton; } + if (e->key() == Qt::Key_Delete) { + removeCurrentControlPoint(); + return; + } -//----------------------------------------------------------------------------- + bool controlPressed = e->modifiers() & Qt::ControlModifier; + bool shiftPressed = e->modifiers() & Qt::ShiftModifier; + float delta = (shiftPressed) ? 10.0 : 1.0; -void ChennelCurveEditor::leaveEvent(QEvent *) { m_mouseButton = Qt::NoButton; } + if (e->key() == Qt::Key_Right) { + if (controlPressed) + selectNextControlPoint(); + else + moveCurrentControlPoint(QPointF(delta, 0.0)); + } else if (e->key() == Qt::Key_Left) { + if (controlPressed) + selectPreviousControlPoint(); + else + moveCurrentControlPoint(QPointF(-delta, 0.0)); + } else if (e->key() == Qt::Key_Up) + moveCurrentControlPoint(QPointF(0.0, delta)); + else if (e->key() == Qt::Key_Down) + moveCurrentControlPoint(QPointF(0.0, -delta)); +} //-------------------------------------------------------- @@ -783,7 +810,6 @@ void ChennelCurveEditor::focusInEvent(QFocusEvent *fe) { //-------------------------------------------------------- void ChennelCurveEditor::focusOutEvent(QFocusEvent *fe) { - m_currentControlPointIndex = -1; emit focusOut(); QWidget::focusOutEvent(fe); qApp->removeEventFilter(this); @@ -796,14 +822,9 @@ void ChennelCurveEditor::focusOutEvent(QFocusEvent *fe) { ToneCurveField::ToneCurveField(QWidget *parent, FxHistogramRender *fxHistogramRender) - : QWidget(parent), m_toneCurveStackedWidget(0) { - setFixedWidth(368); - - int currentChannelIndex = 0; - - QVBoxLayout *mainLayout = new QVBoxLayout(this); - mainLayout->setMargin(0); - mainLayout->setSpacing(0); + : QWidget(parent), m_toneCurveStackedWidget(0), m_currentPointIndex(-1) { + setFixedWidth(400); + // setFixedWidth(368); QStringList channels; channels << "RGBA" @@ -812,22 +833,17 @@ ToneCurveField::ToneCurveField(QWidget *parent, << "Green" << "Blue" << "Alpha"; - int channelCount = channels.size(); - - // lista canali: label+comboBox - QWidget *channelListWidget = new QWidget(this); - QHBoxLayout *channelListLayout = new QHBoxLayout(channelListWidget); - channelListLayout->setMargin(0); - channelListLayout->setSpacing(0); - QLabel *channelLabel = new QLabel(tr("Channel:"), channelListWidget); - channelListLayout->addWidget(channelLabel); - m_channelListChooser = new QComboBox(channelListWidget); + int channelCount = channels.size(); + int currentChannelIndex = 0; + + m_channelListChooser = new QComboBox(this); m_channelListChooser->setFixedSize(100, 20); m_channelListChooser->addItems(channels); m_channelListChooser->setCurrentIndex(currentChannelIndex); - channelListLayout->addWidget(m_channelListChooser); - channelListWidget->setLayout(channelListLayout); - mainLayout->addWidget(channelListWidget, 0, Qt::AlignCenter); + + m_rangeMode = new QComboBox(this); + m_rangeMode->addItems({"0-255", "0.0-1.0"}); + m_rangeMode->setCurrentIndex(0); // stack widget dei grafi m_toneCurveStackedWidget = new QStackedWidget(this); @@ -838,49 +854,106 @@ ToneCurveField::ToneCurveField(QWidget *parent, ChennelCurveEditor *c = new ChennelCurveEditor(this, histograms->getHistogramView(i)); m_toneCurveStackedWidget->addWidget(c); - connect(c, SIGNAL(firstLastXPostionChanged(int, int)), this, - SLOT(onFirstLastXPostionChanged(int, int))); + connect(c, SIGNAL(firstLastXPostionChanged(double, double)), this, + SLOT(onFirstLastXPostionChanged(double, double))); + connect(c, SIGNAL(updateCurrentPosition(int, QPointF)), this, + SLOT(onUpdateCurrentPosition(int, QPointF))); } - - QWidget *w = new QWidget(this); - QHBoxLayout *layout = new QHBoxLayout(w); - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(m_toneCurveStackedWidget); - w->setLayout(layout); - mainLayout->addWidget(w, 0, Qt::AlignHCenter); m_toneCurveStackedWidget->setCurrentIndex(currentChannelIndex); // stack widget degli slider m_sliderStackedWidget = new QStackedWidget(this); for (i = 0; i < channelCount; i++) { - IntPairField *intPairSlider = new IntPairField(this); - intPairSlider->setFixedHeight(20); - intPairSlider->setLabelsEnabled(false); - intPairSlider->setRange(0, 255); - intPairSlider->setValues(std::make_pair(0, 255)); - m_sliderStackedWidget->addWidget(intPairSlider); - connect(intPairSlider, SIGNAL(valuesChanged(bool)), this, + DoublePairField *doublePairSlider = new DoublePairField(this); + doublePairSlider->setFixedHeight(20); + doublePairSlider->setLabelsEnabled(false); + doublePairSlider->setRange(0.0, 255.0); + doublePairSlider->setValues(std::make_pair(0.0, 255.0)); + m_sliderStackedWidget->addWidget(doublePairSlider); + connect(doublePairSlider, SIGNAL(valuesChanged(bool)), this, SLOT(sliderValueChanged(bool))); } - mainLayout->addWidget(m_sliderStackedWidget); m_sliderStackedWidget->setCurrentIndex(currentChannelIndex); - mainLayout->addSpacing(10); - m_isLinearCheckBox = new CheckBox(QString("Linear"), this); - mainLayout->addWidget(m_isLinearCheckBox, 0, Qt::AlignHCenter); + m_isLinearCheckBox = new CheckBox(QString("Linear"), this); + m_isEnlargedCheckBox = new CheckBox(QString("Enlarge"), this); + m_isEnlargedCheckBox->setChecked(false); + m_currentInput = new DoubleLineEdit(this); + m_currentOutput = new DoubleLineEdit(this); + m_currentInput->setFixedWidth(60); + m_currentOutput->setFixedWidth(60); + m_currentInput->setDecimals(2); + m_currentOutput->setDecimals(2); + m_currentInput->setDisabled(true); + m_currentOutput->setDisabled(true); + + //------ layout + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setMargin(0); + mainLayout->setSpacing(0); + { + QHBoxLayout *channelListLayout = new QHBoxLayout(); + channelListLayout->setMargin(0); + channelListLayout->setSpacing(0); + channelListLayout->setAlignment(Qt::AlignCenter); + { + // lista canali: label+comboBox + channelListLayout->addWidget(new QLabel(tr("Channel:"), this)); + channelListLayout->addWidget(m_channelListChooser); + channelListLayout->addSpacing(20); + channelListLayout->addWidget(new QLabel(tr("Range:"), this)); + channelListLayout->addWidget(m_rangeMode); + channelListLayout->addSpacing(20); + channelListLayout->addWidget(m_isEnlargedCheckBox); + } + mainLayout->addLayout(channelListLayout, 0); + + QGridLayout *bottomLayout = new QGridLayout(); + bottomLayout->setMargin(0); + bottomLayout->setHorizontalSpacing(5); + bottomLayout->setVerticalSpacing(0); + { + QVBoxLayout *currentValLayout = new QVBoxLayout(); + currentValLayout->setMargin(0); + currentValLayout->setSpacing(0); + currentValLayout->setAlignment(Qt::AlignLeft); + { + currentValLayout->addStretch(1); + currentValLayout->addWidget(new QLabel(tr("Output:"), this)); + currentValLayout->addWidget(m_currentOutput); + currentValLayout->addSpacing(10); + currentValLayout->addWidget(new QLabel(tr("Input:"), this)); + currentValLayout->addWidget(m_currentInput); + currentValLayout->addSpacing(10); + } + bottomLayout->addLayout(currentValLayout, 0, 0); + + bottomLayout->addWidget(m_toneCurveStackedWidget, 0, 1, Qt::AlignHCenter); + bottomLayout->addWidget(m_sliderStackedWidget, 1, 1); + } + bottomLayout->setColumnStretch(1, 1); + bottomLayout->setRowStretch(0, 1); + + mainLayout->addLayout(bottomLayout); + mainLayout->addSpacing(10); + mainLayout->addWidget(m_isLinearCheckBox, 0, Qt::AlignHCenter); + } + setLayout(mainLayout); + connect(m_isLinearCheckBox, SIGNAL(clicked(bool)), SLOT(setLinearManually(bool))); connect(m_isLinearCheckBox, SIGNAL(toggled(bool)), SLOT(setLinear(bool))); + connect(m_isEnlargedCheckBox, SIGNAL(toggled(bool)), SLOT(setEnlarged(bool))); - connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), - m_toneCurveStackedWidget, SLOT(setCurrentIndex(int))); - connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), - m_sliderStackedWidget, SLOT(setCurrentIndex(int))); connect(m_channelListChooser, SIGNAL(currentIndexChanged(int)), this, - SIGNAL(currentChannelIndexChanged(int))); - - setLayout(mainLayout); + SLOT(onCurrentChannelSwitched(int))); + connect(m_rangeMode, SIGNAL(currentIndexChanged(int)), this, + SLOT(onRangeModeSwitched(int))); + connect(m_currentInput, SIGNAL(editingFinished()), this, + SLOT(onCurrentPointEditted())); + connect(m_currentOutput, SIGNAL(editingFinished()), this, + SLOT(onCurrentPointEditted())); } //----------------------------------------------------------------------------- @@ -906,26 +979,155 @@ ChennelCurveEditor *ToneCurveField::getCurrentChannelEditor() const { //----------------------------------------------------------------------------- -IntPairField *ToneCurveField::getCurrentSlider() const { - return dynamic_cast(m_sliderStackedWidget->currentWidget()); +DoublePairField *ToneCurveField::getCurrentSlider() const { + return dynamic_cast( + m_sliderStackedWidget->currentWidget()); } //----------------------------------------------------------------------------- void ToneCurveField::sliderValueChanged(bool isDragging) { - std::pair values = getCurrentSlider()->getValues(); + std::pair values = getCurrentSlider()->getValues(); + + if (m_rangeMode->currentIndex() == 1) { + values.first *= 255.0; + values.second *= 255.0; + } + getCurrentChannelEditor()->setFirstLastXPosition(values, isDragging); } //----------------------------------------------------------------------------- -void ToneCurveField::onFirstLastXPostionChanged(int x0, int x1) { - std::pair values = getCurrentSlider()->getValues(); +void ToneCurveField::onFirstLastXPostionChanged(double x0, double x1) { + if (m_rangeMode->currentIndex() == 1) { + x0 /= 255.0; + x1 /= 255.0; + } + std::pair values = getCurrentSlider()->getValues(); if (values.first != x0 || values.second != x1) getCurrentSlider()->setValues(std::make_pair(x0, x1)); } //----------------------------------------------------------------------------- +// index = -1 when no point is selected +void ToneCurveField::onUpdateCurrentPosition(int index, QPointF pos) { + auto modify = [&](double val) { + return (m_rangeMode->currentIndex() == 0) ? val : val / 255.0; + }; + + QList points = getCurrentChannelEditor()->getPoints(); + assert(index == -1 || (index >= 3 && index < points.size() - 3)); + if (index != -1 && (index < 3 || index >= points.size() - 3)) return; + + double maxChanVal = 255.0; + + // if no point is selected + if (index == -1) { + if (m_currentPointIndex != index) { + m_currentInput->setDisabled(true); + m_currentOutput->setDisabled(true); + m_currentInput->setRange(-(std::numeric_limits::max)(), + (std::numeric_limits::max)()); + m_currentOutput->setRange(-(std::numeric_limits::max)(), + (std::numeric_limits::max)()); + m_currentPointIndex = index; + } + if (pos.x() < 0.0 || pos.x() > maxChanVal || pos.y() < 0.0 || + pos.y() > maxChanVal) { + m_currentInput->setText(""); + m_currentOutput->setText(""); + } else { + m_currentInput->setValue(modify(pos.x())); + m_currentOutput->setValue(modify(pos.y())); + } + } else { // if any point is selected + if (m_currentPointIndex != index) { + m_currentInput->setEnabled(true); + m_currentOutput->setEnabled(true); + if (index % 3 == 0) { // control point case + // 前後のポイントと4離す + double xmin = (index == 3) ? 0 : points.at(index - 3).x + cpMargin; + double xmax = (index == points.size() - 4) + ? maxChanVal + : points.at(index + 3).x - cpMargin; + m_currentInput->setRange(modify(xmin), modify(xmax)); + m_currentOutput->setRange(0.0, modify(maxChanVal)); + } else if (index % 3 == 2) { // left handle + TPointD cp = points.at(index + 1); + m_currentInput->setRange(-(std::numeric_limits::max)(), + modify(cp.x)); + m_currentOutput->setRange(-(std::numeric_limits::max)(), + (std::numeric_limits::max)()); + } else { // right handle + TPointD cp = points.at(index - 1); + m_currentInput->setRange(modify(cp.x), + (std::numeric_limits::max)()); + m_currentOutput->setRange(-(std::numeric_limits::max)(), + (std::numeric_limits::max)()); + } + m_currentPointIndex = index; + } + m_currentInput->setValue(modify(pos.x())); + m_currentOutput->setValue(modify(pos.y())); + } +} + +//----------------------------------------------------------------------------- + +void ToneCurveField::onCurrentPointEditted() { + TPointD oldPos = + getCurrentChannelEditor()->getPoints().at(m_currentPointIndex); + double factor = (m_rangeMode->currentIndex() == 0) ? 1.0 : 255.0; + QPointF newPos(m_currentInput->getValue() * factor, + m_currentOutput->getValue() * factor); + getCurrentChannelEditor()->moveCurrentControlPoint( + newPos - QPointF(oldPos.x, oldPos.y)); +} + +//----------------------------------------------------------------------------- + +void ToneCurveField::onCurrentChannelSwitched(int channelIndex) { + getCurrentChannelEditor()->setCurrentControlPointIndex(-1); + m_toneCurveStackedWidget->setCurrentIndex(channelIndex); + m_sliderStackedWidget->setCurrentIndex(channelIndex); + emit currentChannelIndexChanged(channelIndex); + onUpdateCurrentPosition(-1, QPointF(-1, -1)); +} + +//----------------------------------------------------------------------------- + +void ToneCurveField::onRangeModeSwitched(int index) { + double maxVal = (index == 0) ? 255.0 : 1.0; + double factor = (index == 0) ? 255.0 : 1.0 / 255.0; + ChannelBar::Range range = + (index == 0) ? ChannelBar::Range_0_255 : ChannelBar::Range_0_1; + + for (int i = 0; i < m_toneCurveStackedWidget->count(); i++) { + ChennelCurveEditor *c = + dynamic_cast(m_toneCurveStackedWidget->widget(i)); + if (c) c->setLabelRange(range); + + DoublePairField *doublePairSlider = + dynamic_cast(m_sliderStackedWidget->widget(i)); + if (doublePairSlider) { + doublePairSlider->setRange(0.0, maxVal); + std::pair val = doublePairSlider->getValues(); + doublePairSlider->setValues( + std::make_pair(val.first * factor, val.second * factor)); + } + } + + if (m_currentPointIndex == -1) return; + + int pointIndex = m_currentPointIndex; + // in order to force updating the value range + m_currentPointIndex = -1; + TPointD point = getCurrentChannelEditor()->getPoints().at(pointIndex); + onUpdateCurrentPosition(pointIndex, QPointF(point.x, point.y)); +} + +//----------------------------------------------------------------------------- void ToneCurveField::setIsLinearCheckBox(bool isChecked) { if (m_isLinearCheckBox->isChecked() == isChecked) return; @@ -942,6 +1144,20 @@ void ToneCurveField::setLinear(bool isLinear) { //----------------------------------------------------------------------------- +void ToneCurveField::setEnlarged(bool isEnlarged) { + int i; + for (i = 0; i < m_sliderStackedWidget->count(); i++) + getChannelEditor(i)->setEnlarged(isEnlarged); + setFixedWidth((isEnlarged) ? 400 + 256 : 400); + emit sizeChanged(); +} + +//----------------------------------------------------------------------------- + void ToneCurveField::setLinearManually(bool isLinear) { emit isLinearChanged(isLinear); } + +//----------------------------------------------------------------------------- + +bool ToneCurveField::isEnlarged() { return m_isEnlargedCheckBox->isChecked(); } \ No newline at end of file