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