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