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);
1185 vector<Grob*> filtered;
1187 We only update these for objects that are too large for quanting
1188 to find a workaround. Typically, these are notes with
1189 stems, and timesig/keysig/clef, which take out the entire area
1190 inside the staff as feasible.
1192 The code below disregards the thickness and multiplicity of the
1193 beam. This should not be a problem, as the beam quanting will
1194 take care of computing the impact those exactly.
1196 Real min_y_size = 2.0;
1198 // A list of intervals into which beams may not fall
1199 vector<Interval> forbidden_intervals;
1201 for (vsize i = 0; i < covered.size(); i++)
1203 if (!covered[i]->is_live())
1206 // TODO - use this logic in is_cross_staff.
1207 if (is_cross_staff (me)
1208 && Align_interface::has_interface (common_refpoint_of_array (stems, me, Y_AXIS)))
1211 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1215 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1216 b[a] = covered[i]->extent (common[a], a);
1218 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1221 if (intersection (b[X_AXIS], x_span).is_empty ())
1224 filtered.push_back (covered[i]);
1225 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1226 if (head_stem && Stem::is_normal_stem (head_stem)
1227 && Note_head::has_interface (covered[i]))
1229 if (Stem::get_beam (head_stem))
1232 We must assume that stems are infinitely long in this
1233 case, as asking for the length of the stem typically
1234 leads to circular dependencies.
1236 This strategy assumes that we don't want to handle the
1237 collision of beams in opposite non-forced directions
1238 with this code, where shortening the stems of both
1239 would resolve the problem, eg.
1249 Such beams would need a coordinating grob to resolve
1250 the collision, since both will likely want to occupy
1253 Direction stemdir = get_grob_direction (head_stem);
1254 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1258 // TODO - should we include the extent of the stem here?
1262 if (b[Y_AXIS].length () < min_y_size)
1268 Real x = b[X_AXIS][d] - x_span[LEFT];
1269 Real dy = slope * x;
1271 Direction yd = DOWN;
1272 Interval disallowed;
1275 Real left_y = b[Y_AXIS][yd];
1279 // Translate back to beam as ref point.
1280 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1282 disallowed[yd] = left_y;
1284 while (flip (&yd) != DOWN);
1286 forbidden_intervals.push_back (disallowed);
1288 while (flip (&d) != LEFT);
1292 Pointer_group_interface::get_grob_array (me,
1293 ly_symbol2scm ("covered-grobs"));
1294 arr->set_array (filtered);
1296 vector_sort (forbidden_intervals, Interval::left_less);
1297 Real epsilon = 1.0e-10;
1298 Interval feasible_beam_placements (beam_left_y, beam_left_y);
1301 forbidden_intervals contains a vector of intervals in which
1302 the beam cannot start. it iterates through these intervals,
1303 pushing feasible_beam_placements epsilon over or epsilon under a
1304 collision. when this type of change happens, the loop is marked
1305 as "dirty" and re-iterated.
1307 TODO: figure out a faster ways that this loop can happen via
1308 a better search algorithm and/or OOP.
1315 for (vsize i = 0; i < forbidden_intervals.size (); i++)
1320 if (forbidden_intervals[i][d] == d * infinity_f)
1321 feasible_beam_placements[d] = d * infinity_f;
1322 else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
1324 feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
1328 while (flip (&d) != DOWN);
1333 // if the beam placement falls out of the feasible region, we push it
1334 // to infinity so that it can never be a feasible candidate below
1338 if (!feasible_left_point.contains (feasible_beam_placements[d]))
1339 feasible_beam_placements[d] = d*infinity_f;
1341 while (flip (&d) != DOWN);
1343 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
1345 // We are somewhat screwed: we have a collision, but at least
1346 // there is a way to satisfy stem length constraints.
1347 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1349 else if (!feasible_left_point.is_empty ())
1351 // Only one of them offers is feasible solution. Pick that one.
1352 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
1353 beam_left_y = feasible_beam_placements[UP];
1355 beam_left_y = feasible_beam_placements[DOWN];
1359 // We are completely screwed.
1360 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1363 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1364 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1366 return ly_interval2scm (pos);
1369 /* This neat trick is by Werner Lemberg,
1370 damped = tanh (slope)
1371 corresponds with some tables in [Wanske] CHECKME */
1372 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1374 Beam::slope_damping (SCM smob, SCM posns)
1376 Grob *me = unsmob_grob (smob);
1377 Drul_array<Real> pos = ly_scm2interval (posns);
1379 if (normal_stem_count (me) <= 1)
1382 SCM s = me->get_property ("damping");
1383 Real damping = scm_to_double (s);
1384 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1385 if (concaveness >= 10000)
1387 pos[LEFT] = pos[RIGHT];
1388 me->set_property ("least-squares-dy", scm_from_double (0));
1394 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1396 Real dy = pos[RIGHT] - pos[LEFT];
1398 Grob *fvs = first_normal_stem (me);
1399 Grob *lvs = last_normal_stem (me);
1401 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1403 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1404 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1406 Real slope = dy && dx ? dy / dx : 0;
1408 slope = 0.6 * tanh (slope) / (damping + concaveness);
1410 Real damped_dy = slope * dx;
1412 set_minimum_dy (me, &damped_dy);
1414 pos[LEFT] += (dy - damped_dy) / 2;
1415 pos[RIGHT] -= (dy - damped_dy) / 2;
1417 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1420 return ly_interval2scm (pos);
1424 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1426 Beam::quanting (SCM smob, SCM posns)
1428 Grob *me = unsmob_grob (smob);
1429 Drul_array<Real> ys(0, 0);
1430 ys = robust_scm2drul (posns, ys);
1431 Beam_scoring_problem problem (me, ys);
1433 ys = problem.solve ();
1434 return ly_interval2scm (ys);
1439 Report slice containing the numbers that are both in (car BEAMING)
1443 where_are_the_whole_beams (SCM beaming)
1447 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1449 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1451 l.add_point (scm_to_int (scm_car (s)));
1457 /* Return the Y position of the stem-end, given the Y-left, Y-right
1458 in POS for stem S. This Y position is relative to S. */
1460 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1461 Real xl, Real xr, Direction feather_dir,
1462 Drul_array<Real> pos, bool french)
1464 Real beam_translation = get_beam_translation (me);
1465 Direction stem_dir = get_grob_direction (stem);
1468 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1469 Real xdir = 2*relx-1;
1471 Real stem_y = linear_combination(pos, xdir);
1473 SCM beaming = stem->get_property ("beaming");
1475 Slice beam_slice (french
1476 ? where_are_the_whole_beams (beaming)
1477 : Stem::beam_multiplicity (stem));
1478 if (beam_slice.is_empty ())
1479 beam_slice = Slice (0,0);
1480 Interval beam_multiplicity(beam_slice[LEFT],
1484 feather dir = 1 , relx 0->1 : factor 0 -> 1
1485 feather dir = 0 , relx 0->1 : factor 1 -> 1
1486 feather dir = -1, relx 0->1 : factor 1 -> 0
1488 Real feather_factor = 1;
1489 if (feather_dir > 0)
1490 feather_factor = relx;
1491 else if (feather_dir < 0)
1492 feather_factor = 1 - relx;
1494 stem_y += feather_factor * beam_translation
1495 * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1496 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1497 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1503 Hmm. At this time, beam position and slope are determined. Maybe,
1504 stem directions and length should set to relative to the chord's
1505 position of the beam. */
1506 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1508 Beam::set_stem_lengths (SCM smob)
1510 Grob *me = unsmob_grob (smob);
1512 /* trigger callbacks. */
1513 (void) me->get_property ("direction");
1514 (void) me->get_property ("beaming");
1516 SCM posns = me->get_property ("positions");
1518 extract_grob_set (me, "stems", stems);
1523 for (int a = 2; a--;)
1524 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1526 Drul_array<Real> pos = ly_scm2realdrul (posns);
1527 Real staff_space = Staff_symbol_referencer::staff_space (me);
1528 scale_drul (&pos, staff_space);
1532 if (robust_scm2int (me->get_property ("gap-count"), 0))
1535 thick = get_beam_thickness (me);
1538 Grob *fvs = first_normal_stem (me);
1539 Grob *lvs = last_normal_stem (me);
1541 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1542 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1543 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1545 for (vsize i = 0; i < stems.size (); i++)
1549 bool french = to_boolean (s->get_property ("french-beaming"));
1550 Real stem_y = calc_stem_y (me, s, common,
1551 xl, xr, feather_dir,
1552 pos, french && s != lvs && s!= fvs);
1555 Make the stems go up to the end of the beam. This doesn't matter
1556 for normal beams, but for tremolo beams it looks silly otherwise.
1559 && !Stem::is_invisible (s))
1560 stem_y += thick * 0.5 * get_grob_direction (s);
1563 Do set_stemend for invisible stems too, so tuplet brackets
1564 have a reference point for sloping
1566 Stem::set_stemend (s, 2 * stem_y / staff_space);
1573 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1575 extract_grob_set (me, "stems", stems);
1578 for (vsize i = 0; i < stems.size (); i++)
1581 Don't overwrite user settings.
1585 Grob *stem = stems[i];
1586 SCM beaming_prop = stem->get_property ("beaming");
1587 if (beaming_prop == SCM_EOL
1588 || index_get_cell (beaming_prop, d) == SCM_EOL)
1590 int count = beaming->beamlet_count (i, d);
1592 && i + 1 < stems.size ()
1593 && Stem::is_invisible (stem))
1594 count = min (count, beaming->beamlet_count (i,-d));
1596 if ( ((i == 0 && d == LEFT)
1597 || (i == stems.size ()-1 && d == RIGHT))
1598 && stems.size () > 1
1599 && to_boolean (me->get_property ("clip-edges")))
1602 Stem::set_beaming (stem, count, d);
1605 while (flip (&d) != LEFT);
1610 Beam::forced_stem_count (Grob *me)
1612 extract_grob_set (me, "normal-stems", stems);
1615 for (vsize i = 0; i < stems.size (); i++)
1619 /* I can imagine counting those boundaries as a half forced stem,
1620 but let's count them full for now. */
1621 Direction defdir = to_dir (s->get_property ("default-direction"));
1623 if (abs (Stem::chord_start_y (s)) > 0.1
1625 && get_grob_direction (s) != defdir)
1632 Beam::normal_stem_count (Grob *me)
1634 extract_grob_set (me, "normal-stems", stems);
1635 return stems.size ();
1639 Beam::first_normal_stem (Grob *me)
1641 extract_grob_set (me, "normal-stems", stems);
1642 return stems.size () ? stems[0] : 0;
1646 Beam::last_normal_stem (Grob *me)
1648 extract_grob_set (me, "normal-stems", stems);
1649 return stems.size () ? stems.back () : 0;
1655 handle rest under beam (do_post: beams are calculated now)
1656 what about combination of collisions and rest under beam.
1660 rest -> stem -> beam -> interpolate_y_position ()
1662 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1664 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1666 Grob *rest = unsmob_grob (smob);
1667 if (scm_is_number (rest->get_property ("staff-position")))
1668 return scm_from_int (0);
1670 Real offset = robust_scm2double (prev_offset, 0.0);
1672 Grob *st = unsmob_grob (rest->get_object ("stem"));
1675 return scm_from_double (0.0);
1676 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1678 || !Beam::has_interface (beam)
1679 || !Beam::normal_stem_count (beam))
1680 return scm_from_double (0.0);
1682 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1683 Drul_array<Real> (0,0)));
1685 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1687 scale_drul (&pos, staff_space);
1689 Real dy = pos[RIGHT] - pos[LEFT];
1691 Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1692 last_normal_stem (beam));
1693 extract_grob_set (beam, "stems", stems);
1695 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1697 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1698 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1699 Real slope = dy && dx ? dy / dx : 0;
1701 Direction d = get_grob_direction (stem);
1702 Real stem_y = pos[LEFT]
1703 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1705 Real beam_translation = get_beam_translation (beam);
1706 Real beam_thickness = Beam::get_beam_thickness (beam);
1709 TODO: this is not strictly correct for 16th knee beams.
1712 = Stem::beam_multiplicity (stem).length () + 1;
1714 Real height_of_my_beams = beam_thickness / 2
1715 + (beam_count - 1) * beam_translation;
1716 Real beam_y = stem_y - d * height_of_my_beams;
1718 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1720 Interval rest_extent = rest->extent (rest, Y_AXIS);
1721 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1723 Real rest_dim = rest_extent[d];
1724 Real minimum_distance
1725 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1726 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1728 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1730 shift /= staff_space;
1731 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1733 /* Always move discretely by half spaces */
1734 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1736 /* Inside staff, move by whole spaces*/
1737 if ((rest_extent[d] + staff_space * shift) * d
1739 || (rest_extent[-d] + staff_space * shift) * -d
1741 shift = ceil (fabs (shift)) * sign (shift);
1743 return scm_from_double (offset + staff_space * shift);
1747 Beam::is_knee (Grob *me)
1749 SCM k = me->get_property ("knee");
1750 if (scm_is_bool (k))
1751 return ly_scm2bool (k);
1755 extract_grob_set (me, "stems", stems);
1756 for (vsize i = stems.size (); i--;)
1758 Direction dir = get_grob_direction (stems[i]);
1767 me->set_property ("knee", ly_bool2scm (knee));
1773 Beam::is_cross_staff (Grob *me)
1775 extract_grob_set (me, "stems", stems);
1776 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1777 for (vsize i = 0; i < stems.size (); i++)
1778 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1783 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1785 Beam::calc_cross_staff (SCM smob)
1787 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1791 Beam::get_direction_beam_count (Grob *me, Direction d)
1793 extract_grob_set (me, "stems", stems);
1796 for (vsize i = stems.size (); i--;)
1799 Should we take invisible stems into account?
1801 if (get_grob_direction (stems[i]) == d)
1802 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1808 ADD_INTERFACE (Beam,
1811 "The @code{beam-thickness} property is the weight of beams,"
1812 " measured in staffspace. The @code{direction} property is"
1813 " not user-serviceable. Use the @code{direction} property"
1814 " of @code{Stem} instead.\n"
1816 "The following properties may be set in the @code{details}"
1820 "@item stem-length-demerit-factor\n"
1821 "Demerit factor used for inappropriate stem lengths.\n"
1822 "@item secondary-beam-demerit\n"
1823 "Demerit used in quanting calculations for multiple"
1825 "@item region-size\n"
1826 "Size of region for checking quant scores.\n"
1828 "Epsilon for beam quant code to check for presence"
1830 "@item stem-length-limit-penalty\n"
1831 "Penalty for differences in stem lengths on a beam.\n"
1832 "@item damping-direction-penalty\n"
1833 "Demerit penalty applied when beam direction is different"
1834 " from damping direction.\n"
1835 "@item hint-direction-penalty\n"
1836 "Demerit penalty applied when beam direction is different"
1837 " from damping direction, but damping slope is"
1838 " <= @code{round-to-zero-slope}.\n"
1839 "@item musical-direction-factor\n"
1840 "Demerit scaling factor for difference between"
1841 " beam slope and music slope.\n"
1842 "@item ideal-slope-factor\n"
1843 "Demerit scaling factor for difference between"
1844 " beam slope and damping slope.\n"
1845 "@item round-to-zero-slope\n"
1846 "Damping slope which is considered zero for purposes of"
1847 " calculating direction penalties.\n"
1853 "beamed-stem-shorten "
1859 "collision-interfaces "
1860 "collision-voice-only "
1872 "neutral-direction "
1875 "quantized-positions "