]> git.tdb.fi Git - r2c2.git/blob - svgexporter.cpp
a9ba131c6bedc88a70d43e9b157b55d3b393ce8d
[r2c2.git] / svgexporter.cpp
1 /* $Id$
2
3 This file is part of R²C²
4 Copyright © 2010 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6 */
7
8 #include <cmath>
9 #include <msp/strings/formatter.h>
10 #include "libr2c2/catalogue.h"
11 #include "libr2c2/track.h"
12 #include "libr2c2/tracktype.h"
13 #include "svgexporter.h"
14
15 using namespace std;
16 using namespace Msp;
17 using namespace R2C2;
18
19 SvgExporter::SvgExporter(const Layout &l):
20         layout(l),
21         gauge(0),
22         rail_width(0)
23 { }
24
25 void SvgExporter::save(const string &fn)
26 {
27         gauge = layout.get_catalogue().get_gauge()*1000;
28
29         const Profile &rail_profile = layout.get_catalogue().get_rail_profile();
30         const Vector &rail_min = rail_profile.get_min_coords();
31         const Vector &rail_max = rail_profile.get_max_coords();
32         rail_width = (rail_max.x-rail_min.x)*1000;
33
34         xmlpp::Document *doc = new xmlpp::Document;
35         xmlpp::Element *root = doc->create_root_node("svg", "http://www.w3.org/2000/svg");
36
37         xmlpp::Element *style = root->add_child("style");
38         style->set_attribute("type", "text/css");
39         style->set_child_text(format("\n.rail { fill: none; stroke: #000000; stroke-width: %.3f; }\n"
40                 ".endpoint { fill: none; stroke: #808080; stroke-width: %.3f; }\n"
41                 ".artnr { text-anchor: middle; font-size: %.3f; }\n",
42                 rail_width, rail_width, gauge*0.75));
43
44         Vector minp;
45         Vector maxp;
46         const set<Track *> &tracks = layout.get_tracks();
47         for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
48         {
49                 xmlpp::Element *elem = root->add_child("g");
50                 save_track(**i, *elem);
51
52                 unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
53                 for(unsigned j=0; j<n_endpoints; ++j)
54                 {
55                         Vector pos = (*i)->get_endpoint_position(j);
56                         if(i==tracks.begin() && j==0)
57                                 minp = maxp = pos;
58                         else
59                         {
60                                 minp.x = min(minp.x, pos.x);
61                                 minp.y = min(minp.y, pos.y);
62                                 maxp.x = max(maxp.x, pos.x);
63                                 maxp.y = max(maxp.y, pos.y);
64                         }
65                 }
66         }
67
68         root->set_attribute("viewBox", format("%.3f %.3f %.3f %.3f",
69                 minp.x*1000-gauge*3, -maxp.y*1000-gauge*3, (maxp.x-minp.x)*1000+gauge*6, (maxp.y-minp.y)*1000+gauge*6));
70
71         doc->write_to_file_formatted(fn);
72 }
73
74 void SvgExporter::save_track(const Track &track, xmlpp::Element &group)
75 {
76         const Vector &pos = track.get_position();
77         float rot = track.get_rotation();
78         string transform = format("translate(%.3f %.3f) rotate(%.3f)", pos.x*1000, -pos.y*1000, -rot*180/M_PI);
79         group.set_attribute("transform", transform);
80
81         const TrackType &type = track.get_type();
82
83         const vector<TrackType::Endpoint> &endpoints = type.get_endpoints();
84         for(vector<TrackType::Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
85         {
86                 xmlpp::Element *elem = group.add_child("path");
87                 elem->set_attribute("class", "endpoint");
88
89                 float dx = -sin(i->dir)*gauge;
90                 float dy = -cos(i->dir)*gauge;
91
92                 string data = format("M %.3f %.3f L %.3f %.3f",
93                         i->pos.x*1000+dx, -i->pos.y*1000+dy,
94                         i->pos.x*1000-dx, -i->pos.y*1000-dy);
95                 elem->set_attribute("d", data);
96         }
97
98         const vector<TrackPart> &parts = type.get_parts();
99         for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
100         {
101                 TrackPoint start = i->get_point(0);
102                 TrackPoint end = i->get_point(i->get_length());
103                 if(i->is_curved())
104                 {
105                         xmlpp::Element *elem = group.add_child("path");
106                         elem->set_attribute("class", "rail");
107
108                         float cs = cos(start.dir);
109                         float ss = sin(start.dir);
110                         float ce = cos(end.dir);
111                         float se = sin(end.dir);
112                         float dx1 = -ss*(gauge+rail_width)*0.5;
113                         float dy1 = -cs*(gauge+rail_width)*0.5;
114                         float dx2 = -se*(gauge+rail_width)*0.5;
115                         float dy2 = -ce*(gauge+rail_width)*0.5;
116                         // Largely an educated guess, but seems to be accurate enough
117                         float clen = i->get_length()*1000/2.9;
118
119                         string data = format("M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
120                                 (start.pos.x*1000+dx1)(-start.pos.y*1000+dy1)
121                                 (start.pos.x*1000+dx1+cs*clen)(-start.pos.y*1000+dy1-ss*clen)
122                                 (end.pos.x*1000+dx2-ce*clen)(-end.pos.y*1000+dy2+se*clen)
123                                 (end.pos.x*1000+dx2)(-end.pos.y*1000+dy2).str();
124                         data += format(" M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
125                                 (start.pos.x*1000-dx1)(-start.pos.y*1000-dy1)
126                                 (start.pos.x*1000-dx1+cs*clen)(-start.pos.y*1000-dy1-ss*clen)
127                                 (end.pos.x*1000-dx2-ce*clen)(-end.pos.y*1000-dy2+se*clen)
128                                 (end.pos.x*1000-dx2)(-end.pos.y*1000-dy2).str();
129                         elem->set_attribute("d", data);
130                 }
131                 else
132                 {
133                         xmlpp::Element *elem = group.add_child("path");
134                         elem->set_attribute("class", "rail");
135
136                         float dx = -sin(start.dir)*(gauge+rail_width)*0.5;
137                         float dy = -cos(start.dir)*(gauge+rail_width)*0.5;
138
139                         string data = format("M %.3f %.3f L %.3f %.3f",
140                                 start.pos.x*1000+dx, -start.pos.y*1000+dy,
141                                 end.pos.x*1000+dx, -end.pos.y*1000+dy);
142                         data += format(" M %.3f %.3f L %.3f %.3f",
143                                 start.pos.x*1000-dx, -start.pos.y*1000-dy,
144                                 end.pos.x*1000-dx, -end.pos.y*1000-dy);
145                         elem->set_attribute("d", data);
146                 }
147         }
148
149         TrackPoint label_pt = parts.front().get_point(parts.front().get_length()/2);
150
151         while(rot+label_pt.dir>M_PI/2)
152                 label_pt.dir -= M_PI;
153         while(rot+label_pt.dir<-M_PI/2)
154                 label_pt.dir += M_PI;
155
156         label_pt.pos.x = label_pt.pos.x*1000+sin(label_pt.dir)*gauge*0.25;
157         label_pt.pos.y = label_pt.pos.y*1000-cos(label_pt.dir)*gauge*0.25;
158
159         xmlpp::Element *elem = group.add_child("text");
160         elem->set_attribute("class", "artnr");
161         elem->set_attribute("transform", format("translate(%.3f %.3f) rotate(%.3f)",
162                 label_pt.pos.x, -label_pt.pos.y, -label_pt.dir*180/M_PI));
163         elem->set_child_text(track.get_type().get_article_number().str());
164 }