]> git.tdb.fi Git - r2c2.git/blob - source/designer/svgexporter.cpp
890112f84c5d57431ddd81f36e357b1feb46288a
[r2c2.git] / source / designer / svgexporter.cpp
1 #include <cmath>
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"
7
8 using namespace std;
9 using namespace Msp;
10 using namespace R2C2;
11
12 SvgExporter::SvgExporter(const Layout &l):
13         layout(l),
14         gauge(0),
15         rail_width(0)
16 { }
17
18 void SvgExporter::save(const string &fn)
19 {
20         gauge = layout.get_catalogue().get_gauge()*1000;
21
22         // XXX This should be retrieved from track appearance
23         rail_width = 2;
24
25         xmlpp::Document *doc = new xmlpp::Document;
26         xmlpp::Element *root = doc->create_root_node("svg", "http://www.w3.org/2000/svg");
27
28         xmlpp::Element *style = root->add_child("style");
29         style->set_attribute("type", "text/css");
30         style->set_child_text(format("\n.rail { fill: none; stroke: #000000; stroke-width: %.3f; }\n"
31                 ".endpoint { fill: none; stroke: #808080; stroke-width: %.3f; }\n"
32                 ".artnr { text-anchor: middle; font-size: %.3f; }\n",
33                 rail_width, rail_width, gauge*0.75));
34
35         Vector minp;
36         Vector maxp;
37         const set<Track *> &tracks = layout.get_all<Track>();
38         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
39         {
40                 xmlpp::Element *elem = root->add_child("g");
41                 save_track(**i, *elem);
42
43                 unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
44                 for(unsigned j=0; j<n_endpoints; ++j)
45                 {
46                         Vector pos = (*i)->get_snap_node(j).position;
47                         if(i==tracks.begin() && j==0)
48                                 minp = maxp = pos;
49                         else
50                         {
51                                 minp.x = min(minp.x, pos.x);
52                                 minp.y = min(minp.y, pos.y);
53                                 maxp.x = max(maxp.x, pos.x);
54                                 maxp.y = max(maxp.y, pos.y);
55                         }
56                 }
57         }
58
59         root->set_attribute("viewBox", format("%.3f %.3f %.3f %.3f",
60                 minp.x*1000-gauge*3, -maxp.y*1000-gauge*3, (maxp.x-minp.x)*1000+gauge*6, (maxp.y-minp.y)*1000+gauge*6));
61
62         doc->write_to_file_formatted(fn);
63 }
64
65 void SvgExporter::save_track(const Track &track, xmlpp::Element &group)
66 {
67         const Vector &pos = track.get_position();
68         const Angle &rot = track.get_rotation();
69         string transform = format("translate(%.3f %.3f) rotate(%.3f)", pos.x*1000, -pos.y*1000, -rot.degrees());
70         group.set_attribute("transform", transform);
71
72         const TrackType &type = track.get_type();
73
74         const vector<TrackType::Endpoint> &endpoints = type.get_endpoints();
75         for(vector<TrackType::Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
76         {
77                 xmlpp::Element *elem = group.add_child("path");
78                 elem->set_attribute("class", "endpoint");
79
80                 Vector delta = rotated_vector(Vector(0, gauge, 0), i->dir);
81
82                 string data = format("M %.3f %.3f L %.3f %.3f",
83                         i->pos.x*1000+delta.x, -i->pos.y*1000-delta.y,
84                         i->pos.x*1000-delta.x, -i->pos.y*1000+delta.y);
85                 elem->set_attribute("d", data);
86         }
87
88         const vector<TrackPart> &parts = type.get_parts();
89         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
90         {
91                 OrientedPoint start = i->get_point(0);
92                 OrientedPoint end = i->get_point(i->get_length());
93                 if(i->is_curved())
94                 {
95                         xmlpp::Element *elem = group.add_child("path");
96                         elem->set_attribute("class", "rail");
97
98                         Vector delta1 = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), start.rotation);
99                         Vector delta2 = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), end.rotation);
100                         // Largely an educated guess, but seems to be accurate enough
101                         float clen = i->get_length()*1000/2.9;
102                         Vector ctrl1 = rotated_vector(Vector(clen, 0, 0), start.rotation);
103                         Vector ctrl2 = rotated_vector(Vector(clen, 0, 0), end.rotation);
104
105                         string data = format("M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
106                                 (start.position.x*1000+delta1.x)(-start.position.y*1000-delta1.y)
107                                 (start.position.x*1000+delta1.x+ctrl1.x)(-start.position.y*1000-delta1.y-ctrl1.y)
108                                 (end.position.x*1000+delta2.x-ctrl2.x)(-end.position.y*1000-delta2.y+ctrl2.y)
109                                 (end.position.x*1000+delta2.x)(-end.position.y*1000-delta2.y).str();
110                         data += format(" M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
111                                 (start.position.x*1000-delta1.x)(-start.position.y*1000+delta1.y)
112                                 (start.position.x*1000-delta1.x+ctrl1.x)(-start.position.y*1000+delta1.y-ctrl1.y)
113                                 (end.position.x*1000-delta2.x-ctrl2.x)(-end.position.y*1000+delta2.y+ctrl2.y)
114                                 (end.position.x*1000-delta2.x)(-end.position.y*1000+delta2.y).str();
115                         elem->set_attribute("d", data);
116                 }
117                 else
118                 {
119                         xmlpp::Element *elem = group.add_child("path");
120                         elem->set_attribute("class", "rail");
121
122                         Vector delta = rotated_vector(Vector(0, (gauge+rail_width)*0.5, 0), start.rotation);
123
124                         string data = format("M %.3f %.3f L %.3f %.3f",
125                                 start.position.x*1000+delta.x, -start.position.y*1000-delta.y,
126                                 end.position.x*1000+delta.x, -end.position.y*1000-delta.y);
127                         data += format(" M %.3f %.3f L %.3f %.3f",
128                                 start.position.x*1000-delta.x, -start.position.y*1000+delta.y,
129                                 end.position.x*1000-delta.x, -end.position.y*1000+delta.y);
130                         elem->set_attribute("d", data);
131                 }
132         }
133
134         OrientedPoint label_pt = parts.front().get_point(parts.front().get_length()/2);
135
136         label_pt.rotation.wrap_with_base(-Angle::quarter_turn());
137         if(label_pt.rotation>Angle::quarter_turn())
138                 label_pt.rotation -= Angle::half_turn();
139
140         label_pt.position *= 1000;
141         label_pt.position += rotated_vector(Vector(0, -gauge*0.25, 0), label_pt.rotation);
142
143         xmlpp::Element *elem = group.add_child("text");
144         elem->set_attribute("class", "artnr");
145         elem->set_attribute("transform", format("translate(%.3f %.3f) rotate(%.3f)",
146                 label_pt.position.x, -label_pt.position.y, -label_pt.rotation.degrees()));
147         elem->set_child_text(track.get_type().get_article_number().str());
148 }