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