]> git.tdb.fi Git - libs/gltk.git/blob - source/layout.cpp
ce9c8dcc9831afd3aaf0af1d345d5b40490d35ab
[libs/gltk.git] / source / layout.cpp
1 #include <algorithm>
2 #include <limits>
3 #include <msp/core/maputils.h>
4 #include <msp/strings/format.h>
5 #include "arrangement.h"
6 #include "container.h"
7 #include "layout.h"
8 #include "widget.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GLtk {
14
15 class Layout::LinearProgram
16 {
17 public:
18         class Row
19         {
20         private:
21                 LinearProgram &linprog;
22                 unsigned index;
23
24         public:
25                 Row(LinearProgram &, unsigned);
26
27                 float &operator[](unsigned);
28                 float &back();
29         };
30
31 private:
32         struct Column
33         {
34                 unsigned basic;
35                 std::vector<float> values;
36
37                 Column();
38         };
39
40         unsigned n_columns;
41         unsigned n_rows;
42         std::vector<Column> columns;
43         bool solved;
44         bool infeasible;
45
46 public:
47         LinearProgram(unsigned);
48
49         Row add_row();
50         Row operator[](unsigned);
51         Row get_objective_row();
52         float get_variable(unsigned);
53         bool solve();
54 private:
55         void prepare_columns();
56         void add_artificial_variables();
57         void remove_artificial_variables();
58         unsigned find_minimal_ratio(unsigned);
59         void make_basic_column(unsigned, unsigned);
60         bool pivot();
61 };
62
63
64 struct Layout::Pointers
65 {
66         int Geometry::*pos;
67         unsigned Geometry::*dim;
68         Packing Slot::*packing;
69         unsigned Sides::*low_margin;
70         unsigned Sides::*high_margin;
71         unsigned Layout::*spacing;
72 };
73
74 Layout::Pointers Layout::pointers[2] =
75 { {
76                 &Geometry::x, &Geometry::w,
77                 &Slot::horiz_pack,
78                 &Sides::left, &Sides::right,
79                 &Layout::col_spacing
80         }, {
81                 &Geometry::y, &Geometry::h,
82                 &Slot::vert_pack,
83                 &Sides::bottom, &Sides::top,
84                 &Layout::row_spacing
85 } };
86
87
88 Layout::Layout():
89         container(0),
90         n_active_slots(0),
91         margin(8),
92         row_spacing(5),
93         col_spacing(4)
94 {
95         n_slack_vars[0] = 0;
96         n_slack_vars[1] = 0;
97 }
98
99 Layout::~Layout()
100 {
101         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
102                 delete *i;
103 }
104
105 void Layout::set_container(Container &c)
106 {
107         if(container)
108                 throw logic_error("container!=0");
109
110         container = &c;
111 }
112
113 void Layout::set_margin(const Sides &m)
114 {
115         margin = m;
116         if(container)
117                 update();
118 }
119
120 void Layout::set_spacing(unsigned s)
121 {
122         row_spacing = s;
123         col_spacing = s;
124         if(container)
125                 update();
126 }
127
128 void Layout::set_row_spacing(unsigned s)
129 {
130         row_spacing = s;
131         if(container)
132                 update();
133 }
134
135 void Layout::set_column_spacing(unsigned s)
136 {
137         col_spacing = s;
138         if(container)
139                 update();
140 }
141
142 void Layout::push_arrangement(Arrangement &arr)
143 {
144         arrangement_stack.push_back(&arr);
145 }
146
147 Arrangement *Layout::get_arrangement() const
148 {
149         if(arrangement_stack.empty())
150                 return 0;
151         else
152                 return arrangement_stack.back();
153 }
154
155 void Layout::pop_arrangement(Arrangement &arr)
156 {
157         list<Arrangement *>::iterator begin = find(arrangement_stack.begin(), arrangement_stack.end(), &arr);
158         if(begin==arrangement_stack.end())
159                 return;
160
161         while(1)
162         {
163                 Arrangement *top = arrangement_stack.back();
164                 arrangement_stack.pop_back();
165                 if(!arrangement_stack.empty())
166                         arrangement_stack.back()->arrange(*top);
167                 if(top==&arr)
168                         break;
169         }
170 }
171
172 void Layout::add_widget(Widget &wdg)
173 {
174         if(!container)
175                 throw logic_error("!container");
176
177         slots.push_back(new Slot(*this, wdg));
178         update_slot_indices();
179         if(!arrangement_stack.empty())
180                 arrangement_stack.back()->arrange(wdg);
181         if(container)
182                 update();
183 }
184
185 void Layout::remove_widget(Widget &wdg)
186 {
187         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
188                 if(&(*i)->widget==&wdg)
189                 {
190                         for(list<Slot *>::iterator j=slots.begin(); j!=slots.end(); ++j)
191                                 if(j!=i)
192                                 {
193                                         for(list<Constraint>::iterator k=(*j)->constraints.begin(); k!=(*j)->constraints.end(); )
194                                         {
195                                                 if(&k->target==*i)
196                                                         (*j)->constraints.erase(k++);
197                                                 else
198                                                         ++k;
199                                         }
200                                 }
201
202                         delete *i;
203                         slots.erase(i);
204
205                         update_slot_indices();
206                         update();
207                         return;
208                 }
209 }
210
211 void Layout::update_slot_indices()
212 {
213         n_active_slots = 0;
214         unsigned n_floating = 0;
215         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
216         {
217                 if((*i)->widget.is_visible() || (*i)->ghost)
218                 {
219                         (*i)->index = n_active_slots++;
220                         if((*i)->floating)
221                                 ++n_floating;
222                 }
223                 else
224                         (*i)->index = -1;
225         }
226
227         n_slack_vars[0] = n_floating*2;
228         n_slack_vars[1] = n_floating*2;
229         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
230                 if((*i)->index>=0)
231                 {
232                         if(!(*i)->floating)
233                         {
234                                 for(unsigned j=0; j<2; ++j)
235                                         if(((*i)->*(pointers[j].packing)).gravity==0)
236                                                 n_slack_vars[j] += 2;
237                         }
238
239                         for(list<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
240                                 if(j->target.index>(*i)->index && (j->type&SLACK))
241                                         ++n_slack_vars[j->type&1];
242                 }
243 }
244
245 Layout::Slot &Layout::get_slot_for_widget(Widget &wdg)
246 {
247         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
248                 if(&(*i)->widget==&wdg)
249                         return **i;
250
251         throw hierarchy_error("widget not in layout");
252 }
253
254 Layout::ConstraintType Layout::complement(ConstraintType type)
255 {
256         return static_cast<ConstraintType>((type&~(SELF_MASK|TARGET_MASK)) | ((type&SELF_MASK)<<2) | ((type&TARGET_MASK)>>2));
257 }
258
259 void Layout::create_constraint(Widget &src, ConstraintType type, Widget &tgt, int sp)
260 {
261         if(&src==&tgt)
262                 throw invalid_argument("Layout::create_constraint");
263
264         Slot &src_slot = get_slot_for_widget(src);
265         Slot &tgt_slot = get_slot_for_widget(tgt);
266
267         for(list<Constraint>::iterator i=src_slot.constraints.begin(); i!=src_slot.constraints.end(); ++i)
268                 if(i->type==type && &i->target==&tgt_slot)
269                         return;
270
271         src_slot.constraints.push_back(Constraint(type, tgt_slot));
272         src_slot.constraints.back().spacing = sp;
273         tgt_slot.constraints.push_back(Constraint(complement(type), src_slot));
274         tgt_slot.constraints.back().spacing = sp;
275
276         update_slot_indices();
277         update();
278 }
279
280 void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt)
281 {
282         create_constraint(src, type, tgt, -1);
283 }
284
285 void Layout::add_constraint(Widget &src, ConstraintType type, Widget &tgt, unsigned spacing)
286 {
287         create_constraint(src, type, tgt, spacing);
288 }
289
290 void Layout::set_gravity(Widget &wdg, int h, int v)
291 {
292         Slot &slot = get_slot_for_widget(wdg);
293
294         slot.horiz_pack.gravity = h;
295         slot.vert_pack.gravity = v;
296
297         update_slot_indices();
298         update();
299 }
300
301 void Layout::set_expand(Widget &wdg, bool h, bool v)
302 {
303         Slot &slot = get_slot_for_widget(wdg);
304
305         slot.horiz_pack.expand = h;
306         slot.vert_pack.expand = v;
307
308         update();
309 }
310
311 void Layout::set_ghost(Widget &wdg, bool g)
312 {
313         Slot &slot = get_slot_for_widget(wdg);
314
315         slot.ghost = g;
316
317         if(!wdg.is_visible())
318         {
319                 update_slot_indices();
320                 update();
321         }
322 }
323
324 void Layout::set_floating(Widget &wdg, bool f)
325 {
326         Slot &slot = get_slot_for_widget(wdg);
327
328         slot.floating = f;
329
330         update_slot_indices();
331         update();
332 }
333
334 void Layout::update()
335 {
336         solve_constraints(HORIZONTAL, UPDATE);
337         solve_constraints(VERTICAL, UPDATE);
338
339         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
340                 (*i)->widget.set_geometry((*i)->geom);
341 }
342
343 void Layout::autosize(Geometry &geom)
344 {
345         solve_constraints(HORIZONTAL, AUTOSIZE);
346         solve_constraints(VERTICAL, AUTOSIZE);
347
348         geom.w = max(geom.w, autosize_geom.w);
349         geom.h = max(geom.h, autosize_geom.h);
350 }
351
352 void Layout::solve_constraints(int dir, SolveMode mode)
353 {
354         Pointers &ptrs = pointers[dir&VERTICAL];
355
356         const Geometry &geom = container->get_geometry();
357         if(mode==UPDATE && geom.*(ptrs.dim)<margin.*(ptrs.low_margin)+margin.*(ptrs.high_margin))
358                 return;
359
360         /* Set up a linear program to solve the constraints.  The program matrix has
361         five columns for each widget, and one constant column.  The first and second
362         columns of a widget are its position and dimension, respectively.  The
363         remaining three are slack columns; see below for their purposes. */
364         LinearProgram linprog(n_active_slots*5+n_slack_vars[dir]+1);
365         float weight = slots.size()+1;
366         unsigned k = n_active_slots*5;
367         for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
368         {
369                 if((*i)->index<0)
370                         continue;
371
372                 LinearProgram::Row objective = linprog.get_objective_row();
373                 if(mode==AUTOSIZE)
374                 {
375                         objective[(*i)->index*5] = -1;
376                         objective[(*i)->index*5+1] = -1;
377                 }
378                 else
379                 {
380                         if(!(*i)->floating)
381                                 objective[(*i)->index*5] = ((*i)->*(ptrs.packing)).gravity/weight;
382                         objective[(*i)->index*5+1] = (((*i)->*(ptrs.packing)).expand ? weight : -1);
383                 }
384
385                 {
386                         // Prevent the widget from going past the container's low edge.
387                         LinearProgram::Row row = linprog.add_row();
388                         row[(*i)->index*5] = 1;
389                         row[(*i)->index*5+2] = -1;
390                         row.back() = margin.*(ptrs.low_margin);
391                 }
392
393                 if(mode==UPDATE)
394                 {
395                         // Prevent the widget from going past the container's high edge.
396                         LinearProgram::Row row = linprog.add_row();
397                         row[(*i)->index*5] = 1;
398                         row[(*i)->index*5+1] = 1;
399                         row[(*i)->index*5+3] = 1;
400                         row.back() = geom.*(ptrs.dim)-margin.*(ptrs.high_margin);
401                 }
402
403                 if((*i)->floating || ((*i)->*(ptrs.packing)).gravity==0)
404                 {
405                         /* Try to keep the widget as close to a target position as possible.
406                         Since linear programs can't express absolute values directly, use two
407                         opposing slack variables that are optimized for a low value. */
408                         float a = ((*i)->*(ptrs.packing)).gravity*0.5+0.5;
409                         LinearProgram::Row row = linprog.add_row();
410                         row[(*i)->index*5] = 1;
411                         row[(*i)->index*5+1] = a;
412                         row[k] = 1;
413                         row[k+1] = -1;
414                         if((*i)->floating)
415                         {
416                                 const Geometry &cgeom = (*i)->widget.get_geometry();
417                                 row.back() = cgeom.*(ptrs.pos)+cgeom.*(ptrs.dim)*a;
418                         }
419                         else
420                                 row.back() = geom.*(ptrs.dim)/2;
421                         objective[k] = -1;
422                         objective[k+1] = -1;
423                         k += 2;
424                 }
425
426                 {
427                         /* Don't allow the widget's dimension to get below that determined
428                         by autosizing. */
429                         LinearProgram::Row row = linprog.add_row();
430                         row[(*i)->index*5+1] = 1;
431                         row[(*i)->index*5+4] = -1;
432                         row.back() = (*i)->autosize_geom.*(ptrs.dim);
433                 }
434
435                 /* Add rows for user-defined constraints.  Constraints are always added
436                 in pairs, so it's only necessary to create a row for one half. */
437                 for(list<Constraint>::iterator j=(*i)->constraints.begin(); j!=(*i)->constraints.end(); ++j)
438                         if(j->target.index>(*i)->index && (j->type&1)==dir)
439                         {
440                                 LinearProgram::Row row = linprog.add_row();
441                                 float polarity = ((j->type&SELF_DIM) ? -1 : 1);
442                                 float dim_weight = ((j->type&HALF_DIM) ? 0.5f : 1);
443                                 if(j->type&SELF_POS)
444                                         row[(*i)->index*5] = polarity;
445                                 if(j->type&SELF_DIM)
446                                         row[(*i)->index*5+1] = polarity*dim_weight;
447                                 if(j->type&TARGET_POS)
448                                         row[j->target.index*5] = -polarity;
449                                 if(j->type&TARGET_DIM)
450                                         row[j->target.index*5+1] = -polarity*dim_weight;
451                                 if(j->type&SPACING)
452                                         row.back() = (j->spacing>=0 ? j->spacing : this->*(ptrs.spacing));
453                                 if(j->type&SLACK)
454                                         row[k++] = -1;
455                         }
456         }
457
458         if(!linprog.solve())
459                 return;
460
461         if(mode==AUTOSIZE)
462         {
463                 autosize_geom.*(ptrs.dim) = 0;
464                 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
465                         if((*i)->index>=0)
466                         {
467                                 int high_edge = linprog.get_variable((*i)->index*5)+linprog.get_variable((*i)->index*5+1);
468                                 autosize_geom.*(ptrs.dim) = max(autosize_geom.*(ptrs.dim), high_edge+margin.*(ptrs.high_margin));
469                         }
470         }
471         else
472         {
473                 for(list<Slot *>::iterator i=slots.begin(); i!=slots.end(); ++i)
474                         if((*i)->index>=0)
475                         {
476                                 (*i)->geom.*(ptrs.pos) = linprog.get_variable((*i)->index*5);
477                                 (*i)->geom.*(ptrs.dim) = linprog.get_variable((*i)->index*5+1);
478                         }
479         }
480 }
481
482
483 Layout::Constraint::Constraint(ConstraintType t, Slot &s):
484         type(t),
485         target(s),
486         spacing(-1)
487 { }
488
489
490 Layout::Packing::Packing():
491         gravity(-1),
492         expand(false)
493 { }
494
495
496 Layout::Slot::Slot(Layout &l, Widget &w):
497         layout(l),
498         index(0),
499         widget(w),
500         ghost(false),
501         floating(false)
502 {
503         vert_pack.gravity = 1;
504         widget.signal_autosize_changed.connect(sigc::mem_fun(this, &Slot::autosize_changed));
505         widget.signal_visibility_changed.connect(sigc::mem_fun(this, &Slot::visibility_changed));
506         widget.autosize(autosize_geom);
507 }
508
509 void Layout::Slot::autosize_changed()
510 {
511         widget.autosize(autosize_geom);
512
513         if(!widget.is_visible() && !ghost)
514                 return;
515
516         // Only trigger an update if the widget won't fit in its current area.
517         if(autosize_geom.w>geom.w || autosize_geom.h>geom.h)
518         {
519                 layout.container->signal_autosize_changed.emit();
520                 layout.update();
521         }
522 }
523
524 void Layout::Slot::visibility_changed(bool v)
525 {
526         layout.update_slot_indices();
527         if(v || ghost)
528         {
529                 layout.container->signal_autosize_changed.emit();
530                 layout.update();
531         }
532 }
533
534
535 Layout::Loader::Loader(Layout &l, const WidgetMap &wm):
536         DataFile::ObjectLoader<Layout>(l),
537         wdg_map(wm)
538 {
539         add("column_spacing", &Loader::column_spacing);
540         add("margin",         &Loader::margin);
541         add("row_spacing",    &Loader::row_spacing);
542         add("spacing",        &Loader::spacing);
543         add("widget",         &Loader::widget);
544 }
545
546 void Layout::Loader::column_spacing(unsigned s)
547 {
548         obj.set_column_spacing(s);
549 }
550
551 void Layout::Loader::margin()
552 {
553         Sides sides;
554         load_sub(sides);
555         obj.set_margin(sides);
556 }
557
558 void Layout::Loader::spacing(unsigned s)
559 {
560         obj.set_spacing(s);
561 }
562
563 void Layout::Loader::row_spacing(unsigned s)
564 {
565         obj.set_row_spacing(s);
566 }
567
568 void Layout::Loader::widget(const string &n)
569 {
570         Widget &wdg = *get_item(wdg_map, n);
571         WidgetLoader ldr(obj, wdg, wdg_map);
572         load_sub_with(ldr);
573 }
574
575
576 Layout::WidgetLoader::WidgetLoader(Layout &l, Widget &w, const Layout::Loader::WidgetMap &wm):
577         layout(l),
578         widget(w),
579         wdg_map(wm)
580 {
581         add("constraint", &WidgetLoader::constraint);
582         add("expand",     &WidgetLoader::expand);
583         add("ghost",      &WidgetLoader::ghost);
584         add("gravity",    &WidgetLoader::gravity);
585 }
586
587 void Layout::WidgetLoader::constraint(ConstraintType type, const string &n)
588 {
589         Widget &target = *get_item(wdg_map, n);
590         layout.add_constraint(widget, type, target);
591 }
592
593 void Layout::WidgetLoader::expand(bool h, bool v)
594 {
595         layout.set_expand(widget, h, v);
596 }
597
598 void Layout::WidgetLoader::ghost(bool g)
599 {
600         layout.set_ghost(widget, g);
601 }
602
603 void Layout::WidgetLoader::gravity(int h, int v)
604 {
605         layout.set_gravity(widget, h, v);
606 }
607
608
609 void operator>>(const LexicalConverter &conv, Layout::ConstraintType &ctype)
610 {
611         const string &str = conv.get();
612         if(str=="ABOVE")
613                 ctype = Layout::ABOVE;
614         else if(str=="BELOW")
615                 ctype = Layout::BELOW;
616         else if(str=="RIGHT_OF")
617                 ctype = Layout::RIGHT_OF;
618         else if(str=="LEFT_OF")
619                 ctype = Layout::LEFT_OF;
620         else if(str=="FAR_ABOVE")
621                 ctype = Layout::FAR_ABOVE;
622         else if(str=="FAR_BELOW")
623                 ctype = Layout::FAR_BELOW;
624         else if(str=="FAR_RIGHT_OF")
625                 ctype = Layout::FAR_RIGHT_OF;
626         else if(str=="FAR_LEFT_OF")
627                 ctype = Layout::FAR_LEFT_OF;
628         else if(str=="ALIGN_TOP")
629                 ctype = Layout::ALIGN_TOP;
630         else if(str=="ALIGN_BOTTOM")
631                 ctype = Layout::ALIGN_BOTTOM;
632         else if(str=="ALIGN_RIGHT")
633                 ctype = Layout::ALIGN_RIGHT;
634         else if(str=="ALIGN_LEFT")
635                 ctype = Layout::ALIGN_LEFT;
636         else if(str=="COPY_WIDTH")
637                 ctype = Layout::COPY_WIDTH;
638         else if(str=="COPY_HEIGHT")
639                 ctype = Layout::COPY_HEIGHT;
640         else
641                 throw lexical_error(format("conversion of '%s' to ConstraintType", str));
642 }
643
644
645 Layout::LinearProgram::LinearProgram(unsigned s):
646         n_columns(s),
647         n_rows(1),
648         columns(n_columns),
649         solved(false),
650         infeasible(false)
651 { }
652
653 Layout::LinearProgram::Row Layout::LinearProgram::add_row()
654 {
655         return Row(*this, n_rows++);
656 }
657
658 Layout::LinearProgram::Row Layout::LinearProgram::operator[](unsigned r)
659 {
660         if(r>=n_rows)
661                 throw out_of_range("LinearProgram::operator[]");
662
663         return Row(*this, r);
664 }
665
666 Layout::LinearProgram::Row Layout::LinearProgram::get_objective_row()
667 {
668         return Row(*this, 0);
669 }
670
671 float Layout::LinearProgram::get_variable(unsigned i)
672 {
673         if(!solved || infeasible)
674                 throw logic_error("not solved");
675         if(i+1>=n_columns)
676                 throw out_of_range("LinearProgram::get_variable");
677
678         if(unsigned r = columns[i].basic)
679                 return columns.back().values[r];
680         else
681                 return 0;
682 }
683
684 bool Layout::LinearProgram::solve()
685 {
686         if(solved || infeasible)
687                 return !infeasible;
688
689         /* Solve the program using the simplex method.  The column representing the
690         objective variable is kept implicit, as it would never change during the
691         execution of the algorithm. */
692
693         prepare_columns();
694
695         add_artificial_variables();
696
697         // Solve the phase 1 problem.
698         while(pivot()) ;
699
700         /* All artificial variables should now be non-basic and thus zero, so the
701         objective function's value should also be zero.  If it isn't, the original
702         program can't be solved. */
703         if(columns.back().values.front())
704         {
705                 infeasible = true;
706                 return false;
707         }
708
709         remove_artificial_variables();
710
711         // Solve the phase 2 problem.  We already know it to be feasible.
712         while(pivot()) ;
713
714         solved = true;
715
716         return true;
717 }
718
719 void Layout::LinearProgram::prepare_columns()
720 {
721         /* See if any variables are already basic.  A basic variable must only have
722         a nonzero coefficient on one row, and its product with the constant column
723         must not be negative.  Only one variable can be basic for any given row. */
724         vector<float> obj_coeff(n_rows, 0.0f);
725         vector<float> row_coeff(n_rows, 1.0f);
726         const vector<float> &constants = columns.back().values;
727         for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
728         {
729                 if(i->values.size()>=2 && i->values.back()!=0.0f && (constants.size()<i->values.size() || i->values.back()*constants[i->values.size()-1]>=0.0f) && obj_coeff[i->values.size()-1]==0.0f)
730                 {
731                         bool basic = true;
732                         for(unsigned j=1; (basic && j+1<i->values.size()); ++j)
733                                 basic = (i->values[j]==0.0f);
734                         if(basic)
735                         {
736                                 i->basic = i->values.size()-1;
737                                 row_coeff[i->basic] = 1.0f/i->values.back();
738                                 obj_coeff[i->basic] = -i->values.front();
739                                 i->values.clear();
740                         }
741                 }
742         }
743
744         // Price out the newly-created basic variables.
745         for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
746                 if(!i->values.empty())
747                 {
748                         for(unsigned j=0; j<i->values.size(); ++j)
749                         {
750                                 i->values[j] *= row_coeff[j];
751                                 i->values.front() += obj_coeff[j]*i->values[j];
752                         }
753                 }
754 }
755
756 void Layout::LinearProgram::add_artificial_variables()
757 {
758         vector<unsigned> artificial_rows(n_rows-1);
759         for(unsigned i=0; i<artificial_rows.size(); ++i)
760                 artificial_rows[i] = i+1;
761
762         for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
763                 if(i->basic)
764                         artificial_rows[i->basic-1] = 0;
765         artificial_rows.erase(std::remove(artificial_rows.begin(), artificial_rows.end(), 0), artificial_rows.end());
766
767         /* Force all non-basic columns fully into existence and relocate objective
768         row to bottom in preparation of phase 1.  A new objective row is calculated
769         by pricing out the constraint rows. */
770         for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
771                 if(!i->basic)
772                 {
773                         float objective = 0.0f;
774                         if(!i->values.empty())
775                         {
776                                 objective = i->values.front();
777                                 i->values.front() = 0.0f;
778                                 for(vector<unsigned>::iterator j=artificial_rows.begin(); j!=artificial_rows.end(); ++j)
779                                         if(*j<i->values.size())
780                                                 i->values.front() += i->values[*j];
781                         }
782                         i->values.resize(n_rows+1, 0.0f);
783                         i->values.back() = objective;
784                 }
785
786         if(artificial_rows.empty())
787                 return;
788
789         /* Create artificial variables for phase 1.  This ensures that each row has
790         a basic variable associated with it.  The original objective row already
791         contains the implicit objective variable, which is basic. */
792         columns.resize(n_columns+artificial_rows.size());
793         columns.back() = columns[n_columns-1];
794         columns[n_columns-1].values.clear();
795         for(unsigned i=0; i<artificial_rows.size(); ++i)
796                 columns[n_columns+i-1].basic = artificial_rows[i];
797 }
798
799 void Layout::LinearProgram::remove_artificial_variables()
800 {
801         /* See if any artificial variables are still basic.  This could be because
802         the program is degenerate.  To avoid trouble later on, use pivots to make
803         some of the original variables basic instead.
804
805         I don't fully understand why this is needed, but it appears to work. */
806         for(unsigned i=n_columns-1; i+1<columns.size(); ++i)
807                 if(columns[i].basic && columns.back().values[columns[i].basic]==0.0f)
808                 {
809                         for(unsigned j=0; j+1<n_columns; ++j)
810                                 if(!columns[j].basic && columns[j].values[columns[i].basic]!=0.0f)
811                                 {
812                                         make_basic_column(j, columns[i].basic);
813                                         break;
814                                 }
815                 }
816
817         /* Get rid of the artificial variables and restore the original objective
818         row to form the phase 2 problem. */
819         columns.erase(columns.begin()+(n_columns-1), columns.end()-1);
820         for(vector<Column>::iterator i=columns.begin(); i!=columns.end(); ++i)
821                 if(!i->basic)
822                 {
823                         i->values.front() = i->values.back();
824                         i->values.pop_back();
825                 }
826 }
827
828 unsigned Layout::LinearProgram::find_minimal_ratio(unsigned c)
829 {
830         /* Pick the row with the minimum ratio between the constant column and the
831         pivot column.  This ensures that when the pivot column is made basic, values
832         in the constant column stay positive.
833
834         The use of n_rows instead of the true size of the column is intentional,
835         since the relocated objective row must be ignored in phase 1. */
836         float best = numeric_limits<float>::infinity();
837         unsigned row = 0;
838         for(unsigned i=1; i<n_rows; ++i)
839                 if(columns[c].values[i]>0)
840                 {
841                         float ratio = columns.back().values[i]/columns[c].values[i];
842                         if(ratio<best)
843                         {
844                                 best = ratio;
845                                 row = i;
846                         }
847                 }
848
849         return row;
850 }
851
852 void Layout::LinearProgram::make_basic_column(unsigned c, unsigned r)
853 {
854         /* Perform row transfer operations to make the pivot column basic,
855         containing a 1 on the pivot row. */
856         for(unsigned i=0; i<columns.size(); ++i)
857                 if(i!=c && (columns[i].basic==r || (!columns[i].basic && columns[i].values[r])))
858                 {
859                         if(columns[i].basic)
860                         {
861                                 columns[i].values.resize(columns.back().values.size(), 0.0f);
862                                 columns[i].values[columns[i].basic] = 1.0f;
863                                 columns[i].basic = 0;
864                         }
865
866                         float scale = columns[i].values[r]/columns[c].values[r];
867
868                         columns[i].values[r] = scale;
869                         for(unsigned j=0; j<columns[i].values.size(); ++j)
870                                 if(j!=r)
871                                         columns[i].values[j] -= scale*columns[c].values[j];
872                 }
873
874         columns[c].basic = r;
875         columns[c].values.clear();
876 }
877
878 bool Layout::LinearProgram::pivot()
879 {
880         /* Pick a nonbasic column and make it basic.  Requiring a positive objective
881         coefficient ensures that the objective function's value will decrease in the
882         process. */
883         for(unsigned i=0; i+1<columns.size(); ++i)
884                 if(!columns[i].basic && columns[i].values.front()>0)
885                         if(unsigned row = find_minimal_ratio(i))
886                         {
887                                 make_basic_column(i, row);
888                                 return true;
889                         }
890
891         return false;
892 }
893
894
895 Layout::LinearProgram::Row::Row(LinearProgram &lp, unsigned i):
896         linprog(lp),
897         index(i)
898 { }
899
900 float &Layout::LinearProgram::Row::operator[](unsigned c)
901 {
902         if(c>=linprog.n_columns)
903                 throw out_of_range("Row::operator[]");
904
905         Column &column = linprog.columns[c];
906         if(column.values.size()<=index)
907                 column.values.resize(index+1);
908
909         return column.values[index];
910 }
911
912 float &Layout::LinearProgram::Row::back()
913 {
914         return (*this)[linprog.n_columns-1];
915 }
916
917
918 Layout::LinearProgram::Column::Column():
919         basic(0)
920 { }
921
922 } // namespace GLtk
923 } // namespace Msp