Remove unused prototype in score.hh.
[lilypond/mpolesky.git] / lily / beam.cc
blob1cb48dade12133fa2e0dfc56ffe4a35d17014f3c
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2010 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/>.
22 TODO:
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.)
31 Notes:
33 - Stems run to the Y-center of the beam.
35 - beam_translation is the offset between Y centers of the beam.
38 #include "beam.hh"
40 #include "beaming-pattern.hh"
41 #include "directional-element-interface.hh"
42 #include "main.hh"
43 #include "international.hh"
44 #include "interval-set.hh"
45 #include "item.hh"
46 #include "least-squares.hh"
47 #include "lookup.hh"
48 #include "misc.hh"
49 #include "output-def.hh"
50 #include "pointer-group-interface.hh"
51 #include "spanner.hh"
52 #include "staff-symbol-referencer.hh"
53 #include "stem.hh"
54 #include "warn.hh"
55 #include "grob-array.hh"
57 #if DEBUG_BEAM_SCORING
58 #include "text-interface.hh" // debug output.
59 #include "font-interface.hh" // debug output.
60 #endif
62 #include <map>
65 Beam_stem_segment::Beam_stem_segment ()
67 max_connect_ = 1000; // infinity
68 stem_ = 0;
69 width_ = 0.0;
70 stem_x_ = 0.0;
71 rank_ = 0;
72 stem_index_ = 0;
73 dir_ = CENTER;
76 Beam_segment::Beam_segment ()
78 vertical_count_ = 0;
81 void
82 Beam::add_stem (Grob *me, Grob *s)
84 if (Stem::get_beam (s))
86 programming_error ("Stem already has beam");
87 return ;
90 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
91 s->set_object ("beam", me->self_scm ());
92 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
95 Real
96 Beam::get_beam_thickness (Grob *me)
98 return robust_scm2double (me->get_property ("beam-thickness"), 0)
99 * Staff_symbol_referencer::staff_space (me);
102 /* Return the translation between 2 adjoining beams. */
103 Real
104 Beam::get_beam_translation (Grob *me)
106 int beam_count = get_beam_count (me);
107 Real staff_space = Staff_symbol_referencer::staff_space (me);
108 Real line = Staff_symbol_referencer::line_thickness (me);
109 Real beam_thickness = get_beam_thickness (me);
110 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
112 Real beam_translation = beam_count < 4
113 ? (2 * staff_space + line - beam_thickness) / 2.0
114 : (3 * staff_space + line - beam_thickness) / 3.0;
116 return fract * beam_translation;
119 /* Maximum beam_count. */
121 Beam::get_beam_count (Grob *me)
123 int m = 0;
125 extract_grob_set (me, "stems", stems);
126 for (vsize i = 0; i < stems.size (); i++)
128 Grob *stem = stems[i];
129 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
131 return m;
134 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
136 Beam::calc_normal_stems (SCM smob)
138 Grob *me = unsmob_grob (smob);
140 extract_grob_set (me, "stems", stems);
141 SCM val = Grob_array::make_array ();
142 Grob_array *ga = unsmob_grob_array (val);
143 for (vsize i = 0; i < stems.size (); i++)
144 if (Stem::is_normal_stem (stems[i]))
145 ga->add (stems[i]);
147 return val;
150 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
152 Beam::calc_direction (SCM smob)
154 Grob *me = unsmob_grob (smob);
156 /* Beams with less than 2 two stems don't make much sense, but could happen
157 when you do
159 r8[ c8 r8]
163 Direction dir = CENTER;
165 int count = normal_stem_count (me);
166 if (count < 2)
168 extract_grob_set (me, "stems", stems);
169 if (stems.size () == 0)
171 me->warning (_ ("removing beam with no stems"));
172 me->suicide ();
174 return SCM_UNSPECIFIED;
176 else
178 Grob *stem = first_normal_stem (me);
181 This happens for chord tremolos.
183 if (!stem)
184 stem = stems[0];
186 if (is_direction (stem->get_property_data ("direction")))
187 dir = to_dir (stem->get_property_data ("direction"));
188 else
189 dir = to_dir (stem->get_property ("default-direction"));
193 if (count >= 1)
195 if (!dir)
196 dir = get_default_dir (me);
198 consider_auto_knees (me);
201 if (dir)
203 set_stem_directions (me, dir);
206 return scm_from_int (dir);
211 /* We want a maximal number of shared beams, but if there is choice, we
212 * take the one that is closest to the end of the stem. This is for
213 * situations like
218 * |===|
219 * |=
224 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
225 Direction left_dir,
226 Direction right_dir)
228 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
230 int best_count = 0;
231 int best_start = 0;
232 for (int i = lslice[-left_dir];
233 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
235 int count = 0;
236 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
238 int k = -right_dir * scm_to_int (scm_car (s)) + i;
239 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
240 count++;
243 if (count >= best_count)
245 best_count = count;
246 best_start = i;
250 return best_start;
253 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
255 Beam::calc_beaming (SCM smob)
257 Grob *me = unsmob_grob (smob);
259 extract_grob_set (me, "stems", stems);
261 Slice last_int;
262 last_int.set_empty ();
264 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
265 Direction last_dir = CENTER;
266 for (vsize i = 0; i < stems.size (); i++)
268 Grob *this_stem = stems[i];
269 SCM this_beaming = this_stem->get_property ("beaming");
271 Direction this_dir = get_grob_direction (this_stem);
272 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
274 int start_point = position_with_maximal_common_beams
275 (last_beaming, this_beaming,
276 last_dir ? last_dir : this_dir,
277 this_dir);
279 Direction d = LEFT;
280 Slice new_slice;
283 new_slice.set_empty ();
284 SCM s = index_get_cell (this_beaming, d);
285 for (; scm_is_pair (s); s = scm_cdr (s))
287 int new_beam_pos
288 = start_point - this_dir * scm_to_int (scm_car (s));
290 new_slice.add_point (new_beam_pos);
291 scm_set_car_x (s, scm_from_int (new_beam_pos));
294 while (flip (&d) != LEFT);
296 if (!new_slice.is_empty ())
297 last_int = new_slice;
299 else
302 FIXME: what's this for?
304 SCM s = scm_cdr (this_beaming);
305 for (; scm_is_pair (s); s = scm_cdr (s))
307 int np = -this_dir * scm_to_int (scm_car (s));
308 scm_set_car_x (s, scm_from_int (np));
309 last_int.add_point (np);
313 if (scm_ilength (scm_cdr (this_beaming)) > 0)
315 last_beaming = this_beaming;
316 last_dir = this_dir;
320 return SCM_EOL;
323 bool
324 operator <(Beam_stem_segment const &a,
325 Beam_stem_segment const &b)
327 return a.rank_ < b.rank_;
330 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
332 vector<Beam_segment>
333 Beam::get_beam_segments (Grob *me_grob, Grob **common)
335 /* ugh, this has a side-effect that we need to ensure that
336 Stem #'beaming is correct */
337 (void) me_grob->get_property ("beaming");
339 Spanner *me = dynamic_cast<Spanner*> (me_grob);
341 extract_grob_set (me, "stems", stems);
342 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
344 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
345 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
347 *common = commonx;
349 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
350 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
352 Position_stem_segments_map stem_segments;
353 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
355 /* There are two concepts of "rank" that are used in the following code.
356 The beam_rank is the vertical position of the beam (larger numbers are
357 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
358 is the horizontal position of the segment (this is incremented by two
359 for each stem; the beam segment on the right side of the stem has
360 a higher rank (by one) than its neighbour to the left). */
361 Slice ranks;
362 for (vsize i = 0; i < stems.size (); i++)
364 Grob *stem = stems[i];
365 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
366 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
367 SCM beaming = stem->get_property ("beaming");
368 Direction d = LEFT;
371 // Find the maximum and minimum beam ranks.
372 // Given that RANKS is never reset to empty, the interval will always be
373 // smallest for the left beamlet of the first stem, and then it might grow.
374 // Do we really want this? (It only affects the tremolo gaps) --jneem
375 for (SCM s = index_get_cell (beaming, d);
376 scm_is_pair (s); s = scm_cdr (s))
378 if (!scm_is_integer (scm_car (s)))
379 continue;
381 int beam_rank = scm_to_int (scm_car (s));
382 ranks.add_point (beam_rank);
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)))
389 continue;
391 int beam_rank = scm_to_int (scm_car (s));
392 Beam_stem_segment seg;
393 seg.stem_ = stem;
394 seg.stem_x_ = stem_x;
395 seg.rank_ = 2 * i + (d+1)/2;
396 seg.width_ = stem_width;
397 seg.stem_index_ = i;
398 seg.dir_ = d;
399 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
401 Direction stem_dir = get_grob_direction (stem);
403 seg.gapped_
404 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
405 stem_segments[beam_rank].push_back (seg);
408 while (flip (&d) != LEFT);
411 Drul_array<Real> break_overshoot
412 = robust_scm2drul (me->get_property ("break-overshoot"),
413 Drul_array<Real> (-0.5, 0.0));
415 vector<Beam_segment> segments;
416 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
417 i != stem_segments.end (); i++)
419 vector<Beam_stem_segment> segs = (*i).second;
420 vector_sort (segs, less<Beam_stem_segment> ());
422 Beam_segment current;
424 // Iterate over all of the segments of the current beam rank,
425 // merging the adjacent Beam_stem_segments into one Beam_segment
426 // when appropriate.
427 int vertical_count = (*i).first;
428 for (vsize j = 0; j < segs.size (); j++)
430 // Keeping track of the different directions here is a little tricky.
431 // segs[j].dir_ is the direction of the beam segment relative to the stem
432 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
433 // its stem) whereas event_dir refers to the edge of the beam segment that
434 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
435 // are looking at that edge of the beam segment that is furthest from its
436 // stem).
437 Direction event_dir = LEFT;
438 Beam_stem_segment const& seg = segs[j];
441 Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
442 // TODO: make names clearer? --jneem
443 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
444 // on_beam_bound: whether the current segment is on the boundary of just that part
445 // of the beam with the current beam_rank
446 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
447 : seg.stem_index_ == stems.size() - 1;
448 bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
449 j == segs.size () - 1;
450 bool inside_stem = (event_dir == LEFT)
451 ? seg.stem_index_ > 0
452 : seg.stem_index_ + 1 < stems.size () ;
454 bool event = on_beam_bound
455 || abs (seg.rank_ - neighbor_seg.rank_) > 1
456 || (abs (vertical_count) >= seg.max_connect_
457 || abs (vertical_count) >= neighbor_seg.max_connect_);
459 if (!event)
460 // Then this edge of the current segment is irrelevent because it will
461 // be connected with the next segment in the event_dir direction.
462 continue;
464 current.vertical_count_ = vertical_count;
465 current.horizontal_[event_dir] = seg.stem_x_;
466 if (seg.dir_ == event_dir)
467 // then we are examining the edge of a beam segment that is furthest
468 // from its stem.
470 if (on_line_bound
471 && me->get_bound (event_dir)->break_status_dir ())
473 current.horizontal_[event_dir]
474 = (robust_relative_extent (me->get_bound (event_dir),
475 commonx, X_AXIS)[RIGHT]
476 + event_dir * break_overshoot[event_dir]);
478 else
480 Grob *stem = stems[seg.stem_index_];
481 Drul_array<Real> beamlet_length =
482 robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
483 Drul_array<Real> max_proportion =
484 robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
485 Real length = beamlet_length[seg.dir_];
487 if (inside_stem)
489 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
490 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
492 length = min (length,
493 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
495 current.horizontal_[event_dir] += event_dir * length;
498 else
499 // we are examining the edge of a beam segment that is closest
500 // (ie. touching, unless there is a gap) its stem.
502 current.horizontal_[event_dir] += event_dir * seg.width_/2;
503 if (seg.gapped_)
505 current.horizontal_[event_dir] -= event_dir * gap_length;
507 if (Stem::is_invisible (seg.stem_))
510 Need to do this in case of whole notes. We don't want the
511 heads to collide with the beams.
513 extract_grob_set (seg.stem_, "note-heads", heads);
515 for (vsize k = 0; k < heads.size (); k ++)
516 current.horizontal_[event_dir]
517 = event_dir * min (event_dir * current.horizontal_[event_dir],
518 - gap_length/2
519 + event_dir
520 * heads[k]->extent (commonx,
521 X_AXIS)[-event_dir]);
526 if (event_dir == RIGHT)
528 segments.push_back (current);
529 current = Beam_segment ();
532 while (flip (&event_dir) != LEFT);
537 return segments;
540 MAKE_SCHEME_CALLBACK (Beam, print, 1);
542 Beam::print (SCM grob)
544 Spanner *me = unsmob_spanner (grob);
545 Grob *commonx = 0;
546 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
548 Interval span;
549 if (normal_stem_count (me))
551 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
552 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
554 else
556 extract_grob_set (me, "stems", stems);
557 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
558 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
561 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
563 SCM posns = me->get_property ("quantized-positions");
564 Interval pos;
565 if (!is_number_pair (posns))
567 programming_error ("no beam positions?");
568 pos = Interval (0, 0);
570 else
571 pos = ly_scm2realdrul (posns);
573 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
575 Real dy = pos[RIGHT] - pos[LEFT];
576 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
578 Real beam_thickness = get_beam_thickness (me);
579 Real beam_dy = get_beam_translation (me);
581 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
583 Stencil the_beam;
584 for (vsize i = 0; i < segments.size (); i ++)
586 Real local_slope = slope;
587 if (feather_dir)
589 local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length ();
592 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
594 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
596 b.translate_axis (local_slope
597 * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir))
598 + pos.linear_combination (feather_dir)
599 + beam_dy * segments[i].vertical_count_, Y_AXIS);
600 the_beam.add_stencil (b);
603 #if (DEBUG_BEAM_SCORING)
604 SCM annotation = me->get_property ("annotation");
605 if (!scm_is_string (annotation))
607 SCM debug = me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"));
608 if (to_boolean (debug))
609 annotation = me->get_property ("quant-score");
612 if (scm_is_string (annotation))
614 extract_grob_set (me, "stems", stems);
617 This code prints the demerits for each beam. Perhaps this
618 should be switchable for those who want to twiddle with the
619 parameters.
621 string str;
622 SCM properties = Font_interface::text_font_alist_chain (me);
624 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
626 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
627 (me->layout ()->self_scm (), properties, annotation));
629 if (!score.is_empty ())
631 score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
632 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
635 #endif
637 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
638 return the_beam.smobbed_copy ();
641 Direction
642 Beam::get_default_dir (Grob *me)
644 extract_grob_set (me, "stems", stems);
646 Drul_array<Real> extremes (0.0, 0.0);
647 for (iterof (s, stems); s != stems.end (); s++)
649 Interval positions = Stem::head_positions (*s);
650 Direction d = DOWN;
653 if (sign (positions[d]) == d)
654 extremes[d] = d * max (d * positions[d], d * extremes[d]);
656 while (flip (&d) != DOWN);
659 Drul_array<int> total (0, 0);
660 Drul_array<int> count (0, 0);
662 bool force_dir = false;
663 for (vsize i = 0; i < stems.size (); i++)
665 Grob *s = stems[i];
666 Direction stem_dir = CENTER;
667 SCM stem_dir_scm = s->get_property_data ("direction");
668 if (is_direction (stem_dir_scm))
670 stem_dir = to_dir (stem_dir_scm);
671 force_dir = true;
673 else
674 stem_dir = to_dir (s->get_property ("default-direction"));
676 if (!stem_dir)
677 stem_dir = to_dir (s->get_property ("neutral-direction"));
679 if (stem_dir)
681 count[stem_dir] ++;
682 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
687 if (!force_dir)
689 if (abs (extremes[UP]) > -extremes[DOWN])
690 return DOWN;
691 else if (extremes[UP] < -extremes[DOWN])
692 return UP;
695 Direction dir = CENTER;
696 Direction d = CENTER;
697 if ((d = (Direction) sign (count[UP] - count[DOWN])))
698 dir = d;
699 else if (count[UP]
700 && count[DOWN]
701 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
702 dir = d;
703 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
704 dir = d;
705 else
706 dir = to_dir (me->get_property ("neutral-direction"));
708 return dir;
711 /* Set all stems with non-forced direction to beam direction.
712 Urg: non-forced should become `without/with unforced' direction,
713 once stem gets cleaned-up. */
714 void
715 Beam::set_stem_directions (Grob *me, Direction d)
717 extract_grob_set (me, "stems", stems);
719 for (vsize i = 0; i < stems.size (); i++)
721 Grob *s = stems[i];
723 SCM forcedir = s->get_property_data ("direction");
724 if (!to_dir (forcedir))
725 set_grob_direction (s, d);
730 Only try horizontal beams for knees. No reliable detection of
731 anything else is possible here, since we don't know funky-beaming
732 settings, or X-distances (slopes!) People that want sloped
733 knee-beams, should set the directions manually.
736 TODO:
738 this routine should take into account the stemlength scoring
739 of a possible knee/nonknee beam.
741 void
742 Beam::consider_auto_knees (Grob *me)
744 SCM scm = me->get_property ("auto-knee-gap");
745 if (!scm_is_number (scm))
746 return;
748 Interval_set gaps;
750 gaps.set_full ();
752 extract_grob_set (me, "normal-stems", stems);
754 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
755 Real staff_space = Staff_symbol_referencer::staff_space (me);
757 vector<Interval> head_extents_array;
758 for (vsize i = 0; i < stems.size (); i++)
760 Grob *stem = stems[i];
762 Interval head_extents = Stem::head_positions (stem);
763 if (!head_extents.is_empty ())
765 head_extents[LEFT] += -1;
766 head_extents[RIGHT] += 1;
767 head_extents *= staff_space * 0.5;
770 We could subtract beam Y position, but this routine only
771 sets stem directions, a constant shift does not have an
772 influence.
774 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
776 if (to_dir (stem->get_property_data ("direction")))
778 Direction stemdir = to_dir (stem->get_property ("direction"));
779 head_extents[-stemdir] = -stemdir * infinity_f;
782 head_extents_array.push_back (head_extents);
784 gaps.remove_interval (head_extents);
787 Interval max_gap;
788 Real max_gap_len = 0.0;
790 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
792 Interval gap = gaps.allowed_regions_[i];
795 the outer gaps are not knees.
797 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
798 continue;
800 if (gap.length () >= max_gap_len)
802 max_gap_len = gap.length ();
803 max_gap = gap;
807 Real beam_translation = get_beam_translation (me);
808 Real beam_thickness = Beam::get_beam_thickness (me);
809 int beam_count = Beam::get_beam_count (me);
810 Real height_of_beams = beam_thickness / 2
811 + (beam_count - 1) * beam_translation;
812 Real threshold = scm_to_double (scm) + height_of_beams;
814 if (max_gap_len > threshold)
816 int j = 0;
817 for (vsize i = 0; i < stems.size (); i++)
819 Grob *stem = stems[i];
820 Interval head_extents = head_extents_array[j++];
822 Direction d = (head_extents.center () < max_gap.center ())
823 ? UP : DOWN;
825 stem->set_property ("direction", scm_from_int (d));
827 head_extents.intersect (max_gap);
828 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
833 /* Set stem's shorten property if unset.
835 TODO:
836 take some y-position (chord/beam/nearest?) into account
837 scmify forced-fraction
839 This is done in beam because the shorten has to be uniform over the
840 entire beam.
845 void
846 set_minimum_dy (Grob *me, Real *dy)
848 if (*dy)
851 If dy is smaller than the smallest quant, we
852 get absurd direction-sign penalties.
855 Real ss = Staff_symbol_referencer::staff_space (me);
856 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
857 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
858 Real sit = (beam_thickness - slt) / 2;
859 Real inter = 0.5;
860 Real hang = 1.0 - (beam_thickness - slt) / 2;
862 *dy = sign (*dy) * max (fabs (*dy),
863 min (min (sit, inter), hang));
869 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
871 Beam::calc_stem_shorten (SCM smob)
873 Grob *me = unsmob_grob (smob);
876 shortening looks silly for x staff beams
878 if (is_knee (me))
879 return scm_from_int (0);
881 Real forced_fraction = 1.0 * forced_stem_count (me)
882 / normal_stem_count (me);
884 int beam_count = get_beam_count (me);
886 SCM shorten_list = me->get_property ("beamed-stem-shorten");
887 if (shorten_list == SCM_EOL)
888 return scm_from_int (0);
890 Real staff_space = Staff_symbol_referencer::staff_space (me);
892 SCM shorten_elt
893 = robust_list_ref (beam_count -1, shorten_list);
894 Real shorten = scm_to_double (shorten_elt) * staff_space;
896 shorten *= forced_fraction;
899 if (shorten)
900 return scm_from_double (shorten);
902 return scm_from_double (0.0);
906 Interval
907 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
909 extract_grob_set (me, "stems", stems);
910 if (stems.empty ())
911 return default_value;
913 Interval head_positions;
914 Slice multiplicity;
915 for (vsize i = 0; i < stems.size(); i++)
917 head_positions.unite (Stem::head_positions (stems[i]));
918 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
921 Direction dir = get_grob_direction (me);
922 Real y = head_positions[dir]
923 * 0.5 * Staff_symbol_referencer::staff_space (me)
924 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
926 y /= Staff_symbol_referencer::staff_space (me);
927 return Interval (y,y);
932 Compute a first approximation to the beam slope.
934 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
936 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
938 Grob *me = unsmob_grob (smob);
940 int count = normal_stem_count (me);
941 Interval pos (0,0);
942 if (count < 1)
943 return ly_interval2scm (no_visible_stem_positions (me, pos));
945 vector<Real> x_posns;
946 extract_grob_set (me, "normal-stems", stems);
947 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
948 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
950 Real my_y = me->relative_coordinate (commony, Y_AXIS);
952 Grob *fvs = first_normal_stem (me);
953 Grob *lvs = last_normal_stem (me);
955 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
956 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
957 Stem::get_stem_info (lvs).ideal_y_
958 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
960 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
961 for (vsize i = 0; i < stems.size (); i++)
963 Grob *s = stems[i];
965 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
966 x_posns.push_back (x);
968 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
970 Real y = 0;
971 Real slope = 0;
972 Real dy = 0;
973 Real ldy = 0.0;
974 if (!ideal.delta ())
976 Interval chord (Stem::chord_start_y (stems[0]),
977 Stem::chord_start_y (stems.back ()));
979 /* Simple beams (2 stems) on middle line should be allowed to be
980 slightly sloped.
982 However, if both stems reach middle line,
983 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
985 For that case, we apply artificial slope */
986 if (!ideal[LEFT] && chord.delta () && count == 2)
988 /* FIXME. -> UP */
989 Direction d = (Direction) (sign (chord.delta ()) * UP);
990 pos[d] = get_beam_thickness (me) / 2;
991 pos[-d] = -pos[d];
993 else
994 pos = ideal;
997 For broken beams this doesn't work well. In this case, the
998 slope esp. of the first part of a broken beam should predict
999 where the second part goes.
1001 ldy = pos[RIGHT] - pos[LEFT];
1003 else
1005 vector<Offset> ideals;
1006 for (vsize i = 0; i < stems.size (); i++)
1008 Grob *s = stems[i];
1009 ideals.push_back (Offset (x_posns[i],
1010 Stem::get_stem_info (s).ideal_y_
1011 + s->relative_coordinate (commony, Y_AXIS)
1012 - my_y));
1015 minimise_least_squares (&slope, &y, ideals);
1017 dy = slope * dx;
1019 set_minimum_dy (me, &dy);
1021 ldy = dy;
1022 pos = Interval (y, (y + dy));
1026 "position" is relative to the staff.
1028 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1030 me->set_property ("least-squares-dy", scm_from_double (ldy));
1031 return ly_interval2scm (pos);
1035 We can't combine with previous function, since check concave and
1036 slope damping comes first.
1038 TODO: we should use the concaveness to control the amount of damping
1039 applied.
1041 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1043 Beam::shift_region_to_valid (SCM grob, SCM posns)
1045 Grob *me = unsmob_grob (grob);
1047 Code dup.
1049 vector<Real> x_posns;
1050 extract_grob_set (me, "stems", stems);
1051 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1052 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1054 Grob *fvs = first_normal_stem (me);
1056 if (!fvs)
1057 return posns;
1059 Real x0 = fvs->relative_coordinate (commonx, X_AXIS);
1060 for (vsize i = 0; i < stems.size (); i++)
1062 Grob *s = stems[i];
1064 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1065 x_posns.push_back (x);
1068 Grob *lvs = last_normal_stem (me);
1069 if (!lvs)
1070 return posns;
1072 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
1074 Drul_array<Real> pos = ly_scm2interval (posns);
1076 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1078 Real dy = pos[RIGHT] - pos[LEFT];
1079 Real y = pos[LEFT];
1080 Real slope = dx ? (dy / dx) : 0.0;
1083 Shift the positions so that we have a chance of finding good
1084 quants (i.e. no short stem failures.)
1086 Interval feasible_left_point;
1087 feasible_left_point.set_full ();
1088 for (vsize i = 0; i < stems.size (); i++)
1090 Grob *s = stems[i];
1091 if (Stem::is_invisible (s))
1092 continue;
1094 Direction d = get_grob_direction (s);
1096 Real left_y
1097 = Stem::get_stem_info (s).shortest_y_
1098 - slope * x_posns [i];
1101 left_y is now relative to the stem S. We want relative to
1102 ourselves, so translate:
1104 left_y
1105 += + s->relative_coordinate (commony, Y_AXIS)
1106 - me->relative_coordinate (commony, Y_AXIS);
1108 Interval flp;
1109 flp.set_full ();
1110 flp[-d] = left_y;
1112 feasible_left_point.intersect (flp);
1115 if (feasible_left_point.is_empty ())
1116 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1117 else if (!feasible_left_point.contains (y))
1119 const int REGION_SIZE = 2; // UGH UGH
1120 if (isinf (feasible_left_point[DOWN]))
1121 y = feasible_left_point[UP] - REGION_SIZE;
1122 else if (isinf (feasible_left_point[UP]))
1123 y = feasible_left_point[DOWN]+ REGION_SIZE;
1124 else
1125 y = feasible_left_point.center ();
1128 pos = Drul_array<Real> (y, (y + dy));
1129 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1131 return ly_interval2scm (pos);
1134 /* This neat trick is by Werner Lemberg,
1135 damped = tanh (slope)
1136 corresponds with some tables in [Wanske] CHECKME */
1137 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1139 Beam::slope_damping (SCM smob, SCM posns)
1141 Grob *me = unsmob_grob (smob);
1142 Drul_array<Real> pos = ly_scm2interval (posns);
1144 if (normal_stem_count (me) <= 1)
1145 return posns;
1148 SCM s = me->get_property ("damping");
1149 Real damping = scm_to_double (s);
1150 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1151 if (concaveness >= 10000)
1153 pos[LEFT] = pos[RIGHT];
1154 me->set_property ("least-squares-dy", scm_from_double (0));
1155 damping = 0;
1158 if (damping)
1160 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1162 Real dy = pos[RIGHT] - pos[LEFT];
1164 Grob *fvs = first_normal_stem (me);
1165 Grob *lvs = last_normal_stem (me);
1167 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1169 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1170 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1172 Real slope = dy && dx ? dy / dx : 0;
1174 slope = 0.6 * tanh (slope) / (damping + concaveness);
1176 Real damped_dy = slope * dx;
1178 set_minimum_dy (me, &damped_dy);
1180 pos[LEFT] += (dy - damped_dy) / 2;
1181 pos[RIGHT] -= (dy - damped_dy) / 2;
1183 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1186 return ly_interval2scm (pos);
1190 Report slice containing the numbers that are both in (car BEAMING)
1191 and (cdr BEAMING)
1193 Slice
1194 where_are_the_whole_beams (SCM beaming)
1196 Slice l;
1198 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1200 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1202 l.add_point (scm_to_int (scm_car (s)));
1205 return l;
1208 /* Return the Y position of the stem-end, given the Y-left, Y-right
1209 in POS for stem S. This Y position is relative to S. */
1210 Real
1211 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1212 Real xl, Real xr, Direction feather_dir,
1213 Drul_array<Real> pos, bool french)
1215 Real beam_translation = get_beam_translation (me);
1216 Direction stem_dir = get_grob_direction (stem);
1218 Real dx = xr - xl;
1219 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1220 Real xdir = 2*relx-1;
1222 Real stem_y = linear_combination(pos, xdir);
1224 SCM beaming = stem->get_property ("beaming");
1226 Slice beam_slice (french
1227 ? where_are_the_whole_beams (beaming)
1228 : Stem::beam_multiplicity (stem));
1229 if (beam_slice.is_empty ())
1230 beam_slice = Slice (0,0);
1231 Interval beam_multiplicity(beam_slice[LEFT],
1232 beam_slice[RIGHT]);
1235 feather dir = 1 , relx 0->1 : factor 0 -> 1
1236 feather dir = 0 , relx 0->1 : factor 1 -> 1
1237 feather dir = -1, relx 0->1 : factor 1 -> 0
1239 Real feather_factor = 1;
1240 if (feather_dir > 0)
1241 feather_factor = relx;
1242 else if (feather_dir < 0)
1243 feather_factor = 1 - relx;
1245 stem_y += feather_factor * beam_translation
1246 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1247 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1248 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1250 return stem_y + id;
1254 Hmm. At this time, beam position and slope are determined. Maybe,
1255 stem directions and length should set to relative to the chord's
1256 position of the beam. */
1257 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1259 Beam::set_stem_lengths (SCM smob)
1261 Grob *me = unsmob_grob (smob);
1263 /* trigger callbacks. */
1264 (void) me->get_property ("direction");
1265 (void) me->get_property ("beaming");
1267 SCM posns = me->get_property ("positions");
1269 extract_grob_set (me, "stems", stems);
1270 if (!stems.size ())
1271 return posns;
1273 Grob *common[2];
1274 for (int a = 2; a--;)
1275 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1277 Drul_array<Real> pos = ly_scm2realdrul (posns);
1278 Real staff_space = Staff_symbol_referencer::staff_space (me);
1279 scale_drul (&pos, staff_space);
1281 bool gap = false;
1282 Real thick = 0.0;
1283 if (robust_scm2int (me->get_property ("gap-count"), 0))
1285 gap = true;
1286 thick = get_beam_thickness (me);
1289 Grob *fvs = first_normal_stem (me);
1290 Grob *lvs = last_normal_stem (me);
1292 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1293 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1294 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1296 for (vsize i = 0; i < stems.size (); i++)
1298 Grob *s = stems[i];
1300 bool french = to_boolean (s->get_property ("french-beaming"));
1301 Real stem_y = calc_stem_y (me, s, common,
1302 xl, xr, feather_dir,
1303 pos, french && s != lvs && s!= fvs);
1306 Make the stems go up to the end of the beam. This doesn't matter
1307 for normal beams, but for tremolo beams it looks silly otherwise.
1309 if (gap
1310 && !Stem::is_invisible (s))
1311 stem_y += thick * 0.5 * get_grob_direction (s);
1314 Do set_stemend for invisible stems too, so tuplet brackets
1315 have a reference point for sloping
1317 Stem::set_stemend (s, 2 * stem_y / staff_space);
1320 return posns;
1323 void
1324 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1326 extract_grob_set (me, "stems", stems);
1328 Direction d = LEFT;
1329 for (vsize i = 0; i < stems.size (); i++)
1332 Don't overwrite user settings.
1336 Grob *stem = stems[i];
1337 SCM beaming_prop = stem->get_property ("beaming");
1338 if (beaming_prop == SCM_EOL
1339 || index_get_cell (beaming_prop, d) == SCM_EOL)
1341 int count = beaming->beamlet_count (i, d);
1342 if (i > 0
1343 && i + 1 < stems.size ()
1344 && Stem::is_invisible (stem))
1345 count = min (count, beaming->beamlet_count (i,-d));
1347 if ( ((i == 0 && d == LEFT)
1348 || (i == stems.size ()-1 && d == RIGHT))
1349 && stems.size () > 1
1350 && to_boolean (me->get_property ("clip-edges")))
1351 count = 0;
1353 Stem::set_beaming (stem, count, d);
1356 while (flip (&d) != LEFT);
1361 Beam::forced_stem_count (Grob *me)
1363 extract_grob_set (me, "normal-stems", stems);
1365 int f = 0;
1366 for (vsize i = 0; i < stems.size (); i++)
1368 Grob *s = stems[i];
1370 /* I can imagine counting those boundaries as a half forced stem,
1371 but let's count them full for now. */
1372 Direction defdir = to_dir (s->get_property ("default-direction"));
1374 if (abs (Stem::chord_start_y (s)) > 0.1
1375 && defdir
1376 && get_grob_direction (s) != defdir)
1377 f++;
1379 return f;
1383 Beam::normal_stem_count (Grob *me)
1385 extract_grob_set (me, "normal-stems", stems);
1386 return stems.size ();
1389 Grob *
1390 Beam::first_normal_stem (Grob *me)
1392 extract_grob_set (me, "normal-stems", stems);
1393 return stems.size () ? stems[0] : 0;
1396 Grob *
1397 Beam::last_normal_stem (Grob *me)
1399 extract_grob_set (me, "normal-stems", stems);
1400 return stems.size () ? stems.back () : 0;
1404 [TODO]
1406 handle rest under beam (do_post: beams are calculated now)
1407 what about combination of collisions and rest under beam.
1409 Should lookup
1411 rest -> stem -> beam -> interpolate_y_position ()
1413 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1415 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1417 Grob *rest = unsmob_grob (smob);
1418 if (scm_is_number (rest->get_property ("staff-position")))
1419 return scm_from_int (0);
1421 Real offset = robust_scm2double (prev_offset, 0.0);
1423 Grob *st = unsmob_grob (rest->get_object ("stem"));
1424 Grob *stem = st;
1425 if (!stem)
1426 return scm_from_double (0.0);
1427 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1428 if (!beam
1429 || !Beam::has_interface (beam)
1430 || !Beam::normal_stem_count (beam))
1431 return scm_from_double (0.0);
1433 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1434 Drul_array<Real> (0,0)));
1436 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1438 scale_drul (&pos, staff_space);
1440 Real dy = pos[RIGHT] - pos[LEFT];
1442 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1443 last_normal_stem (beam));
1444 extract_grob_set (beam, "stems", stems);
1446 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1448 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1449 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1450 Real slope = dy && dx ? dy / dx : 0;
1452 Direction d = get_grob_direction (stem);
1453 Real stem_y = pos[LEFT]
1454 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1456 Real beam_translation = get_beam_translation (beam);
1457 Real beam_thickness = Beam::get_beam_thickness (beam);
1460 TODO: this is not strictly correct for 16th knee beams.
1462 int beam_count
1463 = Stem::beam_multiplicity (stem).length () + 1;
1465 Real height_of_my_beams = beam_thickness / 2
1466 + (beam_count - 1) * beam_translation;
1467 Real beam_y = stem_y - d * height_of_my_beams;
1469 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1472 TODO: this is dubious, because this call needs the info we're
1473 computing right now.
1475 Interval rest_extent = rest->extent (common_y, Y_AXIS);
1476 rest_extent.translate (offset);
1478 Real rest_dim = rest_extent[d];
1479 Real minimum_distance
1480 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1481 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1483 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1485 shift /= staff_space;
1486 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1488 /* Always move discretely by half spaces */
1489 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1491 /* Inside staff, move by whole spaces*/
1492 if ((rest_extent[d] + staff_space * shift) * d
1493 < rad
1494 || (rest_extent[-d] + staff_space * shift) * -d
1495 < rad)
1496 shift = ceil (fabs (shift)) * sign (shift);
1498 return scm_from_double (offset + staff_space * shift);
1501 bool
1502 Beam::is_knee (Grob *me)
1504 SCM k = me->get_property ("knee");
1505 if (scm_is_bool (k))
1506 return ly_scm2bool (k);
1508 bool knee = false;
1509 int d = 0;
1510 extract_grob_set (me, "stems", stems);
1511 for (vsize i = stems.size (); i--;)
1513 Direction dir = get_grob_direction (stems[i]);
1514 if (d && d != dir)
1516 knee = true;
1517 break;
1519 d = dir;
1522 me->set_property ("knee", ly_bool2scm (knee));
1524 return knee;
1527 bool
1528 Beam::is_cross_staff (Grob *me)
1530 extract_grob_set (me, "stems", stems);
1531 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1532 for (vsize i = 0; i < stems.size (); i++)
1533 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1534 return true;
1535 return false;
1538 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1540 Beam::calc_cross_staff (SCM smob)
1542 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1546 Beam::get_direction_beam_count (Grob *me, Direction d)
1548 extract_grob_set (me, "stems", stems);
1549 int bc = 0;
1551 for (vsize i = stems.size (); i--;)
1554 Should we take invisible stems into account?
1556 if (get_grob_direction (stems[i]) == d)
1557 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1560 return bc;
1563 ADD_INTERFACE (Beam,
1564 "A beam.\n"
1565 "\n"
1566 "The @code{beam-thickness} property is the weight of beams,"
1567 " measured in staffspace. The @code{direction} property is"
1568 " not user-serviceable. Use the @code{direction} property"
1569 " of @code{Stem} instead.\n"
1570 "\n"
1571 "The following properties may be set in the @code{details}"
1572 " list.\n"
1573 "\n"
1574 "@table @code\n"
1575 "@item stem-length-demerit-factor\n"
1576 "Demerit factor used for inappropriate stem lengths.\n"
1577 "@item secondary-beam-demerit\n"
1578 "Demerit used in quanting calculations for multiple"
1579 " beams.\n"
1580 "@item region-size\n"
1581 "Size of region for checking quant scores.\n"
1582 "@item beam-eps\n"
1583 "Epsilon for beam quant code to check for presence"
1584 " in gap.\n"
1585 "@item stem-length-limit-penalty\n"
1586 "Penalty for differences in stem lengths on a beam.\n"
1587 "@item damping-direction-penalty\n"
1588 "Demerit penalty applied when beam direction is different"
1589 " from damping direction.\n"
1590 "@item hint-direction-penalty\n"
1591 "Demerit penalty applied when beam direction is different"
1592 " from damping direction, but damping slope is"
1593 " <= @code{round-to-zero-slope}.\n"
1594 "@item musical-direction-factor\n"
1595 "Demerit scaling factor for difference between"
1596 " beam slope and music slope.\n"
1597 "@item ideal-slope-factor\n"
1598 "Demerit scaling factor for difference between"
1599 " beam slope and damping slope.\n"
1600 "@item round-to-zero-slope\n"
1601 "Damping slope which is considered zero for purposes of"
1602 " calculating direction penalties.\n"
1603 "@end table\n",
1605 /* properties */
1606 "annotation "
1607 "auto-knee-gap "
1608 "beamed-stem-shorten "
1609 "beaming "
1610 "beam-thickness "
1611 "break-overshoot "
1612 "clip-edges "
1613 "concaveness "
1614 "damping "
1615 "details "
1616 "direction "
1617 "gap "
1618 "gap-count "
1619 "grow-direction "
1620 "inspect-quants "
1621 "knee "
1622 "length-fraction "
1623 "least-squares-dy "
1624 "neutral-direction "
1625 "normal-stems "
1626 "positions "
1627 "quant-score "
1628 "quantized-positions "
1629 "shorten "
1630 "stems "