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-column.hh"
53 #include "note-head.hh"
54 #include "output-def.hh"
55 #include "pointer-group-interface.hh"
56 #include "rhythmic-head.hh"
58 #include "staff-symbol.hh"
59 #include "staff-symbol-referencer.hh"
63 #if DEBUG_BEAM_SCORING
64 #include "text-interface.hh" // debug output.
65 #include "font-interface.hh" // debug output.
70 Beam_stem_segment::Beam_stem_segment ()
72 max_connect_ = 1000; // infinity
82 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
84 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
87 Beam_segment::Beam_segment ()
93 Beam::add_stem (Grob *me, Grob *s)
95 if (Stem::get_beam (s))
97 programming_error ("Stem already has beam");
101 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
102 s->set_object ("beam", me->self_scm ());
103 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
107 Beam::get_beam_thickness (Grob *me)
109 return robust_scm2double (me->get_property ("beam-thickness"), 0)
110 * Staff_symbol_referencer::staff_space (me);
113 /* Return the translation between 2 adjoining beams. */
115 Beam::get_beam_translation (Grob *me)
117 int beam_count = get_beam_count (me);
118 Real staff_space = Staff_symbol_referencer::staff_space (me);
119 Real line = Staff_symbol_referencer::line_thickness (me);
120 Real beam_thickness = get_beam_thickness (me);
121 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
123 Real beam_translation = beam_count < 4
124 ? (2 * staff_space + line - beam_thickness) / 2.0
125 : (3 * staff_space + line - beam_thickness) / 3.0;
127 return fract * beam_translation;
130 /* Maximum beam_count. */
132 Beam::get_beam_count (Grob *me)
136 extract_grob_set (me, "stems", stems);
137 for (vsize i = 0; i < stems.size (); i++)
139 Grob *stem = stems[i];
140 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
145 //------ for whole note chord tremolos
148 Beam::whole_note_close_chord_tremolo (Grob *me)
150 if (!scm_is_integer (me->get_property ("gap-count")))
153 extract_grob_set (me, "stems", stems);
154 for (vsize i = 0; i < stems.size (); i++)
155 if (Stem::duration_log (stems[i]))
158 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
161 Grob *outside_stems[2] = {Stem::extremal_heads (stems[0])[DOWN],
162 Stem::extremal_heads (stems.back ())[DOWN]};
164 Interval lines = Staff_symbol::line_span (staff);
165 for (int i = 0; i < 2; i++)
167 Real my_pos = Staff_symbol_referencer::get_position (outside_stems[i]);
168 if (my_pos > lines[UP] + 1)
170 else if (my_pos < lines[DOWN] - 1)
175 return (Staff_symbol_referencer::get_position (Stem::extremal_heads (stems.back ())[DOWN])
176 - Staff_symbol_referencer::get_position (Stem::extremal_heads (stems[0])[DOWN]))
180 MAKE_SCHEME_CALLBACK (Beam, calc_beam_gap, 1);
182 Beam::calc_beam_gap (SCM smob)
184 Spanner *me = unsmob_spanner (smob);
185 SCM default_value = scm_cons (scm_from_double (0.8), scm_from_double (0.8));
186 if (!whole_note_close_chord_tremolo (me))
187 return default_value;
189 Interval left = Note_column::accidental_width
190 (me->get_bound (RIGHT)->get_parent (X_AXIS));
192 if (left.length () > 0.4)
193 return scm_cons (scm_from_double (0.8), scm_from_double (1.3 + left.length ()));
195 return default_value;
198 MAKE_SCHEME_CALLBACK (Beam, calc_springs_and_rods, 1);
200 Beam::calc_springs_and_rods (SCM smob)
202 Grob *me = unsmob_grob (smob);
204 if (!whole_note_close_chord_tremolo (me))
207 return scm_call_1 (Spanner::set_spacing_rods_proc, smob);
210 MAKE_SCHEME_CALLBACK (Beam, calc_minimum_length, 1);
212 Beam::calc_minimum_length (SCM smob)
214 Spanner *me = unsmob_spanner (smob);
215 SCM default_value = scm_from_double (0.0);
217 if (!whole_note_close_chord_tremolo (me))
220 Interval left = Note_column::accidental_width
221 (me->get_bound (RIGHT)->get_parent (X_AXIS));
223 if (left.length () > 0.4)
224 return scm_from_double (left.length () + 4.0);
226 return default_value;
229 //------ and everything else
231 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
233 Beam::calc_normal_stems (SCM smob)
235 Grob *me = unsmob_grob (smob);
237 extract_grob_set (me, "stems", stems);
238 SCM val = Grob_array::make_array ();
239 Grob_array *ga = unsmob_grob_array (val);
240 for (vsize i = 0; i < stems.size (); i++)
241 if (Stem::is_normal_stem (stems[i]))
247 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
249 Beam::calc_direction (SCM smob)
251 Grob *me = unsmob_grob (smob);
253 /* Beams with less than 2 two stems don't make much sense, but could happen
260 Direction dir = CENTER;
262 int count = normal_stem_count (me);
265 extract_grob_set (me, "stems", stems);
266 if (stems.size () == 0)
268 me->warning (_ ("removing beam with no stems"));
271 return SCM_UNSPECIFIED;
275 Grob *stem = first_normal_stem (me);
278 This happens for chord tremolos.
283 if (is_direction (stem->get_property_data ("direction")))
284 dir = to_dir (stem->get_property_data ("direction"));
286 dir = to_dir (stem->get_property ("default-direction"));
288 extract_grob_set (stem, "note-heads", heads);
289 /* default position of Kievan heads with beams is down
290 placing this here avoids warnings downstream */
293 if (heads[0]->get_property ("style") == ly_symbol2scm ("kievan"))
305 dir = get_default_dir (me);
307 consider_auto_knees (me);
312 set_stem_directions (me, dir);
315 return scm_from_int (dir);
318 /* We want a maximal number of shared beams, but if there is choice, we
319 * take the one that is closest to the end of the stem. This is for
331 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
335 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
339 for (int i = lslice[-left_dir];
340 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
343 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
345 int k = -right_dir * scm_to_int (scm_car (s)) + i;
346 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
350 if (count >= best_count)
360 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
362 Beam::calc_beaming (SCM smob)
364 Grob *me = unsmob_grob (smob);
366 extract_grob_set (me, "stems", stems);
369 last_int.set_empty ();
371 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
372 Direction last_dir = CENTER;
373 for (vsize i = 0; i < stems.size (); i++)
375 Grob *this_stem = stems[i];
376 SCM this_beaming = this_stem->get_property ("beaming");
378 Direction this_dir = get_grob_direction (this_stem);
379 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
381 int start_point = position_with_maximal_common_beams
382 (last_beaming, this_beaming,
383 last_dir ? last_dir : this_dir,
387 for (LEFT_and_RIGHT (d))
389 new_slice.set_empty ();
390 SCM s = index_get_cell (this_beaming, d);
391 for (; scm_is_pair (s); s = scm_cdr (s))
394 = start_point - this_dir * scm_to_int (scm_car (s));
396 new_slice.add_point (new_beam_pos);
397 scm_set_car_x (s, scm_from_int (new_beam_pos));
401 if (!new_slice.is_empty ())
402 last_int = new_slice;
407 FIXME: what's this for?
409 SCM s = scm_cdr (this_beaming);
410 for (; scm_is_pair (s); s = scm_cdr (s))
412 int np = -this_dir * scm_to_int (scm_car (s));
413 scm_set_car_x (s, scm_from_int (np));
414 last_int.add_point (np);
418 if (scm_ilength (scm_cdr (this_beaming)) > 0)
420 last_beaming = this_beaming;
429 operator <(Beam_stem_segment const &a,
430 Beam_stem_segment const &b)
432 return a.rank_ < b.rank_;
435 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
437 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
439 Beam::calc_beam_segments (SCM smob)
441 /* ugh, this has a side-effect that we need to ensure that
442 Stem #'beaming is correct */
443 Grob *me_grob = unsmob_grob (smob);
444 (void) me_grob->get_property ("beaming");
446 Spanner *me = dynamic_cast<Spanner *> (me_grob);
448 extract_grob_set (me, "stems", stems);
450 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
451 for (LEFT_and_RIGHT (d))
452 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
454 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
455 Interval gap_lengths = robust_scm2interval (me->get_property ("beam-gap"), Interval (0.0, 0.0));
457 Position_stem_segments_map stem_segments;
458 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
460 /* There are two concepts of "rank" that are used in the following code.
461 The beam_rank is the vertical position of the beam (larger numbers are
462 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
463 is the horizontal position of the segment (this is incremented by two
464 for each stem; the beam segment on the right side of the stem has
465 a higher rank (by one) than its neighbour to the left). */
467 for (vsize i = 0; i < stems.size (); i++)
469 Grob *stem = stems[i];
470 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
471 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
472 SCM beaming = stem->get_property ("beaming");
474 for (LEFT_and_RIGHT (d))
476 // Find the maximum and minimum beam ranks.
477 // Given that RANKS is never reset to empty, the interval will always be
478 // smallest for the left beamlet of the first stem, and then it might grow.
479 // Do we really want this? (It only affects the tremolo gaps) --jneem
480 for (SCM s = index_get_cell (beaming, d);
481 scm_is_pair (s); s = scm_cdr (s))
483 if (!scm_is_integer (scm_car (s)))
486 int beam_rank = scm_to_int (scm_car (s));
487 ranks.add_point (beam_rank);
490 for (SCM s = index_get_cell (beaming, d);
491 scm_is_pair (s); s = scm_cdr (s))
493 if (!scm_is_integer (scm_car (s)))
496 int beam_rank = scm_to_int (scm_car (s));
497 Beam_stem_segment seg;
499 seg.stem_x_ = stem_x;
500 seg.rank_ = 2 * i + (d + 1) / 2;
501 seg.width_ = stem_width;
504 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
506 Direction stem_dir = get_grob_direction (stem);
509 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
510 stem_segments[beam_rank].push_back (seg);
515 Drul_array<Real> break_overshoot
516 = robust_scm2drul (me->get_property ("break-overshoot"),
517 Drul_array<Real> (-0.5, 0.0));
519 vector<Beam_segment> segments;
520 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
521 i != stem_segments.end (); i++)
523 vector<Beam_stem_segment> segs = (*i).second;
524 vector_sort (segs, less<Beam_stem_segment> ());
526 Beam_segment current;
528 // Iterate over all of the segments of the current beam rank,
529 // merging the adjacent Beam_stem_segments into one Beam_segment
531 int vertical_count = (*i).first;
532 for (vsize j = 0; j < segs.size (); j++)
534 // Keeping track of the different directions here is a little tricky.
535 // segs[j].dir_ is the direction of the beam segment relative to the stem
536 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
537 // its stem) whereas event_dir refers to the edge of the beam segment that
538 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
539 // are looking at that edge of the beam segment that is furthest from its
541 Beam_stem_segment const &seg = segs[j];
542 for (LEFT_and_RIGHT (event_dir))
544 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
545 // TODO: make names clearer? --jneem
546 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
547 // on_beam_bound: whether the current segment is on the boundary of just that part
548 // of the beam with the current beam_rank
549 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
550 : seg.stem_index_ == stems.size () - 1;
551 bool on_beam_bound = (event_dir == LEFT) ? j == 0
552 : j == segs.size () - 1;
553 bool inside_stem = (event_dir == LEFT)
554 ? seg.stem_index_ > 0
555 : seg.stem_index_ + 1 < stems.size ();
557 bool event = on_beam_bound
558 || abs (seg.rank_ - neighbor_seg.rank_) > 1
559 || (abs (vertical_count) >= seg.max_connect_
560 || abs (vertical_count) >= neighbor_seg.max_connect_);
563 // Then this edge of the current segment is irrelevant because it will
564 // be connected with the next segment in the event_dir direction.
565 // If we skip the left edge here, the right edge of
566 // the previous segment has already been skipped since
567 // the conditions are symmetric
570 current.vertical_count_ = vertical_count;
571 current.horizontal_[event_dir] = seg.stem_x_;
572 if (seg.dir_ == event_dir)
573 // then we are examining the edge of a beam segment that is furthest
577 && me->get_bound (event_dir)->break_status_dir ())
579 current.horizontal_[event_dir]
580 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
581 commonx, X_AXIS)[RIGHT]
582 + event_dir * break_overshoot[event_dir]);
586 Grob *stem = stems[seg.stem_index_];
587 Drul_array<Real> beamlet_length
588 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
589 Drul_array<Real> max_proportion
590 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
591 Real length = beamlet_length[seg.dir_];
595 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
596 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
598 length = min (length,
599 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
601 current.horizontal_[event_dir] += event_dir * length;
605 // we are examining the edge of a beam segment that is closest
606 // (ie. touching, unless there is a gap) its stem.
608 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
611 current.horizontal_[event_dir] -= event_dir * gap_lengths[event_dir];
613 if (Stem::is_invisible (seg.stem_))
616 Need to do this in case of whole notes. We don't want the
617 heads to collide with the beams.
619 extract_grob_set (seg.stem_, "note-heads", heads);
621 for (vsize k = 0; k < heads.size (); k++)
622 current.horizontal_[event_dir]
623 = event_dir * min (event_dir * current.horizontal_[event_dir],
624 - gap_lengths[event_dir] / 2
626 * heads[k]->extent (commonx,
627 X_AXIS)[-event_dir]);
632 if (event_dir == RIGHT)
634 segments.push_back (current);
635 current = Beam_segment ();
642 SCM segments_scm = SCM_EOL;
644 for (vsize i = segments.size (); i--;)
646 segments_scm = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
647 scm_from_int (segments[i].vertical_count_)),
648 scm_cons (ly_symbol2scm ("horizontal"),
649 ly_interval2scm (segments[i].horizontal_))),
656 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
658 Beam::calc_x_positions (SCM smob)
660 Spanner *me = unsmob_spanner (smob);
661 SCM segments = me->get_property ("beam-segments");
662 Interval x_positions;
663 x_positions.set_empty ();
664 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
665 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
668 Interval (0.0, 0.0)));
670 // Case for beams without segments (i.e. uniting two skips with a beam)
671 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
672 if (x_positions.is_empty ())
674 extract_grob_set (me, "stems", stems);
675 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
676 for (LEFT_and_RIGHT (d))
677 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
679 return ly_interval2scm (x_positions);
683 Beam::get_beam_segments (Grob *me)
685 SCM segments_scm = me->get_property ("beam-segments");
686 vector<Beam_segment> segments;
687 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
689 segments.push_back (Beam_segment ());
690 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
691 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
697 MAKE_SCHEME_CALLBACK (Beam, print, 1);
699 Beam::print (SCM grob)
701 Spanner *me = unsmob_spanner (grob);
703 TODO - mild code dup for all the commonx calls.
704 Some use just common_refpoint_of_array, some (in print and
705 calc_beam_segments) use this plus calls to get_bound.
707 Figure out if there is any particular reason for this and
708 consolidate in one Beam::get_common function.
710 extract_grob_set (me, "stems", stems);
711 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
712 for (LEFT_and_RIGHT (d))
713 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
715 vector<Beam_segment> segments = get_beam_segments (me);
717 if (!segments.size ())
720 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
722 SCM posns = me->get_property ("quantized-positions");
723 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
725 if (!is_number_pair (posns))
727 programming_error ("no beam positions?");
728 pos = Interval (0, 0);
731 pos = ly_scm2realdrul (posns);
733 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
735 Real dy = pos[RIGHT] - pos[LEFT];
736 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
738 Real beam_thickness = get_beam_thickness (me);
739 Real beam_dy = get_beam_translation (me);
741 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
743 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
746 int extreme = (segments[0].vertical_count_ == 0
747 ? segments[0].vertical_count_
748 : segments.back ().vertical_count_);
750 for (vsize i = 0; i < segments.size (); i++)
752 Real local_slope = slope;
754 Makes local slope proportional to the ratio of the length of this beam
758 local_slope += (feather_dir * segments[i].vertical_count_
760 * placements.length ()
763 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
765 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
766 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
768 Interval weights (1 - multiplier, multiplier);
770 if (feather_dir != LEFT)
773 // we need two translations: the normal one and
774 // the one of the lowest segment
775 size_t idx[] = {i, extreme};
776 Real translations[2];
778 for (int j = 0; j < 2; j++)
779 translations[j] = slope
780 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
781 + pos.linear_combination (CENTER)
782 + beam_dy * segments[idx[j]].vertical_count_;
784 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
787 Tricky. The manipulation of the variable `weighted_average' below ensures
788 that beams with a RIGHT grow direction will start from the position of the
789 lowest segment at 0, and this error will decrease and decrease over the
790 course of the beam. Something with a LEFT grow direction, on the other
791 hand, will always start in the correct place but progressively accrue
792 error at broken places. This code shifts beams up given where they are
793 in the total span length (controlled by the variable `multiplier'). To
794 better understand what it does, try commenting it out: you'll see that
795 all of the RIGHT growing beams immediately start too low and get better
796 over line breaks, whereas all of the LEFT growing beams start just right
797 and get worse over line breaks.
799 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
801 if (segments[0].vertical_count_ < 0 && feather_dir)
802 weighted_average += beam_dy * (segments.size () - 1) * factor;
804 b.translate_axis (weighted_average, Y_AXIS);
806 the_beam.add_stencil (b);
810 #if (DEBUG_BEAM_SCORING)
811 SCM annotation = me->get_property ("annotation");
812 if (scm_is_string (annotation))
814 extract_grob_set (me, "stems", stems);
817 This code prints the demerits for each beam. Perhaps this
818 should be switchable for those who want to twiddle with the
822 SCM properties = Font_interface::text_font_alist_chain (me);
824 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
827 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
829 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
830 (me->layout ()->self_scm (), properties, annotation));
832 if (!score.is_empty ())
834 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
835 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
840 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
841 return the_beam.smobbed_copy ();
845 Beam::get_default_dir (Grob *me)
847 extract_grob_set (me, "stems", stems);
849 Drul_array<Real> extremes (0.0, 0.0);
850 for (iterof (s, stems); s != stems.end (); s++)
852 Interval positions = Stem::head_positions (*s);
853 for (DOWN_and_UP (d))
855 if (sign (positions[d]) == d)
856 extremes[d] = d * max (d * positions[d], d * extremes[d]);
860 Drul_array<int> total (0, 0);
861 Drul_array<int> count (0, 0);
863 bool force_dir = false;
864 for (vsize i = 0; i < stems.size (); i++)
867 Direction stem_dir = CENTER;
868 SCM stem_dir_scm = s->get_property_data ("direction");
869 if (is_direction (stem_dir_scm))
871 stem_dir = to_dir (stem_dir_scm);
875 stem_dir = to_dir (s->get_property ("default-direction"));
878 stem_dir = to_dir (s->get_property ("neutral-direction"));
883 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
889 if (abs (extremes[UP]) > -extremes[DOWN])
891 else if (extremes[UP] < -extremes[DOWN])
895 Direction dir = CENTER;
896 Direction d = CENTER;
897 if ((d = (Direction) sign (count[UP] - count[DOWN])))
901 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
903 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
906 dir = to_dir (me->get_property ("neutral-direction"));
911 /* Set all stems with non-forced direction to beam direction.
912 Urg: non-forced should become `without/with unforced' direction,
913 once stem gets cleaned-up. */
915 Beam::set_stem_directions (Grob *me, Direction d)
917 extract_grob_set (me, "stems", stems);
919 for (vsize i = 0; i < stems.size (); i++)
923 SCM forcedir = s->get_property_data ("direction");
924 if (!to_dir (forcedir))
925 set_grob_direction (s, d);
930 Only try horizontal beams for knees. No reliable detection of
931 anything else is possible here, since we don't know funky-beaming
932 settings, or X-distances (slopes!) People that want sloped
933 knee-beams, should set the directions manually.
938 this routine should take into account the stemlength scoring
939 of a possible knee/nonknee beam.
942 Beam::consider_auto_knees (Grob *me)
944 SCM scm = me->get_property ("auto-knee-gap");
945 if (!scm_is_number (scm))
948 vector<Interval> forbidden_intervals;
950 extract_grob_set (me, "normal-stems", stems);
952 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
953 Real staff_space = Staff_symbol_referencer::staff_space (me);
955 vector<Interval> head_extents_array;
956 for (vsize i = 0; i < stems.size (); i++)
958 Grob *stem = stems[i];
960 Interval head_extents = Stem::head_positions (stem);
961 if (!head_extents.is_empty ())
963 head_extents[LEFT] += -1;
964 head_extents[RIGHT] += 1;
965 head_extents *= staff_space * 0.5;
968 We could subtract beam Y position, but this routine only
969 sets stem directions, a constant shift does not have an
972 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
974 if (to_dir (stem->get_property_data ("direction")))
976 Direction stemdir = to_dir (stem->get_property ("direction"));
977 head_extents[-stemdir] = -stemdir * infinity_f;
980 head_extents_array.push_back (head_extents);
982 forbidden_intervals.push_back (head_extents);
986 Real max_gap_len = 0.0;
988 vector<Interval> allowed_regions
989 = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
990 for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
992 Interval gap = allowed_regions[i];
995 the outer gaps are not knees.
997 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
1000 if (gap.length () >= max_gap_len)
1002 max_gap_len = gap.length ();
1007 Real beam_translation = get_beam_translation (me);
1008 Real beam_thickness = Beam::get_beam_thickness (me);
1009 int beam_count = Beam::get_beam_count (me);
1010 Real height_of_beams = beam_thickness / 2
1011 + (beam_count - 1) * beam_translation;
1012 Real threshold = scm_to_double (scm) + height_of_beams;
1014 if (max_gap_len > threshold)
1017 for (vsize i = 0; i < stems.size (); i++)
1019 Grob *stem = stems[i];
1020 Interval head_extents = head_extents_array[j++];
1022 Direction d = (head_extents.center () < max_gap.center ())
1025 stem->set_property ("direction", scm_from_int (d));
1027 head_extents.intersect (max_gap);
1028 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
1033 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
1035 Beam::calc_stem_shorten (SCM smob)
1037 Grob *me = unsmob_grob (smob);
1040 shortening looks silly for x staff beams
1043 return scm_from_int (0);
1045 Real forced_fraction = 1.0 * forced_stem_count (me)
1046 / normal_stem_count (me);
1048 int beam_count = get_beam_count (me);
1050 SCM shorten_list = me->get_property ("beamed-stem-shorten");
1051 if (shorten_list == SCM_EOL)
1052 return scm_from_int (0);
1054 Real staff_space = Staff_symbol_referencer::staff_space (me);
1057 = robust_list_ref (beam_count - 1, shorten_list);
1058 Real shorten = scm_to_double (shorten_elt) * staff_space;
1060 shorten *= forced_fraction;
1063 return scm_from_double (shorten);
1065 return scm_from_double (0.0);
1068 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
1070 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
1072 Grob *me = unsmob_grob (smob);
1073 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
1074 bool cbs = to_boolean (align_broken_intos);
1076 Beam_scoring_problem problem (me, ys, cbs);
1077 ys = problem.solve ();
1079 return ly_interval2scm (ys);
1083 Report slice containing the numbers that are both in (car BEAMING)
1087 where_are_the_whole_beams (SCM beaming)
1091 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1093 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1095 l.add_point (scm_to_int (scm_car (s)));
1101 /* Return the Y position of the stem-end, given the Y-left, Y-right
1102 in POS for stem S. This Y position is relative to S. */
1104 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1105 Real xl, Real xr, Direction feather_dir,
1106 Drul_array<Real> pos, bool french)
1108 Real beam_translation = get_beam_translation (me);
1109 Direction stem_dir = get_grob_direction (stem);
1112 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1113 Real xdir = 2 * relx - 1;
1115 Real stem_y = linear_combination (pos, xdir);
1117 SCM beaming = stem->get_property ("beaming");
1119 Slice beam_slice (french
1120 ? where_are_the_whole_beams (beaming)
1121 : Stem::beam_multiplicity (stem));
1122 if (beam_slice.is_empty ())
1123 beam_slice = Slice (0, 0);
1124 Interval beam_multiplicity (beam_slice[LEFT],
1128 feather dir = 1 , relx 0->1 : factor 0 -> 1
1129 feather dir = 0 , relx 0->1 : factor 1 -> 1
1130 feather dir = -1, relx 0->1 : factor 1 -> 0
1132 Real feather_factor = 1;
1133 if (feather_dir > 0)
1134 feather_factor = relx;
1135 else if (feather_dir < 0)
1136 feather_factor = 1 - relx;
1138 stem_y += feather_factor * beam_translation
1139 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1140 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1141 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1147 Hmm. At this time, beam position and slope are determined. Maybe,
1148 stem directions and length should set to relative to the chord's
1149 position of the beam. */
1150 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1152 Beam::set_stem_lengths (SCM smob)
1154 Grob *me = unsmob_grob (smob);
1156 /* trigger callbacks. */
1157 (void) me->get_property ("direction");
1158 (void) me->get_property ("beaming");
1160 SCM posns = me->get_property ("positions");
1162 extract_grob_set (me, "stems", stems);
1167 for (int a = 2; a--;)
1168 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1170 Drul_array<Real> pos = ly_scm2realdrul (posns);
1171 Real staff_space = Staff_symbol_referencer::staff_space (me);
1172 scale_drul (&pos, staff_space);
1176 if (robust_scm2int (me->get_property ("gap-count"), 0))
1179 thick = get_beam_thickness (me);
1182 Grob *fvs = first_normal_stem (me);
1183 Grob *lvs = last_normal_stem (me);
1185 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1186 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1188 for (vsize i = 0; i < stems.size (); i++)
1192 bool french = to_boolean (s->get_property ("french-beaming"));
1193 Real stem_y = calc_stem_y (me, s, common,
1194 x_span[LEFT], x_span[RIGHT], feather_dir,
1195 pos, french && s != lvs && s != fvs);
1198 Make the stems go up to the end of the beam. This doesn't matter
1199 for normal beams, but for tremolo beams it looks silly otherwise.
1202 && !Stem::is_invisible (s))
1203 stem_y += thick * 0.5 * get_grob_direction (s);
1206 Do set_stem_positions for invisible stems too, so tuplet brackets
1207 have a reference point for sloping
1209 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1216 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1218 extract_grob_set (me, "stems", stems);
1220 for (vsize i = 0; i < stems.size (); i++)
1223 Don't overwrite user settings.
1225 for (LEFT_and_RIGHT (d))
1227 Grob *stem = stems[i];
1228 SCM beaming_prop = stem->get_property ("beaming");
1229 if (beaming_prop == SCM_EOL
1230 || index_get_cell (beaming_prop, d) == SCM_EOL)
1232 int count = beaming->beamlet_count (i, d);
1234 && i + 1 < stems.size ()
1235 && Stem::is_invisible (stem))
1236 count = min (count, beaming->beamlet_count (i, -d));
1238 if ( ((i == 0 && d == LEFT)
1239 || (i == stems.size () - 1 && d == RIGHT))
1240 && stems.size () > 1
1241 && to_boolean (me->get_property ("clip-edges")))
1244 Stem::set_beaming (stem, count, d);
1251 Beam::forced_stem_count (Grob *me)
1253 extract_grob_set (me, "normal-stems", stems);
1256 for (vsize i = 0; i < stems.size (); i++)
1260 /* I can imagine counting those boundaries as a half forced stem,
1261 but let's count them full for now. */
1262 Direction defdir = to_dir (s->get_property ("default-direction"));
1264 if (abs (Stem::chord_start_y (s)) > 0.1
1266 && get_grob_direction (s) != defdir)
1273 Beam::normal_stem_count (Grob *me)
1275 extract_grob_set (me, "normal-stems", stems);
1276 return stems.size ();
1280 Beam::first_normal_stem (Grob *me)
1282 extract_grob_set (me, "normal-stems", stems);
1283 return stems.size () ? stems[0] : 0;
1287 Beam::last_normal_stem (Grob *me)
1289 extract_grob_set (me, "normal-stems", stems);
1290 return stems.size () ? stems.back () : 0;
1296 handle rest under beam (do_post: beams are calculated now)
1297 what about combination of collisions and rest under beam.
1301 rest -> stem -> beam -> interpolate_y_position ()
1303 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1305 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1307 Grob *rest = unsmob_grob (smob);
1308 if (scm_is_number (rest->get_property ("staff-position")))
1309 return scm_from_int (0);
1311 Real offset = robust_scm2double (prev_offset, 0.0);
1313 Grob *st = unsmob_grob (rest->get_object ("stem"));
1316 return scm_from_double (0.0);
1317 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1319 || !Beam::has_interface (beam)
1320 || !Beam::normal_stem_count (beam))
1321 return scm_from_double (0.0);
1323 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1325 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1326 Drul_array<Real> (0, 0)));
1328 for (LEFT_and_RIGHT (dir))
1329 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1331 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1333 scale_drul (&pos, staff_space);
1335 Real dy = pos[RIGHT] - pos[LEFT];
1337 extract_grob_set (beam, "stems", stems);
1338 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1340 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1341 Interval (0.0, 0.0));
1342 Real x0 = x_span[LEFT];
1343 Real dx = x_span.length ();
1344 Real slope = dy && dx ? dy / dx : 0;
1346 Direction d = get_grob_direction (stem);
1347 Real stem_y = pos[LEFT]
1348 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1350 Real beam_translation = get_beam_translation (beam);
1351 Real beam_thickness = Beam::get_beam_thickness (beam);
1354 TODO: this is not strictly correct for 16th knee beams.
1357 = Stem::beam_multiplicity (stem).length () + 1;
1359 Real height_of_my_beams = beam_thickness / 2
1360 + (beam_count - 1) * beam_translation;
1361 Real beam_y = stem_y - d * height_of_my_beams;
1363 Interval rest_extent = rest->extent (rest, Y_AXIS);
1364 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1366 Real rest_dim = rest_extent[d];
1367 Real minimum_distance
1368 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1369 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1371 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1373 shift /= staff_space;
1375 /* Always move discretely by half spaces */
1376 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1378 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1379 staff_span *= staff_space / 2;
1381 /* Inside staff, move by whole spaces*/
1382 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1383 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1384 shift = ceil (fabs (shift)) * sign (shift);
1386 return scm_from_double (offset + staff_space * shift);
1390 Estimate the position of a rest under a beam,
1391 as the average position of its neighboring heads.
1393 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1395 Beam::pure_rest_collision_callback (SCM smob,
1400 Real previous = robust_scm2double (prev_offset, 0.0);
1402 Grob *me = unsmob_grob (smob);
1403 Grob *stem = unsmob_grob (me->get_object ("stem"));
1405 return scm_from_double (previous);
1406 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1408 || !Beam::normal_stem_count (beam)
1409 || !is_direction (beam->get_property_data ("direction")))
1410 return scm_from_double (previous);
1412 Real ss = Staff_symbol_referencer::staff_space (me);
1415 This gives the extrema of rest positions.
1416 Even with noteheads on ledgers, beams typically remain within the staff,
1417 and push rests at most one staff-space (2 positions) from the staff.
1419 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1420 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1421 rest_max_pos.widen (2);
1423 extract_grob_set (beam, "stems", stems);
1424 vector<Grob *> my_stems;
1426 for (vsize i = 0; i < stems.size (); i++)
1427 if (Stem::head_count (stems[i]) || stems[i] == stem)
1428 my_stems.push_back (stems[i]);
1432 for (vsize i = 0; i < my_stems.size (); i++)
1433 if (my_stems[i] == stem)
1441 if (idx == (vsize) - 1 || my_stems.size () == 1)
1442 return scm_from_double (previous);
1444 left = right = my_stems[1];
1445 else if (idx == my_stems.size () - 1)
1446 left = right = my_stems[idx - 1];
1449 left = my_stems[idx - 1];
1450 right = my_stems[idx + 1];
1453 /* In stems with several heads, use the one closest to the beam. */
1454 Direction beamdir = get_grob_direction (beam);
1455 Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1456 + Stem::head_positions (right)[beamdir]) / 2.0,
1457 rest_max_pos[DOWN]),
1462 // So that ceil below kicks in for rests that would otherwise brush
1463 // up against a beam quanted to a ledger line, add a bit of space
1464 // between the beam and the rest.
1465 shift += (0.01 * beamdir);
1467 /* Always move by a whole number of staff spaces */
1468 shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1470 return scm_from_double (previous + shift);
1474 Beam::is_knee (Grob *me)
1476 SCM k = me->get_property ("knee");
1477 if (scm_is_bool (k))
1478 return ly_scm2bool (k);
1482 extract_grob_set (me, "stems", stems);
1483 for (vsize i = stems.size (); i--;)
1485 Direction dir = get_grob_direction (stems[i]);
1494 me->set_property ("knee", ly_bool2scm (knee));
1500 Beam::is_cross_staff (Grob *me)
1502 extract_grob_set (me, "stems", stems);
1503 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1504 for (vsize i = 0; i < stems.size (); i++)
1505 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1510 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1512 Beam::calc_cross_staff (SCM smob)
1514 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1518 Beam::get_direction_beam_count (Grob *me, Direction d)
1520 extract_grob_set (me, "stems", stems);
1523 for (vsize i = stems.size (); i--;)
1526 Should we take invisible stems into account?
1528 if (get_grob_direction (stems[i]) == d)
1529 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1535 ADD_INTERFACE (Beam,
1538 "The @code{beam-thickness} property is the weight of beams,"
1539 " measured in staffspace. The @code{direction} property is"
1540 " not user-serviceable. Use the @code{direction} property"
1541 " of @code{Stem} instead.\n"
1542 "The following properties may be set in the @code{details}"
1546 "@item stem-length-demerit-factor\n"
1547 "Demerit factor used for inappropriate stem lengths.\n"
1548 "@item secondary-beam-demerit\n"
1549 "Demerit used in quanting calculations for multiple"
1551 "@item region-size\n"
1552 "Size of region for checking quant scores.\n"
1554 "Epsilon for beam quant code to check for presence"
1556 "@item stem-length-limit-penalty\n"
1557 "Penalty for differences in stem lengths on a beam.\n"
1558 "@item damping-direction-penalty\n"
1559 "Demerit penalty applied when beam direction is different"
1560 " from damping direction.\n"
1561 "@item hint-direction-penalty\n"
1562 "Demerit penalty applied when beam direction is different"
1563 " from damping direction, but damping slope is"
1564 " <= @code{round-to-zero-slope}.\n"
1565 "@item musical-direction-factor\n"
1566 "Demerit scaling factor for difference between"
1567 " beam slope and music slope.\n"
1568 "@item ideal-slope-factor\n"
1569 "Demerit scaling factor for difference between"
1570 " beam slope and damping slope.\n"
1571 "@item round-to-zero-slope\n"
1572 "Damping slope which is considered zero for purposes of"
1573 " calculating direction penalties.\n"
1579 "beamed-stem-shorten "
1587 "collision-interfaces "
1588 "collision-voice-only "
1599 "neutral-direction "
1602 "quantized-positions "