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