00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "hexsvg.h"
00025
00026 #include <cassert>
00027 #include <fstream>
00028 #include <sstream>
00029
00030
00031 namespace hex {
00032 namespace svg {
00033
00034
00036 std::string
00037 strip(const std::string& s)
00038 {
00039 std::string::size_type p0 =s.find_first_not_of(" \t\n");
00040 std::string::size_type p1 =s.find_last_of(" \t\n");
00041 return s.substr(p0,p1-p0);
00042 }
00043
00044
00045 std::string
00046 style_str(const Style& st)
00047 {
00048 std::string result ="";
00049 for(Style::const_iterator it=st.begin(); it!=st.end(); ++it)
00050 {
00051 if(!result.empty())
00052 result += ';';
00053 result += it->first + ":" + it->second;
00054 }
00055 return result;
00056 }
00057
00058
00059 Style
00060 style_dict(const std::string& s) throw(hex::invalid_argument)
00061 {
00062 Style result;
00063 std::string::size_type pos =0;
00064 while(true)
00065 {
00066 pos=s.find_first_not_of(";",pos);
00067 if(pos==std::string::npos)
00068 break;
00069 std::string::size_type semi =s.find(";",pos);
00070 std::string clause =s.substr(pos,semi-pos);
00071 std::string::size_type colon =clause.find(":");
00072 if(colon==std::string::npos || colon==0 || (colon+1)>=clause.size())
00073 throw hex::invalid_argument(s);
00074 result[ strip(s.substr(0,colon)) ] = strip(s.substr(colon+1));
00075 pos=semi;
00076 }
00077 return result;
00078 }
00079
00080
00087 std::string
00088 path_append(char& cmd, Point& p, char cmd1, const Point& p1)
00089 {
00090 std::ostringstream ss;
00091 if(p != p1)
00092 {
00093 if(cmd != cmd1)
00094 {
00095 ss<<" "<<cmd1;
00096 cmd=cmd1;
00097 }
00098 ss<<" "<<(p1-p);
00099 p=p1;
00100 }
00101 return ss.str();
00102 }
00103
00104
00106 template<class InputIterator>
00107 std::ostream&
00108 output_path_data(
00109 const Document& doc,
00110 std::ostream& os,
00111 InputIterator first,
00112 InputIterator last
00113 )
00114 {
00115 assert(first!=last);
00116 --last;
00117 os<<"M "<<doc.T(*first)<<" L";
00118 for(InputIterator p =++first; p!=last; ++p)
00119 os<<" "<<doc.T(*p);
00120 os<<" Z";
00121 return os;
00122 }
00123
00124
00125
00126
00127
00128 std::string
00129 Identity::attributes(void) const
00130 {
00131 std::string result ="";
00132 if(!this->id.empty())
00133 result += std::string(" id=\"") + this->id + "\"";
00134 if(!this->style.empty())
00135 result += std::string(" style=\"") + this->style + "\"";
00136 if(!this->className.empty())
00137 result += std::string(" class=\"") + this->className + "\"";
00138 return result;
00139 }
00140
00141
00142
00143
00144
00145 Point
00146 Document::T(const Point& p) const
00147 {
00148 return Point( p.x - p0.x, p1.y - p.y );
00149 }
00150
00151
00152 std::string
00153 Document::header() const
00154 {
00155 std::ostringstream os;
00156 os<<
00157 "<?xml version=\"1.0\" standalone=\"no\"?>\n"
00158 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
00159 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
00160 #if defined(HEX_EXTERNAL_CSS) && HEX_EXTERNAL_CSS!=0
00161 for(std::list<std::string>::const_iterator s =this->stylesheets.begin();
00162 s!=this->stylesheets.end();
00163 ++s)
00164 {
00165 os<<"<?xml-stylesheet href=\""<<(*s)<<"\" type=\"text/css\"?>\n";
00166 }
00167 #endif
00168 Point extent =p1-p0;
00169 os<<
00170 "<svg width=\"100%\" height=\"100%\" viewBox=\""
00171 << 0L <<" "<< 0L << " " << extent.x <<" "<< extent.y <<
00172 "\" version=\"1.1\""
00173 " xmlns=\"http://www.w3.org/2000/svg\""
00174 " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
00175
00176 "<defs>\n";
00177 #if !defined(HEX_EXTERNAL_CSS) || HEX_EXTERNAL_CSS==0
00178 if(!this->stylesheets.empty())
00179 {
00180 os<<"<style type=\"text/css\"><![CDATA[\n";
00181 for(std::list<std::string>::const_iterator s =this->stylesheets.begin();
00182 s!=this->stylesheets.end();
00183 ++s)
00184 {
00185 std::ifstream css(s->c_str());
00186
00187
00188 if(css)
00189 {
00190 char buf[1024];
00191 std::streamsize total =0;
00192 while(css.good())
00193 {
00194 std::streamsize bytes =css.readsome(buf,sizeof(buf));
00195 if(!bytes)
00196 break;
00197 total+=bytes;
00198 os.write(buf,bytes);
00199 }
00200 }
00201 }
00202 os<<"]]></style>\n";
00203 }
00204 #endif
00205 os<<
00206 "<marker id=\"Triangle\""
00207 " viewBox=\"0 0 10 10\" refX=\"0\" refY=\"5\" "
00208 " markerUnits=\"strokeWidth\""
00209 " markerWidth=\"4\" markerHeight=\"3\""
00210 " orient=\"auto\">\n"
00211 "<path d=\"M 0 0 L 10 5 L 0 10 z\" />\n"
00212 "</marker>\n";
00213 for(std::list<std::string>::const_iterator d =this->defs.begin();
00214 d!=this->defs.end();
00215 ++d)
00216 {
00217 os<<(*d)<<"\n";
00218 }
00219 os<<"</defs>\n";
00220 return os.str();
00221 }
00222
00223
00224 std::string
00225 Document::footer(void) const
00226 {
00227 return "</svg>\n";
00228 }
00229
00230
00231
00232
00233
00234 std::string
00235 Document::draw_simple_area(const Area& a, float bias)
00236 {
00237 return draw_poly(a.boundary().stroke(bias),true,&a);
00238 }
00239
00240
00241 std::string
00242 Document::draw_complex_area(const Area& a, float bias)
00243 {
00244 using namespace std;
00245 std::ostringstream os;
00246 os<<"<path fill-rule=\"nonzero\""<<a.attributes()<<" d=\"";
00247 const std::list<Point> apoints =a.boundary().stroke(bias);
00248 output_path_data(*this,os,apoints.begin(),apoints.end());
00249 std::list<Area> voids =a.enclosed_areas();
00250 for(list<Area>::const_iterator v=voids.begin(); v!=voids.end(); ++v)
00251 {
00252 os<<" ";
00253 const std::list<Point> vpoints =v->boundary().stroke(-bias);
00254 output_path_data(*this,os,vpoints.rbegin(),vpoints.rend());
00255 }
00256 os<<"\"/>\n";
00257 return os.str();
00258 }
00259
00260
00261 std::string
00262 Document::draw_skeleton(const Area& a, bool include_boundary)
00263 {
00264 std::ostringstream os;
00265 os<<"<path"<<a.attributes()<<" d=\"";
00266 const std::list<Boundary> bb =a.skeleton(include_boundary);
00267 Point curr =T( bb.front().edges().front()->start_point() );
00268 os<<"M "<<curr;
00269 char cmd ='\0';
00270 for(std::list<Boundary>::const_iterator b=bb.begin(); b!=bb.end(); ++b)
00271 {
00272 const std::list<Edge*> edges =b->edges();
00273 assert(!edges.empty());
00274 os<<path_append(cmd,curr,'m',T( edges.front()->start_point() ));
00275 for(std::list<Edge*>::const_iterator e=edges.begin(); e!=edges.end(); ++e)
00276 {
00277 os<<path_append(cmd,curr,'l',T( (**e).end_point() ));
00278 }
00279 }
00280 os<<"\"/>\n";
00281 return os.str();
00282 }
00283
00284
00285 std::string
00286 Document::draw_boundary(const Boundary& b, float bias)
00287 {
00288 return draw_poly(b.stroke(bias), b.is_closed(), &b);
00289 }
00290
00291
00292 std::string
00293 Document::draw_path(const Path& p)
00294 {
00295 std::ostringstream os;
00296 const std::list<Hex*>& hexes =p.hexes();
00297 assert(!hexes.empty());
00298 if(hexes.size()>1)
00299 {
00300 std::list<Point> points;
00301 for(std::list<Hex*>::const_iterator h =hexes.begin(); h!=hexes.end(); ++h)
00302 points.push_back( (**h).centre() );
00303 bool is_closed =( hexes.front()==hexes.back() );
00304 os<<draw_poly(points,is_closed,&p);
00305 }
00306 return os.str();
00307 }
00308
00309
00310 std::string
00311 Document::draw_poly(
00312 std::list<Point> points,
00313 bool closed,
00314 const Identity* identity
00315 )
00316 {
00317 assert(!points.empty());
00318 std::ostringstream os;
00319 if(closed)
00320 points.pop_back();
00321 if(closed)
00322 os<<"<polygon";
00323 else
00324 os<<"<polyline";
00325 if(identity)
00326 os<<identity->attributes();
00327 os<<" points=\"";
00328 for(std::list<Point>::const_iterator p=points.begin(); p!=points.end(); ++p)
00329 {
00330 if(p!=points.begin())
00331 os<<" ";
00332 os<<T(*p);
00333 }
00334 os<<"\"/>\n";
00335 return os.str();
00336 }
00337
00338
00339
00340
00341 Document::Document(const Grid& grid)
00342 : _grid(grid),
00343 p0(0.0,0.0),
00344 p1(grid.width(),grid.height()),
00345 stylesheets(),
00346 defs()
00347 {}
00348
00349
00350 }
00351 }