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