From 3de100ae5eb894ad1b0e8ee69f2c5d8dc377994d Mon Sep 17 00:00:00 2001 From: Hombre Date: Mon, 18 Apr 2011 15:40:22 +0200 Subject: [PATCH] "SnapTo" feature enabled in the curve editor + non periodic handling of the flat curve editor. Here is the new modifiew key behaviour : * CONTROL while dragging a point : slow down 20x the point move (i.e. 1pixel of cursor move move the control point by 0.05px) * SHIFT while dragging a point : snap the point to the following elements, on a "use nearest solution" method : o Diagonal curve editor : + top bound, + bottom bound, + identity (diagonal) line, + same Y than previous point, + same Y than next point, + line made by the previous and next point ; this will help to create linear parts in the curve more easilly o Flat curve editor (when moving a point) : + top bound, + bottom bound, + identity (horizontal) line, + same Y than previous point (or last point if you move the first point), + same Y than next point (or first point if you move the last point o Flat curve editor (when moving a tangential handle) : + 0.0, 0.35, 0.5, 1.0 values 0.35 is the default handles value and does create an smooth "diagonal" transition between points. --- rtengine/curves.h | 3 +- rtengine/flatcurves.cc | 66 +++++++++++------- rtgui/curveeditor.cc | 4 +- rtgui/curveeditor.h | 3 +- rtgui/curveeditorgroup.cc | 6 +- rtgui/curveeditorgroup.h | 2 +- rtgui/flatcurveeditorsubgroup.cc | 7 +- rtgui/flatcurveeditorsubgroup.h | 2 +- rtgui/mycurve.cc | 14 ++++ rtgui/mycurve.h | 15 ++++- rtgui/mydiagonalcurve.cc | 101 ++++++++++++++++++++++------ rtgui/myflatcurve.cc | 112 ++++++++++++++++++++++++------- rtgui/myflatcurve.h | 3 +- 13 files changed, 258 insertions(+), 80 deletions(-) diff --git a/rtengine/curves.h b/rtengine/curves.h index 2983a98ba..b8878442e 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -264,12 +264,13 @@ class FlatCurve : public Curve { FlatCurveType kind; double* leftTangent; double* rightTangent; + bool periodic; void CtrlPoints_set (); public: - FlatCurve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); + FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn=CURVES_MIN_POLY_POINTS); ~FlatCurve (); double getVal (double t); diff --git a/rtengine/flatcurves.cc b/rtengine/flatcurves.cc index 46dfa0b83..f2adef33b 100644 --- a/rtengine/flatcurves.cc +++ b/rtengine/flatcurves.cc @@ -29,21 +29,24 @@ namespace rtengine { -FlatCurve::FlatCurve (const std::vector& p, int poly_pn) : leftTangent(NULL), rightTangent(NULL) { +FlatCurve::FlatCurve (const std::vector& p, bool isPeriodic, int poly_pn) : leftTangent(NULL), rightTangent(NULL) { ppn = poly_pn; + poly_x.clear(); + poly_y.clear(); - if (p.size()<5) { - kind = FCT_Empty; - } - else { + kind = FCT_Empty; + periodic = isPeriodic; + + if (p.size()>4) { kind = (FlatCurveType)p[0]; if (kind==FCT_MinMaxCPoints) { + int oneMorePoint = periodic ? 1:0; N = (p.size()-1)/4; - x = new double[N+1]; - y = new double[N+1]; - leftTangent = new double[N+1]; - rightTangent = new double[N+1]; + x = new double[N+oneMorePoint]; + y = new double[N+oneMorePoint]; + leftTangent = new double[N+oneMorePoint]; + rightTangent = new double[N+oneMorePoint]; int ix = 1; for (int i=0; i& p, int poly_pn) : leftTangent(N rightTangent[i] = p[ix++]; } // The first point is copied to the end of the point list, to handle the curve periodicity - x[N] = p[1]+1.0; - y[N] = p[2]; - leftTangent[N] = p[3]; - rightTangent[N] = p[4]; - if (N > 1) + if (periodic) { + x[N] = p[1]+1.0; + y[N] = p[2]; + leftTangent[N] = p[3]; + rightTangent[N] = p[4]; + } + else { + N--; + } + if (N > 0+(periodic?1:0) ) CtrlPoints_set (); } /*else if (kind==FCT_Parametric) { @@ -239,24 +247,30 @@ void FlatCurve::CtrlPoints_set () { poly_y.clear(); j = 0; - // very first point of the curve + // adding an initial horizontal line if necessary + if (!periodic && sc_x[j] != 0.) { + poly_x.push_back(0.); + poly_y.push_back(sc_y[j]); + } + + // the first point of the curves poly_x.push_back(sc_x[j]); poly_y.push_back(sc_y[j]); firstPointIncluded = false; // create the polyline with the number of points adapted to the X range of the sub-curve - for (unsigned int i=0; i < k; i++) { - if (sc_isLinear[i]) { - j++; // skip the first point - poly_x.push_back(sc_x[j]); - poly_y.push_back(sc_y[j++]); - } - else { + for (unsigned int i=0; i < k; i++) { + if (sc_isLinear[i]) { + j++; // skip the first point + poly_x.push_back(sc_x[j]); + poly_y.push_back(sc_y[j++]); + } + else { nbr_points = (int)(((double)(ppn) * sc_length[i] )/ total_length); if (nbr_points<0){ for(unsigned int it=0;it < sc_x.size(); it+=3) printf("sc_length[%d/3]=%f \n",it,sc_length[it/3]); - printf("Flat curve: error detected!\n i=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,nbr_points,ppn,N,sc_length[i/3],total_length); + printf("Flat curve: error detected!\n i=%d periodic=%d nbr_points=%d ppn=%d N=%d sc_length[i/3]=%f total_length=%f",i,periodic,nbr_points,ppn,N,sc_length[i/3],total_length); exit(0); } // increment along the curve, not along the X axis @@ -268,6 +282,12 @@ void FlatCurve::CtrlPoints_set () { } } + // adding an final horizontal line if necessary + if (!periodic && sc_x[--j] != 1.) { + poly_x.push_back(1.); + poly_y.push_back(sc_y[j]); + } + /* // Checking the values Glib::ustring fname = "Curve.xyz"; // TopSolid'Design "plot" file format diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 1a720aa81..7f7fc92e9 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -56,7 +56,9 @@ std::vector DiagonalCurveEditor::getCurve () { } } -FlatCurveEditor::FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup) : CurveEditor::CurveEditor(text, (CurveEditorGroup*) ceGroup, ceSubGroup) { +FlatCurveEditor::FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup, bool isPeriodic) : CurveEditor::CurveEditor(text, (CurveEditorGroup*) ceGroup, ceSubGroup) { + + periodic = isPeriodic; // Order set in the same order than "enum FlatCurveType". Shouldn't change, for compatibility reason curveType->addEntry(argv0+"/images/curveType-flatLinear.png", M("CURVEEDITOR_LINEAR")); // 0 Linear diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index 11e71614d..01f38b609 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -110,9 +110,10 @@ class FlatCurveEditor : public CurveEditor { protected: // reflects the buttonType active selection ; used as a pre-'selectionChange' reminder value std::vector controlPointsCurveEd; + bool periodic; public: - FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup); + FlatCurveEditor (Glib::ustring text, CurveEditorGroup* ceGroup, CurveEditorSubGroup* ceSubGroup, bool isPeriodic = true); std::vector getCurve (); }; diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc index bc54cf953..59624d251 100644 --- a/rtgui/curveeditorgroup.cc +++ b/rtgui/curveeditorgroup.cc @@ -56,8 +56,10 @@ void CurveEditorGroup::hideCurrentCurve() { /* * Add a new curve to the curves list + * + * The "periodic" parameter is only used by flat curve editors */ -CurveEditor* CurveEditorGroup::addCurve(CurveType cType, Glib::ustring curveLabel) { +CurveEditor* CurveEditorGroup::addCurve(CurveType cType, Glib::ustring curveLabel, bool periodic) { switch (cType) { case (CT_Diagonal): if (!diagonalSubGroup) { @@ -68,7 +70,7 @@ CurveEditor* CurveEditorGroup::addCurve(CurveType cType, Glib::ustring curveLabe if (!flatSubGroup) { flatSubGroup = new FlatCurveEditorSubGroup(this); } - return (CurveEditor*)flatSubGroup->addCurve(curveLabel); + return (CurveEditor*)flatSubGroup->addCurve(curveLabel, periodic); default: return (CurveEditor*)NULL; break; diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h index b84347b7c..136f9901b 100644 --- a/rtgui/curveeditorgroup.h +++ b/rtgui/curveeditorgroup.h @@ -70,7 +70,7 @@ public: void setColorProvider (ColorProvider* p) { cp = p; } CurveEditor* getDisplayedCurve () { return displayedCurve; } //void on_realize (); - CurveEditor* addCurve(CurveType cType, Glib::ustring curveLabel); + CurveEditor* addCurve(CurveType cType, Glib::ustring curveLabel, bool periodic = true); protected: //void curveTypeToggled (); diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc index 860b3a42d..c491f584a 100644 --- a/rtgui/flatcurveeditorsubgroup.cc +++ b/rtgui/flatcurveeditorsubgroup.cc @@ -72,8 +72,8 @@ FlatCurveEditorSubGroup::~FlatCurveEditorSubGroup() { /* * Add a new curve to the curves list */ -FlatCurveEditor* FlatCurveEditorSubGroup::addCurve(Glib::ustring curveLabel) { - FlatCurveEditor* newCE = new FlatCurveEditor(curveLabel, parent, this); +FlatCurveEditor* FlatCurveEditorSubGroup::addCurve(Glib::ustring curveLabel, bool isPeriodic) { + FlatCurveEditor* newCE = new FlatCurveEditor(curveLabel, parent, this, isPeriodic); // Initialization of the new curve storeCurveValues(newCE, getCurveFromGUI(FCT_MinMaxCPoints)); @@ -99,6 +99,7 @@ void FlatCurveEditorSubGroup::switchGUI() { switch((FlatCurveType)(dCurve->curveType->getSelected())) { case (FCT_MinMaxCPoints): + CPointsCurve->setPeriodicity(dCurve->periodic); // Setting Periodicity before setting points CPointsCurve->setPoints (dCurve->controlPointsCurveEd); parent->pack_start (*CPointsCurveBox); CPointsCurveBox->check_resize(); @@ -196,7 +197,7 @@ void FlatCurveEditorSubGroup::restoreDisplayedHistogram() { //paramCurve->updateBackgroundHistogram (parent->displayedCurve->histogram); CPointsCurve->updateBackgroundHistogram (parent->displayedCurve->histogram); } - + } void FlatCurveEditorSubGroup::storeCurveValues (CurveEditor* ce, const std::vector& p) { diff --git a/rtgui/flatcurveeditorsubgroup.h b/rtgui/flatcurveeditorsubgroup.h index f94b7ca23..05c9841d1 100644 --- a/rtgui/flatcurveeditorsubgroup.h +++ b/rtgui/flatcurveeditorsubgroup.h @@ -40,7 +40,7 @@ public: FlatCurveEditorSubGroup(CurveEditorGroup* prt); ~FlatCurveEditorSubGroup(); - FlatCurveEditor* addCurve(Glib::ustring curveLabel = ""); + FlatCurveEditor* addCurve(Glib::ustring curveLabel = "", bool periodic = true); //virtual void updateBackgroundHistogram (CurveEditor* ce); virtual void setColorProvider (ColorProvider* p); diff --git a/rtgui/mycurve.cc b/rtgui/mycurve.cc index 167971897..e9f9e6ccd 100644 --- a/rtgui/mycurve.cc +++ b/rtgui/mycurve.cc @@ -31,6 +31,7 @@ MyCurve::MyCurve () : listener(NULL) { snapTo = ST_None; colorProvider = NULL; sized = RS_Pending; + snapToElmt = -100; set_extension_events(Gdk::EXTENSION_EVENTS_ALL); add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON1_MOTION_MASK); @@ -54,3 +55,16 @@ void MyCurve::notifyListener () { if (listener) listener->curveChanged (); } + +bool MyCurve::snapCoordinate(double testedVal, double realVal) { + + double distY = realVal - testedVal; + + if (distY < 0.) distY = -distY; + if (distY < snapToMinDist) { + snapToMinDist = distY; + snapToVal = testedVal; + return true; + } + return false; +} diff --git a/rtgui/mycurve.h b/rtgui/mycurve.h index 2b8c346d6..5649b46a9 100644 --- a/rtgui/mycurve.h +++ b/rtgui/mycurve.h @@ -70,12 +70,25 @@ class MyCurve : public Gtk::DrawingArea { std::vector upoint; std::vector lpoint; bool buttonPressed; - enum SnapToType snapTo; + /* + * snapToElmt must be interpreted like this: + * -100 : no element (default) + * -3 : maximum value + * -2 : identity value + * -1 : minimum value + * [0;1000[ : control point that it's snapped to + * >=1000 : moved control point which is snapped to to the line made by it neighbors + */ + int snapToElmt; + bool snapTo; + double snapToMinDist; + double snapToVal; MyCurveIdleHelper* mcih; enum ResizeState sized; virtual std::vector get_vector (int veclen) = 0; int getGraphMinSize() { return GRAPH_SIZE + RADIUS + 1; } + bool snapCoordinate(double testedVal, double realVal); public: MyCurve (); diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index f8f3d5c4b..8843958b5 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -178,7 +178,10 @@ void MyDiagonalCurve::draw (int handle) { cr->stroke (); // draw f(x)=x line - cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); + if (snapToElmt == -2) + cr->set_source_rgb (1.0, 0.0, 0.0); + else + cr->set_source_rgb (c.get_red_p(), c.get_green_p(), c.get_blue_p()); std::valarray ds (1); ds[0] = 4; cr->set_dash (ds, 0); @@ -205,20 +208,30 @@ void MyDiagonalCurve::draw (int handle) { // draw the cage of the NURBS curve if (curve.type==DCT_NURBS) { + unsigned int nbPoints; std::valarray ch_ds (1); ch_ds[0] = 2; cr->set_dash (ch_ds, 0); cr->set_source_rgb (0.0, 0.0, 0.0); std::vector points = getPoints(); - for (int i = 1; i < (int)points.size(); ) { - double x = ((innerWidth-1) * points[i++] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); - double y = innerHeight - ((innerHeight-1) * points[i++] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); - if (i==3) - cr->move_to (x, y); + nbPoints = ((int)points.size()-1)/2; + for (unsigned int i = 1; i < nbPoints; i++) { + int pos = i*2+1; + + double x1 = ((innerWidth-1) * points[pos-2] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); + double y1 = innerHeight - ((innerHeight-1) * points[pos-1] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); + double x2 = ((innerWidth-1) * points[pos] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); + double y2 = innerHeight - ((innerHeight-1) * points[pos+1] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); + + // set the color of the line when the point is snapped to the cage + if (curve.x.size() == nbPoints && snapToElmt >= 1000 && ((i == (snapToElmt-1000)) || (i == (snapToElmt-999)))) + cr->set_source_rgb (1.0, 0.0, 0.0); else - cr->line_to (x, y); + cr->set_source_rgb (0.0, 0.0, 0.0); + cr->move_to (x1, y1); + cr->line_to (x2, y2); + cr->stroke (); } - cr->stroke (); cr->unset_dash (); } @@ -232,7 +245,17 @@ void MyDiagonalCurve::draw (int handle) { // draw bullets if (curve.type!=DCT_Parametric) for (int i = 0; i < (int)curve.x.size(); ++i) { - cr->set_source_rgb ((i == handle ? 1.0 : 0.0), 0.0, 0.0); + if (curve.x[i] == -1) continue; + if (snapToElmt >= 1000) { + int pt = snapToElmt-1000; + if (i >= (pt-1) && i <= (pt+1)) + cr->set_source_rgb(1.0, 0.0, 0.0); + else + cr->set_source_rgb(0.0, 0.0, 0.0); + } + else + cr->set_source_rgb ((i == handle || i == snapToElmt ? 1.0 : 0.0), 0.0, 0.0); + double x = ((innerWidth-1) * curve.x[i] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, innerWidth); double y = innerHeight - ((innerHeight-1) * curve.y[i] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, innerHeight); @@ -297,6 +320,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { break; case Gdk::BUTTON_PRESS: + snapToElmt = -100; if (curve.type!=DCT_Parametric) { if (event->button.button == 1) { buttonPressed = true; @@ -338,6 +362,7 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { break; case Gdk::BUTTON_RELEASE: + snapToElmt = -100; if (curve.type!=DCT_Parametric) { if (buttonPressed && event->button.button == 1) { buttonPressed = false; @@ -398,7 +423,13 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { break; case Gdk::MOTION_NOTIFY: + snapToElmt = -100; if (curve.type == DCT_Linear || curve.type == DCT_Spline || curve.type == DCT_NURBS) { + + snapToMinDist = 10.; + snapToVal = 0.; + snapToElmt = -100; + // get the pointer position getCursorPosition(event); @@ -439,6 +470,9 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { ugpX += deltaX; ugpY += deltaY; + // the unclamped grabbed point is brought back in the range when snapTo is active + if (snapTo) ugpY = CLAMP(ugpY, 0.0, 1.0); + // handling limitations along X axis if (ugpX >= rightDeletionBound && (grab_point > 0 && grab_point < (num-1))) { curve.x[grab_point] = -1.; @@ -457,9 +491,39 @@ bool MyDiagonalCurve::handleEvents (GdkEvent* event) { else if (ugpY <= bottomDeletionBound && grab_point != 0 && grab_point != num-1) { curve.x[grab_point] = -1.; } - else - // nextPosY is in the bounds - curve.y[grab_point] = CLAMP(ugpY, 0.0, 1.0); + else { + // snapping point to specific values + if (snapTo && curve.x[grab_point] != -1.) { + if (grab_point > 0 && grab_point < (curve.y.size()-1)) { + double prevX = curve.x[grab_point-1]; + double prevY = curve.y[grab_point-1]; + double nextX = curve.x[grab_point+1]; + double nextY = curve.y[grab_point+1]; + + double ratio = (curve.x[grab_point]-prevX)/(nextX-prevX); + double y = (nextY-prevY) * ratio + prevY; + + if (snapCoordinate(y, ugpY)) snapToElmt = 1000+grab_point; + } + if (grab_point > 0) { + int prevP = grab_point-1; + if (snapCoordinate(curve.y[prevP], ugpY)) snapToElmt = prevP; + } + if (grab_point < (curve.y.size()-1)) { + int nextP = grab_point+1; + if (snapCoordinate(curve.y[nextP], ugpY)) snapToElmt = nextP; + } + if (snapCoordinate(1.0, ugpY)) snapToElmt = -3; + if (snapCoordinate(curve.x[grab_point], ugpY)) snapToElmt = -2; + if (snapCoordinate(0.0, ugpY)) snapToElmt = -1; + + curve.y[grab_point] = snapToVal; + } + else { + // nextPosY is in the bounds + curve.y[grab_point] = CLAMP(ugpY, 0.0, 1.0); + } + } if (curve.x[grab_point] != prevPosX || curve.y[grab_point] != prevPosY) { // we recalculate the curve only if we have to @@ -529,9 +593,8 @@ void MyDiagonalCurve::getCursorPosition(GdkEvent* event) { int shift_key = mod_type & GDK_SHIFT_MASK; // the increment get smaller if modifier key are used, and "snap to" may be enabled - if (control_key && shift_key) { snapTo = ST_Neighbors; } - else if (control_key) { snapTo = ST_Identity; } - else if (shift_key) { incrementX *= 0.04; incrementY *= 0.04; } + if (control_key) { incrementX *= 0.05; incrementY *= 0.05; } + if (shift_key) { snapTo = true; } deltaX = (double)(cursorX - prevCursorX) * incrementX; deltaY = (double)(cursorY - prevCursorY) * incrementY; @@ -660,16 +723,16 @@ void MyDiagonalCurve::updateBackgroundHistogram (LUTu & hist) { if (hist!=NULL) { //memcpy (bghist, hist, 256*sizeof(unsigned int)); - for (int i=0; i<256; i++) bghist[i]=hist[i]; - //hist = bghist; + for (int i=0; i<256; i++) bghist[i]=hist[i]; + //hist = bghist; bghistvalid = true; } else bghistvalid = false; - + mcih->pending++; g_idle_add (diagonalmchistupdate, mcih); - + } void MyDiagonalCurve::reset() { diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index 88bcb40e9..d96cd192a 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -54,7 +54,7 @@ std::vector MyFlatCurve::get_vector (int veclen) { // Get the curve control points std::vector curveDescr = getPoints (); - rtengine::FlatCurve* rtcurve = new rtengine::FlatCurve (curveDescr, veclen*1.5 > 5000 ? 5000 : veclen*1.5); + rtengine::FlatCurve* rtcurve = new rtengine::FlatCurve (curveDescr, periodic, veclen*1.5 > 5000 ? 5000 : veclen*1.5); // Create the sample values that will be converted std::vector samples; @@ -157,13 +157,20 @@ void MyFlatCurve::draw () { double y0 = (double)RADIUS-0.5; double y1 = (double)RADIUS-0.5 + (double)innerHeight + 2.; for (int i = 0; i < 5; i++) { - double currX = (double)RADIUS-0.5 + (double)i*((double)innerWidth + 2.)/4.; - double currY = (double)RADIUS-0.5 + (double)i*((double)innerHeight + 2.)/4.; + cr->move_to (x0, y0); + cr->line_to (x0, y1); + cr->line_to (x1, y1); + cr->line_to (x1, y0); + cr->line_to (x0, y0); + } + /*for (int i = 0; i < 5; i++) { + double currX = (double)RADIUS-0.5 + (double)i*((double)innerWidth + 2.)/4.; + double currY = (double)RADIUS-0.5 + (double)i*((double)innerHeight + 2.)/4.; cr->move_to (x0, currY); cr->line_to (x1, currY); cr->move_to (currX, y0); cr->line_to (currX, y1); - } + }*/ cr->stroke (); // draw f(x)=0.5 line @@ -171,13 +178,14 @@ void MyFlatCurve::draw () { std::valarray ds (1); ds[0] = 4; cr->set_dash (ds, 0); - cr->move_to ((double)RADIUS+0.5 , (double)RADIUS+0.5 + (double)innerHeight/2.); - cr->line_to ((double)RADIUS+0.5 + (double)innerWidth/2., (double)RADIUS+0.5 + (double)innerHeight/2.); + cr->move_to (x0, (double)RADIUS+0.5 + (double)innerHeight/2.); + cr->line_to (x1, (double)RADIUS+0.5 + (double)innerHeight/2.); cr->stroke (); + cr->unset_dash (); + cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); - cr->unset_dash (); cr->set_line_width (1.0); // draw the color feedback of the control points @@ -347,6 +355,9 @@ void MyFlatCurve::draw () { else cr->set_source_rgb (1.0, 0.0, 0.0); } + else if (i == snapToElmt) { + cr->set_source_rgb (1.0, 0.0, 0.0); + } else if (curve.y[i] == 0.5) cr->set_source_rgb (0.0, 0.5, 0.0); else @@ -473,6 +484,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { int src, dst; std::vector::iterator itx, ity, itlt, itrt; + snapToElmt = -100; bool retval = false; int num = (int)curve.x.size(); @@ -548,7 +560,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { curve.rightTangent.insert (itrt, 0); num++; - // the graph is refreshed only if a new point is created (snaped to a pixel) + // the graph is refreshed only if a new point is created curve.x[closest_point] = clampedX; curve.y[closest_point] = clampedY; curve.leftTangent[closest_point] = 0.35; @@ -695,14 +707,13 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { case Gdk::MOTION_NOTIFY: if (curve.type == FCT_Linear || curve.type == FCT_MinMaxCPoints) { - int leftNeigborPoint = -1; - int rightNeigborPoint = -1; - double leftNeigborY = -1.; - double rightNeigborY = -1.; - int previous_lit_point = lit_point; enum MouseOverAreas prevArea = area; + snapToMinDist = 10.; + snapToVal = 0.; + snapToElmt = -100; + // get the pointer position getCursorPosition(event); getMouseOverArea(); @@ -826,13 +837,22 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { break; case (FCT_EditedHandle_LeftTan): { - double prevValue = ugpX; + double prevValue = curve.leftTangent[lit_point]; ugpX -= deltaX*3; ugpX = CLAMP(ugpX, 0., 1.); - curve.leftTangent[lit_point] = ugpX; + if (snapTo) { + snapCoordinate(0.0, ugpX); + snapCoordinate(0.35, ugpX); + snapCoordinate(0.5, ugpX); + snapCoordinate(1.0, ugpX); + curve.leftTangent[lit_point] = snapToVal; + } + else { + curve.leftTangent[lit_point] = ugpX; + } - if (ugpX != prevValue) { + if (curve.leftTangent[lit_point] != prevValue) { interpolate (); draw (); notifyListener (); @@ -841,13 +861,22 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { } case (FCT_EditedHandle_RightTan): { - double prevValue = ugpX; + double prevValue = curve.rightTangent[lit_point]; ugpX += deltaX*3; ugpX = CLAMP(ugpX, 0., 1.); - curve.rightTangent[lit_point] = ugpX; + if (snapTo) { + snapCoordinate(0.0, ugpX); + snapCoordinate(0.35, ugpX); + snapCoordinate(0.5, ugpX); + snapCoordinate(1.0, ugpX); + curve.rightTangent[lit_point] = snapToVal; + } + else { + curve.rightTangent[lit_point] = ugpX; + } - if (ugpX != prevValue) { + if (curve.rightTangent[lit_point] != prevValue) { interpolate (); draw (); notifyListener (); @@ -855,7 +884,7 @@ bool MyFlatCurve::handleEvents (GdkEvent* event) { break; } - // already process before the "switch" instruction + // already processed before the "switch" instruction //case (FCT_EditedHandle_CPointUD): default: @@ -995,9 +1024,41 @@ void MyFlatCurve::movePoint(bool moveX, bool moveY) { } if (moveY) { + // we memorize the previous position of the point, for optimization purpose ugpY += deltaY; + // snapping point to specific values + if (snapTo && curve.x[lit_point] != -1) { + + // the unclamped grabbed point is brought back in the range + ugpY = CLAMP(ugpY, 0.0, 1.0); + + if (lit_point == 0) { + int prevP = curve.y.size()-1; + if (snapCoordinate(curve.y[prevP], ugpY)) snapToElmt = prevP; + } + else { + int prevP = lit_point-1; + if (snapCoordinate(curve.y[prevP], ugpY)) snapToElmt = prevP; + } + + if (curve.y.size() > 2) { + if (lit_point == (curve.y.size()-1)) { + if (snapCoordinate(curve.y[0], ugpY)) snapToElmt = 0; + } + else { + int nextP = lit_point+1; + if (snapCoordinate(curve.y[nextP], ugpY)) snapToElmt = nextP; + } + } + if (snapCoordinate(1.0, ugpY)) snapToElmt = -3; + if (snapCoordinate(0.5, ugpY)) snapToElmt = -2; + if (snapCoordinate(0.0, ugpY)) snapToElmt = -1; + + curve.y[lit_point] = snapToVal; + } + // Handling limitations along Y axis if (ugpY >= topDeletionBound && nbPoints>2) { if (curve.x[lit_point] != -1.) { @@ -1015,7 +1076,7 @@ void MyFlatCurve::movePoint(bool moveX, bool moveY) { } else { // nextPosY is in the bounds - curve.y[lit_point] = CLAMP(ugpY, 0.0, 1.0); + if (!snapTo) curve.y[lit_point] = CLAMP(ugpY, 0.0, 1.0); if (!moveX && curve.x[lit_point] == -1.) { // bring back the X value of the point if it reappear curve.x[lit_point] = deletedPointX; @@ -1024,7 +1085,7 @@ void MyFlatCurve::movePoint(bool moveX, bool moveY) { } if (curve.x[lit_point] != prevPosX || curve.y[lit_point] != prevPosY) { - // we recalculate the curve only if we have to + // we recompute the curve only if we have to interpolate (); draw (); notifyListener (); @@ -1071,7 +1132,7 @@ void MyFlatCurve::getCursorPosition(GdkEvent* event) { preciseCursorX = cursorX * incrementX; preciseCursorY = cursorY * incrementY; - snapTo = ST_None; + snapTo = false; // update deltaX/Y if the user drags a point if (editedHandle != FCT_EditedHandle_None) { @@ -1080,9 +1141,8 @@ void MyFlatCurve::getCursorPosition(GdkEvent* event) { int shift_key = mod_type & GDK_SHIFT_MASK; // the increment get smaller if modifier key are used, and "snap to" may be enabled - if (control_key && shift_key) { snapTo = ST_Neighbors; } - else if (control_key) { snapTo = ST_Identity; } - else if (shift_key) { incrementX *= 0.04; incrementY *= 0.04; } + if (control_key) { incrementX *= 0.05; incrementY *= 0.05; } + if (shift_key) { snapTo = true; } deltaX = (double)(cursorX - prevCursorX) * incrementX; deltaY = (double)(cursorY - prevCursorY) * incrementY; diff --git a/rtgui/myflatcurve.h b/rtgui/myflatcurve.h index 9cf81641e..ccf556b55 100644 --- a/rtgui/myflatcurve.h +++ b/rtgui/myflatcurve.h @@ -93,7 +93,7 @@ class MyFlatCurve : public MyCurve { double minDistanceX; // X minimal distance before point suppression double minDistanceY; // Y minimal distance before point suppression double deletedPointX; // Backup of the X value of the edited point, when deleted while being dragged - HandlePosition leftTanHandle; // XY coordinate if the upper left and bottom right corner of the left tangent handle + HandlePosition leftTanHandle; // XY coordinate if the upper left and bottom right corner of the left tangent handle HandlePosition rightTanHandle; // XY coordinate if the upper left and bottom right corner of the right tangent handle bool tanHandlesDisplayed; // True if the tangent handles are displayed bool periodic; // Flat curves are periodic by default @@ -114,6 +114,7 @@ class MyFlatCurve : public MyCurve { MyFlatCurve (); //~MyFlatCurve (); std::vector getPoints (); + void setPeriodicity (bool isPeriodic) { periodic = isPeriodic; }; void setPoints (const std::vector& p); void setType (FlatCurveType t); bool handleEvents (GdkEvent* event);