2 spring-spacer.cc -- implement Spring_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1996, 1997--1998, 1998 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"
23 #include "main.hh" // experimental_fietsers
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_b (Paper_column 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++)
71 connected.connect (i->left_i_,i->right_i_);
73 for (int i = 0; i < cols_.size(); i++)
74 if (cols_[i].fixed_b())
76 for (int i=1; i < fixed.size(); i++)
77 connected.connect (fixed[i-1], fixed[i]);
79 for (int i = cols_.size(); i--;)
81 if (! connected.equiv (fixed[0], i))
83 warning (_("unconnected column: ") + String (i));
92 Guess a stupid position for loose columns. Put loose columns at
93 regular distances from enclosing calced columns
96 Spring_spacer::position_loose_cols (Vector &sol_vec) const
98 if (!loose_col_arr_.size())
100 assert (sol_vec.dim());
101 Array<bool> fix_b_arr;
102 fix_b_arr.set_size (cols_.size() + loose_col_arr_.size ());
103 Real utter_right_f=-infinity_f;
104 Real utter_left_f =infinity_f;
105 for (int i=0; i < loose_col_arr_.size(); i++)
107 fix_b_arr[loose_col_arr_[i].rank_i_] = false;
109 for (int i=0; i < cols_.size(); i++)
111 int r= cols_[i].rank_i_;
113 utter_right_f = utter_right_f >? sol_vec (i);
114 utter_left_f = utter_left_f <? sol_vec (i);
116 Vector v (fix_b_arr.size());
119 for (int i=0; i < v.dim(); i++)
123 assert (cols_[j].rank_i_ == i);
124 v (i) = sol_vec (j++);
129 (j>0) ?sol_vec (j-1) : utter_left_f;
131 (j < sol_vec.dim()) ? sol_vec (j) : utter_right_f;
132 int left_rank = (j>0) ? cols_[j-1].rank_i_ : 0;
133 int right_rank = (j<sol_vec.dim()) ? cols_[j].rank_i_ : sol_vec.dim ();
135 int d_r = right_rank - left_rank;
136 Colinfo loose=loose_col_arr_[k++];
137 int r = loose.rank_i_ ;
138 assert (r > left_rank && r < right_rank);
140 v (i) = (r - left_rank)*left_pos_f/ d_r +
141 (right_rank - r) *right_pos_f /d_r;
148 Spring_spacer::check_constraints (Vector v) const
151 assert (dim == cols_.size());
152 DOUT << "checking " << v;
153 for (int i=0; i < dim; i++)
155 if (cols_[i].fixed_b() &&
156 abs (cols_[i].fixed_position() - v (i)) > COLFUDGE)
158 DOUT << "Fixpos broken\n";
161 Array<Spacer_rod> const &rods (cols_[i].rods_[RIGHT]);
162 for (int j =0; j < rods.size (); j++)
164 int other =rods[j].other_idx_;
165 Real diff =v (other) - v (i) ;
166 if (COLFUDGE +diff < rods[j].distance_f_)
168 DOUT << "i, other_i: " << i << " " << other << "\n";
169 DOUT << "dist, minimal = " << diff <<" "
170 << rods[j].distance_f_<<'\n';
179 /** try to generate a solution which obeys the min distances and fixed positions
182 Spring_spacer::try_initial_solution() const
185 if (!try_initial_solution_and_tell (v))
187 warning ("I'm too fat; call Oprah");
194 Spring_spacer::try_initial_solution_and_tell (Vector &v) const
196 int dim=cols_.size();
197 bool succeeded = true;
198 Vector initsol (dim);
200 assert (cols_[0].fixed_b ());
201 DOUT << "fixpos 0 " << cols_[0].fixed_position ();
202 for (int i=0; i < dim; i++)
204 Real min_x = i ? initsol (i-1) : cols_[0].fixed_position ();
205 Array<Spacer_rod> const &sr_arr(cols_[i].rods_[LEFT]);
206 for (int j=0; j < sr_arr.size (); j++)
208 min_x = min_x >? (initsol (sr_arr[j].other_idx_) + sr_arr[j].distance_f_);
212 if (cols_[i].fixed_b())
214 initsol (i)=cols_[i].fixed_position();
215 if (initsol (i) < min_x )
217 DOUT << "failing: init, min : " << initsol (i) << " " << min_x << "\n";
225 DOUT << "tried and told solution: " << v;
227 DOUT << "(failed)\n";
233 // generate the matrices
235 Spring_spacer::make_matrices (Matrix &quad, Vector &lin, Real &c) const
241 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok (); i++)
246 quad (r,r) += i->hooke_f_;
247 quad (r,l) -= i->hooke_f_;
248 quad (l,r) -= i->hooke_f_;
249 quad (l,l) += i->hooke_f_;
251 lin (r) -= i->space_f_*i->hooke_f_;
252 lin (l) += i->space_f_*i->hooke_f_;
254 c += sqr (i->space_f_);
264 Spring_spacer::set_fixed_cols (Mixed_qp &qp) const
266 for (int j=0; j < cols_.size(); j++)
267 if (cols_[j].fixed_b())
268 qp.add_fixed_var (j,cols_[j].fixed_position());
271 // put the constraints into the LP problem
273 Spring_spacer::make_constraints (Mixed_qp& lp) const
275 int dim=cols_.size();
277 for (int j=0; j < dim -1; j++)
279 Array<Spacer_rod> const&rod_arr (cols_[j].rods_[RIGHT]);
280 for (int i = 0; i < rod_arr.size (); i++)
283 c1(rod_arr[i].other_idx_)=1.0 ;
286 lp.add_inequality_cons (c1, rod_arr[i].distance_f_);
293 Spring_spacer::calculate_energy_f (Vector solution) const
296 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok(); i++)
298 e += i->energy_f(solution(i->right_i_) - solution(i->left_i_));
304 Spring_spacer::lower_bound_solution (Col_hpositions*positions) const
306 Mixed_qp lp (cols_.size());
307 make_matrices (lp.quad_,lp.lin_, lp.const_term_);
310 Vector start (cols_.size());
312 Vector solution_vec (lp.solve (start));
314 DOUT << "Lower bound sol: " << solution_vec;
315 positions->energy_f_ = calculate_energy_f (solution_vec);
316 positions->config = solution_vec;
317 positions->satisfies_constraints_b_ = check_constraints (solution_vec);
321 Spring_spacer::solve (Col_hpositions*positions) const
324 DOUT << "Spring_spacer::solve ()...";
327 bool constraint_satisfaction = try_initial_solution_and_tell (solution_try);
328 if (constraint_satisfaction)
330 Mixed_qp lp (cols_.size());
331 make_matrices (lp.quad_,lp.lin_, lp.const_term_);
332 make_constraints (lp);
335 Vector solution_vec (lp.solve (solution_try));
337 positions->satisfies_constraints_b_ = check_constraints (solution_vec);
338 if (!positions->satisfies_constraints_b_)
340 WARN << _("solution doesn't satisfy constraints.\n") ;
342 position_loose_cols (solution_vec);
343 positions->energy_f_ = calculate_energy_f (solution_vec);
344 positions->config = solution_vec;
345 positions->error_col_l_arr_ = error_pcol_l_arr();
349 positions->set_stupid_solution (solution_try);
351 DOUT << "Finished Spring_spacer::solve ()...";
355 add one column to the problem.
358 Spring_spacer::add_column (Paper_column *col, bool fixed, Real fixpos)
360 Colinfo c (col,(fixed)? &fixpos : 0);
361 int this_rank = cols_.size();
362 c.rank_i_ = this_rank;
364 for (int i=0; i < col->minimal_dists_arr_drul_[LEFT].size (); i++)
366 Column_rod &cr = col->minimal_dists_arr_drul_[LEFT][i];
367 int left_idx = cr.other_l_->rank_i () - cols_[0].pcol_l_->rank_i ();
371 if (cols_[left_idx].pcol_l_ != cr.other_l_)
375 l_rod.distance_f_ = cr.distance_f_;
376 l_rod.other_idx_ = left_idx;
377 c.rods_[LEFT].push (l_rod);
380 r_rod.distance_f_ = cr.distance_f_;
381 r_rod.other_idx_ = this_rank;
382 cols_[left_idx].rods_[RIGHT].push (r_rod);
389 Spring_spacer::error_pcol_l_arr() const
391 Array<Paper_column*> retval;
392 for (int i=0; i< cols_.size(); i++)
394 retval.push (cols_[i].pcol_l_);
395 for (int i=0; i < loose_col_arr_.size(); i++)
397 retval.push (loose_col_arr_[i].pcol_l_);
403 Spring_spacer::loosen_column (int i)
405 Colinfo c=cols_.get (i);
406 for (PCursor<Idealspacing*> j (ideal_p_list_.top()); j.ok (); j++)
408 if (j->left_i_ == i|| j->right_i_ == i)
416 for (; j < loose_col_arr_.size(); j++)
418 if (loose_col_arr_[j].rank_i_ > c.rank_i_)
421 loose_col_arr_.insert (c,j);
426 Spring_spacer::print() const
429 for (int i=0; i < cols_.size(); i++)
431 DOUT << "col " << i<<' ';
434 for (PCursor<Idealspacing*> i (ideal_p_list_.top()); i.ok (); i++)
443 Spring_spacer::connect (int i, int j, Real d, Real h)
445 assert(d >= 0 && d <= 100 CM);
448 Idealspacing * s = new Idealspacing;
455 ideal_p_list_.bottom().add (s);
462 Spring_spacer::prepare()
464 DOUT << "Preparing..";
468 DOUT << "finished preparing.\n";
472 Spring_spacer::constructor()
474 return new Spring_spacer;
480 get the shortest_playing running note at a time. */
482 Spring_spacer::get_ruling_durations(Array<Moment> &shortest_playing_arr,
483 Array<Moment> &context_shortest_arr)
485 for (int i=0; i < cols_.size(); i++)
487 scol_l (i)->preprocess();
488 scol_l (i)->print ();
490 int start_context_i=0;
491 Moment context_shortest;
492 context_shortest.set_infinite (1);
493 context_shortest_arr.set_size(cols_.size());
495 for (int i=0; i < cols_.size(); i++)
497 Moment now = scol_l (i)->when();
498 Moment shortest_playing;
499 shortest_playing.set_infinite (1);
501 if (scol_l (i)->breakable_b_)
503 for (int ji=i; ji >= start_context_i; ji--)
504 context_shortest_arr[ji] = context_shortest;
506 context_shortest.set_infinite (1);
508 if (scol_l (i)->durations.size())
510 context_shortest = context_shortest <? scol_l(i)->durations[0];
513 // ji was j, but triggered ICE
514 for (int ji=i+1; ji --;)
516 if (scol_l(ji)->durations.size() &&
517 now - scol_l(ji)->when() >= shortest_playing)
520 for (int k = scol_l (ji)->durations.size();
521 k-- && scol_l(ji)->durations[k] + scol_l(ji)->when() > now;
524 shortest_playing = shortest_playing <? scol_l(ji)->durations[k];
527 shortest_playing_arr.push(shortest_playing);
531 DOUT << "shortest_playing/:[ ";
532 for (int i=0; i < shortest_playing_arr.size(); i++)
534 DOUT << shortest_playing_arr[i] << " ";
535 DOUT << context_shortest_arr[i] << ", ";
542 TODO: take out the refs to width
545 generate springs between columns.
547 TODO: This needs rethinking. Spacing should take optical
550 The algorithm is taken from :
552 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
553 OSU-CISRC-10/87-TR35, Department of Computer and Information
554 Science, The Ohio State University, 1987.
558 Spring_spacer::calc_idealspacing()
560 Array<Moment> shortest_playing_arr;
561 Array<Moment> context_shortest_arr;
562 get_ruling_durations(shortest_playing_arr, context_shortest_arr);
564 Real interline_f = paper_l ()->interline_f ();
565 Real nw_f = paper_l ()->note_width ();
567 Array<Real> ideal_arr_;
568 Array<Real> hooke_arr_;
569 for (int i=0; i < cols_.size() - 1; i++){
570 ideal_arr_.push (-1.0);
571 hooke_arr_.push (1.0);
575 First do all non-musical columns
577 for (int i=0; i < cols_.size(); i++)
579 if (!scol_l (i)->musical_b() && i+1 < cols_.size())
581 Real symbol_distance =cols_[i].width_[RIGHT] + 2 PT;
582 Real durational_distance = 0;
585 Moment delta_t = scol_l (i+1)->when() - scol_l (i)->when () ;
587 Real k= paper_l()->arithmetic_constant (context_shortest_arr[i]);
589 ugh should use shortest_playing distance
592 durational_distance = paper_l()->duration_to_dist (delta_t,k);
593 symbol_distance += -cols_[i+1].width_[LEFT];
596 ideal_arr_[i] = symbol_distance >? durational_distance;
597 hooke_arr_[i] = 1; //2.0;
604 for (int i=0; i < cols_.size(); i++)
606 if (scol_l (i)->musical_b())
608 Moment shortest_playing_len = shortest_playing_arr[i];
609 Moment context_shortest = context_shortest_arr[i];
610 if (! shortest_playing_len)
612 warning (_("Can't find a ruling note at ")
613 +scol_l (i)->when().str ());
614 shortest_playing_len = 1;
616 if (! context_shortest)
618 warning(_("No minimum in measure at ")
619 + scol_l (i)->when().str ());
620 context_shortest = 1;
622 Moment delta_t = scol_l (i+1)->when() - scol_l (i)->when ();
623 Real k= paper_l()->arithmetic_constant(context_shortest);
624 Real dist = paper_l()->duration_to_dist (shortest_playing_len, k);
625 dist *= (double)(delta_t / shortest_playing_len);
628 According to [Ross] and [Wanske], and from what i've seen:
630 * whitespace at the begin of the bar should be fixed at
631 (about) one interline.
633 when spacing gets real tight, a smaller fixed value may be
634 used, so that there are two discrete amounts of whitespace
635 possible at the begin of a bar; but this is not implemented
638 * whitespace at the end of the bar is the normal amount of
639 "hinterfleish" that would have been used, had there been
640 yet another note in the bar.
642 some editors argue that the bar line should not take any
643 space, not to hinder the flow of music spaced around a bar
645 [Ross] and [Wanske] do not suggest this, however. Further,
646 it introduces some spacing problems and think that it is ugly
652 first musical column of bar
654 if (i && scol_l (i - 1)->breakable_b_)
656 // fixed: probably should set minimum (rod/spring)?
657 cols_[i-1].width_[RIGHT] += interline_f;
658 // should adjust dist too?
659 ideal_arr_[i-1] = ideal_arr_[i-1] >? interline_f;
663 last musical column of bar
665 if (i + 1 < cols_.size () && scol_l(i+1)->breakable_b_)
668 dist = dist >? interline_f;
671 uhuh, this code looks fine, already?
672 someone was junking this last "hinterfleisch" whitespace?!
674 but this seems to be fixed now :-)
677 cols_[i].width_[RIGHT] += interline_f;
680 // ugh, do we need this?
681 if (i < cols_.size () - 1 && !scol_l (i + 1)->musical_b ())
683 Real minimum = -cols_[i + 1].width_[LEFT] + cols_[i].width_[RIGHT]
685 dist = dist >? minimum;
688 // ugh: never let columns touch... try to set over here...
689 // ugh: use j iso i triggers ice in gcc-2.7.2.3
690 cols_[i].width_[LEFT] -= nw_f / 4;
691 ideal_arr_[i] = dist;
695 for (int i=0; i < ideal_arr_.size(); i++)
697 assert (ideal_arr_[i] >=0 && hooke_arr_[i] >=0);
698 connect (i, i+1, ideal_arr_[i], hooke_arr_[i]);