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