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.hh"
58 #include "staff-symbol-referencer.hh"
62 #if DEBUG_BEAM_SCORING
63 #include "text-interface.hh" // debug output.
64 #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);
219 /* We want a maximal number of shared beams, but if there is choice, we
220 * take the one that is closest to the end of the stem. This is for
232 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
236 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
240 for (int i = lslice[-left_dir];
241 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
244 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
246 int k = -right_dir * scm_to_int (scm_car (s)) + i;
247 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
251 if (count >= best_count)
261 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
263 Beam::calc_beaming (SCM smob)
265 Grob *me = unsmob_grob (smob);
267 extract_grob_set (me, "stems", stems);
270 last_int.set_empty ();
272 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
273 Direction last_dir = CENTER;
274 for (vsize i = 0; i < stems.size (); i++)
276 Grob *this_stem = stems[i];
277 SCM this_beaming = this_stem->get_property ("beaming");
279 Direction this_dir = get_grob_direction (this_stem);
280 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
282 int start_point = position_with_maximal_common_beams
283 (last_beaming, this_beaming,
284 last_dir ? last_dir : this_dir,
291 new_slice.set_empty ();
292 SCM s = index_get_cell (this_beaming, d);
293 for (; scm_is_pair (s); s = scm_cdr (s))
296 = start_point - this_dir * scm_to_int (scm_car (s));
298 new_slice.add_point (new_beam_pos);
299 scm_set_car_x (s, scm_from_int (new_beam_pos));
302 while (flip (&d) != LEFT);
304 if (!new_slice.is_empty ())
305 last_int = new_slice;
310 FIXME: what's this for?
312 SCM s = scm_cdr (this_beaming);
313 for (; scm_is_pair (s); s = scm_cdr (s))
315 int np = -this_dir * scm_to_int (scm_car (s));
316 scm_set_car_x (s, scm_from_int (np));
317 last_int.add_point (np);
321 if (scm_ilength (scm_cdr (this_beaming)) > 0)
323 last_beaming = this_beaming;
332 operator <(Beam_stem_segment const &a,
333 Beam_stem_segment const &b)
335 return a.rank_ < b.rank_;
338 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
340 // TODO - should store result in a property?
342 Beam::get_beam_segments (Grob *me_grob, Grob **common)
344 /* ugh, this has a side-effect that we need to ensure that
345 Stem #'beaming is correct */
346 (void) me_grob->get_property ("beaming");
348 Spanner *me = dynamic_cast<Spanner *> (me_grob);
350 extract_grob_set (me, "stems", stems);
351 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
353 commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
354 commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
358 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
359 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
361 Position_stem_segments_map stem_segments;
362 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
364 /* There are two concepts of "rank" that are used in the following code.
365 The beam_rank is the vertical position of the beam (larger numbers are
366 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
367 is the horizontal position of the segment (this is incremented by two
368 for each stem; the beam segment on the right side of the stem has
369 a higher rank (by one) than its neighbour to the left). */
371 for (vsize i = 0; i < stems.size (); i++)
373 Grob *stem = stems[i];
374 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
375 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
376 SCM beaming = stem->get_property ("beaming");
380 // Find the maximum and minimum beam ranks.
381 // Given that RANKS is never reset to empty, the interval will always be
382 // smallest for the left beamlet of the first stem, and then it might grow.
383 // Do we really want this? (It only affects the tremolo gaps) --jneem
384 for (SCM s = index_get_cell (beaming, d);
385 scm_is_pair (s); s = scm_cdr (s))
387 if (!scm_is_integer (scm_car (s)))
390 int beam_rank = scm_to_int (scm_car (s));
391 ranks.add_point (beam_rank);
394 for (SCM s = index_get_cell (beaming, d);
395 scm_is_pair (s); s = scm_cdr (s))
397 if (!scm_is_integer (scm_car (s)))
400 int beam_rank = scm_to_int (scm_car (s));
401 Beam_stem_segment seg;
403 seg.stem_x_ = stem_x;
404 seg.rank_ = 2 * i + (d + 1) / 2;
405 seg.width_ = stem_width;
408 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
410 Direction stem_dir = get_grob_direction (stem);
413 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
414 stem_segments[beam_rank].push_back (seg);
417 while (flip (&d) != LEFT);
420 Drul_array<Real> break_overshoot
421 = robust_scm2drul (me->get_property ("break-overshoot"),
422 Drul_array<Real> (-0.5, 0.0));
424 vector<Beam_segment> segments;
425 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
426 i != stem_segments.end (); i++)
428 vector<Beam_stem_segment> segs = (*i).second;
429 vector_sort (segs, less<Beam_stem_segment> ());
431 Beam_segment current;
433 // Iterate over all of the segments of the current beam rank,
434 // merging the adjacent Beam_stem_segments into one Beam_segment
436 int vertical_count = (*i).first;
437 for (vsize j = 0; j < segs.size (); j++)
439 // Keeping track of the different directions here is a little tricky.
440 // segs[j].dir_ is the direction of the beam segment relative to the stem
441 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
442 // its stem) whereas event_dir refers to the edge of the beam segment that
443 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
444 // are looking at that edge of the beam segment that is furthest from its
446 Direction event_dir = LEFT;
447 Beam_stem_segment const &seg = segs[j];
450 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
451 // TODO: make names clearer? --jneem
452 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
453 // on_beam_bound: whether the current segment is on the boundary of just that part
454 // of the beam with the current beam_rank
455 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
456 : seg.stem_index_ == stems.size () - 1;
457 bool on_beam_bound = (event_dir == LEFT) ? j == 0
458 : j == segs.size () - 1;
459 bool inside_stem = (event_dir == LEFT)
460 ? seg.stem_index_ > 0
461 : seg.stem_index_ + 1 < stems.size ();
463 bool event = on_beam_bound
464 || abs (seg.rank_ - neighbor_seg.rank_) > 1
465 || (abs (vertical_count) >= seg.max_connect_
466 || abs (vertical_count) >= neighbor_seg.max_connect_);
469 // Then this edge of the current segment is irrelevent because it will
470 // be connected with the next segment in the event_dir direction.
473 current.vertical_count_ = vertical_count;
474 current.horizontal_[event_dir] = seg.stem_x_;
475 if (seg.dir_ == event_dir)
476 // then we are examining the edge of a beam segment that is furthest
480 && me->get_bound (event_dir)->break_status_dir ())
482 current.horizontal_[event_dir]
483 = (robust_relative_extent (me->get_bound (event_dir),
484 commonx, X_AXIS)[RIGHT]
485 + event_dir * break_overshoot[event_dir]);
489 Grob *stem = stems[seg.stem_index_];
490 Drul_array<Real> beamlet_length
491 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
492 Drul_array<Real> max_proportion
493 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
494 Real length = beamlet_length[seg.dir_];
498 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
499 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
501 length = min (length,
502 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
504 current.horizontal_[event_dir] += event_dir * length;
508 // we are examining the edge of a beam segment that is closest
509 // (ie. touching, unless there is a gap) its stem.
511 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
514 current.horizontal_[event_dir] -= event_dir * gap_length;
516 if (Stem::is_invisible (seg.stem_))
519 Need to do this in case of whole notes. We don't want the
520 heads to collide with the beams.
522 extract_grob_set (seg.stem_, "note-heads", heads);
524 for (vsize k = 0; k < heads.size (); k++)
525 current.horizontal_[event_dir]
526 = event_dir * min (event_dir * current.horizontal_[event_dir],
529 * heads[k]->extent (commonx,
530 X_AXIS)[-event_dir]);
535 if (event_dir == RIGHT)
537 segments.push_back (current);
538 current = Beam_segment ();
541 while (flip (&event_dir) != LEFT);
549 MAKE_SCHEME_CALLBACK (Beam, print, 1);
551 Beam::print (SCM grob)
553 Spanner *me = unsmob_spanner (grob);
555 vector<Beam_segment> segments = get_beam_segments (me, &commonx);
556 if (!segments.size ())
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);
743 if (abs (extremes[UP]) > -extremes[DOWN])
745 else if (extremes[UP] < -extremes[DOWN])
749 Direction dir = CENTER;
750 Direction d = CENTER;
751 if ((d = (Direction) sign (count[UP] - count[DOWN])))
755 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
757 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
760 dir = to_dir (me->get_property ("neutral-direction"));
765 /* Set all stems with non-forced direction to beam direction.
766 Urg: non-forced should become `without/with unforced' direction,
767 once stem gets cleaned-up. */
769 Beam::set_stem_directions (Grob *me, Direction d)
771 extract_grob_set (me, "stems", stems);
773 for (vsize i = 0; i < stems.size (); i++)
777 SCM forcedir = s->get_property_data ("direction");
778 if (!to_dir (forcedir))
779 set_grob_direction (s, d);
784 Only try horizontal beams for knees. No reliable detection of
785 anything else is possible here, since we don't know funky-beaming
786 settings, or X-distances (slopes!) People that want sloped
787 knee-beams, should set the directions manually.
792 this routine should take into account the stemlength scoring
793 of a possible knee/nonknee beam.
796 Beam::consider_auto_knees (Grob *me)
798 SCM scm = me->get_property ("auto-knee-gap");
799 if (!scm_is_number (scm))
806 extract_grob_set (me, "normal-stems", stems);
808 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
809 Real staff_space = Staff_symbol_referencer::staff_space (me);
811 vector<Interval> head_extents_array;
812 for (vsize i = 0; i < stems.size (); i++)
814 Grob *stem = stems[i];
816 Interval head_extents = Stem::head_positions (stem);
817 if (!head_extents.is_empty ())
819 head_extents[LEFT] += -1;
820 head_extents[RIGHT] += 1;
821 head_extents *= staff_space * 0.5;
824 We could subtract beam Y position, but this routine only
825 sets stem directions, a constant shift does not have an
828 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
830 if (to_dir (stem->get_property_data ("direction")))
832 Direction stemdir = to_dir (stem->get_property ("direction"));
833 head_extents[-stemdir] = -stemdir * infinity_f;
836 head_extents_array.push_back (head_extents);
838 gaps.remove_interval (head_extents);
842 Real max_gap_len = 0.0;
844 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
846 Interval gap = gaps.allowed_regions_[i];
849 the outer gaps are not knees.
851 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
854 if (gap.length () >= max_gap_len)
856 max_gap_len = gap.length ();
861 Real beam_translation = get_beam_translation (me);
862 Real beam_thickness = Beam::get_beam_thickness (me);
863 int beam_count = Beam::get_beam_count (me);
864 Real height_of_beams = beam_thickness / 2
865 + (beam_count - 1) * beam_translation;
866 Real threshold = scm_to_double (scm) + height_of_beams;
868 if (max_gap_len > threshold)
871 for (vsize i = 0; i < stems.size (); i++)
873 Grob *stem = stems[i];
874 Interval head_extents = head_extents_array[j++];
876 Direction d = (head_extents.center () < max_gap.center ())
879 stem->set_property ("direction", scm_from_int (d));
881 head_extents.intersect (max_gap);
882 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
887 /* Set stem's shorten property if unset.
890 take some y-position (chord/beam/nearest?) into account
891 scmify forced-fraction
893 This is done in beam because the shorten has to be uniform over the
898 set_minimum_dy (Grob *me, Real *dy)
903 If dy is smaller than the smallest quant, we
904 get absurd direction-sign penalties.
907 Real ss = Staff_symbol_referencer::staff_space (me);
908 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
909 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
910 Real sit = (beam_thickness - slt) / 2;
912 Real hang = 1.0 - (beam_thickness - slt) / 2;
914 *dy = sign (*dy) * max (fabs (*dy),
915 min (min (sit, inter), hang));
919 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
921 Beam::calc_stem_shorten (SCM smob)
923 Grob *me = unsmob_grob (smob);
926 shortening looks silly for x staff beams
929 return scm_from_int (0);
931 Real forced_fraction = 1.0 * forced_stem_count (me)
932 / normal_stem_count (me);
934 int beam_count = get_beam_count (me);
936 SCM shorten_list = me->get_property ("beamed-stem-shorten");
937 if (shorten_list == SCM_EOL)
938 return scm_from_int (0);
940 Real staff_space = Staff_symbol_referencer::staff_space (me);
943 = robust_list_ref (beam_count - 1, shorten_list);
944 Real shorten = scm_to_double (shorten_elt) * staff_space;
946 shorten *= forced_fraction;
949 return scm_from_double (shorten);
951 return scm_from_double (0.0);
955 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
957 extract_grob_set (me, "stems", stems);
959 return default_value;
961 Interval head_positions;
963 for (vsize i = 0; i < stems.size (); i++)
965 head_positions.unite (Stem::head_positions (stems[i]));
966 multiplicity.unite (Stem::beam_multiplicity (stems[i]));
969 Direction dir = get_grob_direction (me);
972 programming_error ("The beam should have a direction by now.");
974 Real y = head_positions.linear_combination (dir)
975 * 0.5 * Staff_symbol_referencer::staff_space (me)
976 + dir * get_beam_translation (me) * (multiplicity.length () + 1);
978 y /= Staff_symbol_referencer::staff_space (me);
979 return Interval (y, y);
983 Compute a first approximation to the beam slope.
985 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
987 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
989 Grob *me = unsmob_grob (smob);
991 int count = normal_stem_count (me);
994 return ly_interval2scm (no_visible_stem_positions (me, pos));
996 vector<Real> x_posns;
997 extract_grob_set (me, "normal-stems", stems);
998 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
999 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1001 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1003 Grob *fvs = first_normal_stem (me);
1004 Grob *lvs = last_normal_stem (me);
1006 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
1007 + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
1008 Stem::get_stem_info (lvs).ideal_y_
1009 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1011 Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1012 for (vsize i = 0; i < stems.size (); i++)
1016 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1017 x_posns.push_back (x);
1019 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1025 if (!ideal.delta ())
1027 Interval chord (Stem::chord_start_y (stems[0]),
1028 Stem::chord_start_y (stems.back ()));
1030 /* Simple beams (2 stems) on middle line should be allowed to be
1033 However, if both stems reach middle line,
1034 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
1036 For that case, we apply artificial slope */
1037 if (!ideal[LEFT] && chord.delta () && count == 2)
1040 Direction d = (Direction) (sign (chord.delta ()) * UP);
1041 pos[d] = get_beam_thickness (me) / 2;
1048 For broken beams this doesn't work well. In this case, the
1049 slope esp. of the first part of a broken beam should predict
1050 where the second part goes.
1052 ldy = pos[RIGHT] - pos[LEFT];
1056 vector<Offset> ideals;
1057 for (vsize i = 0; i < stems.size (); i++)
1060 ideals.push_back (Offset (x_posns[i],
1061 Stem::get_stem_info (s).ideal_y_
1062 + s->relative_coordinate (commony, Y_AXIS)
1066 minimise_least_squares (&slope, &y, ideals);
1070 set_minimum_dy (me, &dy);
1073 pos = Interval (y, (y + dy));
1077 "position" is relative to the staff.
1079 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1081 me->set_property ("least-squares-dy", scm_from_double (ldy));
1082 return ly_interval2scm (pos);
1085 // Assuming V is not empty, pick a 'reasonable' point inside V.
1087 point_in_interval (Interval v, Real dist)
1089 if (isinf (v[DOWN]))
1090 return v[UP] - dist;
1091 else if (isinf (v[UP]))
1092 return v[DOWN] + dist;
1098 We can't combine with previous function, since check concave and
1099 slope damping comes first.
1101 TODO: we should use the concaveness to control the amount of damping
1104 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1106 Beam::shift_region_to_valid (SCM grob, SCM posns)
1108 Grob *me = unsmob_grob (grob);
1113 vector<Real> x_posns;
1114 extract_grob_set (me, "stems", stems);
1115 extract_grob_set (me, "covered-grobs", covered);
1117 Grob *common[NO_AXES] = { me, me };
1118 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1120 common[a] = common_refpoint_of_array (stems, me, a);
1121 common[a] = common_refpoint_of_array (covered, common[a], a);
1123 Grob *fvs = first_normal_stem (me);
1128 x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1129 for (vsize i = 0; i < stems.size (); i++)
1133 Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1134 x_posns.push_back (x);
1137 Grob *lvs = last_normal_stem (me);
1138 x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1140 Drul_array<Real> pos = ly_scm2interval (posns);
1142 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1144 Real beam_dy = pos[RIGHT] - pos[LEFT];
1145 Real beam_left_y = pos[LEFT];
1146 Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1149 Shift the positions so that we have a chance of finding good
1150 quants (i.e. no short stem failures.)
1152 Interval feasible_left_point;
1153 feasible_left_point.set_full ();
1155 for (vsize i = 0; i < stems.size (); i++)
1158 if (Stem::is_invisible (s))
1161 Direction d = get_grob_direction (s);
1163 = Stem::get_stem_info (s).shortest_y_
1164 - slope * x_posns [i];
1167 left_y is now relative to the stem S. We want relative to
1168 ourselves, so translate:
1171 += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1172 - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1178 feasible_left_point.intersect (flp);
1181 vector<Grob *> filtered;
1183 We only update these for objects that are too large for quanting
1184 to find a workaround. Typically, these are notes with
1185 stems, and timesig/keysig/clef, which take out the entire area
1186 inside the staff as feasible.
1188 The code below disregards the thickness and multiplicity of the
1189 beam. This should not be a problem, as the beam quanting will
1190 take care of computing the impact those exactly.
1192 Real min_y_size = 2.0;
1194 // A list of intervals into which beams may not fall
1195 vector<Interval> forbidden_intervals;
1197 for (vsize i = 0; i < covered.size (); i++)
1199 if (!covered[i]->is_live ())
1202 if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1206 for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1207 b[a] = covered[i]->extent (common[a], a);
1209 if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1212 if (intersection (b[X_AXIS], x_span).is_empty ())
1215 filtered.push_back (covered[i]);
1216 Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1217 if (head_stem && Stem::is_normal_stem (head_stem)
1218 && Note_head::has_interface (covered[i]))
1220 if (Stem::get_beam (head_stem))
1223 We must assume that stems are infinitely long in this
1224 case, as asking for the length of the stem typically
1225 leads to circular dependencies.
1227 This strategy assumes that we don't want to handle the
1228 collision of beams in opposite non-forced directions
1229 with this code, where shortening the stems of both
1230 would resolve the problem, eg.
1240 Such beams would need a coordinating grob to resolve
1241 the collision, since both will likely want to occupy
1244 Direction stemdir = get_grob_direction (head_stem);
1245 b[Y_AXIS][stemdir] = stemdir * infinity_f;
1249 // TODO - should we include the extent of the stem here?
1253 if (b[Y_AXIS].length () < min_y_size)
1259 Real x = b[X_AXIS][d] - x_span[LEFT];
1260 Real dy = slope * x;
1262 Direction yd = DOWN;
1263 Interval disallowed;
1266 Real left_y = b[Y_AXIS][yd];
1270 // Translate back to beam as ref point.
1271 left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1273 disallowed[yd] = left_y;
1275 while (flip (&yd) != DOWN);
1277 forbidden_intervals.push_back (disallowed);
1279 while (flip (&d) != LEFT);
1283 = Pointer_group_interface::get_grob_array (me,
1284 ly_symbol2scm ("covered-grobs"));
1285 arr->set_array (filtered);
1287 vector_sort (forbidden_intervals, Interval::left_less);
1288 Real epsilon = 1.0e-10;
1289 Interval feasible_beam_placements (beam_left_y, beam_left_y);
1292 forbidden_intervals contains a vector of intervals in which
1293 the beam cannot start. it iterates through these intervals,
1294 pushing feasible_beam_placements epsilon over or epsilon under a
1295 collision. when this type of change happens, the loop is marked
1296 as "dirty" and re-iterated.
1298 TODO: figure out a faster ways that this loop can happen via
1299 a better search algorithm and/or OOP.
1306 for (vsize i = 0; i < forbidden_intervals.size (); i++)
1311 if (forbidden_intervals[i][d] == d * infinity_f)
1312 feasible_beam_placements[d] = d * infinity_f;
1313 else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
1315 feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
1319 while (flip (&d) != DOWN);
1324 // if the beam placement falls out of the feasible region, we push it
1325 // to infinity so that it can never be a feasible candidate below
1329 if (!feasible_left_point.contains (feasible_beam_placements[d]))
1330 feasible_beam_placements[d] = d * infinity_f;
1332 while (flip (&d) != DOWN);
1334 if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
1336 // We are somewhat screwed: we have a collision, but at least
1337 // there is a way to satisfy stem length constraints.
1338 beam_left_y = point_in_interval (feasible_left_point, 2.0);
1340 else if (!feasible_left_point.is_empty ())
1342 // Only one of them offers is feasible solution. Pick that one.
1343 if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
1344 beam_left_y = feasible_beam_placements[UP];
1346 beam_left_y = feasible_beam_placements[DOWN];
1350 // We are completely screwed.
1351 me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1354 pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1355 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1357 return ly_interval2scm (pos);
1360 /* This neat trick is by Werner Lemberg,
1361 damped = tanh (slope)
1362 corresponds with some tables in [Wanske] CHECKME */
1363 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1365 Beam::slope_damping (SCM smob, SCM posns)
1367 Grob *me = unsmob_grob (smob);
1368 Drul_array<Real> pos = ly_scm2interval (posns);
1370 if (normal_stem_count (me) <= 1)
1373 SCM s = me->get_property ("damping");
1374 Real damping = scm_to_double (s);
1375 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1376 if (concaveness >= 10000)
1378 pos[LEFT] = pos[RIGHT];
1379 me->set_property ("least-squares-dy", scm_from_double (0));
1385 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1387 Real dy = pos[RIGHT] - pos[LEFT];
1389 Grob *fvs = first_normal_stem (me);
1390 Grob *lvs = last_normal_stem (me);
1392 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1394 Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1395 - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1397 Real slope = dy && dx ? dy / dx : 0;
1399 slope = 0.6 * tanh (slope) / (damping + concaveness);
1401 Real damped_dy = slope * dx;
1403 set_minimum_dy (me, &damped_dy);
1405 pos[LEFT] += (dy - damped_dy) / 2;
1406 pos[RIGHT] -= (dy - damped_dy) / 2;
1408 scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1411 return ly_interval2scm (pos);
1414 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1416 Beam::quanting (SCM smob, SCM posns)
1418 Grob *me = unsmob_grob (smob);
1419 Drul_array<Real> ys (0, 0);
1420 ys = robust_scm2drul (posns, ys);
1421 Beam_scoring_problem problem (me, ys);
1423 ys = problem.solve ();
1424 return ly_interval2scm (ys);
1428 Report slice containing the numbers that are both in (car BEAMING)
1432 where_are_the_whole_beams (SCM beaming)
1436 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1438 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1440 l.add_point (scm_to_int (scm_car (s)));
1446 /* Return the Y position of the stem-end, given the Y-left, Y-right
1447 in POS for stem S. This Y position is relative to S. */
1449 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1450 Real xl, Real xr, Direction feather_dir,
1451 Drul_array<Real> pos, bool french)
1453 Real beam_translation = get_beam_translation (me);
1454 Direction stem_dir = get_grob_direction (stem);
1457 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1458 Real xdir = 2 * relx - 1;
1460 Real stem_y = linear_combination (pos, xdir);
1462 SCM beaming = stem->get_property ("beaming");
1464 Slice beam_slice (french
1465 ? where_are_the_whole_beams (beaming)
1466 : Stem::beam_multiplicity (stem));
1467 if (beam_slice.is_empty ())
1468 beam_slice = Slice (0, 0);
1469 Interval beam_multiplicity (beam_slice[LEFT],
1473 feather dir = 1 , relx 0->1 : factor 0 -> 1
1474 feather dir = 0 , relx 0->1 : factor 1 -> 1
1475 feather dir = -1, relx 0->1 : factor 1 -> 0
1477 Real feather_factor = 1;
1478 if (feather_dir > 0)
1479 feather_factor = relx;
1480 else if (feather_dir < 0)
1481 feather_factor = 1 - relx;
1483 stem_y += feather_factor * beam_translation
1484 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1485 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1486 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1492 Hmm. At this time, beam position and slope are determined. Maybe,
1493 stem directions and length should set to relative to the chord's
1494 position of the beam. */
1495 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1497 Beam::set_stem_lengths (SCM smob)
1499 Grob *me = unsmob_grob (smob);
1501 /* trigger callbacks. */
1502 (void) me->get_property ("direction");
1503 (void) me->get_property ("beaming");
1505 SCM posns = me->get_property ("positions");
1507 extract_grob_set (me, "stems", stems);
1512 for (int a = 2; a--;)
1513 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1515 Drul_array<Real> pos = ly_scm2realdrul (posns);
1516 Real staff_space = Staff_symbol_referencer::staff_space (me);
1517 scale_drul (&pos, staff_space);
1521 if (robust_scm2int (me->get_property ("gap-count"), 0))
1524 thick = get_beam_thickness (me);
1527 Grob *fvs = first_normal_stem (me);
1528 Grob *lvs = last_normal_stem (me);
1530 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1531 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1532 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1534 for (vsize i = 0; i < stems.size (); i++)
1538 bool french = to_boolean (s->get_property ("french-beaming"));
1539 Real stem_y = calc_stem_y (me, s, common,
1540 xl, xr, feather_dir,
1541 pos, french && s != lvs && s != fvs);
1544 Make the stems go up to the end of the beam. This doesn't matter
1545 for normal beams, but for tremolo beams it looks silly otherwise.
1548 && !Stem::is_invisible (s))
1549 stem_y += thick * 0.5 * get_grob_direction (s);
1552 Do set_stemend for invisible stems too, so tuplet brackets
1553 have a reference point for sloping
1555 Stem::set_stemend (s, 2 * stem_y / staff_space);
1562 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1564 extract_grob_set (me, "stems", stems);
1567 for (vsize i = 0; i < stems.size (); i++)
1570 Don't overwrite user settings.
1574 Grob *stem = stems[i];
1575 SCM beaming_prop = stem->get_property ("beaming");
1576 if (beaming_prop == SCM_EOL
1577 || index_get_cell (beaming_prop, d) == SCM_EOL)
1579 int count = beaming->beamlet_count (i, d);
1581 && i + 1 < stems.size ()
1582 && Stem::is_invisible (stem))
1583 count = min (count, beaming->beamlet_count (i, -d));
1585 if ( ((i == 0 && d == LEFT)
1586 || (i == stems.size () - 1 && d == RIGHT))
1587 && stems.size () > 1
1588 && to_boolean (me->get_property ("clip-edges")))
1591 Stem::set_beaming (stem, count, d);
1594 while (flip (&d) != LEFT);
1599 Beam::forced_stem_count (Grob *me)
1601 extract_grob_set (me, "normal-stems", stems);
1604 for (vsize i = 0; i < stems.size (); i++)
1608 /* I can imagine counting those boundaries as a half forced stem,
1609 but let's count them full for now. */
1610 Direction defdir = to_dir (s->get_property ("default-direction"));
1612 if (abs (Stem::chord_start_y (s)) > 0.1
1614 && get_grob_direction (s) != defdir)
1621 Beam::normal_stem_count (Grob *me)
1623 extract_grob_set (me, "normal-stems", stems);
1624 return stems.size ();
1628 Beam::first_normal_stem (Grob *me)
1630 extract_grob_set (me, "normal-stems", stems);
1631 return stems.size () ? stems[0] : 0;
1635 Beam::last_normal_stem (Grob *me)
1637 extract_grob_set (me, "normal-stems", stems);
1638 return stems.size () ? stems.back () : 0;
1644 handle rest under beam (do_post: beams are calculated now)
1645 what about combination of collisions and rest under beam.
1649 rest -> stem -> beam -> interpolate_y_position ()
1651 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1653 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1655 Grob *rest = unsmob_grob (smob);
1656 if (scm_is_number (rest->get_property ("staff-position")))
1657 return scm_from_int (0);
1659 Real offset = robust_scm2double (prev_offset, 0.0);
1661 Grob *st = unsmob_grob (rest->get_object ("stem"));
1664 return scm_from_double (0.0);
1665 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1667 || !Beam::has_interface (beam)
1668 || !Beam::normal_stem_count (beam))
1669 return scm_from_double (0.0);
1671 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1672 Drul_array<Real> (0, 0)));
1674 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1676 scale_drul (&pos, staff_space);
1678 Real dy = pos[RIGHT] - pos[LEFT];
1680 Drul_array<Grob *> visible_stems (first_normal_stem (beam),
1681 last_normal_stem (beam));
1682 extract_grob_set (beam, "stems", stems);
1684 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1686 Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1687 Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1688 Real slope = dy && dx ? dy / dx : 0;
1690 Direction d = get_grob_direction (stem);
1691 Real stem_y = pos[LEFT]
1692 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1694 Real beam_translation = get_beam_translation (beam);
1695 Real beam_thickness = Beam::get_beam_thickness (beam);
1698 TODO: this is not strictly correct for 16th knee beams.
1701 = Stem::beam_multiplicity (stem).length () + 1;
1703 Real height_of_my_beams = beam_thickness / 2
1704 + (beam_count - 1) * beam_translation;
1705 Real beam_y = stem_y - d * height_of_my_beams;
1707 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1709 Interval rest_extent = rest->extent (rest, Y_AXIS);
1710 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1712 Real rest_dim = rest_extent[d];
1713 Real minimum_distance
1714 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1715 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1717 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1719 shift /= staff_space;
1720 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1722 /* Always move discretely by half spaces */
1723 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1725 /* Inside staff, move by whole spaces*/
1726 if ((rest_extent[d] + staff_space * shift) * d
1728 || (rest_extent[-d] + staff_space * shift) * -d
1730 shift = ceil (fabs (shift)) * sign (shift);
1732 return scm_from_double (offset + staff_space * shift);
1735 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1737 Beam::pure_rest_collision_callback (SCM smob,
1738 SCM, /* prev_offset */
1744 Grob *me = unsmob_grob (smob);
1745 Grob *stem = unsmob_grob (me->get_object ("stem"));
1747 return scm_from_double (amount);
1748 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1750 || !Beam::normal_stem_count (beam))
1751 return scm_from_double (amount);
1753 Real ss = Staff_symbol_referencer::staff_space (me);
1756 This gives the extrema of rest positions.
1757 In general, beams are never typeset more than one staff space away
1758 from the staff in either direction.
1760 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1761 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1762 rest_max_pos.widen (1);
1763 rest_max_pos *= ss / 2;
1765 extract_grob_set (beam, "stems", stems);
1766 vector<Grob *> my_stems;
1768 for (vsize i = 0; i < stems.size (); i++)
1769 if (Stem::head_count (stems[i]) || stems[i] == stem)
1770 my_stems.push_back (stems[i]);
1774 for (vsize i = 0; i < my_stems.size (); i++)
1775 if (my_stems[i] == stem)
1783 if (idx == -1 || my_stems.size () == 1)
1784 return scm_from_double (amount);
1786 left = right = my_stems[1];
1787 else if (idx == my_stems.size () - 1)
1788 left = right = my_stems[idx - 1];
1791 left = my_stems[idx - 1];
1792 right = my_stems[idx + 1];
1794 Direction beamdir = get_grob_direction (beam);
1796 Take the position between the two bounding head_positions,
1797 then bound it by the minimum and maximum positions outside the staff.
1798 4.0 = 2.0 to get out of staff space * 2.0 for the average
1800 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1802 return scm_from_double (amount);
1807 Beam::is_knee (Grob *me)
1809 SCM k = me->get_property ("knee");
1810 if (scm_is_bool (k))
1811 return ly_scm2bool (k);
1815 extract_grob_set (me, "stems", stems);
1816 for (vsize i = stems.size (); i--;)
1818 Direction dir = get_grob_direction (stems[i]);
1827 me->set_property ("knee", ly_bool2scm (knee));
1833 Beam::is_cross_staff (Grob *me)
1835 extract_grob_set (me, "stems", stems);
1836 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1837 for (vsize i = 0; i < stems.size (); i++)
1838 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1843 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1845 Beam::calc_cross_staff (SCM smob)
1847 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1851 Beam::get_direction_beam_count (Grob *me, Direction d)
1853 extract_grob_set (me, "stems", stems);
1856 for (vsize i = stems.size (); i--;)
1859 Should we take invisible stems into account?
1861 if (get_grob_direction (stems[i]) == d)
1862 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1868 ADD_INTERFACE (Beam,
1871 "The @code{beam-thickness} property is the weight of beams,"
1872 " measured in staffspace. The @code{direction} property is"
1873 " not user-serviceable. Use the @code{direction} property"
1874 " of @code{Stem} instead.\n"
1876 "The following properties may be set in the @code{details}"
1880 "@item stem-length-demerit-factor\n"
1881 "Demerit factor used for inappropriate stem lengths.\n"
1882 "@item secondary-beam-demerit\n"
1883 "Demerit used in quanting calculations for multiple"
1885 "@item region-size\n"
1886 "Size of region for checking quant scores.\n"
1888 "Epsilon for beam quant code to check for presence"
1890 "@item stem-length-limit-penalty\n"
1891 "Penalty for differences in stem lengths on a beam.\n"
1892 "@item damping-direction-penalty\n"
1893 "Demerit penalty applied when beam direction is different"
1894 " from damping direction.\n"
1895 "@item hint-direction-penalty\n"
1896 "Demerit penalty applied when beam direction is different"
1897 " from damping direction, but damping slope is"
1898 " <= @code{round-to-zero-slope}.\n"
1899 "@item musical-direction-factor\n"
1900 "Demerit scaling factor for difference between"
1901 " beam slope and music slope.\n"
1902 "@item ideal-slope-factor\n"
1903 "Demerit scaling factor for difference between"
1904 " beam slope and damping slope.\n"
1905 "@item round-to-zero-slope\n"
1906 "Damping slope which is considered zero for purposes of"
1907 " calculating direction penalties.\n"
1913 "beamed-stem-shorten "
1919 "collision-interfaces "
1920 "collision-voice-only "
1932 "neutral-direction "
1935 "quantized-positions "