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>
9 #include "line-of-score.hh"
10 #include "paper-score.hh"
11 #include "paper-column.hh"
14 #include "note-spacing.hh"
21 right-neighbors = List of spacing-wish grobs that are close to the
27 (TODO: property-doc these!)
29 Don't be confused by right-items: each spacing wish can also contain
30 a number of items, with which a spacing constraint may be kept. It's
31 a little baroque, but it might come in handy later on?
35 class Third_spacing_spanner
38 static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment) ;
39 static Real note_spacing (Grob*,Grob*,Grob*,Moment) ;
40 static Real get_duration_space (Grob*,Moment dur, Moment shortest) ;
42 static void breakable_column_spacing (Item* l, Item *r);
43 static void find_loose_columns () {}
44 static void prune_loose_colunms (Link_array<Grob> *cols);
45 static void find_loose_columns (Link_array<Grob> cols);
46 static void set_explicit_neighbor_columns (Link_array<Grob> cols);
47 static void set_implicit_neighbor_columns (Link_array<Grob> cols);
48 static void do_measure (Grob*me,Link_array<Grob> *cols);
49 DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
55 Return whether COL is fixed to its neighbors by some kind of spacing
59 loose_column (Grob *l, Grob *c, Grob *r)
61 SCM rns = c->get_grob_property ("right-neighbors");
62 SCM lns = c->get_grob_property ("left-neighbors");
65 If this column doesn't have a proper neighbor, we should really
66 make it loose, but spacing it correctly is more than we can
69 (this happens in the following situation:
80 the column containing the clef is really loose, and should be
81 attached right to the first column, but that is a lot of work for
82 such a borderline case.
87 if (!gh_pair_p (lns) || !gh_pair_p (rns))
90 Item * l_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (lns)));
91 Item * r_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (rns)));
93 if (!l_neighbor || !r_neighbor)
96 l_neighbor = l_neighbor->column_l();
97 r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column (r_neighbor));
99 if (l == l_neighbor && r == r_neighbor)
102 if (!l_neighbor || !r_neighbor)
106 Only declare loose if the bounds make a little sense. This means
107 some cases (two isolated, consecutive clef changes) won't be
108 nicely folded, but hey, then don't do that.
110 if( (Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor))
111 && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor)))
118 If in doubt: we're not loose; the spacing engine should space for
119 it, risking suboptimal spacing.
121 (Otherwise, we might risk core dumps, and other weird stuff.)
129 Remove columns that are not tightly fitting from COLS. In the
130 removed columns, set 'between-cols to the columns where it is in
134 Third_spacing_spanner::prune_loose_colunms (Link_array<Grob> *cols)
136 Link_array<Grob> newcols;
138 for (int i=0; i < cols->size (); i++)
140 if (Item::breakable_b (cols->elem(i)) || Paper_column::musical_b (cols->elem (i)))
142 newcols.push (cols->elem(i));
146 Grob *c = cols->elem(i);
147 if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
149 SCM lns = c->get_grob_property ("left-neighbors");
150 lns = gh_pair_p (lns) ? gh_car (lns) : SCM_BOOL_F;
152 SCM rns = c->get_grob_property ("right-neighbors");
153 rns = gh_pair_p (rns) ? gh_car (rns) : SCM_BOOL_F;
156 Either object can be non existent, if the score ends
159 rns = gh_car (unsmob_grob (rns)->get_grob_property ("right-items"));
160 c->set_grob_property ("between-cols", gh_cons (lns,
164 Set distance constraints for loose columns
166 Drul_array<Grob*> next_door;
167 next_door[LEFT] =cols->elem (i - 1);
168 next_door[RIGHT] =cols->elem (i + 1);
170 Drul_array<Real> dists(0,0);
175 Grob *lc = d == LEFT ? lc : c;
176 Grob *rc = d == LEFT ? c : rc;
178 for (SCM s = lc->get_grob_property ("spacing-wishes");
179 gh_pair_p (s); s = gh_cdr (s))
181 Grob *sp = unsmob_grob (gh_car (s));
182 if (Note_spacing::left_column (sp) != lc
183 || Note_spacing::right_column (sp) != rc)
186 dists[d] = dists[d] >? Note_spacing::get_spacing (sp);
189 while (flip (&d) != LEFT);
192 r.distance_f_ = dists[LEFT] + dists[RIGHT];
193 r.item_l_drul_[LEFT] = dynamic_cast<Item*> (cols->elem(i-1));
194 r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
208 Set neighboring columns determined by the spacing-wishes grob property.
211 Third_spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
213 for (int i=0; i < cols.size(); i++)
215 SCM right_neighbors = SCM_EOL;
216 int min_rank = 100000; // inf.
219 SCM wishes= cols[i]->get_grob_property ("spacing-wishes");
220 for (SCM s =wishes; gh_pair_p (s); s = gh_cdr (s))
222 Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
224 Item * lc = wish->column_l ();
225 Grob * right = Note_spacing::right_column (wish);
230 Item * rc = dynamic_cast<Item*> (right);
232 int right_rank = Paper_column::rank_i (rc);
233 int left_rank = Paper_column::rank_i (lc);
236 update the left column.
238 if (right_rank <= min_rank)
240 if (right_rank < min_rank)
241 right_neighbors =SCM_EOL;
243 min_rank = right_rank;
244 right_neighbors = gh_cons (wish->self_scm (), right_neighbors);
248 update the right column of the wish.
251 SCM left_neighs = rc->get_grob_property ("left-neighbors");
252 if (gh_pair_p (left_neighs)
253 && unsmob_grob (gh_car (left_neighs)))
255 Item * it = dynamic_cast<Item*> (unsmob_grob (gh_car (left_neighs)));
256 maxrank = Paper_column::rank_i (it->column_l());
259 if (left_rank >= maxrank)
261 if (left_rank > maxrank)
262 left_neighs = SCM_EOL;
264 left_neighs = gh_cons (wish->self_scm (), left_neighs);
265 rc->set_grob_property ("left-neighbors", right_neighbors);
269 if (gh_pair_p (right_neighbors))
271 cols[i]->set_grob_property ("right-neighbors", right_neighbors);
277 Set neighboring columns that have no left/right-neighbor set
278 yet. Only do breakable non-musical columns, and musical columns.
281 Third_spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
283 for (int i = 0; i < cols.size (); i++)
285 Item * it = dynamic_cast<Item*>(cols[i]);
286 if (!Item::breakable_b (it) && !Paper_column::musical_b (it))
289 // it->breakable || it->musical
292 sloppy with typnig left/right-neighbors should take list, but paper-column found instead.
294 SCM ln = cols[i] ->get_grob_property ("left-neighbors");
295 if (!gh_pair_p (ln) && i )
297 cols[i]->set_grob_property ("left-neighbors", gh_cons (cols[i-1]->self_scm(), SCM_EOL));
300 SCM rn = cols[i] ->get_grob_property ("right-neighbors");
301 if (!gh_pair_p (rn) && i < cols.size () - 1)
303 cols[i]->set_grob_property ("right-neighbors", gh_cons (cols[i + 1]->self_scm(), SCM_EOL));
309 MAKE_SCHEME_CALLBACK (Third_spacing_spanner, set_springs,1);
311 Third_spacing_spanner::set_springs (SCM smob)
313 Grob *me = unsmob_grob (smob);
315 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
317 set_explicit_neighbor_columns (all);
318 prune_loose_colunms (&all);
319 set_implicit_neighbor_columns (all);
322 for (int i = 1; i < all.size (); i++)
325 if (Item::breakable_b (sc))
327 Link_array<Grob> measure (all.slice (j, i+1));
328 do_measure (me, &measure);
333 return SCM_UNSPECIFIED;
338 Third_spacing_spanner::do_measure (Grob*me, Link_array<Grob> *cols)
340 Moment shortest_in_measure;
343 space as if this duration is present.
345 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
346 shortest_in_measure.set_infinite (1);
348 for (int i =0 ; i < cols->size (); i++)
350 if (Paper_column::musical_b (cols->elem (i)))
352 Moment *when = unsmob_moment (cols->elem (i)->get_grob_property ("when"));
355 ignore grace notes for shortest notes.
357 if (when && when->grace_part_)
360 SCM st = cols->elem (i)->get_grob_property ("shortest-starter-duration");
361 Moment this_shortest = *unsmob_moment (st);
362 shortest_in_measure = shortest_in_measure <? this_shortest;
366 Array<Spring> springs;
368 for (int i= 0; i < cols->size () - 1; i++)
370 Item * l = dynamic_cast<Item*> (cols->elem (i));
371 Item * r = dynamic_cast<Item*> (cols->elem (i+1));
373 Paper_column * lc = dynamic_cast<Paper_column*> (l);
374 Paper_column * rc = dynamic_cast<Paper_column*> (r);
376 if (!Paper_column::musical_b (l))
378 breakable_column_spacing (l, r);
382 The case that the right part is broken as well is rather
383 rare, but it is possible, eg. with a single empty measure,
384 or if one staff finishes a tad earlier than the rest.
387 Item *lb = l->find_prebroken_piece (RIGHT);
388 Item *rb = r->find_prebroken_piece (LEFT);
391 breakable_column_spacing (lb,r);
394 breakable_column_spacing (l, rb);
396 breakable_column_spacing (lb, rb);
401 Real note_space = note_spacing (me,lc, rc, shortest_in_measure <? base_shortest_duration);
402 Real hinterfleisch = note_space;
403 Real headwid = gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
405 SCM seq = lc->get_grob_property ("right-neighbors");
408 hinterfleisch = hind-meat = amount of space following a note.
411 We adjust the space following a note only if the next note
412 happens after the current note (this is set in the grob
413 property SPACING-SEQUENCE. */
415 Real stretch_distance = note_space;
417 hinterfleisch = -1.0;
418 Real max_factor = 0.0;
419 for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
421 Grob * wish = unsmob_grob (gh_car (s));
423 if (Note_spacing::left_column (wish) != lc
424 || Note_spacing::right_column (wish) != rc)
428 This is probably a waste of time in the case of polyphonic
430 if (Note_spacing::has_interface (wish))
432 hinterfleisch = hinterfleisch >?
435 (note_space + Note_spacing::get_spacing (wish))
436 *gh_scm2double (wish->get_grob_property ("space-factor"))
438 + Note_spacing::stem_dir_correction (wish));
442 if (hinterfleisch < 0)
444 // maybe should issue a programming error.
445 hinterfleisch = note_space;
448 stretch_distance -= headwid; // why?
450 if (max_factor == 0.0)
454 s.distance_f_ = max_factor * hinterfleisch;
455 s.strength_f_ = 1 / stretch_distance;
457 s.item_l_drul_[LEFT] = l;
458 s.item_l_drul_[RIGHT] = r;
461 if (r->find_prebroken_piece (LEFT))
463 s.item_l_drul_[RIGHT] = r->find_prebroken_piece(LEFT);
472 Read hints from L (todo: R) and generate springs.
475 Third_spacing_spanner::breakable_column_spacing (Item* l, Item *r)
479 Real break_dist = 0.0;
480 SCM espace = l->get_grob_property ("extra-space");
481 if (gh_pair_p (espace))
482 break_dist += gh_scm2double (ly_cdr (espace));
487 Real break_stretch = 0.0;
489 // todo: naming of "distance"
490 espace = l->get_grob_property ("stretch-distance");
491 if (gh_pair_p (espace))
492 break_stretch += gh_scm2double (ly_cdr (espace));
497 s.distance_f_ = break_dist;
498 s.strength_f_ = 1/break_stretch;
499 s.item_l_drul_[LEFT] = l;
500 s.item_l_drul_[RIGHT] = r;
507 Get the measure wide ant for arithmetic spacing.
510 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
511 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
512 The Ohio State University, 1987.
516 Third_spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
518 Real log = log_2 (shortest.main_part_);
519 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
522 Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
524 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
529 Third_spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
532 Moment shortest_playing_len = 0;
533 SCM s = lc->get_grob_property ("shortest-playing-duration");
536 if (unsmob_moment (s))
537 shortest_playing_len = *unsmob_moment (s);
539 if (! shortest_playing_len.to_bool ())
541 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
542 shortest_playing_len = 1;
545 if (! shortest.to_bool ())
547 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
550 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
553 if (delta_t.main_part_)
555 dist = get_duration_space (me, shortest_playing_len, shortest);
556 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
558 else if (delta_t.grace_part_)
560 dist = get_duration_space (me, shortest, shortest);
562 Real grace_fact = 1.0;
563 SCM gf = me->get_grob_property ("grace-space-factor");
564 if (gh_number_p (gf))
565 grace_fact = gh_scm2double (gf);
572 TODO: figure out how to space grace notes.
576 + grace_fact * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
579 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
580 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
584 if (lm->grace_part_ && rm->grace_part_)
586 else if (!rm->grace_part_ && lm->grace_part_)