]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
(calc_stem_info): Take multiplicity into account
[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--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8   
9 */
10
11 /*
12   [TODO]
13
14   * Fix TODO
15   
16   * Junk stem_info.
17   
18   * Remove #'direction from beam.  A beam has no direction per se.
19     It may only set directions for stems.
20
21   * Rewrite stem_beams.
22
23   * Use Number_pair i.s.o Interval to represent (yl, yr).
24   
25   */
26
27
28
29
30 #include <math.h> // tanh.
31
32 #include "molecule.hh" 
33 #include "directional-element-interface.hh"
34 #include "beaming.hh"
35 #include "beam.hh"
36 #include "misc.hh"
37 #include "least-squares.hh"
38 #include "stem.hh"
39 #include "paper-def.hh"
40 #include "lookup.hh"
41 #include "group-interface.hh"
42 #include "staff-symbol-referencer.hh"
43 #include "item.hh"
44 #include "spanner.hh"
45 #include "warn.hh"
46
47
48 #define DEBUG_QUANTING 0
49
50
51 #if DEBUG_QUANTING
52 #include "text-item.hh"  // debug output.
53 #include "font-interface.hh"  // debug output.
54 #endif
55
56
57 const int INTER_QUANT_PENALTY = 1000; 
58 const int SECONDARY_BEAM_DEMERIT  = 15;
59 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
60 const int STEM_LENGTH_LIMIT_PENALTY = 500;
61 const int DAMPING_DIRECTIION_PENALTY = 800;
62 const int MUSICAL_DIRECTION_FACTOR = 400;
63 const int IDEAL_SLOPE_FACTOR = 10;
64
65
66 static Real
67 shrink_extra_weight (Real x)
68 {
69   return fabs (x) * ((x < 0) ? 1.5 : 1.0);
70 }
71
72 void
73 Beam::add_stem (Grob *me, Grob *s)
74 {
75   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
76   
77   s->add_dependency (me);
78
79   assert (!Stem::beam_l (s));
80   s->set_grob_property ("beam", me->self_scm ());
81
82   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
83 }
84
85 Real
86 Beam::get_interbeam (Grob *me)
87 {
88   SCM func = me->get_grob_property ("space-function");
89   SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_multiplicity (me)));
90   return gh_scm2double (s);
91 }
92
93 /*
94   Maximum multiplicity.
95  */
96 int
97 Beam::get_multiplicity (Grob *me) 
98 {
99   int m = 0;
100   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
101     {
102       Grob *sc = unsmob_grob (ly_car (s));
103
104       if (Stem::has_interface (sc))
105         m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
106     }
107   return m;
108 }
109
110 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
111 SCM
112 Beam::space_function (SCM smob, SCM multiplicity)
113 {
114   Grob *me = unsmob_grob (smob);
115   
116   Real staff_space = Staff_symbol_referencer::staff_space (me);
117   Real line = me->paper_l ()->get_var ("linethickness");
118   Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
119     * staff_space;
120   
121   Real interbeam = gh_scm2int (multiplicity) < 4
122     ? (2*staff_space + line - thickness) / 2.0
123     : (3*staff_space + line - thickness) / 3.0;
124   
125   return gh_double2scm (interbeam);
126 }
127
128
129 /* After pre-processing all directions should be set.
130    Several post-processing routines (stem, slur, script) need stem/beam
131    direction.
132    Currenly, this means that beam has set all stem's directions.
133    [Alternatively, stems could set its own directions, according to
134    their beam, during 'final-pre-processing'.] */
135 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
136 SCM
137 Beam::before_line_breaking (SCM smob)
138 {
139   Grob *me =  unsmob_grob (smob);
140
141   /* Beams with less than 2 two stems don't make much sense, but could happen
142      when you do
143      
144      [r8 c8 r8].
145      
146     For a beam that  only has one stem, we try to do some disappearance magic:
147     we revert the flag, and move on to The Eternal Engraving Fields. */
148
149   int count = visible_stem_count (me);
150   if (count < 2)
151     {
152       me->warning (_ ("beam has less than two visible stems"));
153
154       SCM stems = me->get_grob_property ("stems");
155       if (scm_ilength (stems) == 1)
156         {
157           me->warning (_ ("Beam has less than two stems. Removing beam."));
158
159           unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
160           me->suicide ();
161
162           return SCM_UNSPECIFIED;
163         }
164       else if (scm_ilength (stems) == 0)
165         {
166           me->suicide ();
167           return SCM_UNSPECIFIED;         
168         }
169     }
170   if (count >= 1)
171     {
172       Direction d = get_default_dir (me);
173
174       consider_auto_knees (me, d);
175       set_stem_directions (me, d);
176       set_stem_shorten (me);
177     }
178
179   return SCM_EOL;
180 }
181
182 Direction
183 Beam::get_default_dir (Grob *me) 
184 {
185   Drul_array<int> total;
186   total[UP]  = total[DOWN] = 0;
187   Drul_array<int> count; 
188   count[UP]  = count[DOWN] = 0;
189   Direction d = DOWN;
190
191   Link_array<Item> stems=
192         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
193
194   for (int i=0; i <stems.size (); i++)
195     do {
196       Grob *s = stems[i];
197       Direction sd = Directional_element_interface::get (s);
198
199       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
200       int current = sd  ? (1 + d * sd)/2 : center_distance;
201
202       if (current)
203         {
204           total[d] += current;
205           count[d] ++;
206         }
207     } while (flip (&d) != DOWN);
208   
209   SCM func = me->get_grob_property ("dir-function");
210   SCM s = gh_call2 (func,
211                     gh_cons (gh_int2scm (count[UP]),
212                              gh_int2scm (count[DOWN])),
213                     gh_cons (gh_int2scm (total[UP]),
214                              gh_int2scm (total[DOWN])));
215
216   if (gh_number_p (s) && gh_scm2int (s))
217     return to_dir (s);
218   
219   /* If dir is not determined: get default */
220   return to_dir (me->get_grob_property ("neutral-direction"));
221 }
222
223
224 /* Set all stems with non-forced direction to beam direction.
225    Urg: non-forced should become `without/with unforced' direction,
226    once stem gets cleaned-up. */
227 void
228 Beam::set_stem_directions (Grob *me, Direction d)
229 {
230   Link_array<Item> stems
231     =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
232   
233   for (int i=0; i <stems.size (); i++)
234     {
235       Grob *s = stems[i];
236       SCM force = s->remove_grob_property ("dir-forced");
237       if (!gh_boolean_p (force) || !gh_scm2bool (force))
238         Directional_element_interface::set (s, d);
239     }
240
241
242 /* Simplistic auto-knees; only consider vertical gap between two
243    adjacent chords.
244
245   `Forced' stem directions are ignored.  If you don't want auto-knees,
246   don't set, or unset auto-knee-gap. */
247 void
248 Beam::consider_auto_knees (Grob *me, Direction d)
249 {
250   SCM scm = me->get_grob_property ("auto-knee-gap");
251
252   if (gh_number_p (scm))
253     {
254       bool knee_b = false;
255       Real knee_y = 0;
256       Real staff_space = Staff_symbol_referencer::staff_space (me);
257       Real gap = gh_scm2double (scm) / staff_space;
258
259
260       Link_array<Item> stems=
261         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
262       
263       Grob *common = me->common_refpoint (stems[0], Y_AXIS);
264       for (int i=1; i < stems.size (); i++)
265         if (!Stem::invisible_b (stems[i]))
266           common = common->common_refpoint (stems[i], Y_AXIS);
267
268       int l = 0;
269       for (int i=1; i < stems.size (); i++)
270         {
271           if (!Stem::invisible_b (stems[i-1]))
272             l = i - 1;
273           if (Stem::invisible_b (stems[l]))
274             continue;
275           if (Stem::invisible_b (stems[i]))
276             continue;
277           
278           Real left = Stem::extremal_heads (stems[l])[d]
279             ->relative_coordinate (common, Y_AXIS);
280           Real right = Stem::extremal_heads (stems[i])[-d]
281             ->relative_coordinate (common, Y_AXIS);
282
283           Real dy = right - left;
284
285           if (abs (dy) >= gap)
286             {
287               knee_y = (right + left) / 2;
288               knee_b = true;
289               break;
290             }
291         }
292       
293       if (knee_b)
294         {
295           for (int i=0; i < stems.size (); i++)
296             {
297               if (Stem::invisible_b (stems[i]))
298                 continue;
299               Item *s = stems[i];         
300               Real y = Stem::extremal_heads (stems[i])[d]
301                 ->relative_coordinate (common, Y_AXIS);
302
303               Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
304               s->set_grob_property ("dir-forced", SCM_BOOL_T);
305             }
306         }
307     }
308 }
309
310 /* Set stem's shorten property if unset.
311
312  TODO:
313    take some y-position (chord/beam/nearest?) into account
314    scmify forced-fraction
315
316    TODO:
317    
318    why is shorten stored in beam, and not directly in stem?
319
320 */
321 void
322 Beam::set_stem_shorten (Grob *m)
323 {
324   Spanner*me = dynamic_cast<Spanner*> (m);
325
326   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
327
328   int multiplicity = get_multiplicity (me);
329
330   SCM shorten = me->get_grob_property ("beamed-stem-shorten");
331   if (shorten == SCM_EOL)
332     return;
333
334   int sz = scm_ilength (shorten);
335   
336   Real staff_space = Staff_symbol_referencer::staff_space (me);
337   SCM shorten_elt = scm_list_ref (shorten,
338                                   gh_int2scm (multiplicity <? (sz - 1)));
339   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
340
341   /* your similar cute comment here */
342   shorten_f *= forced_fraction;
343
344   if (shorten_f)
345     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
346 }
347
348 /*  Call list of y-dy-callbacks, that handle setting of
349     grob-properties y, dy.
350     
351     User may set grob-properties: y-position-hs and height-hs
352  (to be fixed) that override the calculated y and dy.
353     
354     Because y and dy cannot be calculated and quanted separately, we
355     always calculate both, then check for user override. */
356 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
357 SCM
358 Beam::after_line_breaking (SCM smob)
359 {
360   Grob *me = unsmob_grob (smob);
361   
362   /* Copy to mutable list. */
363   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
364   me->set_grob_property ("positions", s);
365
366   if (ly_car (s) != SCM_BOOL_F)
367     return SCM_UNSPECIFIED;
368
369   // one wonders if such genericity is necessary  --hwn.
370   SCM callbacks = me->get_grob_property ("position-callbacks");
371   for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
372     gh_call1 (ly_car (i), smob);
373
374   set_stem_lengths (me);  
375   return SCM_UNSPECIFIED;
376 }
377
378 struct Quant_score
379 {
380   Real yl;
381   Real yr;
382   Real demerits;
383 };
384
385
386 /*
387   TODO:
388   
389    - Make all demerits customisable
390
391    - One sensible check per demerit (what's this --hwn)
392
393    - Add demerits for quants per se, as to forbid a specific quant
394      entirely
395
396 */
397 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
398 SCM
399 Beam::quanting (SCM smob)
400 {
401   Grob *me = unsmob_grob (smob);
402
403   SCM s = me->get_grob_property ("positions");
404   Real yl = gh_scm2double (gh_car (s));
405   Real yr = gh_scm2double (gh_cdr (s));
406
407   Real ss = Staff_symbol_referencer::staff_space (me);
408   Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
409   Real slt = me->paper_l ()->get_var ("linethickness") / ss;
410
411
412   SCM sdy = me->get_grob_property ("least-squares-dy");
413   Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
414   
415   Real straddle = 0.0;
416   Real sit = (thickness - slt) / 2;
417   Real inter = 0.5;
418   Real hang = 1.0 - (thickness - slt) / 2;
419   Real quants [] = {straddle, sit, inter, hang };
420   
421   int num_quants = int (sizeof (quants)/sizeof (Real));
422   Array<Real> quantsl;
423   Array<Real> quantsr;
424
425   /*
426     going to REGION_SIZE == 2, yields another 0.6 second with
427     wtk1-fugue2.
428
429
430     (result indexes between 70 and 575)  ? --hwn. 
431
432   */
433
434
435   
436   /*
437     Do stem computations.  These depend on YL and YR linearly, so we can
438     precompute for every stem 2 factors.
439    */
440   Link_array<Grob> stems=
441     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
442   Array<Stem_info> stem_infos;
443   Array<Real> lbase_lengths;
444   Array<Real> rbase_lengths;  
445
446   Drul_array<bool> dirs_found(0,0);
447   for (int i= 0; i < stems.size(); i++)
448     {
449       Grob*s = stems[i];
450       stem_infos.push( Stem::calc_stem_info (s));
451
452       Real b = calc_stem_y (me, s, Interval (1,0));
453       lbase_lengths.push (b);
454
455       b = calc_stem_y (me, s, Interval (0,1));
456       rbase_lengths.push (b);
457
458       dirs_found [stem_infos.top().dir_] = true;
459     }
460
461   Direction ldir = Direction (stem_infos[0].dir_);
462   Direction rdir = Direction (stem_infos.top ().dir_);
463   bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
464
465   
466   int REGION_SIZE = 2;
467
468   /*
469     Knees are harder, lets try some more possibilities for knees. 
470    */
471   if (knee_b)
472     REGION_SIZE += 2 ;
473   
474   for (int i = -REGION_SIZE ; i < REGION_SIZE; i++)
475     for (int j = 0; j < num_quants; j++)
476       {
477         quantsl.push (i + quants[j] + int (yl));
478         quantsr.push (i + quants[j] + int (yr));
479       }
480
481   Array<Quant_score> qscores;
482   
483   for (int l =0; l < quantsl.size (); l++)  
484     for (int r =0; r < quantsr.size (); r++)
485       {
486         Quant_score qs;
487         qs.yl = quantsl[l];
488         qs.yr = quantsr[r];
489         qs.demerits = 0.0;
490         
491         qscores.push (qs);
492       }
493
494
495   /*
496     This is a longish function, but we don't separate this out into
497     neat modular separate subfunctions, as the subfunctions would be
498     called for many values of YL, YR. By precomputing various
499     parameters outside of the loop, we can save a lot of time.
500
501   */
502   for (int i = qscores.size (); i--;)
503     if (qscores[i].demerits < 100)
504       {
505         qscores[i].demerits
506           += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
507                               dy_mus, yr- yl); 
508       }
509
510   Real rad = Staff_symbol_referencer::staff_radius (me);
511   int multiplicity = get_multiplicity (me);
512   Real interbeam = multiplicity < 4
513     ? (2*ss + slt - thickness) / 2.0
514      : (3*ss + slt - thickness) / 3.0;
515
516   for (int i = qscores.size (); i--;)
517     if (qscores[i].demerits < 100)
518       {
519         qscores[i].demerits
520           += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
521                                      rad, slt, thickness, interbeam,
522                                      multiplicity, ldir, rdir); 
523       }
524
525
526   for (int i = qscores.size (); i--;)
527     if (qscores[i].demerits < 100)
528       {
529         qscores[i].demerits
530           += score_stem_lengths (stems, stem_infos,
531                                  lbase_lengths, rbase_lengths,
532                                  knee_b,
533                                  me, qscores[i].yl, qscores[i].yr);
534       }
535
536
537   Real best = 1e6;
538   int best_idx = -1;
539   for (int i = qscores.size (); i--;)
540     {
541       if (qscores[i].demerits < best)
542         {
543           best = qscores [i].demerits ;
544           best_idx = i;
545         }
546     }
547
548   
549   me->set_grob_property ("positions",
550                          gh_cons (gh_double2scm (qscores[best_idx].yl),
551                                   gh_double2scm (qscores[best_idx].yr))
552                          );
553
554 #if DEBUG_QUANTING
555
556   // debug quanting
557   me->set_grob_property ("quant-score",
558                          gh_double2scm (qscores[best_idx].demerits));
559   me->set_grob_property ("best-idx", gh_int2scm (best_idx));
560 #endif
561
562   return SCM_UNSPECIFIED;
563 }
564
565 Real
566 Beam::score_stem_lengths (Link_array<Grob>stems,
567                           Array<Stem_info> stem_infos,
568                           Array<Real> left_factor,
569                           Array<Real> right_factor,
570                           bool knee, 
571                           Grob*me,
572                           Real yl, Real yr)
573 {
574   Real demerit_score = 0.0 ;
575   Real pen = STEM_LENGTH_LIMIT_PENALTY;
576   if (knee)
577     pen = sqrt(pen);
578
579   for (int i=0; i < stems.size (); i++)
580     {
581       Grob* s = stems[i];
582       if (Stem::invisible_b (s))
583         continue;
584
585       Real current_y =
586         yl * left_factor[i] + right_factor[i]* yr;
587
588       Stem_info info = stem_infos[i];
589       Direction d = info.dir_;
590
591       demerit_score += pen
592         * ( 0 >? (info.dir_ * (info.shortest_y_ - current_y)));
593       
594       demerit_score += STEM_LENGTH_DEMERIT_FACTOR
595         * shrink_extra_weight (d * current_y  - info.dir_ * info.ideal_y_);
596     }
597
598   demerit_score *= 2.0 / stems.size (); 
599
600   return demerit_score;
601 }
602
603 Real
604 Beam::score_slopes_dy (Grob *me,
605                        Real yl, Real yr,
606                        Real dy_mus, Real dy_damp)
607 {
608   Real dy = yr - yl;
609
610   Real dem = 0.0;
611   if (sign (dy_damp) != sign (dy))
612     {
613       dem += DAMPING_DIRECTIION_PENALTY;
614     }
615
616    dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
617    dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* IDEAL_SLOPE_FACTOR;
618
619    return dem;
620 }
621
622 static Real
623 my_modf (Real x)
624 {
625   return x - floor (x);
626 }
627
628 Real
629 Beam::score_forbidden_quants (Grob*me,
630                               Real yl, Real yr,
631                               Real rad,
632                               Real slt,
633                               Real thickness, Real interbeam,
634                               int multiplicity,
635                               Direction ldir, Direction rdir)
636 {
637   Real dy = yr - yl;
638
639   Real dem = 0.0;
640   if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
641     dem += INTER_QUANT_PENALTY;
642   if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
643     dem += INTER_QUANT_PENALTY;
644
645   // todo: use multiplicity of outer stems.
646   if (multiplicity >= 2)
647     {
648      
649       Real straddle = 0.0;
650       Real sit = (thickness - slt) / 2;
651       Real inter = 0.5;
652       Real hang = 1.0 - (thickness - slt) / 2;
653       
654
655       if (fabs (yl - ldir * interbeam) < rad
656           && fabs (my_modf (yl) - inter) < 1e-3)
657         dem += SECONDARY_BEAM_DEMERIT;
658       if (fabs (yr - rdir * interbeam) < rad
659           && fabs (my_modf (yr) - inter) < 1e-3)
660         dem += SECONDARY_BEAM_DEMERIT;
661
662       Real eps = 1e-3;
663
664       /*
665         Can't we simply compute the distance between the nearest
666         staffline and the secondary beam? That would get rid of the
667         silly case analysis here (which is probably not when we have
668         different beam-thicknesses.)
669
670         --hwn
671        */
672
673
674       // hmm, without Interval/Drul_array, you get ~ 4x same code...
675       if (fabs (yl - ldir * interbeam) < rad + inter)
676         {
677           if (ldir == UP && dy <= eps
678               && fabs (my_modf (yl) - sit) < eps)
679             dem += SECONDARY_BEAM_DEMERIT;
680           
681           if (ldir == DOWN && dy >= eps
682               && fabs (my_modf (yl) - hang) < eps)
683             dem += SECONDARY_BEAM_DEMERIT;
684         }
685
686       if (fabs (yr - rdir * interbeam) < rad + inter)
687         {
688           if (rdir == UP && dy >= eps
689               && fabs (my_modf (yr) - sit) < eps)
690             dem += SECONDARY_BEAM_DEMERIT;
691           
692           if (rdir == DOWN && dy <= eps
693               && fabs (my_modf (yr) - hang) < eps)
694             dem += SECONDARY_BEAM_DEMERIT;
695         }
696       
697       if (multiplicity >= 3)
698         {
699           if (fabs (yl - 2 * ldir * interbeam) < rad + inter)
700             {
701               if (ldir == UP && dy <= eps
702                   && fabs (my_modf (yl) - straddle) < eps)
703                 dem += SECONDARY_BEAM_DEMERIT;
704               
705               if (ldir == DOWN && dy >= eps
706                   && fabs (my_modf (yl) - straddle) < eps)
707                 dem += SECONDARY_BEAM_DEMERIT;
708         }
709           
710           if (fabs (yr - 2 * rdir * interbeam) < rad + inter)
711             {
712               if (rdir == UP && dy >= eps
713                   && fabs (my_modf (yr) - straddle) < eps)
714                 dem += SECONDARY_BEAM_DEMERIT;
715               
716               if (rdir == DOWN && dy <= eps
717                   && fabs (my_modf (yr) - straddle) < eps)
718                 dem += SECONDARY_BEAM_DEMERIT;
719             }
720         }
721     }
722   
723   return dem;
724 }
725
726   
727
728 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
729 SCM
730 Beam::least_squares (SCM smob)
731 {
732   Grob *me = unsmob_grob (smob);
733
734   int count = visible_stem_count (me);
735   Interval pos (0, 0);
736   
737   if (count <= 1)
738     {
739       me->set_grob_property ("positions", ly_interval2scm (pos));
740       return SCM_UNSPECIFIED;
741     }
742
743   Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y_,
744                   Stem::calc_stem_info (last_visible_stem (me)).ideal_y_);
745   
746   if (!ideal.delta ())
747     {
748       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
749                       Stem::chord_start_y (last_visible_stem (me)));
750
751
752       /*
753         TODO  : use scoring for this.
754
755         complicated, because we take stem-info.ideal for determining
756         beam slopes.
757         
758        */
759       /* Make simple beam on middle line have small tilt */
760       if (!ideal[LEFT] && chord.delta () && count == 2)
761         {
762
763           /*
764             FIXME. -> UP
765           */
766           Direction d = (Direction) (sign (chord.delta ()) * UP);
767           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
768           //                * dir;
769           pos[-d] = - pos[d];
770         }
771       else
772         {
773           pos = ideal;
774         }
775     }
776   else
777     {
778       Array<Offset> ideals;
779
780       // ugh -> use commonx
781       Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
782       Link_array<Item> stems=
783         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
784
785       for (int i=0; i < stems.size (); i++)
786         {
787           Item* s = stems[i];
788           if (Stem::invisible_b (s))
789             continue;
790           ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
791                                Stem::calc_stem_info (s).ideal_y_));
792         }
793       Real y; 
794       Real dydx;
795       minimise_least_squares (&dydx, &y, ideals);
796
797       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
798       Real dy = dydx * dx;
799       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
800
801       pos = Interval (y, (y+dy));
802     }
803
804   me->set_grob_property ("positions", ly_interval2scm (pos));
805   return SCM_UNSPECIFIED;
806 }
807
808 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
809 SCM
810 Beam::check_concave (SCM smob)
811 {
812   Grob *me = unsmob_grob (smob);
813
814   Link_array<Item> stems = 
815     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
816
817   for (int i = 0; i < stems.size ();)
818     {
819       if (Stem::invisible_b (stems[i]))
820         stems.del (i);
821       else
822         i++;
823     }
824   
825   if (stems.size () < 3)
826     return SCM_UNSPECIFIED;
827
828
829   /* Concaveness #1: If distance of an inner notehead to line between
830      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
831      beam is concave (Heinz Stolba).
832
833      In the case of knees, the line connecting outer heads is often
834      not related to the beam slope (it may even go in the other
835      direction). Skip the check when the outer stems point in
836      different directions. --hwn
837      
838   */
839   bool concaveness1 = false;
840   SCM gap = me->get_grob_property ("concaveness-gap");
841   if (gh_number_p (gap)
842       && Stem::get_direction(stems.top ())
843          == Stem::get_direction(stems[0]))
844     {
845       Real r1 = gh_scm2double (gap);
846       Real dy = Stem::chord_start_y (stems.top ())
847         - Stem::chord_start_y (stems[0]);
848
849       
850       Real slope = dy / (stems.size () - 1);
851       
852       Real y0 = Stem::chord_start_y (stems[0]);
853       for (int i = 1; i < stems.size () - 1; i++)
854         {
855           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
856           if (c > r1)
857             {
858               concaveness1 = true;
859               break;
860             }
861         }
862     }
863
864     
865   /* Concaveness #2: Sum distances of inner noteheads that fall
866      outside the interval of the two outer noteheads.
867
868      We only do this for beams where first and last stem have the same
869      direction. --hwn.
870
871
872      Note that "convex" stems compensate for "concave" stems.
873      (is that intentional?) --hwn.
874   */
875   
876   Real concaveness2 = 0;
877   SCM thresh = me->get_grob_property ("concaveness-threshold");
878   Real r2 = infinity_f;
879   if (!concaveness1 && gh_number_p (thresh)
880       && Stem::get_direction(stems.top ())
881          == Stem::get_direction(stems[0]))
882     {
883       r2 = gh_scm2double (thresh);
884
885       Direction dir = Stem::get_direction(stems.top ());
886       Real concave = 0;
887       Interval iv (Stem::chord_start_y (stems[0]),
888                    Stem::chord_start_y (stems.top ()));
889       
890       if (iv[MAX] < iv[MIN])
891         iv.swap ();
892       
893       for (int i = 1; i < stems.size () - 1; i++)
894         {
895           Real f = Stem::chord_start_y (stems[i]);
896           concave += ((f - iv[MAX] ) >? 0) +
897             ((f - iv[MIN] ) <? 0);
898         }
899       concave *= dir;
900       concaveness2 = concave / (stems.size () - 2);
901       
902       /* ugh: this is the a kludge to get
903          input/regression/beam-concave.ly to behave as
904          baerenreiter. */
905
906       /*
907         huh? we're dividing twice (which is not scalable) meaning that
908         the longer the beam, the more unlikely it will be
909         concave. Maybe you would even expect the other way around??
910
911         --hwn.
912         
913        */
914       concaveness2 /= (stems.size () - 2);
915     }
916   
917   /* TODO: some sort of damping iso -> plain horizontal */
918   if (concaveness1 || concaveness2 > r2)
919     {
920       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
921       Real r = pos.linear_combination (0);
922       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
923       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
924     }
925
926   return SCM_UNSPECIFIED;
927 }
928
929 /* This neat trick is by Werner Lemberg,
930    damped = tanh (slope)
931    corresponds with some tables in [Wanske] CHECKME */
932 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
933 SCM
934 Beam::slope_damping (SCM smob)
935 {
936   Grob *me = unsmob_grob (smob);
937
938   if (visible_stem_count (me) <= 1)
939     return SCM_UNSPECIFIED;
940
941   SCM s = me->get_grob_property ("damping"); 
942   int damping = gh_scm2int (s);
943
944   if (damping)
945     {
946       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
947       Real dy = pos.delta ();
948       
949       // ugh -> use commonx
950       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
951         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
952       Real dydx = dy && dx ? dy/dx : 0;
953       dydx = 0.6 * tanh (dydx) / damping;
954
955       Real damped_dy = dydx * dx;
956       pos[LEFT] += (dy - damped_dy) / 2;
957       pos[RIGHT] -= (dy - damped_dy) / 2;
958       
959       me->set_grob_property ("positions", ly_interval2scm (pos));
960     }
961   return SCM_UNSPECIFIED;
962 }
963
964 /*
965   Calculate the Y position of the stem-end, given the Y-left, Y-right
966   in POS, and for stem S.
967  */
968 Real
969 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos)
970 {
971   int beam_multiplicity = get_multiplicity (me);
972   int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
973
974   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
975   Real interbeam = get_interbeam (me);
976
977   // ugh -> use commonx
978   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
979   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
980   Real dy = pos.delta ();
981   Real stem_y = (dy && dx
982                  ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
983                  * dy
984                  : 0) + pos[LEFT];
985
986
987   Direction first_dir = Directional_element_interface::get (first_visible_stem (me));
988   Direction my_dir = Directional_element_interface::get (s);
989
990   if (my_dir != first_dir)
991     {
992       /*
993         WTF is happening here ?
994
995          It looks as if this is some kind of fixup for multiple kneed
996          beams to get a piece of stem at the #.
997          
998
999                 x
1000                 |
1001          =======|
1002          |======#
1003          |
1004          |
1005         x 
1006
1007         Rules for this kind of stuff are hairy. In any event, the
1008         current stem should look at the multiplicity of its
1009         predecessor.
1010
1011         --hwn.
1012         
1013        */
1014       stem_y += my_dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
1015
1016       // huh, why not for first visible?
1017
1018       /*
1019         What the heck is happening here?? 
1020        */
1021       Grob *last_visible = last_visible_stem (me);
1022       if (last_visible)
1023         {
1024           if ( Staff_symbol_referencer::staff_symbol_l (s)
1025                != Staff_symbol_referencer::staff_symbol_l (last_visible))
1026             stem_y += Directional_element_interface::get (me)
1027               * (beam_multiplicity - stem_multiplicity) * interbeam;
1028         }
1029       else
1030         programming_error ("No last visible stem");
1031     }
1032
1033   return stem_y;
1034 }
1035
1036 /*
1037   Hmm.  At this time, beam position and slope are determined.  Maybe,
1038   stem directions and length should set to relative to the chord's
1039   position of the beam.  */
1040 void
1041 Beam::set_stem_lengths (Grob *me)
1042 {
1043   Link_array<Item> stems=
1044     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1045
1046   if (stems.size () <= 1)
1047     return;
1048   
1049   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1050   for (int i=1; i < stems.size (); i++)
1051     if (!Stem::invisible_b (stems[i]))
1052       common = common->common_refpoint (stems[i], Y_AXIS);
1053
1054   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1055   Real staff_space = Staff_symbol_referencer::staff_space (me);
1056
1057   /*
1058     DOCUMENT THIS.
1059    */
1060 #if 0
1061   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1062   Direction dir = Directional_element_interface::get (me);
1063   bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1064 #endif
1065   
1066   for (int i=0; i < stems.size (); i++)
1067     {
1068       Item* s = stems[i];
1069       if (Stem::invisible_b (s))
1070         continue;
1071
1072       Real stem_y = calc_stem_y (me, s, pos);
1073
1074 #if 0
1075       // doesn't play well with dvips
1076       if (ps_testing)
1077         if (Stem::get_direction (s) == dir)
1078           stem_y += Stem::get_direction (s) * thick / 2;
1079 #endif
1080       
1081       /* caution: stem measures in staff-positions */
1082       Real id = me->relative_coordinate (common, Y_AXIS)
1083         - stems[i]->relative_coordinate (common, Y_AXIS);
1084       Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1085     }
1086 }
1087
1088 void
1089 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1090 {
1091   Link_array<Grob> stems=
1092     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1093   
1094   Direction d = LEFT;
1095   for (int i=0; i  < stems.size (); i++)
1096     {
1097       do
1098         {
1099           /* Don't overwrite user override (?) */
1100           if (Stem::beam_count (stems[i], d) == -1
1101               /* Don't set beaming for outside of outer stems */
1102               && ! (d == LEFT && i == 0)
1103               && ! (d == RIGHT && i == stems.size () -1))
1104             {
1105               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1106               Stem::set_beaming (stems[i], b, d);
1107             }
1108         }
1109       while (flip (&d) != LEFT);
1110     }
1111 }
1112
1113
1114
1115 /*
1116   beams to go with one stem.
1117
1118   FIXME: clean me up:
1119
1120   The beam should be constructed by one function that knows where the
1121   X and Y points are, and only inspects the stems to obtain
1122   multiplicity and stem directions.
1123   
1124   */
1125 Molecule
1126 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1127 {
1128   // ugh -> use commonx
1129   if ((next
1130        && ! (next->relative_coordinate (0, X_AXIS)
1131             > here->relative_coordinate (0, X_AXIS)))
1132       || (prev
1133           && ! (prev->relative_coordinate (0, X_AXIS)
1134                < here->relative_coordinate (0, X_AXIS))))
1135     programming_error ("Beams are not left-to-right");
1136
1137   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1138   Real bdy = get_interbeam (me);
1139   
1140   Molecule leftbeams;
1141   Molecule rightbeams;
1142
1143   Real nw_f;
1144   if (!Stem::first_head (here))
1145     nw_f = 0;
1146   else
1147     {
1148       int t = Stem::duration_log (here); 
1149
1150       SCM proc = me->get_grob_property ("flag-width-function");
1151       SCM result = gh_call1 (proc, gh_int2scm (t));
1152       nw_f = gh_scm2double (result);
1153     }
1154
1155
1156   /* [Tremolo] beams on whole notes may not have direction set? */
1157   Direction dir = Directional_element_interface::get (here);
1158
1159   /* half beams extending to the left. */
1160   if (prev)
1161     {
1162       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1163         - Stem::beam_count (prev, RIGHT);
1164       int lwholebeams= Stem::beam_count (here, LEFT)
1165         <? Stem::beam_count (prev, RIGHT);
1166       
1167       /* Half beam should be one note-width,
1168          but let's make sure two half-beams never touch */
1169
1170       // FIXME: TODO (check) stem width / sloped beams
1171       Real w = here->relative_coordinate (0, X_AXIS)
1172         - prev->relative_coordinate (0, X_AXIS);
1173       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1174         // URG
1175         * me->paper_l ()->get_var ("linethickness");
1176
1177       w = w/2 <? nw_f;
1178       Molecule a;
1179       if (lhalfs)               // generates warnings if not
1180         a =  Lookup::beam (dydx, w + stem_w, thick);
1181       a.translate (Offset (-w, -w * dydx));
1182       a.translate_axis (-stem_w/2, X_AXIS);
1183       for (int j = 0; j  < lhalfs; j++)
1184         {
1185           Molecule b (a);
1186           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1187           leftbeams.add_molecule (b);
1188         }
1189     }
1190
1191   if (next)
1192     {
1193       int rhalfs  = Stem::beam_count (here, RIGHT)
1194         - Stem::beam_count (next, LEFT);
1195       int rwholebeams= Stem::beam_count (here, RIGHT)
1196         <? Stem::beam_count (next, LEFT);
1197
1198       Real w = next->relative_coordinate (0, X_AXIS)
1199         - here->relative_coordinate (0, X_AXIS);
1200
1201       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1202         // URG
1203         * me->paper_l ()->get_var ("linethickness");
1204
1205       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1206       a.translate_axis (- stem_w/2, X_AXIS);
1207       int j = 0;
1208       Real gap_f = 0;
1209       
1210       SCM gap = me->get_grob_property ("gap");
1211       if (gh_number_p (gap))
1212         {
1213           int gap_i = gh_scm2int ((gap));
1214           int nogap = rwholebeams - gap_i;
1215           
1216           for (; j  < nogap; j++)
1217             {
1218               Molecule b (a);
1219               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1220               rightbeams.add_molecule (b);
1221             }
1222           if (Stem::invisible_b (here))
1223             gap_f = nw_f;
1224           else
1225             gap_f = nw_f / 2;
1226           w -= 2 * gap_f;
1227           a = Lookup::beam (dydx, w + stem_w, thick);
1228         }
1229
1230       for (; j  < rwholebeams; j++)
1231         {
1232           Molecule b (a);
1233           Real tx = 0;
1234           if (Stem::invisible_b (here))
1235             // ugh, see chord-tremolo.ly
1236             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1237           else
1238             tx = gap_f;
1239           b.translate (Offset (tx, -dir * bdy * j));
1240           rightbeams.add_molecule (b);
1241         }
1242
1243       w = w/2 <? nw_f;
1244       if (rhalfs)
1245         a = Lookup::beam (dydx, w, thick);
1246
1247       for (; j  < rwholebeams + rhalfs; j++)
1248         {
1249           Molecule b (a);
1250           b.translate_axis (- dir * bdy * j, Y_AXIS);
1251           rightbeams.add_molecule (b);
1252         }
1253
1254     }
1255   leftbeams.add_molecule (rightbeams);
1256
1257   return leftbeams;
1258 }
1259
1260
1261 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1262 SCM
1263 Beam::brew_molecule (SCM smob)
1264 {
1265   Grob *me =unsmob_grob (smob);
1266
1267   Molecule mol;
1268   if (!gh_pair_p (me->get_grob_property ("stems")))
1269     return SCM_EOL;
1270   Real x0, dx;
1271   Link_array<Item>stems = 
1272     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1273   if (visible_stem_count (me))
1274     {
1275       // ugh -> use commonx
1276       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1277       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1278     }
1279   else
1280     {
1281       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1282       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1283     }
1284
1285   SCM posns = me->get_grob_property ("positions");
1286   Interval pos;
1287   if (!ly_number_pair_p (posns))
1288     {
1289       programming_error ("No beam posns");
1290       pos = Interval (0,0);
1291     }
1292   else
1293     pos= ly_scm2interval (posns);
1294   Real dy = pos.delta ();
1295   Real dydx = dy && dx ? dy/dx : 0;
1296
1297
1298   Direction firstdir = Directional_element_interface::get ( Beam::first_visible_stem (me) );
1299   
1300   for (int i=0; i < stems.size (); i++)
1301     {
1302       Item *item = stems[i];
1303       Item *prev = (i > 0)? stems[i-1] : 0;
1304       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1305
1306
1307       
1308       Molecule sb = stem_beams (me, item, next, prev, dydx);
1309       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1310       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1311
1312       Direction sd = Stem::get_direction (item);      
1313       mol.add_molecule (sb);
1314     }
1315   
1316   mol.translate_axis (x0 
1317                       - dynamic_cast<Spanner*> (me)
1318                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1319                       X_AXIS);
1320
1321 #if (DEBUG_QUANTING)
1322     {
1323       /*
1324         This code prints the demerits for each beam. Perhaps this
1325         should be switchable for those who want to twiddle with the
1326         parameters.
1327       */
1328       String str;
1329       if (1)
1330         {
1331           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1332           str += ":";
1333         }
1334       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1335                      "%.2f");
1336
1337       SCM properties = Font_interface::font_alist_chain (me);
1338
1339       
1340       Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1341       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1342     }
1343 #endif
1344     
1345   return mol.smobbed_copy ();
1346 }
1347
1348 int
1349 Beam::forced_stem_count (Grob *me) 
1350 {
1351   Link_array<Item>stems = 
1352     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1353   int f = 0;
1354   for (int i=0; i < stems.size (); i++)
1355     {
1356       Item *s = stems[i];
1357
1358       if (Stem::invisible_b (s))
1359         continue;
1360
1361       if (((int)Stem::chord_start_y (s)) 
1362         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1363         f++;
1364     }
1365   return f;
1366 }
1367
1368
1369
1370
1371 int
1372 Beam::visible_stem_count (Grob *me) 
1373 {
1374   Link_array<Item>stems = 
1375     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1376   int c = 0;
1377   for (int i = stems.size (); i--;)
1378     {
1379       if (!Stem::invisible_b (stems[i]))
1380         c++;
1381     }
1382   return c;
1383 }
1384
1385 Item*
1386 Beam::first_visible_stem (Grob *me) 
1387 {
1388   Link_array<Item>stems = 
1389     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1390   
1391   for (int i = 0; i < stems.size (); i++)
1392     {
1393       if (!Stem::invisible_b (stems[i]))
1394         return stems[i];
1395     }
1396   return 0;
1397 }
1398
1399 Item*
1400 Beam::last_visible_stem (Grob *me) 
1401 {
1402   Link_array<Item>stems = 
1403     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1404   for (int i = stems.size (); i--;)
1405     {
1406       if (!Stem::invisible_b (stems[i]))
1407         return stems[i];
1408     }
1409   return 0;
1410 }
1411
1412
1413 /*
1414   [TODO]
1415   
1416   handle rest under beam (do_post: beams are calculated now)
1417   what about combination of collisions and rest under beam.
1418
1419   Should lookup
1420     
1421     rest -> stem -> beam -> interpolate_y_position ()
1422 */
1423 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1424 SCM
1425 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1426 {
1427   Grob *rest = unsmob_grob (element_smob);
1428   Axis a = (Axis) gh_scm2int (axis);
1429   
1430   assert (a == Y_AXIS);
1431
1432   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1433   Grob *stem = st;
1434   if (!stem)
1435     return gh_double2scm (0.0);
1436   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1437   if (!beam
1438       || !Beam::has_interface (beam)
1439       || !Beam::visible_stem_count (beam))
1440     return gh_double2scm (0.0);
1441
1442   // make callback for rest from this.
1443   // todo: make sure this calced already.
1444
1445   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1446   Interval pos (0, 0);
1447   SCM s = beam->get_grob_property ("positions");
1448   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1449     pos = ly_scm2interval (s);
1450
1451   Real dy = pos.delta ();
1452   // ugh -> use commonx
1453   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1454   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1455   Real dydx = dy && dx ? dy/dx : 0;
1456   
1457   Direction d = Stem::get_direction (stem);
1458   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1459
1460   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1461
1462   
1463   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1464
1465   Real minimum_dist
1466     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1467   Real dist =
1468     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1469
1470   int stafflines = Staff_symbol_referencer::line_count (rest);
1471
1472   // move discretely by half spaces.
1473   int discrete_dist = int (ceil (dist));
1474
1475   // move by whole spaces inside the staff.
1476   if (discrete_dist < stafflines+1)
1477     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1478
1479   return gh_double2scm (-d *  discrete_dist);
1480 }
1481
1482
1483
1484
1485 ADD_INTERFACE (Beam, "beam-interface",
1486   "A beam.
1487
1488 #'thickness= weight of beams, in staffspace
1489
1490
1491 We take the least squares line through the ideal-length stems, and
1492 then damp that using
1493
1494         damped = tanh (slope)
1495
1496 this gives an unquantized left and right position for the beam end.
1497 Then we take all combinations of quantings near these left and right
1498 positions, and give them a score (according to how close they are to
1499 the ideal slope, how close the result is to the ideal stems, etc.). We
1500 take the best scoring combination.
1501
1502 ",
1503   "position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");
1504
1505