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"));
293 dir = get_default_dir (me);
295 consider_auto_knees (me);
300 set_stem_directions (me, dir);
303 return scm_from_int (dir);
306 /* We want a maximal number of shared beams, but if there is choice, we
307 * take the one that is closest to the end of the stem. This is for
319 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
323 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
327 for (int i = lslice[-left_dir];
328 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
331 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
333 int k = -right_dir * scm_to_int (scm_car (s)) + i;
334 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
338 if (count >= best_count)
348 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
350 Beam::calc_beaming (SCM smob)
352 Grob *me = unsmob_grob (smob);
354 extract_grob_set (me, "stems", stems);
357 last_int.set_empty ();
359 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
360 Direction last_dir = CENTER;
361 for (vsize i = 0; i < stems.size (); i++)
363 Grob *this_stem = stems[i];
364 SCM this_beaming = this_stem->get_property ("beaming");
366 Direction this_dir = get_grob_direction (this_stem);
367 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
369 int start_point = position_with_maximal_common_beams
370 (last_beaming, this_beaming,
371 last_dir ? last_dir : this_dir,
375 for (LEFT_and_RIGHT (d))
377 new_slice.set_empty ();
378 SCM s = index_get_cell (this_beaming, d);
379 for (; scm_is_pair (s); s = scm_cdr (s))
382 = start_point - this_dir * scm_to_int (scm_car (s));
384 new_slice.add_point (new_beam_pos);
385 scm_set_car_x (s, scm_from_int (new_beam_pos));
389 if (!new_slice.is_empty ())
390 last_int = new_slice;
395 FIXME: what's this for?
397 SCM s = scm_cdr (this_beaming);
398 for (; scm_is_pair (s); s = scm_cdr (s))
400 int np = -this_dir * scm_to_int (scm_car (s));
401 scm_set_car_x (s, scm_from_int (np));
402 last_int.add_point (np);
406 if (scm_ilength (scm_cdr (this_beaming)) > 0)
408 last_beaming = this_beaming;
417 operator <(Beam_stem_segment const &a,
418 Beam_stem_segment const &b)
420 return a.rank_ < b.rank_;
423 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
425 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
427 Beam::calc_beam_segments (SCM smob)
429 /* ugh, this has a side-effect that we need to ensure that
430 Stem #'beaming is correct */
431 Grob *me_grob = unsmob_grob (smob);
432 (void) me_grob->get_property ("beaming");
434 Spanner *me = dynamic_cast<Spanner *> (me_grob);
436 extract_grob_set (me, "stems", stems);
438 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
439 for (LEFT_and_RIGHT (d))
440 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
442 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
443 Interval gap_lengths = robust_scm2interval (me->get_property ("beam-gap"), Interval (0.0, 0.0));
445 Position_stem_segments_map stem_segments;
446 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
448 /* There are two concepts of "rank" that are used in the following code.
449 The beam_rank is the vertical position of the beam (larger numbers are
450 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
451 is the horizontal position of the segment (this is incremented by two
452 for each stem; the beam segment on the right side of the stem has
453 a higher rank (by one) than its neighbour to the left). */
455 for (vsize i = 0; i < stems.size (); i++)
457 Grob *stem = stems[i];
458 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
459 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
460 SCM beaming = stem->get_property ("beaming");
462 for (LEFT_and_RIGHT (d))
464 // Find the maximum and minimum beam ranks.
465 // Given that RANKS is never reset to empty, the interval will always be
466 // smallest for the left beamlet of the first stem, and then it might grow.
467 // Do we really want this? (It only affects the tremolo gaps) --jneem
468 for (SCM s = index_get_cell (beaming, d);
469 scm_is_pair (s); s = scm_cdr (s))
471 if (!scm_is_integer (scm_car (s)))
474 int beam_rank = scm_to_int (scm_car (s));
475 ranks.add_point (beam_rank);
478 for (SCM s = index_get_cell (beaming, d);
479 scm_is_pair (s); s = scm_cdr (s))
481 if (!scm_is_integer (scm_car (s)))
484 int beam_rank = scm_to_int (scm_car (s));
485 Beam_stem_segment seg;
487 seg.stem_x_ = stem_x;
488 seg.rank_ = 2 * i + (d + 1) / 2;
489 seg.width_ = stem_width;
492 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
494 Direction stem_dir = get_grob_direction (stem);
497 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
498 stem_segments[beam_rank].push_back (seg);
503 Drul_array<Real> break_overshoot
504 = robust_scm2drul (me->get_property ("break-overshoot"),
505 Drul_array<Real> (-0.5, 0.0));
507 vector<Beam_segment> segments;
508 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
509 i != stem_segments.end (); i++)
511 vector<Beam_stem_segment> segs = (*i).second;
512 vector_sort (segs, less<Beam_stem_segment> ());
514 Beam_segment current;
516 // Iterate over all of the segments of the current beam rank,
517 // merging the adjacent Beam_stem_segments into one Beam_segment
519 int vertical_count = (*i).first;
520 for (vsize j = 0; j < segs.size (); j++)
522 // Keeping track of the different directions here is a little tricky.
523 // segs[j].dir_ is the direction of the beam segment relative to the stem
524 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
525 // its stem) whereas event_dir refers to the edge of the beam segment that
526 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
527 // are looking at that edge of the beam segment that is furthest from its
529 Beam_stem_segment const &seg = segs[j];
530 for (LEFT_and_RIGHT (event_dir))
532 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
533 // TODO: make names clearer? --jneem
534 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
535 // on_beam_bound: whether the current segment is on the boundary of just that part
536 // of the beam with the current beam_rank
537 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
538 : seg.stem_index_ == stems.size () - 1;
539 bool on_beam_bound = (event_dir == LEFT) ? j == 0
540 : j == segs.size () - 1;
541 bool inside_stem = (event_dir == LEFT)
542 ? seg.stem_index_ > 0
543 : seg.stem_index_ + 1 < stems.size ();
545 bool event = on_beam_bound
546 || abs (seg.rank_ - neighbor_seg.rank_) > 1
547 || (abs (vertical_count) >= seg.max_connect_
548 || abs (vertical_count) >= neighbor_seg.max_connect_);
551 // Then this edge of the current segment is irrelevant because it will
552 // be connected with the next segment in the event_dir direction.
553 // If we skip the left edge here, the right edge of
554 // the previous segment has already been skipped since
555 // the conditions are symmetric
558 current.vertical_count_ = vertical_count;
559 current.horizontal_[event_dir] = seg.stem_x_;
560 if (seg.dir_ == event_dir)
561 // then we are examining the edge of a beam segment that is furthest
565 && me->get_bound (event_dir)->break_status_dir ())
567 current.horizontal_[event_dir]
568 = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
569 commonx, X_AXIS)[RIGHT]
570 + event_dir * break_overshoot[event_dir]);
574 Grob *stem = stems[seg.stem_index_];
575 Drul_array<Real> beamlet_length
576 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
577 Drul_array<Real> max_proportion
578 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
579 Real length = beamlet_length[seg.dir_];
583 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
584 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
586 length = min (length,
587 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
589 current.horizontal_[event_dir] += event_dir * length;
593 // we are examining the edge of a beam segment that is closest
594 // (ie. touching, unless there is a gap) its stem.
596 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
599 current.horizontal_[event_dir] -= event_dir * gap_lengths[event_dir];
601 if (Stem::is_invisible (seg.stem_))
604 Need to do this in case of whole notes. We don't want the
605 heads to collide with the beams.
607 extract_grob_set (seg.stem_, "note-heads", heads);
609 for (vsize k = 0; k < heads.size (); k++)
610 current.horizontal_[event_dir]
611 = event_dir * min (event_dir * current.horizontal_[event_dir],
612 - gap_lengths[event_dir] / 2
614 * heads[k]->extent (commonx,
615 X_AXIS)[-event_dir]);
620 if (event_dir == RIGHT)
622 segments.push_back (current);
623 current = Beam_segment ();
630 SCM segments_scm = SCM_EOL;
631 SCM *tail = &segments_scm;
633 for (vsize i = 0; i < segments.size (); i++)
635 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
636 scm_from_int (segments[i].vertical_count_)),
637 scm_cons (ly_symbol2scm ("horizontal"),
638 ly_interval2scm (segments[i].horizontal_))),
640 tail = SCM_CDRLOC (*tail);
646 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
648 Beam::calc_x_positions (SCM smob)
650 Spanner *me = unsmob_spanner (smob);
651 SCM segments = me->get_property ("beam-segments");
652 Interval x_positions;
653 x_positions.set_empty ();
654 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
655 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
658 Interval (0.0, 0.0)));
660 // Case for beams without segments (i.e. uniting two skips with a beam)
661 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
662 if (x_positions.is_empty ())
664 extract_grob_set (me, "stems", stems);
665 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
666 for (LEFT_and_RIGHT (d))
667 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
669 return ly_interval2scm (x_positions);
673 Beam::get_beam_segments (Grob *me)
675 SCM segments_scm = me->get_property ("beam-segments");
676 vector<Beam_segment> segments;
677 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
679 segments.push_back (Beam_segment ());
680 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
681 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
687 MAKE_SCHEME_CALLBACK (Beam, print, 1);
689 Beam::print (SCM grob)
691 Spanner *me = unsmob_spanner (grob);
693 TODO - mild code dup for all the commonx calls.
694 Some use just common_refpoint_of_array, some (in print and
695 calc_beam_segments) use this plus calls to get_bound.
697 Figure out if there is any particular reason for this and
698 consolidate in one Beam::get_common function.
700 extract_grob_set (me, "stems", stems);
701 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
702 for (LEFT_and_RIGHT (d))
703 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
705 vector<Beam_segment> segments = get_beam_segments (me);
707 if (!segments.size ())
710 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
712 SCM posns = me->get_property ("quantized-positions");
713 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
715 if (!is_number_pair (posns))
717 programming_error ("no beam positions?");
718 pos = Interval (0, 0);
721 pos = ly_scm2realdrul (posns);
723 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
725 Real dy = pos[RIGHT] - pos[LEFT];
726 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
728 Real beam_thickness = get_beam_thickness (me);
729 Real beam_dy = get_beam_translation (me);
731 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
733 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
736 int extreme = (segments[0].vertical_count_ == 0
737 ? segments[0].vertical_count_
738 : segments.back ().vertical_count_);
740 for (vsize i = 0; i < segments.size (); i++)
742 Real local_slope = slope;
744 Makes local slope proportional to the ratio of the length of this beam
748 local_slope += (feather_dir * segments[i].vertical_count_
750 * placements.length ()
753 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
755 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
756 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
758 Interval weights (1 - multiplier, multiplier);
760 if (feather_dir != LEFT)
763 // we need two translations: the normal one and
764 // the one of the lowest segment
765 size_t idx[] = {i, extreme};
766 Real translations[2];
768 for (int j = 0; j < 2; j++)
769 translations[j] = slope
770 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
771 + pos.linear_combination (CENTER)
772 + beam_dy * segments[idx[j]].vertical_count_;
774 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
777 Tricky. The manipulation of the variable `weighted_average' below ensures
778 that beams with a RIGHT grow direction will start from the position of the
779 lowest segment at 0, and this error will decrease and decrease over the
780 course of the beam. Something with a LEFT grow direction, on the other
781 hand, will always start in the correct place but progressively accrue
782 error at broken places. This code shifts beams up given where they are
783 in the total span length (controlled by the variable `multiplier'). To
784 better understand what it does, try commenting it out: you'll see that
785 all of the RIGHT growing beams immediately start too low and get better
786 over line breaks, whereas all of the LEFT growing beams start just right
787 and get worse over line breaks.
789 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
791 if (segments[0].vertical_count_ < 0 && feather_dir)
792 weighted_average += beam_dy * (segments.size () - 1) * factor;
794 b.translate_axis (weighted_average, Y_AXIS);
796 the_beam.add_stencil (b);
800 #if (DEBUG_BEAM_SCORING)
801 SCM annotation = me->get_property ("annotation");
802 if (scm_is_string (annotation))
804 extract_grob_set (me, "stems", stems);
807 This code prints the demerits for each beam. Perhaps this
808 should be switchable for those who want to twiddle with the
812 SCM properties = Font_interface::text_font_alist_chain (me);
814 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
817 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
819 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
820 (me->layout ()->self_scm (), properties, annotation));
822 if (!score.is_empty ())
824 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
825 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
830 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
831 return the_beam.smobbed_copy ();
835 Beam::get_default_dir (Grob *me)
837 extract_grob_set (me, "stems", stems);
839 Drul_array<Real> extremes (0.0, 0.0);
840 for (iterof (s, stems); s != stems.end (); s++)
842 Interval positions = Stem::head_positions (*s);
843 for (DOWN_and_UP (d))
845 if (sign (positions[d]) == d)
846 extremes[d] = d * max (d * positions[d], d * extremes[d]);
850 Drul_array<int> total (0, 0);
851 Drul_array<int> count (0, 0);
853 bool force_dir = false;
854 for (vsize i = 0; i < stems.size (); i++)
857 Direction stem_dir = CENTER;
858 SCM stem_dir_scm = s->get_property_data ("direction");
859 if (is_direction (stem_dir_scm))
861 stem_dir = to_dir (stem_dir_scm);
865 stem_dir = to_dir (s->get_property ("default-direction"));
868 stem_dir = to_dir (s->get_property ("neutral-direction"));
873 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
879 if (abs (extremes[UP]) > -extremes[DOWN])
881 else if (extremes[UP] < -extremes[DOWN])
885 Direction dir = CENTER;
886 Direction d = CENTER;
887 if ((d = (Direction) sign (count[UP] - count[DOWN])))
891 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
893 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
896 dir = to_dir (me->get_property ("neutral-direction"));
901 /* Set all stems with non-forced direction to beam direction.
902 Urg: non-forced should become `without/with unforced' direction,
903 once stem gets cleaned-up. */
905 Beam::set_stem_directions (Grob *me, Direction d)
907 extract_grob_set (me, "stems", stems);
909 for (vsize i = 0; i < stems.size (); i++)
913 SCM forcedir = s->get_property_data ("direction");
914 if (!to_dir (forcedir))
915 set_grob_direction (s, d);
920 Only try horizontal beams for knees. No reliable detection of
921 anything else is possible here, since we don't know funky-beaming
922 settings, or X-distances (slopes!) People that want sloped
923 knee-beams, should set the directions manually.
928 this routine should take into account the stemlength scoring
929 of a possible knee/nonknee beam.
932 Beam::consider_auto_knees (Grob *me)
934 SCM scm = me->get_property ("auto-knee-gap");
935 if (!scm_is_number (scm))
938 vector<Interval> forbidden_intervals;
940 extract_grob_set (me, "normal-stems", stems);
942 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
943 Real staff_space = Staff_symbol_referencer::staff_space (me);
945 vector<Interval> head_extents_array;
946 for (vsize i = 0; i < stems.size (); i++)
948 Grob *stem = stems[i];
950 Interval head_extents = Stem::head_positions (stem);
951 if (!head_extents.is_empty ())
953 head_extents[LEFT] += -1;
954 head_extents[RIGHT] += 1;
955 head_extents *= staff_space * 0.5;
958 We could subtract beam Y position, but this routine only
959 sets stem directions, a constant shift does not have an
962 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
964 if (to_dir (stem->get_property_data ("direction")))
966 Direction stemdir = to_dir (stem->get_property ("direction"));
967 head_extents[-stemdir] = -stemdir * infinity_f;
970 head_extents_array.push_back (head_extents);
972 forbidden_intervals.push_back (head_extents);
976 Real max_gap_len = 0.0;
978 vector<Interval> allowed_regions
979 = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
980 for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
982 Interval gap = allowed_regions[i];
985 the outer gaps are not knees.
987 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
990 if (gap.length () >= max_gap_len)
992 max_gap_len = gap.length ();
997 Real beam_translation = get_beam_translation (me);
998 Real beam_thickness = Beam::get_beam_thickness (me);
999 int beam_count = Beam::get_beam_count (me);
1000 Real height_of_beams = beam_thickness / 2
1001 + (beam_count - 1) * beam_translation;
1002 Real threshold = scm_to_double (scm) + height_of_beams;
1004 if (max_gap_len > threshold)
1007 for (vsize i = 0; i < stems.size (); i++)
1009 Grob *stem = stems[i];
1010 Interval head_extents = head_extents_array[j++];
1012 Direction d = (head_extents.center () < max_gap.center ())
1015 stem->set_property ("direction", scm_from_int (d));
1017 head_extents.intersect (max_gap);
1018 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
1023 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
1025 Beam::calc_stem_shorten (SCM smob)
1027 Grob *me = unsmob_grob (smob);
1030 shortening looks silly for x staff beams
1033 return scm_from_int (0);
1035 Real forced_fraction = 1.0 * forced_stem_count (me)
1036 / normal_stem_count (me);
1038 int beam_count = get_beam_count (me);
1040 SCM shorten_list = me->get_property ("beamed-stem-shorten");
1041 if (shorten_list == SCM_EOL)
1042 return scm_from_int (0);
1044 Real staff_space = Staff_symbol_referencer::staff_space (me);
1047 = robust_list_ref (beam_count - 1, shorten_list);
1048 Real shorten = scm_to_double (shorten_elt) * staff_space;
1050 shorten *= forced_fraction;
1053 return scm_from_double (shorten);
1055 return scm_from_double (0.0);
1058 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
1060 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
1062 Grob *me = unsmob_grob (smob);
1063 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
1064 bool cbs = to_boolean (align_broken_intos);
1066 Beam_scoring_problem problem (me, ys, cbs);
1067 ys = problem.solve ();
1069 return ly_interval2scm (ys);
1073 Report slice containing the numbers that are both in (car BEAMING)
1077 where_are_the_whole_beams (SCM beaming)
1081 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1083 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1085 l.add_point (scm_to_int (scm_car (s)));
1091 /* Return the Y position of the stem-end, given the Y-left, Y-right
1092 in POS for stem S. This Y position is relative to S. */
1094 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1095 Real xl, Real xr, Direction feather_dir,
1096 Drul_array<Real> pos, bool french)
1098 Real beam_translation = get_beam_translation (me);
1099 Direction stem_dir = get_grob_direction (stem);
1102 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1103 Real xdir = 2 * relx - 1;
1105 Real stem_y = linear_combination (pos, xdir);
1107 SCM beaming = stem->get_property ("beaming");
1109 Slice beam_slice (french
1110 ? where_are_the_whole_beams (beaming)
1111 : Stem::beam_multiplicity (stem));
1112 if (beam_slice.is_empty ())
1113 beam_slice = Slice (0, 0);
1114 Interval beam_multiplicity (beam_slice[LEFT],
1118 feather dir = 1 , relx 0->1 : factor 0 -> 1
1119 feather dir = 0 , relx 0->1 : factor 1 -> 1
1120 feather dir = -1, relx 0->1 : factor 1 -> 0
1122 Real feather_factor = 1;
1123 if (feather_dir > 0)
1124 feather_factor = relx;
1125 else if (feather_dir < 0)
1126 feather_factor = 1 - relx;
1128 stem_y += feather_factor * beam_translation
1129 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1130 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1131 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1137 Hmm. At this time, beam position and slope are determined. Maybe,
1138 stem directions and length should set to relative to the chord's
1139 position of the beam. */
1140 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1142 Beam::set_stem_lengths (SCM smob)
1144 Grob *me = unsmob_grob (smob);
1146 /* trigger callbacks. */
1147 (void) me->get_property ("direction");
1148 (void) me->get_property ("beaming");
1150 SCM posns = me->get_property ("positions");
1152 extract_grob_set (me, "stems", stems);
1157 for (int a = 2; a--;)
1158 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1160 Drul_array<Real> pos = ly_scm2realdrul (posns);
1161 Real staff_space = Staff_symbol_referencer::staff_space (me);
1162 scale_drul (&pos, staff_space);
1166 if (robust_scm2int (me->get_property ("gap-count"), 0))
1169 thick = get_beam_thickness (me);
1172 Grob *fvs = first_normal_stem (me);
1173 Grob *lvs = last_normal_stem (me);
1175 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1176 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1178 for (vsize i = 0; i < stems.size (); i++)
1182 bool french = to_boolean (s->get_property ("french-beaming"));
1183 Real stem_y = calc_stem_y (me, s, common,
1184 x_span[LEFT], x_span[RIGHT], feather_dir,
1185 pos, french && s != lvs && s != fvs);
1188 Make the stems go up to the end of the beam. This doesn't matter
1189 for normal beams, but for tremolo beams it looks silly otherwise.
1192 && !Stem::is_invisible (s))
1193 stem_y += thick * 0.5 * get_grob_direction (s);
1196 Do set_stem_positions for invisible stems too, so tuplet brackets
1197 have a reference point for sloping
1199 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1206 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1208 extract_grob_set (me, "stems", stems);
1210 for (vsize i = 0; i < stems.size (); i++)
1213 Don't overwrite user settings.
1215 for (LEFT_and_RIGHT (d))
1217 Grob *stem = stems[i];
1218 SCM beaming_prop = stem->get_property ("beaming");
1219 if (beaming_prop == SCM_EOL
1220 || index_get_cell (beaming_prop, d) == SCM_EOL)
1222 int count = beaming->beamlet_count (i, d);
1224 && i + 1 < stems.size ()
1225 && Stem::is_invisible (stem))
1226 count = min (count, beaming->beamlet_count (i, -d));
1228 if ( ((i == 0 && d == LEFT)
1229 || (i == stems.size () - 1 && d == RIGHT))
1230 && stems.size () > 1
1231 && to_boolean (me->get_property ("clip-edges")))
1234 Stem::set_beaming (stem, count, d);
1241 Beam::forced_stem_count (Grob *me)
1243 extract_grob_set (me, "normal-stems", stems);
1246 for (vsize i = 0; i < stems.size (); i++)
1250 /* I can imagine counting those boundaries as a half forced stem,
1251 but let's count them full for now. */
1252 Direction defdir = to_dir (s->get_property ("default-direction"));
1254 if (abs (Stem::chord_start_y (s)) > 0.1
1256 && get_grob_direction (s) != defdir)
1263 Beam::normal_stem_count (Grob *me)
1265 extract_grob_set (me, "normal-stems", stems);
1266 return stems.size ();
1270 Beam::first_normal_stem (Grob *me)
1272 extract_grob_set (me, "normal-stems", stems);
1273 return stems.size () ? stems[0] : 0;
1277 Beam::last_normal_stem (Grob *me)
1279 extract_grob_set (me, "normal-stems", stems);
1280 return stems.size () ? stems.back () : 0;
1286 handle rest under beam (do_post: beams are calculated now)
1287 what about combination of collisions and rest under beam.
1291 rest -> stem -> beam -> interpolate_y_position ()
1293 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1295 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1297 Grob *rest = unsmob_grob (smob);
1298 if (scm_is_number (rest->get_property ("staff-position")))
1299 return scm_from_int (0);
1301 Real offset = robust_scm2double (prev_offset, 0.0);
1303 Grob *st = unsmob_grob (rest->get_object ("stem"));
1306 return scm_from_double (0.0);
1307 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1309 || !Beam::has_interface (beam)
1310 || !Beam::normal_stem_count (beam))
1311 return scm_from_double (0.0);
1313 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1315 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1316 Drul_array<Real> (0, 0)));
1318 for (LEFT_and_RIGHT (dir))
1319 pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1321 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1323 scale_drul (&pos, staff_space);
1325 Real dy = pos[RIGHT] - pos[LEFT];
1327 extract_grob_set (beam, "stems", stems);
1328 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1330 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1331 Interval (0.0, 0.0));
1332 Real x0 = x_span[LEFT];
1333 Real dx = x_span.length ();
1334 Real slope = dy && dx ? dy / dx : 0;
1336 Direction d = get_grob_direction (stem);
1337 Real stem_y = pos[LEFT]
1338 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1340 Real beam_translation = get_beam_translation (beam);
1341 Real beam_thickness = Beam::get_beam_thickness (beam);
1344 TODO: this is not strictly correct for 16th knee beams.
1347 = Stem::beam_multiplicity (stem).length () + 1;
1349 Real height_of_my_beams = beam_thickness / 2
1350 + (beam_count - 1) * beam_translation;
1351 Real beam_y = stem_y - d * height_of_my_beams;
1353 Interval rest_extent = rest->extent (rest, Y_AXIS);
1354 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1356 Real rest_dim = rest_extent[d];
1357 Real minimum_distance
1358 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1359 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1361 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1363 shift /= staff_space;
1365 /* Always move discretely by half spaces */
1366 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1368 Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1369 staff_span *= staff_space / 2;
1371 /* Inside staff, move by whole spaces*/
1372 if (staff_span.contains (rest_extent[d] + staff_space * shift)
1373 || staff_span.contains (rest_extent[-d] + staff_space * shift))
1374 shift = ceil (fabs (shift)) * sign (shift);
1376 return scm_from_double (offset + staff_space * shift);
1380 Estimate the position of a rest under a beam,
1381 as the average position of its neighboring heads.
1383 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1385 Beam::pure_rest_collision_callback (SCM smob,
1390 Real previous = robust_scm2double (prev_offset, 0.0);
1392 Grob *me = unsmob_grob (smob);
1393 Grob *stem = unsmob_grob (me->get_object ("stem"));
1395 return scm_from_double (previous);
1396 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1398 || !Beam::normal_stem_count (beam)
1399 || !is_direction (beam->get_property_data ("direction")))
1400 return scm_from_double (previous);
1402 Real ss = Staff_symbol_referencer::staff_space (me);
1405 This gives the extrema of rest positions.
1406 Even with noteheads on ledgers, beams typically remain within the staff,
1407 and push rests at most one staff-space (2 positions) from the staff.
1409 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1410 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1411 rest_max_pos.widen (2);
1413 extract_grob_set (beam, "stems", stems);
1414 vector<Grob *> my_stems;
1416 for (vsize i = 0; i < stems.size (); i++)
1417 if (Stem::head_count (stems[i]) || stems[i] == stem)
1418 my_stems.push_back (stems[i]);
1422 for (vsize i = 0; i < my_stems.size (); i++)
1423 if (my_stems[i] == stem)
1431 if (idx == (vsize) - 1 || my_stems.size () == 1)
1432 return scm_from_double (previous);
1434 left = right = my_stems[1];
1435 else if (idx == my_stems.size () - 1)
1436 left = right = my_stems[idx - 1];
1439 left = my_stems[idx - 1];
1440 right = my_stems[idx + 1];
1443 /* In stems with several heads, use the one closest to the beam. */
1444 Direction beamdir = get_grob_direction (beam);
1445 Real shift = min (max ( (Stem::head_positions (left)[beamdir]
1446 + Stem::head_positions (right)[beamdir]) / 2.0,
1447 rest_max_pos[DOWN]),
1452 // So that ceil below kicks in for rests that would otherwise brush
1453 // up against a beam quanted to a ledger line, add a bit of space
1454 // between the beam and the rest.
1455 shift += (0.01 * beamdir);
1457 /* Always move by a whole number of staff spaces */
1458 shift = ceil (fabs (shift / ss)) * ss * sign (shift);
1460 return scm_from_double (previous + shift);
1464 Beam::is_knee (Grob *me)
1466 SCM k = me->get_property ("knee");
1467 if (scm_is_bool (k))
1468 return ly_scm2bool (k);
1472 extract_grob_set (me, "stems", stems);
1473 for (vsize i = stems.size (); i--;)
1475 Direction dir = get_grob_direction (stems[i]);
1484 me->set_property ("knee", ly_bool2scm (knee));
1490 Beam::is_cross_staff (Grob *me)
1492 extract_grob_set (me, "stems", stems);
1493 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1494 for (vsize i = 0; i < stems.size (); i++)
1495 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1500 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1502 Beam::calc_cross_staff (SCM smob)
1504 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1508 Beam::get_direction_beam_count (Grob *me, Direction d)
1510 extract_grob_set (me, "stems", stems);
1513 for (vsize i = stems.size (); i--;)
1516 Should we take invisible stems into account?
1518 if (get_grob_direction (stems[i]) == d)
1519 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1525 ADD_INTERFACE (Beam,
1528 "The @code{beam-thickness} property is the weight of beams,"
1529 " measured in staffspace. The @code{direction} property is"
1530 " not user-serviceable. Use the @code{direction} property"
1531 " of @code{Stem} instead.\n"
1532 "The following properties may be set in the @code{details}"
1536 "@item stem-length-demerit-factor\n"
1537 "Demerit factor used for inappropriate stem lengths.\n"
1538 "@item secondary-beam-demerit\n"
1539 "Demerit used in quanting calculations for multiple"
1541 "@item region-size\n"
1542 "Size of region for checking quant scores.\n"
1544 "Epsilon for beam quant code to check for presence"
1546 "@item stem-length-limit-penalty\n"
1547 "Penalty for differences in stem lengths on a beam.\n"
1548 "@item damping-direction-penalty\n"
1549 "Demerit penalty applied when beam direction is different"
1550 " from damping direction.\n"
1551 "@item hint-direction-penalty\n"
1552 "Demerit penalty applied when beam direction is different"
1553 " from damping direction, but damping slope is"
1554 " <= @code{round-to-zero-slope}.\n"
1555 "@item musical-direction-factor\n"
1556 "Demerit scaling factor for difference between"
1557 " beam slope and music slope.\n"
1558 "@item ideal-slope-factor\n"
1559 "Demerit scaling factor for difference between"
1560 " beam slope and damping slope.\n"
1561 "@item round-to-zero-slope\n"
1562 "Damping slope which is considered zero for purposes of"
1563 " calculating direction penalties.\n"
1569 "beamed-stem-shorten "
1577 "collision-interfaces "
1578 "collision-voice-only "
1589 "neutral-direction "
1592 "quantized-positions "