You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
900 lines
22 KiB
900 lines
22 KiB
#ifndef MODGMT_STRUCT_H |
|
#define MODGMT_STRUCT_H |
|
#include <cinttypes> |
|
#include <cmath> |
|
#include <map> |
|
#include <vector> |
|
#include "common.h" |
|
#include "modgmt_colornames.h" |
|
#include "modgmt_strcomp.h" |
|
|
|
// Centimeters to GMT points scale factor |
|
// 1 inch = 72 pt = 2.54 cm --> 1 cm = 72/2.54 pt |
|
// GMT's own scaling factor is 0.06 |
|
inline static double cm2GMT(double cm) |
|
{ |
|
static const double scale=(72.0/2.54)/0.06; |
|
return cm*scale; |
|
} |
|
|
|
// Helper template for checking double numbers acquired from Source with some Policies. |
|
// Main definition, never instantiated |
|
template<class Source, class... Policies> |
|
class GetDouble; |
|
|
|
// Recursive definition |
|
template<class Source, class Policy, class... Policies> |
|
class GetDouble<Source, Policy, Policies...>: public GetDouble<Source, Policies...> |
|
{ |
|
Policy p; |
|
public: |
|
GetDouble(GetDouble&&) = delete; |
|
GetDouble(GetDouble&) = delete; |
|
template<class... Args> |
|
GetDouble(Args... args):GetDouble<Source,Policies...>(args...) {}; |
|
double operator()(bool* suc, std::string& err) const {return p(GetDouble<Source,Policies...>::operator()(suc,err),suc,err);} |
|
}; |
|
|
|
// Bottom of recursion |
|
template<class Source> |
|
class GetDouble<Source>: public Source |
|
{ |
|
public: |
|
GetDouble(GetDouble&&) = delete; |
|
GetDouble(GetDouble&) = delete; |
|
GetDouble() = delete; |
|
template<class... Args> |
|
GetDouble(Args... args):Source(args...) {}; |
|
double operator()(bool* suc, std::string& err) const {return Source::operator()(suc, err);} |
|
}; |
|
|
|
// We use rational representation of floating point number, because double type values not allowed as template parameter |
|
// Policy to check if value is greater or equal num/denum |
|
template<int32_t num, uint32_t denum=1, bool equal=true> |
|
class PMin |
|
{ |
|
static bool Compare(double d1, double d2) {return equal?(d1<=d2):(d1<d2);} |
|
|
|
public: |
|
double operator()(double v, bool* suc, std::string& err) const |
|
{ |
|
if(Compare(v,static_cast<double>(num)/denum)) |
|
{ |
|
*suc=false; |
|
if(err.empty()) err="Value "+ToString(v)+" must be greater "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum))); |
|
} |
|
return v; |
|
} |
|
}; |
|
|
|
// Policy to check if value is lesser or equal num/denum |
|
template<int32_t num, uint32_t denum=1, bool equal=true> |
|
class PMax |
|
{ |
|
static bool Compare(double d1, double d2) {return equal?(d1>=d2):(d1>d2);} |
|
|
|
public: |
|
double operator()(double v, bool* suc, std::string& err) const |
|
{ |
|
if(Compare(v,static_cast<double>(num)/denum)) |
|
{ |
|
*suc=false; |
|
if(err.empty()) err="Value "+ToString(v)+" must be lesser "+(equal?"or equal ":"")+"then "+ToString(num)+((denum==1)?"":("/"+ToString(denum))); |
|
} |
|
return v; |
|
} |
|
}; |
|
|
|
// Get double value from string or number |
|
class SourceValue |
|
{ |
|
double d; |
|
std::string err; |
|
public: |
|
SourceValue(const std::string& s) {if(!str2double(s,&d)) err="Can't convert string \""+s+"\" to double value";} |
|
SourceValue(double s):d(s) {} |
|
double operator()(bool* suc, std::string& ierr) const |
|
{ |
|
if(!err.empty()) {*suc=false; ierr=err;} |
|
return d; |
|
} |
|
}; |
|
|
|
// Helper types for colors |
|
typedef GetDouble<SourceValue,PMin<0>,PMax<255> > Value2RGB; |
|
typedef GetDouble<SourceValue,PMin<0>,PMax<360> > Value2Hue; |
|
typedef GetDouble<SourceValue,PMin<0>,PMax< 1 > > Value2SV; |
|
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2CMYK; |
|
typedef GetDouble<SourceValue,PMin<0>,PMax<100> > Value2Transp; |
|
// Helper type for pens and dashes |
|
typedef GetDouble<SourceValue,PMin<0> > Value2Width; |
|
|
|
class gmt_struct {}; // Base type for all gmt structures |
|
|
|
// Coordinate |
|
struct gmt_coord: public gmt_struct |
|
{ |
|
bool isdeg; |
|
union |
|
{ |
|
double r; |
|
struct |
|
{ |
|
bool sign; |
|
uint16_t d; |
|
uint8_t m; |
|
double s; |
|
}; |
|
}; |
|
|
|
std::string Value() const |
|
{ |
|
if(!isdeg) return ToString(r); |
|
else return (sign?"":"-")+ToString(d)+":"+ToString(m)+(s==0?"":":"+ToString(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, std::string& err) |
|
{ |
|
WordList wl; |
|
WordList::const_iterator cw; |
|
wl=Split(str,":",true); |
|
if(1==wl.size()) // No dd:mm |
|
{ |
|
isdeg=false; |
|
bool res=str2double(str,&r); |
|
if(!res) err="Can't convert string "+str+" to double value"; |
|
return res; |
|
} |
|
if(0==wl.size() || wl.size()>3) {err="String "+str+" have incorrect format for geographic coordinate, must be ddd[:mm[:ss[.fff]]]"; return false;} |
|
|
|
isdeg=true; |
|
int64_t res; |
|
|
|
// degrees |
|
cw=wl.begin(); |
|
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;} |
|
if(res>360 || res<-360) res%=360; |
|
sign=(std::string::npos==cw->find('-')); |
|
d=static_cast<uint16_t>(sign?res:-res); |
|
// minutes |
|
cw++; |
|
if(!str2int(*cw,&res)) {err="Can't convert "+*cw+" to integer"; return false;} |
|
if(res<0 || res>=60) {err="Minutes must be not lesser then 0 and lesser then 60"; return false;} |
|
m=static_cast<uint8_t>(res); |
|
s=0; |
|
|
|
// seconds |
|
cw++; |
|
if(wl.end()==cw) return true; // No seconds |
|
if(!str2double(*cw,&s) ) {err="Can't convert "+*cw+" to double"; return false;} |
|
if(s<0.0 || s>=60.0) {err="Seconds must be not lesser then 0 and lesser then 60"; return false;} |
|
return true; |
|
} |
|
|
|
bool Convert(double num, std::string& err) |
|
{ |
|
isdeg=false; |
|
r=num; |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
// Region |
|
struct gmt_region: public gmt_struct |
|
{ |
|
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& err) |
|
{ |
|
std::string str=istr; |
|
tolower(str); |
|
if("global180"==str) |
|
{ |
|
type=GLOBAL180; |
|
xb.Convert(-180.0,err); xe.Convert(180.0,err); |
|
yb.Convert(-90.0,err); ye.Convert(90.0,err); |
|
return true; |
|
} |
|
if("global360"==str || "global"==str) |
|
{ |
|
type=GLOBAL360; |
|
xb.Convert(0.0,err); xe.Convert(360.0,err); |
|
yb.Convert(-90.0,err); ye.Convert(90.0,err); |
|
return true; |
|
} |
|
err="Can't convert "+istr+" to region."; |
|
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: public gmt_struct |
|
{ |
|
// 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"+ToString(width)+"c/"+ToString(x.height)+"c"; break;} |
|
case(CYL_EQU): {ret="-JQ"+q.cmer.Value()+"/"+q.stpar.Value()+"/"+ToString(width)+"c"; break;} |
|
case(MERCATOR): {ret="-JM"+m.cmer.Value()+"/"+m.stpar.Value()+"/"+ToString(width)+"c"; break;} |
|
case(TRANSMERCATOR): {ret="-JT"+t.cmer.Value()+"/"+t.orlat.Value()+"/"+ToString(width)+"c --PROJ_SCALE_FACTOR="+ToString(t.scale); break;} |
|
case(OBLIQMERCATOR): |
|
{ |
|
switch(o.type) |
|
{ |
|
case(OType::A): {ret="-JOa"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.azimuth.Value()+"/"+ToString(width)+"c"; break;} |
|
case(OType::B): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.eqlon.Value()+"/"+o.eqlat.Value()+"/"+ToString(width)+"c"; break;} |
|
case(OType::C): {ret="-JOb"+o.clon.Value()+"/"+o.clat.Value()+"/"+o.polelon.Value()+"/"+o.polelat.Value()+"/"+ToString(width)+"c"; break;} |
|
default: return ""; |
|
} |
|
break; |
|
} |
|
case(CASSINI): {ret="-JC"+c.clon.Value()+"/"+c.clat.Value()+"/"+ToString(width)+"c"; break;} |
|
case(CYL_EQA): {ret="-JY"+y.cmer.Value()+"/"+y.stpar.Value()+"/"+ToString(width)+"c"; break;} |
|
case(MILLER): {ret="-JJ"+j.cmer.Value()+"/"+ToString(width)+"c"; break;} |
|
case(CYL_STERE): {ret="-JCyl_stere/"+cyl_stere.stpar.Value()+"/"+cyl_stere.cmer.Value()+"/"+ToString(width)+"c"; break;} |
|
default: return ""; |
|
} |
|
ret+=" "+region.Value(); |
|
return ret; |
|
} |
|
bool SetType(const std::string& s, std::string& err) |
|
{ |
|
proj=NOTDEF; |
|
std::string str=s; |
|
tolower(str); |
|
|
|
// Handle one symbol cases |
|
if(str.size()==1) switch(str[0]) |
|
{ |
|
case('c'): {proj=CASSINI; return true;} |
|
case('j'): {proj=MILLER; return true;} |
|
case('m'): {proj=MERCATOR; return true;} |
|
case('o'): {proj=OBLIQMERCATOR; return true;} |
|
case('q'): {proj=CYL_EQU; return true;} |
|
case('t'): {proj=TRANSMERCATOR; return true;} |
|
case('x'): {proj=XY; return true;} |
|
case('y'): {proj=CYL_EQA; return true;} |
|
default: break; |
|
} |
|
// Handle long GMT names |
|
if("cyl_stere"==str) {proj=CYL_STERE; return true;} |
|
// Handle common names |
|
std::string t1; |
|
for(const auto& m: projnames) |
|
{ |
|
TemplateComparator cmp(m.first); |
|
bool res=cmp.Compare(str); |
|
if(!res) continue; |
|
if(NOTDEF!=proj) // Ambiguos string |
|
{ |
|
err="Ambiguous projection definition: did you mean \""+t1+"\" or \""+cmp.Template2Name()+"\"?"; |
|
proj=NOTDEF; |
|
return false; |
|
} |
|
t1=cmp.Template2Name(); |
|
proj=m.second; |
|
} |
|
|
|
if(NOTDEF!=proj) return true; // All Ok |
|
err="Unknown projection: "+s; |
|
return false; |
|
} |
|
|
|
static void FillProjNames(); |
|
|
|
private: |
|
static std::map<std::string,projection> projnames; |
|
}; |
|
|
|
// Color |
|
struct gmt_color: public gmt_struct |
|
{ |
|
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)?("@"+ToString(transparency)):""; |
|
switch(model) |
|
{ |
|
case(RGB): return ToString(r)+"/"+ToString(g)+"/"+ToString(b)+trans; |
|
case(GRAY): return ToString(gray)+trans; |
|
case(HSV): return ToString(hue)+"-"+ToString(saturation)+"-"+ToString(value)+trans; |
|
case(CMYK): return ToString(cyan)+"/"+ToString(magenta)+"/"+ToString(yellow)+"/"+ToString(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; |
|
} |
|
|
|
void ToModel(ColorModel newmodel) |
|
{ |
|
switch(newmodel) |
|
{ |
|
case(RGB): ToRGB(); return; |
|
case(GRAY): ToGray(); return; |
|
case(HSV): ToHSV(); return; |
|
case(CMYK): ToCMYK(); return; |
|
} |
|
} |
|
|
|
// Interpret one numeric argument as gray value |
|
bool Convert(double gr, std::string& err) |
|
{ |
|
Value2RGB g(gr); |
|
bool suc=true; |
|
model=GRAY; |
|
transparency=0.0; |
|
gray=g(&suc,err); |
|
return suc; |
|
} |
|
|
|
bool Convert(const std::string& istr, std::string& err) |
|
{ |
|
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,err); |
|
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) {err="Delimeter of color components must be \"/\" or \"-\", not both"; return false;} |
|
if(2==slash_size || slash_size>4) {err="Number of color components must be 1 for GRAY, 3 for RGB or 4 for CMYK"; return false;} |
|
if(2==hyphen_size || hyphen_size>3) {err="There is 3 color components in HSV model"; return false;} |
|
// 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,err); |
|
err="Can't understand string "+cstr+", this is nor color name, nor gray value (0-255)"; |
|
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,err); if(!suc) return suc; |
|
g=green(&suc,err);if(!suc) return suc; |
|
b=blue(&suc,err); |
|
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,err); if(!suc) return suc; |
|
saturation=s(&suc,err);if(!suc) return suc; |
|
value=v(&suc,err); |
|
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,err); if(!suc) return suc; |
|
magenta=m(&suc,err);if(!suc) return suc; |
|
yellow=y(&suc,err); if(!suc) return suc; |
|
black=k(&suc,err); |
|
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" |
|
}; |
|
|
|
// Dash |
|
struct gmt_dash: public gmt_struct |
|
{ |
|
std::vector<double> dash; |
|
double shift; |
|
double width; |
|
bool wisrel; |
|
static const double default_width; |
|
std::string Value() const |
|
{ |
|
std::string ret; |
|
if(dash.empty()) return ret; |
|
for(auto i:dash) ret+=ToString((wisrel?width:1.0)*i/10.0)+"_"; |
|
ret.back()=':'; |
|
ret+=ToString(shift)+"c"; |
|
return ret; |
|
} |
|
operator bool() const {return !dash.empty();} |
|
void Clear() {dash.clear(); shift=0.0; wisrel=false;} |
|
|
|
bool Convert(const std::string& in, std::string& err, double w=0.0) |
|
{ |
|
Clear(); |
|
shift=0; |
|
if(0==in.size()) return true; // No dash |
|
if(std::string::npos==in.find_first_not_of(".-")) // dot-dash form |
|
{ |
|
width=(0==w)?default_width:w; |
|
wisrel=true; |
|
for(const auto& i:in) |
|
{ |
|
if('.'==i) dash.push_back(1); // Dot |
|
else dash.push_back(8); // Dash |
|
dash.push_back(4); // Gap |
|
} |
|
return true; |
|
} |
|
std::string dstr=in; |
|
// Determine shift |
|
{ |
|
WordList wl=Split(in,":"); |
|
if(wl.size()>2) {err="Incorrect dash format"; return false;} |
|
if(2==wl.size()) |
|
{ |
|
Value2Width s(wl.back()); |
|
bool suc=true; |
|
shift=s(&suc,err); |
|
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,err)); |
|
if(!suc) return false; // Parse error |
|
} |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
// Pen |
|
struct gmt_pen: public gmt_struct |
|
{ |
|
double width; |
|
struct gmt_color color; |
|
struct gmt_dash dash; |
|
static const double default_width; |
|
std::string Value() const {return ToString(width/10.0)+"c,"+color.Value()+(dash?(","+dash.Value()):"");} |
|
|
|
// Interpret one numeric argument as width value |
|
bool Convert(double in, std::string& err) |
|
{ |
|
Value2Width w(in); |
|
bool suc=true; |
|
color.Convert(0,err); // Black |
|
dash.Clear(); |
|
width=w(&suc,err); |
|
return suc; |
|
} |
|
|
|
// Convert from string |
|
bool Convert(const std::string& istr, std::string& err) |
|
{ |
|
std::string str=istr; |
|
WordList::const_iterator ci; |
|
tolower(str); |
|
WordList wl=Split(str,",",true); |
|
|
|
// Defaults |
|
width=default_width; |
|
color.Convert(0,err); // 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,err); |
|
if(!suc) return false; // Parse error |
|
} |
|
if(wl.end()!=ci) ci++; |
|
if(wl.end()!=ci && 0!=ci->size()) |
|
{ |
|
if(!color.Convert(*ci,err)) return false; // Parse error |
|
} |
|
if(wl.end()!=ci) ci++; |
|
if(wl.end()!=ci && 0!=ci->size()) |
|
{ |
|
if(!dash.Convert(*ci,err,width)) return false; // Parse error |
|
} |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
// Font |
|
struct gmt_font: public gmt_struct |
|
{ |
|
double size; |
|
struct gmt_color color; |
|
std::string family; |
|
static std::string default_family; |
|
static std::set<std::string> families; |
|
static const double default_size; |
|
|
|
std::string Value() const {return ToString(size)+"p,"+family+","+color.Value();} |
|
|
|
// Interpret one numeric argument as size of default black font |
|
bool Convert(double in, std::string& err) |
|
{ |
|
Value2Width s(in); |
|
bool suc=true; |
|
color.Convert(0,err); // Black |
|
size=s(&suc,err); |
|
family=default_family; |
|
return suc; |
|
} |
|
|
|
// Convert from string |
|
bool Convert(const std::string& str, std::string& err) |
|
{ |
|
WordList::const_iterator ci; |
|
WordList wl=Split(str,",",true); |
|
std::string fakeerr; |
|
|
|
// Defaults |
|
size=default_size; |
|
family=default_family; |
|
color.Convert(0,fakeerr); // Black |
|
|
|
if(wl.size()>3) {err="String "+str+" is not font string"; return false;} // String is [size][,family][,color] or [family][,color] |
|
ci=wl.begin(); |
|
if(wl.end()!=ci && 0!=ci->size()) |
|
{ |
|
Value2Width s(*ci); |
|
bool suc=true; |
|
{ |
|
std::string fake; |
|
double newsize=s(&suc,fake); |
|
if(suc) size=newsize; |
|
} |
|
if(!suc) // Parse error. check if argument is font name |
|
{ |
|
if(0==families.count(*ci)) return false; // No, argument is not allowed font name |
|
family=*ci; |
|
if(wl.size()>2) {err="String "+str+" is not font string"; return false;} // If first word is font name, then words count is 1 or 2. |
|
goto read_color; |
|
} |
|
} |
|
if(wl.end()!=ci) ci++; |
|
if(wl.end()!=ci && 0!=ci->size()) |
|
{ |
|
if(0==families.count(*ci)) {err="Unknown font family: "+(*ci); return false;} // Argument is not allowed font name |
|
family=*ci; |
|
} |
|
read_color: |
|
if(wl.end()!=ci) ci++; |
|
if(wl.end()!=ci && 0!=ci->size()) |
|
{ |
|
if(!color.Convert(*ci,err)) return false; // Parse error |
|
} |
|
return true; |
|
} |
|
|
|
static bool FillFontNames(); |
|
}; |
|
|
|
|
|
// Layer |
|
struct gmt_layer: public gmt_struct |
|
{ |
|
double shiftx,shifty; |
|
struct gmt_projection proj; |
|
std::string creator; |
|
std::shared_ptr<std::string> data; |
|
|
|
std::string Value() const {return creator+(Shifted()?("("+ToString(shiftx)+"x"+ToString(shifty)+")"):"");} |
|
bool Shifted() const {return shiftx!=0.0 || shifty!=0.0;} |
|
std::string BeginShift() const {return (Shifted()?("V "+ToString(cm2GMT(shiftx))+" "+ToString(cm2GMT(shifty))+" T\n"):"");} |
|
std::string EndShift() const {return (Shifted()?"U\n":"");} |
|
}; |
|
#endif
|
|
|