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