diff --git a/rtdata/languages/English (UK) b/rtdata/languages/English (UK) index 2b4e0656b..8806f0f11 100644 --- a/rtdata/languages/English (UK) +++ b/rtdata/languages/English (UK) @@ -708,3 +708,7 @@ HISTOGRAM_BUTTON_R;R HISTOGRAM_BUTTON_G;G HISTOGRAM_BUTTON_B;B HISTOGRAM_BUTTON_L;L + +### + +CURVEEDITOR_NURBS;Control cage diff --git a/rtdata/languages/English (US) b/rtdata/languages/English (US) index 166886f31..f9d18e74d 100644 --- a/rtdata/languages/English (US) +++ b/rtdata/languages/English (US) @@ -710,3 +710,7 @@ HISTOGRAM_BUTTON_R;R HISTOGRAM_BUTTON_G;G HISTOGRAM_BUTTON_B;B HISTOGRAM_BUTTON_L;L + +### + +CURVEEDITOR_NURBS;Control cage diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais index 972f93746..9a2e70d8f 100644 --- a/rtdata/languages/Francais +++ b/rtdata/languages/Francais @@ -686,3 +686,7 @@ HISTOGRAM_BUTTON_B;B HISTOGRAM_BUTTON_L;L PREFERENCES_DCBENHANCE;Appliquer la phase d'amélioration de DCB PREFERENCES_DCBITERATIONS;Nombre d'itération de DCB + +### + +CURVEEDITOR_NURBS;Cage de contrôle diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 2e9d1e4df..d5cae4e78 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -1,7 +1,8 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../rtexif ${EXTRA_INCDIR} ${GTHREAD_INCLUDE_DIRS} ${GOBJECT_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS} - ${GLIBMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS}) + ${GLIBMM_INCLUDE_DIRS} ${IPTCDATA_INCLUDE_DIRS} ${LCMS_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../rtgui ${GTKMM_INCLUDE_DIRS}) link_directories (${CMAKE_CURRENT_SOURCE_DIR}/../rtexif ${EXTRA_LIBDIR} ${GTHREAD_LIBRARY_DIRS} ${GOBJECT_LIBRARY_DIRS} ${GLIB2_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} ${IPTCDATA_LIBRARY_DIRS} ${LCMS_LIBRARY_DIRS}) diff --git a/rtengine/curves.cc b/rtengine/curves.cc index db20bfd7c..2c0c206b3 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -30,14 +30,16 @@ namespace rtengine { -Curve::Curve (const std::vector& p) : x(NULL), y(NULL), ypp(NULL) { +Curve::Curve (const std::vector& p, int poly_pn) : x(NULL), y(NULL), ypp(NULL) { + + ppn = poly_pn; if (p.size()<3) { - kind = 0; + kind = Empty; } else { - kind = p[0]; - if (kind==-1 || kind==1) { + kind = (CurveType)p[0]; + if (kind==Linear || kind==Spline || kind==NURBS) { N = (p.size()-1)/2; x = new double[N]; y = new double[N]; @@ -46,12 +48,14 @@ Curve::Curve (const std::vector& p) : x(NULL), y(NULL), ypp(NULL) { x[i] = p[ix++]; y[i] = p[ix++]; } - if (kind==1) + if (kind==Spline) spline_cubic_set (); + else if (kind==NURBS && N > 2) + NURBS_set (); } - if (kind==2) { + else if (kind==Parametric) { if (p.size()!=8 && p.size()!=9) - kind = 0; + kind = Empty; else { x = new double[9]; for (int i=0; i<4; i++) @@ -65,7 +69,7 @@ Curve::Curve (const std::vector& p) : x(NULL), y(NULL), ypp(NULL) { } } } -} +} Curve::~Curve () { @@ -98,21 +102,107 @@ void Curve::spline_cubic_set () { delete [] u; } +void Curve::NURBS_set () { + + std::vector sc_x; // X sub-curve points ( XP0,XP1,XP2, XP2,XP3,XP4, ...) + std::vector sc_y; // Y sub-curve points ( YP0,YP1,YP2, YP2,YP3,YP4, ...) + std::vector sc_length; // Length of the subcurves + double total_length=0; + + // Create the list of Bezier sub-curves + // NURBS_set is called if N > 2 only + + for (int i = 0; i < N-1;) { + double length; + + // first point (on the curve) + double sc_x2, sc_y2; + if (!i) { + sc_x2 = x[i]; + sc_y2 = y[i]; + i++; + } + else { + sc_x2 = (x[i-1] + x[i]) / 2.; + sc_y2 = (y[i-1] + y[i]) / 2.; + } + sc_x.push_back(sc_x2); + sc_y.push_back(sc_y2); + + // second point (control point) + sc_x.push_back(x[i]); + sc_y.push_back(y[i]); + length = sqrt(pow(x[i] - sc_x2,2) + pow(y[i] - sc_y2,2)); + i++; + + // third point (on the curve) + if (i==N-1) { + sc_x2 = x[i]; + sc_y2 = y[i]; + i++; + } + else { + sc_x2 = (x[i-1] + x[i]) / 2.; + sc_y2 = (y[i-1] + y[i]) / 2.; + } + sc_x.push_back(sc_x2); + sc_y.push_back(sc_y2); + length += sqrt(pow(x[i] - sc_x2,2) + pow(y[i] - sc_y2,2)); + + // Storing the length of all sub-curves and the total length (to have a better distribution + // of the points along the curve) + sc_length.push_back(length); + total_length += length; + } + sc_x.begin(); + sc_y.begin(); + sc_length.begin(); + + // create the polyline with the number of points adapted to the X range of the sub-curve + for (int i=0; i < sc_x.size(); i+=3) { + // TODO: Speeding-up the interface by caching the polyline, instead of rebuilding it at each action on sliders !!! + int nbr_points = (int)(((double)ppn+N-2) * sc_length[i/3] / total_length) + (i==0 ? 1 : 0); + + // increment along the curve, not along the X axis + double increment = 1.0 / (double)(nbr_points-1); + if (!i) { + poly_x.push_back(sc_x[i]); + poly_y.push_back(sc_y[i]); + } + for (int j=1; jx[N-1]) return y[N-1]; else if (t 1){ int k = (k_hi + k_lo) / 2; @@ -148,23 +241,49 @@ double Curve::getVal (double t) { } double h = x[k_hi] - x[k_lo]; - if (kind==-1) + // linear + if (kind==Linear) return y[k_lo] + (t - x[k_lo]) * ( y[k_hi] - y[k_lo] ) / h; - else if (kind==1) { + // spline curve + else { // if (kind==Spline) { double a = (x[k_hi] - t) / h; double b = (t - x[k_lo]) / h; double r = a*y[k_lo] + b*y[k_hi] + ((a*a*a - a)*ypp[k_lo] + (b*b*b - b)*ypp[k_hi]) * (h*h)/6.0; - if (r < 0.0) return 0.0; - if (r > 1.0) return 1.0; - return r; + return CLIPD(r); } - else - return t; + break; + } + case NURBS : { + // values under and over the first and last point + if (t>x[N-1]) + return y[N-1]; + else if (t 1){ + int k = (k_hi + k_lo) / 2; + if (poly_x[k] > t) + k_hi = k; + else + k_lo = k; + } + + double h = poly_x[k_hi] - poly_x[k_lo]; + return poly_y[k_lo] + (t - poly_x[k_lo]) * ( poly_y[k_hi] - poly_y[k_lo] ) / h; + break; + } + default: + // all other (unknown) kind + return t; } } void Curve::getVal (const std::vector& t, std::vector& res) { - + // TODO!!!! can be made much faster!!! Binary search of getVal(double) at each point can be avoided res.resize (t.size()); @@ -322,7 +441,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou // create a curve if needed Curve* tcurve = NULL; if (curvePoints.size()>0 && curvePoints[0]!=0) - tcurve = new Curve (curvePoints); + tcurve = new Curve (curvePoints, CURVES_MIN_POLY_POINTS/skip); // clear array that stores histogram valid before applying the custom curve if (outBeforeCCurveHistogram) @@ -330,7 +449,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou for (int i=0; i<=0xffff; i+= i<0xffff-skip ? skip : 1 ) { - // change to [0,1] rage + // change to [0,1] range double val = (double)i / 65535.0; // apply default multiplier (that is >1 if highlight recovery is on) @@ -346,7 +465,7 @@ void CurveFactory::complexCurve (double ecomp, double black, double hlcompr, dou // apply brightness curve val = brightness (val, br/100.0); - // apply custom/parametric curve, if any + // apply custom/parametric/NURBS curve, if any if (tcurve) { if (outBeforeCCurveHistogram) { double hval = val; diff --git a/rtengine/curves.h b/rtengine/curves.h index 0d6d6753c..59b3a326d 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -23,6 +23,9 @@ #include #include #include +#include + +#define CURVES_MIN_POLY_POINTS 1000 namespace rtengine { @@ -132,22 +135,26 @@ class Curve { protected: int N; + int ppn; // targeted polyline point number double* x; double* y; + std::vector poly_x; // X points of the faceted curve + std::vector poly_y; // Y points of the faceted curve double* ypp; - int kind; // = -1: linear interp., 0: empty, 1: spline interp., 2: parametric + CurveType kind; protected: void spline_cubic_set (); + void NURBS_set (); static inline double p00 (double x, double prot) { return CurveFactory::clower (x, 2.0, prot); } static inline double p11 (double x, double prot) { return CurveFactory::cupper (x, 2.0, prot); } static inline double p01 (double x, double prot) { return x<=0.5 ? CurveFactory::clower (x*2, 2.0, prot)/2.0 : 0.5 + CurveFactory::cupper ((x-0.5)*2, 2.0, prot)/2.0; } static inline double p10 (double x, double prot) { return x<=0.5 ? CurveFactory::cupper (x*2, 2.0, prot)/2.0 : 0.5 + CurveFactory::clower ((x-0.5)*2, 2.0, prot)/2.0; } static inline double pfull (double x, double prot, double sh, double hl) { return (1-sh)*(1-hl)*p00(x,prot) + sh*hl*p11(x,prot) + (1-sh)*hl*p01(x,prot) + sh*(1-hl)*p10(x,prot); } - + public: - Curve (const std::vector& points); + Curve (const std::vector& points, int ppn=CURVES_MIN_POLY_POINTS); ~Curve (); double getVal (double x); diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 5044fc1c6..c42740678 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -24,7 +24,7 @@ extern Glib::ustring argv0; -CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), curveTypeIx(-1) { +CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), curveTypeIx(Linear) { Gtk::HBox* tsbox = Gtk::manage (new Gtk::HBox ()); Gtk::Label* tslab = Gtk::manage (new Gtk::Label (M("CURVEEDITOR_TYPE"))); @@ -40,11 +40,13 @@ CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), tsbox->pack_start (*curve_reset, Gtk::PACK_SHRINK, 0); pack_start (*tsbox); - - curveType->append_text (M("CURVEEDITOR_LINEAR")); - curveType->append_text (M("CURVEEDITOR_PARAMETRIC")); - curveType->append_text (M("CURVEEDITOR_CUSTOM")); - curveType->set_active (0); + + // Order set in the same order than "enum CurveType". Shouldn't change, for compatibility reason + curveType->append_text (M("CURVEEDITOR_LINEAR")); // 0 Linear + curveType->append_text (M("CURVEEDITOR_CUSTOM")); // 1 Spline + curveType->append_text (M("CURVEEDITOR_PARAMETRIC")); // 2 Parametric + curveType->append_text (M("CURVEEDITOR_NURBS")); // 3 NURBS + curveType->set_active (Linear); curve_reset->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::curveResetPressed) ); @@ -52,7 +54,6 @@ CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), customCurveBox = new Gtk::VBox (); Gtk::HBox* tmpa = Gtk::manage (new Gtk::HBox ()); customCurve = Gtk::manage (new MyCurve ()); - Gtk::Table* cctab = Gtk::manage (new Gtk::Table (2,1)); //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); //af->add (*customCurve); customCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); @@ -61,22 +62,51 @@ CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), tmpa->pack_start (*customCurve, true, false, 4); customCurveBox->pack_start (*tmpa, true, true,4); - Gtk::HBox* bbox = Gtk::manage (new Gtk::HBox ()); - save = Gtk::manage (new Gtk::Button ()); - save->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); - load = Gtk::manage (new Gtk::Button ()); - load->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); + Gtk::HBox* custombbox = Gtk::manage (new Gtk::HBox ()); + saveCustom = Gtk::manage (new Gtk::Button ()); + saveCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); + loadCustom = Gtk::manage (new Gtk::Button ()); + loadCustom->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); - bbox->pack_end (*save, Gtk::PACK_EXPAND_WIDGET, 4); - bbox->pack_end (*load, Gtk::PACK_EXPAND_WIDGET, 4); + custombbox->pack_end (*saveCustom, Gtk::PACK_EXPAND_WIDGET, 4); + custombbox->pack_end (*loadCustom, Gtk::PACK_EXPAND_WIDGET, 4); - customCurveBox->pack_end (*bbox, Gtk::PACK_SHRINK, 2); + customCurveBox->pack_end (*custombbox, Gtk::PACK_SHRINK, 2); customCurveBox->show_all (); - save->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::savePressed) ); - load->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::loadPressed) ); - save->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); - load->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); + saveCustom->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::savePressed) ); + loadCustom->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::loadPressed) ); + saveCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); + loadCustom->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); + + // NURBS curve + NURBSCurveBox = new Gtk::VBox (); + Gtk::HBox* tmpb = Gtk::manage (new Gtk::HBox ()); + NURBSCurve = Gtk::manage (new MyCurve ()); + //Gtk::AspectFrame* af = Gtk::manage (new Gtk::AspectFrame ("",Gtk::ALIGN_CENTER,Gtk::ALIGN_CENTER,1,false)); + //af->add (*customCurve); + NURBSCurve->set_size_request (GRAPH_SIZE+2*RADIUS, GRAPH_SIZE+2*RADIUS); + NURBSCurve->setType (NURBS); + //customCurve->set_tooltip_text (M("CURVEEDITOR_TOOLTIPMOVESPEED")); + tmpb->pack_start (*NURBSCurve, true, false, 4); + NURBSCurveBox->pack_start (*tmpb, true, true,4); + + Gtk::HBox* NURBSbbox = Gtk::manage (new Gtk::HBox ()); + saveNURBS = Gtk::manage (new Gtk::Button ()); + saveNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-save"), Gtk::ICON_SIZE_BUTTON))); + loadNURBS = Gtk::manage (new Gtk::Button ()); + loadNURBS->add (*Gtk::manage (new Gtk::Image (Gtk::StockID("gtk-open"), Gtk::ICON_SIZE_BUTTON))); + + NURBSbbox->pack_end (*saveNURBS, Gtk::PACK_EXPAND_WIDGET, 4); + NURBSbbox->pack_end (*loadNURBS, Gtk::PACK_EXPAND_WIDGET, 4); + + NURBSCurveBox->pack_end (*NURBSbbox, Gtk::PACK_SHRINK, 2); + NURBSCurveBox->show_all (); + + saveNURBS->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::savePressed) ); + loadNURBS->signal_clicked().connect( sigc::mem_fun(*this, &CurveEditor::loadPressed) ); + saveNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPSAVE")); + loadNURBS->set_tooltip_text (M("CURVEEDITOR_TOOLTIPLOAD")); // parametric curve paramCurveBox = new Gtk::VBox (); @@ -92,10 +122,10 @@ CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), paramctab->attach (*paramCurve, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 4, 4); paramctab->attach (*shcSelector, 0, 1, 1, 2, Gtk::FILL, Gtk::SHRINK, RADIUS+4, 0); - Gtk::HBox* tmpb = Gtk::manage (new Gtk::HBox ()); - tmpb->pack_start (*paramctab, true, false); + Gtk::HBox* tmpc = Gtk::manage (new Gtk::HBox ()); + tmpc->pack_start (*paramctab, true, false); - paramCurveBox->pack_start (*tmpb, true, true); + paramCurveBox->pack_start (*tmpc, true, true); highlights = Gtk::manage (new Adjuster (M("CURVEEDITOR_HIGHLIGHTS"), -100, 100, 1, 0)); lights = Gtk::manage (new Adjuster (M("CURVEEDITOR_LIGHTS"), -100, 100, 1, 0)); @@ -123,6 +153,7 @@ CurveEditor::CurveEditor () : cl(NULL), activeParamControl(-1), realized(false), paramCurveBox->reference (); customCurve->setCurveListener (this); + NURBSCurve->setCurveListener (this); paramCurve->setCurveListener (this); shcSelector->setSHCListener (this); @@ -196,11 +227,26 @@ void CurveEditor::savePressed () { std::ofstream f (fname.c_str()); std::vector p = customCurve->getPoints (); + + switch (curveType->get_active_row_number()) { + case Spline: // custom + p = customCurve->getPoints (); + break; + case NURBS: // NURBS + p = NURBSCurve->getPoints (); + break; + default: + break; + } + int ix = 0; - if (p[ix++]<0) + if (p[ix]==(double)(Linear)) f << "Linear\n"; - else + else if (p[ix]==(double)(Spline)) f << "Spline\n"; + else if (p[ix]==(double)(NURBS)) + f << "NURBS\n"; + ix++; for (int i=0; i> s; if (s=="Linear") - p.push_back (-1); + p.push_back ((double)(Linear)); else if (s=="Spline") - p.push_back (1); + p.push_back ((double)(Spline)); + else if (s=="NURBS") + p.push_back ((double)(NURBS)); else return; double x; while (f) { @@ -243,9 +291,16 @@ void CurveEditor::loadPressed () { if (f) p.push_back (x); } - customCurve->setPoints (p); - customCurve->queue_draw (); - customCurve->notifyListener (); + if (p[0] == (double)(Spline)) { + customCurve->setPoints (p); + customCurve->queue_draw (); + customCurve->notifyListener (); + } + else if (p[0] == (double)(NURBS)) { + NURBSCurve->setPoints (p); + NURBSCurve->queue_draw (); + NURBSCurve->notifyListener (); + } } } } @@ -261,21 +316,21 @@ void CurveEditor::setCurve (const std::vector& c) { tmpCurve = c; - if (realized && curveType->get_active_row_number()<3) { // if it is not realized or "unchanged" is selected, just store the curve (prev line) and do not change gui - + if (realized && curveType->get_active_row_number()<=Unchanged) { // if it is not realized or "unchanged" is selected, just store the curve (prev line) and do not change gui + typeconn.block(true); - if (c.size()==0 || c[0]==0) { - curveType->set_active (0); - curveTypeIx = 0; + if (c.size()==0 || c[0]==(double)(Linear)) { + curveType->set_active (Linear); // Change the combo selection + curveTypeIx = Linear; } - else if (c[0]==1) { - curveType->set_active (2); - curveTypeIx = 2; + else if (c[0]==(double)(Spline)) { + curveType->set_active (Spline); + curveTypeIx = Spline; customCurve->setPoints (c); } - else if (c[0]==2) { - curveType->set_active (1); - curveTypeIx = 1; + else if (c[0]==(double)(Parametric)) { + curveType->set_active (Parametric); + curveTypeIx = Parametric; paramCurve->setPoints (c); shcSelector->setPositions (c[1], c[2], c[3]); highlights->setValue (c[4]); @@ -283,13 +338,21 @@ void CurveEditor::setCurve (const std::vector& c) { darks->setValue (c[6]); shadows->setValue (c[7]); } + else if (c[0]==(double)(NURBS)) { + curveType->set_active (NURBS); + curveTypeIx = NURBS; + NURBSCurve->setPoints (c); + } removeIfThere (this, customCurveBox, false); removeIfThere (this, paramCurveBox, false); + removeIfThere (this, NURBSCurveBox, false); - if (curveType->get_active_row_number()==1) - pack_start (*paramCurveBox); - else if (curveType->get_active_row_number()==2) + if (curveType->get_active_row_number()==Spline) pack_start (*customCurveBox); + else if (curveType->get_active_row_number()==Parametric) + pack_start (*paramCurveBox); + else if (curveType->get_active_row_number()==NURBS) + pack_start (*NURBSCurveBox); typeconn.block(false); } @@ -297,17 +360,19 @@ void CurveEditor::setCurve (const std::vector& c) { std::vector CurveEditor::getCurve () { - if (!realized || curveType->get_active_row_number()==3) + if (!realized || curveType->get_active_row_number()==Unchanged) return tmpCurve; - if (curveTypeIx<=0) { + // linear + if (curveTypeIx<=Linear) { std::vector lcurve (1); - lcurve[0] = 0.0; + lcurve[0] = (double)(Linear); return lcurve; } - else if (curveTypeIx==1) { + // parametric + else if (curveTypeIx==Parametric) { std::vector lcurve (8); - lcurve[0] = 2.0; + lcurve[0] = (double)(Parametric); shcSelector->getPositions (lcurve[1], lcurve[2], lcurve[3]); lcurve[4] = highlights->getValue (); lcurve[5] = lights->getValue (); @@ -315,23 +380,33 @@ std::vector CurveEditor::getCurve () { lcurve[7] = shadows->getValue (); return lcurve; } - else if (curveTypeIx==2) + // spline (custom) + else if (curveTypeIx==Spline) return customCurve->getPoints (); + // NURBS (control cage) + else if (curveTypeIx==NURBS) + return NURBSCurve->getPoints (); } void CurveEditor::typeSelectionChanged () { removeIfThere (this, customCurveBox, false); removeIfThere (this, paramCurveBox, false); + removeIfThere (this, NURBSCurveBox, false); - if (curveType->get_active_row_number()==1) - pack_start (*paramCurveBox); - else if (curveType->get_active_row_number()==2) + if (curveType->get_active_row_number()==Spline) { pack_start (*customCurveBox); + } + else if (curveType->get_active_row_number()==Parametric) { + pack_start (*paramCurveBox); + } + else if (curveType->get_active_row_number()==NURBS) { + pack_start (*NURBSCurveBox); + } + + if (curveType->get_active_row_number() < Unchanged) + curveTypeIx = (CurveType)curveType->get_active_row_number(); - if (curveType->get_active_row_number()<3) - curveTypeIx = curveType->get_active_row_number(); - curveChanged (); } @@ -343,10 +418,13 @@ void CurveEditor::curveChanged () { void CurveEditor::curveResetPressed () { switch (curveTypeIx) { - case 2 : // Custom + case NURBS : // = Control cage + NURBSCurve->reset (); + break; + case Spline : // = Custom customCurve->reset (); break; - case 1 : // Parametric + case Parametric : highlights->resetPressed(); lights->resetPressed(); darks->resetPressed(); @@ -399,7 +477,7 @@ void CurveEditor::setBatchMode (bool batchMode) { bool CurveEditor::isUnChanged () { - return curveType->get_active_row_number()==3; + return curveType->get_active_row_number()==Unchanged; } void CurveEditor::setUnChanged (bool uc) { @@ -408,7 +486,8 @@ void CurveEditor::setUnChanged (bool uc) { typeconn.block(true); removeIfThere (this, customCurveBox, false); removeIfThere (this, paramCurveBox, false); - curveType->set_active (3); + removeIfThere (this, NURBSCurveBox, false); + curveType->set_active (Unchanged); typeconn.block(false); } else { @@ -423,4 +502,5 @@ void CurveEditor::updateBackgroundHistogram (unsigned int* hist) { paramCurve->updateBackgroundHistogram (hist); customCurve->updateBackgroundHistogram (hist); + NURBSCurve->updateBackgroundHistogram (hist); } diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index d989e4929..bb9cd3638 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -30,27 +30,31 @@ class CurveEditor : public Gtk::VBox, public CurveListener, public SHCListener, Gtk::Button* curve_reset; Gtk::VBox* paramCurveBox; Gtk::VBox* customCurveBox; + Gtk::VBox* NURBSCurveBox; MyCurve* customCurve; + MyCurve* NURBSCurve; MyCurve* paramCurve; SHCSelector* shcSelector; - + Adjuster* highlights; Adjuster* lights; Adjuster* darks; Adjuster* shadows; - - Gtk::Button* save; - Gtk::Button* load; - + + Gtk::Button* saveCustom; + Gtk::Button* loadCustom; + Gtk::Button* saveNURBS; + Gtk::Button* loadNURBS; + CurveListener* cl; - + bool realized; std::vector tmpCurve; - int curveTypeIx; - + CurveType curveTypeIx; + int activeParamControl; - + sigc::connection typeconn; public: diff --git a/rtgui/mycurve.cc b/rtgui/mycurve.cc index e7baafdeb..a08988d2d 100644 --- a/rtgui/mycurve.cc +++ b/rtgui/mycurve.cc @@ -56,9 +56,9 @@ std::vector MyCurve::get_vector (int veclen) { std::vector vector; vector.resize (veclen); - + if (curve.type != Parametric) { - // count active points: + // count active points: double prev =- 1.0; int active = 0; int firstact = -1; @@ -69,7 +69,7 @@ std::vector MyCurve::get_vector (int veclen) { prev = curve.x[i]; ++active; } - // handle degenerate case: + // handle degenerate case: if (active < 2) { double ry; if (active > 0) @@ -86,7 +86,7 @@ std::vector MyCurve::get_vector (int veclen) { // calculate remaining points std::vector curveDescr = getPoints (); - rtengine::Curve* rtcurve = new rtengine::Curve (curveDescr); + rtengine::Curve* rtcurve = new rtengine::Curve (curveDescr, veclen*1.5); std::vector t; t.resize (veclen); for (int i = 0; i < veclen; i++) @@ -211,6 +211,25 @@ void MyCurve::draw (int width, int height, int handle) { cr->fill (); } + // draw the cage of the NURBS curve + if (curve.type==NURBS) { + 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 < points.size(); ) { + double x = ((width-1) * points[i++] + 0.5)+RADIUS; // project (curve.x[i], 0, 1, width); + double y = height - ((height-1) * points[i++] + 0.5)+RADIUS; // project (curve.y[i], 0, 1, height); + if (i==3) + cr->move_to (x, y); + else + cr->line_to (x, y); + } + cr->stroke (); + cr->unset_dash (); + } + // draw curve cr->set_source_rgb (0.0, 0.0, 0.0); cr->move_to (point[0].get_x(), point[0].get_y()); @@ -258,17 +277,26 @@ bool MyCurve::handleEvents (GdkEvent* event) { int x = CLAMP ((tx - RADIUS), 0, width-1); // X position of the pointer from the origin of the graph int y = height-1 - CLAMP ((ty - RADIUS), 0, height-1); // Y position of the pointer from the origin of the graph - unsigned int distance = ~0U; + unsigned int distance_x = ~0U, distance_y = ~0U; int num = curve.x.size(); int closest_point = -1; if (curve.type!=Parametric) { for (int i = 0; i < num; ++i) { int cx = (int)((width-1) * curve.x[i] + 0.5); //project (c->ctlpoint[i][0], min_x, c->max_x, width); - if ((unsigned int) abs (x - cx) < distance) { - distance = abs (x - cx); + int cy = (int)((height-1) * curve.y[i] + 0.5); //project (c->ctlpoint[i][0], min_x, c->max_x, width); + unsigned int curr_dist_x = abs (x - cx); + unsigned int curr_dist_y = abs (y - cy); + if (curr_dist_x < distance_x) { + distance_x = curr_dist_x; + distance_y = curr_dist_y; closest_point = i; } + else if (curr_dist_x == distance_x && curr_dist_y < distance_y) { + // there is mode than 1 point for that X coordinate, we select the point closest to the cursor + distance_y = curr_dist_y; + closest_point = i; + } } } @@ -295,7 +323,7 @@ bool MyCurve::handleEvents (GdkEvent* event) { rt_display->get_pointer(cursor_x, cursor_y, mod_type); new_type = CSEmpty; - if (distance > MIN_DISTANCE) { + if (distance_x > MIN_DISTANCE) { /* insert a new control point */ if (num > 0) { int cx = (int)((width-1)*curve.x[closest_point]+0.5); @@ -350,7 +378,7 @@ bool MyCurve::handleEvents (GdkEvent* event) { draw (width, height, lit_point); } } - if (distance <= MIN_DISTANCE) { + if (distance_x <= MIN_DISTANCE) { new_type = CSMove; lit_point = closest_point; } @@ -376,11 +404,11 @@ bool MyCurve::handleEvents (GdkEvent* event) { case Gdk::MOTION_NOTIFY: mevent = (GdkEventMotion *) event; - if (curve.type == Linear || curve.type == Spline) { + if (curve.type == Linear || curve.type == Spline || curve.type == NURBS) { if (grab_point == -1) { int previous_lit_point = lit_point; /* if no point is grabbed... */ - if (distance <= MIN_DISTANCE) { + if (distance_x <= MIN_DISTANCE) { new_type = CSMove; lit_point = closest_point; } @@ -501,15 +529,19 @@ std::vector MyCurve::getPoints () { std::vector result; if (curve.type==Parametric) { - result.push_back (+2.0); + result.push_back ((double)(Parametric)); for (int i=0; i=0) { result.push_back (curve.x[i]); @@ -522,19 +554,15 @@ std::vector MyCurve::getPoints () { void MyCurve::setPoints (const std::vector& p) { int ix = 0; - int t = p[ix++]; - if (t==2) { - curve.type = Parametric; + CurveType t = (CurveType)p[ix++]; + curve.type = t; + if (t==Parametric) { curve.x.clear (); curve.y.clear (); for (int i=1; i