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