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