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