From 6ba6af3637c299ab00828c49de9151429488cc17 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 7 Nov 2010 21:30:58 +0000 Subject: [PATCH] Add an SVG exporter to Designer --- Build | 1 + source/designer/designer.cpp | 17 +++- source/designer/designer.h | 2 + source/designer/svgexporter.cpp | 164 ++++++++++++++++++++++++++++++++ source/designer/svgexporter.h | 29 ++++++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 source/designer/svgexporter.cpp create mode 100644 source/designer/svgexporter.h diff --git a/Build b/Build index a9b5308..6c6687d 100644 --- 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"; diff --git a/source/designer/designer.cpp b/source/designer/designer.cpp index 51dad8f..279d83b 100644 --- a/source/designer/designer.cpp +++ b/source/designer/designer.cpp @@ -8,7 +8,7 @@ Distributed under the GPL #include #include #include -#include +#include #include #include #include @@ -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)) diff --git a/source/designer/designer.h b/source/designer/designer.h index 90d0d37..c82a254 100644 --- a/source/designer/designer.h +++ b/source/designer/designer.h @@ -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 index 0000000..56945f7 --- /dev/null +++ b/source/designer/svgexporter.cpp @@ -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 +#include +#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 &tracks = layout.get_tracks(); + for(set::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; jget_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 &endpoints = type.get_endpoints(); + for(vector::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 &parts = type.get_parts(); + for(vector::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 index 0000000..6e93ca4 --- /dev/null +++ b/source/designer/svgexporter.h @@ -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 +#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 -- 2.45.2