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);
558 if (!segments.size ())
562 if (normal_stem_count (me))
564 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
565 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
569 extract_grob_set (me, "stems", stems);
570 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
571 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
574 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
576 SCM posns = me->get_property ("quantized-positions");
578 if (!is_number_pair (posns))
580 programming_error ("no beam positions?");
581 pos = Interval (0, 0);
584 pos = ly_scm2realdrul (posns);
586 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
588 Real dy = pos[RIGHT] - pos[LEFT];
589 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
591 Real beam_thickness = get_beam_thickness (me);
592 Real beam_dy = get_beam_translation (me);
594 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
596 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
600 int extreme = (segments[0].vertical_count_ == 0
601 ? segments[0].vertical_count_
602 : segments.back ().vertical_count_);
604 for (vsize i = 0; i < segments.size (); i ++)
606 Real local_slope = slope;
608 Makes local slope proportional to the ratio of the length of this beam
612 local_slope += (feather_dir * segments[i].vertical_count_
614 * placements.length ()
617 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
619 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
620 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
622 Interval weights (1 - multiplier, multiplier);
624 if (feather_dir != LEFT)
627 // we need two translations: the normal one and
628 // the one of the lowest segment
629 int idx[] = {i, extreme};
630 Real translations[2];
632 for (int j = 0; j < 2; j++)
633 translations[j] = slope
634 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
635 + pos.linear_combination (CENTER)
636 + beam_dy * segments[idx[j]].vertical_count_;
638 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
641 Tricky. The manipulation of the variable `weighted_average' below ensures
642 that beams with a RIGHT grow direction will start from the position of the
643 lowest segment at 0, and this error will decrease and decrease over the
644 course of the beam. Something with a LEFT grow direction, on the other
645 hand, will always start in the correct place but progressively accrue
646 error at broken places. This code shifts beams up given where they are
647 in the total span length (controlled by the variable `multiplier'). To
648 better understand what it does, try commenting it out: you'll see that
649 all of the RIGHT growing beams immediately start too low and get better
650 over line breaks, whereas all of the LEFT growing beams start just right
651 and get worse over line breaks.
653 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
655 if (segments[0].vertical_count_ < 0 && feather_dir)
656 weighted_average += beam_dy * (segments.size () - 1) * factor;
658 b.translate_axis (weighted_average, Y_AXIS);
660 the_beam.add_stencil (b);
664 #if (DEBUG_BEAM_SCORING)
665 SCM annotation = me->get_property ("annotation");
666 if (scm_is_string (annotation))
668 extract_grob_set (me, "stems", stems);
671 This code prints the demerits for each beam. Perhaps this
672 should be switchable for those who want to twiddle with the
676 SCM properties = Font_interface::text_font_alist_chain (me);
678 properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
681 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
683 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
684 (me->layout ()->self_scm (), properties, annotation));
686 if (!score.is_empty ())
688 score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
689 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
694 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
695 return the_beam.smobbed_copy ();
699 Beam::get_default_dir (Grob *me)
701 extract_grob_set (me, "stems", stems);
703 Drul_array<Real> extremes (0.0, 0.0);
704 for (iterof (s, stems); s != stems.end (); s++)
706 Interval positions = Stem::head_positions (*s);
710 if (sign (positions[d]) == d)
711 extremes[d] = d * max (d * positions[d], d * extremes[d]);
713 while (flip (&d) != DOWN);
716 Drul_array<int> total (0, 0);
717 Drul_array<int> count (0, 0);
719 bool force_dir = false;
720 for (vsize i = 0; i < stems.size (); i++)
723 Direction stem_dir = CENTER;
724 SCM stem_dir_scm = s->get_property_data ("direction");
725 if (is_direction (stem_dir_scm))
727 stem_dir = to_dir (stem_dir_scm);
731 stem_dir = to_dir (s->get_property ("default-direction"));
734 stem_dir = to_dir (s->get_property ("neutral-direction"));
739 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
746 if (abs (extremes[UP]) > -extremes[DOWN])
748 else if (extremes[UP] < -extremes[DOWN])
752 Direction dir = CENTER;
753 Direction d = CENTER;
754 if ((d = (Direction) sign (count[UP] - count[DOWN])))
758 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
760 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
763 dir = to_dir (me->get_property ("neutral-direction"));
768 /* Set all stems with non-forced direction to beam direction.
769 Urg: non-forced should become `without/with unforced' direction,
770 once stem gets cleaned-up. */
772 Beam::set_stem_directions (Grob *me, Direction d)
774 extract_grob_set (me, "stems", stems);
776 for (vsize i = 0; i < stems.size (); i++)
780 SCM forcedir = s->get_property_data ("direction");
781 if (!to_dir (forcedir))
782 set_grob_direction (s, d);
787 Only try horizontal beams for knees. No reliable detection of
788 anything else is possible here, since we don't know funky-beaming
789 settings, or X-distances (slopes!) People that want sloped
790 knee-beams, should set the directions manually.
795 this routine should take into account the stemlength scoring
796 of a possible knee/nonknee beam.
799 Beam::consider_auto_knees (Grob *me)
801 SCM scm = me->get_property ("auto-knee-gap");
802 if (!scm_is_number (scm))
809 extract_grob_set (me, "normal-stems", stems);
811 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
812 Real staff_space = Staff_symbol_referencer::staff_space (me);
814 vector<Interval> head_extents_array;
815 for (vsize i = 0; i < stems.size (); i++)
817 Grob *stem = stems[i];
819 Interval head_extents = Stem::head_positions (stem);
820 if (!head_extents.is_empty ())
822 head_extents[LEFT] += -1;
823 head_extents[RIGHT] += 1;
824 head_extents *= staff_space * 0.5;
827 We could subtract beam Y position, but this routine only
828 sets stem directions, a constant shift does not have an
831 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
833 if (to_dir (stem->get_property_data ("direction")))
835 Direction stemdir = to_dir (stem->get_property ("direction"));
836 head_extents[-stemdir] = -stemdir * infinity_f;
839 head_extents_array.push_back (head_extents);
841 gaps.remove_interval (head_extents);
845 Real max_gap_len = 0.0;
847 for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
849 Interval gap = gaps.allowed_regions_[i];
852 the outer gaps are not knees.
854 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
857 if (gap.length () >= max_gap_len)
859 max_gap_len = gap.length ();
864 Real beam_translation = get_beam_translation (me);
865 Real beam_thickness = Beam::get_beam_thickness (me);
866 int beam_count = Beam::get_beam_count (me);
867 Real height_of_beams = beam_thickness / 2
868 + (beam_count - 1) * beam_translation;
869 Real threshold = scm_to_double (scm) + height_of_beams;
871 if (max_gap_len > threshold)
874 for (vsize i = 0; i < stems.size (); i++)
876 Grob *stem = stems[i];
877 Interval head_extents = head_extents_array[j++];
879 Direction d = (head_extents.center () < max_gap.center ())
882 stem->set_property ("direction", scm_from_int (d));
884 head_extents.intersect (max_gap);
885 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
890 /* Set stem's shorten property if unset.
893 take some y-position (chord/beam/nearest?) into account
894 scmify forced-fraction
896 This is done in beam because the shorten has to be uniform over the
903 set_minimum_dy (Grob *me, Real *dy)
908 If dy is smaller than the smallest quant, we
909 get absurd direction-sign penalties.
912 Real ss = Staff_symbol_referencer::staff_space (me);
913 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
914 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
915 Real sit = (beam_thickness - slt) / 2;
917 Real hang = 1.0 - (beam_thickness - slt) / 2;
919 *dy = sign (*dy) * max (fabs (*dy),
920 min (min (sit, inter), hang));
926 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
928 Beam::calc_stem_shorten (SCM smob)
930 Grob *me = unsmob_grob (smob);
933 shortening looks silly for x staff beams
936 return scm_from_int (0);
938 Real forced_fraction = 1.0 * forced_stem_count (me)
939 / normal_stem_count (me);
941 int beam_count = get_beam_count (me);
943 SCM shorten_list = me->get_property ("beamed-stem-shorten");
944 if (shorten_list == SCM_EOL)
945 return scm_from_int (0);
947 Real staff_space = Staff_symbol_referencer::staff_space (me);
950 = robust_list_ref (beam_count -1, shorten_list);
951 Real shorten = scm_to_double (shorten_elt) * staff_space;
953 shorten *= forced_fraction;
957 return scm_from_double (shorten);
959 return scm_from_double (0.0);
964 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
966 extract_grob_set (me, "stems", stems);
968 return default_value;
970 Interval head_positions;
972 for (vsize i = 0; i < stems.size(); i++)
974 head_positions.unite (Stem::head_positions (stems[i]));
975 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
978 Direction dir = get_grob_direction (me);
981 programming_error ("The beam should have a direction by now.");
983 Real y = head_positions.linear_combination (dir)
984 * 0.5 * Staff_symbol_referencer::staff_space (me)
985 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
987 y /= Staff_symbol_referencer::staff_space (me);
988 return Interval (y,y);
993 Compute a first approximation to the beam slope.
995 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
997 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
999 Grob *me = unsmob_grob (smob);
1001 int count = normal_stem_count (me);
1004 return ly_interval2scm (no_visible_stem_positions (me, pos));
1006 vector<Real> x_posns;
1007 extract_grob_set (me, "normal-stems", stems);
1008 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1009 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1011 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1013 Grob *fvs = first_normal_stem (me);
1014 Grob *lvs = last_normal_stem (me);
1016 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1017 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1018 Stem::get_stem_info (lvs).ideal_y_
1019 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1021 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1022 for (vsize i = 0; i < stems.size (); i++)
1026 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1027 x_posns.push_back (x);
1029 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1035 if (!ideal.delta ())
1037 Interval chord (Stem::chord_start_y (stems[0]),
1038 Stem::chord_start_y (stems.back ()));
1040 /* Simple beams (2 stems) on middle line should be allowed to be
1043 However, if both stems reach middle line,
1044 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1046 For that case, we apply artificial slope */
1047 if (!ideal[LEFT] && chord.delta () && count == 2)
1050 Direction d = (Direction) (sign (chord.delta ()) * UP);
1051 pos[d] = get_beam_thickness (me) / 2;
1058 For broken beams this doesn't work well. In this case, the
1059 slope esp. of the first part of a broken beam should predict
1060 where the second part goes.
1062 ldy = pos[RIGHT] - pos[LEFT];
1066 vector<Offset> ideals;
1067 for (vsize i = 0; i < stems.size (); i++)
1070 ideals.push_back (Offset (x_posns[i],
1071 Stem::get_stem_info (s).ideal_y_
1072 + s->relative_coordinate (commony, Y_AXIS)
1076 minimise_least_squares (&slope, &y, ideals);
1080 set_minimum_dy (me, &dy);
1083 pos = Interval (y, (y + dy));
1087 "position" is relative to the staff.
1089 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1091 me->set_property ("least-squares-dy", scm_from_double (ldy));
1092 return ly_interval2scm (pos);
1096 // Assuming V is not empty, pick a 'reasonable' point inside V.
1098 point_in_interval (Interval v, Real dist)
1100 if (isinf (v[DOWN]))
1101 return v[UP] - dist;
1102 else if (isinf (v[UP]))
1103 return v[DOWN] + dist;
1109 We can't combine with previous function, since check concave and
1110 slope damping comes first.
1112 TODO: we should use the concaveness to control the amount of damping
1115 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1117 Beam::shift_region_to_valid (SCM grob, SCM posns)
1119 Grob *me = unsmob_grob (grob);
1124 vector<Real> x_posns;
1125 extract_grob_set (me, "stems", stems);
1126 extract_grob_set (me, "covered-grobs", covered);
1128 Grob *common[NO_AXES] = { me, me };
1129 for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1130 common[a] = common_refpoint_of_array (stems, me, a);
1131 common[a] = common_refpoint_of_array (covered, common[a], a);
1133 Grob *fvs = first_normal_stem (me);
1138 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1139 for (vsize i = 0; i < stems.size (); i++)
1143 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1144 x_posns.push_back (x);
1147 Grob *lvs = last_normal_stem (me);
1148 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1150 Drul_array<Real> pos = ly_scm2interval (posns);
1152 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1154 Real beam_dy = pos[RIGHT] - pos[LEFT];
1155 Real beam_left_y = pos[LEFT];
1156 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1159 Shift the positions so that we have a chance of finding good
1160 quants (i.e. no short stem failures.)
1162 Interval feasible_left_point;
1163 feasible_left_point.set_full ();
1165 for (vsize i = 0; i < stems.size (); i++)
1168 if (Stem::is_invisible (s))
1171 Direction d = get_grob_direction (s);
1173 = Stem::get_stem_info (s).shortest_y_
1174 - slope * x_posns [i];
1177 left_y is now relative to the stem S. We want relative to
1178 ourselves, so translate:
1181 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1182 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1188 feasible_left_point.intersect (flp);
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;
1204 // A list of intervals into which beams may not fall
1205 vector<Interval> forbidden_intervals;
1207 for (vsize i = 0; i < covered.size(); i++)
1209 if (!covered[i]->is_live())
1212 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1216 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1217 b[a] = covered[i]->extent (common[a], a);
1219 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1222 if (intersection (b[X_AXIS], x_span).is_empty ())
1225 filtered.push_back (covered[i]);
1226 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1227 if (head_stem && Stem::is_normal_stem (head_stem)
1228 && Note_head::has_interface (covered[i]))
1230 if (Stem::get_beam (head_stem))
1233 We must assume that stems are infinitely long in this
1234 case, as asking for the length of the stem typically
1235 leads to circular dependencies.
1237 This strategy assumes that we don't want to handle the
1238 collision of beams in opposite non-forced directions
1239 with this code, where shortening the stems of both
1240 would resolve the problem, eg.
1250 Such beams would need a coordinating grob to resolve
1251 the collision, since both will likely want to occupy
1254 Direction stemdir = get_grob_direction (head_stem);
1255 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1259 // TODO - should we include the extent of the stem here?
1263 if (b[Y_AXIS].length () < min_y_size)
1269 Real x = b[X_AXIS][d] - x_span[LEFT];
1270 Real dy = slope * x;
1272 Direction yd = DOWN;
1273 Interval disallowed;
1276 Real left_y = b[Y_AXIS][yd];
1280 // Translate back to beam as ref point.
1281 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1283 disallowed[yd] = left_y;
1285 while (flip (&yd) != DOWN);
1287 forbidden_intervals.push_back (disallowed);
1289 while (flip (&d) != LEFT);
1293 Pointer_group_interface::get_grob_array (me,
1294 ly_symbol2scm ("covered-grobs"));
1295 arr->set_array (filtered);
1297 vector_sort (forbidden_intervals, Interval::left_less);
1298 Real epsilon = 1.0e-10;
1299 Interval feasible_beam_placements (beam_left_y, beam_left_y);
1302 forbidden_intervals contains a vector of intervals in which
1303 the beam cannot start. it iterates through these intervals,
1304 pushing feasible_beam_placements epsilon over or epsilon under a
1305 collision. when this type of change happens, the loop is marked
1306 as "dirty" and re-iterated.
1308 TODO: figure out a faster ways that this loop can happen via
1309 a better search algorithm and/or OOP.
1316 for (vsize i = 0; i < forbidden_intervals.size (); i++)
1321 if (forbidden_intervals[i][d] == d * infinity_f)
1322 feasible_beam_placements[d] = d * infinity_f;
1323 else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
1325 feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
1329 while (flip (&d) != DOWN);
1334 // if the beam placement falls out of the feasible region, we push it
1335 // to infinity so that it can never be a feasible candidate below
1339 if (!feasible_left_point.contains (feasible_beam_placements[d]))
1340 feasible_beam_placements[d] = d*infinity_f;
1342 while (flip (&d) != DOWN);
1344 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
1346 // We are somewhat screwed: we have a collision, but at least
1347 // there is a way to satisfy stem length constraints.
1348 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1350 else if (!feasible_left_point.is_empty ())
1352 // Only one of them offers is feasible solution. Pick that one.
1353 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
1354 beam_left_y = feasible_beam_placements[UP];
1356 beam_left_y = feasible_beam_placements[DOWN];
1360 // We are completely screwed.
1361 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1364 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1365 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1367 return ly_interval2scm (pos);
1370 /* This neat trick is by Werner Lemberg,
1371 damped = tanh (slope)
1372 corresponds with some tables in [Wanske] CHECKME */
1373 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1375 Beam::slope_damping (SCM smob, SCM posns)
1377 Grob *me = unsmob_grob (smob);
1378 Drul_array<Real> pos = ly_scm2interval (posns);
1380 if (normal_stem_count (me) <= 1)
1383 SCM s = me->get_property ("damping");
1384 Real damping = scm_to_double (s);
1385 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1386 if (concaveness >= 10000)
1388 pos[LEFT] = pos[RIGHT];
1389 me->set_property ("least-squares-dy", scm_from_double (0));
1395 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1397 Real dy = pos[RIGHT] - pos[LEFT];
1399 Grob *fvs = first_normal_stem (me);
1400 Grob *lvs = last_normal_stem (me);
1402 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1404 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1405 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1407 Real slope = dy && dx ? dy / dx : 0;
1409 slope = 0.6 * tanh (slope) / (damping + concaveness);
1411 Real damped_dy = slope * dx;
1413 set_minimum_dy (me, &damped_dy);
1415 pos[LEFT] += (dy - damped_dy) / 2;
1416 pos[RIGHT] -= (dy - damped_dy) / 2;
1418 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1421 return ly_interval2scm (pos);
1425 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1427 Beam::quanting (SCM smob, SCM posns)
1429 Grob *me = unsmob_grob (smob);
1430 Drul_array<Real> ys(0, 0);
1431 ys = robust_scm2drul (posns, ys);
1432 Beam_scoring_problem problem (me, ys);
1434 ys = problem.solve ();
1435 return ly_interval2scm (ys);
1440 Report slice containing the numbers that are both in (car BEAMING)
1444 where_are_the_whole_beams (SCM beaming)
1448 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1450 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1452 l.add_point (scm_to_int (scm_car (s)));
1458 /* Return the Y position of the stem-end, given the Y-left, Y-right
1459 in POS for stem S. This Y position is relative to S. */
1461 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1462 Real xl, Real xr, Direction feather_dir,
1463 Drul_array<Real> pos, bool french)
1465 Real beam_translation = get_beam_translation (me);
1466 Direction stem_dir = get_grob_direction (stem);
1469 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1470 Real xdir = 2*relx-1;
1472 Real stem_y = linear_combination(pos, xdir);
1474 SCM beaming = stem->get_property ("beaming");
1476 Slice beam_slice (french
1477 ? where_are_the_whole_beams (beaming)
1478 : Stem::beam_multiplicity (stem));
1479 if (beam_slice.is_empty ())
1480 beam_slice = Slice (0,0);
1481 Interval beam_multiplicity(beam_slice[LEFT],
1485 feather dir = 1 , relx 0->1 : factor 0 -> 1
1486 feather dir = 0 , relx 0->1 : factor 1 -> 1
1487 feather dir = -1, relx 0->1 : factor 1 -> 0
1489 Real feather_factor = 1;
1490 if (feather_dir > 0)
1491 feather_factor = relx;
1492 else if (feather_dir < 0)
1493 feather_factor = 1 - relx;
1495 stem_y += feather_factor * beam_translation
1496 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1497 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1498 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1504 Hmm. At this time, beam position and slope are determined. Maybe,
1505 stem directions and length should set to relative to the chord's
1506 position of the beam. */
1507 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1509 Beam::set_stem_lengths (SCM smob)
1511 Grob *me = unsmob_grob (smob);
1513 /* trigger callbacks. */
1514 (void) me->get_property ("direction");
1515 (void) me->get_property ("beaming");
1517 SCM posns = me->get_property ("positions");
1519 extract_grob_set (me, "stems", stems);
1524 for (int a = 2; a--;)
1525 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1527 Drul_array<Real> pos = ly_scm2realdrul (posns);
1528 Real staff_space = Staff_symbol_referencer::staff_space (me);
1529 scale_drul (&pos, staff_space);
1533 if (robust_scm2int (me->get_property ("gap-count"), 0))
1536 thick = get_beam_thickness (me);
1539 Grob *fvs = first_normal_stem (me);
1540 Grob *lvs = last_normal_stem (me);
1542 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1543 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1544 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1546 for (vsize i = 0; i < stems.size (); i++)
1550 bool french = to_boolean (s->get_property ("french-beaming"));
1551 Real stem_y = calc_stem_y (me, s, common,
1552 xl, xr, feather_dir,
1553 pos, french && s != lvs && s!= fvs);
1556 Make the stems go up to the end of the beam. This doesn't matter
1557 for normal beams, but for tremolo beams it looks silly otherwise.
1560 && !Stem::is_invisible (s))
1561 stem_y += thick * 0.5 * get_grob_direction (s);
1564 Do set_stemend for invisible stems too, so tuplet brackets
1565 have a reference point for sloping
1567 Stem::set_stemend (s, 2 * stem_y / staff_space);
1574 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1576 extract_grob_set (me, "stems", stems);
1579 for (vsize i = 0; i < stems.size (); i++)
1582 Don't overwrite user settings.
1586 Grob *stem = stems[i];
1587 SCM beaming_prop = stem->get_property ("beaming");
1588 if (beaming_prop == SCM_EOL
1589 || index_get_cell (beaming_prop, d) == SCM_EOL)
1591 int count = beaming->beamlet_count (i, d);
1593 && i + 1 < stems.size ()
1594 && Stem::is_invisible (stem))
1595 count = min (count, beaming->beamlet_count (i,-d));
1597 if ( ((i == 0 && d == LEFT)
1598 || (i == stems.size ()-1 && d == RIGHT))
1599 && stems.size () > 1
1600 && to_boolean (me->get_property ("clip-edges")))
1603 Stem::set_beaming (stem, count, d);
1606 while (flip (&d) != LEFT);
1611 Beam::forced_stem_count (Grob *me)
1613 extract_grob_set (me, "normal-stems", stems);
1616 for (vsize i = 0; i < stems.size (); i++)
1620 /* I can imagine counting those boundaries as a half forced stem,
1621 but let's count them full for now. */
1622 Direction defdir = to_dir (s->get_property ("default-direction"));
1624 if (abs (Stem::chord_start_y (s)) > 0.1
1626 && get_grob_direction (s) != defdir)
1633 Beam::normal_stem_count (Grob *me)
1635 extract_grob_set (me, "normal-stems", stems);
1636 return stems.size ();
1640 Beam::first_normal_stem (Grob *me)
1642 extract_grob_set (me, "normal-stems", stems);
1643 return stems.size () ? stems[0] : 0;
1647 Beam::last_normal_stem (Grob *me)
1649 extract_grob_set (me, "normal-stems", stems);
1650 return stems.size () ? stems.back () : 0;
1656 handle rest under beam (do_post: beams are calculated now)
1657 what about combination of collisions and rest under beam.
1661 rest -> stem -> beam -> interpolate_y_position ()
1663 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1665 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1667 Grob *rest = unsmob_grob (smob);
1668 if (scm_is_number (rest->get_property ("staff-position")))
1669 return scm_from_int (0);
1671 Real offset = robust_scm2double (prev_offset, 0.0);
1673 Grob *st = unsmob_grob (rest->get_object ("stem"));
1676 return scm_from_double (0.0);
1677 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1679 || !Beam::has_interface (beam)
1680 || !Beam::normal_stem_count (beam))
1681 return scm_from_double (0.0);
1683 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1684 Drul_array<Real> (0,0)));
1686 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1688 scale_drul (&pos, staff_space);
1690 Real dy = pos[RIGHT] - pos[LEFT];
1692 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1693 last_normal_stem (beam));
1694 extract_grob_set (beam, "stems", stems);
1696 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1698 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1699 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1700 Real slope = dy && dx ? dy / dx : 0;
1702 Direction d = get_grob_direction (stem);
1703 Real stem_y = pos[LEFT]
1704 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1706 Real beam_translation = get_beam_translation (beam);
1707 Real beam_thickness = Beam::get_beam_thickness (beam);
1710 TODO: this is not strictly correct for 16th knee beams.
1713 = Stem::beam_multiplicity (stem).length () + 1;
1715 Real height_of_my_beams = beam_thickness / 2
1716 + (beam_count - 1) * beam_translation;
1717 Real beam_y = stem_y - d * height_of_my_beams;
1719 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1721 Interval rest_extent = rest->extent (rest, Y_AXIS);
1722 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1724 Real rest_dim = rest_extent[d];
1725 Real minimum_distance
1726 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1727 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1729 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1731 shift /= staff_space;
1732 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1734 /* Always move discretely by half spaces */
1735 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1737 /* Inside staff, move by whole spaces*/
1738 if ((rest_extent[d] + staff_space * shift) * d
1740 || (rest_extent[-d] + staff_space * shift) * -d
1742 shift = ceil (fabs (shift)) * sign (shift);
1744 return scm_from_double (offset + staff_space * shift);
1748 Beam::is_knee (Grob *me)
1750 SCM k = me->get_property ("knee");
1751 if (scm_is_bool (k))
1752 return ly_scm2bool (k);
1756 extract_grob_set (me, "stems", stems);
1757 for (vsize i = stems.size (); i--;)
1759 Direction dir = get_grob_direction (stems[i]);
1768 me->set_property ("knee", ly_bool2scm (knee));
1774 Beam::is_cross_staff (Grob *me)
1776 extract_grob_set (me, "stems", stems);
1777 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1778 for (vsize i = 0; i < stems.size (); i++)
1779 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1784 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1786 Beam::calc_cross_staff (SCM smob)
1788 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1792 Beam::get_direction_beam_count (Grob *me, Direction d)
1794 extract_grob_set (me, "stems", stems);
1797 for (vsize i = stems.size (); i--;)
1800 Should we take invisible stems into account?
1802 if (get_grob_direction (stems[i]) == d)
1803 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1809 ADD_INTERFACE (Beam,
1812 "The @code{beam-thickness} property is the weight of beams,"
1813 " measured in staffspace. The @code{direction} property is"
1814 " not user-serviceable. Use the @code{direction} property"
1815 " of @code{Stem} instead.\n"
1817 "The following properties may be set in the @code{details}"
1821 "@item stem-length-demerit-factor\n"
1822 "Demerit factor used for inappropriate stem lengths.\n"
1823 "@item secondary-beam-demerit\n"
1824 "Demerit used in quanting calculations for multiple"
1826 "@item region-size\n"
1827 "Size of region for checking quant scores.\n"
1829 "Epsilon for beam quant code to check for presence"
1831 "@item stem-length-limit-penalty\n"
1832 "Penalty for differences in stem lengths on a beam.\n"
1833 "@item damping-direction-penalty\n"
1834 "Demerit penalty applied when beam direction is different"
1835 " from damping direction.\n"
1836 "@item hint-direction-penalty\n"
1837 "Demerit penalty applied when beam direction is different"
1838 " from damping direction, but damping slope is"
1839 " <= @code{round-to-zero-slope}.\n"
1840 "@item musical-direction-factor\n"
1841 "Demerit scaling factor for difference between"
1842 " beam slope and music slope.\n"
1843 "@item ideal-slope-factor\n"
1844 "Demerit scaling factor for difference between"
1845 " beam slope and damping slope.\n"
1846 "@item round-to-zero-slope\n"
1847 "Damping slope which is considered zero for purposes of"
1848 " calculating direction penalties.\n"
1854 "beamed-stem-shorten "
1860 "collision-interfaces "
1861 "collision-voice-only "
1873 "neutral-direction "
1876 "quantized-positions "