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