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