38class sufficient_stats{
40 double Sx, Sy, Sxx, Syy, Sxy;
43 sufficient_stats() : Sx(0), Sy(0), Sxx(0), Syy(0), Sxy(0), n(0) {}
68 double best_line(
Point normal,
const double dist,
70 mean = (normal[0]*Sx + normal[1]*Sy);
71 return normal[0]*normal[0]*Sxx
72 + normal[1]*normal[1]*Syy
73 + 2*normal[0]*normal[1]*Sxy
74 - 2*dist*mean + n*dist*dist;
78 unsigned best_schematised_line(vector<Point>& angles,
Point p,
79 double & ,
double & cost) {
81 unsigned bestAngle = 0;
82 for(
unsigned i=0;i<angles.size();i++) {
84 double dist =
dot(n, p);
86 double bl = best_line(n, dist, mean);
95 double best_angled_line(
Point normal,
97 mean = (normal[0]*Sx + normal[1]*Sy);
99 return normal[0]*normal[0]*Sxx
100 + normal[1]*normal[1]*Syy
101 + 2*normal[0]*normal[1]*Sxy
102 - 2*dist*mean + n*dist*dist;
107operator+(sufficient_stats
const & a, sufficient_stats
const &b) {
111 ss.Sxx = a.Sxx + b.Sxx;
112 ss.Sxy = a.Sxy + b.Sxy;
113 ss.Syy = a.Syy + b.Syy;
119operator-(sufficient_stats
const & a, sufficient_stats
const &b) {
123 ss.Sxx = a.Sxx - b.Sxx;
124 ss.Sxy = a.Sxy - b.Sxy;
125 ss.Syy = a.Syy - b.Syy;
130inline std::ostream &operator<< (std::ostream &out_file,
const sufficient_stats &s) {
131 out_file <<
"Sx: " << s.Sx
145 vector<Point> solution;
147 vector<pair<Point,Point> > lines;
148 vector<int> thickness;
162 for(
unsigned i = 0;i<lines.size();i++) {
163 if(thickness.size()>i) {
164 cairo_set_line_width (cr, thickness[i]);
173 solution.push_back(input[0]);
174 solution.push_back(input.back());
181 vector<sufficient_stats> ac_ss;
184 double measure(
unsigned from,
unsigned to,
double & mean) {
185 sufficient_stats ss = ac_ss[to+1] - ac_ss[from];
188 double dist =
dot(n, input[from]);
189 return ss.best_line(n, dist, mean);
193 double best_angled_line(
unsigned from,
unsigned to,
196 sufficient_stats ss = ac_ss[to+1] - ac_ss[from];
197 return ss.best_angled_line(n, mean);
200 double simple_measure(
unsigned from,
unsigned to,
double & mean) {
202 double dist =
dot(n, input[from]);
205 for(
unsigned l = from; l <= to; l++) {
206 double d =
dot(input[l], n) -
dist;
207 mean +=
dot(input[l], n);
218 vector<Geom::Point> angles;
219 fit(vector<Point>
const & in)
228 ss.Sx = ss.Sy = ss.Sxx = ss.Sxy = ss.Syy = 0;
230 for(
auto & l : input) {
243 vector<block> blocks;
245 void merging_version();
246 void schematised_merging(
unsigned number_of_directions);
250 c =
Point(b.ss.Sx/b.ss.n,b.ss.Sy/b.ss.n);
260 void add_point(
Point const &P) {
262 starting[total_n] = P;
265 combined =
Rect(starting[0], starting[1]);
276 build_bounds() : total_n(0) {}
285 p[0] /= src[0].extent();
286 p[1] /= src[1].extent();
288 return Point(topleft[0]*(1-p[0]) + bottomright[0]*p[0],
289 topleft[1]*(1-p[1]) + bottomright[1]*p[1]);
293 string location_file_name,
294 string path_file_name) {
295 ifstream location_file(location_file_name.c_str()),
296 path_file(path_file_name.c_str());
298 map<string,Point> idlookup;
299 build_bounds bld_bounds;
300 while (getline(location_file,
id,
','))
302 getline(location_file,sx,
',');
303 getline(location_file,sy,
'\n');
306 Point p(strtod(sx.c_str(),&e), strtod(sy.c_str(),&e));
313 while (getline(path_file,l,
'\n')) {
315 if(l.size() < 2)
continue;
316 if(l.find(
":",0)!=string::npos)
continue;
317 string::size_type p=0,q;
318 while((q=l.find(
",",p))!=string::npos || (p < l.size() && (q = l.size()))) {
319 id = l.substr(p,q-p);
321 Point pt = 2*idlookup[id];
323 bld_bounds.add_point(pt);
332 for(
auto & path :
paths) {
333 for(
auto & j : path) {
365 double minProj = DBL_MAX, maxProj = -DBL_MAX;
367 double p =
dot(pt,dir);
381 const unsigned N = input.size();
382 for(
unsigned i = 0; i <
N; i++) {
385 double best_bl = DBL_MAX;
387 for(
unsigned i=0;i<angles.size();i++) {
389 double dist =
dot(n, input[0]);
391 double bl = ss.best_line(n, dist, mean);
406 thickness.push_back(1);
411void fit::schematised_merging(
unsigned number_of_directions) {
412 const double link_cost = 0;
413 const unsigned N = input.size()-1;
415 for(
unsigned i = 0; i<number_of_directions ; i++) {
416 double t = M_PI*i/float(number_of_directions);
417 angles.emplace_back(
cos(t),
sin(t));
420 for(
unsigned i = 0; i <
N; i++) {
426 double mean, newcost;
427 b.angle = ss.best_schematised_line(angles, input[i], mean, newcost);
428 b.cost = link_cost + newcost;
439 unsigned best_idx = 0;
442 unsigned middle = blocks[beg].next;
443 unsigned end = blocks[middle].next;
445 sufficient_stats ss = blocks[beg].ss + blocks[middle].ss;
447 double mean, newcost;
448 unsigned bestAngle = ss.best_schematised_line(angles, input[beg], mean, newcost);
449 double deltaCost = -link_cost - blocks[beg].cost - blocks[middle].cost
460 if(blocks[beg].angle==blocks[middle].angle) {
465 best_block.cost = newcost;
466 best_block.next =
end;
467 best_block.angle = bestAngle;
474 blocks[best_idx] = best_block;
483 block b = blocks[beg];
487 Point d = angles[b.angle];
488 get_block_line(b,d,n,
c);
501 block p = blocks[prev];
502 if(b.angle!=p.angle) {
503 get_block_line(p,angles[p.angle],
n1,c1);
514 block next = blocks[b.next];
515 if(b.angle!=next.angle) {
516 get_block_line(next,angles[next.angle],
n1,c1);
539void fit::merging_version() {
540 const double link_cost = 100;
541 const unsigned N = input.size();
544 for(
unsigned i = 0; i <
N; i++) {
547 ss.Sx = ss.Sy = ss.Sxx = ss.Sxy = ss.Syy = 0;
563 unsigned best_idx = 0;
566 unsigned middle = blocks[beg].next;
567 unsigned end = blocks[middle].next;
569 sufficient_stats ss = blocks[beg].ss + blocks[middle].ss;
573 double dist =
dot(normal, input[beg]);
574 double newcost = ss.best_line(normal, dist, mean);
575 double deltaCost = -link_cost - blocks[beg].cost - blocks[middle].cost
585 if(deltaCost < best) {
589 best_block.cost = newcost;
590 best_block.next =
end;
597 blocks[best_idx] = best_block;
605 solution.push_back(input[beg]);
606 beg = blocks[beg].next;
612void fit::arbitrary() {
617 double best_error = INFINITY;
618 double best_mean = 0;
619 unsigned best_angle = 0;
620 for(
unsigned i = 0; i < angles.size(); i++) {
624 for(
unsigned l = 0; l < input.size(); l++) {
625 mean +=
dot(input[i], angle);
627 mean /= input.size();
628 for(
unsigned l = 0; l < input.size(); l++) {
629 double d =
dot(input[i], angle) - mean;
632 if(error < best_error) {
639 solution.push_back(angle*best_mean +
dot(input[0],
rot90(angle))*
rot90(angle));
640 solution.push_back(angle*best_mean +
dot(input.back(),
rot90(angle))*
rot90(angle));
661 for(T p = b; p != e; p++) {
664 Sxx += (*p)[0]*(*p)[0];
665 Syy += (*p)[1]*(*p)[1];
666 Sxy += (*p)[0]*(*p)[1];
671 rl.normal =
rot90(rl.parallel);
672 rl.centre =
Point(Sx/n, Sy/n);
674 for(T p = b; p != e; p++) {
675 double r =
dot(rl.parallel, (*p) - rl.centre);
682void fit::linear_reg() {
685 solution.push_back(rl.centre +
dot(rl.parallel, input[0] - rl.centre)*rl.parallel);
686 solution.push_back(rl.centre +
dot(rl.parallel, input.back() - rl.centre)*rl.parallel);
689void fit::simple_dp() {
690 const unsigned N = input.size();
691 vector<unsigned> prev(
N);
692 vector<double> penalty(
N);
693 const double bend_pen = 100;
695 for(
unsigned i = 1; i < input.size(); i++) {
697 double best = measure(0, i, mean);
698 unsigned best_prev = 0;
699 for(
unsigned j = 1; j < i; j++) {
700 double err = penalty[j] + bend_pen + measure(j, i, mean);
710 unsigned i = prev.size()-1;
712 solution.push_back(input[i]);
715 solution.push_back(input[i]);
716 reverse(solution.begin(), solution.end());
719void fit::C_endpoint() {
720 const unsigned N = input.size();
723 double best = best_angled_line(0,
N-1, angles[0], best_mean);
724 unsigned best_dir = 0;
725 for(
unsigned c = 1;
c < angles.size();
c++) {
727 double err = best_angled_line(0,
N-1, angles[
c], m);
735 Point dir = angles[best_dir];
737 Point centre = dir*best_mean/
N;
739 solution.push_back(centre +
dot(dirT, input[0] - centre)*dirT);
740 solution.push_back(centre +
dot(dirT, input.back() - centre)*dirT);
743void fit::C_simple_dp() {
744 const unsigned N = input.size();
747 vector<double> penalty(
N);
748 vector<unsigned> dir(
N);
749 vector<double> mean(
N);
750 const double bend_pen = 0;
752 for(
unsigned i = 1; i < input.size(); i++) {
754 double best = best_angled_line(0, i, angles[0], best_mean);
755 unsigned best_prev = 0;
756 unsigned best_dir = 0;
757 for(
unsigned c = 1;
c < angles.size();
c++) {
759 double err = best_angled_line(0, i, angles[
c], m);
768 for(
unsigned j = 1; j < i; j++) {
769 for(
unsigned c = 0;
c < angles.size();
c++) {
773 double err = penalty[j] + bend_pen +
774 best_angled_line(j, i, angles[
c], m);
791 unsigned i = prev.size()-1;
794 Point bdir = angles[dir[i]];
796 Point centre = bdir*mean[i]/
N;
797 solution.push_back(centre +
dot(bdirT, input[i] - centre)*bdirT);
798 solution.push_back(centre +
dot(bdirT, input[pi] - centre)*bdirT);
806 reverse(solution.begin(), solution.end());
814class MetroMap:
public Toy {
816 vector<PointSetHandle> metro_lines;
821 void draw(
cairo_t *cr, std::ostringstream *notify,
int width,
int height,
bool save, std::ostringstream *timer_stream)
override {
822 double slider_margin = 20;
823 double slider_top = 20;
824 double slider_bot = 200;
825 directions.
pos[
X] = slider_margin;
826 if (directions.
pos[
Y]<slider_top) directions.
pos[
Y] = slider_top;
827 if (directions.
pos[
Y]>slider_bot) directions.
pos[
Y] = slider_bot;
829 unsigned num_directions = 2 + 15*(slider_bot-directions.
pos[
Y])/(slider_bot-slider_top);
833 cairo_set_line_width(cr,.5);
838 cairo_set_line_width (cr, 1);
840 cairo_set_line_width (cr, 1);
843 for(
unsigned i=0;i<
N;i++) {
846 metro_lines[i].rgb[0] =
R;
847 metro_lines[i].rgb[1] = G;
848 metro_lines[i].rgb[2] = B;
850 fit f(metro_lines[i].pts);
851 f.schematised_merging(num_directions);
857 PangoLayout* layout = pango_cairo_create_layout (cr);
858 pango_layout_set_text(layout,
859 notify->str().c_str(), -1);
862 pango_font_description_set_family(font_desc,
"Sans");
863 const unsigned size_px = 10;
864 pango_font_description_set_absolute_size(font_desc, size_px * 1024.0);
865 pango_layout_set_font_description(layout, font_desc);
866 PangoRectangle logical_extent;
867 pango_layout_get_pixel_extents(layout,
871 pango_cairo_show_layout(cr, layout);
876 void first_time(
int argc,
char** argv)
override {
877 string location_file_name(
"data/london-locations.csv");
878 string path_file_name(
"data/london.txt");
880 location_file_name = argv[1];
881 path_file_name = argv[2];
883 cout << location_file_name <<
", " << path_file_name << endl;
885 for(
auto & path :
paths) {
886 metro_lines.emplace_back();
887 for(
auto & j : path) {
888 metro_lines.back().push_back(j);
891 for(
auto & metro_line : metro_lines) {
892 handles.push_back(&metro_line);
894 handles.push_back(&directions);
906int main(
int argc,
char **argv) {
907 init(argc, argv,
new MetroMap());
Various geometrical calculations.
Cartesian point / 2D vector and related operations.
_PangoFontDescription PangoFontDescription
struct _PangoLayout PangoLayout
void expandTo(CPoint const &p)
Enlarge the rectangle to contain the given point.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
Infinite line on a plane.
static Line from_normal_distance(Point const &n, Coord c)
Create a line normal to a vector at a specified distance from origin.
Point pointAt(Coord t) const
Axis-aligned rectangle that can be empty.
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
virtual void first_time(int, char **)
virtual bool should_draw_numbers()
vector< Handle * > handles
virtual void save(FILE *f)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
void draw(cairo_t *cr, xAx C, Rect bnd)
void parse_data(vector< vector< Point > > &paths, string location_file_name, string path_file_name)
void extremePoints(vector< Point > const &pts, Point const &dir, Point &min, Point &max)
reg_line line_best_fit(T b, T e)
Point map_point(Point p, Rect src, Point topleft, Point bottomright)
returns a point which is portionally between topleft and bottomright in the same way that p was in sr...
vector< vector< Point > > paths
double dist(const Point &a, const Point &b)
void normal(std::vector< Point > &N, std::vector< Point > const &B)
double angle(std::vector< Point > const &A)
Various utility functions.
SBasisN< n > cos(LinearN< n > bo, int k)
Bezier reverse(const Bezier &a)
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
D2< T > operator+=(D2< T > &a, D2< T > const &b)
std::optional< Crossing > OptCrossing
OptCrossing intersection(Ray const &r1, Line const &l2)
D2< T > operator-(D2< T > const &a, D2< T > const &b)
D2< T > operator-=(D2< T > &a, D2< T > const &b)
D2< T > operator+(D2< T > const &a, D2< T > const &b)
Piecewise< SBasis > min(SBasis const &f, SBasis const &g)
Return the more negative of the two functions pointwise.
T dot(D2< T > const &a, D2< T > const &b)
Point unit_vector(Point const &a)
SBasisN< n > sin(LinearN< n > bo, int k)
D2< T > rot90(D2< T > const &a)
void cairo_line_to(cairo_t *cr, Geom::Point p1)
void cairo_move_to(cairo_t *cr, Geom::Point p1)
void convertHSVtoRGB(const double H, const double S, const double V, double &R, double &G, double &B)
void cairo_set_source_rgba(cairo_t *cr, colour c)
void init(int argc, char **argv, Toy *t, int width=600, int height=600)