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