]> git.tdb.fi Git - r2c2.git/commitdiff
Add an SVG exporter to Designer
authorMikko Rasa <tdb@tdb.fi>
Sun, 7 Nov 2010 21:30:58 +0000 (21:30 +0000)
committerMikko Rasa <tdb@tdb.fi>
Sun, 7 Nov 2010 21:30:58 +0000 (21:30 +0000)
Build
source/designer/designer.cpp
source/designer/designer.h
source/designer/svgexporter.cpp [new file with mode: 0644]
source/designer/svgexporter.h [new file with mode: 0644]

diff --git a/Build b/Build
index a9b5308fdab52e2ae038501f280afb0d3b2d2bc9..6c6687d2c6d23c1caa6a934261c2d7be67c53f02 100644 (file)
--- a/Build
+++ b/Build
@@ -51,6 +51,7 @@ package "märklin"
                require "mspstrings";
                require "mspgltk";
                require "mspgbase";
+               require "libxml++-2.6";
                build_info
                {
                        incpath "source";
index 51dad8f1a6ee4d42b3bcab4bbae1667952e5953e..279d83b6df952654c38b0abcc2467b4e61656d65 100644 (file)
@@ -8,7 +8,7 @@ Distributed under the GPL
 #include <signal.h>
 #include <algorithm>
 #include <cmath>
-#include <GL/gl.h>
+#include <msp/fs/utils.h>
 #include <msp/gl/blend.h>
 #include <msp/gl/framebuffer.h>
 #include <msp/gl/matrix.h>
@@ -34,6 +34,7 @@ Distributed under the GPL
 #include "manipulator.h"
 #include "measure.h"
 #include "selection.h"
+#include "svgexporter.h"
 #include "toolbar.h"
 
 using namespace std;
@@ -225,6 +226,12 @@ void Designer::rename_route()
        input->signal_accept.connect(sigc::mem_fun(this, &Designer::route_name_accept));
 }
 
+void Designer::svg_export()
+{
+       InputDialog *input = new InputDialog(*this, "SVG export", FS::basepart(filename)+".svg");
+       input->signal_accept.connect(sigc::mem_fun(this, &Designer::svg_export_accept));
+}
+
 void Designer::edit_route(Route *r)
 {
        cur_route = r;
@@ -372,6 +379,8 @@ void Designer::key_press(unsigned key, unsigned mod, wchar_t)
                add_selection_to_route();
        else if(key==Msp::Input::KEY_C)
                manipulator.connect();
+       else if(key==Msp::Input::KEY_V)
+               svg_export();
 }
 
 void Designer::button_press(int x, int y, unsigned btn, unsigned mod)
@@ -596,6 +605,12 @@ void Designer::route_name_accept(const string &text)
                cur_route->set_name(text);
 }
 
+void Designer::svg_export_accept(const string &text)
+{
+       SvgExporter svg_exp(*layout);
+       svg_exp.save(text);
+}
+
 string Designer::tooltip(int x, int y)
 {
        if(Track3D *t3d = pick_track(x, y))
index 90d0d3717286cc629cc40d4b48750746fbbd3e8e..c82a2541574506d00427376831da949daa487bb5 100644 (file)
@@ -96,6 +96,7 @@ public:
        void set_turnout_id();
        void set_sensor_id();
        void rename_route();
+       void svg_export();
 
        void edit_route(Marklin::Route *);
        Marklin::Route *get_current_route() const { return cur_route; }
@@ -121,6 +122,7 @@ private:
        void turnout_id_accept(const std::string &);
        void sensor_id_accept(const std::string &);
        void route_name_accept(const std::string &);
+       void svg_export_accept(const std::string &);
        std::string tooltip(int, int);
        void show_route(const Marklin::Route *);
 };
