]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
release: 1.5.37
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include <math.h>
11
12 #include "line-of-score.hh"
13 #include "paper-score.hh"
14 #include "paper-column.hh"
15 #include "item.hh"
16 #include "moment.hh"
17 #include "note-spacing.hh"
18 #include "misc.hh"
19 #include "warn.hh"
20 #include "staff-spacing.hh"
21
22 /*
23   paper-column:
24
25   Don't be confused by right-items: each spacing wish can also contain
26   a number of items, with which a spacing constraint may be kept. It's
27   a little baroque, but it might come in handy later on?
28     
29  */
30
31 class Spacing_spanner
32 {
33 public:
34   static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment)  ;
35   static Real note_spacing (Grob*,Grob*,Grob*,Moment)  ;
36   static Real get_duration_space (Grob*,Moment dur, Moment shortest) ;
37   
38   static void breakable_column_spacing (Item* l, Item *r);
39   static void find_loose_columns () {}
40   static void prune_loose_colunms (Link_array<Grob> *cols);
41   static void find_loose_columns (Link_array<Grob> cols);
42   static void set_explicit_neighbor_columns (Link_array<Grob> cols);
43   static void set_implicit_neighbor_columns (Link_array<Grob> cols);
44   static void do_measure (Grob*me,Link_array<Grob> *cols);
45   DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
46 };
47
48
49
50 /*
51   Return whether COL is fixed to its neighbors by some kind of spacing
52   constraint.
53 */
54 static bool
55 loose_column (Grob *l, Grob *c, Grob *r) 
56 {
57   SCM rns = c->get_grob_property ("right-neighbors");
58   SCM lns = c->get_grob_property ("left-neighbors");
59
60  /*
61     If this column doesn't have a proper neighbor, we should really
62     make it loose, but spacing it correctly is more than we can
63     currently can handle.
64
65     (this happens in the following situation:
66
67        |
68        |    clef G 
69       *
70
71        |               |      ||
72        |               |      || 
73       O               O       ||
74
75
76     the column containing the clef is really loose, and should be
77     attached right to the first column, but that is a lot of work for
78     such a borderline case.
79
80     )
81     
82   */  
83   if (!gh_pair_p (lns) || !gh_pair_p (rns))
84     return false;
85
86   Item * l_neighbor = dynamic_cast<Item*>  (unsmob_grob (gh_car (lns)));
87   Item * r_neighbor = dynamic_cast<Item*>  (unsmob_grob (gh_car (rns)));
88
89   if (!l_neighbor || !r_neighbor)
90     return false;
91
92   l_neighbor = l_neighbor->column_l();
93   r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column  (r_neighbor));
94
95   if (l == l_neighbor && r == r_neighbor)
96     return false;
97
98   if (!l_neighbor || !r_neighbor)
99     return false;
100
101   /*
102     Only declare loose if the bounds make a little sense.  This means
103     some cases (two isolated, consecutive clef changes) won't be
104     nicely folded, but hey, then don't do that.
105   */
106   if( (Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor))
107       && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor)))
108     {
109       return true;
110     }
111
112
113   /*
114     If in doubt: we're not loose; the spacing engine should space for
115     it, risking suboptimal spacing.
116
117     (Otherwise, we might risk core dumps, and other weird stuff.)
118
119
120   */
121   return false;
122 }
123
124 /*
125   Remove columns that are not tightly fitting from COLS. In the
126   removed columns, set 'between-cols to the columns where it is in
127   between.
128 */
129 void
130 Spacing_spanner::prune_loose_colunms (Link_array<Grob> *cols)
131 {
132   Link_array<Grob> newcols;
133   
134   for (int i=0; i < cols->size ();  i++)
135     {
136       if (Item::breakable_b (cols->elem(i)) || Paper_column::musical_b (cols->elem (i)))
137         {
138           newcols.push (cols->elem(i));
139           continue;
140         }
141
142       Grob *c = cols->elem(i);
143       if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
144         {
145           SCM lns = c->get_grob_property ("left-neighbors");
146           lns = gh_pair_p (lns) ? gh_car (lns) : SCM_BOOL_F;
147
148           SCM rns = c->get_grob_property ("right-neighbors");
149           rns = gh_pair_p (rns) ? gh_car (rns) : SCM_BOOL_F;
150
151           /*
152             Either object can be non existent, if the score ends
153             prematurely.
154            */
155           rns = gh_car (unsmob_grob (rns)->get_grob_property ("right-items"));
156           c->set_grob_property ("between-cols", gh_cons (lns,
157                                                          rns));
158
159           /*
160             Set distance constraints for loose columns
161           */
162           Drul_array<Grob*> next_door;
163           next_door[LEFT] =cols->elem (i - 1);
164           next_door[RIGHT] =cols->elem (i + 1);   
165           Direction d = LEFT;
166           Drul_array<Real> dists(0,0);
167
168           do
169             {
170               dists[d] = 0.0;
171               Grob *lc = (d == LEFT)  ? next_door[LEFT] : c;
172               Grob *rc = d == LEFT  ? c : next_door[RIGHT];           
173
174               for (SCM s = lc->get_grob_property ("spacing-wishes");
175                    gh_pair_p (s); s = gh_cdr (s))
176                 {
177                   Grob *sp = unsmob_grob (gh_car (s));
178                   if (Note_spacing::left_column (sp) != lc
179                       || Note_spacing::right_column (sp) != rc)
180                     continue;
181
182                   dists[d] = dists[d] >? Note_spacing::get_spacing (sp);
183                 }
184             }
185           while (flip (&d) != LEFT);
186
187           Rod r;
188           r.distance_f_ = dists[LEFT] + dists[RIGHT];
189           r.item_l_drul_[LEFT] = dynamic_cast<Item*> (cols->elem(i-1));
190           r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
191
192           r.add_to_cols ();
193         }
194       else
195         {
196           newcols.push (c);
197         }
198     }
199
200   *cols = newcols;
201 }
202
203 /*
204   Set neighboring columns determined by the spacing-wishes grob property.  
205 */
206 void
207 Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
208 {
209   for (int i=0; i < cols.size(); i++)
210     {
211       SCM right_neighbors = SCM_EOL;
212       int min_rank = 100000;    // inf.
213
214
215       SCM wishes=  cols[i]->get_grob_property ("spacing-wishes");
216       for (SCM s =wishes; gh_pair_p (s); s = gh_cdr (s))
217         {
218           Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
219
220           Item * lc = wish->column_l ();
221           Grob * right = Note_spacing::right_column (wish);
222
223           if (!right)
224             continue;
225
226           Item * rc = dynamic_cast<Item*> (right);
227
228           int right_rank = Paper_column::rank_i (rc);
229           int left_rank = Paper_column::rank_i (lc);      
230
231           /*
232             update the left column.
233            */
234           if (right_rank <= min_rank)
235             {
236               if (right_rank < min_rank)
237                 right_neighbors  =SCM_EOL;
238               
239               min_rank = right_rank;
240               right_neighbors = gh_cons (wish->self_scm (), right_neighbors);
241             }
242
243           /*
244             update the right column of the wish.
245            */
246           int maxrank = 0;
247           SCM left_neighs = rc->get_grob_property ("left-neighbors");
248           if (gh_pair_p (left_neighs)
249               && unsmob_grob (gh_car (left_neighs)))
250             {
251               Item * it = dynamic_cast<Item*> (unsmob_grob (gh_car (left_neighs)));
252               maxrank = Paper_column::rank_i (it->column_l());
253             }
254
255           if (left_rank >= maxrank)
256             {
257               if (left_rank > maxrank)
258                 left_neighs = SCM_EOL;
259
260               left_neighs = gh_cons (wish->self_scm (), left_neighs);
261               rc->set_grob_property ("left-neighbors", right_neighbors);
262             }
263         }
264
265       if (gh_pair_p (right_neighbors))
266         {
267           cols[i]->set_grob_property ("right-neighbors", right_neighbors);
268         }
269     }
270 }
271
272 /*
273   Set neighboring columns that have no left/right-neighbor set
274   yet. Only do breakable non-musical columns, and musical columns. 
275 */
276 void
277 Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
278 {
279   for (int i = 0; i < cols.size (); i++)
280     {
281       Item * it = dynamic_cast<Item*>(cols[i]);
282       if (!Item::breakable_b (it) && !Paper_column::musical_b (it))
283         continue;
284
285       // it->breakable || it->musical
286
287       /*
288         sloppy with typnig left/right-neighbors should take list, but paper-column found instead.
289        */
290       SCM ln = cols[i] ->get_grob_property ("left-neighbors");
291       if (!gh_pair_p (ln) && i ) 
292         {
293           cols[i]->set_grob_property ("left-neighbors", gh_cons (cols[i-1]->self_scm(), SCM_EOL));
294         }
295
296       SCM rn = cols[i] ->get_grob_property ("right-neighbors");
297       if (!gh_pair_p (rn) && i < cols.size () - 1) 
298         {
299           cols[i]->set_grob_property ("right-neighbors", gh_cons (cols[i + 1]->self_scm(), SCM_EOL));
300         }
301     }
302 }
303
304
305 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
306 SCM
307 Spacing_spanner::set_springs (SCM smob)
308 {
309   Grob *me = unsmob_grob (smob);
310
311   Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
312
313   set_explicit_neighbor_columns (all);
314   prune_loose_colunms (&all);
315   set_implicit_neighbor_columns (all);
316   
317   int j = 0;
318   for (int i = 1; i < all.size (); i++)
319     {
320       Grob *sc = all[i];
321       if (Item::breakable_b (sc))
322         {
323           Link_array<Grob> measure (all.slice (j, i+1));          
324           do_measure (me, &measure);
325           j = i;
326         }
327     }
328
329   return SCM_UNSPECIFIED;
330 }
331
332
333 void
334 Spacing_spanner::do_measure (Grob*me, Link_array<Grob> *cols) 
335 {
336   Moment shortest_in_measure;
337
338   /*
339     space as if this duration  is present. 
340   */
341   Moment base_shortest_duration = *unsmob_moment (me->get_grob_property ("maximum-duration-for-spacing"));
342   shortest_in_measure.set_infinite (1);
343
344   for (int i =0 ; i < cols->size (); i++)  
345     {
346       if (Paper_column::musical_b (cols->elem (i)))
347         {
348           Moment *when = unsmob_moment (cols->elem (i)->get_grob_property  ("when"));
349
350           /*
351             ignore grace notes for shortest notes.
352           */
353           if (when && when->grace_part_)
354             continue;
355           
356           SCM  st = cols->elem (i)->get_grob_property ("shortest-starter-duration");
357           Moment this_shortest = *unsmob_moment (st);
358           shortest_in_measure = shortest_in_measure <? this_shortest;
359         }
360     }
361   
362   Array<Spring> springs;
363
364   for (int i= 0; i < cols->size () - 1; i++)
365     {
366       Item * l = dynamic_cast<Item*> (cols->elem (i));
367       Item * r =  dynamic_cast<Item*> (cols->elem (i+1));
368
369       Paper_column * lc = dynamic_cast<Paper_column*> (l);
370       Paper_column * rc = dynamic_cast<Paper_column*> (r);
371
372       if (!Paper_column::musical_b (l))
373         {
374           breakable_column_spacing (l, r);
375
376           /*
377             
378             The case that the right part is broken as well is rather
379             rare, but it is possible, eg. with a single empty measure,
380             or if one staff finishes a tad earlier than the rest.
381             
382            */
383           Item *lb = l->find_prebroken_piece (RIGHT);
384           Item *rb = r->find_prebroken_piece (LEFT);
385           
386           if (lb)
387             breakable_column_spacing (lb,r);
388
389           if (rb)
390             breakable_column_spacing (l, rb);
391           if (lb && rb)
392             breakable_column_spacing (lb, rb);
393           
394           continue ; 
395         }
396       
397       Real note_space = note_spacing (me,lc, rc, shortest_in_measure <? base_shortest_duration);
398       Real hinterfleisch = note_space;
399       Real headwid = gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
400
401       SCM seq  = lc->get_grob_property ("right-neighbors");
402
403       /*
404         hinterfleisch = hind-meat = amount of space following a note.
405
406         
407         We adjust the space following a note only if the next note
408         happens after the current note (this is set in the grob
409         property SPACING-SEQUENCE.  */
410
411       Real stretch_distance = note_space;
412
413       hinterfleisch = -1.0;
414       Real max_factor = 0.0;
415       for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
416         {
417           Grob * wish = unsmob_grob (gh_car (s));
418
419           if (Note_spacing::left_column (wish) != lc
420               || Note_spacing::right_column (wish) != rc)
421             continue;
422
423           /*
424             This is probably a waste of time in the case of polyphonic
425             music.  */
426           if (Note_spacing::has_interface (wish))
427             {
428               hinterfleisch = hinterfleisch >?
429                 ( - headwid +
430
431                   (note_space + Note_spacing::get_spacing (wish))
432                   *gh_scm2double (wish->get_grob_property ("space-factor"))
433
434                   + Note_spacing::stem_dir_correction (wish));
435             }
436         }
437
438       if (hinterfleisch < 0)
439         {
440           // maybe should issue a programming error.
441           hinterfleisch = note_space;
442         }
443       else
444         stretch_distance -= headwid; // why?
445
446       if (max_factor == 0.0)
447         max_factor = 1.0; 
448       
449       Spring s;
450       s.distance_f_ = max_factor *  hinterfleisch;
451       s.strength_f_ = 1 / stretch_distance;
452
453       s.item_l_drul_[LEFT] = l;
454       s.item_l_drul_[RIGHT] = r;
455
456       s.add_to_cols();
457       if (r->find_prebroken_piece (LEFT))
458         {
459           s.item_l_drul_[RIGHT] = r->find_prebroken_piece(LEFT);
460           s.add_to_cols();
461         }
462     }
463
464 }
465
466
467 /*
468   Read hints from L (todo: R) and generate springs.
469  */
470 void
471 Spacing_spanner::breakable_column_spacing (Item* l, Item *r)
472 {
473   Real max_fixed = -infinity_f;
474   Real max_space = -infinity_f;
475   
476   for (SCM s = l->get_grob_property ("spacing-wishes");
477        gh_pair_p (s); s = gh_cdr (s))
478     {
479       Grob * spacing_grob = unsmob_grob (gh_car (s));
480
481       if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
482         continue;
483
484       Real space;
485       Real fixed_space;
486
487       Staff_spacing::get_spacing_params (spacing_grob,
488                                          &space, &fixed_space);  
489       if (space > max_space)
490         {
491           max_space = space;
492           max_fixed = fixed_space;
493         }
494     }
495
496   if (isinf (max_space))
497     {
498       programming_error ("No pref spacing found");
499       max_space = 2.0;
500       max_fixed = 1.0;
501     }
502   
503   Spring s;
504   s.distance_f_ = max_space;
505   s.strength_f_ = 1/(max_space - max_fixed);
506   
507   s.item_l_drul_[LEFT] = l;
508   s.item_l_drul_[RIGHT] = r;
509
510   s.add_to_cols ();
511 }
512
513
514 /**
515   Get the measure wide ant for arithmetic spacing.
516
517   @see
518   John S. Gourlay. ``Spacing a Line of Music,'' Technical Report
519   OSU-CISRC-10/87-TR35, Department of Computer and Information Science,
520   The Ohio State University, 1987.
521
522   */
523 Real
524 Spacing_spanner::get_duration_space (Grob*me, Moment d, Moment shortest) 
525 {
526   Real log =  log_2 (shortest.main_part_);
527   Real k = gh_scm2double (me->get_grob_property ("arithmetic-basicspace"))
528     - log;
529
530   Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
531   
532   return (log_2 (compdur) + k) * gh_scm2double (me->get_grob_property ("arithmetic-multiplier"));
533 }
534
535
536 Real
537 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
538                                    Moment shortest) 
539 {
540   Moment shortest_playing_len = 0;
541   SCM s = lc->get_grob_property ("shortest-playing-duration");
542
543
544   if (unsmob_moment (s))
545     shortest_playing_len = *unsmob_moment (s);
546   
547   if (! shortest_playing_len.to_bool ())
548     {
549       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
550       shortest_playing_len = 1;
551     }
552   
553   if (! shortest.to_bool ())
554     {
555       programming_error ("no minimum in measure at " + Paper_column::when_mom (lc).str ());
556       shortest = 1;
557     }
558   Moment delta_t = Paper_column::when_mom (rc) - Paper_column::when_mom (lc);
559   Real dist = 0.0;
560
561   if (delta_t.main_part_)
562     {
563       dist = get_duration_space (me, shortest_playing_len, shortest);
564       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
565     }
566   else if (delta_t.grace_part_)
567     {
568       dist = get_duration_space (me, shortest, shortest);
569
570       Real grace_fact = 1.0;
571       SCM gf = me->get_grob_property ("grace-space-factor");
572       if (gh_number_p (gf))
573         grace_fact = gh_scm2double (gf);
574
575       dist *= grace_fact; 
576     }
577
578 #if 0
579   /*
580     TODO: figure out how to space grace notes.
581    */
582
583   dist *= 
584     +  grace_fact * (double) (delta_t.grace_part_ / shortest_playing_len.main_part_);
585
586
587   Moment *lm = unsmob_moment (lc->get_grob_property ("when"));
588   Moment *rm = unsmob_moment (rc->get_grob_property ("when"));
589
590   if (lm && rm)
591     {
592       if (lm->grace_part_ && rm->grace_part_)
593         dist *= 0.5;
594       else if (!rm->grace_part_ && lm->grace_part_)
595         dist *= 0.7;
596     }
597 #endif
598   
599   return dist;
600 }
601