]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
655e760322dc8ccb4240e390318be11b01598f50
[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::beam_l (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->paper_l ()->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->paper_l ()->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_str (gh_scm2int (me->get_grob_property ("best-idx")));
412           str += ":";
413         }
414       str += to_str (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.ch_C ()), 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       if (knee_b(me))
491         Stem::get_direction (s); // this actually sets it, if necessary
492       else
493         {
494           SCM force = s->remove_grob_property ("dir-forced");
495           if (!gh_boolean_p (force) || !gh_scm2bool (force))
496             Directional_element_interface::set (s, d);
497         }
498     }
499
500
501 /* Simplistic auto-knees; only consider vertical gap between two
502    adjacent chords.
503
504    This may decide for a knee that's impossible to fit sane scoring
505    criteria (eg, stem lengths).  We may need something smarter. */
506 void
507 Beam::consider_auto_knees (Grob *me, Direction d)
508 {
509   SCM scm = me->get_grob_property ("auto-knee-gap");
510
511   if (!gh_number_p (scm))
512     return;
513   
514   bool knee_b = false;
515   
516   Real staff_space = Staff_symbol_referencer::staff_space (me);
517   Real gap = gh_scm2double (scm) / staff_space;
518
519   Link_array<Grob> stems=
520     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
521       
522   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
523
524   int l = 0;
525   for (int r=1; r < stems.size (); r++)
526     {
527       if (!Stem::invisible_b (stems[r-1]))
528         l = r - 1;
529       Grob *right = stems[r];
530       Grob *left = stems[l];
531       if (Stem::invisible_b (left))
532         continue;
533       if (Stem::invisible_b (right))
534         continue;
535           
536       Real left_y = Stem::extremal_heads (left)[d]
537         ->relative_coordinate (common, Y_AXIS);
538       Real right_y = Stem::extremal_heads (right)[-d]
539         ->relative_coordinate (common, Y_AXIS);
540
541       Real dy = right_y - left_y;
542
543       if (abs (dy) >= gap)
544         {
545           knee_b = true;
546           Direction knee_dir = (right_y > left_y ? UP : DOWN);
547           if (!Stem::invisible_b (left)
548               && left->get_grob_property ("dir-forced") != SCM_BOOL_T)
549             {
550               Directional_element_interface::set (left, knee_dir);
551               left->set_grob_property ("dir-forced", SCM_BOOL_T);
552
553             }
554           if (!Stem::invisible_b (right)
555               && stems[r]->get_grob_property ("dir-forced") != SCM_BOOL_T)
556             {
557               Directional_element_interface::set (right, -knee_dir);
558               right->set_grob_property ("dir-forced", SCM_BOOL_T);
559             }
560         }
561     }
562
563   if (knee_b)
564     {
565       me->set_grob_property ("knee", SCM_BOOL_T);
566        
567       for (int i=0; i < stems.size (); i++)
568         stems[i]->set_grob_property ("stem-info", SCM_EOL);
569     }
570 }
571
572 /* Set stem's shorten property if unset.
573
574  TODO:
575    take some y-position (chord/beam/nearest?) into account
576    scmify forced-fraction
577  
578   This is done in beam because the shorten has to be uniform over the
579   entire beam.
580
581 */
582 void
583 Beam::set_stem_shorten (Grob *me)
584 {
585   /*
586     shortening looks silly for x staff beams
587    */
588   if (knee_b(me))
589     return ;
590   
591   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
592
593   int beam_count = get_beam_count (me);
594
595   SCM shorten = me->get_grob_property ("beamed-stem-shorten");
596   if (shorten == SCM_EOL)
597     return;
598
599   int sz = scm_ilength (shorten);
600   
601   Real staff_space = Staff_symbol_referencer::staff_space (me);
602   SCM shorten_elt = scm_list_ref (shorten,
603                                   gh_int2scm (beam_count <? (sz - 1)));
604   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
605
606   /* your similar cute comment here */
607   shorten_f *= forced_fraction;
608
609   if (shorten_f)
610     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
611 }
612
613 /*  Call list of y-dy-callbacks, that handle setting of
614     grob-properties
615
616 */
617 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
618 SCM
619 Beam::after_line_breaking (SCM smob)
620 {
621   Grob *me = unsmob_grob (smob);
622   
623   /* Copy to mutable list. */
624   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
625   me->set_grob_property ("positions", s);
626
627   if (ly_car (s) == SCM_BOOL_F)
628     {
629
630       // one wonders if such genericity is necessary  --hwn.
631       SCM callbacks = me->get_grob_property ("position-callbacks");
632       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
633         gh_call1 (ly_car (i), smob);
634     }
635
636   set_stem_lengths (me);  
637   return SCM_UNSPECIFIED;
638 }
639
640 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
641 SCM
642 Beam::least_squares (SCM smob)
643 {
644   Grob *me = unsmob_grob (smob);
645
646   int count = visible_stem_count (me);
647   Interval pos (0, 0);
648   
649   if (count <= 1)
650     {
651       me->set_grob_property ("positions", ly_interval2scm (pos));
652       return SCM_UNSPECIFIED;
653     }
654
655
656   Array<Real> x_posns ;
657   Link_array<Grob> stems=
658     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
659   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
660   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
661
662   Real my_y = me->relative_coordinate (commony, Y_AXIS);
663   
664   Grob *fvs  = first_visible_stem (me);
665   Grob *lvs  = last_visible_stem (me);
666   
667   Interval ideal (Stem::calc_stem_info (fvs).ideal_y_
668                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
669                   Stem::calc_stem_info (lvs).ideal_y_
670                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
671   
672   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
673   for (int i=0; i < stems.size (); i++)
674     {
675       Grob* s = stems[i];
676
677       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
678       x_posns.push (x);
679     }
680   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
681
682   Real y =0;  
683   Real dydx = 0;
684   Real dy = 0;
685   
686   if (!ideal.delta ())
687     {
688       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
689                       Stem::chord_start_y (last_visible_stem (me)));
690
691
692       /*
693         TODO -- use scoring for this.
694
695         complicated, because we take stem-info.ideal for determining
696         beam slopes.
697        */
698       /* Make simple beam on middle line have small tilt */
699       if (!ideal[LEFT] && chord.delta () && count == 2)
700         {
701
702           /*
703             FIXME. -> UP
704           */
705           Direction d = (Direction) (sign (chord.delta ()) * UP);
706           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
707           //                * dir;
708           pos[-d] = - pos[d];
709         }
710       else
711         {
712           pos = ideal;
713         }
714
715       y = pos[LEFT];
716       dy = pos[RIGHT]- y;
717       dydx = dy/dx;
718     }
719   else
720     {
721       Array<Offset> ideals;
722       for (int i=0; i < stems.size (); i++)
723         {
724           Grob* s = stems[i];
725           if (Stem::invisible_b (s))
726             continue;
727           ideals.push (Offset (x_posns[i],
728                                Stem::calc_stem_info (s).ideal_y_
729                                + s->relative_coordinate (commony, Y_AXIS)
730                                - my_y));
731         }
732       minimise_least_squares (&dydx, &y, ideals);
733
734       dy = dydx * dx;
735       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
736       pos = Interval (y, (y+dy));
737     }
738
739   me->set_grob_property ("positions", ly_interval2scm (pos));
740  
741   return SCM_UNSPECIFIED;
742 }
743
744
745 /*
746   We can't combine with previous function, since check concave and
747   slope damping comes first.
748  */
749 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
750 SCM
751 Beam::shift_region_to_valid (SCM grob)
752 {
753   Grob *me = unsmob_grob (grob);
754   /*
755     Code dup.
756    */
757   Array<Real> x_posns ;
758   Link_array<Grob> stems=
759     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
760   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
761   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
762
763   Grob *fvs = first_visible_stem (me);
764
765   if (!fvs)
766     return SCM_UNSPECIFIED;
767     
768   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
769   for (int i=0; i < stems.size (); i++)
770     {
771       Grob* s = stems[i];
772
773       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
774       x_posns.push (x);
775     }
776
777   Grob *lvs = last_visible_stem (me);
778   if (!lvs)
779     return SCM_UNSPECIFIED;
780   
781   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
782
783   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
784   Real dy = pos.delta();
785   Real y = pos[LEFT];
786   Real dydx =dy/dx;
787
788   
789   /*
790     Shift the positions so that we have a chance of finding good
791     quants (i.e. no short stem failures.)
792    */
793   Interval feasible_left_point;
794   feasible_left_point.set_full ();
795   for (int i=0; i < stems.size (); i++)
796     {
797       Grob* s = stems[i];
798       if (Stem::invisible_b (s))
799         continue;
800
801       Direction d = Stem::get_direction (s);
802
803       Real left_y =
804         Stem::calc_stem_info (s).shortest_y_
805         - dydx * x_posns [i];
806
807       /*
808         left_y is now relative to the stem S. We want relative to
809         ourselves, so translate:
810        */
811       left_y += 
812         + s->relative_coordinate (commony, Y_AXIS)
813         - me->relative_coordinate (commony, Y_AXIS);
814
815       Interval flp ;
816       flp.set_full ();
817       flp[-d] = left_y;
818
819       feasible_left_point.intersect (flp);
820     }
821       
822   if (feasible_left_point.empty_b())
823     {
824       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
825     }
826   else if (!feasible_left_point.elem_b(y))
827     {
828       if (isinf (feasible_left_point[DOWN]))
829         y = feasible_left_point[UP] - REGION_SIZE;
830       else if (isinf (feasible_left_point[UP]))
831         y = feasible_left_point[DOWN]+ REGION_SIZE;
832       else
833         y = feasible_left_point.center ();
834     }
835   pos = Interval (y, (y+dy));
836   me->set_grob_property ("positions", ly_interval2scm (pos));
837   return SCM_UNSPECIFIED;
838 }
839
840
841 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
842 SCM
843 Beam::check_concave (SCM smob)
844 {
845   Grob *me = unsmob_grob (smob);
846
847   Link_array<Grob> stems = 
848     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
849
850   for (int i = 0; i < stems.size ();)
851     {
852       if (Stem::invisible_b (stems[i]))
853         stems.del (i);
854       else
855         i++;
856     }
857   
858   if (stems.size () < 3)
859     return SCM_UNSPECIFIED;
860
861
862   /* Concaveness #1: If distance of an inner notehead to line between
863      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
864      beam is concave (Heinz Stolba).
865
866      In the case of knees, the line connecting outer heads is often
867      not related to the beam slope (it may even go in the other
868      direction). Skip the check when the outer stems point in
869      different directions. --hwn
870      
871   */
872   bool concaveness1 = false;
873   SCM gap = me->get_grob_property ("concaveness-gap");
874   if (gh_number_p (gap)
875       && Stem::get_direction(stems.top ())
876          == Stem::get_direction(stems[0]))
877     {
878       Real r1 = gh_scm2double (gap);
879       Real dy = Stem::chord_start_y (stems.top ())
880         - Stem::chord_start_y (stems[0]);
881
882       
883       Real slope = dy / (stems.size () - 1);
884       
885       Real y0 = Stem::chord_start_y (stems[0]);
886       for (int i = 1; i < stems.size () - 1; i++)
887         {
888           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
889           if (c > r1)
890             {
891               concaveness1 = true;
892               break;
893             }
894         }
895     }
896
897     
898   /* Concaveness #2: Sum distances of inner noteheads that fall
899      outside the interval of the two outer noteheads.
900
901      We only do this for beams where first and last stem have the same
902      direction. --hwn.
903
904
905      Note that "convex" stems compensate for "concave" stems.
906      (is that intentional?) --hwn.
907   */
908   
909   Real concaveness2 = 0;
910   SCM thresh = me->get_grob_property ("concaveness-threshold");
911   Real r2 = infinity_f;
912   if (!concaveness1 && gh_number_p (thresh)
913       && Stem::get_direction(stems.top ())
914          == Stem::get_direction(stems[0]))
915     {
916       r2 = gh_scm2double (thresh);
917
918       Direction dir = Stem::get_direction(stems.top ());
919       Real concave = 0;
920       Interval iv (Stem::chord_start_y (stems[0]),
921                    Stem::chord_start_y (stems.top ()));
922       
923       if (iv[MAX] < iv[MIN])
924         iv.swap ();
925       
926       for (int i = 1; i < stems.size () - 1; i++)
927         {
928           Real f = Stem::chord_start_y (stems[i]);
929           concave += ((f - iv[MAX] ) >? 0) +
930             ((f - iv[MIN] ) <? 0);
931         }
932       concave *= dir;
933       concaveness2 = concave / (stems.size () - 2);
934       
935       /* ugh: this is the a kludge to get
936          input/regression/beam-concave.ly to behave as
937          baerenreiter. */
938
939       /*
940         huh? we're dividing twice (which is not scalable) meaning that
941         the longer the beam, the more unlikely it will be
942         concave. Maybe you would even expect the other way around??
943
944         --hwn.
945         
946        */
947       concaveness2 /= (stems.size () - 2);
948     }
949   
950   /* TODO: some sort of damping iso -> plain horizontal */
951   if (concaveness1 || concaveness2 > r2)
952     {
953       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
954       Real r = pos.linear_combination (0);
955       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
956       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
957     }
958
959   return SCM_UNSPECIFIED;
960 }
961
962 /* This neat trick is by Werner Lemberg,
963    damped = tanh (slope)
964    corresponds with some tables in [Wanske] CHECKME */
965 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
966 SCM
967 Beam::slope_damping (SCM smob)
968 {
969   Grob *me = unsmob_grob (smob);
970
971   if (visible_stem_count (me) <= 1)
972     return SCM_UNSPECIFIED;
973
974   SCM s = me->get_grob_property ("damping"); 
975   int damping = gh_scm2int (s);
976
977   if (damping)
978     {
979       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
980       Real dy = pos.delta ();
981
982       Grob *fvs  = first_visible_stem (me);
983       Grob *lvs  = last_visible_stem (me);
984
985       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
986
987
988       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
989         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
990       Real dydx = dy && dx ? dy/dx : 0;
991       dydx = 0.6 * tanh (dydx) / damping;
992
993       Real damped_dy = dydx * dx;
994       pos[LEFT] += (dy - damped_dy) / 2;
995       pos[RIGHT] -= (dy - damped_dy) / 2;
996       
997       me->set_grob_property ("positions", ly_interval2scm (pos));
998     }
999   return SCM_UNSPECIFIED;
1000 }
1001
1002 Slice
1003 where_are_the_whole_beams(SCM beaming)
1004 {
1005   Slice l; 
1006   
1007   for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1008     {
1009       if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1010         
1011         l.add_point (gh_scm2int (gh_car (s)));
1012     }
1013
1014   return l;
1015 }
1016
1017 /*
1018   Calculate the Y position of the stem-end, given the Y-left, Y-right
1019   in POS for stem S. This Y position is relative to S.
1020  */
1021 Real
1022 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1023                    Real xl, Real xr,
1024                    Interval pos, bool french) 
1025 {
1026   Real beam_translation = get_beam_translation (me);
1027
1028     
1029   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1030   Real dy = pos.delta ();
1031   Real dx = xr - xl;
1032   Real stem_y_beam0 = (dy && dx
1033                        ? r / dx
1034                        * dy
1035                        : 0) + pos[LEFT];
1036   
1037   Direction my_dir = Directional_element_interface::get (s);
1038   SCM beaming = s->get_grob_property ("beaming");
1039  
1040   Real stem_y = stem_y_beam0;
1041   if (french)
1042     {
1043       Slice bm = where_are_the_whole_beams (beaming);
1044       if (!bm.empty_b())
1045         stem_y += beam_translation * bm[-my_dir];
1046     }
1047   else
1048     {
1049       Slice bm = Stem::beam_multiplicity(s);
1050       if (!bm.empty_b())
1051         stem_y +=bm[my_dir] * beam_translation;
1052     }
1053   
1054   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1055     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1056   
1057   return stem_y + id;
1058 }
1059
1060 /*
1061   Hmm.  At this time, beam position and slope are determined.  Maybe,
1062   stem directions and length should set to relative to the chord's
1063   position of the beam.  */
1064 void
1065 Beam::set_stem_lengths (Grob *me)
1066 {
1067   Link_array<Grob> stems=
1068     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1069
1070   if (stems.size () <= 1)
1071     return;
1072   
1073   Grob *common[2];
1074   for (int a = 2; a--;)
1075     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1076   
1077   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1078   Real staff_space = Staff_symbol_referencer::staff_space (me);
1079
1080   bool french = to_boolean (me->get_grob_property ("french-beaming"));
1081
1082   
1083   bool gap = false;
1084   Real thick =0.0;
1085   if (gh_number_p (me->get_grob_property ("gap"))
1086       &&gh_scm2double (me->get_grob_property ("gap")))
1087   {
1088     gap = true;
1089     thick = gh_scm2double (me->get_grob_property ("thickness"))
1090       * Staff_symbol_referencer::staff_space(me);
1091   }
1092       
1093   // ugh -> use commonx
1094   Grob * fvs = first_visible_stem (me);
1095   Grob *lvs = last_visible_stem (me);
1096     
1097   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1098   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1099   
1100   for (int i=0; i < stems.size (); i++)
1101     {
1102       Grob* s = stems[i];
1103       if (Stem::invisible_b (s))
1104         continue;
1105
1106       Real stem_y = calc_stem_y (me, s, common,
1107                                  xl, xr,
1108                                  pos, french && i > 0&& (i < stems.size  () -1));
1109
1110       /*
1111         Make the stems go up to the end of the beam. This doesn't matter
1112         for normal beams, but for tremolo beams it looks silly otherwise.
1113        */
1114       if (gap)
1115         stem_y += thick * 0.5 * Directional_element_interface::get(s);
1116       
1117       Stem::set_stemend (s, 2* stem_y / staff_space);
1118     }
1119 }
1120
1121 void
1122 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1123 {
1124   Link_array<Grob> stems=
1125     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1126   
1127   Direction d = LEFT;
1128   for (int i=0; i  < stems.size (); i++)
1129     {
1130       /*
1131         Don't overwrite user settings.
1132        */
1133       
1134       do
1135         {
1136           /* Don't set beaming for outside of outer stems */      
1137           if ((d == LEFT && i == 0)
1138               ||(d == RIGHT && i == stems.size () -1))
1139             continue;
1140
1141
1142           SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1143           if (beaming_prop == SCM_EOL ||
1144               index_get_cell (beaming_prop, d) == SCM_EOL)
1145             {
1146               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1147               Stem::set_beaming (stems[i], b, d);
1148             }
1149         }
1150       while (flip (&d) != LEFT);
1151     }
1152 }
1153
1154 int
1155 Beam::forced_stem_count (Grob *me) 
1156 {
1157   Link_array<Grob>stems = 
1158     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1159   int f = 0;
1160   for (int i=0; i < stems.size (); i++)
1161     {
1162       Grob *s = stems[i];
1163
1164       if (Stem::invisible_b (s))
1165         continue;
1166
1167       if (((int)Stem::chord_start_y (s)) 
1168         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1169         f++;
1170     }
1171   return f;
1172 }
1173
1174
1175
1176
1177 int
1178 Beam::visible_stem_count (Grob *me) 
1179 {
1180   Link_array<Grob>stems = 
1181     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1182   int c = 0;
1183   for (int i = stems.size (); i--;)
1184     {
1185       if (!Stem::invisible_b (stems[i]))
1186         c++;
1187     }
1188   return c;
1189 }
1190
1191 Grob*
1192 Beam::first_visible_stem (Grob *me) 
1193 {
1194   Link_array<Grob>stems = 
1195     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1196   
1197   for (int i = 0; i < stems.size (); i++)
1198     {
1199       if (!Stem::invisible_b (stems[i]))
1200         return stems[i];
1201     }
1202   return 0;
1203 }
1204
1205 Grob*
1206 Beam::last_visible_stem (Grob *me) 
1207 {
1208   Link_array<Grob>stems = 
1209     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1210   for (int i = stems.size (); i--;)
1211     {
1212       if (!Stem::invisible_b (stems[i]))
1213         return stems[i];
1214     }
1215   return 0;
1216 }
1217
1218
1219 /*
1220   [TODO]
1221   
1222   handle rest under beam (do_post: beams are calculated now)
1223   what about combination of collisions and rest under beam.
1224
1225   Should lookup
1226     
1227     rest -> stem -> beam -> interpolate_y_position ()
1228 */
1229 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1230 SCM
1231 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1232 {
1233   Grob *rest = unsmob_grob (element_smob);
1234   Axis a = (Axis) gh_scm2int (axis);
1235   
1236   assert (a == Y_AXIS);
1237
1238   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1239   Grob *stem = st;
1240   if (!stem)
1241     return gh_double2scm (0.0);
1242   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1243   if (!beam
1244       || !Beam::has_interface (beam)
1245       || !Beam::visible_stem_count (beam))
1246     return gh_double2scm (0.0);
1247
1248   // make callback for rest from this.
1249   // todo: make sure this calced already.
1250
1251   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1252   Interval pos (0, 0);
1253   SCM s = beam->get_grob_property ("positions");
1254   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1255     pos = ly_scm2interval (s);
1256
1257   Real dy = pos.delta ();
1258   // ugh -> use commonx
1259   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1260   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1261   Real dydx = dy && dx ? dy/dx : 0;
1262   
1263   Direction d = Stem::get_direction (stem);
1264   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1265
1266   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1267
1268   
1269   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1270
1271   Real minimum_dist
1272     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1273   Real dist =
1274     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1275
1276   int stafflines = Staff_symbol_referencer::line_count (rest);
1277
1278   // move discretely by half spaces.
1279   int discrete_dist = int (ceil (dist));
1280
1281   // move by whole spaces inside the staff.
1282   if (discrete_dist < stafflines+1)
1283     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1284
1285   return gh_double2scm (-d *  discrete_dist);
1286 }
1287
1288 bool
1289 Beam::knee_b (Grob*me)
1290 {
1291   SCM k = me->get_grob_property ("knee");
1292   if (gh_boolean_p (k))
1293     return gh_scm2bool (k);
1294
1295   bool knee = false;
1296   int d = 0;
1297   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1298     if (d != Directional_element_interface::get (unsmob_grob (ly_car (s))))
1299       {
1300         knee = true;
1301         break;
1302       }
1303   
1304   me->set_grob_property ("knee", gh_bool2scm (knee));
1305
1306   return knee;
1307 }
1308
1309
1310 ADD_INTERFACE (Beam, "beam-interface",
1311   "A beam.
1312
1313 #'thickness= weight of beams, in staffspace
1314
1315
1316 We take the least squares line through the ideal-length stems, and
1317 then damp that using
1318
1319         damped = tanh (slope)
1320
1321 this gives an unquantized left and right position for the beam end.
1322 Then we take all combinations of quantings near these left and right
1323 positions, and give them a score (according to how close they are to
1324 the ideal slope, how close the result is to the ideal stems, etc.). We
1325 take the best scoring combination.
1326
1327 ",
1328   "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");
1329
1330