diff --git a/source/designer/svgexporter.cpp b/source/designer/svgexporter.cpp
new file mode 100644 (file)
index 0000000..56945f7
--- /dev/null
@@ -0,0 +1,164 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include <msp/strings/formatter.h>
+#include "libmarklin/catalogue.h"
+#include "libmarklin/track.h"
+#include "libmarklin/tracktype.h"
+#include "svgexporter.h"
+
+using namespace std;
+using namespace Msp;
+using namespace Marklin;
+
+SvgExporter::SvgExporter(const Layout &l):
+       layout(l),
+       gauge(0),
+       rail_width(0)
+{ }
+
+void SvgExporter::save(const string &fn)
+{
+       gauge = layout.get_catalogue().get_gauge()*1000;
+
+       const Profile &rail_profile = layout.get_catalogue().get_rail_profile();
+       const Point &rail_min = rail_profile.get_min_coords();
+       const Point &rail_max = rail_profile.get_max_coords();
+       rail_width = (rail_max.x-rail_min.x)*1000;
+
+       xmlpp::Document *doc = new xmlpp::Document;
+       xmlpp::Element *root = doc->create_root_node("svg", "http://www.w3.org/2000/svg");
+
+       xmlpp::Element *style = root->add_child("style");
+       style->set_attribute("type", "text/css");
+       style->set_child_text(format("\n.rail { fill: none; stroke: #000000; stroke-width: %.3f; }\n"
+               ".endpoint { fill: none; stroke: #808080; stroke-width: %.3f; }\n"
+               ".artnr { text-anchor: middle; font-size: %.3f; }\n",
+               rail_width, rail_width, gauge*0.75));
+
+       Point minp;
+       Point maxp;
+       const set<Track *> &tracks = layout.get_tracks();
+       for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+       {
+               xmlpp::Element *elem = root->add_child("g");
+               save_track(**i, *elem);
+
+               unsigned n_endpoints = (*i)->get_type().get_endpoints().size();
+               for(unsigned j=0; j<n_endpoints; ++j)
+               {
+                       Point pos = (*i)->get_endpoint_position(j);
+                       if(i==tracks.begin() && j==0)
+                               minp = maxp = pos;
+                       else
+                       {
+                               minp.x = min(minp.x, pos.x);
+                               minp.y = min(minp.y, pos.y);
+                               maxp.x = max(maxp.x, pos.x);
+                               maxp.y = max(maxp.y, pos.y);
+                       }
+               }
+       }
+
+       root->set_attribute("viewBox", format("%.3f %.3f %.3f %.3f",
+               minp.x*1000-gauge*3, -maxp.y*1000-gauge*3, (maxp.x-minp.x)*1000+gauge*6, (maxp.y-minp.y)*1000+gauge*6));
+
+       doc->write_to_file_formatted(fn);
+}
+
+void SvgExporter::save_track(const Track &track, xmlpp::Element &group)
+{
+       const Point &pos = track.get_position();
+       float rot = track.get_rotation();
+       string transform = format("translate(%.3f %.3f) rotate(%.3f)", pos.x*1000, -pos.y*1000, -rot*180/M_PI);
+       group.set_attribute("transform", transform);
+
+       const TrackType &type = track.get_type();
+
+       const vector<TrackType::Endpoint> &endpoints = type.get_endpoints();
+       for(vector<TrackType::Endpoint>::const_iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+       {
+               xmlpp::Element *elem = group.add_child("path");
+               elem->set_attribute("class", "endpoint");
+
+               float dx = -sin(i->dir)*gauge;
+               float dy = -cos(i->dir)*gauge;
+
+               string data = format("M %.3f %.3f L %.3f %.3f",
+                       i->pos.x*1000+dx, -i->pos.y*1000+dy,
+                       i->pos.x*1000-dx, -i->pos.y*1000-dy);
+               elem->set_attribute("d", data);
+       }
+
+       const vector<TrackPart> &parts = type.get_parts();
+       for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+       {
+               TrackPoint start = i->get_point(0);
+               TrackPoint end = i->get_point(i->get_length());
+               if(i->is_curved())
+               {
+                       xmlpp::Element *elem = group.add_child("path");
+                       elem->set_attribute("class", "rail");
+
+                       float cs = cos(start.dir);
+                       float ss = sin(start.dir);
+                       float ce = cos(end.dir);
+                       float se = sin(end.dir);
+                       float dx1 = -ss*(gauge+rail_width)*0.5;
+                       float dy1 = -cs*(gauge+rail_width)*0.5;
+                       float dx2 = -se*(gauge+rail_width)*0.5;
+                       float dy2 = -ce*(gauge+rail_width)*0.5;
+                       // Largely an educated guess, but seems to be accurate enough
+                       float clen = i->get_length()*1000/2.9;
+
+                       string data = format("M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
+                               (start.pos.x*1000+dx1)(-start.pos.y*1000+dy1)
+                               (start.pos.x*1000+dx1+cs*clen)(-start.pos.y*1000+dy1-ss*clen)
+                               (end.pos.x*1000+dx2-ce*clen)(-end.pos.y*1000+dy2+se*clen)
+                               (end.pos.x*1000+dx2)(-end.pos.y*1000+dy2).str();
+                       data += format(" M %.3f %.3f C %.3f %.3f %.3f %.3f %.3f %.3f")
+                               (start.pos.x*1000-dx1)(-start.pos.y*1000-dy1)
+                               (start.pos.x*1000-dx1+cs*clen)(-start.pos.y*1000-dy1-ss*clen)
+                               (end.pos.x*1000-dx2-ce*clen)(-end.pos.y*1000-dy2+se*clen)
+                               (end.pos.x*1000-dx2)(-end.pos.y*1000-dy2).str();
+                       elem->set_attribute("d", data);
+               }
+               else
+               {
+                       xmlpp::Element *elem = group.add_child("path");
+                       elem->set_attribute("class", "rail");
+
+                       float dx = -sin(start.dir)*(gauge+rail_width)*0.5;
+                       float dy = -cos(start.dir)*(gauge+rail_width)*0.5;
+
+                       string data = format("M %.3f %.3f L %.3f %.3f",
+                               start.pos.x*1000+dx, -start.pos.y*1000+dy,
+                               end.pos.x*1000+dx, -end.pos.y*1000+dy);
+                       data += format(" M %.3f %.3f L %.3f %.3f",
+                               start.pos.x*1000-dx, -start.pos.y*1000-dy,
+                               end.pos.x*1000-dx, -end.pos.y*1000-dy);
+                       elem->set_attribute("d", data);
+               }
+       }
+
+       TrackPoint label_pt = parts.front().get_point(parts.front().get_length()/2);
+
+       while(rot+label_pt.dir>M_PI/2)
+               label_pt.dir -= M_PI;
+       while(rot+label_pt.dir<-M_PI/2)
+               label_pt.dir += M_PI;
+
+       label_pt.pos.x = label_pt.pos.x*1000+sin(label_pt.dir)*gauge*0.25;
+       label_pt.pos.y = label_pt.pos.y*1000-cos(label_pt.dir)*gauge*0.25;
+
+       xmlpp::Element *elem = group.add_child("text");
+       elem->set_attribute("class", "artnr");
+       elem->set_attribute("transform", format("translate(%.3f %.3f) rotate(%.3f)",
+               label_pt.pos.x, -label_pt.pos.y, -label_pt.dir*180/M_PI));
+       elem->set_child_text(track.get_type().get_article_number().str());
+}
diff --git a/source/designer/svgexporter.h b/source/designer/svgexporter.h
new file mode 100644 (file)
index 0000000..6e93ca4
--- /dev/null
@@ -0,0 +1,29 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef SVGEXPORTER_H_
+#define SVGEXPORTER_H_
+
+#include <libxml++/libxml++.h>
+#include "libmarklin/layout.h"
+
+class SvgExporter
+{
+private:
+       const Marklin::Layout &layout;
+       float gauge;
+       float rail_width;
+
+public:
+       SvgExporter(const Marklin::Layout &);
+
+       void save(const std::string &);
+private:
+       void save_track(const Marklin::Track &, xmlpp::Element &);
+};
+
+#endif