#ifndef MODGMT_STRUCT_H #define MODGMT_STRUCT_H #include #include #include #include "common.h" #include "modgmt_colornames.h" // Helper classes for conversion of doubles from strings with varios checks // We use rational representation of floating point number, because double type values not allowed as template parameter template class PMin { public: double operator()(double v, bool* suc) const {if(v(num)/denum) *suc=false; return v;} }; template class PMax { public: double operator()(double v, bool* suc) const {if(v>static_cast(num)/denum) *suc=false; return v;} }; class PFromValue { double d; bool ok; public: PFromValue(const std::string& s) {ok=str2double(s,&d);} PFromValue(double s):d(s),ok(true) {} double operator()(bool* suc) const { if(!ok) *suc=false; return d; } }; // Main definition, never instantiated template class GetDouble; // Recursive definition template class GetDouble: public GetDouble { Policy p; public: GetDouble(GetDouble&&) = delete; GetDouble(GetDouble&) = delete; template GetDouble(Args... args):GetDouble(args...) {}; double operator()(bool* suc) const {return p(GetDouble::operator()(suc),suc);} }; // Special case for PFromString policy. This policy mast be last in policies list. template<> class GetDouble { PFromValue p; public: GetDouble(GetDouble&&) = delete; GetDouble(GetDouble&) = delete; GetDouble() = delete; GetDouble(const std::string& str):p(str) {}; GetDouble(double d):p(d) {}; double operator()(bool* suc) const {return p(suc);} }; // Coordinate struct gmt_coord { bool isdeg; union { double r; struct { bool sign; uint16_t d; uint8_t m; double s; }; }; std::string Value() const { if(!isdeg) return std::to_string(r); else return (sign?"":"-")+std::to_string(d)+":"+std::to_string(m)+(s==0?"":":"+std::to_string(s)); } operator double() const { if(isdeg) return (d+(m+s/60.0)/60.0)*(sign?1:-1); else return r; } bool Convert(const std::string& str) { WordList wl; WordList::const_iterator cw; wl=Split(str,":",true); if(1==wl.size()) // No dd:mm { isdeg=false; return str2double(str,&r); } if(0==wl.size() || wl.size()>3) return false; isdeg=true; int64_t res; // degrees cw=wl.begin(); if(!str2int(*cw,&res)) return false; if(res>360 || res<-360) res%=360; sign=(std::string::npos==cw->find('-')); d=static_cast(sign?res:-res); // minutes cw++; if(!str2int(*cw,&res)) return false; if(res<0 || res>=60) return false; m=static_cast(res); s=0; // seconds cw++; if(wl.end()==cw) return true; // No seconds if(!str2double(*cw,&s) ) return false; if(s<0.0 || s>=60.0) return false; return true; } bool Convert(double num) { isdeg=false; r=num; return true; } }; // Region struct gmt_region { enum Type {NORMAL,BBOX,GLOBAL360,GLOBAL180}; Type type; struct gmt_coord xb,xe,yb,ye; std::string Value() const { switch(type) { case(NORMAL): return std::string("-R")+xb.Value()+"/"+xe.Value()+"/"+yb.Value()+"/"+ye.Value(); case(BBOX): return std::string("-R")+xb.Value()+"/"+yb.Value()+"/"+xe.Value()+"/"+ye.Value()+"r"; case(GLOBAL360): return "-Rg"; case(GLOBAL180): return "-Rd"; } return ""; } // Only "global", "global180" and "global360" // TODO: add parsing of "-R" strings bool Convert(const std::string& istr) { std::string str=istr; tolower(str); if("global180"==str) { type=GLOBAL180; xb.Convert(-180.0); xe.Convert(180.0); yb.Convert(-90.0); ye.Convert(90.0); return true; } if("global360"==str || "global"==str) { type=GLOBAL360; xb.Convert(0.0); xe.Convert(360.0); yb.Convert(-90.0); ye.Convert(90.0); return true; } return false; } // Make by coordinates bool Convert(struct gmt_coord ixb, struct gmt_coord ixe, struct gmt_coord iyb, struct gmt_coord iye, bool isbbox=false) { type=isbbox?BBOX:NORMAL; xb=ixb; yb=iyb; xe=ixe; ye=iye; return true; } }; // Projection struct gmt_projection { // OBLIQMERCATOR types enum class OType {NOTDEF,A,B,C}; // No UTM (-Ju) enum projection {NOTDEF,XY,CYL_EQU,MERCATOR,TRANSMERCATOR,OBLIQMERCATOR,CASSINI,CYL_EQA,MILLER,CYL_STERE}; // Real size in cm of drawing area double rwidth,rheight; struct gmt_region region; projection proj; double width; // parameter of projection static const double default_width; union // other projection parameters { // XY struct {double height;} x; // Cylindrical projections // CYL_EQU (Cylindrical Equidistant -Jq) struct {struct gmt_coord stpar,cmer;} q; // MERCATOR (-Jm) struct {struct gmt_coord stpar,cmer;} m; // TRANSMERCATOR (-Jt) struct {struct gmt_coord cmer,orlat; double scale;} t; // OBLIQMERCATOR (-Jo) struct { OType type; struct gmt_coord clon,clat; union { struct gmt_coord azimuth; // A struct {struct gmt_coord eqlon,eqlat;}; // B struct {struct gmt_coord polelon,polelat;}; // C }; } o; // CASSINI (-Jc) struct {struct gmt_coord clon,clat;} c; // CYL_EQA (Cylindrical equal-area -Jy) struct {struct gmt_coord stpar,cmer;} y; // MILLER (-Jj) struct {struct gmt_coord cmer;} j; // CYL_STERE (Cylindrical stereographic -Jcyl_stere) struct {struct gmt_coord stpar,cmer;} cyl_stere; }; std::string Value() const { std::string ret; switch(proj) { case(XY): {ret="-JX"+std::to_string(width)+"c/"+std::to_string(x.height)+"c"; break;} case(CYL_EQU): {ret="-JQ"+q.cmer.Value()+"/"+q.stpar.Value()+"/"+std::to_string(width)+"c"; break;} case(MERCATOR): {ret="-JM"+m.cmer.Value()+"/"+m.stpar.Value()+"/"+std::to_string(width)+"c"; break;} case(TRANSMERCATOR): {ret="-JT"+t.cmer.Value()+"/"+t.orlat.Value()+"/"+std::to_string(width)+"c --PROJ_SCALE_FACTOR="+std::to_string(t.scale); break;} case(OBLIQMERCATOR): { switch(o.type) { case(OType::A): {ret="-JOa"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.azimuth.Value()+"/"+std::to_string(width)+"c"; break;} case(OType::B): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.eqlon.Value()+"/"+o.eqlat.Value()+"/"+std::to_string(width)+"c"; break;} case(OType::C): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.polelon.Value()+"/"+o.polelat.Value()+"/"+std::to_string(width)+"c"; break;} default: return ""; } } case(CASSINI): {ret="-JC"+c.clon.Value()+"/"+c.clat.Value()+"/"+std::to_string(width)+"c"; break;} case(CYL_EQA): {ret="-JY"+y.stpar.Value()+"/"+y.cmer.Value()+"/"+std::to_string(width)+"c"; break;} case(MILLER): {ret="-JJ"+j.cmer.Value()+"/"+std::to_string(width)+"c"; break;} case(CYL_STERE): {ret="-JCyl_stere"+cyl_stere.stpar.Value()+"/"+cyl_stere.cmer.Value()+"/"+std::to_string(width)+"c"; break;} default: return ""; } ret+=" "+region.Value(); return ret; } bool SetType(const std::string& s) { proj=NOTDEF; std::string str=s; tolower(str); if(projnames.end()==projnames.find(str)) return false; proj=projnames[str]; return true; } static void FillProjNames(); private: static std::map projnames; }; // Helper types for colors typedef GetDouble,PMax<255>,PFromValue> Value2RGB; typedef GetDouble,PMax<360>,PFromValue> Value2Hue; typedef GetDouble,PMax<1>,PFromValue> Value2SV; typedef GetDouble,PMax<100>,PFromValue> Value2CMYK; typedef GetDouble,PMax<100>,PFromValue> Value2Transp; // Color struct gmt_color { public: enum ColorModel {RGB,GRAY,HSV,CMYK}; ColorModel model; union { double gray; // 0-255 struct {double r,g,b;}; // 0-255 struct {double hue,saturation,value;}; // 0-360, 0-1, 0-1 struct {double cyan,magenta,yellow,black;}; // 0-100 }; double transparency; std::string Value() const { std::string trans=(transparency!=0)?("@"+std::to_string(transparency)):""; switch(model) { case(RGB): return std::to_string(r)+"/"+std::to_string(g)+"/"+std::to_string(b)+trans; case(GRAY): return std::to_string(gray)+trans; case(HSV): return std::to_string(hue)+"-"+std::to_string(saturation)+"-"+std::to_string(value)+trans; case(CMYK): return std::to_string(cyan)+"/"+std::to_string(magenta)+"/"+std::to_string(yellow)+"/"+std::to_string(black)+trans; } return ""; } double Gray() const { switch(model) { case(RGB): return RGB2Gray().gray; case(GRAY): return gray; case(HSV): return HSV2Gray().gray; case(CMYK): return CMYK2Gray().gray; } return 0; } void ToGray() { switch(model) { case(RGB): gray=RGB2Gray().gray; break; case(GRAY): return; case(HSV): gray=HSV2Gray().gray; break; case(CMYK): gray=CMYK2Gray().gray; break; } model=GRAY; } double R() const { switch(model) { case(RGB): return r; case(GRAY): return Gray2RGB().r; case(HSV): return HSV2RGB().r; case(CMYK): return CMYK2RGB().r; } return 0; } double G() const { switch(model) { case(RGB): return g; case(GRAY): return Gray2RGB().g; case(HSV): return HSV2RGB().g; case(CMYK): return CMYK2RGB().g; } return 0; } double B() const { switch(model) { case(RGB): return b; case(GRAY): return Gray2RGB().b; case(HSV): return HSV2RGB().b; case(CMYK): return CMYK2RGB().b; } return 0; } void ToRGB() { gmt_color c; switch(model) { case(RGB): return; case(GRAY): c=Gray2RGB(); break; case(HSV): c=HSV2RGB(); break; case(CMYK): c=CMYK2RGB(); break; } model=RGB; r=c.r; g=c.g; b=c.b; } double H() const { switch(model) { case(RGB): return RGB2HSV().hue; case(GRAY): return Gray2HSV().hue; case(HSV): return hue; case(CMYK): return CMYK2HSV().hue; } return 0;// Own functions } double S() const { switch(model) { case(RGB): return RGB2HSV().saturation; case(GRAY): return Gray2HSV().saturation; case(HSV): return saturation; case(CMYK): return CMYK2HSV().saturation; } return 0; } double V() const { switch(model) { case(RGB): return RGB2HSV().value; case(GRAY): return Gray2HSV().value; case(HSV): return value; case(CMYK): return CMYK2HSV().value; } return 0; } void ToHSV() { gmt_color c; switch(model) { case(RGB): c=RGB2HSV(); break; case(GRAY): c=Gray2HSV(); break; case(HSV): return; case(CMYK): c=CMYK2HSV(); break; } model=HSV; hue=c.hue; saturation=c.saturation; value=c.value; } double C() const { switch(model) { case(RGB): return RGB2CMYK().cyan; case(GRAY): return Gray2CMYK().cyan; case(HSV): return HSV2CMYK().cyan; case(CMYK): return cyan; } return 0; } double M() const { switch(model) { case(RGB): return RGB2CMYK().magenta; case(GRAY): return Gray2CMYK().magenta; case(HSV): return HSV2CMYK().magenta; case(CMYK): return magenta; } return 0; } double Y() const { switch(model) { case(RGB): return RGB2CMYK().yellow; case(GRAY): return Gray2CMYK().yellow; case(HSV): return HSV2CMYK().yellow; case(CMYK): return yellow; } return 0; } double K() const { switch(model) { case(RGB): return RGB2CMYK().black; case(GRAY): return Gray2CMYK().black; case(HSV): return HSV2CMYK().black; case(CMYK): return black; } return 0; } void ToCMYK() { gmt_color cc; switch(model) { case(RGB): cc=RGB2CMYK(); break; case(GRAY): cc=Gray2CMYK(); break; case(HSV): cc=HSV2CMYK(); break; case(CMYK): return; } model=CMYK; cyan=cc.cyan; magenta=cc.magenta; yellow=cc.yellow; black=cc.black; } // Interpret one numeric argument as gray value bool Convert(double gr) { Value2RGB g(gr); bool suc=true; model=GRAY; transparency=0.0; gray=g(&suc); return suc; } bool Convert(const std::string& istr) { std::string cstr=istr; tolower(cstr); transparency=0.0; // Transparency check { WordList wl=Split(cstr,"@"); if(1!=wl.size() && 2!=wl.size()) return false; if(2==wl.size()) { WordList::const_iterator ci=wl.begin(); bool suc=true; cstr=*ci; ci++; Value2Transp t(*ci); transparency=t(&suc); if(!suc) return false; // Parse error } } { WordList wl_slash=Split(cstr,"/"),wl_hyphen=Split(cstr,"-"); WordList::size_type slash_size=wl_slash.size(),hyphen_size=wl_hyphen.size(); // Checks if(slash_size>1 && hyphen_size>1) return false; // Delimiter can be "/" or "-", not both if(2==slash_size || slash_size>4) return false; // Size can be 1 or 3 for rgb or 4 for cmyk if(2==hyphen_size || hyphen_size>3) return false; // Size can be 1 or 3 for hsv // Gray or name // TODO: Hex representation if(1==slash_size && 1==hyphen_size) { if(SetByName(cstr)) return true; // Set color by name, all ok Value2RGB g(cstr); bool suc=true; model=GRAY; gray=g(&suc); return suc; } // RGB if(3==slash_size) { Value2RGB red(wl_slash.front()); wl_slash.pop_front(); Value2RGB green(wl_slash.front()); wl_slash.pop_front(); Value2RGB blue(wl_slash.front()); bool suc=true; model=RGB; r=red(&suc); g=green(&suc); b=blue(&suc); return suc; } // HSV if(3==hyphen_size) { Value2Hue h(wl_hyphen.front()); wl_hyphen.pop_front(); Value2SV s(wl_hyphen.front()); wl_hyphen.pop_front(); Value2SV v(wl_hyphen.front()); bool suc=true; model=HSV; hue=h(&suc); saturation=s(&suc); value=v(&suc); return suc; } // CMYK if(4==slash_size) { Value2CMYK c(wl_slash.front()); wl_slash.pop_front(); Value2CMYK m(wl_slash.front()); wl_slash.pop_front(); Value2CMYK y(wl_slash.front()); wl_slash.pop_front(); Value2CMYK k(wl_slash.front()); bool suc=true; model=CMYK; cyan=c(&suc); magenta=m(&suc); yellow=y(&suc); black=k(&suc); return suc; } } return false; } bool SetByName(const std::string& name) { const struct colorname* cdata; model=RGB; cdata=ColorHash::in_colors_set(name.c_str(),name.length()); if(0==cdata) return false; r=cdata->r; g=cdata->g; b=cdata->b; return true; } // Transformation functions private: #include "modgmt_colortransform.h" }; // Helper types for pens typedef GetDouble,PFromValue> Value2Width; // Dash struct gmt_dash { std::vector dash; double shift; std::string Value() const { std::string ret; if(dash.empty()) return ret; for(auto i:dash) ret+=std::to_string(i/10.0)+"_"; ret.back()=':'; ret+=std::to_string(shift)+"c"; return ret; } operator bool() const {return !dash.empty();} void Clear() {dash.clear(); shift=0.0;} bool Convert(const std::string& in, const double& w) { Clear(); shift=0; if(0==in.size()) return true; // No dash if(std::string::npos==in.find_first_not_of(".-")) // dot-dash form { double width=(0==w)?1:w; for(const auto& i:in) { if('.'==i) dash.push_back(width); // Dot else dash.push_back(8*width); // Dash dash.push_back(4*width); // Gap } return true; } std::string dstr=in; // Determine shift { WordList wl=Split(in,":"); if(wl.size()>1) return false; if(1==wl.size()) { Value2Width s(wl.back()); bool suc=true; shift=s(&suc); if(!suc) return false; // Parse error dstr=wl.front(); } } WordList wl=Split(dstr,"_"); for(const auto& i:wl) { Value2Width d(i); bool suc=true; dash.push_back(d(&suc)); if(!suc) return false; // Parse error } return true; } }; // Pen struct gmt_pen { double width; struct gmt_color color; struct gmt_dash dash; std::string Value() const {return std::to_string(width/10.0)+"c,"+color.Value()+(dash?(","+dash.Value()):"");} // Interpret one numeric argument as width value bool Convert(double in) { Value2Width w(in); bool suc=true; color.Convert(0); // Black dash.Clear(); width=w(&suc); return suc; } // Convert from string bool Convert(const std::string& istr) { std::string str=istr; WordList::const_iterator ci; tolower(str); WordList wl=Split(str,",",true); // Defaults width=1; color.Convert(0); // Black dash.Clear(); if(wl.size()>3) return false; // String is [width][,color][,dash] ci=wl.begin(); if(wl.end()!=ci && 0!=ci->size()) { Value2Width w(*ci); bool suc=true; width=w(&suc); if(!suc) return false; // Parse error } if(wl.end()!=ci) ci++; if(wl.end()!=ci && 0!=ci->size()) { if(!color.Convert(*ci)) return false; // Parse error } if(wl.end()!=ci) ci++; if(wl.end()!=ci && 0!=ci->size()) { if(!dash.Convert(*ci,width)) return false; // Parse error } return true; } }; #endif