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