]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* lily/spacing-determine-loose-columns.cc: new file.
[lilypond.git] / lily / spacing-spanner.cc
1 /*
2   spacing-spanner.cc -- implement Spacing_spanner
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1999--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <math.h>
10 #include <cstdio>
11
12 #include "spacing-spanner.hh"
13 #include "paper-column.hh"
14 #include "output-def.hh"
15 #include "paper-score.hh"
16 #include "system.hh"
17 #include "moment.hh"
18 #include "note-spacing.hh"
19 #include "main.hh"
20 #include "warn.hh"
21 #include "pointer-group-interface.hh"
22 #include "spaceable-grob.hh"
23 #include "staff-spacing.hh"
24 #include "spacing-interface.hh"
25
26
27
28 void
29 Spacing_options::init (Grob *me)
30 {
31   increment_ = robust_scm2double (me->get_property ("spacing-increment"), 1);
32   packed_ = to_boolean (me->get_layout ()->c_variable ("packed"));
33   stretch_uniformly_ = to_boolean (me->get_property ("uniform-stretching"));
34   float_nonmusical_columns_
35     = to_boolean (me->get_property ("strict-note-spacing"));
36 }
37
38
39 Rational
40 Spacing_spanner::effective_shortest_duration (Grob *me, Link_array<Grob> const &all)
41 {
42   SCM preset_shortest = me->get_property ("common-shortest-duration");
43   Rational global_shortest;
44   if (unsmob_moment (preset_shortest))
45     {
46       global_shortest = unsmob_moment (preset_shortest)->main_part_;
47     }
48   else
49     {
50       global_shortest = Spacing_spanner::find_shortest (me, all);
51       if (be_verbose_global)
52         message (_f ("Global shortest duration is %s", global_shortest.to_string ()) + "\n");
53     }
54
55   return global_shortest;
56 }
57
58
59 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
60 SCM
61 Spacing_spanner::set_springs (SCM smob)
62 {
63   Grob *me = unsmob_grob (smob);
64
65   /*
66     can't use get_system() ? --hwn.
67   */
68   Link_array<Grob> all (me->pscore_->root_system ()->columns ());
69
70   set_explicit_neighbor_columns (all);
71
72   Spacing_options options;
73   options.init (me);
74   options.global_shortest_ = effective_shortest_duration (me, all);
75   
76   prune_loose_columns (me, &all, &options);
77   set_implicit_neighbor_columns (all);
78   generate_springs (me, all, &options);
79
80   return SCM_UNSPECIFIED;
81 }
82
83 /*
84   We want the shortest note that is also "common" in the piece, so we
85   find the shortest in each measure, and take the most frequently
86   found duration.
87
88   This probably gives weird effects with modern music, where every
89   note has a different duration, but hey, don't write that kind of
90   stuff, then.
91 */
92 Rational
93 Spacing_spanner::find_shortest (Grob *me, Link_array<Grob> const &cols)
94 {
95   /*
96     ascending in duration
97   */
98   Array<Rational> durations;
99   Array<int> counts;
100
101   Rational shortest_in_measure;
102   shortest_in_measure.set_infinite (1);
103
104   for (int i = 0; i < cols.size (); i++)
105     {
106       if (Paper_column::is_musical (cols[i]))
107         {
108           Moment *when = unsmob_moment (cols[i]->get_property ("when"));
109
110           /*
111             ignore grace notes for shortest notes.
112           */
113           if (when && when->grace_part_)
114             continue;
115
116           SCM st = cols[i]->get_property ("shortest-starter-duration");
117           Moment this_shortest = *unsmob_moment (st);
118           assert (this_shortest.to_bool ());
119           shortest_in_measure = min (shortest_in_measure, this_shortest.main_part_);
120         }
121       else if (!shortest_in_measure.is_infinity ()
122                && Item::is_breakable (cols[i]))
123         {
124           int j = 0;
125           for (; j < durations.size (); j++)
126             {
127               if (durations[j] > shortest_in_measure)
128                 {
129                   counts.insert (1, j);
130                   durations.insert (shortest_in_measure, j);
131                   break;
132                 }
133               else if (durations[j] == shortest_in_measure)
134                 {
135                   counts[j]++;
136                   break;
137                 }
138             }
139
140           if (durations.size () == j)
141             {
142               durations.push (shortest_in_measure);
143               counts.push (1);
144             }
145
146           shortest_in_measure.set_infinite (1);
147         }
148     }
149
150   int max_idx = -1;
151   int max_count = 0;
152   for (int i = durations.size (); i--;)
153     {
154       if (counts[i] >= max_count)
155         {
156           max_idx = i;
157           max_count = counts[i];
158         }
159
160       // printf ("duration %d/%d, count %d\n",
161       // durations[i].num (), durations[i].den (), counts[i]);
162     }
163
164   SCM bsd = me->get_property ("base-shortest-duration");
165   Rational d = Rational (1, 8);
166   if (Moment *m = unsmob_moment (bsd))
167     d = m->main_part_;
168
169   if (max_idx >= 0)
170     d = min (d, durations[max_idx]);
171
172   return d;
173 }
174
175 void
176 Spacing_spanner::generate_pair_spacing (Grob *me,
177                                         Paper_column *left_col, Paper_column *right_col,
178                                         Paper_column *after_right_col,
179                                         Spacing_options const *options)
180 {
181   if (Paper_column::is_musical (left_col))
182     {
183       bool skip_unbroken_right = false;
184       
185       if (!Paper_column::is_musical (right_col)
186           && options->float_nonmusical_columns_
187           && after_right_col
188           && Paper_column::is_musical (after_right_col))
189         {
190           skip_unbroken_right = true;
191         }
192
193       if (skip_unbroken_right)
194         {
195           /*
196             TODO: should generate rods to prevent collisions.
197           */
198           musical_column_spacing (me, left_col, after_right_col, options);
199           right_col->set_object ("between-cols", scm_cons (left_col->self_scm (),
200                                                            after_right_col->self_scm ()));
201         }
202       else
203         musical_column_spacing (me, left_col, right_col, options);
204       
205
206       if (Item *rb = right_col->find_prebroken_piece (LEFT))
207         musical_column_spacing (me, left_col, rb, options);
208     }
209   else
210     {
211       /*
212         The case that the right part is broken as well is rather
213         rare, but it is possible, eg. with a single empty measure,
214         or if one staff finishes a tad earlier than the rest.
215       */
216       Item *lb = left_col->find_prebroken_piece (RIGHT);
217       Item *rb = right_col->find_prebroken_piece (LEFT);
218
219       if (left_col && right_col)
220         breakable_column_spacing (me, left_col, right_col, options);
221           
222       if (lb && right_col)
223         breakable_column_spacing (me, lb, right_col, options);
224
225       if (left_col && rb)
226         breakable_column_spacing (me, left_col, rb, options);
227
228       if (lb && rb)
229         breakable_column_spacing (me, lb, rb, options);
230     }
231 }
232
233 void
234 Spacing_spanner::generate_springs (Grob *me,
235                                    Link_array<Grob> const &cols,
236                                    Spacing_options const *options)
237 {
238   Paper_column *next = 0;
239   Paper_column *next_next = 0;
240   for (int i = cols.size (); i--;)
241     {
242       Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
243       if (next)
244         generate_pair_spacing (me, col, next, next_next, options);
245
246       next_next = next;
247       next = col;
248     }
249 }
250
251 /*
252   Generate the space between two musical columns LEFT_COL and RIGHT_COL, given
253   spacing parameters INCR and SHORTEST.
254 */
255 void
256 Spacing_spanner::musical_column_spacing (Grob *me,
257                                          Item *left_col,
258                                          Item *right_col,
259                                          Spacing_options const *options)
260 {
261   bool expand_only = false;
262   Real base_note_space = note_spacing (me, left_col, right_col, options, &expand_only);
263   
264   Real compound_note_space = 0.0;
265   Real compound_fixed_note_space = 0.0;
266
267   if (options->stretch_uniformly_)
268     {
269       compound_note_space = base_note_space;
270     }
271   else
272     {
273       int wish_count = 0;
274
275       extract_grob_set (left_col, "right-neighbors", neighbors);
276
277       /*
278         We adjust the space following a note only if the next note
279         happens after the current note (this is set in the grob
280         property SPACING-SEQUENCE.
281       */
282       for (int i = 0; i < neighbors.size (); i++)
283         {
284           Grob *wish = neighbors[i];
285
286           Item *wish_rcol = Note_spacing::right_column (wish);
287           if (Note_spacing::left_column (wish) != left_col
288               || (wish_rcol != right_col && wish_rcol != right_col->original_))
289             continue;
290
291           /*
292             This is probably a waste of time in the case of polyphonic
293             music.  */
294           if (Note_spacing::has_interface (wish))
295             {
296               Real space = 0.0;
297               Real fixed = 0.0;
298
299               Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
300
301               compound_note_space = compound_note_space + space;
302               compound_fixed_note_space = compound_fixed_note_space + fixed;
303               wish_count++;
304             }
305         }
306
307       if (Paper_column::when_mom (right_col).grace_part_
308           && !Paper_column::when_mom (left_col).grace_part_)
309         {
310           /*
311             Ugh. 0.8 is arbitrary.
312           */
313           compound_note_space *= 0.8;
314         }
315
316       if (compound_note_space < 0 || wish_count == 0)
317         {
318           compound_note_space = base_note_space;
319           compound_fixed_note_space = options->increment_;
320         }
321       else
322         {
323           compound_note_space /= wish_count;
324           compound_fixed_note_space /= wish_count;
325         }
326
327       /*
328         Whatever we do, the fixed space is smaller than the real
329         space.
330
331         TODO: this criterion is discontinuous in the derivative.
332         Maybe it should be continuous?
333       */
334       compound_fixed_note_space = min (compound_fixed_note_space,
335                                        compound_note_space);
336     }
337   
338   Real inverse_strength = 1.0;
339   Real distance = 1.0;
340
341   /*
342     TODO: make sure that the space doesn't exceed the right margin.
343   */
344   if (options->packed_)
345     {
346       /*
347         In packed mode, pack notes as tight as possible.  This makes
348         sense mostly in combination with raggedright mode: the notes
349         are then printed at minimum distance.  This is mostly useful
350         for ancient notation, but may also be useful for some flavours
351         of contemporary music.  If not in raggedright mode, lily will
352         pack as much bars of music as possible into a line, but the
353         line will then be stretched to fill the whole linewidth.
354       */
355       inverse_strength = 1.0;
356       distance = compound_fixed_note_space;
357     }
358   else
359     {
360       inverse_strength = (compound_note_space - compound_fixed_note_space);
361       distance = compound_note_space;
362     }
363
364   Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
365 }
366
367 /*
368   Read hints from L and generate springs.
369 */
370 void
371 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
372                                            Spacing_options const *options)
373 {
374   Real compound_fixed = 0.0;
375   Real compound_space = 0.0;
376   int wish_count = 0;
377
378   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
379
380   if (dt == Moment (0, 0))
381     {
382       extract_grob_set (l, "spacing-wishes", wishes);
383
384       for (int i = 0; i < wishes.size (); i++)
385         {
386           Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
387
388           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
389             continue;
390
391           Real space;
392           Real fixed_space;
393
394           /*
395             column for the left one settings should be ok due automatic
396             pointer munging.
397
398           */
399           assert (spacing_grob->get_column () == l);
400
401           Staff_spacing::get_spacing_params (spacing_grob,
402                                              &space, &fixed_space);
403
404           if (Paper_column::when_mom (r).grace_part_)
405             {
406               /*
407                 Correct for grace notes.
408
409                 Ugh. The 0.8 is arbitrary.
410               */
411               space *= 0.8;
412             }
413
414           compound_space += space;
415           compound_fixed += fixed_space;
416           wish_count++;
417         }
418     }
419
420   if (compound_space <= 0.0 || !wish_count)
421     {
422       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
423                                          options);
424       wish_count = 1;
425     }
426   else
427     {
428       compound_space /= wish_count;
429       compound_fixed /= wish_count;
430     }
431
432   if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
433     {
434       compound_fixed = 0.0;
435     }
436   
437   assert (!isinf (compound_space));
438   compound_space = max (compound_space, compound_fixed);
439
440   /*
441     There used to be code that changed spacing depending on
442     raggedright setting.  Ugh.
443
444     Do it more cleanly, or rename the property.
445
446   */
447   Real inverse_strength = (compound_space - compound_fixed);
448   Real distance = compound_space;
449   Spaceable_grob::add_spring (l, r, distance, inverse_strength);
450 }
451
452 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
453                "The space taken by a note is dependent on its duration. Doubling a\n"
454                "duration adds spacing-increment to the space. The most common shortest\n"
455                "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
456                "spaced proportonial to their duration.\n"
457                "\n"
458                "Typically, the increment is the width of a black note head.  In a\n"
459                "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
460                "gets 2 note heads width (i.e. the space following a note is 1 note\n"
461                "head width) A 16th note is followed by 0.5 note head width. The\n"
462                "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
463                
464                "grace-space-factor spacing-increment base-shortest-duration strict-note-spacing "
465                "shortest-duration-space common-shortest-duration uniform-stretching"
466
467                );
468
469 ADD_INTERFACE (Spacing_interface, "spacing-interface",
470                "Something to do with line breaking and spacing. "
471                "Kill this one after determining line breaks.",
472                "");
473