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