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 "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"
48 #include "least-squares.hh"
52 #include "note-head.hh"
53 #include "output-def.hh"
54 #include "pointer-group-interface.hh"
55 #include "rhythmic-head.hh"
57 #include "staff-symbol-referencer.hh"
61 #if DEBUG_BEAM_SCORING
62 #include "text-interface.hh" // debug output.
63 #include "font-interface.hh" // debug output.
69 Beam_stem_segment::Beam_stem_segment ()
71 max_connect_ = 1000; // infinity
81 beam_segment_less (Beam_segment const& a, Beam_segment const& b)
83 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
86 Beam_segment::Beam_segment ()
92 Beam::add_stem (Grob *me, Grob *s)
94 if (Stem::get_beam (s))
96 programming_error ("Stem already has beam");
100 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
101 s->set_object ("beam", me->self_scm ());
102 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
106 Beam::get_beam_thickness (Grob *me)
108 return robust_scm2double (me->get_property ("beam-thickness"), 0)
109 * Staff_symbol_referencer::staff_space (me);
112 /* Return the translation between 2 adjoining beams. */
114 Beam::get_beam_translation (Grob *me)
116 int beam_count = get_beam_count (me);
117 Real staff_space = Staff_symbol_referencer::staff_space (me);
118 Real line = Staff_symbol_referencer::line_thickness (me);
119 Real beam_thickness = get_beam_thickness (me);
120 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
122 Real beam_translation = beam_count < 4
123 ? (2 * staff_space + line - beam_thickness) / 2.0
124 : (3 * staff_space + line - beam_thickness) / 3.0;
126 return fract * beam_translation;
129 /* Maximum beam_count. */
131 Beam::get_beam_count (Grob *me)
135 extract_grob_set (me, "stems", stems);
136 for (vsize i = 0; i < stems.size (); i++)
138 Grob *stem = stems[i];
139 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
144 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
146 Beam::calc_normal_stems (SCM smob)
148 Grob *me = unsmob_grob (smob);
150 extract_grob_set (me, "stems", stems);
151 SCM val = Grob_array::make_array ();
152 Grob_array *ga = unsmob_grob_array (val);
153 for (vsize i = 0; i < stems.size (); i++)
154 if (Stem::is_normal_stem (stems[i]))
160 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
162 Beam::calc_direction (SCM smob)
164 Grob *me = unsmob_grob (smob);
166 /* Beams with less than 2 two stems don't make much sense, but could happen
173 Direction dir = CENTER;
175 int count = normal_stem_count (me);
178 extract_grob_set (me, "stems", stems);
179 if (stems.size () == 0)
181 me->warning (_ ("removing beam with no stems"));
184 return SCM_UNSPECIFIED;
188 Grob *stem = first_normal_stem (me);
191 This happens for chord tremolos.
196 if (is_direction (stem->get_property_data ("direction")))
197 dir = to_dir (stem->get_property_data ("direction"));
199 dir = to_dir (stem->get_property ("default-direction"));
206 dir = get_default_dir (me);
208 consider_auto_knees (me);
213 set_stem_directions (me, dir);
216 return scm_from_int (dir);
221 /* We want a maximal number of shared beams, but if there is choice, we
222 * take the one that is closest to the end of the stem. This is for
234 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
238 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
242 for (int i = lslice[-left_dir];
243 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
246 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
248 int k = -right_dir * scm_to_int (scm_car (s)) + i;
249 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
253 if (count >= best_count)
263 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
265 Beam::calc_beaming (SCM smob)
267 Grob *me = unsmob_grob (smob);
269 extract_grob_set (me, "stems", stems);
272 last_int.set_empty ();
274 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
275 Direction last_dir = CENTER;
276 for (vsize i = 0; i < stems.size (); i++)
278 Grob *this_stem = stems[i];
279 SCM this_beaming = this_stem->get_property ("beaming");
281 Direction this_dir = get_grob_direction (this_stem);
282 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
284 int start_point = position_with_maximal_common_beams
285 (last_beaming, this_beaming,
286 last_dir ? last_dir : this_dir,
293 new_slice.set_empty ();
294 SCM s = index_get_cell (this_beaming, d);
295 for (; scm_is_pair (s); s = scm_cdr (s))
298 = start_point - this_dir * scm_to_int (scm_car (s));
300 new_slice.add_point (new_beam_pos);
301 scm_set_car_x (s, scm_from_int (new_beam_pos));
304 while (flip (&d) != LEFT);
306 if (!new_slice.is_empty ())
307 last_int = new_slice;
312 FIXME: what's this for?
314 SCM s = scm_cdr (this_beaming);
315 for (; scm_is_pair (s); s = scm_cdr (s))
317 int np = -this_dir * scm_to_int (scm_car (s));
318 scm_set_car_x (s, scm_from_int (np));
319 last_int.add_point (np);
323 if (scm_ilength (scm_cdr (this_beaming)) > 0)
325 last_beaming = this_beaming;
334 operator <(Beam_stem_segment const &a,
335 Beam_stem_segment const &b)
337 return a.rank_ < b.rank_;
340 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
342 // TODO - should store result in a property?
344 Beam::get_beam_segments (Grob *me_grob, Grob **common)
346 /* ugh, this has a side-effect that we need to ensure that
347 Stem #'beaming is correct */
348 (void) me_grob->get_property ("beaming");
350 Spanner *me = dynamic_cast<Spanner*> (me_grob);
352 extract_grob_set (me, "stems", stems);
353 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
355 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
356 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
360 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
361 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
363 Position_stem_segments_map stem_segments;
364 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
366 /* There are two concepts of "rank" that are used in the following code.
367 The beam_rank is the vertical position of the beam (larger numbers are
368 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
369 is the horizontal position of the segment (this is incremented by two
370 for each stem; the beam segment on the right side of the stem has
371 a higher rank (by one) than its neighbour to the left). */
373 for (vsize i = 0; i < stems.size (); i++)
375 Grob *stem = stems[i];
376 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
377 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
378 SCM beaming = stem->get_property ("beaming");
382 // Find the maximum and minimum beam ranks.
383 // Given that RANKS is never reset to empty, the interval will always be
384 // smallest for the left beamlet of the first stem, and then it might grow.
385 // Do we really want this? (It only affects the tremolo gaps) --jneem
386 for (SCM s = index_get_cell (beaming, d);
387 scm_is_pair (s); s = scm_cdr (s))
389 if (!scm_is_integer (scm_car (s)))
392 int beam_rank = scm_to_int (scm_car (s));
393 ranks.add_point (beam_rank);
396 for (SCM s = index_get_cell (beaming, d);
397 scm_is_pair (s); s = scm_cdr (s))
399 if (!scm_is_integer (scm_car (s)))
402 int beam_rank = scm_to_int (scm_car (s));
403 Beam_stem_segment seg;
405 seg.stem_x_ = stem_x;
406 seg.rank_ = 2 * i + (d+1)/2;
407 seg.width_ = stem_width;
410 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
412 Direction stem_dir = get_grob_direction (stem);
415 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
416 stem_segments[beam_rank].push_back (seg);
419 while (flip (&d) != LEFT);
422 Drul_array<Real> break_overshoot
423 = robust_scm2drul (me->get_property ("break-overshoot"),
424 Drul_array<Real> (-0.5, 0.0));
426 vector<Beam_segment> segments;
427 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
428 i != stem_segments.end (); i++)
430 vector<Beam_stem_segment> segs = (*i).second;
431 vector_sort (segs, less<Beam_stem_segment> ());
433 Beam_segment current;
435 // Iterate over all of the segments of the current beam rank,
436 // merging the adjacent Beam_stem_segments into one Beam_segment
438 int vertical_count = (*i).first;
439 for (vsize j = 0; j < segs.size (); j++)
441 // Keeping track of the different directions here is a little tricky.
442 // segs[j].dir_ is the direction of the beam segment relative to the stem
443 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
444 // its stem) whereas event_dir refers to the edge of the beam segment that
445 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
446 // are looking at that edge of the beam segment that is furthest from its
448 Direction event_dir = LEFT;
449 Beam_stem_segment const& seg = segs[j];
452 Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
453 // TODO: make names clearer? --jneem
454 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
455 // on_beam_bound: whether the current segment is on the boundary of just that part
456 // of the beam with the current beam_rank
457 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
458 : seg.stem_index_ == stems.size() - 1;
459 bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
460 j == segs.size () - 1;
461 bool inside_stem = (event_dir == LEFT)
462 ? seg.stem_index_ > 0
463 : seg.stem_index_ + 1 < stems.size () ;
465 bool event = on_beam_bound
466 || abs (seg.rank_ - neighbor_seg.rank_) > 1
467 || (abs (vertical_count) >= seg.max_connect_
468 || abs (vertical_count) >= neighbor_seg.max_connect_);
471 // Then this edge of the current segment is irrelevent because it will
472 // be connected with the next segment in the event_dir direction.
475 current.vertical_count_ = vertical_count;
476 current.horizontal_[event_dir] = seg.stem_x_;
477 if (seg.dir_ == event_dir)
478 // then we are examining the edge of a beam segment that is furthest
482 && me->get_bound (event_dir)->break_status_dir ())
484 current.horizontal_[event_dir]
485 = (robust_relative_extent (me->get_bound (event_dir),
486 commonx, X_AXIS)[RIGHT]
487 + event_dir * break_overshoot[event_dir]);
491 Grob *stem = stems[seg.stem_index_];
492 Drul_array<Real> beamlet_length =
493 robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
494 Drul_array<Real> max_proportion =
495 robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
496 Real length = beamlet_length[seg.dir_];
500 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
501 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
503 length = min (length,
504 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
506 current.horizontal_[event_dir] += event_dir * length;
510 // we are examining the edge of a beam segment that is closest
511 // (ie. touching, unless there is a gap) its stem.
513 current.horizontal_[event_dir] += event_dir * seg.width_/2;
516 current.horizontal_[event_dir] -= event_dir * gap_length;
518 if (Stem::is_invisible (seg.stem_))
521 Need to do this in case of whole notes. We don't want the
522 heads to collide with the beams.
524 extract_grob_set (seg.stem_, "note-heads", heads);
526 for (vsize k = 0; k < heads.size (); k ++)
527 current.horizontal_[event_dir]
528 = event_dir * min (event_dir * current.horizontal_[event_dir],
531 * heads[k]->extent (commonx,
532 X_AXIS)[-event_dir]);
537 if (event_dir == RIGHT)
539 segments.push_back (current);
540 current = Beam_segment ();
543 while (flip (&event_dir) != LEFT);
551 MAKE_SCHEME_CALLBACK (Beam, print, 1);
553 Beam::print (SCM grob)
555 Spanner *me = unsmob_spanner (grob);
557 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
560 if (normal_stem_count (me))
562 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
563 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
567 extract_grob_set (me, "stems", stems);
568 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
569 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
572 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
574 SCM posns = me->get_property ("quantized-positions");
576 if (!is_number_pair (posns))
578 programming_error ("no beam positions?");
579 pos = Interval (0, 0);
582 pos = ly_scm2realdrul (posns);
584 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
586 Real dy = pos[RIGHT] - pos[LEFT];
587 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
589 Real beam_thickness = get_beam_thickness (me);
590 Real beam_dy = get_beam_translation (me);
592 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
594 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
598 int extreme = (segments[0].vertical_count_ == 0
599 ? segments[0].vertical_count_
600 : segments.back ().vertical_count_);
602 for (vsize i = 0; i < segments.size (); i ++)
604 Real local_slope = slope;
606 Makes local slope proportional to the ratio of the length of this beam
610 local_slope += (feather_dir * segments[i].vertical_count_
612 * placements.length ()
615 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
617 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
618 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
620 Interval weights (1 - multiplier, multiplier);
622 if (feather_dir != LEFT)
625 // we need two translations: the normal one and
626 // the one of the lowest segment
627 int idx[] = {i, extreme};
628 Real translations[2];
630 for (int j = 0; j < 2; j++)
631 translations[j] = slope
632 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
633 + pos.linear_combination (CENTER)
634 + beam_dy * segments[idx[j]].vertical_count_;
636 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
639 Tricky. The manipulation of the variable `weighted_average' below ensures
640 that beams with a RIGHT grow direction will start from the position of the
641 lowest segment at 0, and this error will decrease and decrease over the
642 course of the beam. Something with a LEFT grow direction, on the other
643 hand, will always start in the correct place but progressively accrue
644 error at broken places. This code shifts beams up given where they are
645 in the total span length (controlled by the variable `multiplier'). To
646 better understand what it does, try commenting it out: you'll see that
647 all of the RIGHT growing beams immediately start too low and get better
648 over line breaks, whereas all of the LEFT growing beams start just right
649 and get worse over line breaks.
651 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
653 if (segments[0].vertical_count_ < 0 && feather_dir)
654 weighted_average += beam_dy * (segments.size () - 1) * factor;
656 b.translate_axis (weighted_average, Y_AXIS);
658 the_beam.add_stencil (b);
662 #if (DEBUG_BEAM_SCORING)
663 SCM annotation = me->get_property ("annotation");
664 if (scm_is_string (annotation))
666 extract_grob_set (me, "stems", stems);
669 This code prints the demerits for each beam. Perhaps this
670 should be switchable for those who want to twiddle with the
674 SCM properties = Font_interface::text_font_alist_chain (me);
676 properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
679 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
681 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
682 (me->layout ()->self_scm (), properties, annotation));
684 if (!score.is_empty ())
686 score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
687 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
692 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
693 return the_beam.smobbed_copy ();
697 Beam::get_default_dir (Grob *me)
699 extract_grob_set (me, "stems", stems);
701 Drul_array<Real> extremes (0.0, 0.0);
702 for (iterof (s, stems); s != stems.end (); s++)
704 Interval positions = Stem::head_positions (*s);
708 if (sign (positions[d]) == d)
709 extremes[d] = d * max (d * positions[d], d * extremes[d]);
711 while (flip (&d) != DOWN);
714 Drul_array<int> total (0, 0);
715 Drul_array<int> count (0, 0);
717 bool force_dir = false;
718 for (vsize i = 0; i < stems.size (); i++)
721 Direction stem_dir = CENTER;
722 SCM stem_dir_scm = s->get_property_data ("direction");
723 if (is_direction (stem_dir_scm))
725 stem_dir = to_dir (stem_dir_scm);
729 stem_dir = to_dir (s->get_property ("default-direction"));
732 stem_dir = to_dir (s->get_property ("neutral-direction"));
737 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
744 if (abs (extremes[UP]) > -extremes[DOWN])
746 else if (extremes[UP] < -extremes[DOWN])
750 Direction dir = CENTER;
751 Direction d = CENTER;
752 if ((d = (Direction) sign (count[UP] - count[DOWN])))
756 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
758 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
761 dir = to_dir (me->get_property ("neutral-direction"));
766 /* Set all stems with non-forced direction to beam direction.
767 Urg: non-forced should become `without/with unforced' direction,
768 once stem gets cleaned-up. */
770 Beam::set_stem_directions (Grob *me, Direction d)
772 extract_grob_set (me, "stems", stems);
774 for (vsize i = 0; i < stems.size (); i++)
778 SCM forcedir = s->get_property_data ("direction");
779 if (!to_dir (forcedir))
780 set_grob_direction (s, d);
785 Only try horizontal beams for knees. No reliable detection of
786 anything else is possible here, since we don't know funky-beaming
787 settings, or X-distances (slopes!) People that want sloped
788 knee-beams, should set the directions manually.
793 this routine should take into account the stemlength scoring
794 of a possible knee/nonknee beam.
797 Beam::consider_auto_knees (Grob *me)
799 SCM scm = me->get_property ("auto-knee-gap");
800 if (!scm_is_number (scm))
807 extract_grob_set (me, "normal-stems", stems);
809 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
810 Real staff_space = Staff_symbol_referencer::staff_space (me);
812 vector<Interval> head_extents_array;
813 for (vsize i = 0; i < stems.size (); i++)
815 Grob *stem = stems[i];
817 Interval head_extents = Stem::head_positions (stem);
818 if (!head_extents.is_empty ())
820 head_extents[LEFT] += -1;
821 head_extents[RIGHT] += 1;
822 head_extents *= staff_space * 0.5;
825 We could subtract beam Y position, but this routine only
826 sets stem directions, a constant shift does not have an
829 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
831 if (to_dir (stem->get_property_data ("direction")))
833 Direction stemdir = to_dir (stem->get_property ("direction"));
834 head_extents[-stemdir] = -stemdir * infinity_f;
837 head_extents_array.push_back (head_extents);
839 gaps.remove_interval (head_extents);
843 Real max_gap_len = 0.0;
845 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
847 Interval gap = gaps.allowed_regions_[i];
850 the outer gaps are not knees.
852 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
855 if (gap.length () >= max_gap_len)
857 max_gap_len = gap.length ();
862 Real beam_translation = get_beam_translation (me);
863 Real beam_thickness = Beam::get_beam_thickness (me);
864 int beam_count = Beam::get_beam_count (me);
865 Real height_of_beams = beam_thickness / 2
866 + (beam_count - 1) * beam_translation;
867 Real threshold = scm_to_double (scm) + height_of_beams;
869 if (max_gap_len > threshold)
872 for (vsize i = 0; i < stems.size (); i++)
874 Grob *stem = stems[i];
875 Interval head_extents = head_extents_array[j++];
877 Direction d = (head_extents.center () < max_gap.center ())
880 stem->set_property ("direction", scm_from_int (d));
882 head_extents.intersect (max_gap);
883 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
888 /* Set stem's shorten property if unset.
891 take some y-position (chord/beam/nearest?) into account
892 scmify forced-fraction
894 This is done in beam because the shorten has to be uniform over the
901 set_minimum_dy (Grob *me, Real *dy)
906 If dy is smaller than the smallest quant, we
907 get absurd direction-sign penalties.
910 Real ss = Staff_symbol_referencer::staff_space (me);
911 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
912 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
913 Real sit = (beam_thickness - slt) / 2;
915 Real hang = 1.0 - (beam_thickness - slt) / 2;
917 *dy = sign (*dy) * max (fabs (*dy),
918 min (min (sit, inter), hang));
924 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
926 Beam::calc_stem_shorten (SCM smob)
928 Grob *me = unsmob_grob (smob);
931 shortening looks silly for x staff beams
934 return scm_from_int (0);
936 Real forced_fraction = 1.0 * forced_stem_count (me)
937 / normal_stem_count (me);
939 int beam_count = get_beam_count (me);
941 SCM shorten_list = me->get_property ("beamed-stem-shorten");
942 if (shorten_list == SCM_EOL)
943 return scm_from_int (0);
945 Real staff_space = Staff_symbol_referencer::staff_space (me);
948 = robust_list_ref (beam_count -1, shorten_list);
949 Real shorten = scm_to_double (shorten_elt) * staff_space;
951 shorten *= forced_fraction;
955 return scm_from_double (shorten);
957 return scm_from_double (0.0);
962 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
964 extract_grob_set (me, "stems", stems);
966 return default_value;
968 Interval head_positions;
970 for (vsize i = 0; i < stems.size(); i++)
972 head_positions.unite (Stem::head_positions (stems[i]));
973 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
976 Direction dir = get_grob_direction (me);
977 Real y = head_positions[dir]
978 * 0.5 * Staff_symbol_referencer::staff_space (me)
979 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
981 y /= Staff_symbol_referencer::staff_space (me);
982 return Interval (y,y);
987 Compute a first approximation to the beam slope.
989 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
991 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
993 Grob *me = unsmob_grob (smob);
995 int count = normal_stem_count (me);
998 return ly_interval2scm (no_visible_stem_positions (me, pos));
1000 vector<Real> x_posns;
1001 extract_grob_set (me, "normal-stems", stems);
1002 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1003 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1005 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1007 Grob *fvs = first_normal_stem (me);
1008 Grob *lvs = last_normal_stem (me);
1010 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1011 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1012 Stem::get_stem_info (lvs).ideal_y_
1013 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1015 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1016 for (vsize i = 0; i < stems.size (); i++)
1020 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1021 x_posns.push_back (x);
1023 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1029 if (!ideal.delta ())
1031 Interval chord (Stem::chord_start_y (stems[0]),
1032 Stem::chord_start_y (stems.back ()));
1034 /* Simple beams (2 stems) on middle line should be allowed to be
1037 However, if both stems reach middle line,
1038 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1040 For that case, we apply artificial slope */
1041 if (!ideal[LEFT] && chord.delta () && count == 2)
1044 Direction d = (Direction) (sign (chord.delta ()) * UP);
1045 pos[d] = get_beam_thickness (me) / 2;
1052 For broken beams this doesn't work well. In this case, the
1053 slope esp. of the first part of a broken beam should predict
1054 where the second part goes.
1056 ldy = pos[RIGHT] - pos[LEFT];
1060 vector<Offset> ideals;
1061 for (vsize i = 0; i < stems.size (); i++)
1064 ideals.push_back (Offset (x_posns[i],
1065 Stem::get_stem_info (s).ideal_y_
1066 + s->relative_coordinate (commony, Y_AXIS)
1070 minimise_least_squares (&slope, &y, ideals);
1074 set_minimum_dy (me, &dy);
1077 pos = Interval (y, (y + dy));
1081 "position" is relative to the staff.
1083 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1085 me->set_property ("least-squares-dy", scm_from_double (ldy));
1086 return ly_interval2scm (pos);
1090 // Assuming V is not empty, pick a 'reasonable' point inside V.
1092 point_in_interval (Interval v, Real dist)
1094 if (isinf (v[DOWN]))
1095 return v[UP] - dist;
1096 else if (isinf (v[UP]))
1097 return v[DOWN] + dist;
1103 We can't combine with previous function, since check concave and
1104 slope damping comes first.
1106 TODO: we should use the concaveness to control the amount of damping
1109 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1111 Beam::shift_region_to_valid (SCM grob, SCM posns)
1113 Grob *me = unsmob_grob (grob);
1118 vector<Real> x_posns;
1119 extract_grob_set (me, "stems", stems);
1120 extract_grob_set (me, "covered-grobs", covered);
1122 Grob *common[NO_AXES] = { me, me };
1123 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1124 common[a] = common_refpoint_of_array (stems, me, a);
1125 common[a] = common_refpoint_of_array (covered, common[a], a);
1127 Grob *fvs = first_normal_stem (me);
1132 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1133 for (vsize i = 0; i < stems.size (); i++)
1137 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1138 x_posns.push_back (x);
1141 Grob *lvs = last_normal_stem (me);
1142 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1144 Drul_array<Real> pos = ly_scm2interval (posns);
1146 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1148 Real beam_dy = pos[RIGHT] - pos[LEFT];
1149 Real beam_left_y = pos[LEFT];
1150 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1153 Shift the positions so that we have a chance of finding good
1154 quants (i.e. no short stem failures.)
1156 Interval feasible_left_point;
1157 feasible_left_point.set_full ();
1159 for (vsize i = 0; i < stems.size (); i++)
1162 if (Stem::is_invisible (s))
1165 Direction d = get_grob_direction (s);
1167 = Stem::get_stem_info (s).shortest_y_
1168 - slope * x_posns [i];
1171 left_y is now relative to the stem S. We want relative to
1172 ourselves, so translate:
1175 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1176 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1182 feasible_left_point.intersect (flp);
1186 We have two intervals here, one for the up variant (beams goes
1187 over the collision) one for the down.
1189 Drul_array<Interval> collision_free (feasible_left_point,
1190 feasible_left_point);
1192 vector<Grob*> filtered;
1194 We only update these for objects that are too large for quanting
1195 to find a workaround. Typically, these are notes with
1196 stems, and timesig/keysig/clef, which take out the entire area
1197 inside the staff as feasible.
1199 The code below disregards the thickness and multiplicity of the
1200 beam. This should not be a problem, as the beam quanting will
1201 take care of computing the impact those exactly.
1203 Real min_y_size = 2.0;
1204 for (vsize i = 0; i < covered.size(); i++)
1206 if (!covered[i]->is_live())
1209 // TODO - use this logic in is_cross_staff.
1210 if (is_cross_staff (me)
1211 && Align_interface::has_interface (common_refpoint_of_array (stems, me, Y_AXIS)))
1214 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1218 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1219 b[a] = covered[i]->extent (common[a], a);
1221 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1224 if (intersection (b[X_AXIS], x_span).is_empty ())
1227 filtered.push_back (covered[i]);
1228 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1229 if (head_stem && Stem::is_normal_stem (head_stem)
1230 && Note_head::has_interface (covered[i]))
1232 if (Stem::get_beam (head_stem))
1235 We must assume that stems are infinitely long in this
1236 case, as asking for the length of the stem typically
1237 leads to circular dependencies.
1239 This strategy assumes that we don't want to handle the
1240 collision of beams in opposite non-forced directions
1241 with this code, where shortening the stems of both
1242 would resolve the problem, eg.
1252 Such beams would need a coordinating grob to resolve
1253 the collision, since both will likely want to occupy
1256 Direction stemdir = get_grob_direction (head_stem);
1257 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1261 // TODO - should we include the extent of the stem here?
1265 if (b[Y_AXIS].length () < min_y_size)
1271 Real x = b[X_AXIS][d] - x_span[LEFT];
1272 Real dy = slope * x;
1274 Direction yd = DOWN;
1277 Real left_y = b[Y_AXIS][yd];
1279 if (left_y == yd * infinity_f)
1281 collision_free[yd].set_empty ();
1287 // Translate back to beam as ref point.
1288 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1291 allowed.set_full ();
1293 allowed[-yd] = left_y;
1294 collision_free[yd].intersect (allowed);
1296 while (flip (&yd) != DOWN);
1298 while (flip (&d) != LEFT);
1302 Pointer_group_interface::get_grob_array (me,
1303 ly_symbol2scm ("covered-grobs"));
1304 arr->set_array (filtered);
1306 if (collision_free[DOWN].contains (beam_left_y)
1307 || collision_free[UP].contains (beam_left_y))
1309 // We're good to go. Do nothing.
1311 else if (collision_free[DOWN].is_empty() != collision_free[UP].is_empty())
1313 // Only one of them offers is feasible solution. Pick that one.
1315 (!collision_free[DOWN].is_empty()) ?
1316 collision_free[DOWN] :
1319 beam_left_y = point_in_interval (v, 2.0);
1321 else if (!collision_free[DOWN].is_empty ()
1322 && !collision_free[UP].is_empty ())
1324 // Above and below are candidates, take the one closest to the
1325 // starting solution.
1327 (collision_free[DOWN].distance (beam_left_y)
1328 < collision_free[UP].distance (beam_left_y)) ?
1329 collision_free[DOWN] : collision_free[UP];
1331 beam_left_y = point_in_interval (best, 2.0);
1333 else if (!feasible_left_point.is_empty ())
1335 // We are somewhat screwed: we have a collision, but at least
1336 // there is a way to satisfy stem length constraints.
1337 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1341 // We are completely screwed.
1342 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1345 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1346 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1348 return ly_interval2scm (pos);
1351 /* This neat trick is by Werner Lemberg,
1352 damped = tanh (slope)
1353 corresponds with some tables in [Wanske] CHECKME */
1354 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1356 Beam::slope_damping (SCM smob, SCM posns)
1358 Grob *me = unsmob_grob (smob);
1359 Drul_array<Real> pos = ly_scm2interval (posns);
1361 if (normal_stem_count (me) <= 1)
1364 SCM s = me->get_property ("damping");
1365 Real damping = scm_to_double (s);
1366 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1367 if (concaveness >= 10000)
1369 pos[LEFT] = pos[RIGHT];
1370 me->set_property ("least-squares-dy", scm_from_double (0));
1376 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1378 Real dy = pos[RIGHT] - pos[LEFT];
1380 Grob *fvs = first_normal_stem (me);
1381 Grob *lvs = last_normal_stem (me);
1383 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1385 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1386 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1388 Real slope = dy && dx ? dy / dx : 0;
1390 slope = 0.6 * tanh (slope) / (damping + concaveness);
1392 Real damped_dy = slope * dx;
1394 set_minimum_dy (me, &damped_dy);
1396 pos[LEFT] += (dy - damped_dy) / 2;
1397 pos[RIGHT] -= (dy - damped_dy) / 2;
1399 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1402 return ly_interval2scm (pos);
1406 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1408 Beam::quanting (SCM smob, SCM posns)
1410 Grob *me = unsmob_grob (smob);
1411 Drul_array<Real> ys(0, 0);
1412 ys = robust_scm2drul (posns, ys);
1413 Beam_scoring_problem problem (me, ys);
1415 ys = problem.solve ();
1416 return ly_interval2scm (ys);
1421 Report slice containing the numbers that are both in (car BEAMING)
1425 where_are_the_whole_beams (SCM beaming)
1429 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1431 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1433 l.add_point (scm_to_int (scm_car (s)));
1439 /* Return the Y position of the stem-end, given the Y-left, Y-right
1440 in POS for stem S. This Y position is relative to S. */
1442 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1443 Real xl, Real xr, Direction feather_dir,
1444 Drul_array<Real> pos, bool french)
1446 Real beam_translation = get_beam_translation (me);
1447 Direction stem_dir = get_grob_direction (stem);
1450 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1451 Real xdir = 2*relx-1;
1453 Real stem_y = linear_combination(pos, xdir);
1455 SCM beaming = stem->get_property ("beaming");
1457 Slice beam_slice (french
1458 ? where_are_the_whole_beams (beaming)
1459 : Stem::beam_multiplicity (stem));
1460 if (beam_slice.is_empty ())
1461 beam_slice = Slice (0,0);
1462 Interval beam_multiplicity(beam_slice[LEFT],
1466 feather dir = 1 , relx 0->1 : factor 0 -> 1
1467 feather dir = 0 , relx 0->1 : factor 1 -> 1
1468 feather dir = -1, relx 0->1 : factor 1 -> 0
1470 Real feather_factor = 1;
1471 if (feather_dir > 0)
1472 feather_factor = relx;
1473 else if (feather_dir < 0)
1474 feather_factor = 1 - relx;
1476 stem_y += feather_factor * beam_translation
1477 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1478 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1479 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1485 Hmm. At this time, beam position and slope are determined. Maybe,
1486 stem directions and length should set to relative to the chord's
1487 position of the beam. */
1488 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1490 Beam::set_stem_lengths (SCM smob)
1492 Grob *me = unsmob_grob (smob);
1494 /* trigger callbacks. */
1495 (void) me->get_property ("direction");
1496 (void) me->get_property ("beaming");
1498 SCM posns = me->get_property ("positions");
1500 extract_grob_set (me, "stems", stems);
1505 for (int a = 2; a--;)
1506 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1508 Drul_array<Real> pos = ly_scm2realdrul (posns);
1509 Real staff_space = Staff_symbol_referencer::staff_space (me);
1510 scale_drul (&pos, staff_space);
1514 if (robust_scm2int (me->get_property ("gap-count"), 0))
1517 thick = get_beam_thickness (me);
1520 Grob *fvs = first_normal_stem (me);
1521 Grob *lvs = last_normal_stem (me);
1523 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1524 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1525 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1527 for (vsize i = 0; i < stems.size (); i++)
1531 bool french = to_boolean (s->get_property ("french-beaming"));
1532 Real stem_y = calc_stem_y (me, s, common,
1533 xl, xr, feather_dir,
1534 pos, french && s != lvs && s!= fvs);
1537 Make the stems go up to the end of the beam. This doesn't matter
1538 for normal beams, but for tremolo beams it looks silly otherwise.
1541 && !Stem::is_invisible (s))
1542 stem_y += thick * 0.5 * get_grob_direction (s);
1545 Do set_stemend for invisible stems too, so tuplet brackets
1546 have a reference point for sloping
1548 Stem::set_stemend (s, 2 * stem_y / staff_space);
1555 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1557 extract_grob_set (me, "stems", stems);
1560 for (vsize i = 0; i < stems.size (); i++)
1563 Don't overwrite user settings.
1567 Grob *stem = stems[i];
1568 SCM beaming_prop = stem->get_property ("beaming");
1569 if (beaming_prop == SCM_EOL
1570 || index_get_cell (beaming_prop, d) == SCM_EOL)
1572 int count = beaming->beamlet_count (i, d);
1574 && i + 1 < stems.size ()
1575 && Stem::is_invisible (stem))
1576 count = min (count, beaming->beamlet_count (i,-d));
1578 if ( ((i == 0 && d == LEFT)
1579 || (i == stems.size ()-1 && d == RIGHT))
1580 && stems.size () > 1
1581 && to_boolean (me->get_property ("clip-edges")))
1584 Stem::set_beaming (stem, count, d);
1587 while (flip (&d) != LEFT);
1592 Beam::forced_stem_count (Grob *me)
1594 extract_grob_set (me, "normal-stems", stems);
1597 for (vsize i = 0; i < stems.size (); i++)
1601 /* I can imagine counting those boundaries as a half forced stem,
1602 but let's count them full for now. */
1603 Direction defdir = to_dir (s->get_property ("default-direction"));
1605 if (abs (Stem::chord_start_y (s)) > 0.1
1607 && get_grob_direction (s) != defdir)
1614 Beam::normal_stem_count (Grob *me)
1616 extract_grob_set (me, "normal-stems", stems);
1617 return stems.size ();
1621 Beam::first_normal_stem (Grob *me)
1623 extract_grob_set (me, "normal-stems", stems);
1624 return stems.size () ? stems[0] : 0;
1628 Beam::last_normal_stem (Grob *me)
1630 extract_grob_set (me, "normal-stems", stems);
1631 return stems.size () ? stems.back () : 0;
1637 handle rest under beam (do_post: beams are calculated now)
1638 what about combination of collisions and rest under beam.
1642 rest -> stem -> beam -> interpolate_y_position ()
1644 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1646 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1648 Grob *rest = unsmob_grob (smob);
1649 if (scm_is_number (rest->get_property ("staff-position")))
1650 return scm_from_int (0);
1652 Real offset = robust_scm2double (prev_offset, 0.0);
1654 Grob *st = unsmob_grob (rest->get_object ("stem"));
1657 return scm_from_double (0.0);
1658 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1660 || !Beam::has_interface (beam)
1661 || !Beam::normal_stem_count (beam))
1662 return scm_from_double (0.0);
1664 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1665 Drul_array<Real> (0,0)));
1667 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1669 scale_drul (&pos, staff_space);
1671 Real dy = pos[RIGHT] - pos[LEFT];
1673 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1674 last_normal_stem (beam));
1675 extract_grob_set (beam, "stems", stems);
1677 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1679 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1680 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1681 Real slope = dy && dx ? dy / dx : 0;
1683 Direction d = get_grob_direction (stem);
1684 Real stem_y = pos[LEFT]
1685 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1687 Real beam_translation = get_beam_translation (beam);
1688 Real beam_thickness = Beam::get_beam_thickness (beam);
1691 TODO: this is not strictly correct for 16th knee beams.
1694 = Stem::beam_multiplicity (stem).length () + 1;
1696 Real height_of_my_beams = beam_thickness / 2
1697 + (beam_count - 1) * beam_translation;
1698 Real beam_y = stem_y - d * height_of_my_beams;
1700 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1702 Interval rest_extent = rest->extent (rest, Y_AXIS);
1703 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1705 Real rest_dim = rest_extent[d];
1706 Real minimum_distance
1707 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1708 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1710 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1712 shift /= staff_space;
1713 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1715 /* Always move discretely by half spaces */
1716 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1718 /* Inside staff, move by whole spaces*/
1719 if ((rest_extent[d] + staff_space * shift) * d
1721 || (rest_extent[-d] + staff_space * shift) * -d
1723 shift = ceil (fabs (shift)) * sign (shift);
1725 return scm_from_double (offset + staff_space * shift);
1729 Beam::is_knee (Grob *me)
1731 SCM k = me->get_property ("knee");
1732 if (scm_is_bool (k))
1733 return ly_scm2bool (k);
1737 extract_grob_set (me, "stems", stems);
1738 for (vsize i = stems.size (); i--;)
1740 Direction dir = get_grob_direction (stems[i]);
1749 me->set_property ("knee", ly_bool2scm (knee));
1755 Beam::is_cross_staff (Grob *me)
1757 extract_grob_set (me, "stems", stems);
1758 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1759 for (vsize i = 0; i < stems.size (); i++)
1760 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1765 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1767 Beam::calc_cross_staff (SCM smob)
1769 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1773 Beam::get_direction_beam_count (Grob *me, Direction d)
1775 extract_grob_set (me, "stems", stems);
1778 for (vsize i = stems.size (); i--;)
1781 Should we take invisible stems into account?
1783 if (get_grob_direction (stems[i]) == d)
1784 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1790 ADD_INTERFACE (Beam,
1793 "The @code{beam-thickness} property is the weight of beams,"
1794 " measured in staffspace. The @code{direction} property is"
1795 " not user-serviceable. Use the @code{direction} property"
1796 " of @code{Stem} instead.\n"
1798 "The following properties may be set in the @code{details}"
1802 "@item stem-length-demerit-factor\n"
1803 "Demerit factor used for inappropriate stem lengths.\n"
1804 "@item secondary-beam-demerit\n"
1805 "Demerit used in quanting calculations for multiple"
1807 "@item region-size\n"
1808 "Size of region for checking quant scores.\n"
1810 "Epsilon for beam quant code to check for presence"
1812 "@item stem-length-limit-penalty\n"
1813 "Penalty for differences in stem lengths on a beam.\n"
1814 "@item damping-direction-penalty\n"
1815 "Demerit penalty applied when beam direction is different"
1816 " from damping direction.\n"
1817 "@item hint-direction-penalty\n"
1818 "Demerit penalty applied when beam direction is different"
1819 " from damping direction, but damping slope is"
1820 " <= @code{round-to-zero-slope}.\n"
1821 "@item musical-direction-factor\n"
1822 "Demerit scaling factor for difference between"
1823 " beam slope and music slope.\n"
1824 "@item ideal-slope-factor\n"
1825 "Demerit scaling factor for difference between"
1826 " beam slope and damping slope.\n"
1827 "@item round-to-zero-slope\n"
1828 "Damping slope which is considered zero for purposes of"
1829 " calculating direction penalties.\n"
1835 "beamed-stem-shorten "
1841 "collision-interfaces "
1842 "collision-voice-only "
1854 "neutral-direction "
1857 "quantized-positions "