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