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