]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
Merge remote-tracking branch 'origin/translation' into master
[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   SCM *tail = &segments_scm;
644
645   for (vsize i = 0; i < segments.size (); i++)
646     {
647       *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
648                                               scm_from_int (segments[i].vertical_count_)),
649                                     scm_cons (ly_symbol2scm ("horizontal"),
650                                               ly_interval2scm (segments[i].horizontal_))),
651                         SCM_EOL);
652       tail = SCM_CDRLOC (*tail);
653     }
654
655   return segments_scm;
656 }
657
658 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
659 SCM
660 Beam::calc_x_positions (SCM smob)
661 {
662   Spanner *me = unsmob_spanner (smob);
663   SCM segments = me->get_property ("beam-segments");
664   Interval x_positions;
665   x_positions.set_empty ();
666   for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
667     x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
668                                                           scm_car (s),
669                                                           SCM_EOL),
670                                             Interval (0.0, 0.0)));
671
672   // Case for beams without segments (i.e. uniting two skips with a beam)
673   // TODO: should issue a warning?  warning likely issued downstream, but couldn't hurt...
674   if (x_positions.is_empty ())
675     {
676       extract_grob_set (me, "stems", stems);
677       Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
678       for (LEFT_and_RIGHT (d))
679         x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
680     }
681   return ly_interval2scm (x_positions);
682 }
683
684 vector<Beam_segment>
685 Beam::get_beam_segments (Grob *me)
686 {
687   SCM segments_scm = me->get_property ("beam-segments");
688   vector<Beam_segment> segments;
689   for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
690     {
691       segments.push_back (Beam_segment ());
692       segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
693       segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
694     }
695
696   return segments;
697 }
698
699 MAKE_SCHEME_CALLBACK (Beam, print, 1);
700 SCM
701 Beam::print (SCM grob)
702 {
703   Spanner *me = unsmob_spanner (grob);
704   /*
705     TODO - mild code dup for all the commonx calls.
706     Some use just common_refpoint_of_array, some (in print and
707     calc_beam_segments) use this plus calls to get_bound.
708
709     Figure out if there is any particular reason for this and
710     consolidate in one Beam::get_common function.
711   */
712   extract_grob_set (me, "stems", stems);
713   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
714   for (LEFT_and_RIGHT (d))
715     commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
716
717   vector<Beam_segment> segments = get_beam_segments (me);
718
719   if (!segments.size ())
720     return SCM_EOL;
721
722   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
723
724   SCM posns = me->get_property ("quantized-positions");
725   Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
726   Interval pos;
727   if (!is_number_pair (posns))
728     {
729       programming_error ("no beam positions?");
730       pos = Interval (0, 0);
731     }
732   else
733     pos = ly_scm2realdrul (posns);
734
735   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
736
737   Real dy = pos[RIGHT] - pos[LEFT];
738   Real slope = (dy && span.length ()) ? dy / span.length () : 0;
739
740   Real beam_thickness = get_beam_thickness (me);
741   Real beam_dy = get_beam_translation (me);
742
743   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
744
745   Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
746
747   Stencil the_beam;
748   int extreme = (segments[0].vertical_count_ == 0
749                  ? segments[0].vertical_count_
750                  : segments.back ().vertical_count_);
751
752   for (vsize i = 0; i < segments.size (); i++)
753     {
754       Real local_slope = slope;
755       /*
756         Makes local slope proportional to the ratio of the length of this beam
757         to the total length.
758       */
759       if (feather_dir)
760         local_slope += (feather_dir * segments[i].vertical_count_
761                         * beam_dy
762                         * placements.length ()
763                         / span.length ());
764
765       Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
766
767       b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
768       Real multiplier = feather_dir ? placements[LEFT] : 1.0;
769
770       Interval weights (1 - multiplier, multiplier);
771
772       if (feather_dir != LEFT)
773         weights.swap ();
774
775       // we need two translations: the normal one and
776       // the one of the lowest segment
777       size_t idx[] = {i, extreme};
778       Real translations[2];
779
780       for (int j = 0; j < 2; j++)
781         translations[j] = slope
782                           * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
783                           + pos.linear_combination (CENTER)
784                           + beam_dy * segments[idx[j]].vertical_count_;
785
786       Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
787
788       /*
789         Tricky.  The manipulation of the variable `weighted_average' below ensures
790         that beams with a RIGHT grow direction will start from the position of the
791         lowest segment at 0, and this error will decrease and decrease over the
792         course of the beam.  Something with a LEFT grow direction, on the other
793         hand, will always start in the correct place but progressively accrue
794         error at broken places.  This code shifts beams up given where they are
795         in the total span length (controlled by the variable `multiplier').  To
796         better understand what it does, try commenting it out: you'll see that
797         all of the RIGHT growing beams immediately start too low and get better
798         over line breaks, whereas all of the LEFT growing beams start just right
799         and get worse over line breaks.
800       */
801       Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
802
803       if (segments[0].vertical_count_ < 0 && feather_dir)
804         weighted_average += beam_dy * (segments.size () - 1) * factor;
805
806       b.translate_axis (weighted_average, Y_AXIS);
807
808       the_beam.add_stencil (b);
809
810     }
811
812 #if (DEBUG_BEAM_SCORING)
813   SCM annotation = me->get_property ("annotation");
814   if (scm_is_string (annotation))
815     {
816       extract_grob_set (me, "stems", stems);
817
818       /*
819         This code prints the demerits for each beam. Perhaps this
820         should be switchable for those who want to twiddle with the
821         parameters.
822       */
823       string str;
824       SCM properties = Font_interface::text_font_alist_chain (me);
825
826       properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
827                              properties);
828
829       Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
830
831       Stencil score = *unsmob_stencil (Text_interface::interpret_markup
832                                        (me->layout ()->self_scm (), properties, annotation));
833
834       if (!score.is_empty ())
835         {
836           score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
837           the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
838         }
839     }
840 #endif
841
842   the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
843   return the_beam.smobbed_copy ();
844 }
845
846 Direction
847 Beam::get_default_dir (Grob *me)
848 {
849   extract_grob_set (me, "stems", stems);
850
851   Drul_array<Real> extremes (0.0, 0.0);
852   for (iterof (s, stems); s != stems.end (); s++)
853     {
854       Interval positions = Stem::head_positions (*s);
855       for (DOWN_and_UP (d))
856         {
857           if (sign (positions[d]) == d)
858             extremes[d] = d * max (d * positions[d], d * extremes[d]);
859         }
860     }
861
862   Drul_array<int> total (0, 0);
863   Drul_array<int> count (0, 0);
864
865   bool force_dir = false;
866   for (vsize i = 0; i < stems.size (); i++)
867     {
868       Grob *s = stems[i];
869       Direction stem_dir = CENTER;
870       SCM stem_dir_scm = s->get_property_data ("direction");
871       if (is_direction (stem_dir_scm))
872         {
873           stem_dir = to_dir (stem_dir_scm);
874           force_dir = true;
875         }
876       else
877         stem_dir = to_dir (s->get_property ("default-direction"));
878
879       if (!stem_dir)
880         stem_dir = to_dir (s->get_property ("neutral-direction"));
881
882       if (stem_dir)
883         {
884           count[stem_dir]++;
885           total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
886         }
887     }
888
889   if (!force_dir)
890     {
891       if (abs (extremes[UP]) > -extremes[DOWN])
892         return DOWN;
893       else if (extremes[UP] < -extremes[DOWN])
894         return UP;
895     }
896
897   Direction dir = CENTER;
898   Direction d = CENTER;
899   if ((d = (Direction) sign (count[UP] - count[DOWN])))
900     dir = d;
901   else if (count[UP]
902            && count[DOWN]
903            && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
904     dir = d;
905   else if ((d = (Direction) sign (total[UP] - total[DOWN])))
906     dir = d;
907   else
908     dir = to_dir (me->get_property ("neutral-direction"));
909
910   return dir;
911 }
912
913 /* Set all stems with non-forced direction to beam direction.
914    Urg: non-forced should become `without/with unforced' direction,
915    once stem gets cleaned-up. */
916 void
917 Beam::set_stem_directions (Grob *me, Direction d)
918 {
919   extract_grob_set (me, "stems", stems);
920
921   for (vsize i = 0; i < stems.size (); i++)
922     {
923       Grob *s = stems[i];
924
925       SCM forcedir = s->get_property_data ("direction");
926       if (!to_dir (forcedir))
927         set_grob_direction (s, d);
928     }
929 }
930
931 /*
932   Only try horizontal beams for knees.  No reliable detection of
933   anything else is possible here, since we don't know funky-beaming
934   settings, or X-distances (slopes!)  People that want sloped
935   knee-beams, should set the directions manually.
936
937
938   TODO:
939
940   this routine should take into account the stemlength scoring
941   of a possible knee/nonknee beam.
942 */
943 void
944 Beam::consider_auto_knees (Grob *me)
945 {
946   SCM scm = me->get_property ("auto-knee-gap");
947   if (!scm_is_number (scm))
948     return;
949
950   vector<Interval> forbidden_intervals;
951
952   extract_grob_set (me, "normal-stems", stems);
953
954   Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
955   Real staff_space = Staff_symbol_referencer::staff_space (me);
956
957   vector<Interval> head_extents_array;
958   for (vsize i = 0; i < stems.size (); i++)
959     {
960       Grob *stem = stems[i];
961
962       Interval head_extents = Stem::head_positions (stem);
963       if (!head_extents.is_empty ())
964         {
965           head_extents[LEFT] += -1;
966           head_extents[RIGHT] += 1;
967           head_extents *= staff_space * 0.5;
968
969           /*
970             We could subtract beam Y position, but this routine only
971             sets stem directions, a constant shift does not have an
972             influence.
973           */
974           head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
975
976           if (to_dir (stem->get_property_data ("direction")))
977             {
978               Direction stemdir = to_dir (stem->get_property ("direction"));
979               head_extents[-stemdir] = -stemdir * infinity_f;
980             }
981         }
982       head_extents_array.push_back (head_extents);
983
984       forbidden_intervals.push_back (head_extents);
985     }
986
987   Interval max_gap;
988   Real max_gap_len = 0.0;
989
990   vector<Interval> allowed_regions
991     = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
992   for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
993     {
994       Interval gap = allowed_regions[i];
995
996       /*
997         the outer gaps are not knees.
998       */
999       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
1000         continue;
1001
1002       if (gap.length () >= max_gap_len)
1003         {
1004           max_gap_len = gap.length ();
1005           max_gap = gap;
1006         }
1007     }
1008
1009   Real beam_translation = get_beam_translation (me);
1010   Real beam_thickness = Beam::get_beam_thickness (me);
1011   int beam_count = Beam::get_beam_count (me);
1012   Real height_of_beams = beam_thickness / 2
1013                          + (beam_count - 1) * beam_translation;
1014   Real threshold = scm_to_double (scm) + height_of_beams;
1015
1016   if (max_gap_len > threshold)
1017     {
1018       int j = 0;
1019       for (vsize i = 0; i < stems.size (); i++)
1020         {
1021           Grob *stem = stems[i];
1022           Interval head_extents = head_extents_array[j++];
1023
1024           Direction d = (head_extents.center () < max_gap.center ())
1025                         ? UP : DOWN;
1026
1027           stem->set_property ("direction", scm_from_int (d));
1028
1029           head_extents.intersect (max_gap);
1030           assert (head_extents.is_empty () || head_extents.length () < 1e-6);
1031         }
1032     }
1033 }
1034
1035 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
1036 SCM
1037 Beam::calc_stem_shorten (SCM smob)
1038 {
1039   Grob *me = unsmob_grob (smob);
1040
1041   /*
1042     shortening looks silly for x staff beams
1043   */
1044   if (is_knee (me))
1045     return scm_from_int (0);
1046
1047   Real forced_fraction = 1.0 * forced_stem_count (me)
1048                          / normal_stem_count (me);
1049
1050   int beam_count = get_beam_count (me);
1051
1052   SCM shorten_list = me->get_property ("beamed-stem-shorten");
1053   if (shorten_list == SCM_EOL)
1054     return scm_from_int (0);
1055
1056   Real staff_space = Staff_symbol_referencer::staff_space (me);
1057
1058   SCM shorten_elt
1059     = robust_list_ref (beam_count - 1, shorten_list);
1060   Real shorten = scm_to_double (shorten_elt) * staff_space;
1061
1062   shorten *= forced_fraction;
1063
1064   if (shorten)
1065     return scm_from_double (shorten);
1066
1067   return scm_from_double (0.0);
1068 }
1069
1070 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
1071 SCM
1072 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
1073 {
1074   Grob *me = unsmob_grob (smob);
1075   Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
1076   bool cbs = to_boolean (align_broken_intos);
1077
1078   Beam_scoring_problem problem (me, ys, cbs);
1079   ys = problem.solve ();
1080
1081   return ly_interval2scm (ys);
1082 }
1083
1084 /*
1085   Report slice containing the numbers that are both in (car BEAMING)
1086   and (cdr BEAMING)
1087 */
1088 Slice
1089 where_are_the_whole_beams (SCM beaming)
1090 {
1091   Slice l;
1092
1093   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1094     {
1095       if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1096
1097         l.add_point (scm_to_int (scm_car (s)));
1098     }
1099
1100   return l;
1101 }
1102
1103 /* Return the Y position of the stem-end, given the Y-left, Y-right
1104    in POS for stem S.  This Y position is relative to S. */
1105 Real
1106 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1107                    Real xl, Real xr, Direction feather_dir,
1108                    Drul_array<Real> pos, bool french)
1109 {
1110   Real beam_translation = get_beam_translation (me);
1111   Direction stem_dir = get_grob_direction (stem);
1112
1113   Real dx = xr - xl;
1114   Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1115   Real xdir = 2 * relx - 1;
1116
1117   Real stem_y = linear_combination (pos, xdir);
1118
1119   SCM beaming = stem->get_property ("beaming");
1120
1121   Slice beam_slice (french
1122                     ? where_are_the_whole_beams (beaming)
1123                     : Stem::beam_multiplicity (stem));
1124   if (beam_slice.is_empty ())
1125     beam_slice = Slice (0, 0);
1126   Interval beam_multiplicity (beam_slice[LEFT],
1127                               beam_slice[RIGHT]);
1128
1129   /*
1130     feather dir = 1 , relx 0->1 : factor 0 -> 1
1131     feather dir = 0 , relx 0->1 : factor 1 -> 1
1132     feather dir = -1, relx 0->1 : factor 1 -> 0
1133    */
1134   Real feather_factor = 1;
1135   if (feather_dir > 0)
1136     feather_factor = relx;
1137   else if (feather_dir < 0)
1138     feather_factor = 1 - relx;
1139
1140   stem_y += feather_factor * beam_translation
1141             * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1142   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1143             - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1144
1145   return stem_y + id;
1146 }
1147
1148 /*
1149   Hmm.  At this time, beam position and slope are determined.  Maybe,
1150   stem directions and length should set to relative to the chord's
1151   position of the beam.  */
1152 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1153 SCM
1154 Beam::set_stem_lengths (SCM smob)
1155 {
1156   Grob *me = unsmob_grob (smob);
1157
1158   /* trigger callbacks. */
1159   (void) me->get_property ("direction");
1160   (void) me->get_property ("beaming");
1161
1162   SCM posns = me->get_property ("positions");
1163
1164   extract_grob_set (me, "stems", stems);
1165   if (!stems.size ())
1166     return posns;
1167
1168   Grob *common[2];
1169   for (int a = 2; a--;)
1170     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1171
1172   Drul_array<Real> pos = ly_scm2realdrul (posns);
1173   Real staff_space = Staff_symbol_referencer::staff_space (me);
1174   scale_drul (&pos, staff_space);
1175
1176   bool gap = false;
1177   Real thick = 0.0;
1178   if (robust_scm2int (me->get_property ("gap-count"), 0))
1179     {
1180       gap = true;
1181       thick = get_beam_thickness (me);
1182     }
1183
1184   Grob *fvs = first_normal_stem (me);
1185   Grob *lvs = last_normal_stem (me);
1186
1187   Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1188   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1189
1190   for (vsize i = 0; i < stems.size (); i++)
1191     {
1192       Grob *s = stems[i];
1193
1194       bool french = to_boolean (s->get_property ("french-beaming"));
1195       Real stem_y = calc_stem_y (me, s, common,
1196                                  x_span[LEFT], x_span[RIGHT], feather_dir,
1197                                  pos, french && s != lvs && s != fvs);
1198
1199       /*
1200         Make the stems go up to the end of the beam. This doesn't matter
1201         for normal beams, but for tremolo beams it looks silly otherwise.
1202       */
1203       if (gap
1204           && !Stem::is_invisible (s))
1205         stem_y += thick * 0.5 * get_grob_direction (s);
1206
1207       /*
1208         Do set_stem_positions for invisible stems too, so tuplet brackets
1209         have a reference point for sloping
1210        */
1211       Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1212     }
1213
1214   return posns;
1215 }
1216
1217 void
1218 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1219 {
1220   extract_grob_set (me, "stems", stems);
1221
1222   for (vsize i = 0; i < stems.size (); i++)
1223     {
1224       /*
1225         Don't overwrite user settings.
1226       */
1227       for (LEFT_and_RIGHT (d))
1228         {
1229           Grob *stem = stems[i];
1230           SCM beaming_prop = stem->get_property ("beaming");
1231           if (beaming_prop == SCM_EOL
1232               || index_get_cell (beaming_prop, d) == SCM_EOL)
1233             {
1234               int count = beaming->beamlet_count (i, d);
1235               if (i > 0
1236                   && i + 1 < stems.size ()
1237                   && Stem::is_invisible (stem))
1238                 count = min (count, beaming->beamlet_count (i, -d));
1239
1240               if ( ((i == 0 && d == LEFT)
1241                     || (i == stems.size () - 1 && d == RIGHT))
1242                    && stems.size () > 1
1243                    && to_boolean (me->get_property ("clip-edges")))
1244                 count = 0;
1245
1246               Stem::set_beaming (stem, count, d);
1247             }
1248         }
1249     }
1250 }
1251
1252 int
1253 Beam::forced_stem_count (Grob *me)
1254 {
1255   extract_grob_set (me, "normal-stems", stems);
1256
1257   int f = 0;
1258   for (vsize i = 0; i < stems.size (); i++)
1259     {
1260       Grob *s = stems[i];
1261
1262       /* I can imagine counting those boundaries as a half forced stem,
1263          but let's count them full for now. */
1264       Direction defdir = to_dir (s->get_property ("default-direction"));
1265
1266       if (abs (Stem::chord_start_y (s)) > 0.1
1267           && defdir
1268           && get_grob_direction (s) != defdir)
1269         f++;
1270     }
1271   return f;
1272 }
1273
1274 int
1275 Beam::normal_stem_count (Grob *me)
1276 {
1277   extract_grob_set (me, "normal-stems", stems);
1278   return stems.size ();
1279 }
1280
1281 Grob *
1282 Beam::first_normal_stem (Grob *me)
1283 {
1284   extract_grob_set (me, "normal-stems", stems);
1285   return stems.size () ? stems[0] : 0;
1286 }
1287
1288 Grob *
1289 Beam::last_normal_stem (Grob *me)
1290 {
1291   extract_grob_set (me, "normal-stems", stems);
1292   return stems.size () ? stems.back () : 0;
1293 }
1294
1295 /*
1296   [TODO]
1297
1298   handle rest under beam (do_post: beams are calculated now)
1299   what about combination of collisions and rest under beam.
1300
1301   Should lookup
1302
1303   rest -> stem -> beam -> interpolate_y_position ()
1304 */
1305 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1306 SCM
1307 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1308 {
1309   Grob *rest = unsmob_grob (smob);
1310   if (scm_is_number (rest->get_property ("staff-position")))
1311     return scm_from_int (0);
1312
1313   Real offset = robust_scm2double (prev_offset, 0.0);
1314
1315   Grob *st = unsmob_grob (rest->get_object ("stem"));
1316   Grob *stem = st;
1317   if (!stem)
1318     return scm_from_double (0.0);
1319   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1320   if (!beam
1321       || !Beam::has_interface (beam)
1322       || !Beam::normal_stem_count (beam))
1323     return scm_from_double (0.0);
1324
1325   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1326
1327   Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1328                                          Drul_array<Real> (0, 0)));
1329
1330   for (LEFT_and_RIGHT (dir))
1331     pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1332
1333   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1334
1335   scale_drul (&pos, staff_space);
1336
1337   Real dy = pos[RIGHT] - pos[LEFT];
1338
1339   extract_grob_set (beam, "stems", stems);
1340   Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1341
1342   Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1343                                          Interval (0.0, 0.0));
1344   Real x0 = x_span[LEFT];
1345   Real dx = x_span.length ();
1346   Real slope = dy && dx ? dy / dx : 0;
1347
1348   Direction d = get_grob_direction (stem);
1349   Real stem_y = pos[LEFT]
1350                 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1351
1352   Real beam_translation = get_beam_translation (beam);
1353   Real beam_thickness = Beam::get_beam_thickness (beam);
1354
1355   /*
1356     TODO: this is not strictly correct for 16th knee beams.
1357   */
1358   int beam_count
1359     = Stem::beam_multiplicity (stem).length () + 1;
1360
1361   Real height_of_my_beams = beam_thickness / 2
1362                             + (beam_count - 1) * beam_translation;
1363   Real beam_y = stem_y - d * height_of_my_beams;
1364
1365   Interval rest_extent = rest->extent (rest, Y_AXIS);
1366   rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1367
1368   Real rest_dim = rest_extent[d];
1369   Real minimum_distance
1370     = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1371                      + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1372
1373   Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1374
1375   shift /= staff_space;
1376
1377   /* Always move discretely by half spaces */
1378   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1379
1380   Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1381   staff_span *= staff_space / 2;
1382
1383   /* Inside staff, move by whole spaces*/
1384   if (staff_span.contains (rest_extent[d] + staff_space * shift)
1385       || staff_span.contains (rest_extent[-d] + staff_space * shift))
1386     shift = ceil (fabs (shift)) * sign (shift);
1387
1388   return scm_from_double (offset + staff_space * shift);
1389 }
1390
1391 /*
1392   Estimate the position of a rest under a beam,
1393   as the average position of its neighboring heads.
1394 */
1395 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1396 SCM
1397 Beam::pure_rest_collision_callback (SCM smob,
1398                                     SCM, /* start */
1399                                     SCM, /* end */
1400                                     SCM prev_offset)
1401 {
1402   Real previous = robust_scm2double (prev_offset, 0.0);
1403
1404   Grob *me = unsmob_grob (smob);
1405   Grob *stem = unsmob_grob (me->get_object ("stem"));
1406   if (!stem)
1407     return scm_from_double (previous);
1408   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1409   if (!beam
1410       || !Beam::normal_stem_count (beam)
1411       || !is_direction (beam->get_property_data ("direction")))
1412     return scm_from_double (previous);
1413
1414   Real ss = Staff_symbol_referencer::staff_space (me);
1415
1416   /*
1417     This gives the extrema of rest positions.
1418     Even with noteheads on ledgers, beams typically remain within the staff,
1419     and push rests at most one staff-space (2 positions) from the staff.
1420   */
1421   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1422   Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1423   rest_max_pos.widen (2);
1424
1425   extract_grob_set (beam, "stems", stems);
1426   vector<Grob *> my_stems;
1427
1428   for (vsize i = 0; i < stems.size (); i++)
1429     if (Stem::head_count (stems[i]) || stems[i] == stem)
1430       my_stems.push_back (stems[i]);
1431
1432   vsize idx = -1;
1433
1434   for (vsize i = 0; i < my_stems.size (); i++)
1435     if (my_stems[i] == stem)
1436       {
1437         idx = i;
1438         break;
1439       }
1440   Grob *left;
1441   Grob *right;
1442
1443   if (idx == (vsize) - 1 || my_stems.size () == 1)
1444     return scm_from_double (previous);
1445   else if (idx == 0)
1446     left = right = my_stems[1];
1447   else if (idx == my_stems.size () - 1)
1448     left = right = my_stems[idx - 1];
1449   else
1450     {
1451       left = my_stems[idx - 1];
1452       right = my_stems[idx + 1];
1453     }
1454
1455   /* In stems with several heads, use the one closest to the beam. */
1456   Direction beamdir = get_grob_direction (beam);
1457   Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1458                            + Stem::head_positions (right)[beamdir]) / 2.0,
1459                           rest_max_pos[DOWN]),
1460                     rest_max_pos[UP]
1461                    ) * ss / 2.0
1462                - previous;
1463
1464   // So that ceil below kicks in for rests that would otherwise brush
1465   // up against a beam quanted to a ledger line, add a bit of space
1466   // between the beam and the rest.
1467   shift += (0.01 * beamdir);
1468
1469   /* Always move by a whole number of staff spaces */
1470   shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1471
1472   return scm_from_double (previous + shift);
1473 }
1474
1475 bool
1476 Beam::is_knee (Grob *me)
1477 {
1478   SCM k = me->get_property ("knee");
1479   if (scm_is_bool (k))
1480     return ly_scm2bool (k);
1481
1482   bool knee = false;
1483   int d = 0;
1484   extract_grob_set (me, "stems", stems);
1485   for (vsize i = stems.size (); i--;)
1486     {
1487       Direction dir = get_grob_direction (stems[i]);
1488       if (d && d != dir)
1489         {
1490           knee = true;
1491           break;
1492         }
1493       d = dir;
1494     }
1495
1496   me->set_property ("knee", ly_bool2scm (knee));
1497
1498   return knee;
1499 }
1500
1501 bool
1502 Beam::is_cross_staff (Grob *me)
1503 {
1504   extract_grob_set (me, "stems", stems);
1505   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1506   for (vsize i = 0; i < stems.size (); i++)
1507     if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1508       return true;
1509   return false;
1510 }
1511
1512 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1513 SCM
1514 Beam::calc_cross_staff (SCM smob)
1515 {
1516   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1517 }
1518
1519 int
1520 Beam::get_direction_beam_count (Grob *me, Direction d)
1521 {
1522   extract_grob_set (me, "stems", stems);
1523   int bc = 0;
1524
1525   for (vsize i = stems.size (); i--;)
1526     {
1527       /*
1528         Should we take invisible stems into account?
1529       */
1530       if (get_grob_direction (stems[i]) == d)
1531         bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1532     }
1533
1534   return bc;
1535 }
1536
1537 ADD_INTERFACE (Beam,
1538                "A beam.\n"
1539                "\n"
1540                "The @code{beam-thickness} property is the weight of beams,"
1541                " measured in staffspace.  The @code{direction} property is"
1542                " not user-serviceable.  Use the @code{direction} property"
1543                " of @code{Stem} instead.\n"
1544                "The following properties may be set in the @code{details}"
1545                " list.\n"
1546                "\n"
1547                "@table @code\n"
1548                "@item stem-length-demerit-factor\n"
1549                "Demerit factor used for inappropriate stem lengths.\n"
1550                "@item secondary-beam-demerit\n"
1551                "Demerit used in quanting calculations for multiple"
1552                " beams.\n"
1553                "@item region-size\n"
1554                "Size of region for checking quant scores.\n"
1555                "@item beam-eps\n"
1556                "Epsilon for beam quant code to check for presence"
1557                " in gap.\n"
1558                "@item stem-length-limit-penalty\n"
1559                "Penalty for differences in stem lengths on a beam.\n"
1560                "@item damping-direction-penalty\n"
1561                "Demerit penalty applied when beam direction is different"
1562                " from damping direction.\n"
1563                "@item hint-direction-penalty\n"
1564                "Demerit penalty applied when beam direction is different"
1565                " from damping direction, but damping slope is"
1566                " <= @code{round-to-zero-slope}.\n"
1567                "@item musical-direction-factor\n"
1568                "Demerit scaling factor for difference between"
1569                " beam slope and music slope.\n"
1570                "@item ideal-slope-factor\n"
1571                "Demerit scaling factor for difference between"
1572                " beam slope and damping slope.\n"
1573                "@item round-to-zero-slope\n"
1574                "Damping slope which is considered zero for purposes of"
1575                " calculating direction penalties.\n"
1576                "@end table\n",
1577
1578                /* properties */
1579                "annotation "
1580                "auto-knee-gap "
1581                "beamed-stem-shorten "
1582                "beaming "
1583                "beam-gap "
1584                "beam-segments "
1585                "beam-thickness "
1586                "break-overshoot "
1587                "clip-edges "
1588                "concaveness "
1589                "collision-interfaces "
1590                "collision-voice-only "
1591                "covered-grobs "
1592                "damping "
1593                "details "
1594                "direction "
1595                "gap-count "
1596                "grow-direction "
1597                "inspect-quants "
1598                "knee "
1599                "length-fraction "
1600                "least-squares-dy "
1601                "neutral-direction "
1602                "normal-stems "
1603                "positions "
1604                "quantized-positions "
1605                "shorten "
1606                "skip-quanting "
1607                "stems "
1608                "X-positions "
1609               );