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 "align-interface.hh"
41 #include "beam-scoring-problem.hh"
42 #include "beaming-pattern.hh"
43 #include "directional-element-interface.hh"
44 #include "grob-array.hh"
45 #include "international.hh"
46 #include "interval-set.hh"
51 #include "note-head.hh"
52 #include "output-def.hh"
53 #include "pointer-group-interface.hh"
54 #include "rhythmic-head.hh"
56 #include "staff-symbol.hh"
57 #include "staff-symbol-referencer.hh"
61 #if DEBUG_BEAM_SCORING
62 #include "text-interface.hh" // debug output.
63 #include "font-interface.hh" // debug output.
68 Beam_stem_segment::Beam_stem_segment ()
70 max_connect_ = 1000; // infinity
80 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
82 return a.horizontal_[LEFT] < b.horizontal_[LEFT];
85 Beam_segment::Beam_segment ()
91 Beam::add_stem (Grob *me, Grob *s)
93 if (Stem::get_beam (s))
95 programming_error ("Stem already has beam");
99 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
100 s->set_object ("beam", me->self_scm ());
101 add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
105 Beam::get_beam_thickness (Grob *me)
107 return robust_scm2double (me->get_property ("beam-thickness"), 0)
108 * Staff_symbol_referencer::staff_space (me);
111 /* Return the translation between 2 adjoining beams. */
113 Beam::get_beam_translation (Grob *me)
115 int beam_count = get_beam_count (me);
116 Real staff_space = Staff_symbol_referencer::staff_space (me);
117 Real line = Staff_symbol_referencer::line_thickness (me);
118 Real beam_thickness = get_beam_thickness (me);
119 Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
121 Real beam_translation = beam_count < 4
122 ? (2 * staff_space + line - beam_thickness) / 2.0
123 : (3 * staff_space + line - beam_thickness) / 3.0;
125 return fract * beam_translation;
128 /* Maximum beam_count. */
130 Beam::get_beam_count (Grob *me)
134 extract_grob_set (me, "stems", stems);
135 for (vsize i = 0; i < stems.size (); i++)
137 Grob *stem = stems[i];
138 m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
145 Beam::calc_normal_stems (SCM smob)
147 Grob *me = unsmob_grob (smob);
149 extract_grob_set (me, "stems", stems);
150 SCM val = Grob_array::make_array ();
151 Grob_array *ga = unsmob_grob_array (val);
152 for (vsize i = 0; i < stems.size (); i++)
153 if (Stem::is_normal_stem (stems[i]))
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
161 Beam::calc_direction (SCM smob)
163 Grob *me = unsmob_grob (smob);
165 /* Beams with less than 2 two stems don't make much sense, but could happen
172 Direction dir = CENTER;
174 int count = normal_stem_count (me);
177 extract_grob_set (me, "stems", stems);
178 if (stems.size () == 0)
180 me->warning (_ ("removing beam with no stems"));
183 return SCM_UNSPECIFIED;
187 Grob *stem = first_normal_stem (me);
190 This happens for chord tremolos.
195 if (is_direction (stem->get_property_data ("direction")))
196 dir = to_dir (stem->get_property_data ("direction"));
198 dir = to_dir (stem->get_property ("default-direction"));
205 dir = get_default_dir (me);
207 consider_auto_knees (me);
212 set_stem_directions (me, dir);
215 return scm_from_int (dir);
218 /* We want a maximal number of shared beams, but if there is choice, we
219 * take the one that is closest to the end of the stem. This is for
231 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
235 Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
239 for (int i = lslice[-left_dir];
240 (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
243 for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
245 int k = -right_dir * scm_to_int (scm_car (s)) + i;
246 if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
250 if (count >= best_count)
260 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
262 Beam::calc_beaming (SCM smob)
264 Grob *me = unsmob_grob (smob);
266 extract_grob_set (me, "stems", stems);
269 last_int.set_empty ();
271 SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
272 Direction last_dir = CENTER;
273 for (vsize i = 0; i < stems.size (); i++)
275 Grob *this_stem = stems[i];
276 SCM this_beaming = this_stem->get_property ("beaming");
278 Direction this_dir = get_grob_direction (this_stem);
279 if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
281 int start_point = position_with_maximal_common_beams
282 (last_beaming, this_beaming,
283 last_dir ? last_dir : this_dir,
290 new_slice.set_empty ();
291 SCM s = index_get_cell (this_beaming, d);
292 for (; scm_is_pair (s); s = scm_cdr (s))
295 = start_point - this_dir * scm_to_int (scm_car (s));
297 new_slice.add_point (new_beam_pos);
298 scm_set_car_x (s, scm_from_int (new_beam_pos));
301 while (flip (&d) != LEFT);
303 if (!new_slice.is_empty ())
304 last_int = new_slice;
309 FIXME: what's this for?
311 SCM s = scm_cdr (this_beaming);
312 for (; scm_is_pair (s); s = scm_cdr (s))
314 int np = -this_dir * scm_to_int (scm_car (s));
315 scm_set_car_x (s, scm_from_int (np));
316 last_int.add_point (np);
320 if (scm_ilength (scm_cdr (this_beaming)) > 0)
322 last_beaming = this_beaming;
331 operator <(Beam_stem_segment const &a,
332 Beam_stem_segment const &b)
334 return a.rank_ < b.rank_;
337 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
339 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
341 Beam::calc_beam_segments (SCM smob)
343 /* ugh, this has a side-effect that we need to ensure that
344 Stem #'beaming is correct */
345 Grob *me_grob = unsmob_grob (smob);
346 (void) me_grob->get_property ("beaming");
348 Spanner *me = dynamic_cast<Spanner *> (me_grob);
350 extract_grob_set (me, "stems", stems);
352 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
355 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
356 while (flip (&d) != LEFT);
358 int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
359 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
361 Position_stem_segments_map stem_segments;
362 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
364 /* There are two concepts of "rank" that are used in the following code.
365 The beam_rank is the vertical position of the beam (larger numbers are
366 closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
367 is the horizontal position of the segment (this is incremented by two
368 for each stem; the beam segment on the right side of the stem has
369 a higher rank (by one) than its neighbour to the left). */
371 for (vsize i = 0; i < stems.size (); i++)
373 Grob *stem = stems[i];
374 Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
375 Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
376 SCM beaming = stem->get_property ("beaming");
380 // Find the maximum and minimum beam ranks.
381 // Given that RANKS is never reset to empty, the interval will always be
382 // smallest for the left beamlet of the first stem, and then it might grow.
383 // Do we really want this? (It only affects the tremolo gaps) --jneem
384 for (SCM s = index_get_cell (beaming, d);
385 scm_is_pair (s); s = scm_cdr (s))
387 if (!scm_is_integer (scm_car (s)))
390 int beam_rank = scm_to_int (scm_car (s));
391 ranks.add_point (beam_rank);
394 for (SCM s = index_get_cell (beaming, d);
395 scm_is_pair (s); s = scm_cdr (s))
397 if (!scm_is_integer (scm_car (s)))
400 int beam_rank = scm_to_int (scm_car (s));
401 Beam_stem_segment seg;
403 seg.stem_x_ = stem_x;
404 seg.rank_ = 2 * i + (d + 1) / 2;
405 seg.width_ = stem_width;
408 seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
410 Direction stem_dir = get_grob_direction (stem);
413 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
414 stem_segments[beam_rank].push_back (seg);
417 while (flip (&d) != LEFT);
420 Drul_array<Real> break_overshoot
421 = robust_scm2drul (me->get_property ("break-overshoot"),
422 Drul_array<Real> (-0.5, 0.0));
424 vector<Beam_segment> segments;
425 for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
426 i != stem_segments.end (); i++)
428 vector<Beam_stem_segment> segs = (*i).second;
429 vector_sort (segs, less<Beam_stem_segment> ());
431 Beam_segment current;
433 // Iterate over all of the segments of the current beam rank,
434 // merging the adjacent Beam_stem_segments into one Beam_segment
436 int vertical_count = (*i).first;
437 for (vsize j = 0; j < segs.size (); j++)
439 // Keeping track of the different directions here is a little tricky.
440 // segs[j].dir_ is the direction of the beam segment relative to the stem
441 // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
442 // its stem) whereas event_dir refers to the edge of the beam segment that
443 // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
444 // are looking at that edge of the beam segment that is furthest from its
446 Direction event_dir = LEFT;
447 Beam_stem_segment const &seg = segs[j];
450 Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
451 // TODO: make names clearer? --jneem
452 // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
453 // on_beam_bound: whether the current segment is on the boundary of just that part
454 // of the beam with the current beam_rank
455 bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
456 : seg.stem_index_ == stems.size () - 1;
457 bool on_beam_bound = (event_dir == LEFT) ? j == 0
458 : j == segs.size () - 1;
459 bool inside_stem = (event_dir == LEFT)
460 ? seg.stem_index_ > 0
461 : seg.stem_index_ + 1 < stems.size ();
463 bool event = on_beam_bound
464 || abs (seg.rank_ - neighbor_seg.rank_) > 1
465 || (abs (vertical_count) >= seg.max_connect_
466 || abs (vertical_count) >= neighbor_seg.max_connect_);
469 // Then this edge of the current segment is irrelevant because it will
470 // be connected with the next segment in the event_dir direction.
471 // If we skip the left edge here, the right edge of
472 // the previous segment has already been skipped since
473 // the conditions are symmetric
476 current.vertical_count_ = vertical_count;
477 current.horizontal_[event_dir] = seg.stem_x_;
478 if (seg.dir_ == event_dir)
479 // then we are examining the edge of a beam segment that is furthest
483 && me->get_bound (event_dir)->break_status_dir ())
485 current.horizontal_[event_dir]
486 = (robust_relative_extent (me->get_bound (event_dir),
487 commonx, X_AXIS)[RIGHT]
488 + event_dir * break_overshoot[event_dir]);
492 Grob *stem = stems[seg.stem_index_];
493 Drul_array<Real> beamlet_length
494 = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
495 Drul_array<Real> max_proportion
496 = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
497 Real length = beamlet_length[seg.dir_];
501 Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
502 Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
504 length = min (length,
505 fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
507 current.horizontal_[event_dir] += event_dir * length;
511 // we are examining the edge of a beam segment that is closest
512 // (ie. touching, unless there is a gap) its stem.
514 current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
517 current.horizontal_[event_dir] -= event_dir * gap_length;
519 if (Stem::is_invisible (seg.stem_))
522 Need to do this in case of whole notes. We don't want the
523 heads to collide with the beams.
525 extract_grob_set (seg.stem_, "note-heads", heads);
527 for (vsize k = 0; k < heads.size (); k++)
528 current.horizontal_[event_dir]
529 = event_dir * min (event_dir * current.horizontal_[event_dir],
532 * heads[k]->extent (commonx,
533 X_AXIS)[-event_dir]);
538 if (event_dir == RIGHT)
540 segments.push_back (current);
541 current = Beam_segment ();
544 while (flip (&event_dir) != LEFT);
549 SCM segments_scm = SCM_EOL;
550 SCM *tail = &segments_scm;
552 for (vsize i = 0; i < segments.size (); i++)
554 *tail = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
555 scm_from_int (segments[i].vertical_count_)),
556 scm_cons (ly_symbol2scm ("horizontal"),
557 ly_interval2scm (segments[i].horizontal_))),
559 tail = SCM_CDRLOC (*tail);
565 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
567 Beam::calc_x_positions (SCM smob)
569 Spanner *me = unsmob_spanner (smob);
570 SCM segments = me->get_property ("beam-segments");
571 Interval x_positions;
572 x_positions.set_empty ();
573 for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
574 x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
577 Interval (0.0, 0.0)));
579 // Case for beams without segments (i.e. uniting two skips with a beam)
580 // TODO: should issue a warning? warning likely issued downstream, but couldn't hurt...
581 if (x_positions.is_empty ())
583 extract_grob_set (me, "stems", stems);
584 Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
587 x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
588 while (flip (&d) != LEFT);
590 return ly_interval2scm (x_positions);
594 Beam::get_beam_segments (Grob *me)
596 SCM segments_scm = me->get_property ("beam-segments");
597 vector<Beam_segment> segments;
598 for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
600 segments.push_back (Beam_segment ());
601 segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
602 segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
608 MAKE_SCHEME_CALLBACK (Beam, print, 1);
610 Beam::print (SCM grob)
612 Spanner *me = unsmob_spanner (grob);
614 TODO - mild code dup for all the commonx calls.
615 Some use just common_refpoint_of_array, some (in print and
616 calc_beam_segments) use this plus calls to get_bound.
618 Figure out if there is any particular reason for this and
619 consolidate in one Beam::get_common function.
621 extract_grob_set (me, "stems", stems);
622 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
625 commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
626 while (flip (&d) != LEFT);
628 vector<Beam_segment> segments = get_beam_segments (me);
630 if (!segments.size ())
633 Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
635 SCM posns = me->get_property ("quantized-positions");
636 Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
638 if (!is_number_pair (posns))
640 programming_error ("no beam positions?");
641 pos = Interval (0, 0);
644 pos = ly_scm2realdrul (posns);
646 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
648 Real dy = pos[RIGHT] - pos[LEFT];
649 Real slope = (dy && span.length ()) ? dy / span.length () : 0;
651 Real beam_thickness = get_beam_thickness (me);
652 Real beam_dy = get_beam_translation (me);
654 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
656 Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
659 int extreme = (segments[0].vertical_count_ == 0
660 ? segments[0].vertical_count_
661 : segments.back ().vertical_count_);
663 for (vsize i = 0; i < segments.size (); i++)
665 Real local_slope = slope;
667 Makes local slope proportional to the ratio of the length of this beam
671 local_slope += (feather_dir * segments[i].vertical_count_
673 * placements.length ()
676 Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
678 b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
679 Real multiplier = feather_dir ? placements[LEFT] : 1.0;
681 Interval weights (1 - multiplier, multiplier);
683 if (feather_dir != LEFT)
686 // we need two translations: the normal one and
687 // the one of the lowest segment
688 int idx[] = {i, extreme};
689 Real translations[2];
691 for (int j = 0; j < 2; j++)
692 translations[j] = slope
693 * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
694 + pos.linear_combination (CENTER)
695 + beam_dy * segments[idx[j]].vertical_count_;
697 Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
700 Tricky. The manipulation of the variable `weighted_average' below ensures
701 that beams with a RIGHT grow direction will start from the position of the
702 lowest segment at 0, and this error will decrease and decrease over the
703 course of the beam. Something with a LEFT grow direction, on the other
704 hand, will always start in the correct place but progressively accrue
705 error at broken places. This code shifts beams up given where they are
706 in the total span length (controlled by the variable `multiplier'). To
707 better understand what it does, try commenting it out: you'll see that
708 all of the RIGHT growing beams immediately start too low and get better
709 over line breaks, whereas all of the LEFT growing beams start just right
710 and get worse over line breaks.
712 Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
714 if (segments[0].vertical_count_ < 0 && feather_dir)
715 weighted_average += beam_dy * (segments.size () - 1) * factor;
717 b.translate_axis (weighted_average, Y_AXIS);
719 the_beam.add_stencil (b);
723 #if (DEBUG_BEAM_SCORING)
724 SCM annotation = me->get_property ("annotation");
725 if (scm_is_string (annotation))
727 extract_grob_set (me, "stems", stems);
730 This code prints the demerits for each beam. Perhaps this
731 should be switchable for those who want to twiddle with the
735 SCM properties = Font_interface::text_font_alist_chain (me);
737 properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
740 Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
742 Stencil score = *unsmob_stencil (Text_interface::interpret_markup
743 (me->layout ()->self_scm (), properties, annotation));
745 if (!score.is_empty ())
747 score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
748 the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
753 the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
754 return the_beam.smobbed_copy ();
758 Beam::get_default_dir (Grob *me)
760 extract_grob_set (me, "stems", stems);
762 Drul_array<Real> extremes (0.0, 0.0);
763 for (iterof (s, stems); s != stems.end (); s++)
765 Interval positions = Stem::head_positions (*s);
769 if (sign (positions[d]) == d)
770 extremes[d] = d * max (d * positions[d], d * extremes[d]);
772 while (flip (&d) != DOWN);
775 Drul_array<int> total (0, 0);
776 Drul_array<int> count (0, 0);
778 bool force_dir = false;
779 for (vsize i = 0; i < stems.size (); i++)
782 Direction stem_dir = CENTER;
783 SCM stem_dir_scm = s->get_property_data ("direction");
784 if (is_direction (stem_dir_scm))
786 stem_dir = to_dir (stem_dir_scm);
790 stem_dir = to_dir (s->get_property ("default-direction"));
793 stem_dir = to_dir (s->get_property ("neutral-direction"));
798 total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
804 if (abs (extremes[UP]) > -extremes[DOWN])
806 else if (extremes[UP] < -extremes[DOWN])
810 Direction dir = CENTER;
811 Direction d = CENTER;
812 if ((d = (Direction) sign (count[UP] - count[DOWN])))
816 && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
818 else if ((d = (Direction) sign (total[UP] - total[DOWN])))
821 dir = to_dir (me->get_property ("neutral-direction"));
826 /* Set all stems with non-forced direction to beam direction.
827 Urg: non-forced should become `without/with unforced' direction,
828 once stem gets cleaned-up. */
830 Beam::set_stem_directions (Grob *me, Direction d)
832 extract_grob_set (me, "stems", stems);
834 for (vsize i = 0; i < stems.size (); i++)
838 SCM forcedir = s->get_property_data ("direction");
839 if (!to_dir (forcedir))
840 set_grob_direction (s, d);
845 Only try horizontal beams for knees. No reliable detection of
846 anything else is possible here, since we don't know funky-beaming
847 settings, or X-distances (slopes!) People that want sloped
848 knee-beams, should set the directions manually.
853 this routine should take into account the stemlength scoring
854 of a possible knee/nonknee beam.
857 Beam::consider_auto_knees (Grob *me)
859 SCM scm = me->get_property ("auto-knee-gap");
860 if (!scm_is_number (scm))
867 extract_grob_set (me, "normal-stems", stems);
869 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
870 Real staff_space = Staff_symbol_referencer::staff_space (me);
872 vector<Interval> head_extents_array;
873 for (vsize i = 0; i < stems.size (); i++)
875 Grob *stem = stems[i];
877 Interval head_extents = Stem::head_positions (stem);
878 if (!head_extents.is_empty ())
880 head_extents[LEFT] += -1;
881 head_extents[RIGHT] += 1;
882 head_extents *= staff_space * 0.5;
885 We could subtract beam Y position, but this routine only
886 sets stem directions, a constant shift does not have an
889 head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
891 if (to_dir (stem->get_property_data ("direction")))
893 Direction stemdir = to_dir (stem->get_property ("direction"));
894 head_extents[-stemdir] = -stemdir * infinity_f;
897 head_extents_array.push_back (head_extents);
899 gaps.remove_interval (head_extents);
903 Real max_gap_len = 0.0;
905 for (vsize i = gaps.allowed_regions_.size () - 1; i != VPOS; i--)
907 Interval gap = gaps.allowed_regions_[i];
910 the outer gaps are not knees.
912 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
915 if (gap.length () >= max_gap_len)
917 max_gap_len = gap.length ();
922 Real beam_translation = get_beam_translation (me);
923 Real beam_thickness = Beam::get_beam_thickness (me);
924 int beam_count = Beam::get_beam_count (me);
925 Real height_of_beams = beam_thickness / 2
926 + (beam_count - 1) * beam_translation;
927 Real threshold = scm_to_double (scm) + height_of_beams;
929 if (max_gap_len > threshold)
932 for (vsize i = 0; i < stems.size (); i++)
934 Grob *stem = stems[i];
935 Interval head_extents = head_extents_array[j++];
937 Direction d = (head_extents.center () < max_gap.center ())
940 stem->set_property ("direction", scm_from_int (d));
942 head_extents.intersect (max_gap);
943 assert (head_extents.is_empty () || head_extents.length () < 1e-6);
948 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
950 Beam::calc_stem_shorten (SCM smob)
952 Grob *me = unsmob_grob (smob);
955 shortening looks silly for x staff beams
958 return scm_from_int (0);
960 Real forced_fraction = 1.0 * forced_stem_count (me)
961 / normal_stem_count (me);
963 int beam_count = get_beam_count (me);
965 SCM shorten_list = me->get_property ("beamed-stem-shorten");
966 if (shorten_list == SCM_EOL)
967 return scm_from_int (0);
969 Real staff_space = Staff_symbol_referencer::staff_space (me);
972 = robust_list_ref (beam_count - 1, shorten_list);
973 Real shorten = scm_to_double (shorten_elt) * staff_space;
975 shorten *= forced_fraction;
978 return scm_from_double (shorten);
980 return scm_from_double (0.0);
983 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
985 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
987 Grob *me = unsmob_grob (smob);
988 Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
989 bool cbs = to_boolean (align_broken_intos);
991 Beam_scoring_problem problem (me, ys, cbs);
992 ys = problem.solve ();
994 return ly_interval2scm (ys);
998 Report slice containing the numbers that are both in (car BEAMING)
1002 where_are_the_whole_beams (SCM beaming)
1006 for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1008 if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1010 l.add_point (scm_to_int (scm_car (s)));
1016 /* Return the Y position of the stem-end, given the Y-left, Y-right
1017 in POS for stem S. This Y position is relative to S. */
1019 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1020 Real xl, Real xr, Direction feather_dir,
1021 Drul_array<Real> pos, bool french)
1023 Real beam_translation = get_beam_translation (me);
1024 Direction stem_dir = get_grob_direction (stem);
1027 Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1028 Real xdir = 2 * relx - 1;
1030 Real stem_y = linear_combination (pos, xdir);
1032 SCM beaming = stem->get_property ("beaming");
1034 Slice beam_slice (french
1035 ? where_are_the_whole_beams (beaming)
1036 : Stem::beam_multiplicity (stem));
1037 if (beam_slice.is_empty ())
1038 beam_slice = Slice (0, 0);
1039 Interval beam_multiplicity (beam_slice[LEFT],
1043 feather dir = 1 , relx 0->1 : factor 0 -> 1
1044 feather dir = 0 , relx 0->1 : factor 1 -> 1
1045 feather dir = -1, relx 0->1 : factor 1 -> 0
1047 Real feather_factor = 1;
1048 if (feather_dir > 0)
1049 feather_factor = relx;
1050 else if (feather_dir < 0)
1051 feather_factor = 1 - relx;
1053 stem_y += feather_factor * beam_translation
1054 * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1055 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1056 - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1062 Hmm. At this time, beam position and slope are determined. Maybe,
1063 stem directions and length should set to relative to the chord's
1064 position of the beam. */
1065 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1067 Beam::set_stem_lengths (SCM smob)
1069 Grob *me = unsmob_grob (smob);
1071 /* trigger callbacks. */
1072 (void) me->get_property ("direction");
1073 (void) me->get_property ("beaming");
1075 SCM posns = me->get_property ("positions");
1077 extract_grob_set (me, "stems", stems);
1082 for (int a = 2; a--;)
1083 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1085 Drul_array<Real> pos = ly_scm2realdrul (posns);
1086 Real staff_space = Staff_symbol_referencer::staff_space (me);
1087 scale_drul (&pos, staff_space);
1091 if (robust_scm2int (me->get_property ("gap-count"), 0))
1094 thick = get_beam_thickness (me);
1097 Grob *fvs = first_normal_stem (me);
1098 Grob *lvs = last_normal_stem (me);
1100 Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1101 Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1103 for (vsize i = 0; i < stems.size (); i++)
1107 bool french = to_boolean (s->get_property ("french-beaming"));
1108 Real stem_y = calc_stem_y (me, s, common,
1109 x_span[LEFT], x_span[RIGHT], feather_dir,
1110 pos, french && s != lvs && s != fvs);
1113 Make the stems go up to the end of the beam. This doesn't matter
1114 for normal beams, but for tremolo beams it looks silly otherwise.
1117 && !Stem::is_invisible (s))
1118 stem_y += thick * 0.5 * get_grob_direction (s);
1121 Do set_stem_positions for invisible stems too, so tuplet brackets
1122 have a reference point for sloping
1124 Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1131 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1133 extract_grob_set (me, "stems", stems);
1136 for (vsize i = 0; i < stems.size (); i++)
1139 Don't overwrite user settings.
1143 Grob *stem = stems[i];
1144 SCM beaming_prop = stem->get_property ("beaming");
1145 if (beaming_prop == SCM_EOL
1146 || index_get_cell (beaming_prop, d) == SCM_EOL)
1148 int count = beaming->beamlet_count (i, d);
1150 && i + 1 < stems.size ()
1151 && Stem::is_invisible (stem))
1152 count = min (count, beaming->beamlet_count (i, -d));
1154 if ( ((i == 0 && d == LEFT)
1155 || (i == stems.size () - 1 && d == RIGHT))
1156 && stems.size () > 1
1157 && to_boolean (me->get_property ("clip-edges")))
1160 Stem::set_beaming (stem, count, d);
1163 while (flip (&d) != LEFT);
1168 Beam::forced_stem_count (Grob *me)
1170 extract_grob_set (me, "normal-stems", stems);
1173 for (vsize i = 0; i < stems.size (); i++)
1177 /* I can imagine counting those boundaries as a half forced stem,
1178 but let's count them full for now. */
1179 Direction defdir = to_dir (s->get_property ("default-direction"));
1181 if (abs (Stem::chord_start_y (s)) > 0.1
1183 && get_grob_direction (s) != defdir)
1190 Beam::normal_stem_count (Grob *me)
1192 extract_grob_set (me, "normal-stems", stems);
1193 return stems.size ();
1197 Beam::first_normal_stem (Grob *me)
1199 extract_grob_set (me, "normal-stems", stems);
1200 return stems.size () ? stems[0] : 0;
1204 Beam::last_normal_stem (Grob *me)
1206 extract_grob_set (me, "normal-stems", stems);
1207 return stems.size () ? stems.back () : 0;
1213 handle rest under beam (do_post: beams are calculated now)
1214 what about combination of collisions and rest under beam.
1218 rest -> stem -> beam -> interpolate_y_position ()
1220 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1222 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1224 Grob *rest = unsmob_grob (smob);
1225 if (scm_is_number (rest->get_property ("staff-position")))
1226 return scm_from_int (0);
1228 Real offset = robust_scm2double (prev_offset, 0.0);
1230 Grob *st = unsmob_grob (rest->get_object ("stem"));
1233 return scm_from_double (0.0);
1234 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1236 || !Beam::has_interface (beam)
1237 || !Beam::normal_stem_count (beam))
1238 return scm_from_double (0.0);
1240 Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1241 Drul_array<Real> (0, 0)));
1243 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1245 scale_drul (&pos, staff_space);
1247 Real dy = pos[RIGHT] - pos[LEFT];
1249 extract_grob_set (beam, "stems", stems);
1250 Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1252 Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1253 Interval (0.0, 0.0));
1254 Real x0 = x_span[LEFT];
1255 Real dx = x_span.length ();
1256 Real slope = dy && dx ? dy / dx : 0;
1258 Direction d = get_grob_direction (stem);
1259 Real stem_y = pos[LEFT]
1260 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1262 Real beam_translation = get_beam_translation (beam);
1263 Real beam_thickness = Beam::get_beam_thickness (beam);
1266 TODO: this is not strictly correct for 16th knee beams.
1269 = Stem::beam_multiplicity (stem).length () + 1;
1271 Real height_of_my_beams = beam_thickness / 2
1272 + (beam_count - 1) * beam_translation;
1273 Real beam_y = stem_y - d * height_of_my_beams;
1275 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1277 Interval rest_extent = rest->extent (rest, Y_AXIS);
1278 rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1280 Real rest_dim = rest_extent[d];
1281 Real minimum_distance
1282 = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1283 + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1285 Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1287 shift /= staff_space;
1288 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1290 /* Always move discretely by half spaces */
1291 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1293 /* Inside staff, move by whole spaces*/
1294 if ((rest_extent[d] + staff_space * shift) * d
1296 || (rest_extent[-d] + staff_space * shift) * -d
1298 shift = ceil (fabs (shift)) * sign (shift);
1300 return scm_from_double (offset + staff_space * shift);
1303 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1305 Beam::pure_rest_collision_callback (SCM smob,
1306 SCM, /* prev_offset */
1312 Grob *me = unsmob_grob (smob);
1313 Grob *stem = unsmob_grob (me->get_object ("stem"));
1315 return scm_from_double (amount);
1316 Grob *beam = unsmob_grob (stem->get_object ("beam"));
1318 || !Beam::normal_stem_count (beam))
1319 return scm_from_double (amount);
1321 Real ss = Staff_symbol_referencer::staff_space (me);
1324 This gives the extrema of rest positions.
1325 In general, beams are never typeset more than one staff space away
1326 from the staff in either direction.
1328 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
1329 Interval rest_max_pos = staff ? Staff_symbol::line_span (staff) : Interval (0.0, 0.0);
1330 rest_max_pos.widen (1);
1331 rest_max_pos *= ss / 2;
1333 extract_grob_set (beam, "stems", stems);
1334 vector<Grob *> my_stems;
1336 for (vsize i = 0; i < stems.size (); i++)
1337 if (Stem::head_count (stems[i]) || stems[i] == stem)
1338 my_stems.push_back (stems[i]);
1342 for (vsize i = 0; i < my_stems.size (); i++)
1343 if (my_stems[i] == stem)
1351 if (idx == (vsize) - 1 || my_stems.size () == 1)
1352 return scm_from_double (amount);
1354 left = right = my_stems[1];
1355 else if (idx == my_stems.size () - 1)
1356 left = right = my_stems[idx - 1];
1359 left = my_stems[idx - 1];
1360 right = my_stems[idx + 1];
1362 Direction beamdir = get_grob_direction (beam);
1364 Take the position between the two bounding head_positions,
1365 then bound it by the minimum and maximum positions outside the staff.
1366 4.0 = 2.0 to get out of staff space * 2.0 for the average
1368 amount = min (max ((Stem::head_positions (left)[beamdir] + Stem::head_positions (right)[beamdir]) / 4.0, rest_max_pos[DOWN]), rest_max_pos[UP]);
1370 return scm_from_double (amount);
1374 Beam::is_knee (Grob *me)
1376 SCM k = me->get_property ("knee");
1377 if (scm_is_bool (k))
1378 return ly_scm2bool (k);
1382 extract_grob_set (me, "stems", stems);
1383 for (vsize i = stems.size (); i--;)
1385 Direction dir = get_grob_direction (stems[i]);
1394 me->set_property ("knee", ly_bool2scm (knee));
1400 Beam::is_cross_staff (Grob *me)
1402 extract_grob_set (me, "stems", stems);
1403 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1404 for (vsize i = 0; i < stems.size (); i++)
1405 if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1410 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1412 Beam::calc_cross_staff (SCM smob)
1414 return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1418 Beam::get_direction_beam_count (Grob *me, Direction d)
1420 extract_grob_set (me, "stems", stems);
1423 for (vsize i = stems.size (); i--;)
1426 Should we take invisible stems into account?
1428 if (get_grob_direction (stems[i]) == d)
1429 bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1435 ADD_INTERFACE (Beam,
1438 "The @code{beam-thickness} property is the weight of beams,"
1439 " measured in staffspace. The @code{direction} property is"
1440 " not user-serviceable. Use the @code{direction} property"
1441 " of @code{Stem} instead.\n"
1442 "The following properties may be set in the @code{details}"
1446 "@item stem-length-demerit-factor\n"
1447 "Demerit factor used for inappropriate stem lengths.\n"
1448 "@item secondary-beam-demerit\n"
1449 "Demerit used in quanting calculations for multiple"
1451 "@item region-size\n"
1452 "Size of region for checking quant scores.\n"
1454 "Epsilon for beam quant code to check for presence"
1456 "@item stem-length-limit-penalty\n"
1457 "Penalty for differences in stem lengths on a beam.\n"
1458 "@item damping-direction-penalty\n"
1459 "Demerit penalty applied when beam direction is different"
1460 " from damping direction.\n"
1461 "@item hint-direction-penalty\n"
1462 "Demerit penalty applied when beam direction is different"
1463 " from damping direction, but damping slope is"
1464 " <= @code{round-to-zero-slope}.\n"
1465 "@item musical-direction-factor\n"
1466 "Demerit scaling factor for difference between"
1467 " beam slope and music slope.\n"
1468 "@item ideal-slope-factor\n"
1469 "Demerit scaling factor for difference between"
1470 " beam slope and damping slope.\n"
1471 "@item round-to-zero-slope\n"
1472 "Damping slope which is considered zero for purposes of"
1473 " calculating direction penalties.\n"
1479 "beamed-stem-shorten "
1486 "collision-interfaces "
1487 "collision-voice-only "
1499 "neutral-direction "
1502 "quantized-positions "