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