]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
stem tremolo fix
[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_y (first_visible_stem (me)),
739                       Stem::chord_start_y (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_y (stems.top ())
835         - Stem::chord_start_y (stems[0]);
836
837       
838       Real slope = dy / (stems.size () - 1);
839       
840       Real y0 = Stem::chord_start_y (stems[0]);
841       for (int i = 1; i < stems.size () - 1; i++)
842         {
843           Real c = (Stem::chord_start_y (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_y (stems[0]),
866                    Stem::chord_start_y (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_y (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     {
1085       int t = Stem::duration_log (here); 
1086
1087       SCM proc = me->get_grob_property ("flag-width-function");
1088       SCM result = gh_call1 (proc, gh_int2scm (t));
1089       nw_f = gh_scm2double (result);
1090     }
1091
1092
1093   Direction dir = Directional_element_interface::get (me);
1094
1095   /* [Tremolo] beams on whole notes may not have direction set? */
1096  if (dir == CENTER)
1097     dir = Directional_element_interface::get (here);
1098
1099
1100   /* half beams extending to the left. */
1101   if (prev)
1102     {
1103       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1104         - Stem::beam_count (prev, RIGHT);
1105       int lwholebeams= Stem::beam_count (here, LEFT)
1106         <? Stem::beam_count (prev, RIGHT);
1107       
1108       /* Half beam should be one note-width,
1109          but let's make sure two half-beams never touch */
1110
1111       // FIXME: TODO (check) stem width / sloped beams
1112       Real w = here->relative_coordinate (0, X_AXIS)
1113         - prev->relative_coordinate (0, X_AXIS);
1114       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1115         // URG
1116         * me->paper_l ()->get_var ("linethickness");
1117
1118       w = w/2 <? nw_f;
1119       Molecule a;
1120       if (lhalfs)               // generates warnings if not
1121         a =  Lookup::beam (dydx, w + stem_w, thick);
1122       a.translate (Offset (-w, -w * dydx));
1123       a.translate_axis (-stem_w/2, X_AXIS);
1124       for (int j = 0; j  < lhalfs; j++)
1125         {
1126           Molecule b (a);
1127           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1128           leftbeams.add_molecule (b);
1129         }
1130     }
1131
1132   if (next)
1133     {
1134       int rhalfs  = Stem::beam_count (here, RIGHT)
1135         - Stem::beam_count (next, LEFT);
1136       int rwholebeams= Stem::beam_count (here, RIGHT)
1137         <? Stem::beam_count (next, LEFT);
1138
1139       Real w = next->relative_coordinate (0, X_AXIS)
1140         - here->relative_coordinate (0, X_AXIS);
1141
1142       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1143         // URG
1144         * me->paper_l ()->get_var ("linethickness");
1145
1146       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1147       a.translate_axis (- stem_w/2, X_AXIS);
1148       int j = 0;
1149       Real gap_f = 0;
1150       
1151       SCM gap = me->get_grob_property ("gap");
1152       if (gh_number_p (gap))
1153         {
1154           int gap_i = gh_scm2int ((gap));
1155           int nogap = rwholebeams - gap_i;
1156           
1157           for (; j  < nogap; j++)
1158             {
1159               Molecule b (a);
1160               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1161               rightbeams.add_molecule (b);
1162             }
1163           if (Stem::invisible_b (here))
1164             gap_f = nw_f;
1165           else
1166             gap_f = nw_f / 2;
1167           w -= 2 * gap_f;
1168           a = Lookup::beam (dydx, w + stem_w, thick);
1169         }
1170
1171       for (; j  < rwholebeams; j++)
1172         {
1173           Molecule b (a);
1174           Real tx = 0;
1175           if (Stem::invisible_b (here))
1176             // ugh, see chord-tremolo.ly
1177             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1178           else
1179             tx = gap_f;
1180           b.translate (Offset (tx, -dir * bdy * j));
1181           rightbeams.add_molecule (b);
1182         }
1183
1184       w = w/2 <? nw_f;
1185       if (rhalfs)
1186         a = Lookup::beam (dydx, w, thick);
1187
1188       for (; j  < rwholebeams + rhalfs; j++)
1189         {
1190           Molecule b (a);
1191           b.translate_axis (- dir * bdy * j, Y_AXIS);
1192           rightbeams.add_molecule (b);
1193         }
1194
1195     }
1196   leftbeams.add_molecule (rightbeams);
1197
1198   /* Does beam quanting think  of the asymetry of beams? 
1199      Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1200   return leftbeams;
1201 }
1202
1203
1204 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1205 SCM
1206 Beam::brew_molecule (SCM smob)
1207 {
1208   Grob *me =unsmob_grob (smob);
1209
1210   Molecule mol;
1211   if (!gh_pair_p (me->get_grob_property ("stems")))
1212     return SCM_EOL;
1213   Real x0, dx;
1214   Link_array<Item>stems = 
1215     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1216   if (visible_stem_count (me))
1217     {
1218       // ugh -> use commonx
1219       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1220       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1221     }
1222   else
1223     {
1224       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1225       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1226     }
1227
1228   SCM posns = me->get_grob_property ("positions");
1229   Interval pos;
1230   if (!ly_number_pair_p (posns))
1231     {
1232       programming_error ("No beam posns");
1233       pos = Interval (0,0);
1234     }
1235   else
1236     pos= ly_scm2interval (posns);
1237   Real dy = pos.delta ();
1238   Real dydx = dy && dx ? dy/dx : 0;
1239
1240   for (int i=0; i < stems.size (); i++)
1241     {
1242       Item *item = stems[i];
1243       Item *prev = (i > 0)? stems[i-1] : 0;
1244       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1245
1246       Molecule sb = stem_beams (me, item, next, prev, dydx);
1247       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1248       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1249       mol.add_molecule (sb);
1250     }
1251   
1252   mol.translate_axis (x0 
1253                       - dynamic_cast<Spanner*> (me)
1254                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1255                       X_AXIS);
1256
1257 #if (DEBUG_QUANTING)
1258     {
1259       /*
1260         This code prints the demerits for each beam. Perhaps this
1261         should be switchable for those who want to twiddle with the
1262         parameters.
1263       */
1264       String str;
1265       if (1)
1266         {
1267           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1268           str += ":";
1269         }
1270       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1271                      "%.2f");
1272
1273       SCM properties = Font_interface::font_alist_chain (me);
1274
1275       
1276       Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1277       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1278     }
1279 #endif
1280     
1281   return mol.smobbed_copy ();
1282 }
1283
1284 int
1285 Beam::forced_stem_count (Grob *me) 
1286 {
1287   Link_array<Item>stems = 
1288     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1289   int f = 0;
1290   for (int i=0; i < stems.size (); i++)
1291     {
1292       Item *s = stems[i];
1293
1294       if (Stem::invisible_b (s))
1295         continue;
1296
1297       if (((int)Stem::chord_start_y (s)) 
1298         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1299         f++;
1300     }
1301   return f;
1302 }
1303
1304
1305
1306
1307 int
1308 Beam::visible_stem_count (Grob *me) 
1309 {
1310   Link_array<Item>stems = 
1311     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1312   int c = 0;
1313   for (int i = stems.size (); i--;)
1314     {
1315       if (!Stem::invisible_b (stems[i]))
1316         c++;
1317     }
1318   return c;
1319 }
1320
1321 Item*
1322 Beam::first_visible_stem (Grob *me) 
1323 {
1324   Link_array<Item>stems = 
1325     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1326   
1327   for (int i = 0; i < stems.size (); i++)
1328     {
1329       if (!Stem::invisible_b (stems[i]))
1330         return stems[i];
1331     }
1332   return 0;
1333 }
1334
1335 Item*
1336 Beam::last_visible_stem (Grob *me) 
1337 {
1338   Link_array<Item>stems = 
1339     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1340   for (int i = stems.size (); i--;)
1341     {
1342       if (!Stem::invisible_b (stems[i]))
1343         return stems[i];
1344     }
1345   return 0;
1346 }
1347
1348
1349 /*
1350   [TODO]
1351   
1352   handle rest under beam (do_post: beams are calculated now)
1353   what about combination of collisions and rest under beam.
1354
1355   Should lookup
1356     
1357     rest -> stem -> beam -> interpolate_y_position ()
1358 */
1359 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1360 SCM
1361 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1362 {
1363   Grob *rest = unsmob_grob (element_smob);
1364   Axis a = (Axis) gh_scm2int (axis);
1365   
1366   assert (a == Y_AXIS);
1367
1368   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1369   Grob *stem = st;
1370   if (!stem)
1371     return gh_double2scm (0.0);
1372   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1373   if (!beam
1374       || !Beam::has_interface (beam)
1375       || !Beam::visible_stem_count (beam))
1376     return gh_double2scm (0.0);
1377
1378   // make callback for rest from this.
1379   // todo: make sure this calced already.
1380
1381   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1382   Interval pos (0, 0);
1383   SCM s = beam->get_grob_property ("positions");
1384   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1385     pos = ly_scm2interval (s);
1386
1387   Real dy = pos.delta ();
1388   // ugh -> use commonx
1389   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1390   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1391   Real dydx = dy && dx ? dy/dx : 0;
1392   
1393   Direction d = Stem::get_direction (stem);
1394   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1395
1396   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1397
1398   
1399   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1400
1401   Real minimum_dist
1402     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1403   Real dist =
1404     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1405
1406   int stafflines = Staff_symbol_referencer::line_count (rest);
1407
1408   // move discretely by half spaces.
1409   int discrete_dist = int (ceil (dist));
1410
1411   // move by whole spaces inside the staff.
1412   if (discrete_dist < stafflines+1)
1413     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1414
1415   return gh_double2scm (-d *  discrete_dist);
1416 }
1417
1418
1419
1420
1421 ADD_INTERFACE (Beam, "beam-interface",
1422   "A beam.
1423
1424 #'thickness= weight of beams, in staffspace
1425
1426
1427 We take the least squares line through the ideal-length stems, and
1428 then damp that using
1429
1430         damped = tanh (slope)
1431
1432 this gives an unquantized left and right position for the beam end.
1433 Then we take all combinations of quantings near these left and right
1434 positions, and give them a score (according to how close they are to
1435 the ideal slope, how close the result is to the ideal stems, etc.). We
1436 take the best scoring combination.
1437
1438 ",
1439   "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");
1440