]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
Run grand-replace for 2012
[lilypond.git] / lily / beam.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22   TODO:
23
24   - Determine auto knees based on positions if it's set by the user.
25
26   - the code is littered with * and / staff_space calls for
27   #'positions. Consider moving to real-world coordinates?
28
29   Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
30
31   Notes:
32
33   - Stems run to the Y-center of the beam.
34
35   - beam_translation is the offset between Y centers of the beam.
36 */
37
38 #include "beam.hh"
39
40 #include "align-interface.hh"
41 #include "beam-scoring-problem.hh"
42 #include "beaming-pattern.hh"
43 #include "directional-element-interface.hh"
44 #include "grob-array.hh"
45 #include "international.hh"
46 #include "interval-set.hh"
47 #include "item.hh"
48 #include "lookup.hh"
49 #include "main.hh"
50 #include "misc.hh"
51 #include "note-head.hh"
52 #include "output-def.hh"
53 #include "pointer-group-interface.hh"
54 #include "rhythmic-head.hh"
55 #include "spanner.hh"
56 #include "staff-symbol.hh"
57 #include "staff-symbol-referencer.hh"
58 #include "stem.hh"
59 #include "warn.hh"
60
61 #if DEBUG_BEAM_SCORING
62 #include "text-interface.hh" // debug output.
63 #include "font-interface.hh" // debug output.
64 #endif
65
66 #include <map>
67
68 Beam_stem_segment::Beam_stem_segment ()
69 {
70   max_connect_ = 1000;          // infinity
71   stem_ = 0;
72   width_ = 0.0;
73   stem_x_ = 0.0;
74   rank_ = 0;
75   stem_index_ = 0;
76   dir_ = CENTER;
77 }
78
79 bool
80 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
81 {
82   return a.horizontal_[LEFT] < b.horizontal_[LEFT];
83 }
84
85 Beam_segment::Beam_segment ()
86 {
87   vertical_count_ = 0;
88 }
89
90 void
91 Beam::add_stem (Grob *me, Grob *s)
92 {
93   if (Stem::get_beam (s))
94     {
95       programming_error ("Stem already has beam");
96       return;
97     }
98
99   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
100   s->set_object ("beam", me->self_scm ());
101   add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
102 }
103
104 Real
105 Beam::get_beam_thickness (Grob *me)
106 {
107   return robust_scm2double (me->get_property ("beam-thickness"), 0)
108          * Staff_symbol_referencer::staff_space (me);
109 }
110
111 /* Return the translation between 2 adjoining beams. */
112 Real
113 Beam::get_beam_translation (Grob *me)
114 {
115   int beam_count = get_beam_count (me);
116   Real staff_space = Staff_symbol_referencer::staff_space (me);
117   Real line = Staff_symbol_referencer::line_thickness (me);
118   Real beam_thickness = get_beam_thickness (me);
119   Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
120
121   Real beam_translation = beam_count < 4
122                           ? (2 * staff_space + line - beam_thickness) / 2.0
123                           : (3 * staff_space + line - beam_thickness) / 3.0;
124
125   return fract * beam_translation;
126 }
127
128 /* Maximum beam_count. */
129 int
130 Beam::get_beam_count (Grob *me)
131 {
132   int m = 0;
133
134   extract_grob_set (me, "stems", stems);
135   for (vsize i = 0; i < stems.size (); i++)
136     {
137       Grob *stem = stems[i];
138       m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
139     }
140   return m;
141 }
142
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
144 SCM
145 Beam::calc_normal_stems (SCM smob)
146 {
147   Grob *me = unsmob_grob (smob);
148
149   extract_grob_set (me, "stems", stems);
150   SCM val = Grob_array::make_array ();
151   Grob_array *ga = unsmob_grob_array (val);
152   for (vsize i = 0; i < stems.size (); i++)
153     if (Stem::is_normal_stem (stems[i]))
154       ga->add (stems[i]);
155
156   return val;
157 }
158
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
160 SCM
161 Beam::calc_direction (SCM smob)
162 {
163   Grob *me = unsmob_grob (smob);
164
165   /* Beams with less than 2 two stems don't make much sense, but could happen
166      when you do
167
168      r8[ c8 r8]
169
170   */
171
172   Direction dir = CENTER;
173
174   int count = normal_stem_count (me);
175   if (count < 2)
176     {
177       extract_grob_set (me, "stems", stems);
178       if (stems.size () == 0)
179         {
180           me->warning (_ ("removing beam with no stems"));
181           me->suicide ();
182
183           return SCM_UNSPECIFIED;
184         }
185       else
186         {
187           Grob *stem = first_normal_stem (me);
188
189           /*
190             This happens for chord tremolos.
191           */
192           if (!stem)
193             stem = stems[0];
194
195           if (is_direction (stem->get_property_data ("direction")))
196             dir = to_dir (stem->get_property_data ("direction"));
197           else
198             dir = to_dir (stem->get_property ("default-direction"));
199         }
200     }
201
202   if (count >= 1)
203     {
204       if (!dir)
205         dir = get_default_dir (me);
206
207       consider_auto_knees (me);
208     }
209
210   if (dir)
211     {
212       set_stem_directions (me, dir);
213     }
214
215   return scm_from_int (dir);
216 }
217
218 /* We want a maximal number of shared beams, but if there is choice, we
219  * take the one that is closest to the end of the stem. This is for
220  * situations like
221  *
222  *        x
223  *       |
224  *       |
225  *   |===|
226  *   |=
227  *   |
228  *  x
229  */
230 int
231 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
232                                     Direction left_dir,
233                                     Direction right_dir)
234 {
235   Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
236
237   int best_count = 0;
238   int best_start = 0;
239   for (int i = lslice[-left_dir];
240        (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
241     {
242       int count = 0;
243       for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
244         {
245           int k = -right_dir * scm_to_int (scm_car (s)) + i;
246           if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
247             count++;
248         }
249
250       if (count >= best_count)
251         {
252           best_count = count;
253           best_start = i;
254         }
255     }
256
257   return best_start;
258 }
259
260 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
261 SCM
262 Beam::calc_beaming (SCM smob)
263 {
264   Grob *me = unsmob_grob (smob);
265
266   extract_grob_set (me, "stems", stems);
267
268   Slice last_int;
269   last_int.set_empty ();
270
271   SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
272   Direction last_dir = CENTER;
273   for (vsize i = 0; i < stems.size (); i++)
274     {
275       Grob *this_stem = stems[i];
276       SCM this_beaming = this_stem->get_property ("beaming");
277
278       Direction this_dir = get_grob_direction (this_stem);
279       if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
280         {
281           int start_point = position_with_maximal_common_beams
282                             (last_beaming, this_beaming,
283                              last_dir ? last_dir : this_dir,
284                              this_dir);
285
286           Direction d = LEFT;
287           Slice new_slice;
288           do
289             {
290               new_slice.set_empty ();
291               SCM s = index_get_cell (this_beaming, d);
292               for (; scm_is_pair (s); s = scm_cdr (s))
293                 {
294                   int new_beam_pos
295                     = start_point - this_dir * scm_to_int (scm_car (s));
296
297                   new_slice.add_point (new_beam_pos);
298                   scm_set_car_x (s, scm_from_int (new_beam_pos));
299                 }
300             }
301           while (flip (&d) != LEFT);
302
303           if (!new_slice.is_empty ())
304             last_int = new_slice;
305         }
306       else
307         {
308           /*
309             FIXME: what's this for?
310            */
311           SCM s = scm_cdr (this_beaming);
312           for (; scm_is_pair (s); s = scm_cdr (s))
313             {
314               int np = -this_dir * scm_to_int (scm_car (s));
315               scm_set_car_x (s, scm_from_int (np));
316               last_int.add_point (np);
317             }
318         }
319
320       if (scm_ilength (scm_cdr (this_beaming)) > 0)
321         {
322           last_beaming = this_beaming;
323           last_dir = this_dir;
324         }
325     }
326
327   return SCM_EOL;
328 }
329
330 bool
331 operator <(Beam_stem_segment const &a,
332            Beam_stem_segment const &b)
333 {
334   return a.rank_ < b.rank_;
335 }
336
337 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
338
339 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
340 SCM
341 Beam::calc_beam_segments (SCM smob)
342 {
343   /* ugh, this has a side-effect that we need to ensure that
344      Stem #'beaming is correct */
345   Grob *me_grob = unsmob_grob (smob);
346   (void) me_grob->get_property ("beaming");
347
348   Spanner *me = dynamic_cast<Spanner *> (me_grob);
349
350   extract_grob_set (me, "stems", stems);
351
352   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
353   Direction d = LEFT;
354   do
355     commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
356   while (flip (&d) != LEFT);
357
358   int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
359   Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
360
361   Position_stem_segments_map stem_segments;
362   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
363
364   /* There are two concepts of "rank" that are used in the following code.
365      The beam_rank is the vertical position of the beam (larger numbers are
366      closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
367      is the horizontal position of the segment (this is incremented by two
368      for each stem; the beam segment on the right side of the stem has
369      a higher rank (by one) than its neighbour to the left). */
370   Slice ranks;
371   for (vsize i = 0; i < stems.size (); i++)
372     {
373       Grob *stem = stems[i];
374       Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
375       Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
376       SCM beaming = stem->get_property ("beaming");
377
378       do
379         {
380           // Find the maximum and minimum beam ranks.
381           // Given that RANKS is never reset to empty, the interval will always be
382           // smallest for the left beamlet of the first stem, and then it might grow.
383           // Do we really want this? (It only affects the tremolo gaps) --jneem
384           for (SCM s = index_get_cell (beaming, d);
385                scm_is_pair (s); s = scm_cdr (s))
386             {
387               if (!scm_is_integer (scm_car (s)))
388                 continue;
389
390               int beam_rank = scm_to_int (scm_car (s));
391               ranks.add_point (beam_rank);
392             }
393
394           for (SCM s = index_get_cell (beaming, d);
395                scm_is_pair (s); s = scm_cdr (s))
396             {
397               if (!scm_is_integer (scm_car (s)))
398                 continue;
399
400               int beam_rank = scm_to_int (scm_car (s));
401               Beam_stem_segment seg;
402               seg.stem_ = stem;
403               seg.stem_x_ = stem_x;
404               seg.rank_ = 2 * i + (d + 1) / 2;
405               seg.width_ = stem_width;
406               seg.stem_index_ = i;
407               seg.dir_ = d;
408               seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
409
410               Direction stem_dir = get_grob_direction (stem);
411
412               seg.gapped_
413                 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
414               stem_segments[beam_rank].push_back (seg);
415             }
416         }
417       while (flip (&d) != LEFT);
418     }
419
420   Drul_array<Real> break_overshoot
421     = robust_scm2drul (me->get_property ("break-overshoot"),
422                        Drul_array<Real> (-0.5, 0.0));
423
424   vector<Beam_segment> segments;
425   for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
426        i != stem_segments.end (); i++)
427     {
428       vector<Beam_stem_segment> segs = (*i).second;
429       vector_sort (segs, less<Beam_stem_segment> ());
430
431       Beam_segment current;
432
433       // Iterate over all of the segments of the current beam rank,
434       // merging the adjacent Beam_stem_segments into one Beam_segment
435       // when appropriate.
436       int vertical_count = (*i).first;
437       for (vsize j = 0; j < segs.size (); j++)
438         {
439           // Keeping track of the different directions here is a little tricky.
440           // segs[j].dir_ is the direction of the beam segment relative to the stem
441           // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
442           // its stem) whereas event_dir refers to the edge of the beam segment that
443           // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
444           // are looking at that edge of the beam segment that is furthest from its
445           // stem).
446           Direction event_dir = LEFT;
447           Beam_stem_segment const &seg = segs[j];
448           do
449             {
450               Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
451               // TODO: make names clearer? --jneem
452               // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
453               // on_beam_bound: whether the current segment is on the boundary of just that part
454               //   of the beam with the current beam_rank
455               bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
456                                    : seg.stem_index_ == stems.size () - 1;
457               bool on_beam_bound = (event_dir == LEFT) ? j == 0
458                                    : j == segs.size () - 1;
459               bool inside_stem = (event_dir == LEFT)
460                                  ? seg.stem_index_ > 0
461                                  : seg.stem_index_ + 1 < stems.size ();
462
463               bool event = on_beam_bound
464                            || abs (seg.rank_ - neighbor_seg.rank_) > 1
465                            || (abs (vertical_count) >= seg.max_connect_
466                                || abs (vertical_count) >= neighbor_seg.max_connect_);
467
468               if (!event)
469                 // Then this edge of the current segment is irrelevant because it will
470                 // be connected with the next segment in the event_dir direction.
471                 // If we skip the left edge here, the right edge of
472                 // the previous segment has already been skipped since
473                 // the conditions are symmetric
474                 continue;
475
476               current.vertical_count_ = vertical_count;
477               current.horizontal_[event_dir] = seg.stem_x_;
478               if (seg.dir_ == event_dir)
479                 // then we are examining the edge of a beam segment that is furthest
480                 // from its stem.
481                 {
482                   if (on_line_bound
483                       && me->get_bound (event_dir)->break_status_dir ())
484                     {
485                       current.horizontal_[event_dir]
486                         = (robust_relative_extent (me->get_bound (event_dir),
487                                                    commonx, X_AXIS)[RIGHT]
488                            + event_dir * break_overshoot[event_dir]);
489                     }
490                   else
491                     {
492                       Grob *stem = stems[seg.stem_index_];
493                       Drul_array<Real> beamlet_length
494                         = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
495                       Drul_array<Real> max_proportion
496                         = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
497                       Real length = beamlet_length[seg.dir_];
498
499                       if (inside_stem)
500                         {
501                           Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
502                           Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
503
504                           length = min (length,
505                                         fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
506                         }
507                       current.horizontal_[event_dir] += event_dir * length;
508                     }
509                 }
510               else
511                 // we are examining the edge of a beam segment that is closest
512                 // (ie. touching, unless there is a gap) its stem.
513                 {
514                   current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
515                   if (seg.gapped_)
516                     {
517                       current.horizontal_[event_dir] -= event_dir * gap_length;
518
519                       if (Stem::is_invisible (seg.stem_))
520                         {
521                           /*
522                             Need to do this in case of whole notes. We don't want the
523                             heads to collide with the beams.
524                            */
525                           extract_grob_set (seg.stem_, "note-heads", heads);
526
527                           for (vsize k = 0; k < heads.size (); k++)
528                             current.horizontal_[event_dir]
529                               = event_dir * min (event_dir * current.horizontal_[event_dir],
530                                                  - gap_length / 2
531                                                  + event_dir
532                                                  * heads[k]->extent (commonx,
533                                                                      X_AXIS)[-event_dir]);
534                         }
535                     }
536                 }
537
538               if (event_dir == RIGHT)
539                 {
540                   segments.push_back (current);
541                   current = Beam_segment ();
542                 }
543             }
544           while (flip (&event_dir) != LEFT);
545         }
546
547     }
548
549   SCM segments_scm = SCM_EOL;
550   SCM *tail = &segments_scm;
551
552   for (vsize i = 0; i < segments.size (); i++)
553     {
554       *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
555                                               scm_from_int (segments[i].vertical_count_)),
556                                     scm_cons (ly_symbol2scm ("horizontal"),
557                                               ly_interval2scm (segments[i].horizontal_))),
558                         SCM_EOL);
559       tail = SCM_CDRLOC (*tail);
560     }
561
562   return segments_scm;
563 }
564
565 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
566 SCM
567 Beam::calc_x_positions (SCM smob)
568 {
569   Spanner *me = unsmob_spanner (smob);
570   SCM segments = me->get_property ("beam-segments");
571   Interval x_positions;
572   x_positions.set_empty ();
573   for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
574     x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
575                                                           scm_car (s),
576                                                           SCM_EOL),
577                                             Interval (0.0, 0.0)));
578
579   // Case for beams without segments (i.e. uniting two skips with a beam)
580   // TODO: should issue a warning?  warning likely issued downstream, but couldn't hurt...
581   if (x_positions.is_empty ())
582     {
583       extract_grob_set (me, "stems", stems);
584       Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
585       Direction d = LEFT;
586       do
587         x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
588       while (flip (&d) != LEFT);
589     }
590   return ly_interval2scm (x_positions);
591 }
592
593 vector<Beam_segment>
594 Beam::get_beam_segments (Grob *me)
595 {
596   SCM segments_scm = me->get_property ("beam-segments");
597   vector<Beam_segment> segments;
598   for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
599     {
600       segments.push_back (Beam_segment ());
601       segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
602       segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
603     }
604
605   return segments;
606 }
607
608 MAKE_SCHEME_CALLBACK (Beam, print, 1);
609 SCM
610 Beam::print (SCM grob)
611 {
612   Spanner *me = unsmob_spanner (grob);
613   /*
614     TODO - mild code dup for all the commonx calls.
615     Some use just common_refpoint_of_array, some (in print and
616     calc_beam_segments) use this plus calls to get_bound.
617
618     Figure out if there is any particular reason for this and
619     consolidate in one Beam::get_common function.
620   */
621   extract_grob_set (me, "stems", stems);
622   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
623   Direction d = LEFT;
624   do
625     commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
626   while (flip (&d) != LEFT);
627
628   vector<Beam_segment> segments = get_beam_segments (me);
629
630   if (!segments.size ())
631     return SCM_EOL;
632
633   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
634
635   SCM posns = me->get_property ("quantized-positions");
636   Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
637   Interval pos;
638   if (!is_number_pair (posns))
639     {
640       programming_error ("no beam positions?");
641       pos = Interval (0, 0);
642     }
643   else
644     pos = ly_scm2realdrul (posns);
645
646   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
647
648   Real dy = pos[RIGHT] - pos[LEFT];
649   Real slope = (dy && span.length ()) ? dy / span.length () : 0;
650
651   Real beam_thickness = get_beam_thickness (me);
652   Real beam_dy = get_beam_translation (me);
653
654   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
655
656   Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
657
658   Stencil the_beam;
659   int extreme = (segments[0].vertical_count_ == 0
660                  ? segments[0].vertical_count_
661                  : segments.back ().vertical_count_);
662
663   for (vsize i = 0; i < segments.size (); i++)
664     {
665       Real local_slope = slope;
666       /*
667         Makes local slope proportional to the ratio of the length of this beam
668         to the total length.
669       */
670       if (feather_dir)
671         local_slope += (feather_dir * segments[i].vertical_count_
672                         * beam_dy
673                         * placements.length ()
674                         / span.length ());
675
676       Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
677
678       b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
679       Real multiplier = feather_dir ? placements[LEFT] : 1.0;
680
681       Interval weights (1 - multiplier, multiplier);
682
683       if (feather_dir != LEFT)
684         weights.swap ();
685
686       // we need two translations: the normal one and
687       // the one of the lowest segment
688       int idx[] = {i, extreme};
689       Real translations[2];
690
691       for (int j = 0; j < 2; j++)
692         translations[j] = slope
693                           * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
694                           + pos.linear_combination (CENTER)
695                           + beam_dy * segments[idx[j]].vertical_count_;
696
697       Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
698
699       /*
700         Tricky.  The manipulation of the variable `weighted_average' below ensures
701         that beams with a RIGHT grow direction will start from the position of the
702         lowest segment at 0, and this error will decrease and decrease over the
703         course of the beam.  Something with a LEFT grow direction, on the other
704         hand, will always start in the correct place but progressively accrue
705         error at broken places.  This code shifts beams up given where they are
706         in the total span length (controlled by the variable `multiplier').  To
707         better understand what it does, try commenting it out: you'll see that
708         all of the RIGHT growing beams immediately start too low and get better
709         over line breaks, whereas all of the LEFT growing beams start just right
710         and get worse over line breaks.
711       */
712       Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
713
714       if (segments[0].vertical_count_ < 0 && feather_dir)
715         weighted_average += beam_dy * (segments.size () - 1) * factor;
716
717       b.translate_axis (weighted_average, Y_AXIS);
718
719       the_beam.add_stencil (b);
720
721     }
722
723 #if (DEBUG_BEAM_SCORING)
724   SCM annotation = me->get_property ("annotation");
725   if (scm_is_string (annotation))
726     {
727       extract_grob_set (me, "stems", stems);
728
729       /*
730         This code prints the demerits for each beam. Perhaps this
731         should be switchable for those who want to twiddle with the
732         parameters.
733       */
734       string str;
735       SCM properties = Font_interface::text_font_alist_chain (me);
736
737       properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
738                              properties);
739
740       Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
741
742       Stencil score = *unsmob_stencil (Text_interface::interpret_markup
743                                        (me->layout ()->self_scm (), properties, annotation));
744
745       if (!score.is_empty ())
746         {
747           score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
748           the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
749         }
750     }
751 #endif
752
753   the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
754   return the_beam.smobbed_copy ();
755 }
756
757 Direction
758 Beam::get_default_dir (Grob *me)
759 {
760   extract_grob_set (me, "stems", stems);
761
762   Drul_array<Real> extremes (0.0, 0.0);
763   for (iterof (s, stems); s != stems.end (); s++)
764     {
765       Interval positions = Stem::head_positions (*s);
766       Direction d = DOWN;
767       do
768         {
769           if (sign (positions[d]) == d)
770             extremes[d] = d * max (d * positions[d], d * extremes[d]);
771         }
772       while (flip (&d) != DOWN);
773     }
774
775   Drul_array<int> total (0, 0);
776   Drul_array<int> count (0, 0);
777
778   bool force_dir = false;
779   for (vsize i = 0; i < stems.size (); i++)
780     {
781       Grob *s = stems[i];
782       Direction stem_dir = CENTER;
783       SCM stem_dir_scm = s->get_property_data ("direction");
784       if (is_direction (stem_dir_scm))
785         {
786           stem_dir = to_dir (stem_dir_scm);
787           force_dir = true;
788         }
789       else
790         stem_dir = to_dir (s->get_property ("default-direction"));
791
792       if (!stem_dir)
793         stem_dir = to_dir (s->get_property ("neutral-direction"));
794
795       if (stem_dir)
796         {
797           count[stem_dir]++;
798           total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
799         }
800     }
801
802   if (!force_dir)
803     {
804       if (abs (extremes[UP]) > -extremes[DOWN])
805         return DOWN;
806       else if (extremes[UP] < -extremes[DOWN])
807         return UP;
808     }
809
810   Direction dir = CENTER;
811   Direction d = CENTER;
812   if ((d = (Direction) sign (count[UP] - count[DOWN])))
813     dir = d;
814   else if (count[UP]
815            && count[DOWN]
816            && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
817     dir = d;
818   else if ((d = (Direction) sign (total[UP] - total[DOWN])))
819     dir = d;
820   else
821     dir = to_dir (me->get_property ("neutral-direction"));
822
823   return dir;
824 }
825
826 /* Set all stems with non-forced direction to beam direction.
827    Urg: non-forced should become `without/with unforced' direction,
828    once stem gets cleaned-up. */
829 void
830 Beam::set_stem_directions (Grob *me, Direction d)
831 {
832   extract_grob_set (me, "stems", stems);
833
834   for (vsize i = 0; i < stems.size (); i++)
835     {
836       Grob *s = stems[i];
837
838       SCM forcedir = s->get_property_data ("direction");
839       if (!to_dir (forcedir))
840         set_grob_direction (s, d);
841     }
842 }
843
844 /*
845   Only try horizontal beams for knees.  No reliable detection of
846   anything else is possible here, since we don't know funky-beaming
847   settings, or X-distances (slopes!)  People that want sloped
848   knee-beams, should set the directions manually.
849
850
851   TODO:
852
853   this routine should take into account the stemlength scoring
854   of a possible knee/nonknee beam.
855 */
856 void
857 Beam::consider_auto_knees (Grob *me)
858 {
859   SCM scm = me->get_property ("auto-knee-gap");
860   if (!scm_is_number (scm))
861     return;
862
863   Interval_set gaps;
864
865   gaps.set_full ();
866
867   extract_grob_set (me, "normal-stems", stems);
868
869   Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
870   Real staff_space = Staff_symbol_referencer::staff_space (me);
871
872   vector<Interval> head_extents_array;
873   for (vsize i = 0; i < stems.size (); i++)
874     {
875       Grob *stem = stems[i];
876
877       Interval head_extents = Stem::head_positions (stem);
878       if (!head_extents.is_empty ())
879         {
880           head_extents[LEFT] += -1;
881           head_extents[RIGHT] += 1;
882           head_extents *= staff_space * 0.5;
883
884           /*
885             We could subtract beam Y position, but this routine only
886             sets stem directions, a constant shift does not have an
887             influence.
888           */
889           head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
890
891           if (to_dir (stem->get_property_data ("direction")))
892             {
893               Direction stemdir = to_dir (stem->get_property ("direction"));
894               head_extents[-stemdir] = -stemdir * infinity_f;
895             }
896         }
897       head_extents_array.push_back (head_extents);
898
899       gaps.remove_interval (head_extents);
900     }
901
902   Interval max_gap;
903   Real max_gap_len = 0.0;
904
905   for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
906     {
907       Interval gap = gaps.allowed_regions_[i];
908
909       /*
910         the outer gaps are not knees.
911       */
912       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
913         continue;
914
915       if (gap.length () >= max_gap_len)
916         {
917           max_gap_len = gap.length ();
918           max_gap = gap;
919         }
920     }
921
922   Real beam_translation = get_beam_translation (me);
923   Real beam_thickness = Beam::get_beam_thickness (me);
924   int beam_count = Beam::get_beam_count (me);
925   Real height_of_beams = beam_thickness / 2
926                          + (beam_count - 1) * beam_translation;
927   Real threshold = scm_to_double (scm) + height_of_beams;
928
929   if (max_gap_len > threshold)
930     {
931       int j = 0;
932       for (vsize i = 0; i < stems.size (); i++)
933         {
934           Grob *stem = stems[i];
935           Interval head_extents = head_extents_array[j++];
936
937           Direction d = (head_extents.center () < max_gap.center ())
938                         ? UP : DOWN;
939
940           stem->set_property ("direction", scm_from_int (d));
941
942           head_extents.intersect (max_gap);
943           assert (head_extents.is_empty () || head_extents.length () < 1e-6);
944         }
945     }
946 }
947
948 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
949 SCM
950 Beam::calc_stem_shorten (SCM smob)
951 {
952   Grob *me = unsmob_grob (smob);
953
954   /*
955     shortening looks silly for x staff beams
956   */
957   if (is_knee (me))
958     return scm_from_int (0);
959
960   Real forced_fraction = 1.0 * forced_stem_count (me)
961                          / normal_stem_count (me);
962
963   int beam_count = get_beam_count (me);
964
965   SCM shorten_list = me->get_property ("beamed-stem-shorten");
966   if (shorten_list == SCM_EOL)
967     return scm_from_int (0);
968
969   Real staff_space = Staff_symbol_referencer::staff_space (me);
970
971   SCM shorten_elt
972     = robust_list_ref (beam_count - 1, shorten_list);
973   Real shorten = scm_to_double (shorten_elt) * staff_space;
974
975   shorten *= forced_fraction;
976
977   if (shorten)
978     return scm_from_double (shorten);
979
980   return scm_from_double (0.0);
981 }
982
983 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
984 SCM
985 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
986 {
987   Grob *me = unsmob_grob (smob);
988   Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
989   bool cbs = to_boolean (align_broken_intos);
990
991   Beam_scoring_problem problem (me, ys, cbs);
992   ys = problem.solve ();
993
994   return ly_interval2scm (ys);
995 }
996
997 /*
998   Report slice containing the numbers that are both in (car BEAMING)
999   and (cdr BEAMING)
1000 */
1001 Slice
1002 where_are_the_whole_beams (SCM beaming)
1003 {
1004   Slice l;
1005
1006   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1007     {
1008       if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1009
1010         l.add_point (scm_to_int (scm_car (s)));
1011     }
1012
1013   return l;
1014 }
1015
1016 /* Return the Y position of the stem-end, given the Y-left, Y-right
1017    in POS for stem S.  This Y position is relative to S. */
1018 Real
1019 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1020                    Real xl, Real xr, Direction feather_dir,
1021                    Drul_array<Real> pos, bool french)
1022 {
1023   Real beam_translation = get_beam_translation (me);
1024   Direction stem_dir = get_grob_direction (stem);
1025
1026   Real dx = xr - xl;
1027   Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1028   Real xdir = 2 * relx - 1;
1029
1030   Real stem_y = linear_combination (pos, xdir);
1031
1032   SCM beaming = stem->get_property ("beaming");
1033
1034   Slice beam_slice (french
1035                     ? where_are_the_whole_beams (beaming)
1036                     : Stem::beam_multiplicity (stem));
1037   if (beam_slice.is_empty ())
1038     beam_slice = Slice (0, 0);
1039   Interval beam_multiplicity (beam_slice[LEFT],
1040                               beam_slice[RIGHT]);
1041
1042   /*
1043     feather dir = 1 , relx 0->1 : factor 0 -> 1
1044     feather dir = 0 , relx 0->1 : factor 1 -> 1
1045     feather dir = -1, relx 0->1 : factor 1 -> 0
1046    */
1047   Real feather_factor = 1;
1048   if (feather_dir > 0)
1049     feather_factor = relx;
1050   else if (feather_dir < 0)
1051     feather_factor = 1 - relx;
1052
1053   stem_y += feather_factor * beam_translation
1054             * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1055   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1056             - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1057
1058   return stem_y + id;
1059 }
1060
1061 /*
1062   Hmm.  At this time, beam position and slope are determined.  Maybe,
1063   stem directions and length should set to relative to the chord's
1064   position of the beam.  */
1065 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1066 SCM
1067 Beam::set_stem_lengths (SCM smob)
1068 {
1069   Grob *me = unsmob_grob (smob);
1070
1071   /* trigger callbacks. */
1072   (void) me->get_property ("direction");
1073   (void) me->get_property ("beaming");
1074
1075   SCM posns = me->get_property ("positions");
1076
1077   extract_grob_set (me, "stems", stems);
1078   if (!stems.size ())
1079     return posns;
1080
1081   Grob *common[2];
1082   for (int a = 2; a--;)
1083     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1084
1085   Drul_array<Real> pos = ly_scm2realdrul (posns);
1086   Real staff_space = Staff_symbol_referencer::staff_space (me);
1087   scale_drul (&pos, staff_space);
1088
1089   bool gap = false;
1090   Real thick = 0.0;
1091   if (robust_scm2int (me->get_property ("gap-count"), 0))
1092     {
1093       gap = true;
1094       thick = get_beam_thickness (me);
1095     }
1096
1097   Grob *fvs = first_normal_stem (me);
1098   Grob *lvs = last_normal_stem (me);
1099
1100   Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1101   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1102
1103   for (vsize i = 0; i < stems.size (); i++)
1104     {
1105       Grob *s = stems[i];
1106
1107       bool french = to_boolean (s->get_property ("french-beaming"));
1108       Real stem_y = calc_stem_y (me, s, common,
1109                                  x_span[LEFT], x_span[RIGHT], feather_dir,
1110                                  pos, french && s != lvs && s != fvs);
1111
1112       /*
1113         Make the stems go up to the end of the beam. This doesn't matter
1114         for normal beams, but for tremolo beams it looks silly otherwise.
1115       */
1116       if (gap
1117           && !Stem::is_invisible (s))
1118         stem_y += thick * 0.5 * get_grob_direction (s);
1119
1120       /*
1121         Do set_stem_positions for invisible stems too, so tuplet brackets
1122         have a reference point for sloping
1123        */
1124       Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1125     }
1126
1127   return posns;
1128 }
1129
1130 void
1131 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1132 {
1133   extract_grob_set (me, "stems", stems);
1134
1135   Direction d = LEFT;
1136   for (vsize i = 0; i < stems.size (); i++)
1137     {
1138       /*
1139         Don't overwrite user settings.
1140       */
1141       do
1142         {
1143           Grob *stem = stems[i];
1144           SCM beaming_prop = stem->get_property ("beaming");
1145           if (beaming_prop == SCM_EOL
1146               || index_get_cell (beaming_prop, d) == SCM_EOL)
1147             {
1148               int count = beaming->beamlet_count (i, d);
1149               if (i > 0
1150                   && i + 1 < stems.size ()
1151                   && Stem::is_invisible (stem))
1152                 count = min (count, beaming->beamlet_count (i, -d));
1153
1154               if ( ((i == 0 && d == LEFT)
1155                     || (i == stems.size () - 1 && d == RIGHT))
1156                    && stems.size () > 1
1157                    && to_boolean (me->get_property ("clip-edges")))
1158                 count = 0;
1159
1160               Stem::set_beaming (stem, count, d);
1161             }
1162         }
1163       while (flip (&d) != LEFT);
1164     }
1165 }
1166
1167 int
1168 Beam::forced_stem_count (Grob *me)
1169 {
1170   extract_grob_set (me, "normal-stems", stems);
1171
1172   int f = 0;
1173   for (vsize i = 0; i < stems.size (); i++)
1174     {
1175       Grob *s = stems[i];
1176
1177       /* I can imagine counting those boundaries as a half forced stem,
1178          but let's count them full for now. */
1179       Direction defdir = to_dir (s->get_property ("default-direction"));
1180
1181       if (abs (Stem::chord_start_y (s)) > 0.1
1182           && defdir
1183           && get_grob_direction (s) != defdir)
1184         f++;
1185     }
1186   return f;
1187 }
1188
1189 int
1190 Beam::normal_stem_count (Grob *me)
1191 {
1192   extract_grob_set (me, "normal-stems", stems);
1193   return stems.size ();
1194 }
1195
1196 Grob *
1197 Beam::first_normal_stem (Grob *me)
1198 {
1199   extract_grob_set (me, "normal-stems", stems);
1200   return stems.size () ? stems[0] : 0;
1201 }
1202
1203 Grob *
1204 Beam::last_normal_stem (Grob *me)
1205 {
1206   extract_grob_set (me, "normal-stems", stems);
1207   return stems.size () ? stems.back () : 0;
1208 }
1209
1210 /*
1211   [TODO]
1212
1213   handle rest under beam (do_post: beams are calculated now)
1214   what about combination of collisions and rest under beam.
1215
1216   Should lookup
1217
1218   rest -> stem -> beam -> interpolate_y_position ()
1219 */
1220 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1221 SCM
1222 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1223 {
1224   Grob *rest = unsmob_grob (smob);
1225   if (scm_is_number (rest->get_property ("staff-position")))
1226     return scm_from_int (0);
1227
1228   Real offset = robust_scm2double (prev_offset, 0.0);
1229
1230   Grob *st = unsmob_grob (rest->get_object ("stem"));
1231   Grob *stem = st;
1232   if (!stem)
1233     return scm_from_double (0.0);
1234   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1235   if (!beam
1236       || !Beam::has_interface (beam)
1237       || !Beam::normal_stem_count (beam))
1238     return scm_from_double (0.0);
1239
1240   Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1241                                          Drul_array<Real> (0, 0)));
1242
1243   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1244
1245   scale_drul (&pos, staff_space);
1246
1247   Real dy = pos[RIGHT] - pos[LEFT];
1248
1249   extract_grob_set (beam, "stems", stems);
1250   Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1251
1252   Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1253                                          Interval (0.0, 0.0));
1254   Real x0 = x_span[LEFT];
1255   Real dx = x_span.length ();
1256   Real slope = dy && dx ? dy / dx : 0;
1257
1258   Direction d = get_grob_direction (stem);
1259   Real stem_y = pos[LEFT]
1260                 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1261
1262   Real beam_translation = get_beam_translation (beam);
1263   Real beam_thickness = Beam::get_beam_thickness (beam);
1264
1265   /*
1266     TODO: this is not strictly correct for 16th knee beams.
1267   */
1268   int beam_count
1269     = Stem::beam_multiplicity (stem).length () + 1;
1270
1271   Real height_of_my_beams = beam_thickness / 2
1272                             + (beam_count - 1) * beam_translation;
1273   Real beam_y = stem_y - d * height_of_my_beams;
1274
1275   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1276
1277   Interval rest_extent = rest->extent (rest, Y_AXIS);
1278   rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1279
1280   Real rest_dim = rest_extent[d];
1281   Real minimum_distance
1282     = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1283                      + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1284
1285   Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1286
1287   shift /= staff_space;
1288   Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1289
1290   /* Always move discretely by half spaces */
1291   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1292
1293   /* Inside staff, move by whole spaces*/
1294   if ((rest_extent[d] + staff_space * shift) * d
1295       < rad
1296       || (rest_extent[-d] + staff_space * shift) * -d
1297       < rad)
1298     shift = ceil (fabs (shift)) * sign (shift);
1299
1300   return scm_from_double (offset + staff_space * shift);
1301 }
1302
1303 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1304 SCM
1305 Beam::pure_rest_collision_callback (SCM smob,
1306                                     SCM, /* prev_offset */
1307                                     SCM, /* start */
1308                                     SCM /* end */)
1309 {
1310   Real amount = 0.0;
1311
1312   Grob *me = unsmob_grob (smob);
1313   Grob *stem = unsmob_grob (me->get_object ("stem"));
1314   if (!stem)
1315     return scm_from_double (amount);
1316   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1317   if (!beam
1318       || !Beam::normal_stem_count (beam))
1319     return scm_from_double (amount);
1320
1321   Real ss = Staff_symbol_referencer::staff_space (me);
1322
1323   /*
1324     This gives the extrema of rest positions.
1325     In general, beams are never typeset more than one staff space away
1326     from the staff in either direction.
1327   */
1328   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1329   Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1330   rest_max_pos.widen (1);
1331   rest_max_pos *= ss / 2;
1332
1333   extract_grob_set (beam, "stems", stems);
1334   vector<Grob *> my_stems;
1335
1336   for (vsize i = 0; i < stems.size (); i++)
1337     if (Stem::head_count (stems[i]) || stems[i] == stem)
1338       my_stems.push_back (stems[i]);
1339
1340   vsize idx = -1;
1341
1342   for (vsize i = 0; i < my_stems.size (); i++)
1343     if (my_stems[i] == stem)
1344       {
1345         idx = i;
1346         break;
1347       }
1348   Grob *left;
1349   Grob *right;
1350
1351   if (idx == (vsize) - 1 || my_stems.size () == 1)
1352     return scm_from_double (amount);
1353   else if (idx == 0)
1354     left = right = my_stems[1];
1355   else if (idx == my_stems.size () - 1)
1356     left = right = my_stems[idx - 1];
1357   else
1358     {
1359       left = my_stems[idx - 1];
1360       right = my_stems[idx + 1];
1361     }
1362   Direction beamdir = get_grob_direction (beam);
1363   /*
1364     Take the position between the two bounding head_positions,
1365     then bound it by the minimum and maximum positions outside the staff.
1366     4.0 = 2.0 to get out of staff space * 2.0 for the average
1367   */
1368   amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1369
1370   return scm_from_double (amount);
1371 }
1372
1373 bool
1374 Beam::is_knee (Grob *me)
1375 {
1376   SCM k = me->get_property ("knee");
1377   if (scm_is_bool (k))
1378     return ly_scm2bool (k);
1379
1380   bool knee = false;
1381   int d = 0;
1382   extract_grob_set (me, "stems", stems);
1383   for (vsize i = stems.size (); i--;)
1384     {
1385       Direction dir = get_grob_direction (stems[i]);
1386       if (d && d != dir)
1387         {
1388           knee = true;
1389           break;
1390         }
1391       d = dir;
1392     }
1393
1394   me->set_property ("knee", ly_bool2scm (knee));
1395
1396   return knee;
1397 }
1398
1399 bool
1400 Beam::is_cross_staff (Grob *me)
1401 {
1402   extract_grob_set (me, "stems", stems);
1403   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1404   for (vsize i = 0; i < stems.size (); i++)
1405     if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1406       return true;
1407   return false;
1408 }
1409
1410 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1411 SCM
1412 Beam::calc_cross_staff (SCM smob)
1413 {
1414   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1415 }
1416
1417 int
1418 Beam::get_direction_beam_count (Grob *me, Direction d)
1419 {
1420   extract_grob_set (me, "stems", stems);
1421   int bc = 0;
1422
1423   for (vsize i = stems.size (); i--;)
1424     {
1425       /*
1426         Should we take invisible stems into account?
1427       */
1428       if (get_grob_direction (stems[i]) == d)
1429         bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1430     }
1431
1432   return bc;
1433 }
1434
1435 ADD_INTERFACE (Beam,
1436                "A beam.\n"
1437                "\n"
1438                "The @code{beam-thickness} property is the weight of beams,"
1439                " measured in staffspace.  The @code{direction} property is"
1440                " not user-serviceable.  Use the @code{direction} property"
1441                " of @code{Stem} instead.\n"
1442                "The following properties may be set in the @code{details}"
1443                " list.\n"
1444                "\n"
1445                "@table @code\n"
1446                "@item stem-length-demerit-factor\n"
1447                "Demerit factor used for inappropriate stem lengths.\n"
1448                "@item secondary-beam-demerit\n"
1449                "Demerit used in quanting calculations for multiple"
1450                " beams.\n"
1451                "@item region-size\n"
1452                "Size of region for checking quant scores.\n"
1453                "@item beam-eps\n"
1454                "Epsilon for beam quant code to check for presence"
1455                " in gap.\n"
1456                "@item stem-length-limit-penalty\n"
1457                "Penalty for differences in stem lengths on a beam.\n"
1458                "@item damping-direction-penalty\n"
1459                "Demerit penalty applied when beam direction is different"
1460                " from damping direction.\n"
1461                "@item hint-direction-penalty\n"
1462                "Demerit penalty applied when beam direction is different"
1463                " from damping direction, but damping slope is"
1464                " <= @code{round-to-zero-slope}.\n"
1465                "@item musical-direction-factor\n"
1466                "Demerit scaling factor for difference between"
1467                " beam slope and music slope.\n"
1468                "@item ideal-slope-factor\n"
1469                "Demerit scaling factor for difference between"
1470                " beam slope and damping slope.\n"
1471                "@item round-to-zero-slope\n"
1472                "Damping slope which is considered zero for purposes of"
1473                " calculating direction penalties.\n"
1474                "@end table\n",
1475
1476                /* properties */
1477                "annotation "
1478                "auto-knee-gap "
1479                "beamed-stem-shorten "
1480                "beaming "
1481                "beam-segments "
1482                "beam-thickness "
1483                "break-overshoot "
1484                "clip-edges "
1485                "concaveness "
1486                "collision-interfaces "
1487                "collision-voice-only "
1488                "covered-grobs "
1489                "damping "
1490                "details "
1491                "direction "
1492                "gap "
1493                "gap-count "
1494                "grow-direction "
1495                "inspect-quants "
1496                "knee "
1497                "length-fraction "
1498                "least-squares-dy "
1499                "neutral-direction "
1500                "normal-stems "
1501                "positions "
1502                "quantized-positions "
1503                "shorten "
1504                "skip-quanting "
1505                "stems "
1506                "X-positions "
1507               );