]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
* scm/output-lib.scm (grace-spacing::calc-shortest-duration): 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--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 vector<Grob*>
30 Spacing_spanner::get_columns (Spanner *me)
31 {
32   vector<Grob*> all (get_root_system (me)->columns ());
33   vsize start = binary_search (all, (Grob*)me->get_bound (LEFT),
34                                &Paper_column::compare);
35   vsize end = binary_search (all, (Grob*) me->get_bound (RIGHT),
36                              &Paper_column::compare);
37
38   all = vector<Grob*>::vector<Grob*> (all.begin () + start,
39                                       all.begin () + end + 1);
40   return all;
41 }
42
43 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs, 1);
44 SCM
45 Spacing_spanner::set_springs (SCM smob)
46 {
47   Spanner *me = unsmob_spanner (smob);
48
49   /*
50     can't use get_system() ? --hwn.
51   */
52   vector<Grob*> all (get_columns (me));
53   set_explicit_neighbor_columns (all);
54
55   Spacing_options options;
56   options.init_from_grob (me);
57
58   prune_loose_columns (me, &all, &options);
59   set_implicit_neighbor_columns (all);
60   generate_springs (me, all, &options);
61
62   return SCM_UNSPECIFIED;
63 }
64
65 /*
66   We want the shortest note that is also "common" in the piece, so we
67   find the shortest in each measure, and take the most frequently
68   found duration.
69
70   This probably gives weird effects with modern music, where every
71   note has a different duration, but hey, don't write that kind of
72   stuff, then.
73 */
74
75 MAKE_SCHEME_CALLBACK (Spacing_spanner, calc_common_shortest_duration, 1);
76 SCM 
77 Spacing_spanner::calc_common_shortest_duration (SCM grob)
78 {
79   Spanner *me = unsmob_spanner (grob);
80
81   vector<Grob*> cols (get_columns (me));
82   
83   /*
84     ascending in duration
85   */
86   vector<Rational> durations;
87   vector<int> counts;
88
89   Rational shortest_in_measure;
90   shortest_in_measure.set_infinite (1);
91
92   for (vsize 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                && Paper_column::is_breakable (cols[i]))
111         {
112           vsize j = 0;
113           for (; j < durations.size (); j++)
114             {
115               if (durations[j] > shortest_in_measure)
116                 {
117                   counts.insert (counts.begin () + j, 1);
118                   durations.insert (durations.begin () + j, shortest_in_measure);
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_back (shortest_in_measure);
131               counts.push_back (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 (vsize 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 Moment (d).smobbed_copy ();
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                                    vector<Grob*> const &cols,
221                                    Spacing_options const *options)
222 {
223   Paper_column *prev = 0;
224   for (vsize i = 0; i < cols.size (); i++)
225     {
226       Paper_column *col = dynamic_cast<Paper_column *> (cols[i]);
227       Paper_column *next = (i < cols.size()-1) ? dynamic_cast<Paper_column *> (cols[i+1]) : 0;
228       
229       if (i > 0)
230         generate_pair_spacing (me, prev, col, next, options);
231
232       prev = 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     {
256       compound_note_space = base_note_space;
257             
258       if (!Paper_column::is_musical (right_col))
259         {
260           /*
261             Crude fix for notes that lead up to barlines and time sigs.
262           */
263           Interval lext = right_col->extent (right_col, X_AXIS);
264           if (!lext.is_empty ())
265             compound_note_space += -lext[LEFT];
266         }
267     }
268   else
269     {
270       int wish_count = 0;
271
272       extract_grob_set (left_col, "right-neighbors", neighbors);
273
274       /*
275         We adjust the space following a note only if the next note
276         happens after the current note (this is set in the grob
277         property SPACING-SEQUENCE.
278       */
279       for (vsize i = 0; i < neighbors.size (); i++)
280         {
281           Grob *wish = neighbors[i];
282
283           Item *wish_rcol = Note_spacing::right_column (wish);
284           if (Note_spacing::left_column (wish) != left_col
285               || (wish_rcol != right_col && wish_rcol != right_col->original ()))
286             continue;
287
288           /*
289             This is probably a waste of time in the case of polyphonic
290             music.  */
291           if (Note_spacing::has_interface (wish))
292             {
293               Real space = 0.0;
294               Real fixed = 0.0;
295
296               Note_spacing::get_spacing (wish, right_col, base_note_space, options->increment_, &space, &fixed);
297
298
299               max_space = max (max_space, space);
300               max_fixed = max (max_fixed, fixed);
301               
302               compound_note_space += space;
303               compound_fixed_note_space += fixed;
304               wish_count++;
305             }
306         }
307
308       if (Paper_column::when_mom (right_col).grace_part_
309           && !Paper_column::when_mom (left_col).grace_part_)
310         {
311           /*
312             Ugh. 0.8 is arbitrary.
313           */
314           compound_note_space *= 0.8;
315         }
316
317       if (compound_note_space < 0 || wish_count == 0)
318         {
319           /*
320             Fixed should be 0.0. If there are no spacing wishes, we're
321             likely dealing with polyphonic spacing of hemiolas.
322             
323             We used to have compound_fixed_note_space = options->increment_
324
325             but this can lead to numeric instability problems when we
326             do
327             
328                inverse_strength = (compound_note_space - compound_fixed_note_space)
329       
330           */
331           
332           compound_note_space = base_note_space;
333           compound_fixed_note_space = 0.0;
334         }
335       else if (to_boolean (me->get_property ("average-spacing-wishes")))
336         {
337           compound_note_space /= wish_count;
338           compound_fixed_note_space /= wish_count;
339         }
340       else
341         {
342           compound_fixed_note_space = max_fixed;
343           compound_note_space = max_space;
344         }
345
346       /*
347         Whatever we do, the fixed space is smaller than the real
348         space.
349
350         TODO: this criterion is discontinuous in the derivative.
351         Maybe it should be continuous?
352       */
353       compound_fixed_note_space = min (compound_fixed_note_space,
354                                        compound_note_space);
355     }
356
357   Real inverse_strength = 1.0;
358   Real distance = 1.0;
359
360   /*
361     TODO: make sure that the space doesn't exceed the right margin.
362   */
363   if (options->packed_)
364     {
365       /*
366         In packed mode, pack notes as tight as possible.  This makes
367         sense mostly in combination with raggedright mode: the notes
368         are then printed at minimum distance.  This is mostly useful
369         for ancient notation, but may also be useful for some flavours
370         of contemporary music.  If not in raggedright mode, lily will
371         pack as much bars of music as possible into a line, but the
372         line will then be stretched to fill the whole linewidth.
373       */
374       inverse_strength = 1.0;
375       distance = compound_fixed_note_space;
376     }
377   else
378     {
379       inverse_strength = (compound_note_space - compound_fixed_note_space);
380       distance = compound_note_space;
381     }
382
383   Spaceable_grob::add_spring (left_col, right_col, distance, inverse_strength);
384 }
385
386 /*
387   Read hints from L and generate springs.
388 */
389 void
390 Spacing_spanner::breakable_column_spacing (Grob *me, Item *l, Item *r,
391                                            Spacing_options const *options)
392 {
393   Real compound_fixed = 0.0;
394   Real compound_space = 0.0;
395   Real max_fixed = 0.0;
396   Real max_space = 0.0;
397   
398   int wish_count = 0;
399
400   Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
401
402   if (dt == Moment (0, 0))
403     {
404       extract_grob_set (l, "spacing-wishes", wishes);
405
406       for (vsize i = 0; i < wishes.size (); i++)
407         {
408           Item *spacing_grob = dynamic_cast<Item *> (wishes[i]);
409
410           if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
411             continue;
412
413           Real space;
414           Real fixed_space;
415
416           /*
417             column for the left one settings should be ok due automatic
418             pointer munging.
419
420           */
421           assert (spacing_grob->get_column () == l);
422
423           Staff_spacing::get_spacing_params (spacing_grob,
424                                              &space, &fixed_space);
425
426           if (Paper_column::when_mom (r).grace_part_)
427             {
428               /*
429                 Correct for grace notes.
430
431                 Ugh. The 0.8 is arbitrary.
432               */
433               space *= 0.8;
434             }
435
436           max_space = max (max_space, space);
437           max_fixed = max (max_fixed, fixed_space);
438           
439           compound_space += space;
440           compound_fixed += fixed_space;
441           wish_count++;
442         }
443     }
444
445   if (compound_space <= 0.0 || !wish_count)
446     {
447       standard_breakable_column_spacing (me, l, r, &compound_fixed, &compound_space,
448                                          options);
449       wish_count = 1;
450     }
451   else
452     {
453       if (to_boolean (me->get_property ("average-spacing-wishes")))
454         {
455           compound_space /= wish_count;
456           compound_fixed /= wish_count;
457         }
458       else
459         {
460           compound_fixed = max_fixed;
461           compound_space = max_space;
462         }
463       
464     }
465
466   if (options->stretch_uniformly_ && l->break_status_dir () != RIGHT)
467     compound_fixed = 0.0;
468
469   assert (!isinf (compound_space));
470   compound_space = max (compound_space, compound_fixed);
471
472   /*
473     There used to be code that changed spacing depending on
474     raggedright setting.  Ugh.
475
476     Do it more cleanly, or rename the property.
477
478   */
479   Real inverse_strength = (compound_space - compound_fixed);
480   Real distance = compound_space;
481   Spaceable_grob::add_spring (l, r, distance, inverse_strength);
482 }
483
484 ADD_INTERFACE (Spacing_spanner, "spacing-spanner-interface",
485                "The space taken by a note is dependent on its duration. Doubling a\n"
486                "duration adds spacing-increment to the space. The most common shortest\n"
487                "note gets @code{shortest-duration-space}. Notes that are even shorter are\n"
488                "spaced proportonial to their duration.\n"
489                "\n"
490                "Typically, the increment is the width of a black note head.  In a\n"
491                "piece with lots of 8th notes, and some 16th notes, the eighth note\n"
492                "gets 2 note heads width (i.e. the space following a note is 1 note\n"
493                "head width) A 16th note is followed by 0.5 note head width. The\n"
494                "quarter note is followed by  3 NHW, the half by 4 NHW, etc.\n",
495
496                
497                "average-spacing-wishes "
498                "base-shortest-duration "
499                "common-shortest-duration "
500                "packed-spacing "
501                "shortest-duration-space "
502                "spacing-increment "
503                "strict-grace-spacing "
504                "strict-note-spacing "
505                "uniform-stretching "
506                
507                );
508
509 ADD_INTERFACE (Spacing_interface, "spacing-interface",
510                "Something to do with line breaking and spacing. "
511                "Kill this one after determining line breaks.",
512                
513                "");
514