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