2 spring-spacer.cc -- implement Spring_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1996,1997 Han-Wen Nienhuys <hanwen@stack.nl>
12 #include "spring-spacer.hh"
16 #include "unionfind.hh"
17 #include "idealspacing.hh"
18 #include "pointer.tcc"
19 #include "score-column.hh"
20 #include "paper-def.hh"
26 Spring_spacer::default_solution()const
28 return try_initial_solution() ;
32 Spring_spacer::scol_l (int i)
34 return (Score_column*)cols[i].pcol_l_;
37 const Real COLFUDGE=1e-3;
38 template class P<Real>; // ugh.
41 Spring_spacer::contains (PCol const *w)
43 for (int i=0; i< cols.size(); i++)
44 if (cols[i].pcol_l_ == w)
51 Spring_spacer::OK() const
54 for (int i = 1; i < cols.size(); i++)
55 assert (cols[i].rank_i_ > cols[i-1].rank_i_);
56 for (int i = 1; i < loose_col_arr_.size(); i++)
57 assert (loose_col_arr_[i].rank_i_ > loose_col_arr_[i-1].rank_i_);
62 Make sure no unconnected columns happen.
65 Spring_spacer::handle_loose_cols()
67 Union_find connected (cols.size());
69 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok (); i++){
70 connected.connect (i->left_i_,i->right_i_);
72 for (int i = 0; i < cols.size(); i++)
75 for (int i=1; i < fixed.size(); i++)
76 connected.connect (fixed[i-1], fixed[i]);
78 for (int i = cols.size(); i--;) {
79 if (! connected.equiv (fixed[0], i)) {
80 warning ("unconnected column: " + String (i));
89 Guess a stupid position for loose columns. Put loose columns at
90 regular distances from enclosing calced columns
93 Spring_spacer::position_loose_cols (Vector &sol_vec)const
95 if (!loose_col_arr_.size())
97 assert (sol_vec.dim());
98 Array<bool> fix_b_arr;
99 fix_b_arr.set_size (cols.size() + loose_col_arr_.size ());
100 Real utter_right_f=-infinity_f;
101 Real utter_left_f =infinity_f;
102 for (int i=0; i < loose_col_arr_.size(); i++) {
103 fix_b_arr[loose_col_arr_[i].rank_i_] = false;
105 for (int i=0; i < cols.size(); i++) {
106 int r= cols[i].rank_i_;
108 utter_right_f = utter_right_f >? sol_vec (i);
109 utter_left_f = utter_left_f <? sol_vec (i);
111 Vector v (fix_b_arr.size());
114 for (int i=0; i < v.dim(); i++) {
116 assert (cols[j].rank_i_ == i);
117 v (i) = sol_vec (j++);
120 (j>0) ?sol_vec (j-1) : utter_left_f;
122 (j < sol_vec.dim()) ? sol_vec (j) : utter_right_f;
123 int left_rank = (j>0) ? cols[j-1].rank_i_ : 0;
124 int right_rank = (j<sol_vec.dim()) ? cols[j].rank_i_ : sol_vec.dim ();
126 int d_r = right_rank - left_rank;
127 Colinfo loose=loose_col_arr_[k++];
128 int r = loose.rank_i_ ;
129 assert (r > left_rank && r < right_rank);
131 v (i) = (r - left_rank)*left_pos_f/ d_r +
132 (right_rank - r) *right_pos_f /d_r;
139 Spring_spacer::check_constraints (Vector v) const
142 assert (dim == cols.size());
144 for (int i=0; i < dim; i++) {
146 if (cols[i].fixed()&&
147 abs (cols[i].fixed_position() - v (i)) > COLFUDGE)
153 Real mindist=cols[i-1].minright()
157 Real dif =v (i) - v (i-1)- mindist;
158 bool b = (dif > - COLFUDGE);
169 Spring_spacer::check_feasible() const
171 Vector sol (try_initial_solution());
172 return check_constraints (sol);
175 /// generate a solution which obeys the min distances and fixed positions
177 Spring_spacer::try_initial_solution() const
180 Vector initsol (dim);
181 for (int i=0; i < dim; i++) {
182 if (cols[i].fixed()) {
183 initsol (i)=cols[i].fixed_position();
186 Real r =initsol (i-1) + cols[i-1].minright();
187 if (initsol (i) < r) {
188 warning ("overriding fixed position");
194 Real mindist=cols[i-1].minright()
197 warning ("Excentric column");
198 initsol (i)=initsol (i-1)+mindist;
208 Spring_spacer::find_initial_solution() const
210 Vector v (try_initial_solution());
211 assert (check_constraints (v));
215 // generate the matrices
217 Spring_spacer::make_matrices (Matrix &quad, Vector &lin, Real &c) const
223 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok (); i++) {
227 quad (r,r) += i->hooke_f_;
228 quad (r,l) -= i->hooke_f_;
229 quad (l,r) -= i->hooke_f_;
230 quad (l,l) += i->hooke_f_;
232 lin (r) -= i->space_f_*i->hooke_f_;
233 lin (l) += i->space_f_*i->hooke_f_;
235 c += sqr (i->space_f_);
240 Spring_spacer::set_fixed_cols (Mixed_qp &qp)const
242 for (int j=0; j < cols.size(); j++)
244 qp.add_fixed_var (j,cols[j].fixed_position());
249 // put the constraints into the LP problem
251 Spring_spacer::make_constraints (Mixed_qp& lp) const
254 for (int j=0; j < dim; j++) {
261 lp.add_inequality_cons (c1, cols[j-1].minright() +
268 Spring_spacer::lower_bound_solution (Col_hpositions*positions)const
270 Mixed_qp lp (cols.size());
271 make_matrices (lp.quad,lp.lin, lp.const_term);
274 Vector start (cols.size());
276 Vector solution_vec (lp.solve (start));
278 positions->energy_f_ = lp.eval (solution_vec);
279 positions->config = solution_vec;
280 positions->satisfies_constraints_b_ = check_constraints (solution_vec);
284 Spring_spacer::solve (Col_hpositions*positions) const
286 assert (check_feasible());
288 Mixed_qp lp (cols.size());
289 make_matrices (lp.quad,lp.lin, lp.const_term);
290 make_constraints (lp);
292 Vector start=find_initial_solution();
293 Vector solution_vec (lp.solve (start));
296 positions->satisfies_constraints_b_ = check_constraints (solution_vec);
297 if (!positions->satisfies_constraints_b_) {
298 WARN << "solution doesn't satisfy constraints.\n" ;
300 position_loose_cols (solution_vec);
301 positions->energy_f_ = lp.eval (solution_vec);
302 positions->config = solution_vec;
303 positions->error_col_l_arr_ = error_pcol_l_arr();
308 add one column to the problem.
311 Spring_spacer::add_column (PCol *col, bool fixed, Real fixpos)
313 Colinfo c (col,(fixed)? &fixpos : 0);
315 c.rank_i_ = cols.top().rank_i_+1;
322 Spring_spacer::error_pcol_l_arr()const
325 for (int i=0; i< cols.size(); i++)
327 retval.push (cols[i].pcol_l_);
328 for (int i=0; i < loose_col_arr_.size(); i++) {
329 retval.push (loose_col_arr_[i].pcol_l_);
335 Spring_spacer::loosen_column (int i)
337 Colinfo c=cols.get (i);
338 for (PCursor<Idealspacing*> j (ideal_p_list_.top()); j.ok (); j++){
339 if (j->left_i_ == i|| j->right_i_ == i)
347 for (; j < loose_col_arr_.size(); j++) {
348 if (loose_col_arr_[j].rank_i_ > c.rank_i_)
351 loose_col_arr_.insert (c,j);
356 Spring_spacer::print() const
359 for (int i=0; i < cols.size(); i++) {
360 DOUT << "col " << i<<' ';
363 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok (); i++){
371 Spring_spacer::connect (int i, int j, Real d, Real h)
373 Idealspacing * s = new Idealspacing;
379 ideal_p_list_.bottom().add (s);
383 generate springs between columns.
387 TODO: This needs rethinking. Spacing should take optical
388 effects into account, and should be local (measure wide)
390 The algorithm is taken from :
392 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
393 OSU-CISRC-10/87-TR35, Department of Computer and Information
394 Science, The Ohio State University, 1987.
398 Spring_spacer::calc_idealspacing()
401 for (int i=0; i < cols.size(); i++)
402 scol_l (i)->preprocess();
404 /* get the shortest running note at a time. */
405 Array<Moment> shortest_arr;
406 for (int i=0; i < cols.size(); i++) {
407 Moment now = scol_l (i)->when();
408 Moment shortest = infinity_mom;
409 // ji was j, but triggered ICE
410 for (int ji=i+1; ji --; ) {
411 if (scol_l(ji)->durations.size() &&
412 now - scol_l(ji)->when() >= shortest)
415 for (int k = scol_l (ji)->durations.size();
416 k-- && scol_l(ji)->durations[k] + scol_l(ji)->when() > now;
419 shortest = shortest <? scol_l(ji)->durations[k];
422 shortest_arr.push(shortest);
426 DOUT << "shortest:[ ";
427 for (int i=0; i < shortest_arr.size(); i++)
428 DOUT << shortest_arr[i] << " ";
432 Array<Real> ideal_arr_;
433 Array<Real> hooke_arr_;
434 for (int i=0; i < cols.size(); i++){
435 ideal_arr_.push (-1.0);
436 hooke_arr_.push (1.0);
439 for (int i=0; i < cols.size(); i++) {
440 if ( !scol_l (i)->musical_b()) {
441 Real symbol_distance =cols[i].minright() + 2 PT;
442 Real durational_distance = 0;
444 if (i+1 < cols.size()) {
445 Moment delta_t = scol_l (i+1)->when() - scol_l (i)->when () ;
447 ugh should use shortest distance
450 durational_distance = paper_l()->duration_to_dist (delta_t) ;
451 symbol_distance += cols[i+1].minleft();
454 ideal_arr_[i] = symbol_distance >? durational_distance;
458 for (int i=0; i < cols.size(); i++) {
459 if (scol_l (i)->musical_b()) {
460 Moment shortest_len = shortest_arr[i];
461 if ( ! shortest_len) {
462 warning ("Can't find a ruling note at "
463 +String (scol_l (i)->when()));
466 Moment delta_t = scol_l (i+1)->when() - scol_l (i)->when ();
467 Real dist = paper_l()->duration_to_dist (shortest_len);
468 dist *= delta_t / shortest_len;
469 if (!scol_l (i+1)->musical_b()) {
471 Real minimum_dist = cols[i+1].minleft() + 2 PT + cols[i].minright () ;
472 if (ideal_arr_[i+1] + minimum_dist < dist) {
473 ideal_arr_[i] = dist - ideal_arr_[i+1];
474 // hooke_arr_[i+1] =1.0;
476 ideal_arr_[i] = minimum_dist;
480 ideal_arr_[i] = dist;
484 for (int i=0; i < ideal_arr_.size()-1; i++) {
485 assert (ideal_arr_[i] >=0 && hooke_arr_[i] >=0);
486 connect (i, i+1, ideal_arr_[i], hooke_arr_[i]);
493 Spring_spacer::prepare()
501 Spring_spacer::constructor()
503 return new Spring_spacer;