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.
68 Beam_stem_segment::Beam_stem_segment ()
70 max_connect_ = 1000; // infinity
80 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
82 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
85 Beam_segment::Beam_segment ()
91 Beam::add_stem (Grob *me, Grob *s)
93 if (Stem::get_beam (s))
95 programming_error ("Stem already has beam");
99 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
100 s->set_object ("beam", me->self_scm ());
101 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
105 Beam::get_beam_thickness (Grob *me)
107 return robust_scm2double (me->get_property ("beam-thickness"), 0)
108 * Staff_symbol_referencer::staff_space (me);
111 /* Return the translation between 2 adjoining beams. */
113 Beam::get_beam_translation (Grob *me)
115 int beam_count = get_beam_count (me);
116 Real staff_space = Staff_symbol_referencer::staff_space (me);
117 Real line = Staff_symbol_referencer::line_thickness (me);
118 Real beam_thickness = get_beam_thickness (me);
119 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
121 Real beam_translation = beam_count < 4
122 ? (2 * staff_space + line - beam_thickness) / 2.0
123 : (3 * staff_space + line - beam_thickness) / 3.0;
125 return fract * beam_translation;
128 /* Maximum beam_count. */
130 Beam::get_beam_count (Grob *me)
134 extract_grob_set (me, "stems", stems);
135 for (vsize i = 0; i < stems.size (); i++)
137 Grob *stem = stems[i];
138 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
145 Beam::calc_normal_stems (SCM smob)
147 Grob *me = unsmob_grob (smob);
149 extract_grob_set (me, "stems", stems);
150 SCM val = Grob_array::make_array ();
151 Grob_array *ga = unsmob_grob_array (val);
152 for (vsize i = 0; i < stems.size (); i++)
153 if (Stem::is_normal_stem (stems[i]))
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
161 Beam::calc_direction (SCM smob)
163 Grob *me = unsmob_grob (smob);
165 /* Beams with less than 2 two stems don't make much sense, but could happen
172 Direction dir = CENTER;
174 int count = normal_stem_count (me);
177 extract_grob_set (me, "stems", stems);
178 if (stems.size () == 0)
180 me->warning (_ ("removing beam with no stems"));
183 return SCM_UNSPECIFIED;
187 Grob *stem = first_normal_stem (me);
190 This happens for chord tremolos.
195 if (is_direction (stem->get_property_data ("direction")))
196 dir = to_dir (stem->get_property_data ("direction"));
198 dir = to_dir (stem->get_property ("default-direction"));
205 dir = get_default_dir (me);
207 consider_auto_knees (me);
212 set_stem_directions (me, dir);
215 return scm_from_int (dir);
218 /* We want a maximal number of shared beams, but if there is choice, we
219 * take the one that is closest to the end of the stem. This is for
231 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
235 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
239 for (int i = lslice[-left_dir];
240 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
243 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
245 int k = -right_dir * scm_to_int (scm_car (s)) + i;
246 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
250 if (count >= best_count)
260 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
262 Beam::calc_beaming (SCM smob)
264 Grob *me = unsmob_grob (smob);
266 extract_grob_set (me, "stems", stems);
269 last_int.set_empty ();
271 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
272 Direction last_dir = CENTER;
273 for (vsize i = 0; i < stems.size (); i++)
275 Grob *this_stem = stems[i];
276 SCM this_beaming = this_stem->get_property ("beaming");
278 Direction this_dir = get_grob_direction (this_stem);
279 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
281 int start_point = position_with_maximal_common_beams
282 (last_beaming, this_beaming,
283 last_dir ? last_dir : this_dir,
290 new_slice.set_empty ();
291 SCM s = index_get_cell (this_beaming, d);
292 for (; scm_is_pair (s); s = scm_cdr (s))
295 = start_point - this_dir * scm_to_int (scm_car (s));
297 new_slice.add_point (new_beam_pos);
298 scm_set_car_x (s, scm_from_int (new_beam_pos));
301 while (flip (&d) != LEFT);
303 if (!new_slice.is_empty ())
304 last_int = new_slice;
309 FIXME: what's this for?
311 SCM s = scm_cdr (this_beaming);
312 for (; scm_is_pair (s); s = scm_cdr (s))
314 int np = -this_dir * scm_to_int (scm_car (s));
315 scm_set_car_x (s, scm_from_int (np));
316 last_int.add_point (np);
320 if (scm_ilength (scm_cdr (this_beaming)) > 0)
322 last_beaming = this_beaming;
331 operator <(Beam_stem_segment const &a,
332 Beam_stem_segment const &b)
334 return a.rank_ < b.rank_;
337 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
339 // TODO - should store result in a property?
341 Beam::get_beam_segments (Grob *me_grob, Grob **common)
343 /* ugh, this has a side-effect that we need to ensure that
344 Stem #'beaming is correct */
345 (void) me_grob->get_property ("beaming");
347 Spanner *me = dynamic_cast<Spanner *> (me_grob);
349 extract_grob_set (me, "stems", stems);
350 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
352 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
353 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
357 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
358 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
360 Position_stem_segments_map stem_segments;
361 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
363 /* There are two concepts of "rank" that are used in the following code.
364 The beam_rank is the vertical position of the beam (larger numbers are
365 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
366 is the horizontal position of the segment (this is incremented by two
367 for each stem; the beam segment on the right side of the stem has
368 a higher rank (by one) than its neighbour to the left). */
370 for (vsize i = 0; i < stems.size (); i++)
372 Grob *stem = stems[i];
373 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
374 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
375 SCM beaming = stem->get_property ("beaming");
379 // Find the maximum and minimum beam ranks.
380 // Given that RANKS is never reset to empty, the interval will always be
381 // smallest for the left beamlet of the first stem, and then it might grow.
382 // Do we really want this? (It only affects the tremolo gaps) --jneem
383 for (SCM s = index_get_cell (beaming, d);
384 scm_is_pair (s); s = scm_cdr (s))
386 if (!scm_is_integer (scm_car (s)))
389 int beam_rank = scm_to_int (scm_car (s));
390 ranks.add_point (beam_rank);
393 for (SCM s = index_get_cell (beaming, d);
394 scm_is_pair (s); s = scm_cdr (s))
396 if (!scm_is_integer (scm_car (s)))
399 int beam_rank = scm_to_int (scm_car (s));
400 Beam_stem_segment seg;
402 seg.stem_x_ = stem_x;
403 seg.rank_ = 2 * i + (d + 1) / 2;
404 seg.width_ = stem_width;
407 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
409 Direction stem_dir = get_grob_direction (stem);
412 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
413 stem_segments[beam_rank].push_back (seg);
416 while (flip (&d) != LEFT);
419 Drul_array<Real> break_overshoot
420 = robust_scm2drul (me->get_property ("break-overshoot"),
421 Drul_array<Real> (-0.5, 0.0));
423 vector<Beam_segment> segments;
424 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
425 i != stem_segments.end (); i++)
427 vector<Beam_stem_segment> segs = (*i).second;
428 vector_sort (segs, less<Beam_stem_segment> ());
430 Beam_segment current;
432 // Iterate over all of the segments of the current beam rank,
433 // merging the adjacent Beam_stem_segments into one Beam_segment
435 int vertical_count = (*i).first;
436 for (vsize j = 0; j < segs.size (); j++)
438 // Keeping track of the different directions here is a little tricky.
439 // segs[j].dir_ is the direction of the beam segment relative to the stem
440 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
441 // its stem) whereas event_dir refers to the edge of the beam segment that
442 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
443 // are looking at that edge of the beam segment that is furthest from its
445 Direction event_dir = LEFT;
446 Beam_stem_segment const &seg = segs[j];
449 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
450 // TODO: make names clearer? --jneem
451 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
452 // on_beam_bound: whether the current segment is on the boundary of just that part
453 // of the beam with the current beam_rank
454 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
455 : seg.stem_index_ == stems.size () - 1;
456 bool on_beam_bound = (event_dir == LEFT) ? j == 0
457 : j == segs.size () - 1;
458 bool inside_stem = (event_dir == LEFT)
459 ? seg.stem_index_ > 0
460 : seg.stem_index_ + 1 < stems.size ();
462 bool event = on_beam_bound
463 || abs (seg.rank_ - neighbor_seg.rank_) > 1
464 || (abs (vertical_count) >= seg.max_connect_
465 || abs (vertical_count) >= neighbor_seg.max_connect_);
468 // Then this edge of the current segment is irrelevent because it will
469 // be connected with the next segment in the event_dir direction.
472 current.vertical_count_ = vertical_count;
473 current.horizontal_[event_dir] = seg.stem_x_;
474 if (seg.dir_ == event_dir)
475 // then we are examining the edge of a beam segment that is furthest
479 && me->get_bound (event_dir)->break_status_dir ())
481 current.horizontal_[event_dir]
482 = (robust_relative_extent (me->get_bound (event_dir),
483 commonx, X_AXIS)[RIGHT]
484 + event_dir * break_overshoot[event_dir]);
488 Grob *stem = stems[seg.stem_index_];
489 Drul_array<Real> beamlet_length
490 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
491 Drul_array<Real> max_proportion
492 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
493 Real length = beamlet_length[seg.dir_];
497 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
498 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
500 length = min (length,
501 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
503 current.horizontal_[event_dir] += event_dir * length;
507 // we are examining the edge of a beam segment that is closest
508 // (ie. touching, unless there is a gap) its stem.
510 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
513 current.horizontal_[event_dir] -= event_dir * gap_length;
515 if (Stem::is_invisible (seg.stem_))
518 Need to do this in case of whole notes. We don't want the
519 heads to collide with the beams.
521 extract_grob_set (seg.stem_, "note-heads", heads);
523 for (vsize k = 0; k < heads.size (); k++)
524 current.horizontal_[event_dir]
525 = event_dir * min (event_dir * current.horizontal_[event_dir],
528 * heads[k]->extent (commonx,
529 X_AXIS)[-event_dir]);
534 if (event_dir == RIGHT)
536 segments.push_back (current);
537 current = Beam_segment ();
540 while (flip (&event_dir) != LEFT);
548 MAKE_SCHEME_CALLBACK (Beam, print, 1);
550 Beam::print (SCM grob)
552 Spanner *me = unsmob_spanner (grob);
554 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
555 if (!segments.size ())
559 if (normal_stem_count (me))
561 span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
562 span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
566 extract_grob_set (me, "stems", stems);
567 span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
568 span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
571 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
573 SCM posns = me->get_property ("quantized-positions");
575 if (!is_number_pair (posns))
577 programming_error ("no beam positions?");
578 pos = Interval (0, 0);
581 pos = ly_scm2realdrul (posns);
583 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
585 Real dy = pos[RIGHT] - pos[LEFT];
586 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
588 Real beam_thickness = get_beam_thickness (me);
589 Real beam_dy = get_beam_translation (me);
591 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
593 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
597 int extreme = (segments[0].vertical_count_ == 0
598 ? segments[0].vertical_count_
599 : segments.back ().vertical_count_);
601 for (vsize i = 0; i < segments.size (); i++)
603 Real local_slope = slope;
605 Makes local slope proportional to the ratio of the length of this beam
609 local_slope += (feather_dir * segments[i].vertical_count_
611 * placements.length ()
614 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
616 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
617 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
619 Interval weights (1 - multiplier, multiplier);
621 if (feather_dir != LEFT)
624 // we need two translations: the normal one and
625 // the one of the lowest segment
626 int idx[] = {i, extreme};
627 Real translations[2];
629 for (int j = 0; j < 2; j++)
630 translations[j] = slope
631 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
632 + pos.linear_combination (CENTER)
633 + beam_dy * segments[idx[j]].vertical_count_;
635 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
638 Tricky. The manipulation of the variable `weighted_average' below ensures
639 that beams with a RIGHT grow direction will start from the position of the
640 lowest segment at 0, and this error will decrease and decrease over the
641 course of the beam. Something with a LEFT grow direction, on the other
642 hand, will always start in the correct place but progressively accrue
643 error at broken places. This code shifts beams up given where they are
644 in the total span length (controlled by the variable `multiplier'). To
645 better understand what it does, try commenting it out: you'll see that
646 all of the RIGHT growing beams immediately start too low and get better
647 over line breaks, whereas all of the LEFT growing beams start just right
648 and get worse over line breaks.
650 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
652 if (segments[0].vertical_count_ < 0 && feather_dir)
653 weighted_average += beam_dy * (segments.size () - 1) * factor;
655 b.translate_axis (weighted_average, Y_AXIS);
657 the_beam.add_stencil (b);
661 #if (DEBUG_BEAM_SCORING)
662 SCM annotation = me->get_property ("annotation");
663 if (scm_is_string (annotation))
665 extract_grob_set (me, "stems", stems);
668 This code prints the demerits for each beam. Perhaps this
669 should be switchable for those who want to twiddle with the
673 SCM properties = Font_interface::text_font_alist_chain (me);
675 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
678 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
680 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
681 (me->layout ()->self_scm (), properties, annotation));
683 if (!score.is_empty ())
685 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
686 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
691 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
692 return the_beam.smobbed_copy ();
696 Beam::get_default_dir (Grob *me)
698 extract_grob_set (me, "stems", stems);
700 Drul_array<Real> extremes (0.0, 0.0);
701 for (iterof (s, stems); s != stems.end (); s++)
703 Interval positions = Stem::head_positions (*s);
707 if (sign (positions[d]) == d)
708 extremes[d] = d * max (d * positions[d], d * extremes[d]);
710 while (flip (&d) != DOWN);
713 Drul_array<int> total (0, 0);
714 Drul_array<int> count (0, 0);
716 bool force_dir = false;
717 for (vsize i = 0; i < stems.size (); i++)
720 Direction stem_dir = CENTER;
721 SCM stem_dir_scm = s->get_property_data ("direction");
722 if (is_direction (stem_dir_scm))
724 stem_dir = to_dir (stem_dir_scm);
728 stem_dir = to_dir (s->get_property ("default-direction"));
731 stem_dir = to_dir (s->get_property ("neutral-direction"));
736 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
742 if (abs (extremes[UP]) > -extremes[DOWN])
744 else if (extremes[UP] < -extremes[DOWN])
748 Direction dir = CENTER;
749 Direction d = CENTER;
750 if ((d = (Direction) sign (count[UP] - count[DOWN])))
754 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
756 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
759 dir = to_dir (me->get_property ("neutral-direction"));
764 /* Set all stems with non-forced direction to beam direction.
765 Urg: non-forced should become `without/with unforced' direction,
766 once stem gets cleaned-up. */
768 Beam::set_stem_directions (Grob *me, Direction d)
770 extract_grob_set (me, "stems", stems);
772 for (vsize i = 0; i < stems.size (); i++)
776 SCM forcedir = s->get_property_data ("direction");
777 if (!to_dir (forcedir))
778 set_grob_direction (s, d);
783 Only try horizontal beams for knees. No reliable detection of
784 anything else is possible here, since we don't know funky-beaming
785 settings, or X-distances (slopes!) People that want sloped
786 knee-beams, should set the directions manually.
791 this routine should take into account the stemlength scoring
792 of a possible knee/nonknee beam.
795 Beam::consider_auto_knees (Grob *me)
797 SCM scm = me->get_property ("auto-knee-gap");
798 if (!scm_is_number (scm))
805 extract_grob_set (me, "normal-stems", stems);
807 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
808 Real staff_space = Staff_symbol_referencer::staff_space (me);
810 vector<Interval> head_extents_array;
811 for (vsize i = 0; i < stems.size (); i++)
813 Grob *stem = stems[i];
815 Interval head_extents = Stem::head_positions (stem);
816 if (!head_extents.is_empty ())
818 head_extents[LEFT] += -1;
819 head_extents[RIGHT] += 1;
820 head_extents *= staff_space * 0.5;
823 We could subtract beam Y position, but this routine only
824 sets stem directions, a constant shift does not have an
827 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
829 if (to_dir (stem->get_property_data ("direction")))
831 Direction stemdir = to_dir (stem->get_property ("direction"));
832 head_extents[-stemdir] = -stemdir * infinity_f;
835 head_extents_array.push_back (head_extents);
837 gaps.remove_interval (head_extents);
841 Real max_gap_len = 0.0;
843 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
845 Interval gap = gaps.allowed_regions_[i];
848 the outer gaps are not knees.
850 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
853 if (gap.length () >= max_gap_len)
855 max_gap_len = gap.length ();
860 Real beam_translation = get_beam_translation (me);
861 Real beam_thickness = Beam::get_beam_thickness (me);
862 int beam_count = Beam::get_beam_count (me);
863 Real height_of_beams = beam_thickness / 2
864 + (beam_count - 1) * beam_translation;
865 Real threshold = scm_to_double (scm) + height_of_beams;
867 if (max_gap_len > threshold)
870 for (vsize i = 0; i < stems.size (); i++)
872 Grob *stem = stems[i];
873 Interval head_extents = head_extents_array[j++];
875 Direction d = (head_extents.center () < max_gap.center ())
878 stem->set_property ("direction", scm_from_int (d));
880 head_extents.intersect (max_gap);
881 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
886 /* Set stem's shorten property if unset.
889 take some y-position (chord/beam/nearest?) into account
890 scmify forced-fraction
892 This is done in beam because the shorten has to be uniform over the
897 set_minimum_dy (Grob *me, Real *dy)
902 If dy is smaller than the smallest quant, we
903 get absurd direction-sign penalties.
906 Real ss = Staff_symbol_referencer::staff_space (me);
907 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
908 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
909 Real sit = (beam_thickness - slt) / 2;
911 Real hang = 1.0 - (beam_thickness - slt) / 2;
913 *dy = sign (*dy) * max (fabs (*dy),
914 min (min (sit, inter), hang));
918 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
920 Beam::calc_stem_shorten (SCM smob)
922 Grob *me = unsmob_grob (smob);
925 shortening looks silly for x staff beams
928 return scm_from_int (0);
930 Real forced_fraction = 1.0 * forced_stem_count (me)
931 / normal_stem_count (me);
933 int beam_count = get_beam_count (me);
935 SCM shorten_list = me->get_property ("beamed-stem-shorten");
936 if (shorten_list == SCM_EOL)
937 return scm_from_int (0);
939 Real staff_space = Staff_symbol_referencer::staff_space (me);
942 = robust_list_ref (beam_count - 1, shorten_list);
943 Real shorten = scm_to_double (shorten_elt) * staff_space;
945 shorten *= forced_fraction;
948 return scm_from_double (shorten);
950 return scm_from_double (0.0);
954 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
956 extract_grob_set (me, "stems", stems);
958 return default_value;
960 Interval head_positions;
962 for (vsize i = 0; i < stems.size (); i++)
964 head_positions.unite (Stem::head_positions (stems[i]));
965 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
968 Direction dir = get_grob_direction (me);
971 programming_error ("The beam should have a direction by now.");
973 Real y = head_positions.linear_combination (dir)
974 * 0.5 * Staff_symbol_referencer::staff_space (me)
975 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
977 y /= Staff_symbol_referencer::staff_space (me);
978 return Interval (y, y);
982 Compute a first approximation to the beam slope.
984 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
986 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
988 Grob *me = unsmob_grob (smob);
990 int count = normal_stem_count (me);
993 return ly_interval2scm (no_visible_stem_positions (me, pos));
995 vector<Real> x_posns;
996 extract_grob_set (me, "normal-stems", stems);
997 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
998 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1000 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1002 Grob *fvs = first_normal_stem (me);
1003 Grob *lvs = last_normal_stem (me);
1005 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1006 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1007 Stem::get_stem_info (lvs).ideal_y_
1008 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1010 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1011 for (vsize i = 0; i < stems.size (); i++)
1015 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1016 x_posns.push_back (x);
1018 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1024 if (!ideal.delta ())
1026 Interval chord (Stem::chord_start_y (stems[0]),
1027 Stem::chord_start_y (stems.back ()));
1029 /* Simple beams (2 stems) on middle line should be allowed to be
1032 However, if both stems reach middle line,
1033 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1035 For that case, we apply artificial slope */
1036 if (!ideal[LEFT] && chord.delta () && count == 2)
1039 Direction d = (Direction) (sign (chord.delta ()) * UP);
1040 pos[d] = get_beam_thickness (me) / 2;
1047 For broken beams this doesn't work well. In this case, the
1048 slope esp. of the first part of a broken beam should predict
1049 where the second part goes.
1051 ldy = pos[RIGHT] - pos[LEFT];
1055 vector<Offset> ideals;
1056 for (vsize i = 0; i < stems.size (); i++)
1059 ideals.push_back (Offset (x_posns[i],
1060 Stem::get_stem_info (s).ideal_y_
1061 + s->relative_coordinate (commony, Y_AXIS)
1065 minimise_least_squares (&slope, &y, ideals);
1069 set_minimum_dy (me, &dy);
1072 pos = Interval (y, (y + dy));
1076 "position" is relative to the staff.
1078 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1080 me->set_property ("least-squares-dy", scm_from_double (ldy));
1081 return ly_interval2scm (pos);
1084 // Assuming V is not empty, pick a 'reasonable' point inside V.
1086 point_in_interval (Interval v, Real dist)
1088 if (isinf (v[DOWN]))
1089 return v[UP] - dist;
1090 else if (isinf (v[UP]))
1091 return v[DOWN] + dist;
1097 We can't combine with previous function, since check concave and
1098 slope damping comes first.
1100 TODO: we should use the concaveness to control the amount of damping
1103 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1105 Beam::shift_region_to_valid (SCM grob, SCM posns)
1107 Grob *me = unsmob_grob (grob);
1112 vector<Real> x_posns;
1113 extract_grob_set (me, "stems", stems);
1114 extract_grob_set (me, "covered-grobs", covered);
1116 Grob *common[NO_AXES] = { me, me };
1117 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1119 common[a] = common_refpoint_of_array (stems, me, a);
1120 common[a] = common_refpoint_of_array (covered, common[a], a);
1122 Grob *fvs = first_normal_stem (me);
1127 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1128 for (vsize i = 0; i < stems.size (); i++)
1132 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1133 x_posns.push_back (x);
1136 Grob *lvs = last_normal_stem (me);
1137 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1139 Drul_array<Real> pos = ly_scm2interval (posns);
1141 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1143 Real beam_dy = pos[RIGHT] - pos[LEFT];
1144 Real beam_left_y = pos[LEFT];
1145 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1148 Shift the positions so that we have a chance of finding good
1149 quants (i.e. no short stem failures.)
1151 Interval feasible_left_point;
1152 feasible_left_point.set_full ();
1154 for (vsize i = 0; i < stems.size (); i++)
1157 if (Stem::is_invisible (s))
1160 Direction d = get_grob_direction (s);
1162 = Stem::get_stem_info (s).shortest_y_
1163 - slope * x_posns [i];
1166 left_y is now relative to the stem S. We want relative to
1167 ourselves, so translate:
1170 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1171 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1177 feasible_left_point.intersect (flp);
1180 vector<Grob *> filtered;
1182 We only update these for objects that are too large for quanting
1183 to find a workaround. Typically, these are notes with
1184 stems, and timesig/keysig/clef, which take out the entire area
1185 inside the staff as feasible.
1187 The code below disregards the thickness and multiplicity of the
1188 beam. This should not be a problem, as the beam quanting will
1189 take care of computing the impact those exactly.
1191 Real min_y_size = 2.0;
1193 // A list of intervals into which beams may not fall
1194 vector<Interval> forbidden_intervals;
1196 for (vsize i = 0; i < covered.size (); i++)
1198 if (!covered[i]->is_live ())
1201 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1205 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1206 b[a] = covered[i]->extent (common[a], a);
1208 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1211 if (intersection (b[X_AXIS], x_span).is_empty ())
1214 filtered.push_back (covered[i]);
1215 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1216 if (head_stem && Stem::is_normal_stem (head_stem)
1217 && Note_head::has_interface (covered[i]))
1219 if (Stem::get_beam (head_stem))
1222 We must assume that stems are infinitely long in this
1223 case, as asking for the length of the stem typically
1224 leads to circular dependencies.
1226 This strategy assumes that we don't want to handle the
1227 collision of beams in opposite non-forced directions
1228 with this code, where shortening the stems of both
1229 would resolve the problem, eg.
1239 Such beams would need a coordinating grob to resolve
1240 the collision, since both will likely want to occupy
1243 Direction stemdir = get_grob_direction (head_stem);
1244 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1248 // TODO - should we include the extent of the stem here?
1252 if (b[Y_AXIS].length () < min_y_size)
1258 Real x = b[X_AXIS][d] - x_span[LEFT];
1259 Real dy = slope * x;
1261 Direction yd = DOWN;
1262 Interval disallowed;
1265 Real left_y = b[Y_AXIS][yd];
1269 // Translate back to beam as ref point.
1270 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1272 disallowed[yd] = left_y;
1274 while (flip (&yd) != DOWN);
1276 forbidden_intervals.push_back (disallowed);
1278 while (flip (&d) != LEFT);
1282 = Pointer_group_interface::get_grob_array (me,
1283 ly_symbol2scm ("covered-grobs"));
1284 arr->set_array (filtered);
1286 vector_sort (forbidden_intervals, Interval::left_less);
1287 Real epsilon = 1.0e-10;
1288 Interval feasible_beam_placements (beam_left_y, beam_left_y);
1291 forbidden_intervals contains a vector of intervals in which
1292 the beam cannot start. it iterates through these intervals,
1293 pushing feasible_beam_placements epsilon over or epsilon under a
1294 collision. when this type of change happens, the loop is marked
1295 as "dirty" and re-iterated.
1297 TODO: figure out a faster ways that this loop can happen via
1298 a better search algorithm and/or OOP.
1305 for (vsize i = 0; i < forbidden_intervals.size (); i++)
1310 if (forbidden_intervals[i][d] == d * infinity_f)
1311 feasible_beam_placements[d] = d * infinity_f;
1312 else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
1314 feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
1318 while (flip (&d) != DOWN);
1323 // if the beam placement falls out of the feasible region, we push it
1324 // to infinity so that it can never be a feasible candidate below
1328 if (!feasible_left_point.contains (feasible_beam_placements[d]))
1329 feasible_beam_placements[d] = d * infinity_f;
1331 while (flip (&d) != DOWN);
1333 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !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);
1339 else if (!feasible_left_point.is_empty ())
1341 // Only one of them offers is feasible solution. Pick that one.
1342 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
1343 beam_left_y = feasible_beam_placements[UP];
1345 beam_left_y = feasible_beam_placements[DOWN];
1349 // We are completely screwed.
1350 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1353 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1354 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1356 return ly_interval2scm (pos);
1359 /* This neat trick is by Werner Lemberg,
1360 damped = tanh (slope)
1361 corresponds with some tables in [Wanske] CHECKME */
1362 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1364 Beam::slope_damping (SCM smob, SCM posns)
1366 Grob *me = unsmob_grob (smob);
1367 Drul_array<Real> pos = ly_scm2interval (posns);
1369 if (normal_stem_count (me) <= 1)
1372 SCM s = me->get_property ("damping");
1373 Real damping = scm_to_double (s);
1374 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1375 if (concaveness >= 10000)
1377 pos[LEFT] = pos[RIGHT];
1378 me->set_property ("least-squares-dy", scm_from_double (0));
1384 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1386 Real dy = pos[RIGHT] - pos[LEFT];
1388 Grob *fvs = first_normal_stem (me);
1389 Grob *lvs = last_normal_stem (me);
1391 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1393 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1394 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1396 Real slope = dy && dx ? dy / dx : 0;
1398 slope = 0.6 * tanh (slope) / (damping + concaveness);
1400 Real damped_dy = slope * dx;
1402 set_minimum_dy (me, &damped_dy);
1404 pos[LEFT] += (dy - damped_dy) / 2;
1405 pos[RIGHT] -= (dy - damped_dy) / 2;
1407 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1410 return ly_interval2scm (pos);
1413 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1415 Beam::quanting (SCM smob, SCM posns)
1417 Grob *me = unsmob_grob (smob);
1418 Drul_array<Real> ys (0, 0);
1419 ys = robust_scm2drul (posns, ys);
1420 Beam_scoring_problem problem (me, ys);
1422 ys = problem.solve ();
1423 return ly_interval2scm (ys);
1427 Report slice containing the numbers that are both in (car BEAMING)
1431 where_are_the_whole_beams (SCM beaming)
1435 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1437 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1439 l.add_point (scm_to_int (scm_car (s)));
1445 /* Return the Y position of the stem-end, given the Y-left, Y-right
1446 in POS for stem S. This Y position is relative to S. */
1448 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1449 Real xl, Real xr, Direction feather_dir,
1450 Drul_array<Real> pos, bool french)
1452 Real beam_translation = get_beam_translation (me);
1453 Direction stem_dir = get_grob_direction (stem);
1456 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1457 Real xdir = 2 * relx - 1;
1459 Real stem_y = linear_combination (pos, xdir);
1461 SCM beaming = stem->get_property ("beaming");
1463 Slice beam_slice (french
1464 ? where_are_the_whole_beams (beaming)
1465 : Stem::beam_multiplicity (stem));
1466 if (beam_slice.is_empty ())
1467 beam_slice = Slice (0, 0);
1468 Interval beam_multiplicity (beam_slice[LEFT],
1472 feather dir = 1 , relx 0->1 : factor 0 -> 1
1473 feather dir = 0 , relx 0->1 : factor 1 -> 1
1474 feather dir = -1, relx 0->1 : factor 1 -> 0
1476 Real feather_factor = 1;
1477 if (feather_dir > 0)
1478 feather_factor = relx;
1479 else if (feather_dir < 0)
1480 feather_factor = 1 - relx;
1482 stem_y += feather_factor * beam_translation
1483 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1484 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1485 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1491 Hmm. At this time, beam position and slope are determined. Maybe,
1492 stem directions and length should set to relative to the chord's
1493 position of the beam. */
1494 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1496 Beam::set_stem_lengths (SCM smob)
1498 Grob *me = unsmob_grob (smob);
1500 /* trigger callbacks. */
1501 (void) me->get_property ("direction");
1502 (void) me->get_property ("beaming");
1504 SCM posns = me->get_property ("positions");
1506 extract_grob_set (me, "stems", stems);
1511 for (int a = 2; a--;)
1512 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1514 Drul_array<Real> pos = ly_scm2realdrul (posns);
1515 Real staff_space = Staff_symbol_referencer::staff_space (me);
1516 scale_drul (&pos, staff_space);
1520 if (robust_scm2int (me->get_property ("gap-count"), 0))
1523 thick = get_beam_thickness (me);
1526 Grob *fvs = first_normal_stem (me);
1527 Grob *lvs = last_normal_stem (me);
1529 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1530 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1531 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1533 for (vsize i = 0; i < stems.size (); i++)
1537 bool french = to_boolean (s->get_property ("french-beaming"));
1538 Real stem_y = calc_stem_y (me, s, common,
1539 xl, xr, feather_dir,
1540 pos, french && s != lvs && s != fvs);
1543 Make the stems go up to the end of the beam. This doesn't matter
1544 for normal beams, but for tremolo beams it looks silly otherwise.
1547 && !Stem::is_invisible (s))
1548 stem_y += thick * 0.5 * get_grob_direction (s);
1551 Do set_stemend for invisible stems too, so tuplet brackets
1552 have a reference point for sloping
1554 Stem::set_stemend (s, 2 * stem_y / staff_space);
1561 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1563 extract_grob_set (me, "stems", stems);
1566 for (vsize i = 0; i < stems.size (); i++)
1569 Don't overwrite user settings.
1573 Grob *stem = stems[i];
1574 SCM beaming_prop = stem->get_property ("beaming");
1575 if (beaming_prop == SCM_EOL
1576 || index_get_cell (beaming_prop, d) == SCM_EOL)
1578 int count = beaming->beamlet_count (i, d);
1580 && i + 1 < stems.size ()
1581 && Stem::is_invisible (stem))
1582 count = min (count, beaming->beamlet_count (i, -d));
1584 if ( ((i == 0 && d == LEFT)
1585 || (i == stems.size () - 1 && d == RIGHT))
1586 && stems.size () > 1
1587 && to_boolean (me->get_property ("clip-edges")))
1590 Stem::set_beaming (stem, count, d);
1593 while (flip (&d) != LEFT);
1598 Beam::forced_stem_count (Grob *me)
1600 extract_grob_set (me, "normal-stems", stems);
1603 for (vsize i = 0; i < stems.size (); i++)
1607 /* I can imagine counting those boundaries as a half forced stem,
1608 but let's count them full for now. */
1609 Direction defdir = to_dir (s->get_property ("default-direction"));
1611 if (abs (Stem::chord_start_y (s)) > 0.1
1613 && get_grob_direction (s) != defdir)
1620 Beam::normal_stem_count (Grob *me)
1622 extract_grob_set (me, "normal-stems", stems);
1623 return stems.size ();
1627 Beam::first_normal_stem (Grob *me)
1629 extract_grob_set (me, "normal-stems", stems);
1630 return stems.size () ? stems[0] : 0;
1634 Beam::last_normal_stem (Grob *me)
1636 extract_grob_set (me, "normal-stems", stems);
1637 return stems.size () ? stems.back () : 0;
1643 handle rest under beam (do_post: beams are calculated now)
1644 what about combination of collisions and rest under beam.
1648 rest -> stem -> beam -> interpolate_y_position ()
1650 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1652 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1654 Grob *rest = unsmob_grob (smob);
1655 if (scm_is_number (rest->get_property ("staff-position")))
1656 return scm_from_int (0);
1658 Real offset = robust_scm2double (prev_offset, 0.0);
1660 Grob *st = unsmob_grob (rest->get_object ("stem"));
1663 return scm_from_double (0.0);
1664 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1666 || !Beam::has_interface (beam)
1667 || !Beam::normal_stem_count (beam))
1668 return scm_from_double (0.0);
1670 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1671 Drul_array<Real> (0, 0)));
1673 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1675 scale_drul (&pos, staff_space);
1677 Real dy = pos[RIGHT] - pos[LEFT];
1679 Drul_array<Grob *> visible_stems (first_normal_stem (beam),
1680 last_normal_stem (beam));
1681 extract_grob_set (beam, "stems", stems);
1683 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1685 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1686 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1687 Real slope = dy && dx ? dy / dx : 0;
1689 Direction d = get_grob_direction (stem);
1690 Real stem_y = pos[LEFT]
1691 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1693 Real beam_translation = get_beam_translation (beam);
1694 Real beam_thickness = Beam::get_beam_thickness (beam);
1697 TODO: this is not strictly correct for 16th knee beams.
1700 = Stem::beam_multiplicity (stem).length () + 1;
1702 Real height_of_my_beams = beam_thickness / 2
1703 + (beam_count - 1) * beam_translation;
1704 Real beam_y = stem_y - d * height_of_my_beams;
1706 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1708 Interval rest_extent = rest->extent (rest, Y_AXIS);
1709 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1711 Real rest_dim = rest_extent[d];
1712 Real minimum_distance
1713 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1714 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1716 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1718 shift /= staff_space;
1719 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1721 /* Always move discretely by half spaces */
1722 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1724 /* Inside staff, move by whole spaces*/
1725 if ((rest_extent[d] + staff_space * shift) * d
1727 || (rest_extent[-d] + staff_space * shift) * -d
1729 shift = ceil (fabs (shift)) * sign (shift);
1731 return scm_from_double (offset + staff_space * shift);
1735 Beam::is_knee (Grob *me)
1737 SCM k = me->get_property ("knee");
1738 if (scm_is_bool (k))
1739 return ly_scm2bool (k);
1743 extract_grob_set (me, "stems", stems);
1744 for (vsize i = stems.size (); i--;)
1746 Direction dir = get_grob_direction (stems[i]);
1755 me->set_property ("knee", ly_bool2scm (knee));
1761 Beam::is_cross_staff (Grob *me)
1763 extract_grob_set (me, "stems", stems);
1764 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1765 for (vsize i = 0; i < stems.size (); i++)
1766 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1771 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1773 Beam::calc_cross_staff (SCM smob)
1775 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1779 Beam::get_direction_beam_count (Grob *me, Direction d)
1781 extract_grob_set (me, "stems", stems);
1784 for (vsize i = stems.size (); i--;)
1787 Should we take invisible stems into account?
1789 if (get_grob_direction (stems[i]) == d)
1790 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1796 ADD_INTERFACE (Beam,
1799 "The @code{beam-thickness} property is the weight of beams,"
1800 " measured in staffspace. The @code{direction} property is"
1801 " not user-serviceable. Use the @code{direction} property"
1802 " of @code{Stem} instead.\n"
1804 "The following properties may be set in the @code{details}"
1808 "@item stem-length-demerit-factor\n"
1809 "Demerit factor used for inappropriate stem lengths.\n"
1810 "@item secondary-beam-demerit\n"
1811 "Demerit used in quanting calculations for multiple"
1813 "@item region-size\n"
1814 "Size of region for checking quant scores.\n"
1816 "Epsilon for beam quant code to check for presence"
1818 "@item stem-length-limit-penalty\n"
1819 "Penalty for differences in stem lengths on a beam.\n"
1820 "@item damping-direction-penalty\n"
1821 "Demerit penalty applied when beam direction is different"
1822 " from damping direction.\n"
1823 "@item hint-direction-penalty\n"
1824 "Demerit penalty applied when beam direction is different"
1825 " from damping direction, but damping slope is"
1826 " <= @code{round-to-zero-slope}.\n"
1827 "@item musical-direction-factor\n"
1828 "Demerit scaling factor for difference between"
1829 " beam slope and music slope.\n"
1830 "@item ideal-slope-factor\n"
1831 "Demerit scaling factor for difference between"
1832 " beam slope and damping slope.\n"
1833 "@item round-to-zero-slope\n"
1834 "Damping slope which is considered zero for purposes of"
1835 " calculating direction penalties.\n"
1841 "beamed-stem-shorten "
1847 "collision-interfaces "
1848 "collision-voice-only "
1860 "neutral-direction "
1863 "quantized-positions "