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