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