]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 1.3.70
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8
9 */
10
11 /*
12   [TODO]
13     * less hairy code
14     * move paper vars to scm
15
16     remove *-hs variables.
17 */
18
19
20 #include <math.h> // tanh.
21
22
23 #include "directional-element-interface.hh"
24 #include "beaming.hh"
25 #include "beam.hh"
26 #include "misc.hh"
27 #include "least-squares.hh"
28 #include "stem.hh"
29 #include "paper-def.hh"
30 #include "lookup.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "cross-staff.hh"
34 #include "item.hh"
35 #include "spanner.hh"
36 #include "warn.hh"
37
38 void
39 Beam::add_stem (Score_element*me, Score_element*s)
40 {
41   Pointer_group_interface gi (me, "stems");
42   gi.add_element (s);
43   
44   s->add_dependency (me);
45
46   assert (!Stem::beam_l (s));
47   s->set_elt_property ("beam", me->self_scm_);
48
49   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
50 }
51
52 int
53 Beam::get_multiplicity (Score_element*me) 
54 {
55   int m = 0;
56   for (SCM s = me->get_elt_property ("stems"); gh_pair_p (s); s = gh_cdr (s))
57     {
58       Score_element * sc = unsmob_element (gh_car (s));
59
60       if (Stem::has_interface (sc))
61         m = m >? Stem::beam_count (sc,LEFT) >? Stem::beam_count (sc,RIGHT);
62     }
63   return m;
64 }
65
66 /*
67   After pre-processing all directions should be set.
68   Several post-processing routines (stem, slur, script) need stem/beam
69   direction.
70   Currenly, this means that beam has set all stem's directions.
71   [Alternatively, stems could set its own directions, according to
72    their beam, during 'final-pre-processing'.]
73  */
74 MAKE_SCHEME_CALLBACK(Beam,before_line_breaking);
75 SCM
76 Beam::before_line_breaking (SCM smob)
77 {
78   Score_element * me =  unsmob_element (smob);
79
80   // Why?
81   if (visible_stem_count (me) < 2)
82     {
83       warning (_ ("beam has less than two stems"));
84     }
85
86   if (!Directional_element_interface (me).get ())
87     Directional_element_interface (me).set (get_default_dir (me));
88
89   auto_knees (me);
90   set_stem_directions (me);
91   set_stem_shorten (me);
92
93   return SCM_EOL;
94 }
95
96 /*
97  FIXME
98  */
99 Direction
100 Beam::get_default_dir (Score_element*me) 
101 {
102   Drul_array<int> total;
103   total[UP]  = total[DOWN] = 0;
104   Drul_array<int> count; 
105   count[UP]  = count[DOWN] = 0;
106   Direction d = DOWN;
107
108   Link_array<Item> stems=
109         Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
110
111   for (int i=0; i <stems.size (); i++)
112     do { // HUH -- waar slaat dit op?
113       Score_element *s = stems[i];
114       Direction sd = Directional_element_interface (s).get ();
115       int current = sd  ? (1 + d * sd)/2
116         : Stem::get_center_distance (s, (Direction)-d);
117
118       if (current)
119         {
120           total[d] += current;
121           count[d] ++;
122         }
123
124     } while (flip(&d) != DOWN);
125   
126
127   SCM s = scm_eval (gh_list (ly_symbol2scm ("beam-dir-algorithm"),
128                              ly_quote_scm (gh_cons (gh_int2scm (count[UP]),
129                                                     gh_int2scm (count[DOWN]))),
130                              ly_quote_scm (gh_cons (gh_int2scm (total[UP]),
131                                                     gh_int2scm (total[DOWN]))),
132                              SCM_UNDEFINED));
133   if (gh_number_p (s) && gh_scm2int (s))
134     return to_dir (s);
135   
136   /*
137     If dir is not determined: get default
138   */
139   return to_dir (me->get_elt_property ("default-neutral-direction"));
140 }
141
142
143 /*
144   Set all stems with non-forced direction to beam direction.
145   Urg: non-forced should become `without/with unforced' direction,
146        once stem gets cleaned-up.
147  */
148 void
149 Beam::set_stem_directions (Score_element*me)
150 {
151   Link_array<Item> stems
152     =Pointer_group_interface__extract_elements (me,  (Item*) 0, "stems");
153   Direction d = Directional_element_interface (me).get ();
154   
155   for (int i=0; i <stems.size (); i++)
156     {
157       Score_element *s = stems[i];
158       SCM force = s->remove_elt_property ("dir-forced");
159       if (!gh_boolean_p (force) || !gh_scm2bool (force))
160         Directional_element_interface (s).set (d);
161     }
162
163
164 void
165 Beam::auto_knees (Score_element*me)
166 {
167   if (!auto_knee (me,"auto-interstaff-knee-gap", true))
168     auto_knee (me, "auto-knee-gap", false);
169 }
170
171 /*
172   Simplistic auto-knees; only consider vertical gap between two
173   adjacent chords.
174
175   `Forced' stem directions are ignored.  If you don't want auto-knees,
176   don't set, or unset autoKneeGap/autoInterstaffKneeGap.
177  */
178 bool
179 Beam::auto_knee (Score_element*me, String gap_str, bool interstaff_b)
180 {
181   bool knee_b = false;
182   int knee_y = 0;
183   SCM gap = me->get_elt_property (gap_str.ch_C());
184   
185   Direction d = Directional_element_interface (me).get ();
186       Link_array<Item> stems=
187         Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
188   
189   if (gh_number_p (gap))
190     {
191       Spanner*sp = dynamic_cast<Spanner*> (me);
192       int auto_gap_i = gh_scm2int (gap);
193       for (int i=1; i < stems.size (); i++)
194         {
195           bool is_b = (bool)(calc_interstaff_dist (stems[i], sp) 
196             - calc_interstaff_dist (stems[i-1], sp));
197           int l_y = (int)(Stem::head_positions(stems[i-1])[d])
198             + (int)calc_interstaff_dist (stems[i-1], sp);
199           int r_y = (int)(Stem::head_positions(stems[i])[d])
200             + (int)calc_interstaff_dist (stems[i], sp);
201           int gap_i = r_y - l_y;
202
203           if ((abs (gap_i) >= auto_gap_i) && (!interstaff_b || is_b))
204             {
205               knee_y = (r_y + l_y) / 2;
206               knee_b = true;
207               break;
208             }
209         }
210     }
211   if (knee_b)
212     {
213       for (int i=0; i < stems.size (); i++)
214         {
215           Item *s = stems[i];     
216           int y = (int)(Stem::head_positions(s)[d])
217             + (int)calc_interstaff_dist (s, dynamic_cast<Spanner*> (me));
218
219           Directional_element_interface (s).set (y < knee_y ? UP : DOWN);
220           s->set_elt_property ("dir-forced", SCM_BOOL_T);
221         }
222     }
223   return knee_b;
224 }
225
226 /*
227  Set stem's shorten property if unset.
228  TODO:
229     take some y-position (chord/beam/nearest?) into account
230     scmify forced-fraction
231  */
232 void
233 Beam::set_stem_shorten (Score_element*m)
234 {
235   Spanner*me = dynamic_cast<Spanner*> (m);
236   if (!visible_stem_count (me))
237     return;
238
239   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
240   if (forced_fraction < 0.5)
241     return;
242
243   int multiplicity = get_multiplicity (me);
244
245   // grace stems?
246   SCM shorten = scm_eval (ly_symbol2scm ("beamed-stem-shorten"));
247
248   if (shorten == SCM_EOL)
249     return;
250
251   int sz = scm_ilength (shorten);
252   
253   Real staff_space = Staff_symbol_referencer::staff_space (me);
254   SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity <? (sz - 1)));
255   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
256
257   /* cute, but who invented me -- how to customise ? */
258   if (forced_fraction < 1)
259     shorten_f /= 2;
260
261   Link_array<Item> stems=
262     Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
263
264   for (int i=0; i < stems.size (); i++)
265     {
266       Item* s = stems[i];
267       if (Stem::invisible_b (s))
268         continue;
269       if (gh_number_p (s->get_elt_property ("shorten")))
270         s->set_elt_property ("shorten", gh_double2scm (shorten_f));
271     }
272 }
273
274 /*
275   Set elt properties height and y-position if not set.
276   Adjust stem lengths to reach beam.
277  */
278 MAKE_SCHEME_CALLBACK(Beam,after_line_breaking);
279 SCM
280 Beam::after_line_breaking (SCM smob)
281 {
282   Score_element * me =  unsmob_element (smob);
283
284   /* first, calculate y, dy */
285   Real y, dy;
286 calc_default_position_and_height (me, &y, &dy);
287   if (visible_stem_count (me))
288     {
289       if (suspect_slope_b (me, y, dy))
290         dy = 0;
291
292       Real damped_dy = calc_slope_damping_f (me, dy);
293       Real quantised_dy = quantise_dy_f (me, damped_dy);
294
295       y += (dy - quantised_dy) / 2;
296       dy = quantised_dy;
297     }
298   /*
299     until here, we used only stem_info, which acts as if dir=up
300    */
301   y *= Directional_element_interface (me).get ();
302   dy *= Directional_element_interface (me).get ();
303
304
305   Real half_space = Staff_symbol_referencer::staff_space (me) / 2;
306
307   /* check for user-override of dy */
308   SCM s = me->remove_elt_property ("height-hs");
309   if (gh_number_p (s))
310     {
311       dy = gh_scm2double (s) * half_space;
312     }
313   me->set_elt_property ("height", gh_double2scm (dy));
314
315   /* check for user-override of y */
316   s = me->remove_elt_property ("y-position-hs");
317   if (gh_number_p (s))
318     {
319       y = gh_scm2double (s) * half_space;
320     }
321   else
322     { 
323       /* we can modify y, so we should quantise y */
324       Real y_shift = check_stem_length_f (me, y, dy);
325       y += y_shift;
326       y = quantise_y_f (me,y, dy, 0);
327       set_stem_length (me, y, dy);
328       y_shift = check_stem_length_f (me, y, dy);
329
330       if (y_shift > half_space / 4)
331         {
332           y += y_shift;
333
334           /*
335             for significantly lengthened or shortened stems,
336             request quanting the other way.
337           */
338           int quant_dir = 0;
339           if (abs (y_shift) > half_space / 2)
340             quant_dir = sign (y_shift) * Directional_element_interface (me).get ();
341           y = quantise_y_f (me, y, dy, quant_dir);
342         }
343     }
344   // UGH. Y is not in staff position unit?
345   // Ik dacht datwe daar juist van weg wilden?
346   set_stem_length (me, y, dy);
347   me->set_elt_property ("y-position", gh_double2scm (y));
348
349   return SCM_UNDEFINED;
350 }
351
352 /*
353   See Documentation/tex/fonts.doc
354  */
355 void
356 Beam::calc_default_position_and_height (Score_element*me,Real* y, Real* dy) 
357 {
358   *y = 0;
359   *dy = 0;  
360   if (visible_stem_count (me) <= 1)
361     return;
362
363   Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
364   if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_)
365     {
366       *dy = 0;
367       *y = first_ideal;
368       return;
369     }
370
371   Array<Offset> ideals;
372   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
373   Link_array<Item> stems=
374     Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
375
376   for (int i=0; i < stems.size (); i++)
377     {
378       Item* s = stems[i];
379       if (Stem::invisible_b (s))
380         continue;
381       ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, 
382                            Stem::calc_stem_info (s).idealy_f_));
383     }
384   Real dydx;
385   minimise_least_squares (&dydx, y, ideals); // duh, takes references
386
387   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
388   *dy = dydx * dx;
389 }
390
391 bool
392 Beam::suspect_slope_b (Score_element*me, Real y, Real dy) 
393 {
394   /* first, calculate y, dy */
395   /*
396     steep slope running against lengthened stem is suspect
397   */
398   Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
399   Real last_ideal = Stem::calc_stem_info (last_visible_stem (me)).idealy_f_;
400   Real lengthened = me->paper_l ()->get_var ("beam_lengthened");
401   Real steep = me->paper_l ()->get_var ("beam_steep_slope");
402
403   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
404   Real dydx = dy && dx ? dy/dx : 0;
405
406   if (((y - first_ideal > lengthened) && (dydx > steep))
407       || ((y + dy - last_ideal > lengthened) && (dydx < -steep)))
408     {
409       return true;
410     }
411   return false;
412 }
413
414 /*
415   This neat trick is by Werner Lemberg,
416   damped = tanh (slope)
417   corresponds with some tables in [Wanske]
418 */
419 Real
420 Beam::calc_slope_damping_f (Score_element*me,Real dy) 
421 {
422   SCM damp = me->get_elt_property ("damping"); 
423   int damping = gh_scm2int (damp);
424
425   if (damping)
426     {
427       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
428         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
429       Real dydx = dy && dx ? dy/dx : 0;
430       dydx = 0.6 * tanh (dydx) / damping;
431       return dydx * dx;
432     }
433   return dy;
434 }
435
436 Real
437 Beam::calc_stem_y_f (Score_element*me,Item* s, Real y, Real dy) 
438 {
439   Real thick = gh_scm2double (me->get_elt_property ("beam-thickness"));
440   thick *= me->paper_l ()->get_var ("staffspace");
441   
442   int beam_multiplicity = get_multiplicity (me);
443   int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
444
445   Real interbeam_f = me->paper_l ()->interbeam_f (beam_multiplicity);
446   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
447   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
448   Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y;
449
450   /* knee */
451    Direction dir  = Directional_element_interface(me).get ();
452    Direction sdir = Directional_element_interface (s).get ();
453    
454     /* knee */
455    if (dir!= sdir)
456       {
457        stem_y -= dir 
458         * (thick / 2 + (beam_multiplicity - 1) * interbeam_f);
459
460
461       
462       // huh, why not for first visible?
463        if (Staff_symbol_referencer::staff_symbol_l (s)
464            != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
465          stem_y += Directional_element_interface (me).get ()
466            * (beam_multiplicity - stem_multiplicity) * interbeam_f;
467       }
468
469   return stem_y;
470 }
471
472 Real
473 Beam::check_stem_length_f (Score_element*me,Real y, Real dy) 
474 {
475   Real shorten = 0;
476   Real lengthen = 0;
477   Direction dir = Directional_element_interface (me).get ();
478
479   Link_array<Item> stems=
480     Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
481
482   for (int i=0; i < stems.size(); i++)
483     {
484       Item* s = stems[i];
485       if (Stem::invisible_b (s))
486         continue;
487
488       Real stem_y = calc_stem_y_f (me, s, y, dy);
489         
490       stem_y *= dir;
491       Stem_info info = Stem::calc_stem_info (s);
492
493       // if (0 > info.maxy_f_ - stem_y)
494       shorten = shorten <? info.maxy_f_ - stem_y;
495       // if (0 < info.miny_f_ - stem_y)
496       lengthen = lengthen >? info.miny_f_ - stem_y; 
497     }
498
499   if (lengthen && shorten)
500     warning (_ ("weird beam vertical offset"));
501
502   /* when all stems are too short, normal stems win */
503   return dir * ((shorten) ?  shorten : lengthen);
504 }
505
506 /*
507   Hmm.  At this time, beam position and slope are determined.  Maybe,
508   stem directions and length should set to relative to the chord's
509   position of the beam.  */
510 void
511 Beam::set_stem_length (Score_element*me,Real y, Real dy)
512 {
513   Real half_space = Staff_symbol_referencer::staff_space (me)/2;
514   Link_array<Item> stems=
515     Pointer_group_interface__extract_elements (me, (Item*)0, "stems");
516
517
518   for (int i=0; i < stems.size (); i++)
519     {
520       Item* s = stems[i];
521       if (Stem::invisible_b (s))
522         continue;
523
524       Real stem_y = calc_stem_y_f (me, s, y, dy);
525
526       /* caution: stem measures in staff-positions */
527       Stem::set_stemend (s,(stem_y + calc_interstaff_dist (s, dynamic_cast<Spanner*> (me))) / half_space);
528     }
529 }
530
531 /*
532   [Ross] (simplification of)
533   Set dy complying with:
534     - zero
535     - thick / 2 + staffline_f / 2
536     - thick + staffline_f
537   + n * staff_space
538 */
539 Real
540 Beam::quantise_dy_f (Score_element*me,Real dy) 
541 {
542   Array<Real> a;
543   for (SCM s = scm_eval (ly_symbol2scm ("beam-height-quants")); s !=SCM_EOL; s = gh_cdr (s))
544     a.push (gh_scm2double (gh_car (s)));
545   
546   if (a.size () <= 1)
547     return dy;
548
549   Real staff_space = Staff_symbol_referencer::staff_space (me);
550   
551   Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
552   Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
553     ? iv[SMALLER]
554     : iv[BIGGER];
555   
556   return q * sign (dy);
557 }
558
559 /*
560   Prevent interference from stafflines and beams.
561   See Documentation/tex/fonts.doc
562
563   We only need to quantise the (left) y-position of the beam,
564   since dy is quantised too.
565   if extend_b then stems must *not* get shorter
566  */
567 Real
568 Beam::quantise_y_f (Score_element*me,Real y, Real dy, int quant_dir)
569 {
570   int multiplicity = get_multiplicity (me);
571
572   Real staff_space = Staff_symbol_referencer::staff_space (me);
573   SCM quants = scm_eval (gh_list (ly_symbol2scm ("beam-vertical-position-quants"),
574                                   gh_int2scm (multiplicity),
575                                   gh_double2scm (dy/staff_space),
576                                   SCM_UNDEFINED));
577
578   Array<Real> a;
579
580   for (; quants != SCM_EOL; quants = gh_cdr (quants))
581     a.push (gh_scm2double (gh_car (quants)));
582
583   if (a.size () <= 1)
584     return y;
585
586   Real up_y = Directional_element_interface (me).get () * y;
587   Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
588
589   Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y 
590     ? iv[SMALLER] : iv[BIGGER];
591   if (quant_dir)
592     q = iv[(Direction)quant_dir];
593
594   return q * Directional_element_interface (me).get ();
595 }
596
597 void
598 Beam::set_beaming (Score_element*me,Beaming_info_list *beaming)
599 {
600   Link_array<Score_element> stems=
601     Pointer_group_interface__extract_elements (me, (Score_element*)0, "stems");
602   
603   Direction d = LEFT;
604   for (int i=0; i  < stems.size(); i++)
605     {
606       do
607         {
608           if (Stem::beam_count (stems[i], d) == 0)
609             Stem::set_beaming ( stems[i], beaming->infos_.elem (i).beams_i_drul_[d],d);
610         }
611       while (flip (&d) != LEFT);
612     }
613 }
614
615
616
617 /*
618   beams to go with one stem.
619
620   BURP
621   clean  me up.
622   */
623 Molecule
624 Beam::stem_beams (Score_element*me,Item *here, Item *next, Item *prev) 
625 {
626   if ((next && !(next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) ||
627       (prev && !(prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS))))
628       programming_error ("Beams are not left-to-right");
629
630   Real staffline_f = me->paper_l ()->get_var ("stafflinethickness");
631   int multiplicity = get_multiplicity (me);
632
633
634   Real interbeam_f = me->paper_l ()->interbeam_f (multiplicity);
635   Real thick = gh_scm2double (me->get_elt_property ("beam-thickness"));
636   thick *= me->paper_l ()->get_var ("staffspace");
637     
638   Real bdy = interbeam_f;
639   Real stemdx = staffline_f;
640
641   Real dx = visible_stem_count (me) ?
642     last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS)
643     : 0.0;
644   Real dy = gh_scm2double (me->get_elt_property ("height"));
645   Real dydx = dy && dx ? dy/dx : 0;
646
647   Molecule leftbeams;
648   Molecule rightbeams;
649
650   // UGH
651   Real nw_f;
652   if (!Stem::first_head (here))
653     nw_f = 0;
654   else if (Stem::type_i (here)== 1)
655     nw_f = me->paper_l ()->get_var ("wholewidth");
656   else if (Stem::type_i (here) == 2)
657     nw_f = me->paper_l ()->get_var ("notewidth") * 0.8;
658   else
659     nw_f = me->paper_l ()->get_var ("quartwidth");
660
661
662   Direction dir = Directional_element_interface (me).get ();
663   
664   /* half beams extending to the left. */
665   if (prev)
666     {
667       int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT);
668       int lwholebeams= Stem::beam_count (here,LEFT) <? Stem::beam_count (prev,RIGHT) ;
669       /*
670        Half beam should be one note-width, 
671        but let's make sure two half-beams never touch
672        */
673       Real w = here->relative_coordinate (0, X_AXIS) - prev->relative_coordinate (0, X_AXIS);
674       w = w/2 <? nw_f;
675       Molecule a;
676       if (lhalfs)               // generates warnings if not
677         a =  me->lookup_l ()->beam (dydx, w, thick);
678       a.translate (Offset (-w, -w * dydx));
679       for (int j = 0; j  < lhalfs; j++)
680         {
681           Molecule b (a);
682           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
683           leftbeams.add_molecule (b);
684         }
685     }
686
687   if (next)
688     {
689       int rhalfs  = Stem::beam_count (here,RIGHT) - Stem::beam_count (next,LEFT);
690       int rwholebeams= Stem::beam_count (here,RIGHT) <? Stem::beam_count (next,LEFT) ;
691
692       Real w = next->relative_coordinate (0, X_AXIS) - here->relative_coordinate (0, X_AXIS);
693       Molecule a = me->lookup_l ()->beam (dydx, w + stemdx, thick);
694       a.translate_axis( - stemdx/2, X_AXIS);
695       int j = 0;
696       Real gap_f = 0;
697
698       SCM gap = me->get_elt_property ("beam-gap");
699       if (gh_number_p (gap))
700         {
701           int gap_i = gh_scm2int ( (gap));
702           int nogap = rwholebeams - gap_i;
703           
704           for (; j  < nogap; j++)
705             {
706               Molecule b (a);
707               b.translate_axis (-dir  * bdy * j, Y_AXIS);
708               rightbeams.add_molecule (b);
709             }
710           // TODO: notehead widths differ for different types
711           gap_f = nw_f / 2;
712           w -= 2 * gap_f;
713           a = me->lookup_l ()->beam (dydx, w + stemdx, thick);
714         }
715
716       for (; j  < rwholebeams; j++)
717         {
718           Molecule b (a);
719           b.translate (Offset (Stem::invisible_b (here) ? 0 : gap_f, -dir * bdy * j));
720           rightbeams.add_molecule (b);
721         }
722
723       w = w/2 <? nw_f;
724       if (rhalfs)
725         a = me->lookup_l ()->beam (dydx, w, thick);
726
727       for (; j  < rwholebeams + rhalfs; j++)
728         {
729           Molecule b (a);
730           b.translate_axis (- dir * bdy * j, Y_AXIS);
731           rightbeams.add_molecule (b);
732         }
733
734     }
735   leftbeams.add_molecule (rightbeams);
736
737   /*
738     Does beam quanting think  of the asymetry of beams? 
739     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
740    */
741   return leftbeams;
742 }
743
744 MAKE_SCHEME_CALLBACK(Beam,brew_molecule);
745 SCM
746 Beam::brew_molecule (SCM smob)
747 {
748   Score_element * me =unsmob_element (smob);
749
750   Molecule mol;
751   if (!gh_pair_p (me->get_elt_property ("stems")))
752     return SCM_EOL;
753   Real x0,dx;
754   Link_array<Item>stems = 
755     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");  
756   if (visible_stem_count (me))
757     {
758       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
759       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
760     }
761   else
762     {
763       x0 = stems[0]->relative_coordinate (0, X_AXIS);
764       dx = stems.top()->relative_coordinate (0, X_AXIS) - x0;
765     }
766   
767   
768   Real dy = gh_scm2double (me->get_elt_property ("height"));
769   Real dydx = dy && dx ? dy/dx : 0;
770   Real y = gh_scm2double (me->get_elt_property ("y-position"));
771
772
773   for (int j=0; j <stems.size  (); j++)
774     {
775       Item *i = stems[j];
776       Item * prev = (j > 0)? stems[j-1] : 0;
777       Item * next = (j < stems.size()-1) ? stems[j+1] :0;
778
779       Molecule sb = stem_beams (me, i, next, prev);
780       Real x = i->relative_coordinate (0, X_AXIS)-x0;
781       sb.translate (Offset (x, x * dydx + y));
782       mol.add_molecule (sb);
783     }
784   mol.translate_axis (x0 
785     - dynamic_cast<Spanner*> (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS);
786
787   return mol.create_scheme ();
788 }
789
790 int
791 Beam::forced_stem_count (Score_element*me) 
792 {
793   Link_array<Item>stems = 
794     Pointer_group_interface__extract_elements ( me, (Item*) 0, "stems");
795   int f = 0;
796   for (int i=0; i < stems.size (); i++)
797     {
798       Item *s = stems[i];
799
800       if (Stem::invisible_b (s))
801         continue;
802
803       if (((int)Stem::chord_start_f (s)) 
804         && (Stem::get_direction (s ) != Stem::get_default_dir (s )))
805         f++;
806     }
807   return f;
808 }
809
810
811
812
813 /* TODO:
814    use filter and standard list functions.
815  */
816 int
817 Beam::visible_stem_count (Score_element*me) 
818 {
819   Link_array<Item>stems = 
820     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
821   int c = 0;
822   for (int i = stems.size (); i--;)
823     {
824       if (!Stem::invisible_b (stems[i]))
825         c++;
826     }
827   return c;
828 }
829
830 Item*
831 Beam::first_visible_stem(Score_element*me) 
832 {
833   Link_array<Item>stems = 
834     Pointer_group_interface__extract_elements ( me, (Item*) 0, "stems");
835   
836   for (int i = 0; i < stems.size (); i++)
837     {
838       if (!Stem::invisible_b (stems[i]))
839         return stems[i];
840     }
841   return 0;
842 }
843
844 Item*
845 Beam::last_visible_stem(Score_element*me) 
846 {
847   Link_array<Item>stems = 
848     Pointer_group_interface__extract_elements ( me, (Item*) 0, "stems");
849   for (int i = stems.size (); i--;)
850     {
851       if (!Stem::invisible_b (stems[i]))
852         return stems[i];
853     }
854   return 0;
855 }
856
857
858 /*
859   [TODO]
860   handle rest under beam (do_post: beams are calculated now)
861   what about combination of collisions and rest under beam.
862
863   Should lookup
864     
865     rest -> stem -> beam -> interpolate_y_position ()
866 */
867 Real
868 Beam::rest_collision_callback (Score_element *rest, Axis a )
869 {
870   assert (a == Y_AXIS);
871
872   Score_element * st = unsmob_element (rest->get_elt_property ("stem"));
873   Score_element * stem = st;
874   if (!stem)
875     return 0.0;
876   Score_element * beam = unsmob_element (stem->get_elt_property ("beam"));
877   if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam))
878     return 0.0;
879
880   // make callback for rest from this.
881   Real beam_dy = 0;
882   Real beam_y = 0;
883
884
885   // todo: make sure this calced already.
886   SCM s = beam->get_elt_property ("height");
887   if (gh_number_p (s))
888     beam_dy = gh_scm2double (s);
889   
890   s = beam->get_elt_property ("y-position");
891   if (gh_number_p (s))
892     beam_y = gh_scm2double (s);
893   
894   Real x0 = first_visible_stem(beam)->relative_coordinate (0, X_AXIS);
895   Real dx = last_visible_stem(beam)->relative_coordinate (0, X_AXIS) - x0;
896   Real dydx = beam_dy && dx ? beam_dy/dx : 0;
897
898   Direction d = Stem::get_direction (stem);
899   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y;
900
901   Real staff_space =   Staff_symbol_referencer::staff_space (rest);
902   Real rest_dim = rest->extent (Y_AXIS)[d]*2.0 / staff_space ;
903
904   Real minimum_dist
905     = gh_scm2double (rest->get_elt_property ("minimum-beam-collision-distance"));
906   Real dist =
907     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
908
909   int stafflines = Staff_symbol_referencer::line_count (rest);
910
911   // move discretely by half spaces.
912   int discrete_dist = int (ceil (dist));
913
914   // move by whole spaces inside the staff.
915   if (discrete_dist < stafflines+1)
916     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
917
918   return  (-d *  discrete_dist);
919 }
920
921
922 bool
923 Beam::has_interface (Score_element*me)
924 {
925   return me->has_interface (ly_symbol2scm ("beam-interface"));
926 }
927
928 void
929 Beam::set_interface (Score_element*me)
930 {
931   Pointer_group_interface g (me, "stems");
932   g.set_interface ();
933
934   me->set_elt_property ("height", gh_int2scm (0)); // ugh.
935   me->set_elt_property ("y-position" ,gh_int2scm (0));
936   me->set_interface (ly_symbol2scm("beam-interface"));
937 }