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