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