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