2 spacing-spanner.cc -- implement Spacing_spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2001 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!)
30 class Third_spacing_spanner
33 static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment) ;
34 static Real note_spacing (Grob*,Grob*,Grob*,Moment) ;
35 static Real get_duration_space (Grob*,Moment dur, Moment shortest) ;
37 static void breakable_column_spacing (Item* l, Item *r);
38 static void find_loose_columns () {}
39 static void prune_loose_colunms (Link_array<Grob> *cols);
40 static void find_loose_columns (Link_array<Grob> cols);
41 static void set_explicit_neighbor_columns (Link_array<Grob> cols);
42 static void set_implicit_neighbor_columns (Link_array<Grob> cols);
43 static void do_measure (Grob*me,Link_array<Grob> *cols);
44 DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
50 Return whether COL is fixed to its neighbors by some kind of spacing
54 loose_column (Grob *l, Grob *c, Grob *r)
56 SCM rns = c->get_grob_property ("right-neighbors");
57 SCM lns = c->get_grob_property ("left-neighbors");
60 If this column doesn't have a proper neighbor, we should really
61 make it loose, but spacing it correctly is more than we can
64 (this happens in the following situation:
75 the column containing the clef is really loose, and should be
76 attached right to the first column, but that is a lot of work for
77 such a border line case.
82 if (!gh_pair_p (lns) || !gh_pair_p (rns))
85 Item * l_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (lns)));
86 Item * r_neighbor = dynamic_cast<Item*> (unsmob_grob (gh_car (rns)));
88 if (!l_neighbor || !r_neighbor)
91 l_neighbor = l_neighbor->column_l();
92 r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column (r_neighbor));
94 if (l == l_neighbor && r == r_neighbor)
97 if (!l_neighbor || !r_neighbor)
101 Only declare loose if the bounds make a little sense. This means
102 some cases (two isolated, consecutive clef changes) won't be
103 nicely folded, but hey, then don't do that.
105 if( (Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor))
106 && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor)))
113 If in doubt: we're not loose; the spacing engine should space for
114 it, risking suboptimal spacing.
116 (Otherwise, we might risk core dumps, and other weird stuff.)
124 Remove columns that are not tightly fitting from COLS. In the
125 removed columns, set 'between-cols to the columns where it is in
129 Third_spacing_spanner::prune_loose_colunms (Link_array<Grob> *cols)
131 Link_array<Grob> newcols;
133 for (int i=0; i < cols->size (); i++)
135 if (Item::breakable_b (cols->elem(i)) || Paper_column::musical_b (cols->elem (i)))
137 newcols.push (cols->elem(i));
141 Grob *c = cols->elem(i);
142 if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
144 SCM lns = c->get_grob_property ("left-neighbors");
145 lns = gh_pair_p (lns) ? gh_car (lns) : SCM_BOOL_F;
147 SCM rns = c->get_grob_property ("right-neighbors");
148 rns = gh_pair_p (rns) ? gh_car (rns) : SCM_BOOL_F;
151 Either object can be non existent, if the score ends
154 rns = gh_car (unsmob_grob (rns)->get_grob_property ("right-items"));
155 c->set_grob_property ("between-cols", gh_cons (lns,
160 TODO: we should have distance constraints for loose
161 columns anyway, and the placement could be improved. Clefs
162 easily run into their neigbhors when folded into too
176 Set neighboring columns determined by the spacing-wishes grob property.
179 Third_spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
181 for (int i=0; i < cols.size(); i++)
183 SCM right_neighbors = SCM_EOL;
184 int min_rank = 100000; // inf.
187 SCM wishes= cols[i]->get_grob_property ("spacing-wishes");
188 for (SCM s =wishes; gh_pair_p (s); s = gh_cdr (s))
190 Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
192 Item * lc = wish->column_l ();
193 Grob * right = Note_spacing::right_column (wish);
198 Item * rc = dynamic_cast<Item*> (right);
200 int right_rank = Paper_column::rank_i (rc);
201 int left_rank = Paper_column::rank_i (lc);
204 update the left column.
206 if (right_rank <= min_rank)
208 if (right_rank < min_rank)
209 right_neighbors =SCM_EOL;
211 min_rank = right_rank;
212 right_neighbors = gh_cons (wish->self_scm (), right_neighbors);
216 update the right column of the wish.
219 SCM left_neighs = rc->get_grob_property ("left-neighbors");
220 if (gh_pair_p (left_neighs)
221 && unsmob_grob (gh_car (left_neighs)))
223 Item * it = dynamic_cast<Item*> (unsmob_grob (gh_car (left_neighs)));
224 maxrank = Paper_column::rank_i (it->column_l());
227 if (left_rank >= maxrank)
229 if (left_rank > maxrank)
230 left_neighs = SCM_EOL;
232 left_neighs = gh_cons (wish->self_scm (), left_neighs);
233 rc->set_grob_property ("left-neighbors", right_neighbors);
237 if (gh_pair_p (right_neighbors))
239 cols[i]->set_grob_property ("right-neighbors", right_neighbors);
245 Set neighboring columns that have no left/right-neighbor set
246 yet. Only do breakable non-musical columns, and musical columns.
249 Third_spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
251 for (int i = 0; i < cols.size (); i++)
253 Item * it = dynamic_cast<Item*>(cols[i]);
254 if (!Item::breakable_b (it) && !Paper_column::musical_b (it))
257 // it->breakable || it->musical
258 SCM ln = cols[i] ->get_grob_property ("left-neighbors");
259 if (!gh_pair_p (ln) && i )
261 cols[i]->set_grob_property ("left-neighbors", cols[i-1]->self_scm());
264 SCM rn = cols[i] ->get_grob_property ("right-neighbors");
265 if (!gh_pair_p (rn) && i < cols.size () - 1)
267 cols[i]->set_grob_property ("right-neighbors", cols[i + 1]->self_scm());
273 MAKE_SCHEME_CALLBACK (Third_spacing_spanner, set_springs,1);
275 Third_spacing_spanner::set_springs (SCM smob)
277 Grob *me = unsmob_grob (smob);
279 Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
281 set_explicit_neighbor_columns (all);
282 prune_loose_colunms (&all);
283 set_implicit_neighbor_columns (all);
286 for (int i = 1; i < all.size (); i++)
289 if (Item::breakable_b (sc))
291 Link_array<Grob> measure (all.slice (j, i+1));
292 do_measure (me, &measure);
297 return SCM_UNSPECIFIED;
302 Third_spacing_spanner::do_measure (Grob*me, Link_array<Grob> *cols)
304 Moment shortest_in_measure;
307 space as if this duration is present.
309 Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
310 shortest_in_measure.set_infinite (1);
312 for (int i =0 ; i < cols->size (); i++)
314 if (Paper_column::musical_b (cols->elem (i)))
316 Moment *when = unsmob_moment (cols->elem (i)->get_grob_property ("when"));
319 ignore grace notes for shortest notes.
321 if (when && when->grace_part_)
324 SCM st = cols->elem (i)->get_grob_property ("shortest-starter-duration");
325 Moment this_shortest = *unsmob_moment (st);
326 shortest_in_measure = shortest_in_measure <? this_shortest;
330 Array<Spring> springs;
332 for (int i= 0; i < cols->size () - 1; i++)
334 Item * l = dynamic_cast<Item*> (cols->elem (i));
335 Item * r = dynamic_cast<Item*> (cols->elem (i+1));
337 Paper_column * lc = dynamic_cast<Paper_column*> (l);
338 Paper_column * rc = dynamic_cast<Paper_column*> (r);
340 if (!Paper_column::musical_b (l))
342 breakable_column_spacing (l, r);
346 The case that the right part is broken as well is rather
347 rare, but it is possible, eg. with a single empty measure,
348 or if one staff finishes a tad earlier than the rest.
351 Item *lb = l->find_prebroken_piece (RIGHT);
352 Item *rb = r->find_prebroken_piece (LEFT);
355 breakable_column_spacing (lb,r);
358 breakable_column_spacing (l, rb);
360 breakable_column_spacing (lb, rb);
365 Real note_space = note_spacing (me,lc, rc, shortest_in_measure <? base_shortest_duration);
366 Real hinterfleisch = note_space;
367 Real headwid = gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
369 SCM seq = lc->get_grob_property ("right-neighbors");
372 hinterfleisch = hind-meat = amount of space following a note.
375 We adjust the space following a note only if the next note
376 happens after the current note (this is set in the grob
377 property SPACING-SEQUENCE. */
379 Real stretch_distance = note_space;
381 hinterfleisch = -1.0;
382 Real max_factor = 0.0;
383 for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
385 Grob * wish = unsmob_grob (gh_car (s));
390 TODO: don't do stem-dir correction in polyphonic
391 stuff. It wastes CPU time.
393 if (Note_spacing::has_interface (wish))
395 hinterfleisch = hinterfleisch >?
398 (note_space + Note_spacing::get_spacing (wish))
399 *gh_scm2double (wish->get_grob_property ("space-factor"))
401 + Note_spacing::stem_dir_correction (wish));
407 stretch_distance -= headwid;
410 something failed, or we get ridiculous close.
412 if (hinterfleisch < 0)
414 // maybe should issue a programming error.
415 hinterfleisch = note_space;
418 if (max_factor == 0.0)
422 s.distance_f_ = max_factor * hinterfleisch;
423 s.strength_f_ = 1 / stretch_distance;
425 s.item_l_drul_[LEFT] = l;
426 s.item_l_drul_[RIGHT] = r;
429 if (r->find_prebroken_piece (LEFT))
431 s.item_l_drul_[RIGHT] = r->find_prebroken_piece(LEFT);
440 Read hints from L (todo: R) and generate springs.
443 Third_spacing_spanner::breakable_column_spacing (Item* l, Item *r)
447 Real break_dist = 0.0;
448 SCM espace = l->get_grob_property ("extra-space");
449 if (gh_pair_p (espace))
450 break_dist += gh_scm2double (ly_cdr (espace));
455 Real break_stretch = 0.0;
457 // todo: naming of "distance"
458 espace = l->get_grob_property ("stretch-distance");
459 if (gh_pair_p (espace))
460 break_stretch += gh_scm2double (ly_cdr (espace));
465 s.distance_f_ = break_dist;
466 s.strength_f_ = 1/break_stretch;
467 s.item_l_drul_[LEFT] = l;
468 s.item_l_drul_[RIGHT] = r;
475 Get the measure wide ant for arithmetic spacing.
478 John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
479 OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
480 The Ohio State University, 1987.
484 Third_spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest)
486 Real log = log_2 (shortest.main_part_);
487 Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
490 Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
492 return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
497 Third_spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
500 Moment shortest_playing_len = 0;
501 SCM s = lc->get_grob_property ("shortest-playing-duration");
504 if (unsmob_moment (s))
505 shortest_playing_len = *unsmob_moment (s);
507 if (! shortest_playing_len.to_bool ())
509 programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
510 shortest_playing_len = 1;
513 if (! shortest.to_bool ())
515 programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
518 Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
521 if (delta_t.main_part_)
523 dist = get_duration_space (me, shortest_playing_len, shortest);
524 dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
526 else if (delta_t.grace_part_)
528 dist = get_duration_space (me, shortest, shortest);
530 Real grace_fact = 1.0;
531 SCM gf = me->get_grob_property ("grace-space-factor");
532 if (gh_number_p (gf))
533 grace_fact = gh_scm2double (gf);
540 TODO: figure out how to space grace notes.
544 + grace_fact * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
547 Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
548 Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
552 if (lm->grace_part_ && rm->grace_part_)
554 else if (!rm->grace_part_ && lm->grace_part_)