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