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