2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
12 #include "line-of-score.hh"
13 #include "paper-score.hh"
14 #include "paper-column.hh"
17 #include "note-spacing.hh"
20 #include "staff-spacing.hh"
25 Don't be confused by right-items: each spacing wish can also contain
26 a number of items, with which a spacing constraint may be kept. It's
27 a little baroque, but it might come in handy later on?
34 static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment) ;
35 static Real note_spacing (Grob*,Grob*,Grob*,Moment) ;
36 static Real get_duration_space (Grob*,Moment dur, Moment shortest) ;
38 static void breakable_column_spacing (Item* l, Item *r);
39 static void find_loose_columns () {}
40 static void prune_loose_colunms (Link_array<Grob> *cols);
41 static void find_loose_columns (Link_array<Grob> cols);
42 static void set_explicit_neighbor_columns (Link_array<Grob> cols);
43 static void set_implicit_neighbor_columns (Link_array<Grob> cols);
44 static void do_measure (Grob*me,Link_array<Grob> *cols);
45 DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
51 Return whether COL is fixed to its neighbors by some kind of spacing
55 loose_column (Grob *l, Grob *c, Grob *r)
57 SCM rns = c->get_grob_property ("right-neighbors");
58 SCM lns = c->get_grob_property ("left-neighbors");
61 If this column doesn't have a proper neighbor, we should really
62 make it loose, but spacing it correctly is more than we can
65 (this happens in the following situation:
76 the column containing the clef is really loose, and should be
77 attached right to the first column, but that is a lot of work for
78 such a borderline case.
83 if (!gh_pair_p (lns) || !gh_pair_p (rns))
86 Item * l_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (lns)));
87 Item * r_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (rns)));
89 if (!l_neighbor || !r_neighbor)
92 l_neighbor = l_neighbor->column_l();
93 r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column (r_neighbor));
95 if (l == l_neighbor && r == r_neighbor)
98 if (!l_neighbor || !r_neighbor)
102 Only declare loose if the bounds make a little sense. This means
103 some cases (two isolated, consecutive clef changes) won't be
104 nicely folded, but hey, then don't do that.
106 if( (Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor))
107 && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor)))
114 If in doubt: we're not loose; the spacing engine should space for
115 it, risking suboptimal spacing.
117 (Otherwise, we might risk core dumps, and other weird stuff.)
125 Remove columns that are not tightly fitting from COLS. In the
126 removed columns, set 'between-cols to the columns where it is in
130 Spacing_spanner::prune_loose_colunms (Link_array<Grob> *cols)
132 Link_array<Grob> newcols;
134 for (int i=0; i < cols->size (); i++)
136 if (Item::breakable_b (cols->elem(i)) || Paper_column::musical_b (cols->elem (i)))
138 newcols.push (cols->elem(i));
142 Grob *c = cols->elem(i);
143 if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
145 SCM lns = c->get_grob_property ("left-neighbors");
146 lns = gh_pair_p (lns) ? gh_car (lns) : SCM_BOOL_F;
148 SCM rns = c->get_grob_property ("right-neighbors");
149 rns = gh_pair_p (rns) ? gh_car (rns) : SCM_BOOL_F;
152 Either object can be non existent, if the score ends
155 rns = gh_car (unsmob_grob (rns)->get_grob_property ("right-items"));
156 c->set_grob_property ("between-cols", gh_cons (lns,
160 Set distance constraints for loose columns
162 Drul_array<Grob*> next_door;
163 next_door[LEFT] =cols->elem (i - 1);
164 next_door[RIGHT] =cols->elem (i + 1);
166 Drul_array<Real> dists(0,0);
171 Grob *lc = (d == LEFT) ? next_door[LEFT] : c;
172 Grob *rc = d == LEFT ? c : next_door[RIGHT];
174 for (SCM s = lc->get_grob_property ("spacing-wishes");
175 gh_pair_p (s); s = gh_cdr (s))
177 Grob *sp = unsmob_grob (gh_car (s));
178 if (Note_spacing::left_column (sp) != lc
179 || Note_spacing::right_column (sp) != rc)
182 dists[d] = dists[d] >? Note_spacing::get_spacing (sp);
185 while (flip (&d) != LEFT);
188 r.distance_f_ = dists[LEFT] + dists[RIGHT];
189 r.item_l_drul_[LEFT] = dynamic_cast<Item*> (cols->elem(i-1));
190 r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
204 Set neighboring columns determined by the spacing-wishes grob property.
207 Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
209 for (int i=0; i < cols.size(); i++)
211 SCM right_neighbors = SCM_EOL;
212 int min_rank = 100000; // inf.
215 SCM wishes= cols[i]->get_grob_property ("spacing-wishes");
216 for (SCM s =wishes; gh_pair_p (s); s = gh_cdr (s))
218 Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
220 Item * lc = wish->column_l ();
221 Grob * right = Note_spacing::right_column (wish);
226 Item * rc = dynamic_cast<Item*> (right);
228 int right_rank = Paper_column::rank_i (rc);
229 int left_rank = Paper_column::rank_i (lc);
232 update the left column.
234 if (right_rank <= min_rank)
236 if (right_rank < min_rank)
237 right_neighbors =SCM_EOL;
239 min_rank = right_rank;
240 right_neighbors = gh_cons (wish->self_scm (), right_neighbors);
244 update the right column of the wish.
247 SCM left_neighs = rc->get_grob_property ("left-neighbors");
248 if (gh_pair_p (left_neighs)
249 && unsmob_grob (gh_car (left_neighs)))
251 Item * it = dynamic_cast<Item*> (unsmob_grob (gh_car (left_neighs)));
252 maxrank = Paper_column::rank_i (it->column_l());
255 if (left_rank >= maxrank)
257 if (left_rank > maxrank)
258 left_neighs = SCM_EOL;
260 left_neighs = gh_cons (wish->self_scm (), left_neighs);
261 rc->set_grob_property ("left-neighbors", right_neighbors);
265 if (gh_pair_p (right_neighbors))
267 cols[i]->set_grob_property ("right-neighbors", right_neighbors);
273 Set neighboring columns that have no left/right-neighbor set
274 yet. Only do breakable non-musical columns, and musical columns.
277 Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
279 for (int i = 0; i < cols.size (); i++)
281 Item * it = dynamic_cast<Item*>(cols[i]);
282 if (!Item::breakable_b (it) && !Paper_column::musical_b (it))
285 // it->breakable || it->musical
288 sloppy with typnig left/right-neighbors should take list, but paper-column found instead.
290 SCM ln = cols[i] ->get_grob_property ("left-neighbors");
291 if (!gh_pair_p (ln) && i )
293 cols[i]->set_grob_property ("left-neighbors", gh_cons (cols[i-1]->self_scm(), SCM_EOL));
296 SCM rn = cols[i] ->get_grob_property ("right-neighbors");
297 if (!gh_pair_p (rn) && i < cols.size () - 1)
299 cols[i]->set_grob_property ("right-neighbors", gh_cons (cols[i + 1]->self_scm(), SCM_EOL));
305 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
307 Spacing_spanner::set_springs (SCM smob)
309 Grob *me = unsmob_grob (smob);
311 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
313 set_explicit_neighbor_columns (all);
314 prune_loose_colunms (&all);
315 set_implicit_neighbor_columns (all);
318 for (int i = 1; i < all.size (); i++)
321 if (Item::breakable_b (sc))
323 Link_array<Grob> measure (all.slice (j, i+1));
324 do_measure (me, &measure);
329 return SCM_UNSPECIFIED;
334 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> *cols)
336 Moment shortest_in_measure;
339 space as if this duration is present.
341 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
342 shortest_in_measure.set_infinite (1);
344 for (int i =0 ; i < cols->size (); i++)
346 if (Paper_column::musical_b (cols->elem (i)))
348 Moment *when = unsmob_moment (cols->elem (i)->get_grob_property ("when"));
351 ignore grace notes for shortest notes.
353 if (when && when->grace_part_)
356 SCM st = cols->elem (i)->get_grob_property ("shortest-starter-duration");
357 Moment this_shortest = *unsmob_moment (st);
358 shortest_in_measure = shortest_in_measure <? this_shortest;
362 Array<Spring> springs;
364 for (int i= 0; i < cols->size () - 1; i++)
366 Item * l = dynamic_cast<Item*> (cols->elem (i));
367 Item * r = dynamic_cast<Item*> (cols->elem (i+1));
369 Paper_column * lc = dynamic_cast<Paper_column*> (l);
370 Paper_column * rc = dynamic_cast<Paper_column*> (r);
372 if (!Paper_column::musical_b (l))
374 breakable_column_spacing (l, r);
378 The case that the right part is broken as well is rather
379 rare, but it is possible, eg. with a single empty measure,
380 or if one staff finishes a tad earlier than the rest.
383 Item *lb = l->find_prebroken_piece (RIGHT);
384 Item *rb = r->find_prebroken_piece (LEFT);
387 breakable_column_spacing (lb,r);
390 breakable_column_spacing (l, rb);
392 breakable_column_spacing (lb, rb);
397 Real note_space = note_spacing (me,lc, rc, shortest_in_measure <? base_shortest_duration);
398 Real hinterfleisch = note_space;
399 Real headwid = gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
401 SCM seq = lc->get_grob_property ("right-neighbors");
404 hinterfleisch = hind-meat = amount of space following a note.
407 We adjust the space following a note only if the next note
408 happens after the current note (this is set in the grob
409 property SPACING-SEQUENCE. */
411 Real stretch_distance = note_space;
413 hinterfleisch = -1.0;
414 Real max_factor = 0.0;
415 for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
417 Grob * wish = unsmob_grob (gh_car (s));
419 if (Note_spacing::left_column (wish) != lc
420 || Note_spacing::right_column (wish) != rc)
424 This is probably a waste of time in the case of polyphonic
426 if (Note_spacing::has_interface (wish))
428 hinterfleisch = hinterfleisch >?
431 (note_space + Note_spacing::get_spacing (wish))
432 *gh_scm2double (wish->get_grob_property ("space-factor"))
434 + Note_spacing::stem_dir_correction (wish));
438 if (hinterfleisch < 0)
440 // maybe should issue a programming error.
441 hinterfleisch = note_space;
444 stretch_distance -= headwid; // why?
446 if (max_factor == 0.0)
450 s.distance_f_ = max_factor * hinterfleisch;
451 s.strength_f_ = 1 / stretch_distance;
453 s.item_l_drul_[LEFT] = l;
454 s.item_l_drul_[RIGHT] = r;
457 if (r->find_prebroken_piece (LEFT))
459 s.item_l_drul_[RIGHT] = r->find_prebroken_piece(LEFT);
468 Read hints from L (todo: R) and generate springs.
471 Spacing_spanner::breakable_column_spacing (Item* l, Item *r)
473 Real max_fixed = -infinity_f;
474 Real max_space = -infinity_f;
476 for (SCM s = l->get_grob_property ("spacing-wishes");
477 gh_pair_p (s); s = gh_cdr (s))
479 Grob * spacing_grob = unsmob_grob (gh_car (s));
481 if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
487 Staff_spacing::get_spacing_params (spacing_grob,
488 &space, &fixed_space);
489 if (space > max_space)
492 max_fixed = fixed_space;
496 if (isinf (max_space))
498 programming_error ("No pref spacing found");
504 s.distance_f_ = max_space;
505 s.strength_f_ = 1/(max_space - max_fixed);
507 s.item_l_drul_[LEFT] = l;
508 s.item_l_drul_[RIGHT] = r;
515 Get the measure wide ant for arithmetic spacing.
518 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
519 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
520 The Ohio State University, 1987.
524 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
526 Real log = log_2 (shortest.main_part_);
527 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
530 Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
532 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
537 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
540 Moment shortest_playing_len = 0;
541 SCM s = lc->get_grob_property ("shortest-playing-duration");
544 if (unsmob_moment (s))
545 shortest_playing_len = *unsmob_moment (s);
547 if (! shortest_playing_len.to_bool ())
549 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
550 shortest_playing_len = 1;
553 if (! shortest.to_bool ())
555 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
558 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
561 if (delta_t.main_part_)
563 dist = get_duration_space (me, shortest_playing_len, shortest);
564 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
566 else if (delta_t.grace_part_)
568 dist = get_duration_space (me, shortest, shortest);
570 Real grace_fact = 1.0;
571 SCM gf = me->get_grob_property ("grace-space-factor");
572 if (gh_number_p (gf))
573 grace_fact = gh_scm2double (gf);
580 TODO: figure out how to space grace notes.
584 + grace_fact * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
587 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
588 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
592 if (lm->grace_part_ && rm->grace_part_)
594 else if (!rm->grace_part_ && lm->grace_part_)