2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
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.
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.
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/>.
24 - Determine auto knees based on positions if it's set by the user.
26 - the code is littered with * and / staff_space calls for
27 #'positions. Consider moving to real-world coordinates?
29 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
33 - Stems run to the Y-center of the beam.
35 - beam_translation is the offset between Y centers of the beam.
40 #include "beam-scoring-problem.hh"
41 #include "beaming-pattern.hh"
42 #include "directional-element-interface.hh"
43 #include "grob-array.hh"
44 #include "international.hh"
45 #include "interval-set.hh"
47 #include "least-squares.hh"
51 #include "note-head.hh"
52 #include "output-def.hh"
53 #include "pointer-group-interface.hh"
54 #include "rhythmic-head.hh"
56 #include "staff-symbol-referencer.hh"
60 #if DEBUG_BEAM_SCORING
61 #include "text-interface.hh" // debug output.
62 #include "font-interface.hh" // debug output.
68 Beam_stem_segment::Beam_stem_segment ()
70 max_connect_ = 1000; // infinity
80 beam_segment_less (Beam_segment const& a, Beam_segment const& b)
82 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
85 Beam_segment::Beam_segment ()
91 Beam::add_stem (Grob *me, Grob *s)
93 if (Stem::get_beam (s))
95 programming_error ("Stem already has beam");
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));
105 Beam::get_beam_thickness (Grob *me)
107 return robust_scm2double (me->get_property ("beam-thickness"), 0)
108 * Staff_symbol_referencer::staff_space (me);
111 /* Return the translation between 2 adjoining beams. */
113 Beam::get_beam_translation (Grob *me)
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);
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;
125 return fract * beam_translation;
128 /* Maximum beam_count. */
130 Beam::get_beam_count (Grob *me)
134 extract_grob_set (me, "stems", stems);
135 for (vsize i = 0; i < stems.size (); i++)
137 Grob *stem = stems[i];
138 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
145 Beam::calc_normal_stems (SCM smob)
147 Grob *me = unsmob_grob (smob);
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]))
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
161 Beam::calc_direction (SCM smob)
163 Grob *me = unsmob_grob (smob);
165 /* Beams with less than 2 two stems don't make much sense, but could happen
172 Direction dir = CENTER;
174 int count = normal_stem_count (me);
177 extract_grob_set (me, "stems", stems);
178 if (stems.size () == 0)
180 me->warning (_ ("removing beam with no stems"));
183 return SCM_UNSPECIFIED;
187 Grob *stem = first_normal_stem (me);
190 This happens for chord tremolos.
195 if (is_direction (stem->get_property_data ("direction")))
196 dir = to_dir (stem->get_property_data ("direction"));
198 dir = to_dir (stem->get_property ("default-direction"));
205 dir = get_default_dir (me);
207 consider_auto_knees (me);
212 set_stem_directions (me, dir);
215 return scm_from_int (dir);
220 /* We want a maximal number of shared beams, but if there is choice, we
221 * take the one that is closest to the end of the stem. This is for
233 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
237 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
241 for (int i = lslice[-left_dir];
242 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
245 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
247 int k = -right_dir * scm_to_int (scm_car (s)) + i;
248 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
252 if (count >= best_count)
262 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
264 Beam::calc_beaming (SCM smob)
266 Grob *me = unsmob_grob (smob);
268 extract_grob_set (me, "stems", stems);
271 last_int.set_empty ();
273 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
274 Direction last_dir = CENTER;
275 for (vsize i = 0; i < stems.size (); i++)
277 Grob *this_stem = stems[i];
278 SCM this_beaming = this_stem->get_property ("beaming");
280 Direction this_dir = get_grob_direction (this_stem);
281 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
283 int start_point = position_with_maximal_common_beams
284 (last_beaming, this_beaming,
285 last_dir ? last_dir : this_dir,
292 new_slice.set_empty ();
293 SCM s = index_get_cell (this_beaming, d);
294 for (; scm_is_pair (s); s = scm_cdr (s))
297 = start_point - this_dir * scm_to_int (scm_car (s));
299 new_slice.add_point (new_beam_pos);
300 scm_set_car_x (s, scm_from_int (new_beam_pos));
303 while (flip (&d) != LEFT);
305 if (!new_slice.is_empty ())
306 last_int = new_slice;
311 FIXME: what's this for?
313 SCM s = scm_cdr (this_beaming);
314 for (; scm_is_pair (s); s = scm_cdr (s))
316 int np = -this_dir * scm_to_int (scm_car (s));
317 scm_set_car_x (s, scm_from_int (np));
318 last_int.add_point (np);
322 if (scm_ilength (scm_cdr (this_beaming)) > 0)
324 last_beaming = this_beaming;
333 operator <(Beam_stem_segment const &a,
334 Beam_stem_segment const &b)
336 return a.rank_ < b.rank_;
339 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
341 // TODO - should store result in a property?
343 Beam::get_beam_segments (Grob *me_grob, Grob **common)
345 /* ugh, this has a side-effect that we need to ensure that
346 Stem #'beaming is correct */
347 (void) me_grob->get_property ("beaming");
349 Spanner *me = dynamic_cast<Spanner*> (me_grob);
351 extract_grob_set (me, "stems", stems);
352 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
354 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
355 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
359 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
360 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
362 Position_stem_segments_map stem_segments;
363 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
365 /* There are two concepts of "rank" that are used in the following code.
366 The beam_rank is the vertical position of the beam (larger numbers are
367 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
368 is the horizontal position of the segment (this is incremented by two
369 for each stem; the beam segment on the right side of the stem has
370 a higher rank (by one) than its neighbour to the left). */
372 for (vsize i = 0; i < stems.size (); i++)
374 Grob *stem = stems[i];
375 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
376 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
377 SCM beaming = stem->get_property ("beaming");
381 // Find the maximum and minimum beam ranks.
382 // Given that RANKS is never reset to empty, the interval will always be
383 // smallest for the left beamlet of the first stem, and then it might grow.
384 // Do we really want this? (It only affects the tremolo gaps) --jneem
385 for (SCM s = index_get_cell (beaming, d);
386 scm_is_pair (s); s = scm_cdr (s))
388 if (!scm_is_integer (scm_car (s)))
391 int beam_rank = scm_to_int (scm_car (s));
392 ranks.add_point (beam_rank);
395 for (SCM s = index_get_cell (beaming, d);
396 scm_is_pair (s); s = scm_cdr (s))
398 if (!scm_is_integer (scm_car (s)))
401 int beam_rank = scm_to_int (scm_car (s));
402 Beam_stem_segment seg;
404 seg.stem_x_ = stem_x;
405 seg.rank_ = 2 * i + (d+1)/2;
406 seg.width_ = stem_width;
409 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
411 Direction stem_dir = get_grob_direction (stem);
414 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
415 stem_segments[beam_rank].push_back (seg);
418 while (flip (&d) != LEFT);
421 Drul_array<Real> break_overshoot
422 = robust_scm2drul (me->get_property ("break-overshoot"),
423 Drul_array<Real> (-0.5, 0.0));
425 vector<Beam_segment> segments;
426 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
427 i != stem_segments.end (); i++)
429 vector<Beam_stem_segment> segs = (*i).second;
430 vector_sort (segs, less<Beam_stem_segment> ());
432 Beam_segment current;
434 // Iterate over all of the segments of the current beam rank,
435 // merging the adjacent Beam_stem_segments into one Beam_segment
437 int vertical_count = (*i).first;
438 for (vsize j = 0; j < segs.size (); j++)
440 // Keeping track of the different directions here is a little tricky.
441 // segs[j].dir_ is the direction of the beam segment relative to the stem
442 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
443 // its stem) whereas event_dir refers to the edge of the beam segment that
444 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
445 // are looking at that edge of the beam segment that is furthest from its
447 Direction event_dir = LEFT;
448 Beam_stem_segment const& seg = segs[j];
451 Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
452 // TODO: make names clearer? --jneem
453 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
454 // on_beam_bound: whether the current segment is on the boundary of just that part
455 // of the beam with the current beam_rank
456 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
457 : seg.stem_index_ == stems.size() - 1;
458 bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
459 j == segs.size () - 1;
460 bool inside_stem = (event_dir == LEFT)
461 ? seg.stem_index_ > 0
462 : seg.stem_index_ + 1 < stems.size () ;
464 bool event = on_beam_bound
465 || abs (seg.rank_ - neighbor_seg.rank_) > 1
466 || (abs (vertical_count) >= seg.max_connect_
467 || abs (vertical_count) >= neighbor_seg.max_connect_);
470 // Then this edge of the current segment is irrelevent because it will
471 // be connected with the next segment in the event_dir direction.
474 current.vertical_count_ = vertical_count;
475 current.horizontal_[event_dir] = seg.stem_x_;
476 if (seg.dir_ == event_dir)
477 // then we are examining the edge of a beam segment that is furthest
481 && me->get_bound (event_dir)->break_status_dir ())
483 current.horizontal_[event_dir]
484 = (robust_relative_extent (me->get_bound (event_dir),
485 commonx, X_AXIS)[RIGHT]
486 + event_dir * break_overshoot[event_dir]);
490 Grob *stem = stems[seg.stem_index_];
491 Drul_array<Real> beamlet_length =
492 robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
493 Drul_array<Real> max_proportion =
494 robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
495 Real length = beamlet_length[seg.dir_];
499 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
500 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
502 length = min (length,
503 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
505 current.horizontal_[event_dir] += event_dir * length;
509 // we are examining the edge of a beam segment that is closest
510 // (ie. touching, unless there is a gap) its stem.
512 current.horizontal_[event_dir] += event_dir * seg.width_/2;
515 current.horizontal_[event_dir] -= event_dir * gap_length;
517 if (Stem::is_invisible (seg.stem_))
520 Need to do this in case of whole notes. We don't want the
521 heads to collide with the beams.
523 extract_grob_set (seg.stem_, "note-heads", heads);
525 for (vsize k = 0; k < heads.size (); k ++)
526 current.horizontal_[event_dir]
527 = event_dir * min (event_dir * current.horizontal_[event_dir],
530 * heads[k]->extent (commonx,
531 X_AXIS)[-event_dir]);
536 if (event_dir == RIGHT)
538 segments.push_back (current);
539 current = Beam_segment ();
542 while (flip (&event_dir) != LEFT);
550 MAKE_SCHEME_CALLBACK (Beam, print, 1);
552 Beam::print (SCM grob)
554 Spanner *me = unsmob_spanner (grob);
556 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
559 if (normal_stem_count (me))
561 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
562 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
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);
571 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
573 SCM posns = me->get_property ("quantized-positions");
575 if (!is_number_pair (posns))
577 programming_error ("no beam positions?");
578 pos = Interval (0, 0);
581 pos = ly_scm2realdrul (posns);
583 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
585 Real dy = pos[RIGHT] - pos[LEFT];
586 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
588 Real beam_thickness = get_beam_thickness (me);
589 Real beam_dy = get_beam_translation (me);
591 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
593 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
597 int extreme = (segments[0].vertical_count_ == 0
598 ? segments[0].vertical_count_
599 : segments.back ().vertical_count_);
601 for (vsize i = 0; i < segments.size (); i ++)
603 Real local_slope = slope;
605 Makes local slope proportional to the ratio of the length of this beam
609 local_slope += (feather_dir * segments[i].vertical_count_
611 * placements.length ()
614 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
616 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
617 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
619 Interval weights (1 - multiplier, multiplier);
621 if (feather_dir != LEFT)
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];
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_;
635 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
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.
650 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
652 if (segments[0].vertical_count_ < 0 && feather_dir)
653 weighted_average += beam_dy * (segments.size () - 1) * factor;
655 b.translate_axis (weighted_average, Y_AXIS);
657 the_beam.add_stencil (b);
661 #if (DEBUG_BEAM_SCORING)
662 SCM annotation = me->get_property ("annotation");
663 if (scm_is_string (annotation))
665 extract_grob_set (me, "stems", stems);
668 This code prints the demerits for each beam. Perhaps this
669 should be switchable for those who want to twiddle with the
673 SCM properties = Font_interface::text_font_alist_chain (me);
675 properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
678 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
680 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
681 (me->layout ()->self_scm (), properties, annotation));
683 if (!score.is_empty ())
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);
691 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
692 return the_beam.smobbed_copy ();
696 Beam::get_default_dir (Grob *me)
698 extract_grob_set (me, "stems", stems);
700 Drul_array<Real> extremes (0.0, 0.0);
701 for (iterof (s, stems); s != stems.end (); s++)
703 Interval positions = Stem::head_positions (*s);
707 if (sign (positions[d]) == d)
708 extremes[d] = d * max (d * positions[d], d * extremes[d]);
710 while (flip (&d) != DOWN);
713 Drul_array<int> total (0, 0);
714 Drul_array<int> count (0, 0);
716 bool force_dir = false;
717 for (vsize i = 0; i < stems.size (); i++)
720 Direction stem_dir = CENTER;
721 SCM stem_dir_scm = s->get_property_data ("direction");
722 if (is_direction (stem_dir_scm))
724 stem_dir = to_dir (stem_dir_scm);
728 stem_dir = to_dir (s->get_property ("default-direction"));
731 stem_dir = to_dir (s->get_property ("neutral-direction"));
736 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
743 if (abs (extremes[UP]) > -extremes[DOWN])
745 else if (extremes[UP] < -extremes[DOWN])
749 Direction dir = CENTER;
750 Direction d = CENTER;
751 if ((d = (Direction) sign (count[UP] - count[DOWN])))
755 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
757 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
760 dir = to_dir (me->get_property ("neutral-direction"));
765 /* Set all stems with non-forced direction to beam direction.
766 Urg: non-forced should become `without/with unforced' direction,
767 once stem gets cleaned-up. */
769 Beam::set_stem_directions (Grob *me, Direction d)
771 extract_grob_set (me, "stems", stems);
773 for (vsize i = 0; i < stems.size (); i++)
777 SCM forcedir = s->get_property_data ("direction");
778 if (!to_dir (forcedir))
779 set_grob_direction (s, d);
784 Only try horizontal beams for knees. No reliable detection of
785 anything else is possible here, since we don't know funky-beaming
786 settings, or X-distances (slopes!) People that want sloped
787 knee-beams, should set the directions manually.
792 this routine should take into account the stemlength scoring
793 of a possible knee/nonknee beam.
796 Beam::consider_auto_knees (Grob *me)
798 SCM scm = me->get_property ("auto-knee-gap");
799 if (!scm_is_number (scm))
806 extract_grob_set (me, "normal-stems", stems);
808 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
809 Real staff_space = Staff_symbol_referencer::staff_space (me);
811 vector<Interval> head_extents_array;
812 for (vsize i = 0; i < stems.size (); i++)
814 Grob *stem = stems[i];
816 Interval head_extents = Stem::head_positions (stem);
817 if (!head_extents.is_empty ())
819 head_extents[LEFT] += -1;
820 head_extents[RIGHT] += 1;
821 head_extents *= staff_space * 0.5;
824 We could subtract beam Y position, but this routine only
825 sets stem directions, a constant shift does not have an
828 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
830 if (to_dir (stem->get_property_data ("direction")))
832 Direction stemdir = to_dir (stem->get_property ("direction"));
833 head_extents[-stemdir] = -stemdir * infinity_f;
836 head_extents_array.push_back (head_extents);
838 gaps.remove_interval (head_extents);
842 Real max_gap_len = 0.0;
844 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
846 Interval gap = gaps.allowed_regions_[i];
849 the outer gaps are not knees.
851 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
854 if (gap.length () >= max_gap_len)
856 max_gap_len = gap.length ();
861 Real beam_translation = get_beam_translation (me);
862 Real beam_thickness = Beam::get_beam_thickness (me);
863 int beam_count = Beam::get_beam_count (me);
864 Real height_of_beams = beam_thickness / 2
865 + (beam_count - 1) * beam_translation;
866 Real threshold = scm_to_double (scm) + height_of_beams;
868 if (max_gap_len > threshold)
871 for (vsize i = 0; i < stems.size (); i++)
873 Grob *stem = stems[i];
874 Interval head_extents = head_extents_array[j++];
876 Direction d = (head_extents.center () < max_gap.center ())
879 stem->set_property ("direction", scm_from_int (d));
881 head_extents.intersect (max_gap);
882 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
887 /* Set stem's shorten property if unset.
890 take some y-position (chord/beam/nearest?) into account
891 scmify forced-fraction
893 This is done in beam because the shorten has to be uniform over the
900 set_minimum_dy (Grob *me, Real *dy)
905 If dy is smaller than the smallest quant, we
906 get absurd direction-sign penalties.
909 Real ss = Staff_symbol_referencer::staff_space (me);
910 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
911 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
912 Real sit = (beam_thickness - slt) / 2;
914 Real hang = 1.0 - (beam_thickness - slt) / 2;
916 *dy = sign (*dy) * max (fabs (*dy),
917 min (min (sit, inter), hang));
923 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
925 Beam::calc_stem_shorten (SCM smob)
927 Grob *me = unsmob_grob (smob);
930 shortening looks silly for x staff beams
933 return scm_from_int (0);
935 Real forced_fraction = 1.0 * forced_stem_count (me)
936 / normal_stem_count (me);
938 int beam_count = get_beam_count (me);
940 SCM shorten_list = me->get_property ("beamed-stem-shorten");
941 if (shorten_list == SCM_EOL)
942 return scm_from_int (0);
944 Real staff_space = Staff_symbol_referencer::staff_space (me);
947 = robust_list_ref (beam_count -1, shorten_list);
948 Real shorten = scm_to_double (shorten_elt) * staff_space;
950 shorten *= forced_fraction;
954 return scm_from_double (shorten);
956 return scm_from_double (0.0);
961 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
963 extract_grob_set (me, "stems", stems);
965 return default_value;
967 Interval head_positions;
969 for (vsize i = 0; i < stems.size(); i++)
971 head_positions.unite (Stem::head_positions (stems[i]));
972 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
975 Direction dir = get_grob_direction (me);
976 Real y = head_positions[dir]
977 * 0.5 * Staff_symbol_referencer::staff_space (me)
978 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
980 y /= Staff_symbol_referencer::staff_space (me);
981 return Interval (y,y);
986 Compute a first approximation to the beam slope.
988 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
990 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
992 Grob *me = unsmob_grob (smob);
994 int count = normal_stem_count (me);
997 return ly_interval2scm (no_visible_stem_positions (me, pos));
999 vector<Real> x_posns;
1000 extract_grob_set (me, "normal-stems", stems);
1001 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1002 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1004 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1006 Grob *fvs = first_normal_stem (me);
1007 Grob *lvs = last_normal_stem (me);
1009 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1010 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1011 Stem::get_stem_info (lvs).ideal_y_
1012 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1014 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1015 for (vsize i = 0; i < stems.size (); i++)
1019 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1020 x_posns.push_back (x);
1022 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1028 if (!ideal.delta ())
1030 Interval chord (Stem::chord_start_y (stems[0]),
1031 Stem::chord_start_y (stems.back ()));
1033 /* Simple beams (2 stems) on middle line should be allowed to be
1036 However, if both stems reach middle line,
1037 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1039 For that case, we apply artificial slope */
1040 if (!ideal[LEFT] && chord.delta () && count == 2)
1043 Direction d = (Direction) (sign (chord.delta ()) * UP);
1044 pos[d] = get_beam_thickness (me) / 2;
1051 For broken beams this doesn't work well. In this case, the
1052 slope esp. of the first part of a broken beam should predict
1053 where the second part goes.
1055 ldy = pos[RIGHT] - pos[LEFT];
1059 vector<Offset> ideals;
1060 for (vsize i = 0; i < stems.size (); i++)
1063 ideals.push_back (Offset (x_posns[i],
1064 Stem::get_stem_info (s).ideal_y_
1065 + s->relative_coordinate (commony, Y_AXIS)
1069 minimise_least_squares (&slope, &y, ideals);
1073 set_minimum_dy (me, &dy);
1076 pos = Interval (y, (y + dy));
1080 "position" is relative to the staff.
1082 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1084 me->set_property ("least-squares-dy", scm_from_double (ldy));
1085 return ly_interval2scm (pos);
1089 // Assuming V is not empty, pick a 'reasonable' point inside V.
1091 point_in_interval (Interval v, Real dist)
1093 if (isinf (v[DOWN]))
1094 return v[UP] - dist;
1095 else if (isinf (v[UP]))
1096 return v[DOWN] + dist;
1102 We can't combine with previous function, since check concave and
1103 slope damping comes first.
1105 TODO: we should use the concaveness to control the amount of damping
1108 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1110 Beam::shift_region_to_valid (SCM grob, SCM posns)
1112 Grob *me = unsmob_grob (grob);
1117 vector<Real> x_posns;
1118 extract_grob_set (me, "stems", stems);
1119 extract_grob_set (me, "covered-grobs", covered);
1121 Grob *common[NO_AXES] = { me, me };
1122 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1123 common[a] = common_refpoint_of_array (stems, me, a);
1124 common[a] = common_refpoint_of_array (covered, common[a], a);
1126 Grob *fvs = first_normal_stem (me);
1131 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1132 for (vsize i = 0; i < stems.size (); i++)
1136 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1137 x_posns.push_back (x);
1140 Grob *lvs = last_normal_stem (me);
1141 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1143 Drul_array<Real> pos = ly_scm2interval (posns);
1145 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1147 Real beam_dy = pos[RIGHT] - pos[LEFT];
1148 Real beam_left_y = pos[LEFT];
1149 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1152 Shift the positions so that we have a chance of finding good
1153 quants (i.e. no short stem failures.)
1155 Interval feasible_left_point;
1156 feasible_left_point.set_full ();
1158 for (vsize i = 0; i < stems.size (); i++)
1161 if (Stem::is_invisible (s))
1164 Direction d = get_grob_direction (s);
1166 = Stem::get_stem_info (s).shortest_y_
1167 - slope * x_posns [i];
1170 left_y is now relative to the stem S. We want relative to
1171 ourselves, so translate:
1174 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1175 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1181 feasible_left_point.intersect (flp);
1185 We have two intervals here, one for the up variant (beams goes
1186 over the collision) one for the down.
1188 Drul_array<Interval> collision_free (feasible_left_point,
1189 feasible_left_point);
1191 vector<Grob*> filtered;
1193 We only update these for objects that are too large for quanting
1194 to find a workaround. Typically, these are notes with
1195 stems, and timesig/keysig/clef, which take out the entire area
1196 inside the staff as feasible.
1198 The code below disregards the thickness and multiplicity of the
1199 beam. This should not be a problem, as the beam quanting will
1200 take care of computing the impact those exactly.
1202 Real min_y_size = 2.0;
1203 for (vsize i = 0; i < covered.size(); i++)
1205 if (!covered[i]->is_live())
1209 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1210 b[a] = covered[i]->extent (common[a], a);
1212 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1215 if (intersection (b[X_AXIS], x_span).is_empty ())
1218 filtered.push_back (covered[i]);
1219 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1220 if (head_stem && Stem::is_normal_stem (head_stem)
1221 && Note_head::has_interface (covered[i]))
1223 if (Stem::get_beam (head_stem))
1226 We must assume that stems are infinitely long in this
1227 case, as asking for the length of the stem typically
1228 leads to circular dependencies.
1230 This strategy assumes that we don't want to handle the
1231 collision of beams in opposite non-forced directions
1232 with this code, where shortening the stems of both
1233 would resolve the problem, eg.
1243 Such beams would need a coordinating grob to resolve
1244 the collision, since both will likely want to occupy
1247 Direction stemdir = get_grob_direction (head_stem);
1248 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1252 // TODO - should we include the extent of the stem here?
1256 if (b[Y_AXIS].length () < min_y_size)
1262 Real x = b[X_AXIS][d] - x_span[LEFT];
1263 Real dy = slope * x;
1265 Direction yd = DOWN;
1268 Real left_y = b[Y_AXIS][yd];
1270 if (left_y == yd * infinity_f)
1272 collision_free[yd].set_empty ();
1278 // Translate back to beam as ref point.
1279 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1282 allowed.set_full ();
1284 allowed[-yd] = left_y;
1285 collision_free[yd].intersect (allowed);
1287 while (flip (&yd) != DOWN);
1289 while (flip (&d) != LEFT);
1293 Pointer_group_interface::get_grob_array (me,
1294 ly_symbol2scm ("covered-grobs"));
1295 arr->set_array (filtered);
1297 if (collision_free[DOWN].contains (beam_left_y)
1298 || collision_free[UP].contains (beam_left_y))
1300 // We're good to go. Do nothing.
1302 else if (!collision_free[DOWN].is_empty ()
1303 || !collision_free[UP].is_empty ())
1305 // We have space above or below collisions (or, no collisions at
1306 // all). Should we factor in the size of the collision_free
1307 // interval as well?
1309 (collision_free[DOWN].distance(beam_left_y) < collision_free[UP].distance (beam_left_y)) ?
1310 collision_free[DOWN] : collision_free[UP];
1312 beam_left_y = point_in_interval (best, 2.0);
1314 else if (!feasible_left_point.is_empty ())
1316 // We are somewhat screwed: we have a collision, but at least
1317 // there is a way to satisfy stem length constraints.
1318 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1322 // We are completely screwed.
1323 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1326 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1327 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1329 return ly_interval2scm (pos);
1332 /* This neat trick is by Werner Lemberg,
1333 damped = tanh (slope)
1334 corresponds with some tables in [Wanske] CHECKME */
1335 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1337 Beam::slope_damping (SCM smob, SCM posns)
1339 Grob *me = unsmob_grob (smob);
1340 Drul_array<Real> pos = ly_scm2interval (posns);
1342 if (normal_stem_count (me) <= 1)
1345 SCM s = me->get_property ("damping");
1346 Real damping = scm_to_double (s);
1347 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1348 if (concaveness >= 10000)
1350 pos[LEFT] = pos[RIGHT];
1351 me->set_property ("least-squares-dy", scm_from_double (0));
1357 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1359 Real dy = pos[RIGHT] - pos[LEFT];
1361 Grob *fvs = first_normal_stem (me);
1362 Grob *lvs = last_normal_stem (me);
1364 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1366 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1367 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1369 Real slope = dy && dx ? dy / dx : 0;
1371 slope = 0.6 * tanh (slope) / (damping + concaveness);
1373 Real damped_dy = slope * dx;
1375 set_minimum_dy (me, &damped_dy);
1377 pos[LEFT] += (dy - damped_dy) / 2;
1378 pos[RIGHT] -= (dy - damped_dy) / 2;
1380 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1383 return ly_interval2scm (pos);
1387 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1389 Beam::quanting (SCM smob, SCM posns)
1391 Grob *me = unsmob_grob (smob);
1392 Drul_array<Real> ys(0, 0);
1393 ys = robust_scm2drul (posns, ys);
1394 Beam_scoring_problem problem (me, ys);
1396 ys = problem.solve ();
1397 return ly_interval2scm (ys);
1402 Report slice containing the numbers that are both in (car BEAMING)
1406 where_are_the_whole_beams (SCM beaming)
1410 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1412 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1414 l.add_point (scm_to_int (scm_car (s)));
1420 /* Return the Y position of the stem-end, given the Y-left, Y-right
1421 in POS for stem S. This Y position is relative to S. */
1423 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1424 Real xl, Real xr, Direction feather_dir,
1425 Drul_array<Real> pos, bool french)
1427 Real beam_translation = get_beam_translation (me);
1428 Direction stem_dir = get_grob_direction (stem);
1431 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1432 Real xdir = 2*relx-1;
1434 Real stem_y = linear_combination(pos, xdir);
1436 SCM beaming = stem->get_property ("beaming");
1438 Slice beam_slice (french
1439 ? where_are_the_whole_beams (beaming)
1440 : Stem::beam_multiplicity (stem));
1441 if (beam_slice.is_empty ())
1442 beam_slice = Slice (0,0);
1443 Interval beam_multiplicity(beam_slice[LEFT],
1447 feather dir = 1 , relx 0->1 : factor 0 -> 1
1448 feather dir = 0 , relx 0->1 : factor 1 -> 1
1449 feather dir = -1, relx 0->1 : factor 1 -> 0
1451 Real feather_factor = 1;
1452 if (feather_dir > 0)
1453 feather_factor = relx;
1454 else if (feather_dir < 0)
1455 feather_factor = 1 - relx;
1457 stem_y += feather_factor * beam_translation
1458 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1459 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1460 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1466 Hmm. At this time, beam position and slope are determined. Maybe,
1467 stem directions and length should set to relative to the chord's
1468 position of the beam. */
1469 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1471 Beam::set_stem_lengths (SCM smob)
1473 Grob *me = unsmob_grob (smob);
1475 /* trigger callbacks. */
1476 (void) me->get_property ("direction");
1477 (void) me->get_property ("beaming");
1479 SCM posns = me->get_property ("positions");
1481 extract_grob_set (me, "stems", stems);
1486 for (int a = 2; a--;)
1487 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1489 Drul_array<Real> pos = ly_scm2realdrul (posns);
1490 Real staff_space = Staff_symbol_referencer::staff_space (me);
1491 scale_drul (&pos, staff_space);
1495 if (robust_scm2int (me->get_property ("gap-count"), 0))
1498 thick = get_beam_thickness (me);
1501 Grob *fvs = first_normal_stem (me);
1502 Grob *lvs = last_normal_stem (me);
1504 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1505 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1506 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1508 for (vsize i = 0; i < stems.size (); i++)
1512 bool french = to_boolean (s->get_property ("french-beaming"));
1513 Real stem_y = calc_stem_y (me, s, common,
1514 xl, xr, feather_dir,
1515 pos, french && s != lvs && s!= fvs);
1518 Make the stems go up to the end of the beam. This doesn't matter
1519 for normal beams, but for tremolo beams it looks silly otherwise.
1522 && !Stem::is_invisible (s))
1523 stem_y += thick * 0.5 * get_grob_direction (s);
1526 Do set_stemend for invisible stems too, so tuplet brackets
1527 have a reference point for sloping
1529 Stem::set_stemend (s, 2 * stem_y / staff_space);
1536 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1538 extract_grob_set (me, "stems", stems);
1541 for (vsize i = 0; i < stems.size (); i++)
1544 Don't overwrite user settings.
1548 Grob *stem = stems[i];
1549 SCM beaming_prop = stem->get_property ("beaming");
1550 if (beaming_prop == SCM_EOL
1551 || index_get_cell (beaming_prop, d) == SCM_EOL)
1553 int count = beaming->beamlet_count (i, d);
1555 && i + 1 < stems.size ()
1556 && Stem::is_invisible (stem))
1557 count = min (count, beaming->beamlet_count (i,-d));
1559 if ( ((i == 0 && d == LEFT)
1560 || (i == stems.size ()-1 && d == RIGHT))
1561 && stems.size () > 1
1562 && to_boolean (me->get_property ("clip-edges")))
1565 Stem::set_beaming (stem, count, d);
1568 while (flip (&d) != LEFT);
1573 Beam::forced_stem_count (Grob *me)
1575 extract_grob_set (me, "normal-stems", stems);
1578 for (vsize i = 0; i < stems.size (); i++)
1582 /* I can imagine counting those boundaries as a half forced stem,
1583 but let's count them full for now. */
1584 Direction defdir = to_dir (s->get_property ("default-direction"));
1586 if (abs (Stem::chord_start_y (s)) > 0.1
1588 && get_grob_direction (s) != defdir)
1595 Beam::normal_stem_count (Grob *me)
1597 extract_grob_set (me, "normal-stems", stems);
1598 return stems.size ();
1602 Beam::first_normal_stem (Grob *me)
1604 extract_grob_set (me, "normal-stems", stems);
1605 return stems.size () ? stems[0] : 0;
1609 Beam::last_normal_stem (Grob *me)
1611 extract_grob_set (me, "normal-stems", stems);
1612 return stems.size () ? stems.back () : 0;
1618 handle rest under beam (do_post: beams are calculated now)
1619 what about combination of collisions and rest under beam.
1623 rest -> stem -> beam -> interpolate_y_position ()
1625 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1627 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1629 Grob *rest = unsmob_grob (smob);
1630 if (scm_is_number (rest->get_property ("staff-position")))
1631 return scm_from_int (0);
1633 Real offset = robust_scm2double (prev_offset, 0.0);
1635 Grob *st = unsmob_grob (rest->get_object ("stem"));
1638 return scm_from_double (0.0);
1639 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1641 || !Beam::has_interface (beam)
1642 || !Beam::normal_stem_count (beam))
1643 return scm_from_double (0.0);
1645 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1646 Drul_array<Real> (0,0)));
1648 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1650 scale_drul (&pos, staff_space);
1652 Real dy = pos[RIGHT] - pos[LEFT];
1654 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1655 last_normal_stem (beam));
1656 extract_grob_set (beam, "stems", stems);
1658 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1660 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1661 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1662 Real slope = dy && dx ? dy / dx : 0;
1664 Direction d = get_grob_direction (stem);
1665 Real stem_y = pos[LEFT]
1666 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1668 Real beam_translation = get_beam_translation (beam);
1669 Real beam_thickness = Beam::get_beam_thickness (beam);
1672 TODO: this is not strictly correct for 16th knee beams.
1675 = Stem::beam_multiplicity (stem).length () + 1;
1677 Real height_of_my_beams = beam_thickness / 2
1678 + (beam_count - 1) * beam_translation;
1679 Real beam_y = stem_y - d * height_of_my_beams;
1681 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1683 Interval rest_extent = rest->extent (rest, Y_AXIS);
1684 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1686 Real rest_dim = rest_extent[d];
1687 Real minimum_distance
1688 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1689 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1691 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1693 shift /= staff_space;
1694 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1696 /* Always move discretely by half spaces */
1697 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1699 /* Inside staff, move by whole spaces*/
1700 if ((rest_extent[d] + staff_space * shift) * d
1702 || (rest_extent[-d] + staff_space * shift) * -d
1704 shift = ceil (fabs (shift)) * sign (shift);
1706 return scm_from_double (offset + staff_space * shift);
1710 Beam::is_knee (Grob *me)
1712 SCM k = me->get_property ("knee");
1713 if (scm_is_bool (k))
1714 return ly_scm2bool (k);
1718 extract_grob_set (me, "stems", stems);
1719 for (vsize i = stems.size (); i--;)
1721 Direction dir = get_grob_direction (stems[i]);
1730 me->set_property ("knee", ly_bool2scm (knee));
1736 Beam::is_cross_staff (Grob *me)
1738 extract_grob_set (me, "stems", stems);
1739 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1740 for (vsize i = 0; i < stems.size (); i++)
1741 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1746 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1748 Beam::calc_cross_staff (SCM smob)
1750 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1754 Beam::get_direction_beam_count (Grob *me, Direction d)
1756 extract_grob_set (me, "stems", stems);
1759 for (vsize i = stems.size (); i--;)
1762 Should we take invisible stems into account?
1764 if (get_grob_direction (stems[i]) == d)
1765 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1771 ADD_INTERFACE (Beam,
1774 "The @code{beam-thickness} property is the weight of beams,"
1775 " measured in staffspace. The @code{direction} property is"
1776 " not user-serviceable. Use the @code{direction} property"
1777 " of @code{Stem} instead.\n"
1779 "The following properties may be set in the @code{details}"
1783 "@item stem-length-demerit-factor\n"
1784 "Demerit factor used for inappropriate stem lengths.\n"
1785 "@item secondary-beam-demerit\n"
1786 "Demerit used in quanting calculations for multiple"
1788 "@item region-size\n"
1789 "Size of region for checking quant scores.\n"
1791 "Epsilon for beam quant code to check for presence"
1793 "@item stem-length-limit-penalty\n"
1794 "Penalty for differences in stem lengths on a beam.\n"
1795 "@item damping-direction-penalty\n"
1796 "Demerit penalty applied when beam direction is different"
1797 " from damping direction.\n"
1798 "@item hint-direction-penalty\n"
1799 "Demerit penalty applied when beam direction is different"
1800 " from damping direction, but damping slope is"
1801 " <= @code{round-to-zero-slope}.\n"
1802 "@item musical-direction-factor\n"
1803 "Demerit scaling factor for difference between"
1804 " beam slope and music slope.\n"
1805 "@item ideal-slope-factor\n"
1806 "Demerit scaling factor for difference between"
1807 " beam slope and damping slope.\n"
1808 "@item round-to-zero-slope\n"
1809 "Damping slope which is considered zero for purposes of"
1810 " calculating direction penalties.\n"
1816 "beamed-stem-shorten "
1822 "collision-interfaces "
1823 "collision-voice-only "
1835 "neutral-direction "
1838 "quantized-positions "