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