diff options
Diffstat (limited to '')
-rw-r--r-- | src/toys/metro.cpp | 922 |
1 files changed, 922 insertions, 0 deletions
diff --git a/src/toys/metro.cpp b/src/toys/metro.cpp new file mode 100644 index 0000000..5d2804c --- /dev/null +++ b/src/toys/metro.cpp @@ -0,0 +1,922 @@ +/** Generates approximate metromap lines + * Copyright njh + * Copyright Tim Dwyer + * Published in ISVC 2008, Las Vegas, Nevada, USA + */ + +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <cmath> + +#include <gtk/gtk.h> +#include <cassert> +#include <algorithm> +#include <sstream> +#include <iostream> +#include <fstream> +#include <vector> +#include <string> +#include <map> +#include <cairo-pdf.h> +#include <2geom/point.h> +#include <2geom/geom.h> +#include <toys/toy-framework-2.h> + +using std::string; +using std::vector; +using std::pair; +using std::make_pair; +using std::ifstream; +using std::map; +using std::cout; +using std::endl; +using namespace Geom; + +vector<vector<Point> > paths; + +class sufficient_stats{ +public: + double Sx, Sy, Sxx, Syy, Sxy; + double n; + + sufficient_stats() : Sx(0), Sy(0), Sxx(0), Syy(0), Sxy(0), n(0) {} + void + operator+=(Point p) { + Sx += p[0]; + Sy += p[1]; + Sxx += p[0]*p[0]; + Syy += p[1]*p[1]; + Sxy += p[0]*p[1]; + n += 1.0; + } + void + operator-=(Point p) { + Sx -= p[0]; + Sy -= p[1]; + Sxx -= p[0]*p[0]; + Syy -= p[1]*p[1]; + Sxy -= p[0]*p[1]; + n -= 1.0; + } + /*** What is the best regression we can do? . */ + Point best_normal() { + return rot90(unit_vector(Point(n*Sxx - Sx*Sx, + n*Sxy - Sx*Sy))); + } + /*** Compute the best line for the points, given normal. */ + double best_line(Point normal, const double dist, + double & mean) { + mean = (normal[0]*Sx + normal[1]*Sy); + return normal[0]*normal[0]*Sxx + + normal[1]*normal[1]*Syy + + 2*normal[0]*normal[1]*Sxy + - 2*dist*mean + n*dist*dist; + } + /*** Returns the index to the angle in angles that has the line of best fit + * passing through mean */ + unsigned best_schematised_line(vector<Point>& angles, Point p, + double & /*mean*/, double & cost) { + cost = DBL_MAX; + unsigned bestAngle = 0; + for(unsigned i=0;i<angles.size();i++) { + Point n = unit_vector(rot90(angles[i])); + double dist = dot(n, p); + double mean; + double bl = best_line(n, dist, mean); + if(bl < cost) { + cost = bl; + bestAngle = i; + } + } + return bestAngle; + } + /*** Compute the best line for the points, given normal. */ + double best_angled_line(Point normal, + double & mean) { + mean = (normal[0]*Sx + normal[1]*Sy); + double dist = mean/n; + return normal[0]*normal[0]*Sxx + + normal[1]*normal[1]*Syy + + 2*normal[0]*normal[1]*Sxy + - 2*dist*mean + n*dist*dist; + } +}; + +sufficient_stats +operator+(sufficient_stats const & a, sufficient_stats const &b) { + sufficient_stats ss; + ss.Sx = a.Sx + b.Sx; + ss.Sy = a.Sy + b.Sy; + ss.Sxx = a.Sxx + b.Sxx; + ss.Sxy = a.Sxy + b.Sxy; + ss.Syy = a.Syy + b.Syy; + ss.n = a.n + b.n; + return ss; +} + +sufficient_stats +operator-(sufficient_stats const & a, sufficient_stats const &b) { + sufficient_stats ss; + ss.Sx = a.Sx - b.Sx; + ss.Sy = a.Sy - b.Sy; + ss.Sxx = a.Sxx - b.Sxx; + ss.Sxy = a.Sxy - b.Sxy; + ss.Syy = a.Syy - b.Syy; + ss.n = a.n - b.n; + return ss; +} + +inline std::ostream &operator<< (std::ostream &out_file, const sufficient_stats &s) { + out_file << "Sx: " << s.Sx + << "Sy: " << s.Sy + << "Sxx: " << s.Sxx + << "Sxy: " << s.Sxy + << "Syy: " << s.Syy + << "n: " << s.n; + return out_file; +} + + + +class fit{ +public: + vector<Point> input; + vector<Point> solution; + + vector<pair<Point,Point> > lines; + vector<int> thickness; + + void + draw(cairo_t* cr) { + /* + if(solution.size() > 1) { + //cairo_set_line_width (cr, 1); + cairo_move_to(cr, solution[0]); + for(unsigned i = 1; i < solution.size(); i++) { + cairo_line_to(cr, solution[i]); + } + } + */ + //cairo_stroke(cr); + for(unsigned i = 0;i<lines.size();i++) { + if(thickness.size()>i) { + cairo_set_line_width (cr, thickness[i]); + } + cairo_move_to(cr, lines[i].first); + cairo_line_to(cr, lines[i].second); + cairo_stroke(cr); + } + } + + void endpoints() { + solution.push_back(input[0]); + solution.push_back(input.back()); + } + + void arbitrary(); + void linear_reg(); + + // sufficient stats from start to each point + vector<sufficient_stats> ac_ss; + + /*** Compute the least squares error for a line between two points on the line. */ + double measure(unsigned from, unsigned to, double & mean) { + sufficient_stats ss = ac_ss[to+1] - ac_ss[from]; + + Point n = unit_vector(rot90(input[to] - input[from])); + double dist = dot(n, input[from]); + return ss.best_line(n, dist, mean); + } + + /*** Compute the best line for the points, given normal. */ + double best_angled_line(unsigned from, unsigned to, + Point n, + double & mean) { + sufficient_stats ss = ac_ss[to+1] - ac_ss[from]; + return ss.best_angled_line(n, mean); + } + + double simple_measure(unsigned from, unsigned to, double & mean) { + Point n = unit_vector(rot90(input[to] - input[from])); + double dist = dot(n, input[from]); // distance from origin + double error = 0; + mean = 0; + for(unsigned l = from; l <= to; l++) { + double d = dot(input[l], n) - dist; + mean += dot(input[l], n); + error += d*d; + } + return error; + } + + void simple_dp(); + void C_simple_dp(); + + void C_endpoint(); + + vector<Geom::Point> angles; + fit(vector<Point> const & in) + :input(in) { + /* + Geom::Point as[] = {Point(0,1), Point(1,0), Point(1,1), Point(1,-1)}; + for(unsigned c = 0; c < 4; c++) { + angles.push_back(unit_vector(as[c])); + } + */ + sufficient_stats ss; + ss.Sx = ss.Sy = ss.Sxx = ss.Sxy = ss.Syy = 0; + ac_ss.push_back(ss); + for(auto & l : input) { + ss += l; + ac_ss.push_back(ss); + } + } + + class block{ + public: + unsigned next; + unsigned angle; + sufficient_stats ss; + double cost; + }; + vector<block> blocks; + void test(); + void merging_version(); + void schematised_merging(unsigned number_of_directions); + + double get_block_line(block& b, Point& d, Point& n, Point& c) { + n = unit_vector(rot90(d)); + c = Point(b.ss.Sx/b.ss.n,b.ss.Sy/b.ss.n); + return 0; + } +}; + +class build_bounds{ +public: + int total_n; + Point starting[2]; + Rect combined; + void add_point(Point const &P) { + if(total_n < 2) { + starting[total_n] = P; + total_n += 1; + if(total_n == 2) + combined = Rect(starting[0], starting[1]); + } else { + combined.expandTo(P); + total_n += 1; + } + } + OptRect result() const { + if(total_n > 1) + return combined; + return OptRect(); + } + build_bounds() : total_n(0) {} +}; + +/** + * returns a point which is portionally between topleft and bottomright in the same way that p was + * in src. + */ +Point map_point(Point p, Rect src, Point topleft, Point bottomright) { + p -= src.min(); + p[0] /= src[0].extent(); + p[1] /= src[1].extent(); + //cout << p << endl; + return Point(topleft[0]*(1-p[0]) + bottomright[0]*p[0], + topleft[1]*(1-p[1]) + bottomright[1]*p[1]); +} + +void parse_data(vector<vector<Point> >& paths, + string location_file_name, + string path_file_name) { + ifstream location_file(location_file_name.c_str()), + path_file(path_file_name.c_str()); + string id,sx,sy; + map<string,Point> idlookup; + build_bounds bld_bounds; + while (getline(location_file,id,',')) + { + getline(location_file,sx,','); + getline(location_file,sy,'\n'); + char *e; + // negative for coordinate system + Point p(strtod(sx.c_str(),&e), strtod(sy.c_str(),&e)); + //cout << id << p << endl; + idlookup[id] = p; + } + + + string l; + while (getline(path_file,l,'\n')) { + vector<Point> ps; + if(l.size() < 2) continue; // skip blank lines + if(l.find(":",0)!=string::npos) continue; // skip labels + string::size_type p=0,q; + while((q=l.find(",",p))!=string::npos || (p < l.size() && (q = l.size()))) { + id = l.substr(p,q-p); + //cout << id << endl; + Point pt = 2*idlookup[id]; + //cout << pt << endl; + bld_bounds.add_point(pt); + ps.push_back(pt); + p=q+1; + } + paths.push_back(ps); + //cout << "*******************************************" << endl; + } + Rect bounds = *bld_bounds.result(); + //cout << bounds.min() << " - " << bounds.max() << endl; + for(auto & path : paths) { + for(auto & j : path) { + j = map_point(j, bounds, + Point(0,512), Point(512*bounds[0].extent()/bounds[1].extent(),0)); + } + } + /* + for(map<string,Point>::iterator it = idlookup.begin(); + it != idlookup.end(); it++) { + (*it).second = map_point((*it).second, bounds, + Point(0,0), Point(100,100)); + //Point(0, 512), Point(512,0)); + cout << (*it).first << ":" << (*it).second << endl; + }*/ + /* + unsigned biggest = 0, biggest_i; + for(unsigned i=0;i<paths.size();i++) { + vector<Point> ps=paths[i]; + if(ps.size()>biggest) { + biggest_i=i; + biggest = ps.size(); + } + for(unsigned j=0;j<ps.size();j++) { + double x=ps[j][0], y=ps[j][1]; + cout << "(" << x << "," << y << ")" << ","; + } + cout << endl; + } + */ +} + +void extremePoints(vector<Point> const & pts, Point const & dir, + Point & min, Point & max) { + double minProj = DBL_MAX, maxProj = -DBL_MAX; + for(auto pt : pts) { + double p = dot(pt,dir); + if(p < minProj) { + minProj = p; + min = pt; + } + if(p > maxProj) { + maxProj = p; + max = pt; + } + } +} + +void fit::test() { + sufficient_stats ss; + const unsigned N = input.size(); + for(unsigned i = 0; i < N; i++) { + ss += input[i]; + } + double best_bl = DBL_MAX; + unsigned best = 0; + for(unsigned i=0;i<angles.size();i++) { + Point n = unit_vector(rot90(angles[i])); + double dist = dot(n, input[0]); + double mean; + double bl = ss.best_line(n, dist, mean); + if(bl < best_bl) { + best = i; + best_bl = bl; + } + mean/=N; + Point d = angles[i]; + Point a = mean*n; + Point min, max; + extremePoints(input,d,min,max); + Point b = dot(min,d)*d; + Point c = dot(max,d)*d; + Point start = a+b; + Point end = a+c; + lines.emplace_back(start,end); + thickness.push_back(1); + } + thickness[best] = 4; +} + +void fit::schematised_merging(unsigned number_of_directions) { + const double link_cost = 0; + const unsigned N = input.size()-1; + blocks.resize(N); + for(unsigned i = 0; i<number_of_directions ; i++) { + double t = M_PI*i/float(number_of_directions); + angles.emplace_back(cos(t),sin(t)); + } + // pairs + for(unsigned i = 0; i < N; i++) { + block b; + sufficient_stats ss; + ss += input[i]; + ss += input[i+1]; + b.ss = ss; + double mean, newcost; + b.angle = ss.best_schematised_line(angles, input[i], mean, newcost); + b.cost = link_cost + newcost; + b.next = i+1; + blocks[i] = b; + //std::cout << ss + //<< std::endl; + } + + // merge(a,b) + while(N>1) + { + block best_block; + unsigned best_idx = 0; + double best = 0; + unsigned beg = 0; + unsigned middle = blocks[beg].next; + unsigned end = blocks[middle].next; + while(middle < N) { + sufficient_stats ss = blocks[beg].ss + blocks[middle].ss; + //ss -= input[middle]; + double mean, newcost; + unsigned bestAngle = ss.best_schematised_line(angles, input[beg], mean, newcost); + double deltaCost = -link_cost - blocks[beg].cost - blocks[middle].cost + + newcost; + /*std::cout << beg << ", " + << middle << ", " + << end << ", " + << deltaCost <<"; " + << newcost <<"; " + << mean << ": " + << ss + << std::endl;*/ + //if(deltaCost < best) { + if(blocks[beg].angle==blocks[middle].angle) { + best = deltaCost; + best = -1; + best_idx = beg; + best_block.ss = ss; + best_block.cost = newcost; + best_block.next = end; + best_block.angle = bestAngle; + } + beg = middle; + middle = end; + end = blocks[end].next; + } + if(best < 0) + blocks[best_idx] = best_block; + else // no improvement possible + break; + } + { + solution.resize(0); // empty list + unsigned beg = 0; + unsigned prev = 0; + while(beg < N) { + block b = blocks[beg]; + { + Point n, c; + Point n1, c1; + Point d = angles[b.angle]; + get_block_line(b,d,n,c); + Point start = c, end = c+10*angles[b.angle]; + Line ln = Line::from_normal_distance(n, dot(c,n)); + if(beg==0) { + //start = intersection of b.line and + // line through input[0] orthogonal to b.line + OptCrossing c = intersection(ln, + Line::from_normal_distance(d, dot(d,input[0]))); + assert(c); + start = ln.pointAt(c->ta); + //line_intersection(n, dot(c,n), d, dot(d,input[0]), start); + } else { + //start = intersection of b.line and blocks[prev].line + block p = blocks[prev]; + if(b.angle!=p.angle) { + get_block_line(p,angles[p.angle],n1,c1); + //line_intersection(n, dot(c,n), n1, dot(c1,n1), start); + OptCrossing c = intersection(ln, + Line::from_normal_distance(n1, dot(c1,n1))); + assert(c); + start = ln.pointAt(c->ta); + } + } + + if (b.next < N) { + //end = intersection of b.line and blocks[b.next].line + block next = blocks[b.next]; + if(b.angle!=next.angle) { + get_block_line(next,angles[next.angle],n1,c1); + //line_intersection(n, dot(c,n), n1, dot(c1,n1), end); + OptCrossing c = intersection(ln, + Line::from_normal_distance(n1, dot(c1,n1))); + assert(c); + end = ln.pointAt(c->ta); + } + } else { + //end = intersection of b.line and + // line through input[N-1] orthogonal to b.line + //line_intersection(n, dot(c,n), d, dot(d,input[N]), end); + OptCrossing c = intersection(ln, + Line::from_normal_distance(d, dot(d,input[N]))); + assert(c); + end = ln.pointAt(c->ta); + } + lines.emplace_back(start,end); + } + prev = beg; + beg = b.next; + } + } +} +void fit::merging_version() { + const double link_cost = 100; + const unsigned N = input.size(); + blocks.resize(N); + // pairs + for(unsigned i = 0; i < N; i++) { + block b; + sufficient_stats ss; + ss.Sx = ss.Sy = ss.Sxx = ss.Sxy = ss.Syy = 0; + ss.n = 0; + ss += input[i]; + ss += input[i+1]; + b.ss = ss; + b.cost = link_cost; + b.next = i+1; + blocks[i] = b; + //std::cout << ss + //<< std::endl; + } + + // merge(a,b) + while(1) + { + block best_block; + unsigned best_idx = 0; + double best = 0; + unsigned beg = 0; + unsigned middle = blocks[beg].next; + unsigned end = blocks[middle].next; + while(end != N) { + sufficient_stats ss = blocks[beg].ss + blocks[middle].ss; + ss -= input[middle]; + double mean; + Point normal = unit_vector(rot90(input[end] - input[beg])); + double dist = dot(normal, input[beg]); + double newcost = ss.best_line(normal, dist, mean); + double deltaCost = -link_cost - blocks[beg].cost - blocks[middle].cost + + newcost; + /*std::cout << beg << ", " + << middle << ", " + << end << ", " + << deltaCost <<"; " + << newcost <<"; " + << mean << ": " + << ss + << std::endl;*/ + if(deltaCost < best) { + best = deltaCost; + best_idx = beg; + best_block.ss = ss; + best_block.cost = newcost; + best_block.next = end; + } + beg = middle; + middle = end; + end = blocks[end].next; + } + if(best < 0) + blocks[best_idx] = best_block; + else // no improvement possible + break; + } + { + solution.resize(0); // empty list + unsigned beg = 0; + while(beg != N) { + solution.push_back(input[beg]); + beg = blocks[beg].next; + } + } +} + + +void fit::arbitrary() { + /*solution.resize(input.size()); + copy(input.begin(), input.end(), solution.begin());*/ + // normals + + double best_error = INFINITY; + double best_mean = 0; + unsigned best_angle = 0; + for(unsigned i = 0; i < angles.size(); i++) { + Point angle = angles[i]; + double mean = 0; + double error = 0; + for(unsigned l = 0; l < input.size(); l++) { + mean += dot(input[i], angle); + } + mean /= input.size(); + for(unsigned l = 0; l < input.size(); l++) { + double d = dot(input[i], angle) - mean; + error += d*d; + } + if(error < best_error) { + best_mean = mean; + best_error = error; + best_angle = i; + } + } + Point angle = angles[best_angle]; + solution.push_back(angle*best_mean + dot(input[0], rot90(angle))*rot90(angle)); + solution.push_back(angle*best_mean + dot(input.back(), rot90(angle))*rot90(angle)); +} + +class reg_line{ +public: + Point parallel, centre, normal; + double Sr, Srr; + unsigned n; +}; + +template<class T> +reg_line +line_best_fit(T b, T e) { + double Sx = 0, + Sy = 0, + Sxx = 0, + Syy = 0, + Sxy = 0; + unsigned n = e - b; + reg_line rl; + rl.n = n; + for(T p = b; p != e; p++) { + Sx += (*p)[0]; + Sy += (*p)[1]; + Sxx += (*p)[0]*(*p)[0]; + Syy += (*p)[1]*(*p)[1]; + Sxy += (*p)[0]*(*p)[1]; + } + + rl.parallel = unit_vector(Point(n*Sxx - Sx*Sx, + n*Sxy - Sx*Sy)); + rl.normal = rot90(rl.parallel); + rl.centre = Point(Sx/n, Sy/n); + rl.Sr = rl.Srr = 0; + for(T p = b; p != e; p++) { + double r = dot(rl.parallel, (*p) - rl.centre); + rl.Sr += fabs(r); + rl.Srr += r*r; + } + return rl; +} + +void fit::linear_reg() { + reg_line rl = line_best_fit(input.begin(), + input.end()); + solution.push_back(rl.centre + dot(rl.parallel, input[0] - rl.centre)*rl.parallel); + solution.push_back(rl.centre + dot(rl.parallel, input.back() - rl.centre)*rl.parallel); +} + +void fit::simple_dp() { + const unsigned N = input.size(); + vector<unsigned> prev(N); + vector<double> penalty(N); + const double bend_pen = 100; + + for(unsigned i = 1; i < input.size(); i++) { + double mean; + double best = measure(0, i, mean); + unsigned best_prev = 0; + for(unsigned j = 1; j < i; j++) { + double err = penalty[j] + bend_pen + measure(j, i, mean); + if(err < best) { + best = err; + best_prev = j; + } + } + penalty[i] = best; + prev[i] = best_prev; + } + + unsigned i = prev.size()-1; + while(i > 0) { + solution.push_back(input[i]); + i = prev[i]; + } + solution.push_back(input[i]); + reverse(solution.begin(), solution.end()); +} + +void fit::C_endpoint() { + const unsigned N = input.size(); + + double best_mean; + double best = best_angled_line(0, N-1, angles[0], best_mean); + unsigned best_dir = 0; + for(unsigned c = 1; c < angles.size(); c++) { + double m; + double err = best_angled_line(0, N-1, angles[c], m); + if(err < best) { + best = err; + best_mean = m; + best_dir = c; + } + + } + Point dir = angles[best_dir]; + Point dirT = rot90(dir); + Point centre = dir*best_mean/N; + + solution.push_back(centre + dot(dirT, input[0] - centre)*dirT); + solution.push_back(centre + dot(dirT, input.back() - centre)*dirT); +} + +void fit::C_simple_dp() { + const unsigned N = input.size(); + + vector<int> prev(N); + vector<double> penalty(N); + vector<unsigned> dir(N); + vector<double> mean(N); + const double bend_pen = 0; + + for(unsigned i = 1; i < input.size(); i++) { + double best_mean; + double best = best_angled_line(0, i, angles[0], best_mean); + unsigned best_prev = 0; + unsigned best_dir = 0; + for(unsigned c = 1; c < angles.size(); c++) { + double m; + double err = best_angled_line(0, i, angles[c], m); + if(err < best) { + best = err; + best_mean = m; + best_dir = c; + best_prev = 0; + } + + } + for(unsigned j = 1; j < i; j++) { + for(unsigned c = 0; c < angles.size(); c++) { + double m; + if(c == dir[j]) + continue; + double err = penalty[j] + bend_pen + + best_angled_line(j, i, angles[c], m); + if(err < best) { + best = err; + best_mean = m; + best_dir = c; + best_prev = j; + } + + } + } + penalty[i] = best; + prev[i] = best_prev; + dir[i] = best_dir; + mean[i] = best_mean; + } + + prev[0] = -1; + unsigned i = prev.size()-1; + unsigned pi = i; + while(i > 0) { + Point bdir = angles[dir[i]]; + Point bdirT = rot90(bdir); + Point centre = bdir*mean[i]/N; + solution.push_back(centre + dot(bdirT, input[i] - centre)*bdirT); + solution.push_back(centre + dot(bdirT, input[pi] - centre)*bdirT); + pi = i; + i = prev[i]; + } + /*Point a = angles[dir[i]]; + Point aT = rot90(a); + solution.push_back(a*mean[i] + + dot(input[i], aT)*aT);*/ + reverse(solution.begin(), solution.end()); +} + + + + + + +class MetroMap: public Toy { +public: + vector<PointSetHandle> metro_lines; + PointHandle directions; + + bool should_draw_numbers() override { return false; } + + void draw(cairo_t *cr, std::ostringstream *notify, int width, int height, bool save, std::ostringstream *timer_stream) override { + double slider_margin = 20; + double slider_top = 20; + double slider_bot = 200; + directions.pos[X] = slider_margin; + if (directions.pos[Y]<slider_top) directions.pos[Y] = slider_top; + if (directions.pos[Y]>slider_bot) directions.pos[Y] = slider_bot; + + unsigned num_directions = 2 + 15*(slider_bot-directions.pos[Y])/(slider_bot-slider_top); + + cairo_move_to(cr,Geom::Point(slider_margin,slider_bot)); + cairo_line_to(cr,Geom::Point(slider_margin,slider_top)); + cairo_set_line_width(cr,.5); + cairo_set_source_rgba (cr, 0., 0.3, 0., 1.); + cairo_stroke(cr); + + cairo_set_source_rgba (cr, 0., 0.5, 0, 1); + cairo_set_line_width (cr, 1); + cairo_set_source_rgba (cr, 0., 0., 0, 0.8); + cairo_set_line_width (cr, 1); + + unsigned N= paths.size(); + for(unsigned i=0;i<N;i++) { + double R,G,B; + convertHSVtoRGB(360.*double(i)/double(N),1,0.75,R,G,B); + metro_lines[i].rgb[0] = R; + metro_lines[i].rgb[1] = G; + metro_lines[i].rgb[2] = B; + cairo_set_source_rgba (cr, R, G, B, 0.8); + fit f(metro_lines[i].pts); + f.schematised_merging(num_directions); + f.draw(cr); + cairo_stroke(cr); + } + cairo_set_source_rgba (cr, 0., 0., 0, 1); + { + PangoLayout* layout = pango_cairo_create_layout (cr); + pango_layout_set_text(layout, + notify->str().c_str(), -1); + + PangoFontDescription *font_desc = pango_font_description_new(); + pango_font_description_set_family(font_desc, "Sans"); + const unsigned size_px = 10; + pango_font_description_set_absolute_size(font_desc, size_px * 1024.0); + pango_layout_set_font_description(layout, font_desc); + PangoRectangle logical_extent; + pango_layout_get_pixel_extents(layout, + NULL, + &logical_extent); + cairo_move_to(cr, 0, height-logical_extent.height); + pango_cairo_show_layout(cr, layout); + } + Toy::draw(cr, notify, width, height, save,timer_stream); + } + + void first_time(int argc, char** argv) override { + string location_file_name("data/london-locations.csv"); + string path_file_name("data/london.txt"); + if(argc > 2) { + location_file_name = argv[1]; + path_file_name = argv[2]; + } + cout << location_file_name << ", " << path_file_name << endl; + parse_data(paths, location_file_name, path_file_name); + for(auto & path : paths) { + metro_lines.emplace_back(); + for(auto & j : path) { + metro_lines.back().push_back(j); + } + } + for(auto & metro_line : metro_lines) { + handles.push_back(&metro_line); + } + handles.push_back(&directions); + } + + MetroMap() { + } + +}; + + + + + +int main(int argc, char **argv) { + init(argc, argv, new MetroMap()); + + return 0; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : + |