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