2 #include <msp/strings/format.h>
3 #include "libr2c2/catalogue.h"
4 #include "libr2c2/track.h"
5 #include "libr2c2/tracktype.h"
6 #include "svgexporter.h"
12 SvgExporter::SvgExporter(const Layout &l):
18 void SvgExporter::save(const string &fn)
20 gauge = layout.get_catalogue().get_gauge()*1000;
22 const Profile &rail_profile = layout.get_catalogue().get_rail_profile();
23 const Vector &rail_min = rail_profile.get_min_coords();
24 const Vector &rail_max = rail_profile.get_max_coords();
25 rail_width = (rail_max.x-rail_min.x)*1000;
27 xmlpp::Document *doc = new xmlpp::Document;
28 xmlpp::Element *root = doc->create_root_node("svg", "http://www.w3.org/2000/svg");
30 xmlpp::Element *style = root->add_child("style");
31 style->set_attribute("type", "text/css");
32 style->set_child_text(format("\n.rail { fill: none; stroke: #000000; stroke-width: %.3f; }\n"
33 ".endpoint { fill: none; stroke: #808080; stroke-width: %.3f; }\n"
34 ".artnr { text-anchor: middle; font-size: %.3f; }\n",
35 rail_width, rail_width, gauge*0.75));
39 const set<Track *> &tracks = layout.get_tracks();
40 for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
42 xmlpp::Element *elem = root->add_child("g");
43 save_track(**i, *elem);
45 unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
46 for(unsigned j=0; j<n_endpoints; ++j)
48 Vector pos = (*i)->get_endpoint_position(j);
49 if(i==tracks.begin() && j==0)
53 minp.x = min(minp.x, pos.x);
54 minp.y = min(minp.y, pos.y);
55 maxp.x = max(maxp.x, pos.x);
56 maxp.y = max(maxp.y, pos.y);
61 root->set_attribute("viewBox", format("%.3f %.3f %.3f %.3f",
62 minp.x*1000-gauge*3, -maxp.y*1000-gauge*3, (maxp.x-minp.x)*1000+gauge*6, (maxp.y-minp.y)*1000+gauge*6));
64 doc->write_to_file_formatted(fn);
67 void SvgExporter::save_track(const Track &track, xmlpp::Element &group)
69 const Vector &pos = track.get_position();
70 float rot = track.get_rotation();
71 string transform = format("translate(%.3f %.3f) rotate(%.3f)", pos.x*1000, -pos.y*1000, -rot*180/M_PI);
72 group.set_attribute("transform", transform);
74 const TrackType &type = track.get_type();
76 const vector<TrackType::Endpoint> &endpoints = type.get_endpoints();
77 for(vector<TrackType::Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
79 xmlpp::Element *elem = group.add_child("path");
80 elem->set_attribute("class", "endpoint");
82 float dx = -sin(i->dir)*gauge;
83 float dy = -cos(i->dir)*gauge;
85 string data = format("M %.3f %.3f L %.3f %.3f",
86 i->pos.x*1000+dx, -i->pos.y*1000+dy,
87 i->pos.x*1000-dx, -i->pos.y*1000-dy);
88 elem->set_attribute("d", data);
91 const vector<TrackPart> &parts = type.get_parts();
92 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
94 TrackPoint start = i->get_point(0);
95 TrackPoint end = i->get_point(i->get_length());
98 xmlpp::Element *elem = group.add_child("path");
99 elem->set_attribute("class", "rail");
101 float cs = cos(start.dir);
102 float ss = sin(start.dir);
103 float ce = cos(end.dir);
104 float se = sin(end.dir);
105 float dx1 = -ss*(gauge+rail_width)*0.5;
106 float dy1 = -cs*(gauge+rail_width)*0.5;
107 float dx2 = -se*(gauge+rail_width)*0.5;
108 float dy2 = -ce*(gauge+rail_width)*0.5;
109 // Largely an educated guess, but seems to be accurate enough
110 float clen = i->get_length()*1000/2.9;
112 string data = format("M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
113 (start.pos.x*1000+dx1)(-start.pos.y*1000+dy1)
114 (start.pos.x*1000+dx1+cs*clen)(-start.pos.y*1000+dy1-ss*clen)
115 (end.pos.x*1000+dx2-ce*clen)(-end.pos.y*1000+dy2+se*clen)
116 (end.pos.x*1000+dx2)(-end.pos.y*1000+dy2).str();
117 data += format(" M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
118 (start.pos.x*1000-dx1)(-start.pos.y*1000-dy1)
119 (start.pos.x*1000-dx1+cs*clen)(-start.pos.y*1000-dy1-ss*clen)
120 (end.pos.x*1000-dx2-ce*clen)(-end.pos.y*1000-dy2+se*clen)
121 (end.pos.x*1000-dx2)(-end.pos.y*1000-dy2).str();
122 elem->set_attribute("d", data);
126 xmlpp::Element *elem = group.add_child("path");
127 elem->set_attribute("class", "rail");
129 float dx = -sin(start.dir)*(gauge+rail_width)*0.5;
130 float dy = -cos(start.dir)*(gauge+rail_width)*0.5;
132 string data = format("M %.3f %.3f L %.3f %.3f",
133 start.pos.x*1000+dx, -start.pos.y*1000+dy,
134 end.pos.x*1000+dx, -end.pos.y*1000+dy);
135 data += format(" M %.3f %.3f L %.3f %.3f",
136 start.pos.x*1000-dx, -start.pos.y*1000-dy,
137 end.pos.x*1000-dx, -end.pos.y*1000-dy);
138 elem->set_attribute("d", data);
142 TrackPoint label_pt = parts.front().get_point(parts.front().get_length()/2);
144 while(rot+label_pt.dir>M_PI/2)
145 label_pt.dir -= M_PI;
146 while(rot+label_pt.dir<-M_PI/2)
147 label_pt.dir += M_PI;
149 label_pt.pos.x = label_pt.pos.x*1000+sin(label_pt.dir)*gauge*0.25;
150 label_pt.pos.y = label_pt.pos.y*1000-cos(label_pt.dir)*gauge*0.25;
152 xmlpp::Element *elem = group.add_child("text");
153 elem->set_attribute("class", "artnr");
154 elem->set_attribute("transform", format("translate(%.3f %.3f) rotate(%.3f)",
155 label_pt.pos.x, -label_pt.pos.y, -label_pt.dir*180/M_PI));
156 elem->set_child_text(track.get_type().get_article_number().str());