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