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