]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
tremolo spees
[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
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
804      In the case of knees, the line connecting outer heads is often
805      not related to the beam slope (it may even go in the other
806      direction). Skip the check when the outer stems point in
807      different directions. --hwn
808      
809   */
810   bool concaveness1 = false;
811   SCM gap = me->get_grob_property ("concaveness-gap");
812   if (gh_number_p (gap)
813       && Stem::get_direction(stems.top ())
814          == Stem::get_direction(stems[0]))
815     {
816       Real r1 = gh_scm2double (gap);
817       Real dy = Stem::chord_start_f (stems.top ())
818         - Stem::chord_start_f (stems[0]);
819
820       
821       Real slope = dy / (stems.size () - 1);
822       
823       Real y0 = Stem::chord_start_f (stems[0]);
824       for (int i = 1; i < stems.size () - 1; i++)
825         {
826           Real c = (Stem::chord_start_f (stems[i]) - y0) - i * slope;
827           if (c > r1)
828             {
829               concaveness1 = true;
830               break;
831             }
832         }
833     }
834
835     
836   /* Concaveness #2: Sum distances of inner noteheads that fall
837      outside the interval of the two outer noteheads */
838   Real concaveness2 = 0;
839   SCM thresh = me->get_grob_property ("concaveness-threshold");
840   Real r2 = infinity_f;
841   if (!concaveness1 && gh_number_p (thresh))
842     {
843       r2 = gh_scm2double (thresh);
844       
845       Direction dir = Directional_element_interface::get (me);  
846
847       Real concave = 0;
848       Interval iv (Stem::chord_start_f (stems[0]),
849                    Stem::chord_start_f (stems.top ()));
850       
851       if (iv[MAX] < iv[MIN])
852         iv.swap ();
853       
854       for (int i = 1; i < stems.size () - 1; i++)
855         {
856           Real c = 0;
857           Real f = Stem::chord_start_f (stems[i]);
858           if ((c = f - iv[MAX]) > 0)
859             concave += c;
860           else if ((c = f - iv[MIN]) < 0)
861             concave += c;
862         }
863       /*
864         Ugh. This will mess up with knees. Direction should be
865         determined per stem.
866        */
867       concave *= dir;
868
869       concaveness2 = concave / (stems.size () - 2);
870       /* ugh: this is the a kludge to get input/regression/beam-concave.ly
871          to behave as baerenreiter. */
872       concaveness2 /= (stems.size () - 2);
873     }
874   
875   /* TODO: some sort of damping iso -> plain horizontal */
876   if (concaveness1 || concaveness2 > r2)
877     {
878       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
879       Real r = pos.linear_combination (0);
880       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
881       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
882     }
883
884   return SCM_UNSPECIFIED;
885 }
886
887 /* This neat trick is by Werner Lemberg,
888    damped = tanh (slope)
889    corresponds with some tables in [Wanske] CHECKME */
890 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
891 SCM
892 Beam::slope_damping (SCM smob)
893 {
894   Grob *me = unsmob_grob (smob);
895
896   if (visible_stem_count (me) <= 1)
897     return SCM_UNSPECIFIED;
898
899   SCM s = me->get_grob_property ("damping"); 
900   int damping = gh_scm2int (s);
901
902   if (damping)
903     {
904       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
905       Real dy = pos.delta ();
906       
907       // ugh -> use commonx
908       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
909         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
910       Real dydx = dy && dx ? dy/dx : 0;
911       dydx = 0.6 * tanh (dydx) / damping;
912
913       Real damped_dy = dydx * dx;
914       pos[LEFT] += (dy - damped_dy) / 2;
915       pos[RIGHT] -= (dy - damped_dy) / 2;
916       
917       me->set_grob_property ("positions", ly_interval2scm (pos));
918     }
919   return SCM_UNSPECIFIED;
920 }
921
922 /*
923   Calculate the Y position of the stem-end, given the Y-left, Y-right
924   in POS, and for stem S.
925  */
926 Real
927 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos)
928 {
929   int beam_multiplicity = get_multiplicity (me);
930   int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
931
932   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
933   Real interbeam = get_interbeam (me);
934
935   // ugh -> use commonx
936   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
937   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
938   Real dy = pos.delta ();
939   Real stem_y = (dy && dx
940                  ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
941                  * dy
942                  : 0) + pos[LEFT];
943
944   /* knee */
945   Direction dir  = Directional_element_interface::get (me);
946   Direction sdir = Directional_element_interface::get (s);
947   
948   /* knee */
949   if (dir!= sdir)
950     {
951       stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
952
953       // huh, why not for first visible?
954
955       Grob *last_visible = last_visible_stem (me);
956       if (last_visible)
957         {
958           if ( Staff_symbol_referencer::staff_symbol_l (s)
959                != Staff_symbol_referencer::staff_symbol_l (last_visible))
960             stem_y += Directional_element_interface::get (me)
961               * (beam_multiplicity - stem_multiplicity) * interbeam;
962         }
963       else
964         programming_error ("No last visible stem");
965     }
966
967   return stem_y;
968 }
969
970 /*
971   Hmm.  At this time, beam position and slope are determined.  Maybe,
972   stem directions and length should set to relative to the chord's
973   position of the beam.  */
974 void
975 Beam::set_stem_lengths (Grob *me)
976 {
977   Link_array<Item> stems=
978     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
979
980   if (stems.size () <= 1)
981     return;
982   
983   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
984   for (int i=1; i < stems.size (); i++)
985     if (!Stem::invisible_b (stems[i]))
986       common = common->common_refpoint (stems[i], Y_AXIS);
987
988   Direction dir = Directional_element_interface::get (me);
989   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
990   Real staff_space = Staff_symbol_referencer::staff_space (me);
991   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
992   bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
993   for (int i=0; i < stems.size (); i++)
994     {
995       Item* s = stems[i];
996       if (Stem::invisible_b (s))
997         continue;
998
999       Real stem_y = calc_stem_y (me, s, pos);
1000
1001       // doesn't play well with dvips
1002       if (ps_testing)
1003         if (Stem::get_direction (s) == dir)
1004           stem_y += Stem::get_direction (s) * thick / 2;
1005       
1006       /* caution: stem measures in staff-positions */
1007       Real id = me->relative_coordinate (common, Y_AXIS)
1008         - stems[i]->relative_coordinate (common, Y_AXIS);
1009       Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1010     }
1011 }
1012
1013 void
1014 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1015 {
1016   Link_array<Grob> stems=
1017     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1018   
1019   Direction d = LEFT;
1020   for (int i=0; i  < stems.size (); i++)
1021     {
1022       do
1023         {
1024           /* Don't overwrite user override (?) */
1025           if (Stem::beam_count (stems[i], d) == -1
1026               /* Don't set beaming for outside of outer stems */
1027               && ! (d == LEFT && i == 0)
1028               && ! (d == RIGHT && i == stems.size () -1))
1029             {
1030               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1031               Stem::set_beaming (stems[i], b, d);
1032             }
1033         }
1034       while (flip (&d) != LEFT);
1035     }
1036 }
1037
1038
1039
1040 /*
1041   beams to go with one stem.
1042
1043   FIXME: clean me up.
1044   */
1045 Molecule
1046 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1047 {
1048   // ugh -> use commonx
1049   if ((next
1050        && ! (next->relative_coordinate (0, X_AXIS)
1051             > here->relative_coordinate (0, X_AXIS)))
1052       || (prev
1053           && ! (prev->relative_coordinate (0, X_AXIS)
1054                < here->relative_coordinate (0, X_AXIS))))
1055     programming_error ("Beams are not left-to-right");
1056
1057   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1058   Real bdy = get_interbeam (me);
1059   
1060   Molecule leftbeams;
1061   Molecule rightbeams;
1062
1063   Real nw_f;
1064   if (!Stem::first_head (here))
1065     nw_f = 0;
1066   else {
1067     int t = Stem::type_i (here); 
1068
1069     SCM proc = me->get_grob_property ("flag-width-function");
1070     SCM result = gh_call1 (proc, gh_int2scm (t));
1071     nw_f = gh_scm2double (result);
1072   }
1073
1074
1075   Direction dir = Directional_element_interface::get (me);
1076
1077   /* [Tremolo] beams on whole notes may not have direction set? */
1078  if (dir == CENTER)
1079     dir = Directional_element_interface::get (here);
1080
1081
1082   /* half beams extending to the left. */
1083   if (prev)
1084     {
1085       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1086         - Stem::beam_count (prev, RIGHT);
1087       int lwholebeams= Stem::beam_count (here, LEFT)
1088         <? Stem::beam_count (prev, RIGHT);
1089       
1090       /* Half beam should be one note-width,
1091          but let's make sure two half-beams never touch */
1092
1093       // FIXME: TODO (check) stem width / sloped beams
1094       Real w = here->relative_coordinate (0, X_AXIS)
1095         - prev->relative_coordinate (0, X_AXIS);
1096       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1097         // URG
1098         * me->paper_l ()->get_var ("linethickness");
1099
1100       w = w/2 <? nw_f;
1101       Molecule a;
1102       if (lhalfs)               // generates warnings if not
1103         a =  Lookup::beam (dydx, w + stem_w, thick);
1104       a.translate (Offset (-w, -w * dydx));
1105       a.translate_axis (-stem_w/2, X_AXIS);
1106       for (int j = 0; j  < lhalfs; j++)
1107         {
1108           Molecule b (a);
1109           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1110           leftbeams.add_molecule (b);
1111         }
1112     }
1113
1114   if (next)
1115     {
1116       int rhalfs  = Stem::beam_count (here, RIGHT)
1117         - Stem::beam_count (next, LEFT);
1118       int rwholebeams= Stem::beam_count (here, RIGHT)
1119         <? Stem::beam_count (next, LEFT);
1120
1121       Real w = next->relative_coordinate (0, X_AXIS)
1122         - here->relative_coordinate (0, X_AXIS);
1123
1124       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1125         // URG
1126         * me->paper_l ()->get_var ("linethickness");
1127
1128       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1129       a.translate_axis (- stem_w/2, X_AXIS);
1130       int j = 0;
1131       Real gap_f = 0;
1132       
1133       SCM gap = me->get_grob_property ("gap");
1134       if (gh_number_p (gap))
1135         {
1136           int gap_i = gh_scm2int ((gap));
1137           int nogap = rwholebeams - gap_i;
1138           
1139           for (; j  < nogap; j++)
1140             {
1141               Molecule b (a);
1142               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1143               rightbeams.add_molecule (b);
1144             }
1145           if (Stem::invisible_b (here))
1146             gap_f = nw_f;
1147           else
1148             gap_f = nw_f / 2;
1149           w -= 2 * gap_f;
1150           a = Lookup::beam (dydx, w + stem_w, thick);
1151         }
1152
1153       for (; j  < rwholebeams; j++)
1154         {
1155           Molecule b (a);
1156           Real tx = 0;
1157           if (Stem::invisible_b (here))
1158             // ugh, see chord-tremolo.ly
1159             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1160           else
1161             tx = gap_f;
1162           b.translate (Offset (tx, -dir * bdy * j));
1163           rightbeams.add_molecule (b);
1164         }
1165
1166       w = w/2 <? nw_f;
1167       if (rhalfs)
1168         a = Lookup::beam (dydx, w, thick);
1169
1170       for (; j  < rwholebeams + rhalfs; j++)
1171         {
1172           Molecule b (a);
1173           b.translate_axis (- dir * bdy * j, Y_AXIS);
1174           rightbeams.add_molecule (b);
1175         }
1176
1177     }
1178   leftbeams.add_molecule (rightbeams);
1179
1180   /* Does beam quanting think  of the asymetry of beams? 
1181      Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1182   return leftbeams;
1183 }
1184
1185
1186 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1187 SCM
1188 Beam::brew_molecule (SCM smob)
1189 {
1190   Grob *me =unsmob_grob (smob);
1191
1192   Molecule mol;
1193   if (!gh_pair_p (me->get_grob_property ("stems")))
1194     return SCM_EOL;
1195   Real x0, dx;
1196   Link_array<Item>stems = 
1197     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1198   if (visible_stem_count (me))
1199     {
1200       // ugh -> use commonx
1201       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1202       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1203     }
1204   else
1205     {
1206       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1207       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1208     }
1209
1210   SCM posns = me->get_grob_property ("positions");
1211   Interval pos;
1212   if (!ly_number_pair_p (posns))
1213     {
1214       programming_error ("No beam posns");
1215       pos = Interval (0,0);
1216     }
1217   else
1218     pos= ly_scm2interval (posns);
1219   Real dy = pos.delta ();
1220   Real dydx = dy && dx ? dy/dx : 0;
1221
1222   for (int i=0; i < stems.size (); i++)
1223     {
1224       Item *item = stems[i];
1225       Item *prev = (i > 0)? stems[i-1] : 0;
1226       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1227
1228       Molecule sb = stem_beams (me, item, next, prev, dydx);
1229       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1230       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1231       mol.add_molecule (sb);
1232     }
1233   
1234   mol.translate_axis (x0 
1235                       - dynamic_cast<Spanner*> (me)
1236                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1237                       X_AXIS);
1238
1239   if (DEBUG_QUANTING)
1240     {
1241       /*
1242         This code prints the demerits for each beam. Perhaps this
1243         should be switchable for those who want to twiddle with the
1244         parameters.
1245       */
1246       String str;
1247       if (1)
1248         {
1249           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1250           str += ":";
1251         }
1252       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1253                      "%.2f");
1254
1255       SCM properties = Font_interface::font_alist_chain (me);
1256
1257       
1258       Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1259       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1260     }
1261   
1262   return mol.smobbed_copy ();
1263 }
1264
1265 int
1266 Beam::forced_stem_count (Grob *me) 
1267 {
1268   Link_array<Item>stems = 
1269     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1270   int f = 0;
1271   for (int i=0; i < stems.size (); i++)
1272     {
1273       Item *s = stems[i];
1274
1275       if (Stem::invisible_b (s))
1276         continue;
1277
1278       if (((int)Stem::chord_start_f (s)) 
1279         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1280         f++;
1281     }
1282   return f;
1283 }
1284
1285
1286
1287
1288 int
1289 Beam::visible_stem_count (Grob *me) 
1290 {
1291   Link_array<Item>stems = 
1292     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1293   int c = 0;
1294   for (int i = stems.size (); i--;)
1295     {
1296       if (!Stem::invisible_b (stems[i]))
1297         c++;
1298     }
1299   return c;
1300 }
1301
1302 Item*
1303 Beam::first_visible_stem (Grob *me) 
1304 {
1305   Link_array<Item>stems = 
1306     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1307   
1308   for (int i = 0; i < stems.size (); i++)
1309     {
1310       if (!Stem::invisible_b (stems[i]))
1311         return stems[i];
1312     }
1313   return 0;
1314 }
1315
1316 Item*
1317 Beam::last_visible_stem (Grob *me) 
1318 {
1319   Link_array<Item>stems = 
1320     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1321   for (int i = stems.size (); i--;)
1322     {
1323       if (!Stem::invisible_b (stems[i]))
1324         return stems[i];
1325     }
1326   return 0;
1327 }
1328
1329
1330 /*
1331   [TODO]
1332   
1333   handle rest under beam (do_post: beams are calculated now)
1334   what about combination of collisions and rest under beam.
1335
1336   Should lookup
1337     
1338     rest -> stem -> beam -> interpolate_y_position ()
1339 */
1340 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1341 SCM
1342 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1343 {
1344   Grob *rest = unsmob_grob (element_smob);
1345   Axis a = (Axis) gh_scm2int (axis);
1346   
1347   assert (a == Y_AXIS);
1348
1349   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1350   Grob *stem = st;
1351   if (!stem)
1352     return gh_double2scm (0.0);
1353   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1354   if (!beam
1355       || !Beam::has_interface (beam)
1356       || !Beam::visible_stem_count (beam))
1357     return gh_double2scm (0.0);
1358
1359   // make callback for rest from this.
1360   // todo: make sure this calced already.
1361
1362   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1363   Interval pos (0, 0);
1364   SCM s = beam->get_grob_property ("positions");
1365   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1366     pos = ly_scm2interval (s);
1367
1368   Real dy = pos.delta ();
1369   // ugh -> use commonx
1370   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1371   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1372   Real dydx = dy && dx ? dy/dx : 0;
1373   
1374   Direction d = Stem::get_direction (stem);
1375   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1376
1377   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1378
1379   
1380   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1381
1382   Real minimum_dist
1383     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1384   Real dist =
1385     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1386
1387   int stafflines = Staff_symbol_referencer::line_count (rest);
1388
1389   // move discretely by half spaces.
1390   int discrete_dist = int (ceil (dist));
1391
1392   // move by whole spaces inside the staff.
1393   if (discrete_dist < stafflines+1)
1394     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1395
1396   return gh_double2scm (-d *  discrete_dist);
1397 }
1398
1399
1400
1401
1402 ADD_INTERFACE (Beam, "beam-interface",
1403   "A beam.
1404
1405 #'thickness= weight of beams, in staffspace
1406
1407
1408 We take the least squares line through the ideal-length stems, and
1409 then damp that using
1410
1411         damped = tanh (slope)
1412
1413 this gives an unquantized left and right position for the beam end.
1414 Then we take all combinations of quantings near these left and right
1415 positions, and give them a score (according to how close they are to
1416 the ideal slope, how close the result is to the ideal stems, etc.). We
1417 take the best scoring combination.
1418
1419 ",
1420   "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");
1421