--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "catalogue.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Catalogue3D::Catalogue3D(const Catalogue &c):
+ catalogue(c)
+{
+ const map<unsigned, TrackType *> &trks = catalogue.get_tracks();
+ for(map<unsigned, TrackType *>::const_iterator i=trks.begin(); i!=trks.end(); ++i)
+ tracks[i->second] = new TrackType3D(*this, *i->second);
+}
+
+const TrackType3D &Catalogue3D::get_track(const TrackType &tt) const
+{
+ map<const TrackType *, TrackType3D *>::const_iterator i = tracks.find(&tt);
+ if(i==tracks.end())
+ throw KeyError("Unknown track type");
+
+ return *i->second;
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLIN3D_CATALOGUE_H_
+#define MARKLIN3D_CATALOGUE_H_
+
+#include <msp/gl/material.h>
+#include "libmarklin/catalogue.h"
+
+namespace Marklin {
+
+class TrackType3D;
+
+class Catalogue3D
+{
+private:
+ const Catalogue &catalogue;
+ std::map<const TrackType *, TrackType3D *> tracks;
+ Msp::GL::Material *ballast_material;
+
+public:
+ Catalogue3D(const Catalogue &);
+
+ const Catalogue &get_catalogue() const { return catalogue; }
+ const TrackType3D &get_track(const TrackType &) const;
+};
+
+}
+
+#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
Layout3D::Layout3D(Layout &l):
layout(l),
+ catalogue(layout.get_catalogue()),
quality(4)
{
layout.signal_track_added.connect(sigc::mem_fun(this, &Layout3D::track_added));
void Layout3D::set_quality(unsigned q)
{
quality = q;
- for(list<Track3D *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- (*i)->set_quality(quality);
}
void Layout3D::render(bool endpoints) const
void Layout3D::track_added(Track &t)
{
- tracks.push_back(new Track3D(t, quality));
+ tracks.push_back(new Track3D(*this, t));
}
void Layout3D::track_removed(Track &t)
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#define MARKLIN3D_LAYOUT_H_
#include "libmarklin/layout.h"
+#include "catalogue.h"
#include "track.h"
namespace Marklin {
{
private:
Layout &layout;
+ Catalogue3D catalogue;
std::list<Track3D *> tracks;
unsigned quality;
Layout3D(Layout &);
~Layout3D();
+ const Catalogue3D &get_catalogue() const { return catalogue; }
void set_quality(unsigned);
const std::list<Track3D *> &get_tracks() const { return tracks; }
void render(bool =false) const;
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <GL/gl.h>
#include <msp/gl/misc.h>
#include "libmarklin/tracktype.h"
+#include "layout.h"
#include "track.h"
+#include "tracktype.h"
using namespace std;
using namespace Msp;
namespace Marklin {
-Track3D::Track3D(Track &t, unsigned q):
+Track3D::Track3D(Layout3D &l, Track &t):
+ layout(l),
track(t),
- color(1, 1, 1),
- varray((GL::NORMAL3, GL::VERTEX3)),
- quality(q)
-{
- build_object();
-}
+ type(layout.get_catalogue().get_track(track.get_type())),
+ color(1, 1, 1)
+{ }
void Track3D::set_color(const Msp::GL::Color &c)
{
color = c;
}
-void Track3D::set_quality(unsigned q)
-{
- quality = q;
- build_object();
-}
-
void Track3D::get_bounds(float angle, Point &minp, Point &maxp) const
{
- const Point &pos = track.get_position();
- float rot = track.get_rotation();
- float slope = track.get_slope();
+ type.get_bounds(angle-track.get_rotation(), minp, maxp);
float c = cos(-angle);
float s = sin(-angle);
- minp.x = maxp.x = c*pos.x-s*pos.y;
- minp.y = maxp.y = s*pos.x+c*pos.y;
- minp.z = pos.z+min(slope, 0.0f);
- maxp.z = pos.z+0.01+max(slope, 0.0f);
-
- float c2 = cos(rot-angle);
- float s2 = sin(rot-angle);
-
- for(vector<Point>::const_iterator i=border.begin(); i!=border.end(); ++i)
- {
- float x = c*pos.x-s*pos.y + c2*i->x-s2*i->y;
- float y = s*pos.x+c*pos.y + s2*i->x+c2*i->y;
+ const Point &pos = track.get_position();
+ minp.x += c*pos.x-s*pos.y;
+ maxp.x += c*pos.x-s*pos.y;
+ minp.y += s*pos.x+c*pos.y;
+ maxp.y += s*pos.x+c*pos.y;
- minp.x = min(minp.x, x);
- minp.y = min(minp.y, y);
- maxp.x = max(maxp.x, x);
- maxp.y = max(maxp.y, y);
- }
+ float slope = track.get_slope();
+ if(slope>0)
+ maxp.z += slope;
+ else
+ minp.z += slope;
}
void Track3D::render() const
glPushName(reinterpret_cast<unsigned>(this));
- varray.apply();
- glColor4f(0.25*color.r, 0.25*color.g, 0.25*color.b, 1);
- glDrawElements(GL_QUADS, base_seq.size(), GL_UNSIGNED_INT, &base_seq[0]);
- if(quality>1)
- {
- glColor4f(0.85*color.r, 0.85*color.g, 0.85*color.b, 1);
- glDrawElements(GL_QUADS, rail_seq.size(), GL_UNSIGNED_INT, &rail_seq[0]);
- }
+ type.render();
glPopName();
glPopMatrix();
{
prepare_render(true);
- varray.apply();
+ (void)path;
+ /*varray.apply();
if(path>=0 && static_cast<unsigned>(path)<path_seq.size())
glDrawElements(GL_QUADS, path_seq[path].size(), GL_UNSIGNED_INT, &path_seq[path][0]);
else
{
for(unsigned i=0; i<path_seq.size(); ++i)
glDrawElements(GL_QUADS, path_seq[i].size(), GL_UNSIGNED_INT, &path_seq[i][0]);
- }
+ }*/
glPopMatrix();
}
glRotatef(track.get_slope()/track.get_type().get_total_length()*180/M_PI, 0, -1, 0);
}
-void Track3D::build_object()
-{
- varray.clear();
- GL::VertexArrayBuilder builder(varray);
-
- base_seq.clear();
- rail_seq.clear();
- path_seq.clear();
- path_seq.resize(track.get_type().get_n_paths());
-
- const vector<TrackPart> &parts = track.get_type().get_parts();
- unsigned index = 0;
- for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- build_part(*i, builder, index);
-}
-
-void Track3D::build_part(const TrackPart &part, GL::VertexArrayBuilder &va_builder, unsigned &base_index)
-{
- static vector<Point> profile;
- if(profile.empty())
- {
- profile.push_back(Point(0, -0.02, 0));
- profile.push_back(Point(0, -0.014, 0.008));
- profile.push_back(Point(0, -0.014, 0.008));
- profile.push_back(Point(0, 0.014, 0.008));
- profile.push_back(Point(0, 0.014, 0.008));
- profile.push_back(Point(0, 0.02, 0));
- for(unsigned i=0; i<2; ++i)
- {
- profile.push_back(Point(0, -0.009+i*0.017, 0.008));
- profile.push_back(Point(0, -0.009+i*0.017, 0.0103));
- profile.push_back(Point(0, -0.009+i*0.017, 0.0103));
- profile.push_back(Point(0, -0.008+i*0.017, 0.0103));
- profile.push_back(Point(0, -0.008+i*0.017, 0.0103));
- profile.push_back(Point(0, -0.008+i*0.017, 0.008));
- }
- profile.push_back(Point(0, -0.002, 0.012));
- profile.push_back(Point(0, 0.002, 0.012));
- }
- static unsigned psize = profile.size();
-
- unsigned nsegs = (part.radius ? static_cast<unsigned>(part.length*(1<<quality))+1 : 1);
- float plen = part.length;
- if(part.radius)
- plen *= abs(part.radius);
-
- for(unsigned i=0; i<=nsegs; ++i)
- {
- float a = part.dir+(part.radius ? i*plen/nsegs/part.radius : 0);
- float c = cos(a);
- float s = sin(a);
- Point p = part.get_point(i*plen/nsegs);
-
- for(unsigned j=0; j<psize; ++j)
- {
- unsigned k = j&~1;
- float dy = profile[k+1].y-profile[k].y;
- float dz = profile[k+1].z-profile[k].z;
- float d = sqrt(dy*dy+dz*dz);
- va_builder.normal(s*dz/d, -c*dz/d, dy/d);
-
- Point v(p.x+c*profile[j].x-s*profile[j].y, p.y+c*profile[j].y+s*profile[j].x, profile[j].z);
- va_builder.vertex(v.x, v.y, v.z);
- if(profile[j].z==0)
- border.push_back(v);
- }
- }
-
- for(unsigned i=0; i<nsegs; ++i)
- {
- for(unsigned j=0; j<3; ++j)
- {
- base_seq.push_back(base_index+i*psize+j*2);
- base_seq.push_back(base_index+(i+1)*psize+j*2);
- base_seq.push_back(base_index+(i+1)*psize+1+j*2);
- base_seq.push_back(base_index+i*psize+1+j*2);
- }
- for(unsigned j=3; j<9; ++j)
- {
- rail_seq.push_back(base_index+i*psize+j*2);
- rail_seq.push_back(base_index+(i+1)*psize+j*2);
- rail_seq.push_back(base_index+(i+1)*psize+1+j*2);
- rail_seq.push_back(base_index+i*psize+1+j*2);
- }
- path_seq[part.path].push_back(base_index+i*psize+18);
- path_seq[part.path].push_back(base_index+(i+1)*psize+18);
- path_seq[part.path].push_back(base_index+(i+1)*psize+19);
- path_seq[part.path].push_back(base_index+i*psize+19);
- }
-
- base_index += (nsegs+1)*psize;
-}
-
} // namespace Marklin
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
namespace Marklin {
+class Layout3D;
+class TrackType3D;
+
class Track3D
{
private:
+ Layout3D &layout;
Track &track;
+ const TrackType3D &type;
Msp::GL::Color color;
- std::vector<Point> border;
- Msp::GL::VertexArray varray;
- std::vector<unsigned> base_seq;
- std::vector<unsigned> rail_seq;
- std::vector<std::vector<unsigned> > path_seq;
- unsigned quality;
public:
- Track3D(Track &, unsigned);
+ Track3D(Layout3D &, Track &);
Track &get_track() const { return track; }
void set_color(const Msp::GL::Color &);
- void set_quality(unsigned);
void get_bounds(float, Point &, Point &) const;
void render() const;
void render_endpoints() const;
void render_path(int) const;
private:
void prepare_render(bool) const;
- void build_object();
- void build_part(const TrackPart &, Msp::GL::VertexArrayBuilder &, unsigned &);
};
} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include <cmath>
+#include <msp/gl/meshbuilder.h>
+#include "catalogue.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+bool compare_z(const Marklin::Point &p1, const Marklin::Point &p2)
+{
+ return p1.z<p2.z;
+}
+
+template<typename Iter>
+Iter graham_scan(Iter begin, Iter end)
+{
+ // http://en.wikipedia.org/wiki/Graham_scan
+
+ // Find point with lowest X coordinate
+ Marklin::Point lowest = *begin;
+ for(Iter i=begin; i!=end; ++i)
+ if(i->x<lowest.x || (i->x==lowest.x && i->y>lowest.y))
+ lowest = *i;
+
+ // Compute tangents and sort points
+ for(Iter i=begin; i!=end; ++i)
+ i->z = (i->x==lowest.x ? 1e5/(i->y-lowest.y-1) : (i->y-lowest.y)/(i->x-lowest.x));
+ sort(begin, end, compare_z);
+
+ for(Iter k=begin, i=k++, j=k++;; )
+ {
+ // Compute winding by cross product
+ float turn = (j->x-i->x)*(k->y-j->y) - (k->x-j->x)*(j->y-i->y);
+
+ if(turn<1e-5)
+ {
+ // Right turn - throw the middle point away
+ // Special case for collinear vertices in the beginning
+ if(i==begin)
+ j = k++;
+ else
+ j = i--;
+ }
+ else
+ {
+ // Left turn - store the middle point and advance
+ if(++i!=j)
+ *i = *j;
+ j = k++;
+ }
+
+ // Cycle back to beginning and terminate after checking the last point
+ if(k==end)
+ k = begin;
+ else if(j==begin)
+ return ++i;
+ }
+}
+
+}
+
+namespace Marklin {
+
+TrackType3D::TrackType3D(const Catalogue3D &cat3d, const TrackType &tt):
+ ballast_mesh((GL::NORMAL3, GL::COLOR4_UBYTE, GL::VERTEX3)),
+ rail_mesh((GL::NORMAL3, GL::COLOR4_UBYTE, GL::VERTEX3))
+{
+ const Catalogue &cat = cat3d.get_catalogue();
+ const vector<TrackPart> &parts = tt.get_parts();
+
+ const Profile &ballast_profile = cat.get_ballast_profile();
+ const Point &ballast_min = ballast_profile.get_min_coords();
+ const Point &ballast_max = ballast_profile.get_max_coords();
+
+ const Profile &rail_profile = cat.get_rail_profile();
+ const Point &rail_min = rail_profile.get_min_coords();
+ const Point &rail_max = rail_profile.get_max_coords();
+
+ float gauge = cat.get_gauge();
+
+ {
+ unsigned index = 0;
+ GL::MeshBuilder bld(ballast_mesh);
+ bld.color(0.25f, 0.25f, 0.25f);
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ build_part(*i, ballast_profile, Point(0, -ballast_min.y), bld, index);
+ }
+
+ {
+ unsigned index = 0;
+ GL::MeshBuilder bld(rail_mesh);
+ bld.color(0.85f, 0.85f, 0.85f);
+ float y = ballast_max.y-ballast_min.y-rail_min.y;
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ build_part(*i, rail_profile, Point(-gauge/2-rail_max.x, y), bld, index);
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ build_part(*i, rail_profile, Point(gauge/2-rail_min.x, y), bld, index);
+ }
+
+ min_z = max_z = border.front().z;
+ for(vector<Point>::iterator i=border.begin(); i!=border.end(); ++i)
+ {
+ min_z = min(min_z, i->z);
+ max_z = max(max_z, i->z);
+ }
+ border.erase(graham_scan(border.begin(), border.end()), border.end());
+}
+
+void TrackType3D::get_bounds(float angle, Point &minp, Point &maxp) const
+{
+ float c = cos(-angle);
+ float s = sin(-angle);
+
+ minp = maxp = Point();
+ minp.z = min_z;
+ maxp.z = max_z;
+
+ for(vector<Point>::const_iterator i=border.begin(); i!=border.end(); ++i)
+ {
+ float x = c*i->x-s*i->y;
+ float y = s*i->x+c*i->y;
+
+ minp.x = min(minp.x, x);
+ minp.y = min(minp.y, y);
+ maxp.x = max(maxp.x, x);
+ maxp.y = max(maxp.y, y);
+ }
+}
+
+void TrackType3D::render() const
+{
+ ballast_mesh.draw();
+ rail_mesh.draw();
+}
+
+void TrackType3D::build_part(const TrackPart &part, const Profile &profile, const Point &offset, GL::MeshBuilder &bld, unsigned &base_index)
+{
+ unsigned nsegs = (part.radius ? static_cast<unsigned>(part.length*16)+1 : 1);
+ float plen = part.length;
+ if(part.radius)
+ plen *= abs(part.radius);
+
+ unsigned n_points = profile.get_n_points();
+ for(unsigned i=0; i<=nsegs; ++i)
+ {
+ float a = part.dir+(part.radius ? i*plen/nsegs/part.radius : 0);
+ float c = cos(a);
+ float s = sin(a);
+ Point basep = part.get_point(i*plen/nsegs);
+
+ Point p;
+ for(unsigned j=0; j<n_points; ++j)
+ {
+ // TODO: smoothing - only duplicate vertex if the angle is large enough
+
+ p = profile.get_point(j);
+ p.z = basep.z+p.y+offset.y;
+ p.y = basep.y-c*(p.x+offset.x);
+ p.x = basep.x+s*(p.x+offset.x);
+ if(j>0)
+ bld.vertex(p.x, p.y, p.z);
+
+ if(j+1<n_points)
+ {
+ Point n = profile.get_edge_normal(j);
+ bld.normal(s*n.x, -c*n.x, n.y);
+ bld.vertex(p.x, p.y, p.z);
+ }
+
+ border.push_back(p);
+ }
+ }
+
+ for(unsigned i=0; i+1<n_points; ++i)
+ {
+ bld.begin(GL::TRIANGLE_STRIP);
+ for(unsigned j=0; j<=nsegs; ++j)
+ {
+ unsigned k = (j*(n_points-1)+i)*2;
+ bld.element(base_index+k+1);
+ bld.element(base_index+k);
+ }
+ bld.end();
+ }
+
+ base_index += (nsegs+1)*(n_points-1)*2;
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLIN3D_TRACKTYPE_H_
+#define MARKLIN3D_TRACKTYPE_H_
+
+#include <msp/gl/mesh.h>
+#include <msp/gl/meshbuilder.h>
+#include "libmarklin/profile.h"
+#include "libmarklin/tracktype.h"
+
+namespace Marklin {
+
+class Catalogue3D;
+
+class TrackType3D
+{
+private:
+ Msp::GL::Mesh ballast_mesh;
+ Msp::GL::Mesh rail_mesh;
+ std::vector<Point> border;
+ float min_z;
+ float max_z;
+
+public:
+ TrackType3D(const Catalogue3D &, const TrackType &);
+
+ void get_bounds(float, Point &, Point &) const;
+
+ void render() const;
+
+private:
+ void build_part(const TrackPart &, const Profile &, const Point &, Msp::GL::MeshBuilder &, unsigned &);
+ //void optimize_border();
+};
+
+} // namespace Marklin
+
+#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
namespace Marklin {
+Catalogue::Catalogue():
+ scale(1),
+ gauge(1.524)
+{ }
+
Catalogue::~Catalogue()
{
for(map<unsigned, TrackType *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
Catalogue::Loader::Loader(Catalogue &c):
DataFile::BasicLoader<Catalogue>(c)
{
+ add("ballast_profile", &Loader::ballast_profile);
+ add("gauge", &Loader::gauge);
add("locomotive", &Loader::locomotive);
+ add("rail_profile", &Loader::rail_profile);
+ add("scale", &Loader::scale);
add("track", &Loader::track);
}
+void Catalogue::Loader::ballast_profile()
+{
+ load_sub(obj.ballast_profile);
+}
+
+void Catalogue::Loader::gauge(float g)
+{
+ obj.gauge = g/1000;
+}
+
void Catalogue::Loader::locomotive(unsigned art_nr)
{
if(obj.locos.count(art_nr))
obj.locos[art_nr] = loco.release();
}
+void Catalogue::Loader::rail_profile()
+{
+ load_sub(obj.rail_profile);
+}
+
+void Catalogue::Loader::scale(float n, float d)
+{
+ obj.scale = n/d;
+}
+
void Catalogue::Loader::track(unsigned art_nr)
{
if(obj.tracks.count(art_nr))
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <map>
#include <msp/datafile/loader.h>
+#include "profile.h"
namespace Marklin {
public:
Loader(Catalogue &);
private:
+ void ballast_profile();
+ void gauge(float);
void locomotive(unsigned);
+ void rail_profile();
+ void scale(float, float);
void track(unsigned);
};
private:
+ float scale;
+ float gauge;
+ Profile rail_profile;
+ Profile ballast_profile;
std::map<unsigned, TrackType *> tracks;
std::map<unsigned, LocoType *> locos;
public:
+ Catalogue();
~Catalogue();
+ float get_scale() const { return scale; }
+ float get_gauge() const { return gauge; }
+ const Profile &get_rail_profile() const { return rail_profile; }
+ const Profile &get_ballast_profile() const { return ballast_profile; }
TrackType &get_track(unsigned) const;
const std::map<unsigned, TrackType *> &get_tracks() const { return tracks; }
LocoType &get_locomotive(unsigned) const;
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "profile.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+const Point &Profile::get_point(unsigned i) const
+{
+ if(i>=points.size())
+ throw InvalidParameterValue("Index out of range");
+ return points[i];
+}
+
+Point Profile::get_edge_normal(unsigned i) const
+{
+ if(i+1>=points.size())
+ throw InvalidParameterValue("Index out of range");
+ float dx = points[i+1].x-points[i].x;
+ float dy = points[i+1].y-points[i].y;
+ float len = sqrt(dx*dx+dy*dy);
+ return Point(-dx/len, dy/len);
+}
+
+
+Profile::Loader::Loader(Profile &p):
+ DataFile::ObjectLoader<Profile>(p)
+{
+ add("point", &Loader::point);
+}
+
+void Profile::Loader::finish()
+{
+ obj.min_coords = obj.points[0];
+ obj.max_coords = obj.points[0];
+ for(unsigned i=1; i<obj.points.size(); ++i)
+ {
+ obj.min_coords.x = min(obj.min_coords.x, obj.points[i].x);
+ obj.min_coords.y = min(obj.min_coords.y, obj.points[i].y);
+ obj.max_coords.x = max(obj.max_coords.x, obj.points[i].x);
+ obj.max_coords.y = max(obj.max_coords.y, obj.points[i].y);
+ }
+}
+
+void Profile::Loader::point(float x, float y)
+{
+ obj.points.push_back(Point(x/1000, y/1000));
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBMARKLIN_PROFILE_H_
+#define LIBMARKLIN_PROFILE_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "geometry.h"
+
+namespace Marklin {
+
+class Profile
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Profile>
+ {
+ public:
+ Loader(Profile &);
+ private:
+ virtual void finish();
+ void point(float, float);
+ };
+
+private:
+ std::vector<Point> points;
+ Point min_coords;
+ Point max_coords;
+
+public:
+ unsigned get_n_points() const { return points.size(); }
+ const Point &get_point(unsigned) const;
+ const Point &get_min_coords() const { return min_coords; }
+ const Point &get_max_coords() const { return max_coords; }
+ Point get_edge_normal(unsigned) const;
+};
+
+} // namespace Marklin
+
+#endif
/* $Id$ */
+scale 1 87;
+gauge 16.5;
+rail_profile
+{
+ point 0.5 0.0;
+ point 0.5 2.3;
+ point -0.5 2.3;
+ point -0.5 0.0;
+};
+ballast_profile
+{
+ point 20.0 0.0;
+ point 14.0 8.0;
+ point -14.0 8.0;
+ point -20.0 0.0;
+};
+
// Straight
track 24064