]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* rehearsalmark-horiz-extent.ly: add note.
[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 max_fixed = 0;
250   Real max_space = 0;
251   Real compound_note_space = 0.0;
252   Real compound_fixed_note_space = 0.0;
253
254   if (options->stretch_uniformly_)
255     compound_note_space = base_note_space;
256   else
257     {
258       int wish_count = 0;
259
260       extract_grob_set (left_col, "right-neighbors", neighbors);
261
262       /*
263         We adjust the space following a note only if the next note
264         happens after the current note (this is set in the grob
265         property SPACING-SEQUENCE.
266       */
267       for (int i = 0; i < neighbors.size (); i++)
268         {
269           Grob *wish = neighbors[i];
270
271           Item *wish_rcol = Note_spacing::right_column (wish);
272           if (Note_spacing::left_column (wish) != left_col
273               || (wish_rcol != right_col && wish_rcol != right_col->original ()))
274             continue;
275
276           /*
277             This is probably a waste of time in the case of polyphonic
278             music.  */
279           if (Note_spacing::has_interface (wish))
280             {
281               Real space = 0.0;
282               Real fixed = 0.0;
283
284               Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
285
286
287               max_space = max (max_space, space);
288               max_fixed = max (max_fixed, fixed);
289               
290               compound_note_space += space;
291               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 if (to_boolean (me->get_property ("average-spacing-wishes")))
311         {
312           compound_note_space /= wish_count;
313           compound_fixed_note_space /= wish_count;
314         }
315       else
316         {
317           compound_fixed_note_space = max_fixed;
318           compound_note_space = max_space;
319         }
320
321       /*
322         Whatever we do, the fixed space is smaller than the real
323         space.
324
325         TODO: this criterion is discontinuous in the derivative.
326         Maybe it should be continuous?
327       */
328       compound_fixed_note_space = min (compound_fixed_note_space,
329                                        compound_note_space);
330     }
331
332   Real inverse_strength = 1.0;
333   Real distance = 1.0;
334
335   /*
336     TODO: make sure that the space doesn't exceed the right margin.
337   */
338   if (options->packed_)
339     {
340       /*
341         In packed mode, pack notes as tight as possible.  This makes
342         sense mostly in combination with raggedright mode: the notes
343         are then printed at minimum distance.  This is mostly useful
344         for ancient notation, but may also be useful for some flavours
345         of contemporary music.  If not in raggedright mode, lily will
346         pack as much bars of music as possible into a line, but the
347         line will then be stretched to fill the whole linewidth.
348       */
349       inverse_strength = 1.0;
350       distance = compound_fixed_note_space;
351     }
352   else
353     {
354       inverse_strength = (compound_note_space - compound_fixed_note_space);
355       distance = compound_note_space;
356     }
357
358   Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
359 }
360
361 /*
362   Read hints from L and generate springs.
363 */
364 void
365 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
366                                            Spacing_options const *options)
367 {
368   Real compound_fixed = 0.0;
369   Real compound_space = 0.0;
370   Real max_fixed = 0.0;
371   Real max_space = 0.0;
372   
373   int wish_count = 0;
374
375   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
376
377   if (dt == Moment (0, 0))
378     {
379       extract_grob_set (l, "spacing-wishes", wishes);
380
381       for (int i = 0; i < wishes.size (); i++)
382         {
383           Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
384
385           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
386             continue;
387
388           Real space;
389           Real fixed_space;
390
391           /*
392             column for the left one settings should be ok due automatic
393             pointer munging.
394
395           */
396           assert (spacing_grob->get_column () == l);
397
398           Staff_spacing::get_spacing_params (spacing_grob,
399                                              &space, &fixed_space);
400
401           if (Paper_column::when_mom (r).grace_part_)
402             {
403               /*
404                 Correct for grace notes.
405
406                 Ugh. The 0.8 is arbitrary.
407               */
408               space *= 0.8;
409             }
410
411           max_space = max (max_space, space);
412           max_fixed = max (max_fixed, fixed_space);
413           
414           compound_space += space;
415           compound_fixed += fixed_space;
416           wish_count++;
417         }
418     }
419
420   if (compound_space <= 0.0 || !wish_count)
421     {
422       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
423                                          options);
424       wish_count = 1;
425     }
426   else
427     {
428       if (to_boolean (me->get_property ("average-spacing-wishes")))
429         {
430           compound_space /= wish_count;
431           compound_fixed /= wish_count;
432         }
433       else
434         {
435           compound_fixed = max_fixed;
436           compound_space = max_space;
437         }
438       
439     }
440
441   if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
442     compound_fixed = 0.0;
443
444   assert (!isinf (compound_space));
445   compound_space = max (compound_space, compound_fixed);
446
447   /*
448     There used to be code that changed spacing depending on
449     raggedright setting.  Ugh.
450
451     Do it more cleanly, or rename the property.
452
453   */
454   Real inverse_strength = (compound_space - compound_fixed);
455   Real distance = compound_space;
456   Spaceable_grob::add_spring (l, r, distance, inverse_strength);
457 }
458
459 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
460                "The space taken by a note is dependent on its duration. Doubling a\n"
461                "duration adds spacing-increment to the space. The most common shortest\n"
462                "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
463                "spaced proportonial to their duration.\n"
464                "\n"
465                "Typically, the increment is the width of a black note head.  In a\n"
466                "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
467                "gets 2 note heads width (i.e. the space following a note is 1 note\n"
468                "head width) A 16th note is followed by 0.5 note head width. The\n"
469                "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
470
471                "average-spacing-wishes "
472                "grace-space-factor "
473                "spacing-increment "
474                "base-shortest-duration "
475                "strict-note-spacing "
476                "shortest-duration-space "
477                "common-shortest-duration "
478                "uniform-stretching "
479                "packed-spacing "
480                );
481
482 ADD_INTERFACE (Spacing_interface, "spacing-interface",
483                "Something to do with line breaking and spacing. "
484                "Kill this one after determining line breaks.",
485                "");
486