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"));
201 extract_grob_set (stem, "note-heads", heads);
202 /* default position of Kievan heads with beams is down
203 placing this here avoids warnings downstream */
206 if (heads[0]->get_property ("style") == ly_symbol2scm ("kievan"))
218 dir = get_default_dir (me);
220 consider_auto_knees (me);
225 set_stem_directions (me, dir);
228 return scm_from_int (dir);
231 /* We want a maximal number of shared beams, but if there is choice, we
232 * take the one that is closest to the end of the stem. This is for
244 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
248 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
252 for (int i = lslice[-left_dir];
253 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
256 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
258 int k = -right_dir * scm_to_int (scm_car (s)) + i;
259 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
263 if (count >= best_count)
273 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
275 Beam::calc_beaming (SCM smob)
277 Grob *me = unsmob_grob (smob);
279 extract_grob_set (me, "stems", stems);
282 last_int.set_empty ();
284 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
285 Direction last_dir = CENTER;
286 for (vsize i = 0; i < stems.size (); i++)
288 Grob *this_stem = stems[i];
289 SCM this_beaming = this_stem->get_property ("beaming");
291 Direction this_dir = get_grob_direction (this_stem);
292 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
294 int start_point = position_with_maximal_common_beams
295 (last_beaming, this_beaming,
296 last_dir ? last_dir : this_dir,
300 for (LEFT_and_RIGHT (d))
302 new_slice.set_empty ();
303 SCM s = index_get_cell (this_beaming, d);
304 for (; scm_is_pair (s); s = scm_cdr (s))
307 = start_point - this_dir * scm_to_int (scm_car (s));
309 new_slice.add_point (new_beam_pos);
310 scm_set_car_x (s, scm_from_int (new_beam_pos));
314 if (!new_slice.is_empty ())
315 last_int = new_slice;
320 FIXME: what's this for?
322 SCM s = scm_cdr (this_beaming);
323 for (; scm_is_pair (s); s = scm_cdr (s))
325 int np = -this_dir * scm_to_int (scm_car (s));
326 scm_set_car_x (s, scm_from_int (np));
327 last_int.add_point (np);
331 if (scm_ilength (scm_cdr (this_beaming)) > 0)
333 last_beaming = this_beaming;
342 operator <(Beam_stem_segment const &a,
343 Beam_stem_segment const &b)
345 return a.rank_ < b.rank_;
348 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
350 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
352 Beam::calc_beam_segments (SCM smob)
354 /* ugh, this has a side-effect that we need to ensure that
355 Stem #'beaming is correct */
356 Grob *me_grob = unsmob_grob (smob);
357 (void) me_grob->get_property ("beaming");
359 Spanner *me = dynamic_cast<Spanner *> (me_grob);
361 extract_grob_set (me, "stems", stems);
363 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
364 for (LEFT_and_RIGHT (d))
365 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
367 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
368 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
370 Position_stem_segments_map stem_segments;
371 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
373 /* There are two concepts of "rank" that are used in the following code.
374 The beam_rank is the vertical position of the beam (larger numbers are
375 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
376 is the horizontal position of the segment (this is incremented by two
377 for each stem; the beam segment on the right side of the stem has
378 a higher rank (by one) than its neighbour to the left). */
380 for (vsize i = 0; i < stems.size (); i++)
382 Grob *stem = stems[i];
383 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
384 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
385 SCM beaming = stem->get_property ("beaming");
387 for (LEFT_and_RIGHT (d))
389 // Find the maximum and minimum beam ranks.
390 // Given that RANKS is never reset to empty, the interval will always be
391 // smallest for the left beamlet of the first stem, and then it might grow.
392 // Do we really want this? (It only affects the tremolo gaps) --jneem
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 ranks.add_point (beam_rank);
403 for (SCM s = index_get_cell (beaming, d);
404 scm_is_pair (s); s = scm_cdr (s))
406 if (!scm_is_integer (scm_car (s)))
409 int beam_rank = scm_to_int (scm_car (s));
410 Beam_stem_segment seg;
412 seg.stem_x_ = stem_x;
413 seg.rank_ = 2 * i + (d + 1) / 2;
414 seg.width_ = stem_width;
417 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
419 Direction stem_dir = get_grob_direction (stem);
422 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
423 stem_segments[beam_rank].push_back (seg);
428 Drul_array<Real> break_overshoot
429 = robust_scm2drul (me->get_property ("break-overshoot"),
430 Drul_array<Real> (-0.5, 0.0));
432 vector<Beam_segment> segments;
433 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
434 i != stem_segments.end (); i++)
436 vector<Beam_stem_segment> segs = (*i).second;
437 vector_sort (segs, less<Beam_stem_segment> ());
439 Beam_segment current;
441 // Iterate over all of the segments of the current beam rank,
442 // merging the adjacent Beam_stem_segments into one Beam_segment
444 int vertical_count = (*i).first;
445 for (vsize j = 0; j < segs.size (); j++)
447 // Keeping track of the different directions here is a little tricky.
448 // segs[j].dir_ is the direction of the beam segment relative to the stem
449 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
450 // its stem) whereas event_dir refers to the edge of the beam segment that
451 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
452 // are looking at that edge of the beam segment that is furthest from its
454 Beam_stem_segment const &seg = segs[j];
455 for (LEFT_and_RIGHT (event_dir))
457 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
458 // TODO: make names clearer? --jneem
459 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
460 // on_beam_bound: whether the current segment is on the boundary of just that part
461 // of the beam with the current beam_rank
462 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
463 : seg.stem_index_ == stems.size () - 1;
464 bool on_beam_bound = (event_dir == LEFT) ? j == 0
465 : j == segs.size () - 1;
466 bool inside_stem = (event_dir == LEFT)
467 ? seg.stem_index_ > 0
468 : seg.stem_index_ + 1 < stems.size ();
470 bool event = on_beam_bound
471 || abs (seg.rank_ - neighbor_seg.rank_) > 1
472 || (abs (vertical_count) >= seg.max_connect_
473 || abs (vertical_count) >= neighbor_seg.max_connect_);
476 // Then this edge of the current segment is irrelevant because it will
477 // be connected with the next segment in the event_dir direction.
478 // If we skip the left edge here, the right edge of
479 // the previous segment has already been skipped since
480 // the conditions are symmetric
483 current.vertical_count_ = vertical_count;
484 current.horizontal_[event_dir] = seg.stem_x_;
485 if (seg.dir_ == event_dir)
486 // then we are examining the edge of a beam segment that is furthest
490 && me->get_bound (event_dir)->break_status_dir ())
492 current.horizontal_[event_dir]
493 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
494 commonx, X_AXIS)[RIGHT]
495 + event_dir * break_overshoot[event_dir]);
499 Grob *stem = stems[seg.stem_index_];
500 Drul_array<Real> beamlet_length
501 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
502 Drul_array<Real> max_proportion
503 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
504 Real length = beamlet_length[seg.dir_];
508 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
509 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
511 length = min (length,
512 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
514 current.horizontal_[event_dir] += event_dir * length;
518 // we are examining the edge of a beam segment that is closest
519 // (ie. touching, unless there is a gap) its stem.
521 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
524 current.horizontal_[event_dir] -= event_dir * gap_length;
526 if (Stem::is_invisible (seg.stem_))
529 Need to do this in case of whole notes. We don't want the
530 heads to collide with the beams.
532 extract_grob_set (seg.stem_, "note-heads", heads);
534 for (vsize k = 0; k < heads.size (); k++)
535 current.horizontal_[event_dir]
536 = event_dir * min (event_dir * current.horizontal_[event_dir],
539 * heads[k]->extent (commonx,
540 X_AXIS)[-event_dir]);
545 if (event_dir == RIGHT)
547 segments.push_back (current);
548 current = Beam_segment ();
555 SCM segments_scm = SCM_EOL;
557 for (vsize i = segments.size (); i--;)
559 segments_scm = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
560 scm_from_int (segments[i].vertical_count_)),
561 scm_cons (ly_symbol2scm ("horizontal"),
562 ly_interval2scm (segments[i].horizontal_))),
569 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
571 Beam::calc_x_positions (SCM smob)
573 Spanner *me = unsmob_spanner (smob);
574 SCM segments = me->get_property ("beam-segments");
575 Interval x_positions;
576 x_positions.set_empty ();
577 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
578 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
581 Interval (0.0, 0.0)));
583 // Case for beams without segments (i.e. uniting two skips with a beam)
584 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
585 if (x_positions.is_empty ())
587 extract_grob_set (me, "stems", stems);
588 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
589 for (LEFT_and_RIGHT (d))
590 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
592 return ly_interval2scm (x_positions);
596 Beam::get_beam_segments (Grob *me)
598 SCM segments_scm = me->get_property ("beam-segments");
599 vector<Beam_segment> segments;
600 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
602 segments.push_back (Beam_segment ());
603 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
604 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
610 MAKE_SCHEME_CALLBACK (Beam, print, 1);
612 Beam::print (SCM grob)
614 Spanner *me = unsmob_spanner (grob);
616 TODO - mild code dup for all the commonx calls.
617 Some use just common_refpoint_of_array, some (in print and
618 calc_beam_segments) use this plus calls to get_bound.
620 Figure out if there is any particular reason for this and
621 consolidate in one Beam::get_common function.
623 extract_grob_set (me, "stems", stems);
624 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
625 for (LEFT_and_RIGHT (d))
626 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
628 vector<Beam_segment> segments = get_beam_segments (me);
630 if (!segments.size ())
633 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
635 SCM posns = me->get_property ("quantized-positions");
636 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
638 if (!is_number_pair (posns))
640 programming_error ("no beam positions?");
641 pos = Interval (0, 0);
644 pos = ly_scm2realdrul (posns);
646 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
648 Real dy = pos[RIGHT] - pos[LEFT];
649 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
651 Real beam_thickness = get_beam_thickness (me);
652 Real beam_dy = get_beam_translation (me);
654 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
656 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
659 vsize extreme = (segments[0].vertical_count_ == 0
660 ? segments[0].vertical_count_
661 : segments.back ().vertical_count_);
663 for (vsize i = 0; i < segments.size (); i++)
665 Real local_slope = slope;
667 Makes local slope proportional to the ratio of the length of this beam
671 local_slope += (feather_dir * segments[i].vertical_count_
673 * placements.length ()
676 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
678 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
679 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
681 Interval weights (1 - multiplier, multiplier);
683 if (feather_dir != LEFT)
686 // we need two translations: the normal one and
687 // the one of the lowest segment
688 size_t idx[] = {i, extreme};
689 Real translations[2];
691 for (int j = 0; j < 2; j++)
692 translations[j] = slope
693 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
694 + pos.linear_combination (CENTER)
695 + beam_dy * segments[idx[j]].vertical_count_;
697 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
700 Tricky. The manipulation of the variable `weighted_average' below ensures
701 that beams with a RIGHT grow direction will start from the position of the
702 lowest segment at 0, and this error will decrease and decrease over the
703 course of the beam. Something with a LEFT grow direction, on the other
704 hand, will always start in the correct place but progressively accrue
705 error at broken places. This code shifts beams up given where they are
706 in the total span length (controlled by the variable `multiplier'). To
707 better understand what it does, try commenting it out: you'll see that
708 all of the RIGHT growing beams immediately start too low and get better
709 over line breaks, whereas all of the LEFT growing beams start just right
710 and get worse over line breaks.
712 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
714 if (segments[0].vertical_count_ < 0 && feather_dir)
715 weighted_average += beam_dy * (segments.size () - 1) * factor;
717 b.translate_axis (weighted_average, Y_AXIS);
719 the_beam.add_stencil (b);
723 #if (DEBUG_BEAM_SCORING)
724 SCM annotation = me->get_property ("annotation");
725 if (scm_is_string (annotation))
727 extract_grob_set (me, "stems", stems);
730 This code prints the demerits for each beam. Perhaps this
731 should be switchable for those who want to twiddle with the
735 SCM properties = Font_interface::text_font_alist_chain (me);
737 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
740 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
742 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
743 (me->layout ()->self_scm (), properties, annotation));
745 if (!score.is_empty ())
747 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
748 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
753 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
754 return the_beam.smobbed_copy ();
758 Beam::get_default_dir (Grob *me)
760 extract_grob_set (me, "stems", stems);
762 Drul_array<Real> extremes (0.0, 0.0);
763 for (iterof (s, stems); s != stems.end (); s++)
765 Interval positions = Stem::head_positions (*s);
766 for (DOWN_and_UP (d))
768 if (sign (positions[d]) == d)
769 extremes[d] = d * max (d * positions[d], d * extremes[d]);
773 Drul_array<int> total (0, 0);
774 Drul_array<int> count (0, 0);
776 bool force_dir = false;
777 for (vsize i = 0; i < stems.size (); i++)
780 Direction stem_dir = CENTER;
781 SCM stem_dir_scm = s->get_property_data ("direction");
782 if (is_direction (stem_dir_scm))
784 stem_dir = to_dir (stem_dir_scm);
788 stem_dir = to_dir (s->get_property ("default-direction"));
791 stem_dir = to_dir (s->get_property ("neutral-direction"));
796 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
802 if (abs (extremes[UP]) > -extremes[DOWN])
804 else if (extremes[UP] < -extremes[DOWN])
808 Direction dir = CENTER;
809 Direction d = CENTER;
810 if ((d = (Direction) sign (count[UP] - count[DOWN])))
814 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
816 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
819 dir = to_dir (me->get_property ("neutral-direction"));
824 /* Set all stems with non-forced direction to beam direction.
825 Urg: non-forced should become `without/with unforced' direction,
826 once stem gets cleaned-up. */
828 Beam::set_stem_directions (Grob *me, Direction d)
830 extract_grob_set (me, "stems", stems);
832 for (vsize i = 0; i < stems.size (); i++)
836 SCM forcedir = s->get_property_data ("direction");
837 if (!to_dir (forcedir))
838 set_grob_direction (s, d);
843 Only try horizontal beams for knees. No reliable detection of
844 anything else is possible here, since we don't know funky-beaming
845 settings, or X-distances (slopes!) People that want sloped
846 knee-beams, should set the directions manually.
851 this routine should take into account the stemlength scoring
852 of a possible knee/nonknee beam.
855 Beam::consider_auto_knees (Grob *me)
857 SCM scm = me->get_property ("auto-knee-gap");
858 if (!scm_is_number (scm))
861 vector<Interval> forbidden_intervals;
863 extract_grob_set (me, "normal-stems", stems);
865 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
866 Real staff_space = Staff_symbol_referencer::staff_space (me);
868 vector<Interval> head_extents_array;
869 for (vsize i = 0; i < stems.size (); i++)
871 Grob *stem = stems[i];
873 Interval head_extents = Stem::head_positions (stem);
874 if (!head_extents.is_empty ())
876 head_extents[LEFT] += -1;
877 head_extents[RIGHT] += 1;
878 head_extents *= staff_space * 0.5;
881 We could subtract beam Y position, but this routine only
882 sets stem directions, a constant shift does not have an
885 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
887 if (to_dir (stem->get_property_data ("direction")))
889 Direction stemdir = to_dir (stem->get_property ("direction"));
890 head_extents[-stemdir] = -stemdir * infinity_f;
893 head_extents_array.push_back (head_extents);
895 forbidden_intervals.push_back (head_extents);
899 Real max_gap_len = 0.0;
901 vector<Interval> allowed_regions
902 = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
903 for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
905 Interval gap = allowed_regions[i];
908 the outer gaps are not knees.
910 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
913 if (gap.length () >= max_gap_len)
915 max_gap_len = gap.length ();
920 Real beam_translation = get_beam_translation (me);
921 Real beam_thickness = Beam::get_beam_thickness (me);
922 int beam_count = Beam::get_beam_count (me);
923 Real height_of_beams = beam_thickness / 2
924 + (beam_count - 1) * beam_translation;
925 Real threshold = scm_to_double (scm) + height_of_beams;
927 if (max_gap_len > threshold)
930 for (vsize i = 0; i < stems.size (); i++)
932 Grob *stem = stems[i];
933 Interval head_extents = head_extents_array[j++];
935 Direction d = (head_extents.center () < max_gap.center ())
938 stem->set_property ("direction", scm_from_int (d));
940 head_extents.intersect (max_gap);
941 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
946 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
948 Beam::calc_stem_shorten (SCM smob)
950 Grob *me = unsmob_grob (smob);
953 shortening looks silly for x staff beams
956 return scm_from_int (0);
958 Real forced_fraction = 1.0 * forced_stem_count (me)
959 / normal_stem_count (me);
961 int beam_count = get_beam_count (me);
963 SCM shorten_list = me->get_property ("beamed-stem-shorten");
964 if (shorten_list == SCM_EOL)
965 return scm_from_int (0);
967 Real staff_space = Staff_symbol_referencer::staff_space (me);
970 = robust_list_ref (beam_count - 1, shorten_list);
971 Real shorten = scm_to_double (shorten_elt) * staff_space;
973 shorten *= forced_fraction;
976 return scm_from_double (shorten);
978 return scm_from_double (0.0);
981 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
983 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
985 Grob *me = unsmob_grob (smob);
986 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
987 bool cbs = to_boolean (align_broken_intos);
989 Beam_scoring_problem problem (me, ys, cbs);
990 ys = problem.solve ();
992 return ly_interval2scm (ys);
996 Report slice containing the numbers that are both in (car BEAMING)
1000 where_are_the_whole_beams (SCM beaming)
1004 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1006 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1008 l.add_point (scm_to_int (scm_car (s)));
1014 /* Return the Y position of the stem-end, given the Y-left, Y-right
1015 in POS for stem S. This Y position is relative to S. */
1017 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1018 Real xl, Real xr, Direction feather_dir,
1019 Drul_array<Real> pos, bool french)
1021 Real beam_translation = get_beam_translation (me);
1022 Direction stem_dir = get_grob_direction (stem);
1025 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1026 Real xdir = 2 * relx - 1;
1028 Real stem_y = linear_combination (pos, xdir);
1030 SCM beaming = stem->get_property ("beaming");
1032 Slice beam_slice (french
1033 ? where_are_the_whole_beams (beaming)
1034 : Stem::beam_multiplicity (stem));
1035 if (beam_slice.is_empty ())
1036 beam_slice = Slice (0, 0);
1037 Interval beam_multiplicity (beam_slice[LEFT],
1041 feather dir = 1 , relx 0->1 : factor 0 -> 1
1042 feather dir = 0 , relx 0->1 : factor 1 -> 1
1043 feather dir = -1, relx 0->1 : factor 1 -> 0
1045 Real feather_factor = 1;
1046 if (feather_dir > 0)
1047 feather_factor = relx;
1048 else if (feather_dir < 0)
1049 feather_factor = 1 - relx;
1051 stem_y += feather_factor * beam_translation
1052 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1053 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1054 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1060 Hmm. At this time, beam position and slope are determined. Maybe,
1061 stem directions and length should set to relative to the chord's
1062 position of the beam. */
1063 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1065 Beam::set_stem_lengths (SCM smob)
1067 Grob *me = unsmob_grob (smob);
1069 /* trigger callbacks. */
1070 (void) me->get_property ("direction");
1071 (void) me->get_property ("beaming");
1073 SCM posns = me->get_property ("positions");
1075 extract_grob_set (me, "stems", stems);
1080 for (int a = 2; a--;)
1081 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1083 Drul_array<Real> pos = ly_scm2realdrul (posns);
1084 Real staff_space = Staff_symbol_referencer::staff_space (me);
1085 scale_drul (&pos, staff_space);
1089 if (robust_scm2int (me->get_property ("gap-count"), 0))
1092 thick = get_beam_thickness (me);
1095 Grob *fvs = first_normal_stem (me);
1096 Grob *lvs = last_normal_stem (me);
1098 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1099 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1101 for (vsize i = 0; i < stems.size (); i++)
1105 bool french = to_boolean (s->get_property ("french-beaming"));
1106 Real stem_y = calc_stem_y (me, s, common,
1107 x_span[LEFT], x_span[RIGHT], feather_dir,
1108 pos, french && s != lvs && s != fvs);
1111 Make the stems go up to the end of the beam. This doesn't matter
1112 for normal beams, but for tremolo beams it looks silly otherwise.
1115 && !Stem::is_invisible (s))
1116 stem_y += thick * 0.5 * get_grob_direction (s);
1119 Do set_stem_positions for invisible stems too, so tuplet brackets
1120 have a reference point for sloping
1122 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1129 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1131 extract_grob_set (me, "stems", stems);
1133 for (vsize i = 0; i < stems.size (); i++)
1136 Don't overwrite user settings.
1138 for (LEFT_and_RIGHT (d))
1140 Grob *stem = stems[i];
1141 SCM beaming_prop = stem->get_property ("beaming");
1142 if (beaming_prop == SCM_EOL
1143 || index_get_cell (beaming_prop, d) == SCM_EOL)
1145 int count = beaming->beamlet_count (i, d);
1147 && i + 1 < stems.size ()
1148 && Stem::is_invisible (stem))
1149 count = min (count, beaming->beamlet_count (i, -d));
1151 if ( ((i == 0 && d == LEFT)
1152 || (i == stems.size () - 1 && d == RIGHT))
1153 && stems.size () > 1
1154 && to_boolean (me->get_property ("clip-edges")))
1157 Stem::set_beaming (stem, count, d);
1164 Beam::forced_stem_count (Grob *me)
1166 extract_grob_set (me, "normal-stems", stems);
1169 for (vsize i = 0; i < stems.size (); i++)
1173 /* I can imagine counting those boundaries as a half forced stem,
1174 but let's count them full for now. */
1175 Direction defdir = to_dir (s->get_property ("default-direction"));
1177 if (abs (Stem::chord_start_y (s)) > 0.1
1179 && get_grob_direction (s) != defdir)
1186 Beam::normal_stem_count (Grob *me)
1188 extract_grob_set (me, "normal-stems", stems);
1189 return stems.size ();
1193 Beam::first_normal_stem (Grob *me)
1195 extract_grob_set (me, "normal-stems", stems);
1196 return stems.size () ? stems[0] : 0;
1200 Beam::last_normal_stem (Grob *me)
1202 extract_grob_set (me, "normal-stems", stems);
1203 return stems.size () ? stems.back () : 0;
1209 handle rest under beam (do_post: beams are calculated now)
1210 what about combination of collisions and rest under beam.
1214 rest -> stem -> beam -> interpolate_y_position ()
1216 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1218 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1220 Grob *rest = unsmob_grob (smob);
1221 if (scm_is_number (rest->get_property ("staff-position")))
1222 return scm_from_int (0);
1224 Real offset = robust_scm2double (prev_offset, 0.0);
1226 Grob *st = unsmob_grob (rest->get_object ("stem"));
1229 return scm_from_double (0.0);
1230 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1232 || !Beam::has_interface (beam)
1233 || !Beam::normal_stem_count (beam))
1234 return scm_from_double (0.0);
1236 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1238 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1239 Drul_array<Real> (0, 0)));
1241 for (LEFT_and_RIGHT (dir))
1242 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1244 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1246 scale_drul (&pos, staff_space);
1248 Real dy = pos[RIGHT] - pos[LEFT];
1250 extract_grob_set (beam, "stems", stems);
1251 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1253 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1254 Interval (0.0, 0.0));
1255 Real x0 = x_span[LEFT];
1256 Real dx = x_span.length ();
1257 Real slope = dy && dx ? dy / dx : 0;
1259 Direction d = get_grob_direction (stem);
1260 Real stem_y = pos[LEFT]
1261 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1263 Real beam_translation = get_beam_translation (beam);
1264 Real beam_thickness = Beam::get_beam_thickness (beam);
1267 TODO: this is not strictly correct for 16th knee beams.
1270 = Stem::beam_multiplicity (stem).length () + 1;
1272 Real height_of_my_beams = beam_thickness / 2
1273 + (beam_count - 1) * beam_translation;
1274 Real beam_y = stem_y - d * height_of_my_beams;
1276 Interval rest_extent = rest->extent (rest, Y_AXIS);
1277 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1279 Real rest_dim = rest_extent[d];
1280 Real minimum_distance
1281 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1282 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1284 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1286 shift /= staff_space;
1288 /* Always move discretely by half spaces */
1289 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1291 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1292 staff_span *= staff_space / 2;
1294 /* Inside staff, move by whole spaces*/
1295 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1296 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1297 shift = ceil (fabs (shift)) * sign (shift);
1299 return scm_from_double (offset + staff_space * shift);
1303 Estimate the position of a rest under a beam,
1304 as the average position of its neighboring heads.
1306 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1308 Beam::pure_rest_collision_callback (SCM smob,
1313 Real previous = robust_scm2double (prev_offset, 0.0);
1315 Grob *me = unsmob_grob (smob);
1316 Grob *stem = unsmob_grob (me->get_object ("stem"));
1318 return scm_from_double (previous);
1319 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1321 || !Beam::normal_stem_count (beam)
1322 || !is_direction (beam->get_property_data ("direction")))
1323 return scm_from_double (previous);
1325 Real ss = Staff_symbol_referencer::staff_space (me);
1328 This gives the extrema of rest positions.
1329 Even with noteheads on ledgers, beams typically remain within the staff,
1330 and push rests at most one staff-space (2 positions) from the staff.
1332 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1333 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1334 rest_max_pos.widen (2);
1336 extract_grob_set (beam, "stems", stems);
1337 vector<Grob *> my_stems;
1339 for (vsize i = 0; i < stems.size (); i++)
1340 if (Stem::head_count (stems[i]) || stems[i] == stem)
1341 my_stems.push_back (stems[i]);
1345 for (vsize i = 0; i < my_stems.size (); i++)
1346 if (my_stems[i] == stem)
1354 if (idx == (vsize) - 1 || my_stems.size () == 1)
1355 return scm_from_double (previous);
1357 left = right = my_stems[1];
1358 else if (idx == my_stems.size () - 1)
1359 left = right = my_stems[idx - 1];
1362 left = my_stems[idx - 1];
1363 right = my_stems[idx + 1];
1366 /* In stems with several heads, use the one closest to the beam. */
1367 Direction beamdir = get_grob_direction (beam);
1368 Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1369 + Stem::head_positions (right)[beamdir]) / 2.0,
1370 rest_max_pos[DOWN]),
1375 // So that ceil below kicks in for rests that would otherwise brush
1376 // up against a beam quanted to a ledger line, add a bit of space
1377 // between the beam and the rest.
1378 shift += (0.01 * beamdir);
1380 /* Always move by a whole number of staff spaces */
1381 shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1383 return scm_from_double (previous + shift);
1387 Beam::is_knee (Grob *me)
1389 SCM k = me->get_property ("knee");
1390 if (scm_is_bool (k))
1391 return ly_scm2bool (k);
1395 extract_grob_set (me, "stems", stems);
1396 for (vsize i = stems.size (); i--;)
1398 Direction dir = get_grob_direction (stems[i]);
1407 me->set_property ("knee", ly_bool2scm (knee));
1413 Beam::is_cross_staff (Grob *me)
1415 extract_grob_set (me, "stems", stems);
1416 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1417 for (vsize i = 0; i < stems.size (); i++)
1418 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1423 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1425 Beam::calc_cross_staff (SCM smob)
1427 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1431 Beam::get_direction_beam_count (Grob *me, Direction d)
1433 extract_grob_set (me, "stems", stems);
1436 for (vsize i = stems.size (); i--;)
1439 Should we take invisible stems into account?
1441 if (get_grob_direction (stems[i]) == d)
1442 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1448 ADD_INTERFACE (Beam,
1451 "The @code{beam-thickness} property is the weight of beams,"
1452 " measured in staffspace. The @code{direction} property is"
1453 " not user-serviceable. Use the @code{direction} property"
1454 " of @code{Stem} instead.\n"
1455 "The following properties may be set in the @code{details}"
1459 "@item stem-length-demerit-factor\n"
1460 "Demerit factor used for inappropriate stem lengths.\n"
1461 "@item secondary-beam-demerit\n"
1462 "Demerit used in quanting calculations for multiple"
1464 "@item region-size\n"
1465 "Size of region for checking quant scores.\n"
1467 "Epsilon for beam quant code to check for presence"
1469 "@item stem-length-limit-penalty\n"
1470 "Penalty for differences in stem lengths on a beam.\n"
1471 "@item damping-direction-penalty\n"
1472 "Demerit penalty applied when beam direction is different"
1473 " from damping direction.\n"
1474 "@item hint-direction-penalty\n"
1475 "Demerit penalty applied when beam direction is different"
1476 " from damping direction, but damping slope is"
1477 " <= @code{round-to-zero-slope}.\n"
1478 "@item musical-direction-factor\n"
1479 "Demerit scaling factor for difference between"
1480 " beam slope and music slope.\n"
1481 "@item ideal-slope-factor\n"
1482 "Demerit scaling factor for difference between"
1483 " beam slope and damping slope.\n"
1484 "@item round-to-zero-slope\n"
1485 "Damping slope which is considered zero for purposes of"
1486 " calculating direction penalties.\n"
1492 "beamed-stem-shorten "
1499 "collision-interfaces "
1500 "collision-voice-only "
1512 "neutral-direction "
1515 "quantized-positions "