2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 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 "axis-group-interface.hh"
41 #include "align-interface.hh"
42 #include "beam-scoring-problem.hh"
43 #include "beaming-pattern.hh"
44 #include "directional-element-interface.hh"
45 #include "grob-array.hh"
46 #include "international.hh"
47 #include "interval-set.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,
288 for (LEFT_and_RIGHT (d))
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));
302 if (!new_slice.is_empty ())
303 last_int = new_slice;
308 FIXME: what's this for?
310 SCM s = scm_cdr (this_beaming);
311 for (; scm_is_pair (s); s = scm_cdr (s))
313 int np = -this_dir * scm_to_int (scm_car (s));
314 scm_set_car_x (s, scm_from_int (np));
315 last_int.add_point (np);
319 if (scm_ilength (scm_cdr (this_beaming)) > 0)
321 last_beaming = this_beaming;
330 operator <(Beam_stem_segment const &a,
331 Beam_stem_segment const &b)
333 return a.rank_ < b.rank_;
336 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
338 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
340 Beam::calc_beam_segments (SCM smob)
342 /* ugh, this has a side-effect that we need to ensure that
343 Stem #'beaming is correct */
344 Grob *me_grob = unsmob_grob (smob);
345 (void) me_grob->get_property ("beaming");
347 Spanner *me = dynamic_cast<Spanner *> (me_grob);
349 extract_grob_set (me, "stems", stems);
351 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
352 for (LEFT_and_RIGHT (d))
353 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
355 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
356 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
358 Position_stem_segments_map stem_segments;
359 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
361 /* There are two concepts of "rank" that are used in the following code.
362 The beam_rank is the vertical position of the beam (larger numbers are
363 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
364 is the horizontal position of the segment (this is incremented by two
365 for each stem; the beam segment on the right side of the stem has
366 a higher rank (by one) than its neighbour to the left). */
368 for (vsize i = 0; i < stems.size (); i++)
370 Grob *stem = stems[i];
371 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
372 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
373 SCM beaming = stem->get_property ("beaming");
375 for (LEFT_and_RIGHT (d))
377 // Find the maximum and minimum beam ranks.
378 // Given that RANKS is never reset to empty, the interval will always be
379 // smallest for the left beamlet of the first stem, and then it might grow.
380 // Do we really want this? (It only affects the tremolo gaps) --jneem
381 for (SCM s = index_get_cell (beaming, d);
382 scm_is_pair (s); s = scm_cdr (s))
384 if (!scm_is_integer (scm_car (s)))
387 int beam_rank = scm_to_int (scm_car (s));
388 ranks.add_point (beam_rank);
391 for (SCM s = index_get_cell (beaming, d);
392 scm_is_pair (s); s = scm_cdr (s))
394 if (!scm_is_integer (scm_car (s)))
397 int beam_rank = scm_to_int (scm_car (s));
398 Beam_stem_segment seg;
400 seg.stem_x_ = stem_x;
401 seg.rank_ = 2 * i + (d + 1) / 2;
402 seg.width_ = stem_width;
405 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
407 Direction stem_dir = get_grob_direction (stem);
410 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
411 stem_segments[beam_rank].push_back (seg);
416 Drul_array<Real> break_overshoot
417 = robust_scm2drul (me->get_property ("break-overshoot"),
418 Drul_array<Real> (-0.5, 0.0));
420 vector<Beam_segment> segments;
421 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
422 i != stem_segments.end (); i++)
424 vector<Beam_stem_segment> segs = (*i).second;
425 vector_sort (segs, less<Beam_stem_segment> ());
427 Beam_segment current;
429 // Iterate over all of the segments of the current beam rank,
430 // merging the adjacent Beam_stem_segments into one Beam_segment
432 int vertical_count = (*i).first;
433 for (vsize j = 0; j < segs.size (); j++)
435 // Keeping track of the different directions here is a little tricky.
436 // segs[j].dir_ is the direction of the beam segment relative to the stem
437 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
438 // its stem) whereas event_dir refers to the edge of the beam segment that
439 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
440 // are looking at that edge of the beam segment that is furthest from its
442 Beam_stem_segment const &seg = segs[j];
443 for (LEFT_and_RIGHT (event_dir))
445 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
446 // TODO: make names clearer? --jneem
447 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
448 // on_beam_bound: whether the current segment is on the boundary of just that part
449 // of the beam with the current beam_rank
450 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
451 : seg.stem_index_ == stems.size () - 1;
452 bool on_beam_bound = (event_dir == LEFT) ? j == 0
453 : j == segs.size () - 1;
454 bool inside_stem = (event_dir == LEFT)
455 ? seg.stem_index_ > 0
456 : seg.stem_index_ + 1 < stems.size ();
458 bool event = on_beam_bound
459 || abs (seg.rank_ - neighbor_seg.rank_) > 1
460 || (abs (vertical_count) >= seg.max_connect_
461 || abs (vertical_count) >= neighbor_seg.max_connect_);
464 // Then this edge of the current segment is irrelevant because it will
465 // be connected with the next segment in the event_dir direction.
466 // If we skip the left edge here, the right edge of
467 // the previous segment has already been skipped since
468 // the conditions are symmetric
471 current.vertical_count_ = vertical_count;
472 current.horizontal_[event_dir] = seg.stem_x_;
473 if (seg.dir_ == event_dir)
474 // then we are examining the edge of a beam segment that is furthest
478 && me->get_bound (event_dir)->break_status_dir ())
480 current.horizontal_[event_dir]
481 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
482 commonx, X_AXIS)[RIGHT]
483 + event_dir * break_overshoot[event_dir]);
487 Grob *stem = stems[seg.stem_index_];
488 Drul_array<Real> beamlet_length
489 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
490 Drul_array<Real> max_proportion
491 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
492 Real length = beamlet_length[seg.dir_];
496 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
497 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
499 length = min (length,
500 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
502 current.horizontal_[event_dir] += event_dir * length;
506 // we are examining the edge of a beam segment that is closest
507 // (ie. touching, unless there is a gap) its stem.
509 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
512 current.horizontal_[event_dir] -= event_dir * gap_length;
514 if (Stem::is_invisible (seg.stem_))
517 Need to do this in case of whole notes. We don't want the
518 heads to collide with the beams.
520 extract_grob_set (seg.stem_, "note-heads", heads);
522 for (vsize k = 0; k < heads.size (); k++)
523 current.horizontal_[event_dir]
524 = event_dir * min (event_dir * current.horizontal_[event_dir],
527 * heads[k]->extent (commonx,
528 X_AXIS)[-event_dir]);
533 if (event_dir == RIGHT)
535 segments.push_back (current);
536 current = Beam_segment ();
543 SCM segments_scm = SCM_EOL;
544 SCM *tail = &segments_scm;
546 for (vsize i = 0; i < segments.size (); i++)
548 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
549 scm_from_int (segments[i].vertical_count_)),
550 scm_cons (ly_symbol2scm ("horizontal"),
551 ly_interval2scm (segments[i].horizontal_))),
553 tail = SCM_CDRLOC (*tail);
559 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
561 Beam::calc_x_positions (SCM smob)
563 Spanner *me = unsmob_spanner (smob);
564 SCM segments = me->get_property ("beam-segments");
565 Interval x_positions;
566 x_positions.set_empty ();
567 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
568 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
571 Interval (0.0, 0.0)));
573 // Case for beams without segments (i.e. uniting two skips with a beam)
574 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
575 if (x_positions.is_empty ())
577 extract_grob_set (me, "stems", stems);
578 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
579 for (LEFT_and_RIGHT (d))
580 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
582 return ly_interval2scm (x_positions);
586 Beam::get_beam_segments (Grob *me)
588 SCM segments_scm = me->get_property ("beam-segments");
589 vector<Beam_segment> segments;
590 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
592 segments.push_back (Beam_segment ());
593 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
594 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
600 MAKE_SCHEME_CALLBACK (Beam, print, 1);
602 Beam::print (SCM grob)
604 Spanner *me = unsmob_spanner (grob);
606 TODO - mild code dup for all the commonx calls.
607 Some use just common_refpoint_of_array, some (in print and
608 calc_beam_segments) use this plus calls to get_bound.
610 Figure out if there is any particular reason for this and
611 consolidate in one Beam::get_common function.
613 extract_grob_set (me, "stems", stems);
614 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
615 for (LEFT_and_RIGHT (d))
616 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
618 vector<Beam_segment> segments = get_beam_segments (me);
620 if (!segments.size ())
623 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
625 SCM posns = me->get_property ("quantized-positions");
626 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
628 if (!is_number_pair (posns))
630 programming_error ("no beam positions?");
631 pos = Interval (0, 0);
634 pos = ly_scm2realdrul (posns);
636 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
638 Real dy = pos[RIGHT] - pos[LEFT];
639 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
641 Real beam_thickness = get_beam_thickness (me);
642 Real beam_dy = get_beam_translation (me);
644 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
646 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
649 int extreme = (segments[0].vertical_count_ == 0
650 ? segments[0].vertical_count_
651 : segments.back ().vertical_count_);
653 for (vsize i = 0; i < segments.size (); i++)
655 Real local_slope = slope;
657 Makes local slope proportional to the ratio of the length of this beam
661 local_slope += (feather_dir * segments[i].vertical_count_
663 * placements.length ()
666 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
668 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
669 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
671 Interval weights (1 - multiplier, multiplier);
673 if (feather_dir != LEFT)
676 // we need two translations: the normal one and
677 // the one of the lowest segment
678 size_t idx[] = {i, extreme};
679 Real translations[2];
681 for (int j = 0; j < 2; j++)
682 translations[j] = slope
683 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
684 + pos.linear_combination (CENTER)
685 + beam_dy * segments[idx[j]].vertical_count_;
687 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
690 Tricky. The manipulation of the variable `weighted_average' below ensures
691 that beams with a RIGHT grow direction will start from the position of the
692 lowest segment at 0, and this error will decrease and decrease over the
693 course of the beam. Something with a LEFT grow direction, on the other
694 hand, will always start in the correct place but progressively accrue
695 error at broken places. This code shifts beams up given where they are
696 in the total span length (controlled by the variable `multiplier'). To
697 better understand what it does, try commenting it out: you'll see that
698 all of the RIGHT growing beams immediately start too low and get better
699 over line breaks, whereas all of the LEFT growing beams start just right
700 and get worse over line breaks.
702 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
704 if (segments[0].vertical_count_ < 0 && feather_dir)
705 weighted_average += beam_dy * (segments.size () - 1) * factor;
707 b.translate_axis (weighted_average, Y_AXIS);
709 the_beam.add_stencil (b);
713 #if (DEBUG_BEAM_SCORING)
714 SCM annotation = me->get_property ("annotation");
715 if (scm_is_string (annotation))
717 extract_grob_set (me, "stems", stems);
720 This code prints the demerits for each beam. Perhaps this
721 should be switchable for those who want to twiddle with the
725 SCM properties = Font_interface::text_font_alist_chain (me);
727 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
730 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
732 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
733 (me->layout ()->self_scm (), properties, annotation));
735 if (!score.is_empty ())
737 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
738 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
743 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
744 return the_beam.smobbed_copy ();
748 Beam::get_default_dir (Grob *me)
750 extract_grob_set (me, "stems", stems);
752 Drul_array<Real> extremes (0.0, 0.0);
753 for (iterof (s, stems); s != stems.end (); s++)
755 Interval positions = Stem::head_positions (*s);
756 for (DOWN_and_UP (d))
758 if (sign (positions[d]) == d)
759 extremes[d] = d * max (d * positions[d], d * extremes[d]);
763 Drul_array<int> total (0, 0);
764 Drul_array<int> count (0, 0);
766 bool force_dir = false;
767 for (vsize i = 0; i < stems.size (); i++)
770 Direction stem_dir = CENTER;
771 SCM stem_dir_scm = s->get_property_data ("direction");
772 if (is_direction (stem_dir_scm))
774 stem_dir = to_dir (stem_dir_scm);
778 stem_dir = to_dir (s->get_property ("default-direction"));
781 stem_dir = to_dir (s->get_property ("neutral-direction"));
786 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
792 if (abs (extremes[UP]) > -extremes[DOWN])
794 else if (extremes[UP] < -extremes[DOWN])
798 Direction dir = CENTER;
799 Direction d = CENTER;
800 if ((d = (Direction) sign (count[UP] - count[DOWN])))
804 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
806 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
809 dir = to_dir (me->get_property ("neutral-direction"));
814 /* Set all stems with non-forced direction to beam direction.
815 Urg: non-forced should become `without/with unforced' direction,
816 once stem gets cleaned-up. */
818 Beam::set_stem_directions (Grob *me, Direction d)
820 extract_grob_set (me, "stems", stems);
822 for (vsize i = 0; i < stems.size (); i++)
826 SCM forcedir = s->get_property_data ("direction");
827 if (!to_dir (forcedir))
828 set_grob_direction (s, d);
833 Only try horizontal beams for knees. No reliable detection of
834 anything else is possible here, since we don't know funky-beaming
835 settings, or X-distances (slopes!) People that want sloped
836 knee-beams, should set the directions manually.
841 this routine should take into account the stemlength scoring
842 of a possible knee/nonknee beam.
845 Beam::consider_auto_knees (Grob *me)
847 SCM scm = me->get_property ("auto-knee-gap");
848 if (!scm_is_number (scm))
855 extract_grob_set (me, "normal-stems", stems);
857 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
858 Real staff_space = Staff_symbol_referencer::staff_space (me);
860 vector<Interval> head_extents_array;
861 for (vsize i = 0; i < stems.size (); i++)
863 Grob *stem = stems[i];
865 Interval head_extents = Stem::head_positions (stem);
866 if (!head_extents.is_empty ())
868 head_extents[LEFT] += -1;
869 head_extents[RIGHT] += 1;
870 head_extents *= staff_space * 0.5;
873 We could subtract beam Y position, but this routine only
874 sets stem directions, a constant shift does not have an
877 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
879 if (to_dir (stem->get_property_data ("direction")))
881 Direction stemdir = to_dir (stem->get_property ("direction"));
882 head_extents[-stemdir] = -stemdir * infinity_f;
885 head_extents_array.push_back (head_extents);
887 gaps.remove_interval (head_extents);
891 Real max_gap_len = 0.0;
893 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
895 Interval gap = gaps.allowed_regions_[i];
898 the outer gaps are not knees.
900 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
903 if (gap.length () >= max_gap_len)
905 max_gap_len = gap.length ();
910 Real beam_translation = get_beam_translation (me);
911 Real beam_thickness = Beam::get_beam_thickness (me);
912 int beam_count = Beam::get_beam_count (me);
913 Real height_of_beams = beam_thickness / 2
914 + (beam_count - 1) * beam_translation;
915 Real threshold = scm_to_double (scm) + height_of_beams;
917 if (max_gap_len > threshold)
920 for (vsize i = 0; i < stems.size (); i++)
922 Grob *stem = stems[i];
923 Interval head_extents = head_extents_array[j++];
925 Direction d = (head_extents.center () < max_gap.center ())
928 stem->set_property ("direction", scm_from_int (d));
930 head_extents.intersect (max_gap);
931 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
936 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
938 Beam::calc_stem_shorten (SCM smob)
940 Grob *me = unsmob_grob (smob);
943 shortening looks silly for x staff beams
946 return scm_from_int (0);
948 Real forced_fraction = 1.0 * forced_stem_count (me)
949 / normal_stem_count (me);
951 int beam_count = get_beam_count (me);
953 SCM shorten_list = me->get_property ("beamed-stem-shorten");
954 if (shorten_list == SCM_EOL)
955 return scm_from_int (0);
957 Real staff_space = Staff_symbol_referencer::staff_space (me);
960 = robust_list_ref (beam_count - 1, shorten_list);
961 Real shorten = scm_to_double (shorten_elt) * staff_space;
963 shorten *= forced_fraction;
966 return scm_from_double (shorten);
968 return scm_from_double (0.0);
971 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
973 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
975 Grob *me = unsmob_grob (smob);
976 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
977 bool cbs = to_boolean (align_broken_intos);
979 Beam_scoring_problem problem (me, ys, cbs);
980 ys = problem.solve ();
982 return ly_interval2scm (ys);
986 Report slice containing the numbers that are both in (car BEAMING)
990 where_are_the_whole_beams (SCM beaming)
994 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
996 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
998 l.add_point (scm_to_int (scm_car (s)));
1004 /* Return the Y position of the stem-end, given the Y-left, Y-right
1005 in POS for stem S. This Y position is relative to S. */
1007 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1008 Real xl, Real xr, Direction feather_dir,
1009 Drul_array<Real> pos, bool french)
1011 Real beam_translation = get_beam_translation (me);
1012 Direction stem_dir = get_grob_direction (stem);
1015 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1016 Real xdir = 2 * relx - 1;
1018 Real stem_y = linear_combination (pos, xdir);
1020 SCM beaming = stem->get_property ("beaming");
1022 Slice beam_slice (french
1023 ? where_are_the_whole_beams (beaming)
1024 : Stem::beam_multiplicity (stem));
1025 if (beam_slice.is_empty ())
1026 beam_slice = Slice (0, 0);
1027 Interval beam_multiplicity (beam_slice[LEFT],
1031 feather dir = 1 , relx 0->1 : factor 0 -> 1
1032 feather dir = 0 , relx 0->1 : factor 1 -> 1
1033 feather dir = -1, relx 0->1 : factor 1 -> 0
1035 Real feather_factor = 1;
1036 if (feather_dir > 0)
1037 feather_factor = relx;
1038 else if (feather_dir < 0)
1039 feather_factor = 1 - relx;
1041 stem_y += feather_factor * beam_translation
1042 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1043 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1044 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1050 Hmm. At this time, beam position and slope are determined. Maybe,
1051 stem directions and length should set to relative to the chord's
1052 position of the beam. */
1053 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1055 Beam::set_stem_lengths (SCM smob)
1057 Grob *me = unsmob_grob (smob);
1059 /* trigger callbacks. */
1060 (void) me->get_property ("direction");
1061 (void) me->get_property ("beaming");
1063 SCM posns = me->get_property ("positions");
1065 extract_grob_set (me, "stems", stems);
1070 for (int a = 2; a--;)
1071 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1073 Drul_array<Real> pos = ly_scm2realdrul (posns);
1074 Real staff_space = Staff_symbol_referencer::staff_space (me);
1075 scale_drul (&pos, staff_space);
1079 if (robust_scm2int (me->get_property ("gap-count"), 0))
1082 thick = get_beam_thickness (me);
1085 Grob *fvs = first_normal_stem (me);
1086 Grob *lvs = last_normal_stem (me);
1088 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1089 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1091 for (vsize i = 0; i < stems.size (); i++)
1095 bool french = to_boolean (s->get_property ("french-beaming"));
1096 Real stem_y = calc_stem_y (me, s, common,
1097 x_span[LEFT], x_span[RIGHT], feather_dir,
1098 pos, french && s != lvs && s != fvs);
1101 Make the stems go up to the end of the beam. This doesn't matter
1102 for normal beams, but for tremolo beams it looks silly otherwise.
1105 && !Stem::is_invisible (s))
1106 stem_y += thick * 0.5 * get_grob_direction (s);
1109 Do set_stem_positions for invisible stems too, so tuplet brackets
1110 have a reference point for sloping
1112 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1119 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1121 extract_grob_set (me, "stems", stems);
1123 for (vsize i = 0; i < stems.size (); i++)
1126 Don't overwrite user settings.
1128 for (LEFT_and_RIGHT (d))
1130 Grob *stem = stems[i];
1131 SCM beaming_prop = stem->get_property ("beaming");
1132 if (beaming_prop == SCM_EOL
1133 || index_get_cell (beaming_prop, d) == SCM_EOL)
1135 int count = beaming->beamlet_count (i, d);
1137 && i + 1 < stems.size ()
1138 && Stem::is_invisible (stem))
1139 count = min (count, beaming->beamlet_count (i, -d));
1141 if ( ((i == 0 && d == LEFT)
1142 || (i == stems.size () - 1 && d == RIGHT))
1143 && stems.size () > 1
1144 && to_boolean (me->get_property ("clip-edges")))
1147 Stem::set_beaming (stem, count, d);
1154 Beam::forced_stem_count (Grob *me)
1156 extract_grob_set (me, "normal-stems", stems);
1159 for (vsize i = 0; i < stems.size (); i++)
1163 /* I can imagine counting those boundaries as a half forced stem,
1164 but let's count them full for now. */
1165 Direction defdir = to_dir (s->get_property ("default-direction"));
1167 if (abs (Stem::chord_start_y (s)) > 0.1
1169 && get_grob_direction (s) != defdir)
1176 Beam::normal_stem_count (Grob *me)
1178 extract_grob_set (me, "normal-stems", stems);
1179 return stems.size ();
1183 Beam::first_normal_stem (Grob *me)
1185 extract_grob_set (me, "normal-stems", stems);
1186 return stems.size () ? stems[0] : 0;
1190 Beam::last_normal_stem (Grob *me)
1192 extract_grob_set (me, "normal-stems", stems);
1193 return stems.size () ? stems.back () : 0;
1199 handle rest under beam (do_post: beams are calculated now)
1200 what about combination of collisions and rest under beam.
1204 rest -> stem -> beam -> interpolate_y_position ()
1206 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1208 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1210 Grob *rest = unsmob_grob (smob);
1211 if (scm_is_number (rest->get_property ("staff-position")))
1212 return scm_from_int (0);
1214 Real offset = robust_scm2double (prev_offset, 0.0);
1216 Grob *st = unsmob_grob (rest->get_object ("stem"));
1219 return scm_from_double (0.0);
1220 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1222 || !Beam::has_interface (beam)
1223 || !Beam::normal_stem_count (beam))
1224 return scm_from_double (0.0);
1226 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1228 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1229 Drul_array<Real> (0, 0)));
1231 for (LEFT_and_RIGHT (dir))
1232 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1234 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1236 scale_drul (&pos, staff_space);
1238 Real dy = pos[RIGHT] - pos[LEFT];
1240 extract_grob_set (beam, "stems", stems);
1241 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1243 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1244 Interval (0.0, 0.0));
1245 Real x0 = x_span[LEFT];
1246 Real dx = x_span.length ();
1247 Real slope = dy && dx ? dy / dx : 0;
1249 Direction d = get_grob_direction (stem);
1250 Real stem_y = pos[LEFT]
1251 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1253 Real beam_translation = get_beam_translation (beam);
1254 Real beam_thickness = Beam::get_beam_thickness (beam);
1257 TODO: this is not strictly correct for 16th knee beams.
1260 = Stem::beam_multiplicity (stem).length () + 1;
1262 Real height_of_my_beams = beam_thickness / 2
1263 + (beam_count - 1) * beam_translation;
1264 Real beam_y = stem_y - d * height_of_my_beams;
1266 Interval rest_extent = rest->extent (rest, Y_AXIS);
1267 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1269 Real rest_dim = rest_extent[d];
1270 Real minimum_distance
1271 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1272 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1274 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1276 shift /= staff_space;
1278 /* Always move discretely by half spaces */
1279 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1281 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1282 staff_span *= staff_space / 2;
1284 /* Inside staff, move by whole spaces*/
1285 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1286 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1287 shift = ceil (fabs (shift)) * sign (shift);
1289 return scm_from_double (offset + staff_space * shift);
1293 Estimate the position of a rest under a beam,
1294 as the average position of its neighboring heads.
1296 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1298 Beam::pure_rest_collision_callback (SCM smob,
1303 Real previous = robust_scm2double (prev_offset, 0.0);
1305 Grob *me = unsmob_grob (smob);
1306 Grob *stem = unsmob_grob (me->get_object ("stem"));
1308 return scm_from_double (previous);
1309 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1311 || !Beam::normal_stem_count (beam)
1312 || !is_direction (beam->get_property_data ("direction")))
1313 return scm_from_double (previous);
1315 Real ss = Staff_symbol_referencer::staff_space (me);
1318 This gives the extrema of rest positions.
1319 Even with noteheads on ledgers, beams typically remain within the staff,
1320 and push rests at most one staff-space (2 positions) from the staff.
1322 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1323 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1324 rest_max_pos.widen (2);
1326 extract_grob_set (beam, "stems", stems);
1327 vector<Grob *> my_stems;
1329 for (vsize i = 0; i < stems.size (); i++)
1330 if (Stem::head_count (stems[i]) || stems[i] == stem)
1331 my_stems.push_back (stems[i]);
1335 for (vsize i = 0; i < my_stems.size (); i++)
1336 if (my_stems[i] == stem)
1344 if (idx == (vsize) - 1 || my_stems.size () == 1)
1345 return scm_from_double (previous);
1347 left = right = my_stems[1];
1348 else if (idx == my_stems.size () - 1)
1349 left = right = my_stems[idx - 1];
1352 left = my_stems[idx - 1];
1353 right = my_stems[idx + 1];
1356 /* In stems with several heads, use the one closest to the beam. */
1357 Direction beamdir = get_grob_direction (beam);
1358 Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1359 + Stem::head_positions (right)[beamdir]) / 2.0,
1360 rest_max_pos[DOWN]),
1364 /* Always move by a whole number of staff spaces */
1365 shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1367 return scm_from_double (previous + shift);
1371 Beam::is_knee (Grob *me)
1373 SCM k = me->get_property ("knee");
1374 if (scm_is_bool (k))
1375 return ly_scm2bool (k);
1379 extract_grob_set (me, "stems", stems);
1380 for (vsize i = stems.size (); i--;)
1382 Direction dir = get_grob_direction (stems[i]);
1391 me->set_property ("knee", ly_bool2scm (knee));
1397 Beam::is_cross_staff (Grob *me)
1399 extract_grob_set (me, "stems", stems);
1400 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1401 for (vsize i = 0; i < stems.size (); i++)
1402 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1407 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1409 Beam::calc_cross_staff (SCM smob)
1411 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1415 Beam::get_direction_beam_count (Grob *me, Direction d)
1417 extract_grob_set (me, "stems", stems);
1420 for (vsize i = stems.size (); i--;)
1423 Should we take invisible stems into account?
1425 if (get_grob_direction (stems[i]) == d)
1426 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1432 ADD_INTERFACE (Beam,
1435 "The @code{beam-thickness} property is the weight of beams,"
1436 " measured in staffspace. The @code{direction} property is"
1437 " not user-serviceable. Use the @code{direction} property"
1438 " of @code{Stem} instead.\n"
1439 "The following properties may be set in the @code{details}"
1443 "@item stem-length-demerit-factor\n"
1444 "Demerit factor used for inappropriate stem lengths.\n"
1445 "@item secondary-beam-demerit\n"
1446 "Demerit used in quanting calculations for multiple"
1448 "@item region-size\n"
1449 "Size of region for checking quant scores.\n"
1451 "Epsilon for beam quant code to check for presence"
1453 "@item stem-length-limit-penalty\n"
1454 "Penalty for differences in stem lengths on a beam.\n"
1455 "@item damping-direction-penalty\n"
1456 "Demerit penalty applied when beam direction is different"
1457 " from damping direction.\n"
1458 "@item hint-direction-penalty\n"
1459 "Demerit penalty applied when beam direction is different"
1460 " from damping direction, but damping slope is"
1461 " <= @code{round-to-zero-slope}.\n"
1462 "@item musical-direction-factor\n"
1463 "Demerit scaling factor for difference between"
1464 " beam slope and music slope.\n"
1465 "@item ideal-slope-factor\n"
1466 "Demerit scaling factor for difference between"
1467 " beam slope and damping slope.\n"
1468 "@item round-to-zero-slope\n"
1469 "Damping slope which is considered zero for purposes of"
1470 " calculating direction penalties.\n"
1476 "beamed-stem-shorten "
1483 "collision-interfaces "
1484 "collision-voice-only "
1496 "neutral-direction "
1499 "quantized-positions "