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