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