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;
643 SCM *tail = &segments_scm;
645 for (vsize i = 0; i < segments.size (); i++)
647 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
648 scm_from_int (segments[i].vertical_count_)),
649 scm_cons (ly_symbol2scm ("horizontal"),
650 ly_interval2scm (segments[i].horizontal_))),
652 tail = SCM_CDRLOC (*tail);
658 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
660 Beam::calc_x_positions (SCM smob)
662 Spanner *me = unsmob_spanner (smob);
663 SCM segments = me->get_property ("beam-segments");
664 Interval x_positions;
665 x_positions.set_empty ();
666 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
667 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
670 Interval (0.0, 0.0)));
672 // Case for beams without segments (i.e. uniting two skips with a beam)
673 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
674 if (x_positions.is_empty ())
676 extract_grob_set (me, "stems", stems);
677 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
678 for (LEFT_and_RIGHT (d))
679 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
681 return ly_interval2scm (x_positions);
685 Beam::get_beam_segments (Grob *me)
687 SCM segments_scm = me->get_property ("beam-segments");
688 vector<Beam_segment> segments;
689 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
691 segments.push_back (Beam_segment ());
692 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
693 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
699 MAKE_SCHEME_CALLBACK (Beam, print, 1);
701 Beam::print (SCM grob)
703 Spanner *me = unsmob_spanner (grob);
705 TODO - mild code dup for all the commonx calls.
706 Some use just common_refpoint_of_array, some (in print and
707 calc_beam_segments) use this plus calls to get_bound.
709 Figure out if there is any particular reason for this and
710 consolidate in one Beam::get_common function.
712 extract_grob_set (me, "stems", stems);
713 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
714 for (LEFT_and_RIGHT (d))
715 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
717 vector<Beam_segment> segments = get_beam_segments (me);
719 if (!segments.size ())
722 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
724 SCM posns = me->get_property ("quantized-positions");
725 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
727 if (!is_number_pair (posns))
729 programming_error ("no beam positions?");
730 pos = Interval (0, 0);
733 pos = ly_scm2realdrul (posns);
735 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
737 Real dy = pos[RIGHT] - pos[LEFT];
738 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
740 Real beam_thickness = get_beam_thickness (me);
741 Real beam_dy = get_beam_translation (me);
743 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
745 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
748 int extreme = (segments[0].vertical_count_ == 0
749 ? segments[0].vertical_count_
750 : segments.back ().vertical_count_);
752 for (vsize i = 0; i < segments.size (); i++)
754 Real local_slope = slope;
756 Makes local slope proportional to the ratio of the length of this beam
760 local_slope += (feather_dir * segments[i].vertical_count_
762 * placements.length ()
765 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
767 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
768 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
770 Interval weights (1 - multiplier, multiplier);
772 if (feather_dir != LEFT)
775 // we need two translations: the normal one and
776 // the one of the lowest segment
777 size_t idx[] = {i, extreme};
778 Real translations[2];
780 for (int j = 0; j < 2; j++)
781 translations[j] = slope
782 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
783 + pos.linear_combination (CENTER)
784 + beam_dy * segments[idx[j]].vertical_count_;
786 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
789 Tricky. The manipulation of the variable `weighted_average' below ensures
790 that beams with a RIGHT grow direction will start from the position of the
791 lowest segment at 0, and this error will decrease and decrease over the
792 course of the beam. Something with a LEFT grow direction, on the other
793 hand, will always start in the correct place but progressively accrue
794 error at broken places. This code shifts beams up given where they are
795 in the total span length (controlled by the variable `multiplier'). To
796 better understand what it does, try commenting it out: you'll see that
797 all of the RIGHT growing beams immediately start too low and get better
798 over line breaks, whereas all of the LEFT growing beams start just right
799 and get worse over line breaks.
801 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
803 if (segments[0].vertical_count_ < 0 && feather_dir)
804 weighted_average += beam_dy * (segments.size () - 1) * factor;
806 b.translate_axis (weighted_average, Y_AXIS);
808 the_beam.add_stencil (b);
812 #if (DEBUG_BEAM_SCORING)
813 SCM annotation = me->get_property ("annotation");
814 if (scm_is_string (annotation))
816 extract_grob_set (me, "stems", stems);
819 This code prints the demerits for each beam. Perhaps this
820 should be switchable for those who want to twiddle with the
824 SCM properties = Font_interface::text_font_alist_chain (me);
826 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
829 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
831 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
832 (me->layout ()->self_scm (), properties, annotation));
834 if (!score.is_empty ())
836 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
837 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
842 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
843 return the_beam.smobbed_copy ();
847 Beam::get_default_dir (Grob *me)
849 extract_grob_set (me, "stems", stems);
851 Drul_array<Real> extremes (0.0, 0.0);
852 for (iterof (s, stems); s != stems.end (); s++)
854 Interval positions = Stem::head_positions (*s);
855 for (DOWN_and_UP (d))
857 if (sign (positions[d]) == d)
858 extremes[d] = d * max (d * positions[d], d * extremes[d]);
862 Drul_array<int> total (0, 0);
863 Drul_array<int> count (0, 0);
865 bool force_dir = false;
866 for (vsize i = 0; i < stems.size (); i++)
869 Direction stem_dir = CENTER;
870 SCM stem_dir_scm = s->get_property_data ("direction");
871 if (is_direction (stem_dir_scm))
873 stem_dir = to_dir (stem_dir_scm);
877 stem_dir = to_dir (s->get_property ("default-direction"));
880 stem_dir = to_dir (s->get_property ("neutral-direction"));
885 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
891 if (abs (extremes[UP]) > -extremes[DOWN])
893 else if (extremes[UP] < -extremes[DOWN])
897 Direction dir = CENTER;
898 Direction d = CENTER;
899 if ((d = (Direction) sign (count[UP] - count[DOWN])))
903 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
905 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
908 dir = to_dir (me->get_property ("neutral-direction"));
913 /* Set all stems with non-forced direction to beam direction.
914 Urg: non-forced should become `without/with unforced' direction,
915 once stem gets cleaned-up. */
917 Beam::set_stem_directions (Grob *me, Direction d)
919 extract_grob_set (me, "stems", stems);
921 for (vsize i = 0; i < stems.size (); i++)
925 SCM forcedir = s->get_property_data ("direction");
926 if (!to_dir (forcedir))
927 set_grob_direction (s, d);
932 Only try horizontal beams for knees. No reliable detection of
933 anything else is possible here, since we don't know funky-beaming
934 settings, or X-distances (slopes!) People that want sloped
935 knee-beams, should set the directions manually.
940 this routine should take into account the stemlength scoring
941 of a possible knee/nonknee beam.
944 Beam::consider_auto_knees (Grob *me)
946 SCM scm = me->get_property ("auto-knee-gap");
947 if (!scm_is_number (scm))
950 vector<Interval> forbidden_intervals;
952 extract_grob_set (me, "normal-stems", stems);
954 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
955 Real staff_space = Staff_symbol_referencer::staff_space (me);
957 vector<Interval> head_extents_array;
958 for (vsize i = 0; i < stems.size (); i++)
960 Grob *stem = stems[i];
962 Interval head_extents = Stem::head_positions (stem);
963 if (!head_extents.is_empty ())
965 head_extents[LEFT] += -1;
966 head_extents[RIGHT] += 1;
967 head_extents *= staff_space * 0.5;
970 We could subtract beam Y position, but this routine only
971 sets stem directions, a constant shift does not have an
974 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
976 if (to_dir (stem->get_property_data ("direction")))
978 Direction stemdir = to_dir (stem->get_property ("direction"));
979 head_extents[-stemdir] = -stemdir * infinity_f;
982 head_extents_array.push_back (head_extents);
984 forbidden_intervals.push_back (head_extents);
988 Real max_gap_len = 0.0;
990 vector<Interval> allowed_regions
991 = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
992 for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
994 Interval gap = allowed_regions[i];
997 the outer gaps are not knees.
999 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
1002 if (gap.length () >= max_gap_len)
1004 max_gap_len = gap.length ();
1009 Real beam_translation = get_beam_translation (me);
1010 Real beam_thickness = Beam::get_beam_thickness (me);
1011 int beam_count = Beam::get_beam_count (me);
1012 Real height_of_beams = beam_thickness / 2
1013 + (beam_count - 1) * beam_translation;
1014 Real threshold = scm_to_double (scm) + height_of_beams;
1016 if (max_gap_len > threshold)
1019 for (vsize i = 0; i < stems.size (); i++)
1021 Grob *stem = stems[i];
1022 Interval head_extents = head_extents_array[j++];
1024 Direction d = (head_extents.center () < max_gap.center ())
1027 stem->set_property ("direction", scm_from_int (d));
1029 head_extents.intersect (max_gap);
1030 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
1035 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
1037 Beam::calc_stem_shorten (SCM smob)
1039 Grob *me = unsmob_grob (smob);
1042 shortening looks silly for x staff beams
1045 return scm_from_int (0);
1047 Real forced_fraction = 1.0 * forced_stem_count (me)
1048 / normal_stem_count (me);
1050 int beam_count = get_beam_count (me);
1052 SCM shorten_list = me->get_property ("beamed-stem-shorten");
1053 if (shorten_list == SCM_EOL)
1054 return scm_from_int (0);
1056 Real staff_space = Staff_symbol_referencer::staff_space (me);
1059 = robust_list_ref (beam_count - 1, shorten_list);
1060 Real shorten = scm_to_double (shorten_elt) * staff_space;
1062 shorten *= forced_fraction;
1065 return scm_from_double (shorten);
1067 return scm_from_double (0.0);
1070 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
1072 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
1074 Grob *me = unsmob_grob (smob);
1075 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
1076 bool cbs = to_boolean (align_broken_intos);
1078 Beam_scoring_problem problem (me, ys, cbs);
1079 ys = problem.solve ();
1081 return ly_interval2scm (ys);
1085 Report slice containing the numbers that are both in (car BEAMING)
1089 where_are_the_whole_beams (SCM beaming)
1093 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1095 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1097 l.add_point (scm_to_int (scm_car (s)));
1103 /* Return the Y position of the stem-end, given the Y-left, Y-right
1104 in POS for stem S. This Y position is relative to S. */
1106 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1107 Real xl, Real xr, Direction feather_dir,
1108 Drul_array<Real> pos, bool french)
1110 Real beam_translation = get_beam_translation (me);
1111 Direction stem_dir = get_grob_direction (stem);
1114 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1115 Real xdir = 2 * relx - 1;
1117 Real stem_y = linear_combination (pos, xdir);
1119 SCM beaming = stem->get_property ("beaming");
1121 Slice beam_slice (french
1122 ? where_are_the_whole_beams (beaming)
1123 : Stem::beam_multiplicity (stem));
1124 if (beam_slice.is_empty ())
1125 beam_slice = Slice (0, 0);
1126 Interval beam_multiplicity (beam_slice[LEFT],
1130 feather dir = 1 , relx 0->1 : factor 0 -> 1
1131 feather dir = 0 , relx 0->1 : factor 1 -> 1
1132 feather dir = -1, relx 0->1 : factor 1 -> 0
1134 Real feather_factor = 1;
1135 if (feather_dir > 0)
1136 feather_factor = relx;
1137 else if (feather_dir < 0)
1138 feather_factor = 1 - relx;
1140 stem_y += feather_factor * beam_translation
1141 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1142 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1143 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1149 Hmm. At this time, beam position and slope are determined. Maybe,
1150 stem directions and length should set to relative to the chord's
1151 position of the beam. */
1152 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1154 Beam::set_stem_lengths (SCM smob)
1156 Grob *me = unsmob_grob (smob);
1158 /* trigger callbacks. */
1159 (void) me->get_property ("direction");
1160 (void) me->get_property ("beaming");
1162 SCM posns = me->get_property ("positions");
1164 extract_grob_set (me, "stems", stems);
1169 for (int a = 2; a--;)
1170 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1172 Drul_array<Real> pos = ly_scm2realdrul (posns);
1173 Real staff_space = Staff_symbol_referencer::staff_space (me);
1174 scale_drul (&pos, staff_space);
1178 if (robust_scm2int (me->get_property ("gap-count"), 0))
1181 thick = get_beam_thickness (me);
1184 Grob *fvs = first_normal_stem (me);
1185 Grob *lvs = last_normal_stem (me);
1187 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1188 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1190 for (vsize i = 0; i < stems.size (); i++)
1194 bool french = to_boolean (s->get_property ("french-beaming"));
1195 Real stem_y = calc_stem_y (me, s, common,
1196 x_span[LEFT], x_span[RIGHT], feather_dir,
1197 pos, french && s != lvs && s != fvs);
1200 Make the stems go up to the end of the beam. This doesn't matter
1201 for normal beams, but for tremolo beams it looks silly otherwise.
1204 && !Stem::is_invisible (s))
1205 stem_y += thick * 0.5 * get_grob_direction (s);
1208 Do set_stem_positions for invisible stems too, so tuplet brackets
1209 have a reference point for sloping
1211 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1218 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1220 extract_grob_set (me, "stems", stems);
1222 for (vsize i = 0; i < stems.size (); i++)
1225 Don't overwrite user settings.
1227 for (LEFT_and_RIGHT (d))
1229 Grob *stem = stems[i];
1230 SCM beaming_prop = stem->get_property ("beaming");
1231 if (beaming_prop == SCM_EOL
1232 || index_get_cell (beaming_prop, d) == SCM_EOL)
1234 int count = beaming->beamlet_count (i, d);
1236 && i + 1 < stems.size ()
1237 && Stem::is_invisible (stem))
1238 count = min (count, beaming->beamlet_count (i, -d));
1240 if ( ((i == 0 && d == LEFT)
1241 || (i == stems.size () - 1 && d == RIGHT))
1242 && stems.size () > 1
1243 && to_boolean (me->get_property ("clip-edges")))
1246 Stem::set_beaming (stem, count, d);
1253 Beam::forced_stem_count (Grob *me)
1255 extract_grob_set (me, "normal-stems", stems);
1258 for (vsize i = 0; i < stems.size (); i++)
1262 /* I can imagine counting those boundaries as a half forced stem,
1263 but let's count them full for now. */
1264 Direction defdir = to_dir (s->get_property ("default-direction"));
1266 if (abs (Stem::chord_start_y (s)) > 0.1
1268 && get_grob_direction (s) != defdir)
1275 Beam::normal_stem_count (Grob *me)
1277 extract_grob_set (me, "normal-stems", stems);
1278 return stems.size ();
1282 Beam::first_normal_stem (Grob *me)
1284 extract_grob_set (me, "normal-stems", stems);
1285 return stems.size () ? stems[0] : 0;
1289 Beam::last_normal_stem (Grob *me)
1291 extract_grob_set (me, "normal-stems", stems);
1292 return stems.size () ? stems.back () : 0;
1298 handle rest under beam (do_post: beams are calculated now)
1299 what about combination of collisions and rest under beam.
1303 rest -> stem -> beam -> interpolate_y_position ()
1305 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1307 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1309 Grob *rest = unsmob_grob (smob);
1310 if (scm_is_number (rest->get_property ("staff-position")))
1311 return scm_from_int (0);
1313 Real offset = robust_scm2double (prev_offset, 0.0);
1315 Grob *st = unsmob_grob (rest->get_object ("stem"));
1318 return scm_from_double (0.0);
1319 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1321 || !Beam::has_interface (beam)
1322 || !Beam::normal_stem_count (beam))
1323 return scm_from_double (0.0);
1325 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1327 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1328 Drul_array<Real> (0, 0)));
1330 for (LEFT_and_RIGHT (dir))
1331 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1333 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1335 scale_drul (&pos, staff_space);
1337 Real dy = pos[RIGHT] - pos[LEFT];
1339 extract_grob_set (beam, "stems", stems);
1340 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1342 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1343 Interval (0.0, 0.0));
1344 Real x0 = x_span[LEFT];
1345 Real dx = x_span.length ();
1346 Real slope = dy && dx ? dy / dx : 0;
1348 Direction d = get_grob_direction (stem);
1349 Real stem_y = pos[LEFT]
1350 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1352 Real beam_translation = get_beam_translation (beam);
1353 Real beam_thickness = Beam::get_beam_thickness (beam);
1356 TODO: this is not strictly correct for 16th knee beams.
1359 = Stem::beam_multiplicity (stem).length () + 1;
1361 Real height_of_my_beams = beam_thickness / 2
1362 + (beam_count - 1) * beam_translation;
1363 Real beam_y = stem_y - d * height_of_my_beams;
1365 Interval rest_extent = rest->extent (rest, Y_AXIS);
1366 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1368 Real rest_dim = rest_extent[d];
1369 Real minimum_distance
1370 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1371 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1373 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1375 shift /= staff_space;
1377 /* Always move discretely by half spaces */
1378 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1380 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1381 staff_span *= staff_space / 2;
1383 /* Inside staff, move by whole spaces*/
1384 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1385 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1386 shift = ceil (fabs (shift)) * sign (shift);
1388 return scm_from_double (offset + staff_space * shift);
1392 Estimate the position of a rest under a beam,
1393 as the average position of its neighboring heads.
1395 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1397 Beam::pure_rest_collision_callback (SCM smob,
1402 Real previous = robust_scm2double (prev_offset, 0.0);
1404 Grob *me = unsmob_grob (smob);
1405 Grob *stem = unsmob_grob (me->get_object ("stem"));
1407 return scm_from_double (previous);
1408 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1410 || !Beam::normal_stem_count (beam)
1411 || !is_direction (beam->get_property_data ("direction")))
1412 return scm_from_double (previous);
1414 Real ss = Staff_symbol_referencer::staff_space (me);
1417 This gives the extrema of rest positions.
1418 Even with noteheads on ledgers, beams typically remain within the staff,
1419 and push rests at most one staff-space (2 positions) from the staff.
1421 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1422 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1423 rest_max_pos.widen (2);
1425 extract_grob_set (beam, "stems", stems);
1426 vector<Grob *> my_stems;
1428 for (vsize i = 0; i < stems.size (); i++)
1429 if (Stem::head_count (stems[i]) || stems[i] == stem)
1430 my_stems.push_back (stems[i]);
1434 for (vsize i = 0; i < my_stems.size (); i++)
1435 if (my_stems[i] == stem)
1443 if (idx == (vsize) - 1 || my_stems.size () == 1)
1444 return scm_from_double (previous);
1446 left = right = my_stems[1];
1447 else if (idx == my_stems.size () - 1)
1448 left = right = my_stems[idx - 1];
1451 left = my_stems[idx - 1];
1452 right = my_stems[idx + 1];
1455 /* In stems with several heads, use the one closest to the beam. */
1456 Direction beamdir = get_grob_direction (beam);
1457 Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1458 + Stem::head_positions (right)[beamdir]) / 2.0,
1459 rest_max_pos[DOWN]),
1464 // So that ceil below kicks in for rests that would otherwise brush
1465 // up against a beam quanted to a ledger line, add a bit of space
1466 // between the beam and the rest.
1467 shift += (0.01 * beamdir);
1469 /* Always move by a whole number of staff spaces */
1470 shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1472 return scm_from_double (previous + shift);
1476 Beam::is_knee (Grob *me)
1478 SCM k = me->get_property ("knee");
1479 if (scm_is_bool (k))
1480 return ly_scm2bool (k);
1484 extract_grob_set (me, "stems", stems);
1485 for (vsize i = stems.size (); i--;)
1487 Direction dir = get_grob_direction (stems[i]);
1496 me->set_property ("knee", ly_bool2scm (knee));
1502 Beam::is_cross_staff (Grob *me)
1504 extract_grob_set (me, "stems", stems);
1505 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1506 for (vsize i = 0; i < stems.size (); i++)
1507 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1512 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1514 Beam::calc_cross_staff (SCM smob)
1516 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1520 Beam::get_direction_beam_count (Grob *me, Direction d)
1522 extract_grob_set (me, "stems", stems);
1525 for (vsize i = stems.size (); i--;)
1528 Should we take invisible stems into account?
1530 if (get_grob_direction (stems[i]) == d)
1531 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1537 ADD_INTERFACE (Beam,
1540 "The @code{beam-thickness} property is the weight of beams,"
1541 " measured in staffspace. The @code{direction} property is"
1542 " not user-serviceable. Use the @code{direction} property"
1543 " of @code{Stem} instead.\n"
1544 "The following properties may be set in the @code{details}"
1548 "@item stem-length-demerit-factor\n"
1549 "Demerit factor used for inappropriate stem lengths.\n"
1550 "@item secondary-beam-demerit\n"
1551 "Demerit used in quanting calculations for multiple"
1553 "@item region-size\n"
1554 "Size of region for checking quant scores.\n"
1556 "Epsilon for beam quant code to check for presence"
1558 "@item stem-length-limit-penalty\n"
1559 "Penalty for differences in stem lengths on a beam.\n"
1560 "@item damping-direction-penalty\n"
1561 "Demerit penalty applied when beam direction is different"
1562 " from damping direction.\n"
1563 "@item hint-direction-penalty\n"
1564 "Demerit penalty applied when beam direction is different"
1565 " from damping direction, but damping slope is"
1566 " <= @code{round-to-zero-slope}.\n"
1567 "@item musical-direction-factor\n"
1568 "Demerit scaling factor for difference between"
1569 " beam slope and music slope.\n"
1570 "@item ideal-slope-factor\n"
1571 "Demerit scaling factor for difference between"
1572 " beam slope and damping slope.\n"
1573 "@item round-to-zero-slope\n"
1574 "Damping slope which is considered zero for purposes of"
1575 " calculating direction penalties.\n"
1581 "beamed-stem-shorten "
1589 "collision-interfaces "
1590 "collision-voice-only "
1601 "neutral-direction "
1604 "quantized-positions "