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);
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
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 mtor << "col " << i<<' ';
363 for (PCursor<Idealspacing*> i(ideal_p_list_.top()); i.ok(); i++){
372 Spring_spacer::connect(int i, int j, Real d, Real h)
374 Idealspacing * s = new Idealspacing;
380 ideal_p_list_.bottom().add(s);
384 walk through all durations in all Score_columns
386 struct Durations_iter
388 Spring_spacer * sp_l_;
392 Durations_iter(Spring_spacer*);
394 Moment duration()const;
401 Durations_iter::Durations_iter(Spring_spacer * s)
407 if (! sp_l_->scol_l(col_i_)->durations.size() )
412 Durations_iter::duration() const
414 return sp_l_->scol_l(col_i_)->durations[d_i_];
418 Durations_iter::ok()const{
419 return col_i_ < sp_l_->cols.size();
423 Durations_iter::when()const{
424 return sp_l_->scol_l(col_i_)->when();
428 Durations_iter::next()
431 while ( col_i_ < sp_l_->cols.size()
432 && d_i_ >= sp_l_->scol_l(col_i_)->durations.size()){
440 generate springs between columns.
444 TODO: This needs rethinking. Spacing should take optical
445 effects into account, and should be local (measure wide)
447 The algorithm is taken from :
449 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
450 OSU-CISRC-10/87-TR35, Department of Computer and Information
451 Science, The Ohio State University, 1987.
455 Spring_spacer::calc_idealspacing()
458 for (int i=0; i < cols.size(); i++)
459 scol_l(i)->preprocess();
461 /* get the shortest running note at a time. */
462 Array<Moment> shortest_arr_;
464 Durations_iter d_iter(this);
465 for (int i=0; i < cols.size(); i++) {
466 Moment now = scol_l(i)->when();
467 while ( d_iter.ok() && now >= d_iter.when() ) {
468 if ( now < d_iter.when() + d_iter.duration())
472 if ( d_iter.ok() && now >= d_iter.when()) {
473 Durations_iter d2 = d_iter;
474 Moment shortest = infinity_mom;
475 while (d2.ok() && d2.when() <= now) {
476 shortest = shortest <? d2.duration();
479 shortest_arr_.push( shortest );
481 shortest_arr_.push(0);
486 mtor << "shortest:[ ";
487 for (int i=0; i < shortest_arr_.size(); i++)
488 mtor << shortest_arr_[i] << " ";
492 Array<Real> ideal_arr_;
493 Array<Real> hooke_arr_;
494 for (int i=0; i < cols.size(); i++){
495 ideal_arr_.push( -1.0);
496 hooke_arr_.push(1.0);
499 for (int i=0; i < cols.size(); i++) {
500 if ( !scol_l(i)->musical_b()) {
501 Real symbol_distance =cols[i].minright() + 2 PT;
502 Real durational_distance = 0;
504 if (i+1 < cols.size()) {
505 Moment delta_t = scol_l(i+1)->when() - scol_l(i)->when() ;
507 durational_distance = paper_l()->duration_to_dist(delta_t) ;
508 symbol_distance += cols[i+1].minleft();
511 ideal_arr_[i] = symbol_distance >? durational_distance;
515 for (int i=0; i < cols.size(); i++) {
516 if (scol_l(i)->musical_b()) {
517 Moment shortest_len = shortest_arr_[i];
518 if ( ! shortest_len ) {
519 warning( "Can't find a ruling note at "
520 +String( scol_l(i)->when()));
523 Moment delta_t = scol_l(i+1)->when() - scol_l(i)->when();
524 Real dist = paper_l()->duration_to_dist(shortest_len);
525 dist *= delta_t / shortest_len;
526 if (!scol_l(i+1)->musical_b() ) {
528 Real minimum_dist = cols[i+1].minleft() + 2 PT + cols[i].minright() ;
529 if (ideal_arr_[i+1] + minimum_dist < dist) {
530 ideal_arr_[i] = dist - ideal_arr_[i+1];
531 // hooke_arr_[i+1] =1.0;
533 ideal_arr_[i] = minimum_dist;
537 ideal_arr_[i] = dist;
541 for (int i=0; i < ideal_arr_.size()-1; i++) {
542 assert (ideal_arr_[i] >=0 && hooke_arr_[i] >=0);
543 connect(i, i+1, ideal_arr_[i], hooke_arr_[i]);
550 Spring_spacer::prepare()
558 Spring_spacer::constructor()
560 return new Spring_spacer;