From 293f1a4a0a6151fe7eb01d77ab21d5c3fd29d401 Mon Sep 17 00:00:00 2001 From: Ansgar Philippsen <ansgar.philippsen@gmail.com> Date: Fri, 6 Jul 2012 18:34:26 -0400 Subject: [PATCH] added hsv mode to gradient --- modules/gfx/pymod/export_gradient.cc | 24 ++++++++++- modules/gfx/pymod/export_primlist.cc | 4 +- modules/gfx/pymod/gradients.xml | 9 +++++ modules/gfx/src/gradient.cc | 60 +++++++++++++++++++++------- modules/gfx/src/gradient.hh | 54 ++++++++++++++++++------- 5 files changed, 119 insertions(+), 32 deletions(-) diff --git a/modules/gfx/pymod/export_gradient.cc b/modules/gfx/pymod/export_gradient.cc index 6cb26cee0..483249538 100644 --- a/modules/gfx/pymod/export_gradient.cc +++ b/modules/gfx/pymod/export_gradient.cc @@ -16,6 +16,11 @@ // along with this library; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //------------------------------------------------------------------------------ + +/* + Authors: Marco Biasini, Ansgar Philippsen +*/ + #include <boost/python.hpp> #include <boost/python/suite/indexing/vector_indexing_suite.hpp> using namespace boost::python; @@ -76,15 +81,27 @@ namespace { throw std::runtime_error("expected values of gfx.Color or float triplets"); } try { - col=gfx::Color(extract<float>(val[0]),extract<float>(val[1]),extract<float>(val[2])); + col=gfx::RGB(extract<float>(val[0]),extract<float>(val[1]),extract<float>(val[2])); } catch (...) { - throw std::runtime_error("expected values of gfx.Color or float triplets"); + throw std::runtime_error("expected values of gfx.Color float triplets"); } } grad->SetColorAt(mark,col); } return grad.release(); } + + std::string sl_repr(const Gradient::StopList& sl) { + std::ostringstream m; + m << "["; + for(size_t i=0;i<sl.size();++i) { + Color c = sl[i].color; + m << "(" << sl[i].t << "," << "gfx.RGB(" << c[0] << "," << c[1] << "," << c[2] << "))"; + if(i<sl.size()-1) m << ","; + } + m << "]"; + return m.str(); + } } void export_gradient() @@ -96,13 +113,16 @@ void export_gradient() .def("SetColorAt", &Gradient::SetColorAt) .def("GetColorAt", &Gradient::GetColorAt) .def("GetStops", &Gradient::GetStops) + .add_property("stops", &Gradient::GetStops) .def("GradientToInfo", &Gradient::GradientToInfo) .def("GradientFromInfo", &Gradient::GradientFromInfo).staticmethod("GradientFromInfo") + .add_property("hsv_mode",&Gradient::GetHSVMode,&Gradient::SetHSVMode) ; implicitly_convertible<String, Gradient>(); class_<Gradient::StopList>("GradientStopList", init<>()) .def(vector_indexing_suite<Gradient::StopList>()) + .def("__repr__",sl_repr) ; class_<Gradient::Stop>("GradientStop", init<>()) diff --git a/modules/gfx/pymod/export_primlist.cc b/modules/gfx/pymod/export_primlist.cc index bb986d7e1..0d6d2bb16 100644 --- a/modules/gfx/pymod/export_primlist.cc +++ b/modules/gfx/pymod/export_primlist.cc @@ -62,7 +62,7 @@ namespace { if(!PyArray_TYPE(na)==NPY_FLOAT) { throw std::runtime_error("expected normal array to be of dtype=float32"); } - if(PyArray_SIZE(na)!=v_size) { + if((size_t)PyArray_SIZE(na)!=v_size) { throw std::runtime_error("expected normal array size to match vertex array size"); } np=reinterpret_cast<float*>(PyArray_DATA(na)); @@ -78,7 +78,7 @@ namespace { if(!PyArray_TYPE(ca)==NPY_FLOAT) { throw std::runtime_error("expected color array to be of dtype=float32"); } - if(PyArray_SIZE(ca)!=v_count*4) { + if((size_t)PyArray_SIZE(ca)!=v_count*4) { throw std::runtime_error("expected color array size to equal vertex-count x 4"); } cp=reinterpret_cast<float*>(PyArray_DATA(ca)); diff --git a/modules/gfx/pymod/gradients.xml b/modules/gfx/pymod/gradients.xml index dc3e03fd2..e3e1fbd0c 100644 --- a/modules/gfx/pymod/gradients.xml +++ b/modules/gfx/pymod/gradients.xml @@ -17,6 +17,11 @@ 0.0615451 0.0313726 0.286275 0.501961 1 0.502146 1 1 1 1 0.978541 0.890105 0.075639 0.0546578 1 +</Gradient> + <Gradient hsv_color="1" hsv_mode="1" Name="HEAT_MAP_HSV" >3 +0.0615451 0.576389 0.9375 0.501961 1 +0.502146 0 0 1 1 +0.978541 0.00418562 0.938594 0.890105 1 </Gradient> <Gradient Name="AUTUMN" >3 0.0615451 1 0.972015 0.0132753 1 @@ -61,6 +66,10 @@ 0.748835 0 1 1 1 0.857779 0 0.623529 1 1 0.97541 0 0.133333 1 1 +</Gradient> +<Gradient hsv_color="1" hsv_mode="1" Name="RED_TO_BLUE_HSV">2 +0.0 1 1 1 1 +1.0 0.66666667 1 1 1 </Gradient> </Gradients> </EMDataInfo> diff --git a/modules/gfx/src/gradient.cc b/modules/gfx/src/gradient.cc index 3414135af..c65261b16 100644 --- a/modules/gfx/src/gradient.cc +++ b/modules/gfx/src/gradient.cc @@ -19,6 +19,9 @@ #include "gradient.hh" #include <iostream> #include <vector> +#include <algorithm> + +#include <ost/log.hh> #include <ost/info/info.hh> #include <ost/info/info_fw.hh> @@ -28,13 +31,18 @@ namespace ost { namespace gfx { -Gradient::Gradient() -{ -} +Gradient::Gradient(): + stops_(), + hsv_mode_(false) +{} Gradient::Gradient(const String& name) { Gradient gradient = GradientManager::Instance().GetGradient(name); + // why doesn't this work: + // stops_=gradient.stops_ + // or even better + // *this = gradient StopList stops = gradient.GetStops(); for(unsigned int i = 0; i < stops.size(); i++){ Stop stop = stops[i]; @@ -55,8 +63,11 @@ void Gradient::SetColorAt(float t, const Color& c) Color Gradient::GetColorAt(float t) const { - if (stops_.empty()) - throw Error("Can not calculate color with 0 Stops set!"); + if (stops_.empty()) { + return Color(); + } else if(stops_.size()==1) { + return stops_[0].color; + } uint c=0; while (t>=stops_[c].t && c<stops_.size()) { @@ -64,16 +75,17 @@ Color Gradient::GetColorAt(float t) const } if (c==0) { return stops_.front().color; - } - if (c==stops_.size()) { + } else if (c==stops_.size()) { return stops_.back().color; } - float d=stops_[c].t-stops_[c-1].t; - float tt=0.0; - if (d>0.0001) { - tt=(t-stops_[c-1].t)/d; - } - return stops_[c-1].color*(1.0-tt)+stops_[c].color*tt; + float d = stops_[c].t-stops_[c-1].t; + float tt = d>0.001 ? (t-stops_[c-1].t)/d : 0.0; + if(hsv_mode_) { + geom::Vec4 v=stops_[c-1].color.GetHSVA()*(1.0-tt)+stops_[c].color.GetHSVA()*tt; + return HSVA(v[0],v[1],v[2],v[3]); + } else { + return stops_[c-1].color*(1.0-tt)+stops_[c].color*tt; + } } void Gradient::GradientToInfo(info::InfoGroup& group) const @@ -86,6 +98,7 @@ void Gradient::GradientToInfo(info::InfoGroup& group) const ss << stops_[i].t << "\t" << stops_[i].color.Red() << "\t" << stops_[i].color.Green() << "\t" << stops_[i].color.Blue() << "\t" << stops_[i].color.Alpha() << "\n"; } group.SetTextData(ss.str()); + group.SetAttribute("hsv_mode",hsv_mode_? "1" : "0"); } Gradient::StopList Gradient::GetStops() const{ @@ -95,6 +108,16 @@ Gradient::StopList Gradient::GetStops() const{ Gradient Gradient::GradientFromInfo(const info::InfoGroup& group) { std::istringstream ss(group.GetTextData()); + bool hsv_color=false; + if(group.HasAttribute("hsv_color")) { + String hsv_color_s = group.GetAttribute("hsv_color"); + std::transform(hsv_color_s.begin(), hsv_color_s.end(), hsv_color_s.begin(), ::tolower); + if(hsv_color_s=="0" || hsv_color_s=="false" || hsv_color_s=="no" || hsv_color_s=="off") { + hsv_color=false; + } else { + hsv_color=true; + } + } Gradient grad; StopList::size_type size = 0; @@ -102,10 +125,19 @@ Gradient Gradient::GradientFromInfo(const info::InfoGroup& group) float t, r, g, b, a; for( StopList::size_type i = 0; i < size; i++ ) { ss >> t >> r >> g >> b >> a; - Color c = Color(r,g,b,a); + Color c = hsv_color ? HSVA(r,g,b,a) : RGBA(r,g,b,a); grad.SetColorAt(t, c); } + if(group.HasAttribute("hsv_mode")) { + String hsv_mode_s = group.GetAttribute("hsv_mode"); + std::transform(hsv_mode_s.begin(), hsv_mode_s.end(), hsv_mode_s.begin(), ::tolower); + if(hsv_mode_s=="0" || hsv_mode_s=="false" || hsv_mode_s=="no" || hsv_mode_s=="off") { + grad.SetHSVMode(false); + } else { + grad.SetHSVMode(true); + } + } return grad; } diff --git a/modules/gfx/src/gradient.hh b/modules/gfx/src/gradient.hh index 0b220faf0..e08a24eda 100644 --- a/modules/gfx/src/gradient.hh +++ b/modules/gfx/src/gradient.hh @@ -20,8 +20,8 @@ #define OST_GFX_GRADIENT_HH /* - Author: Marco Biasini - */ + Authors: Marco Biasini, Ansgar Philippsen +*/ #include <vector> @@ -30,16 +30,32 @@ #include <ost/info/info.hh> #include <ost/info/info_fw.hh> - namespace ost { namespace gfx { -/// \brief color gradient -/// -/// Gradients map a scalar value in the range of 0 to 1 to a color. The -/// gradient consists of zero or more color stops. These stop points define the -/// output of GetColorAt(). New stops can be added with SetColorAt(). -/// -/// \sa \ref gradient.py "Gradient Example" +/*! + \brief color gradient + + Gradients map a scalar value in the range of 0 to 1 to a color. The + gradient consists of zero or more color stops. These stop points define the + output of GetColorAt(). New stops can be added with SetColorAt(). + + Usually, the gradient operates in RGB colorspace, which means a linear + interpolation of each color component between stops. As an alternative, + interpolation can also be done in HSV colorspace, by setting HSV mode + to true with SetHSVMode(). However, carefully using HSV colorspace will + yield better results, if some potential caveats are taken into consideration. + The best example is a simple gradient from red to blue. The half-point in RGB + colorspace is (0.5,0.0,0.5), so a darkish purple. Converting to HSV one can see + that the saturation is at 50%, which is unfortunate. Using hsv mode can circument + this issue, but in the above example, naively using hsv mode will give green + at the half-point and not purple. Why? Because red translates to HSV(0,1,1) and blue to + HSV(2/3,1,1), which at the half-point is HSV(1/3,1,1), or green. To interpolate + on the hue in the other direction, use HSV and unwrapped hue values, e.g. + [HSV(0.0,1,1),HSV(-1/3,1,1)] or [HSV(1,1,1),HSV(2/3,1,1)] - both of these will + give a half-point or a nice purple with full saturation + + \sa \ref gradient.py "Gradient Example" +*/ class DLLEXPORT_OST_GFX Gradient { public: struct Stop { @@ -49,14 +65,14 @@ public: float t; Color color; - Color GetColor(){ + Color GetColor() const { return color; } - float GetRel(){ + float GetRel() const { return t; } - + bool operator==(const Stop& ref) const { return t==ref.t && color==ref.color; @@ -85,16 +101,26 @@ public: void SetColorAt(float t, const Color& color); /// \brief convert gradient to info + // TODO: rename to ToInfo + // TODO:: add stand-alone function named GradientToInfo(Gradient, InfoGroup) void GradientToInfo(info::InfoGroup& group) const; /// \brief get list of color stops StopList GetStops() const; /// \brief create gradient from info + // TODO: move outside + // TODO: add FromInfo member function that overwrites this static gfx::Gradient GradientFromInfo(const info::InfoGroup& group); + + /// \brief turn HSV interpolation mode on or off + void SetHSVMode(bool m) {hsv_mode_=m;} + /// \brief return the HSV interpolation mode flag + bool GetHSVMode() const {return hsv_mode_;} + private: std::vector<Stop> stops_; - + bool hsv_mode_; }; -- GitLab