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

#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