]> git.tdb.fi Git - r2c2.git/blob - source/designer/svgexporter.cpp
Get rid of some obsolete #includes
[r2c2.git] / source / designer / svgexporter.cpp
1 #include <cmath>
2 #include <msp/strings/format.h>
3 #include <msp/strings/utils.h>
4 #include "libr2c2/catalogue.h"
5 #include "libr2c2/trackappearance.h"
6 #include "libr2c2/track.h"
7 #include "libr2c2/tracktype.h"
8 #include "svgexporter.h"
9
10 using namespace std;
11 using namespace Msp;
12 using namespace R2C2;
13
14 SvgExporter::SvgExporter(const Layout &l):
15         layout(l)
16 { }
17
18 void SvgExporter::save(const string &fn)
19 {
20         xmlpp::Document *doc = new xmlpp::Document;
21         root = doc->create_root_node("svg", "http://www.w3.org/2000/svg");
22
23         xmlpp::Element *style = root->add_child("style");
24         style->set_attribute("type", "text/css");
25         styles.push_back(".rail { fill: none; stroke: #000000; }");
26         styles.push_back(".border { fill: none; stroke: #808080; }");
27         styles.push_back(".artnr { text-anchor: middle; font-family: sans; }");
28
29         defs = root->add_child("defs");
30
31         Vector minp;
32         Vector maxp;
33         const set<Track *> &tracks = layout.get_all<Track>();
34         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
35         {
36                 save_track(**i);
37
38                 float gauge = (*i)->get_type().get_gauge();
39                 unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
40                 for(unsigned j=0; j<n_endpoints; ++j)
41                 {
42                         Vector pos = (*i)->get_snap_node(j).position;
43                         if(i==tracks.begin() && j==0)
44                                 minp = maxp = pos;
45                         else
46                         {
47                                 minp.x = min(minp.x, pos.x-gauge*3);
48                                 minp.y = min(minp.y, pos.y-gauge*3);
49                                 maxp.x = max(maxp.x, pos.x+gauge*3);
50                                 maxp.y = max(maxp.y, pos.y+gauge*3);
51                         }
52                 }
53         }
54
55         style->set_child_text(join(styles.begin(), styles.end(), "\n"));
56
57         root->set_attribute("viewBox", format("%.3f %.3f %.3f %.3f",
58                 minp.x*1000, -maxp.y*1000, (maxp.x-minp.x)*1000, (maxp.y-minp.y)*1000));
59
60         doc->write_to_file_formatted(fn);
61 }
62
63 string SvgExporter::create_appearance(const TrackAppearance &appearance)
64 {
65         float gauge = appearance.get_gauge();
66         string key = format("a%.0f", gauge*1000);
67
68         if(!appearances_created.count(&appearance))
69         {
70                 float rail_width = appearance.get_rail_profile().get_width();
71
72                 styles.push_back(format(".%s .rail { stroke-width: %.3f }", key, rail_width*1000));
73                 styles.push_back(format(".%s .border { stroke-width: %.3f }", key, rail_width*500));
74                 styles.push_back(format(".%s .artnr { font-size: %.3f }", key, gauge*750));
75
76                 appearances_created.insert(&appearance);
77         }
78
79         return key;
80 }
81
82 string SvgExporter::create_point_data(const Vector &p)
83 {
84         return format("%.3f %.3f", p.x*1000, -p.y*1000);
85 }
86
87 string SvgExporter::create_line_data(const Vector &s, const Vector &e)
88 {
89         return format("M %s L %s", create_point_data(s), create_point_data(e));
90 }
91
92 string SvgExporter::create_curve_data(const Vector &s, const Vector &st, const Vector &et, const Vector &e)
93 {
94         return format("M %s C %s %s %s", create_point_data(s),
95                 create_point_data(s+st), create_point_data(e-et), create_point_data(e));
96 }
97
98 string SvgExporter::create_track_type(const TrackType &type)
99 {
100         const ArticleNumber &art_nr = type.get_article_number();
101         string key = format("t%s", art_nr.str());
102
103         if(!tracks_created.count(&type))
104         {
105                 const TrackAppearance &appearance = type.get_appearance();
106                 string appearance_key = create_appearance(appearance);
107
108                 xmlpp::Element *group = defs->add_child("g");
109                 group->set_attribute("id", key);
110                 group->set_attribute("class", appearance_key);
111
112                 float gauge = appearance.get_gauge();
113                 float rail_width = appearance.get_rail_profile().get_width();
114
115                 const vector<TrackType::Endpoint> &endpoints = type.get_endpoints();
116                 for(vector<TrackType::Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
117                 {
118                         xmlpp::Element *path = group->add_child("path");
119                         path->set_attribute("class", "border");
120
121                         Vector delta = rotated_vector(Vector(0, gauge, 0), i->dir);
122
123                         path->set_attribute("d", create_line_data(i->pos+delta, i->pos-delta));
124                 }
125
126                 const vector<TrackPart> &parts = type.get_parts();
127                 for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
128                 {
129                         xmlpp::Element *path = group->add_child("path");
130                         path->set_attribute("class", "rail");
131
132                         string data;
133
134                         OrientedPoint start = i->get_point(0);
135                         OrientedPoint end = i->get_point(i->get_length());
136                         if(i->is_curved())
137                         {
138                                 Vector delta1 = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), start.rotation);
139                                 Vector delta2 = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), end.rotation);
140                                 // Largely an educated guess, but seems to be accurate enough
141                                 float clen = i->get_length()/2.9;
142                                 Vector ctrl1 = rotated_vector(Vector(clen, 0, 0), start.rotation);
143                                 Vector ctrl2 = rotated_vector(Vector(clen, 0, 0), end.rotation);
144
145                                 data = create_curve_data(start.position+delta1, ctrl1, ctrl2, end.position+delta2);
146                                 data += ' ';
147                                 data += create_curve_data(start.position-delta1, ctrl1, ctrl2, end.position-delta2);
148                         }
149                         else
150                         {
151                                 Vector delta = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), start.rotation);
152
153                                 data = create_line_data(start.position+delta, end.position+delta);
154                                 data += ' ';
155                                 data += create_line_data(start.position-delta ,end.position-delta);
156                         }
157
158                         path->set_attribute("d", data);
159                 }
160
161                 OrientedPoint label_pt = parts.front().get_point(parts.front().get_length()/2);
162
163                 label_pt.rotation.wrap_with_base(-Angle::quarter_turn());
164                 if(label_pt.rotation>Angle::quarter_turn())
165                         label_pt.rotation -= Angle::half_turn();
166
167                 label_pt.position += rotated_vector(Vector(0, -gauge*0.25, 0), label_pt.rotation);
168
169                 xmlpp::Element *text = group->add_child("text");
170                 text->set_attribute("class", "artnr");
171                 text->set_attribute("transform", format("translate(%s) rotate(%.3f)",
172                         create_point_data(label_pt.position), -label_pt.rotation.degrees()));
173                 text->set_child_text(type.get_article_number().str());
174
175                 tracks_created.insert(&type);
176         }
177
178         return key;
179 }
180
181 void SvgExporter::save_track(const Track &track)
182 {
183         string type_key = create_track_type(track.get_type());
184         xmlpp::Element *use = root->add_child("use");
185
186         const Vector &pos = track.get_position();
187         const Angle &rot = track.get_rotation();
188         string transform = format("translate(%s) rotate(%.3f)", create_point_data(pos), -rot.degrees());
189         use->set_attribute("transform", transform);
190
191         use->set_attribute("xlink:href", "#"+type_key);
192 }