lilypond-1.5.9
[lilypond.git] / lily / linespace.cc
blobc2fec09918ab6a17845111593d74e9b67f09ce42
1 #include <math.h>
2 #include "linespace.hh"
3 #include "p-col.hh"
4 #include "debug.hh"
5 #include "qlp.hh"
6 #include "unionfind.hh"
7 #include "idealspacing.hh"
8 #include "pointer.tcc"
10 const Real COLFUDGE=1e-3;
11 template class P<Real>; // ugh.
13 bool
14 Spacing_problem::contains(PCol const *w)
16 for (int i=0; i< cols.size(); i++)
17 if (cols[i].pcol_l_ == w)
18 return true;
19 return false;
22 int
23 Spacing_problem::col_id(PCol const *w)const
25 for (int i=0; i< cols.size(); i++)
26 if (cols[i].pcol_l_ == w)
27 return i;
28 assert(false);
29 return -1;
32 void
33 Spacing_problem::OK() const
35 #ifndef NDEBUG
36 for (int i = 1; i < cols.size(); i++)
37 assert(cols[i].rank_i_ > cols[i-1].rank_i_);
38 for (int i = 1; i < loose_col_arr_.size(); i++)
39 assert(loose_col_arr_[i].rank_i_ > loose_col_arr_[i-1].rank_i_);
40 #endif
43 /**
44 Make sure no unconnected columns happen.
46 void
47 Spacing_problem::handle_loose_cols()
49 Union_find connected(cols.size());
50 Array<int> fixed;
51 for (int i=0; i < ideals.size(); i++) {
52 assert(ideals[i]->hooke > 0);
53 int l = col_id(ideals[i]->left);
54 int r = col_id(ideals[i]->right);
55 connected.connect(l,r);
57 for (int i = 0; i < cols.size(); i++)
58 if (cols[i].fixed())
59 fixed.push(i);
60 for (int i=1; i < fixed.size(); i++)
61 connected.connect(fixed[i-1], fixed[i]);
63 for (int i = cols.size(); i--; ) {
64 if (! connected.equiv(fixed[0], i)) {
65 warning("unconnected column: " + String(i));
66 loosen_column(i);
69 OK();
73 /** Guess a stupid position for loose columns. Put loose columns at
74 regular distances from enclosing calced columns */
75 void
76 Spacing_problem::position_loose_cols(Vector &sol_vec)const
78 if (!loose_col_arr_.size())
79 return ;
80 assert(sol_vec.dim());
81 Array<bool> fix_b_arr;
82 fix_b_arr.set_size(cols.size() + loose_col_arr_.size());
83 Real utter_right_f=-INFTY;
84 Real utter_left_f =INFTY;
85 for (int i=0; i < loose_col_arr_.size(); i++) {
86 fix_b_arr[loose_col_arr_[i].rank_i_] = false;
88 for (int i=0; i < cols.size(); i++) {
89 int r= cols[i].rank_i_;
90 fix_b_arr[r] = true;
91 utter_right_f = utter_right_f >? sol_vec(i);
92 utter_left_f = utter_left_f <? sol_vec(i);
94 Vector v(fix_b_arr.size());
95 int j =0;
96 int k =0;
97 for (int i=0; i < v.dim(); i++) {
98 if (fix_b_arr[i]) {
99 assert(cols[j].rank_i_ == i);
100 v(i) = sol_vec(j++);
101 } else {
102 Real left_pos_f =
103 (j>0) ?sol_vec(j-1) : utter_left_f;
104 Real right_pos_f =
105 (j < sol_vec.dim()) ? sol_vec(j) : utter_right_f;
106 int left_rank = (j>0) ? cols[j-1].rank_i_ : 0;
107 int right_rank = (j<sol_vec.dim()) ? cols[j].rank_i_ : sol_vec.dim();
109 int d_r = right_rank - left_rank;
110 Colinfo loose=loose_col_arr_[k++];
111 int r = loose.rank_i_ ;
112 assert(r > left_rank && r < right_rank);
114 v(i) = (r - left_rank)*left_pos_f/ d_r +
115 (right_rank - r) *right_pos_f /d_r;
118 sol_vec = v;
121 bool
122 Spacing_problem::check_constraints(Vector v) const
124 int dim=v.dim();
125 assert(dim == cols.size());
127 for (int i=0; i < dim; i++) {
129 if (cols[i].fixed()&&
130 abs(cols[i].fixed_position() - v(i)) > COLFUDGE)
131 return false;
133 if (!i)
134 continue;
136 Real mindist=cols[i-1].minright()
137 +cols[i].minleft();
139 // ugh... compares
140 Real dif =v(i) - v(i-1)- mindist;
141 bool b = (dif > - COLFUDGE);
144 if (!b)
145 return false;
148 return true;
151 void
152 Spacing_problem::prepare()
154 handle_loose_cols();
157 bool
158 Spacing_problem::check_feasible() const
160 Vector sol(try_initial_solution());
161 return check_constraints(sol);
164 /// generate a solution which obeys the min distances and fixed positions
165 Vector
166 Spacing_problem::try_initial_solution() const
168 int dim=cols.size();
169 Vector initsol(dim);
170 for (int i=0; i < dim; i++) {
171 if (cols[i].fixed()) {
172 initsol(i)=cols[i].fixed_position();
174 if (i > 0) {
175 Real r =initsol(i-1) + cols[i-1].minright();
176 if (initsol(i) < r ) {
177 warning("overriding fixed position");
178 initsol(i) =r;
182 } else {
183 Real mindist=cols[i-1].minright()
184 +cols[i].minleft();
185 if (mindist < 0.0)
186 warning("Excentric column");
187 initsol(i)=initsol(i-1)+mindist;
191 return initsol;
196 Vector
197 Spacing_problem::find_initial_solution() const
199 Vector v(try_initial_solution());
200 assert(check_constraints(v));
201 return v;
204 // generate the matrices
205 void
206 Spacing_problem::make_matrices(Matrix &quad, Vector &lin, Real &c) const
208 quad.fill(0);
209 lin.fill(0);
210 c = 0;
211 for (int j=0; j < ideals.size(); j++){
212 Idealspacing const*i=ideals[j];
213 int l = col_id(i->left);
214 int r = col_id(i->right);
216 quad(r,r) += i->hooke;
217 quad(r,l) -= i->hooke;
218 quad(l,r) -= i->hooke;
219 quad(l,l) += i->hooke;
221 lin(r) -= i->space*i->hooke;
222 lin(l) += i->space*i->hooke;
224 c += sqr(i->space);
228 // put the constraints into the LP problem
229 void
230 Spacing_problem::make_constraints(Mixed_qp& lp) const
232 int dim=cols.size();
233 for (int j=0; j < dim; j++) {
234 Colinfo c=cols[j];
235 if (c.fixed()) {
236 lp.add_fixed_var(j,c.fixed_position());
238 if (j > 0){
239 Vector c1(dim);
241 c1(j)=1.0 ;
242 c1(j-1)=-1.0 ;
243 lp.add_inequality_cons(c1, cols[j-1].minright() +
244 cols[j].minleft());
249 Array<Real>
250 Spacing_problem::solve() const
252 assert(check_feasible());
253 print();
255 Mixed_qp lp(cols.size());
256 make_matrices(lp.quad,lp.lin, lp.const_term);
257 make_constraints(lp);
258 Vector start=find_initial_solution();
259 Vector sol(lp.solve(start));
260 if (!check_constraints(sol)) {
261 WARN << "solution doesn't satisfy constraints.\n" ;
263 Real energy_f =lp.eval(sol);
264 position_loose_cols(sol);
266 Array<Real> posns(sol);
268 posns.push(energy_f);
269 return posns;
273 add one column to the problem.
275 void
276 Spacing_problem::add_column(PCol *col, bool fixed, Real fixpos)
278 Colinfo c(col,(fixed)? &fixpos : 0);
279 if (cols.size())
280 c.rank_i_ = cols.top().rank_i_+1;
281 else
282 c.rank_i_ = 0;
283 cols.push(c);
286 Array<PCol*>
287 Spacing_problem::error_pcol_l_arr()const
289 Array<PCol*> retval;
290 for (int i=0; i< cols.size(); i++)
291 if (cols[i].ugh_b_)
292 retval.push(cols[i].pcol_l_);
293 for (int i=0; i < loose_col_arr_.size(); i++) {
294 retval.push(loose_col_arr_[i].pcol_l_);
296 return retval;
299 void
300 Spacing_problem::loosen_column(int i)
302 Colinfo c=cols.get(i);
303 for (int i=0; i < ideals.size(); ) {
304 Idealspacing const *i_l =ideals[i];
305 if (i_l->left == c.pcol_l_ || i_l->right == c.pcol_l_)
306 ideals.del(i);
307 else
308 i++;
310 c.ugh_b_ = true;
312 int i=0;
313 for (; i < loose_col_arr_.size(); i++) {
314 if (loose_col_arr_[i].rank_i_ > c.rank_i_)
315 break;
317 loose_col_arr_.insert(c,i);
320 void
321 Spacing_problem::add_ideal(Idealspacing const *i)
323 PCol const *l =i->left;
324 PCol const *r= i->right;
326 if (!contains(l) || !contains(r)) {
327 return;
329 ideals.push(i);
332 void
333 Spacing_problem::print_ideal(Idealspacing const *id)const
335 #ifndef NPRINT
336 int l = col_id(id->left);
337 int r = col_id(id->right);
339 mtor << "between " << l <<","<<r<<":" ;
340 id->print();
341 #endif
344 void
345 Spacing_problem::print() const
347 #ifndef NPRINT
348 for (int i=0; i < cols.size(); i++) {
349 mtor << "col " << i<<' ';
350 cols[i].print();
352 for (int i=0; i < ideals.size(); i++) {
353 print_ideal(ideals[i]);
355 #endif
359 /* **************** */
361 void
362 Colinfo::print() const
364 #ifndef NPRINT
365 mtor << "column { ";
366 if (fixed())
367 mtor << "fixed at " << fixed_position()<<", ";
368 assert(pcol_l_);
369 mtor << "[" << minleft() << ", " << minright() << "]";
370 mtor <<"}\n";
371 #endif
374 Colinfo::Colinfo(PCol *col_l, Real const *fixed_C)
376 if (fixed_C)
377 fixpos_p_.set_l(fixed_C);
378 ugh_b_ = false;
379 pcol_l_ = col_l;
380 width = pcol_l_->width();
384 Colinfo::Colinfo()
386 ugh_b_ = false;
387 pcol_l_ =0;